<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>程式開發 &#8211; 記下來</title>
	<atom:link href="https://noter.tw/code/feed/" rel="self" type="application/rss+xml" />
	<link>https://noter.tw</link>
	<description>一路上踩到的坑、遇到的問題，一點一滴記下來，希望能幫助到需要的人~</description>
	<lastBuildDate>Thu, 25 Apr 2024 08:54:06 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.3</generator>

<image>
	<url>https://noter.tw/wp-content/uploads/cropped-old-1130742_1920-1-32x32.jpg</url>
	<title>程式開發 &#8211; 記下來</title>
	<link>https://noter.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>PHPMailer + Gmail OAuth2 寄信</title>
		<link>https://noter.tw/12780/phpmailer-gmail-oauth2-%e5%af%84%e4%bf%a1/</link>
					<comments>https://noter.tw/12780/phpmailer-gmail-oauth2-%e5%af%84%e4%bf%a1/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Thu, 25 Apr 2024 08:08:36 +0000</pubDate>
				<category><![CDATA[程式開發]]></category>
		<category><![CDATA[網頁後端]]></category>
		<category><![CDATA[技術相關]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[建立 OAuth 用戶端 ID 憑證]]></category>
		<category><![CDATA[OAuth token]]></category>
		<category><![CDATA[Google Workspace]]></category>
		<category><![CDATA[PHP Gmail 寄信]]></category>
		<category><![CDATA[PHP 發送郵件]]></category>
		<category><![CDATA[get_oauth_token.php]]></category>
		<category><![CDATA[已授權的重新導向 URI]]></category>
		<category><![CDATA[oauth2 refresh token]]></category>
		<category><![CDATA[PHPMailer]]></category>
		<category><![CDATA[OAuth 用戶端 ID]]></category>
		<category><![CDATA[Gmail API]]></category>
		<category><![CDATA[Google Cloud 憑證]]></category>
		<category><![CDATA[PHPMailer Gmail OAuth2]]></category>
		<category><![CDATA[OAuth2 憑證]]></category>
		<category><![CDATA[XOAUTH2]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=12780</guid>

					<description><![CDATA[<p>小蛙有個維護的系統使用 PHPMailer 透過 Gmail 帳號密碼發送郵件，近期收到 Google 寄來的通知「自 2024 年 9 月 30 日起，應用程式必須採用 OAuth 才能存取 Google Workspace 帳戶。系統將不再支援使用密碼 (應用程式密碼除外) 的存取行為。」為了避免哪一天突然通通都只支援 OAuth，趁現在有一點時間趕快來改一下。</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/12780/phpmailer-gmail-oauth2-%e5%af%84%e4%bf%a1/" data-wpel-link="internal">PHPMailer + Gmail OAuth2 寄信</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>小蛙有個維護的系統使用 PHPMailer 透過 Gmail 帳號密碼發送郵件，近期收到 Google 寄來的通知「自 2024 年 9 月 30 日起，應用程式必須採用 OAuth 才能存取 Google Workspace 帳戶。系統將不再支援使用密碼 (應用程式密碼除外) 的存取行為。」為了避免哪一天突然通通都只支援 OAuth，趁現在有一點時間趕快來改一下。</p>



<span id="more-12780"></span>



<p>PHPMailer + Gmail OAuth 發信大概有幾個步驟：</p>



<ol class="my-li bg-darkblue wp-block-list">
<li><strong>安裝必要套件</strong>：使用 composer 在網站上安裝 phpmailer/phpmailer 及 league/oauth2-google 套件。</li>



<li><strong>建立 OAuth 2.0 用戶端 ID 憑證</strong>：到 Goolge Cloud 建立一個專案，並開啟 Gmail API 與建立一個 OAuth 2.0 用戶端 ID 憑證。</li>



<li><strong>取得 Refresh Token</strong>：設定 <code>get_oauth_token.php</code> 通過 OAuth 驗證，取得 Refresh Token。</li>



<li><strong>使用 PHPMailer 寄信</strong>：使用 PHPMailer 搭配前幾步得到的資訊寄送郵件。</li>
</ol>



<h2 class="wp-block-heading para">安裝必要套件</h2>



<p>需使用 composer 安裝必要套件，沒有 composer 的話可參考<a href="https://getcomposer.org/doc/00-intro.md" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">這邊<span class="wpel-icon wpel-image wpel-icon-6"></span></a>。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">composer require phpmailer/phpmailer
composer require league/oauth2-google</pre>



<h2 class="wp-block-heading para">建立 OAuth 2.0 用戶端 ID 憑證</h2>



<h3 class="wp-block-heading para">啟用 Gmail API</h3>



<p>進入到 <a href="https://console.cloud.google.com/apis/dashboard" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">Google Cloud<span class="wpel-icon wpel-image wpel-icon-6"></span></a>，點擊左上方專案下拉選單 -> 新增專案 -> 輸入專案名稱，只要自己能辨識的即可，下面若有機構、位置，也都必須填寫上去</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczP4fogGZPjU75mdxaeI0tKW2eGRS6FBUuaLQDUBqfgiO7knRvicztaTrMIUvqtpjyHL1b3_UA2wW586_u2pjqPXf2i5JVbEQwBDqsgfF_49ZwFj4gnTYZxNYJ6CP86gNEcwB1UuMCIhRLv6_xQfPFlq=w666-h715-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 1" style="width:500px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>專案建立完成後進入該專案，左側選擇「已啟用的 API 和服務」，點擊「啟用 API 和服務」</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczMKjfxWGckFT4SDqI9q5OGQi6NYeo_gpu-MQduZVVo3A5ArRza7b9UuIB4DF8LMkVhGYIeyC8oGBTCOQcq0vh4m4SQIzI84ARFBNMbgCIoKTGo_FG8adK4gWlOjolBpIpv3Z4v0UDeI7X8-vnvo0S64=w714-h390-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 2" style="width:500px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>找到並啟用 Gmail API</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczN60kf2CLZeKdaaKKq_T_dfsTQ51TsKgzgjJoK5oPxoAi69KAAQuqX3GWE5u5nNGBe7zP7V3EKceOYUe2cHXtLpFjLAbQgOm9lJQ1FVRBInF3Up-pmebuYjoRS37QywSXmEJk1rrieIxFPiD-oemF-X=w594-h401-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 3" style="width:400px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<h3 class="wp-block-heading para">建立 OAuth 用戶端 ID 憑證</h3>



<p>成功啟用 Gmail API 後，左側進入「憑證」選單 -> 點擊「建立憑證」 -> OAuth 用戶端 ID</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczPJKZpCSzpeLLRDHvkcwdJD2f82V1YY0UX5OZE9ykHd8uya60uNODJxm49OV5r5Qmv3F1JLR1DFHrUrGDD2fMKWaHXiF2miu0JIAHax-roCaD5dAJI4A16PN-RdZG0xIlmNhSD3LIpm8fLS3FNqSA9x=w935-h456-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 4" style="width:600px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>第一次建立需要先設定同意畫面，點擊「設定同意畫面」</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczOGpbByPCfX_QYNdc3Tty8-pY4dMxWJQdOxwQobKf0GaWAvTLjax_su14ODzwzsrdZ2RsU8t8OwBr2b4TWiI8OQ7ZStwA_Av-K3l8dkgTFGtlVaWcV1sravgQpNQQcVt2MZwamni3tLnDlWAd54SrMe=w690-h276-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 5" style="width:500px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>我們的目的是完成至少一次 OAuth 驗證取得 Refresh Token，這邊選「內部」就可以了</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczPGy3-iYpG5LY2fG9QKVqTJAah6GW-_2JCtjqsHeeYV9y2dHxGjF_aW7IR-lFGfI3cnEJyLXg8h4p6x_AvY5CRhrizpFWg9Dc2aCUp-MDQ9_S6rgUDoKVxDP8hCWRUDNu29lPxlPo87aMlhY8pTL1gj=w679-h581-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 6" style="width:400px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>應用程式名稱、使用者支援電子郵件及開發人員聯絡資訊為必填項目，其餘部份視情況填寫，填寫完成後點擊「儲存並繼續」。</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"><div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczN61095TO7WUG8Jydw_-ZEoOhID5Y4tzbiDnmw_EIlI3ueo2-T-LMeWGh0oEkOosg26OKJulTg1vTsPF-uCZD-5oXDv_iPtwv4-vlFaaULC10U_zRvGVVxjAtYVKQYxdm-QAunv4opzFP9zcAP3zcHN=w679-h798-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 7" style="object-fit:cover" title="PHPMailer + Gmail OAuth2 寄信"></figure></div></div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"><div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczNQiuqmUzaJJl-XySD31aSUGYPYyVDAVZltwi4U-9m2cpKP-4gKABuJzPwt1Dg2HfeIHFasME78Z8_TRyyXzwcq_gwbpZpvd7UNRph2x2ekx6o4zuoMD1sGyutyLuIpulDbyxdU7OqLBtaW_Cl8opWg=w677-h862-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 8" title="PHPMailer + Gmail OAuth2 寄信"></figure></div></div>
</div>



<p>第二步範圍部份直接留空，點擊「儲存並繼續」</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-2 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"><div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczMBKau2JTNrXXjqDFkP6vISLfQa_hyHJQZRr_w9v9na-CXiiEKI5e8TO7t0C3wKt5v-8aYVfi4YgRvXzNvvJ4QkjXPVHZBtmBlt4H3FkXCyXPGKRSNUNObiptfhmJ1UEhYPT5loGB9gTJ6wZKmZvJNA=w676-h552-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 9" title="PHPMailer + Gmail OAuth2 寄信"></figure></div></div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"><div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczPL1myaeyJMZsXqKb_MX2HErqMs1VuvwGiBzmkDAyYLb4BoaNkDvV1DS72TAcrkCDLkkAsGJSavFnwjg_t-cq28V3ezmndNqK-Z2OqmDslV5_YMKhD8Kq8jk3Om8akIA3lSIImxgLQfyE3e28hJ5JIh=w676-h537-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 10" title="PHPMailer + Gmail OAuth2 寄信"></figure></div></div>
</div>



<p>OAuth 同意畫面建立完成</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-3 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"><div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczPNN115kjpIKr-OPaknHyw3-TG0PVuuC9RCkdtZP5YgXMIQ8tq4-IpdYERZJ4wm0X8CFzz4nmvnSuPMhuXXuvLHu-qoQbJ7VAuR-Nu28p6jfmQmgkYYzJm6PDPPONbVzFuUCR6vR745pdeEq9fxllq2=w675-h617-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 11" title="PHPMailer + Gmail OAuth2 寄信"></figure></div></div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"><div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczP6GUlp18EkN6JAvl90LMqTV67IcvEvLRLdIZN5eL-a97ABLE9FficqfhZqCGq8hrSjQBGRmyDuFp2FP7OSTC4iwXY5Eq_SPh39XTBoGN0NH2paLIG5b8UsF4cawd3WMvmaiEUSKJvyAX3eAolyW6C7=w676-h569-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 12" style="width:416px;height:auto" title="PHPMailer + Gmail OAuth2 寄信"></figure></div></div>
</div>



<p>再回到「憑證」->「建立憑證」->「OAuth 用戶端 ID」</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczPJKZpCSzpeLLRDHvkcwdJD2f82V1YY0UX5OZE9ykHd8uya60uNODJxm49OV5r5Qmv3F1JLR1DFHrUrGDD2fMKWaHXiF2miu0JIAHax-roCaD5dAJI4A16PN-RdZG0xIlmNhSD3LIpm8fLS3FNqSA9x=w935-h456-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 13" style="width:600px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>應用程式類型選擇「網頁應用程式」，名稱為必填，可以跟小蛙一樣輸入 PHPMailer，只要能辨識即可。「已授權的重新導向 URI」填入網站下 phpmailer 套件下的 <code>get_oauth_token.php</code> 路徑 (也可以把它拉出來到外層比較好處理的地方)，小蛙這邊的例子是 </p>



<pre class="wp-block-preformatted">https://xx.xxx/vendor/phpmailer/phpmailer/get_oauth_token.php</pre>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczOvK99odz0V_ok4Ie8wEihuHHv3Xd3nohjEQ1SmNSN1I50Ee6aQvGsCmfK7gHRbfmJ5ZcSVeeErcTpRocoGuWztkqtHUyZt1oilrTZdcgLYnkeM8e4ZMt7Am8nv05r2prtZfGseGj2stkKXgvBz7dgz=w681-h993-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 14" style="width:503px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>建立完成後會得到<strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">用戶端編號</mark></strong>及<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"><strong>用戶端密鑰</strong></mark>，先存起來等一下馬上會用到。</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczPcJ7LUPlQ7amLYg7ihWiFq4zwGti6hcZPBNouXbgkQ_tRXUQmQ9CrD9mPV_-LsRN2wybLy7tyxGnXAI0ndlSzMC1TGMR3QuT8TNQYwXeBn6HTxwvEE_J_U_vmVtEymVD4jh9EnFf4UYMtweBlLZ2K1=w570-h480-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 15" style="width:400px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<h2 class="wp-block-heading para">取得 Refresh Token</h2>



<p>找到 PHPMailer 下的 <code>get_oauth_token.php</code> (預設應該會在網頁目錄下的 <code>vender/phpmailer/phpmailer/</code>)，PHPMailer 原始附帶的檔案會一直卡在 Provider missing 或 Invalid state，沒辦法正確取得 Refresh Token，因此小蛙對 <code>get_oauth_token.php</code> 做了一些修改，確保可以拿到 token。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;?php
/**
 * PHPMailer - PHP email creation and transport class.
 * PHP Version 5.5
 * @package PHPMailer
 * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
 * @author Marcus Bointon (Synchro/coolbru) &lt;phpmailer@synchromedia.co.uk>
 * @author Jim Jagielski (jimjag) &lt;jimjag@gmail.com>
 * @author Andy Prevost (codeworxtech) &lt;codeworxtech@users.sourceforge.net>
 * @author Brent R. Matzelle (original founder)
 * @copyright 2012 - 2020 Marcus Bointon
 * @copyright 2010 - 2012 Jim Jagielski
 * @copyright 2004 - 2009 Andy Prevost
 * @license https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU Lesser General Public License
 * @note This program is distributed in the hope that it will be useful - WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.
 */

/**
 * Get an OAuth2 token from an OAuth2 provider.
 * * Install this script on your server so that it's accessible
 * as [https/http]://&lt;yourdomain>/&lt;folder>/get_oauth_token.php
 * e.g.: http://localhost/phpmailer/get_oauth_token.php
 * * Ensure dependencies are installed with 'composer install'
 * * Set up an app in your Google/Yahoo/Microsoft account
 * * Set the script address as the app's redirect URL
 * If no refresh token is obtained when running this file,
 * revoke access to your app and run the script again.
 */

namespace PHPMailer\PHPMailer;

// 用來看錯誤訊息以及刪除 opcache
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
@opcache_reset();
	
/**
 * Aliases for League Provider Classes
 * Make sure you have added these to your composer.json and run `composer install`
 * Plenty to choose from here:
 * @see https://oauth2-client.thephpleague.com/providers/thirdparty/
 */
//@see https://github.com/thephpleague/oauth2-google
use League\OAuth2\Client\Provider\Google;

// 根據自己的 vendor 路徑進行調整
require '../../autoload.php';

session_start();

// 改成剛剛在 Google Cloud 上設定的值
$providerName = 'Google';
// 用戶端編號
$clientId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com';
// 用戶端密碼
$clientSecret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
// 已授權的重新導向 URI
$redirectUri = 'https://xxx.xxxx.xxx/get_oauth_token.php';

$_SESSION['provider'] = $providerName;
$_SESSION['clientId'] = $clientId;
$_SESSION['clientSecret'] = $clientSecret;

$params = [
    'clientId' => $clientId,
    'clientSecret' => $clientSecret,
    'redirectUri' => $redirectUri,
    'accessType' => 'offline'
];

$options = [];
$provider = null;

switch ($providerName) {
    case 'Google':
        $provider = new Google($params);
        $options = [
            'scope' => [
                'https://mail.google.com/'
            ]
        ];
        break;
}

if (null === $provider) {
    exit('Provider missing');
}

if (!isset($_GET['code'])) {
    //If we don't have an authorization code then get one
    $authUrl = $provider->getAuthorizationUrl($options);
    $_SESSION['oauth2state'] = $provider->getState();
    header('Location: ' . $authUrl);
    exit;
    //Check given state against previously stored one to mitigate CSRF attack
	// 這段不註解掉的話，會一直卡在 Invalid state，沒辦法拿到正確的 token
/*} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
	error_log("3333");
	error_log("state 3333 " . $_SESSION['oauth2state']);
    //unset($_SESSION['oauth2state']);
    //unset($_SESSION['provider']);
    exit('Invalid state');*/
} else {
    //unset($_SESSION['provider']);
    //Try to get an access token (using the authorization code grant)
    $token = $provider->getAccessToken(
        'authorization_code',
        [
            'code' => $_GET['code']
        ]
    );
    //Use this to interact with an API on the users behalf
    //Use this to get a new access token if the old one expires
    echo 'Refresh Token: ', htmlspecialchars($token->getRefreshToken());
}</pre>



<p>將內容貼上後，開啟瀏覽器連到該網址 (就是<strong>已授權的重新導向 URI)</strong> 開始進入 OAuth 登入流程，選擇要登入的帳號</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczNd2HKRgMmOhBQaMmcC9pgUefAAsic1hkMqQzNHntO-1WCADxGh2gI74MXUtCztrElVXrZnqGeFwdlhg_Wu_VqUPq8esg9tm6kbcFcFJ6ixXkpRNfY7_t6sFZkMkzJBqqt9NtKBjoHpawuXktXa34LZ=w1058-h401-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 16" style="width:700px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>確認登入</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczNyIy5svb1IInWm1pj7vJurWmbOEW6vM4LNL0JxtFFqilMaB_mu_PT3WV-MMYfXhKIDG99pTDOlE9fOKxFH8Z4MUhbM-E-bWcSaOt5cJ3hoNDTeWIut3PJ3g-TgfLLJZNkOiyQjGLX6-Yr4VXKBW7sh=w1063-h490-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 17" style="width:700px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>信任應用程式</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczOW1-cRB-uxqhR9v7znPOoAw006Lx3tuBqFpba4WSijsidXKh0hKuWr1kvTz7KYfo0n38zK11rLkHPdvRNdOIqcpKiQcYfUz3zLxmJomAcC5j_gg2zUM8-yvCaGCw4EWqImteEhvYos6aLjoRAoojr2=w1057-h699-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 18" style="width:700px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>最後會印出 Refresh Token: xxxxxxxxxx，這個 token 一定要存好，少了這個 PHPMailer 就沒辦法透過 OAuth 驗證寄信喔，原本以為 <code>get_oauth_token.php</code> 每次執行應該 &#8220;有機會&#8221; 去換一個可使用的 Token 回來，不過再執行一次會發現 Refresh Token: 變成空白。</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczMAEzc9EkM0XaVMs8px7YKL3EXAUJ2L_uJ8YlSTBBnEJAP8BH4JqC2s_b5_Rm1LNffiyRiTBmehd0jhvLbPhpAslSkq-8Lx1BfxwSTfg1jLVcAgOcadwiOl_V0YmZzKy86GplsW1NK61FBz9sCKGyCH=w861-h72-s-rw-gm" alt="AP1GczMAEzc9EkM0XaVMs8px7YKL3EXAUJ2L uJ8YlSTBBnEJAP8BH4JqC2s b5 Rm1LNffiyRiTBmehd0jhvLbPhpAslSkq 8Lx1BfxwSTfg1jLVcAgOcadwiOl V0YmZzKy86GplsW1NK61FBz9sCKGyCH=w861 h72 s rw gm PHPMailer + Gmail OAuth2 寄信" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<p>到這邊就成功取得可以使用的 Token 了，回到 PHPMailer 寄信程式吧！</p>



<h2 class="wp-block-heading para">使用 PHPMailer 寄信</h2>



<p>複製下面的檔案，視自身情況修改幾個地方：</p>



<ol class="my-li bg-darkblue wp-block-list">
<li><strong>require(&#8216;./vendor/autoload.php&#8217;);</strong>：將位置指向正確的路徑。</li>



<li><strong>$client_id</strong>：前面步驟取得的用戶端編號。</li>



<li><strong>$client_secret</strong>：前面步驟取得的用戶端密碼。</li>



<li><strong>$refresh_token</strong>：前面步驟取得的 Token。</li>



<li><strong>$email</strong>：前面步驟通過 OAuth 的信箱。</li>



<li>信件相關之寄件人、收件人、主旨、內容 &#8230; 等。</li>
</ol>



<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;?php

// 用來看錯誤訊息以及刪除 opcache
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
@opcache_reset();

// 根據自己的路徑修改 composer 的載入位置
require('./vendor/autoload.php');

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\OAuth;
use League\OAuth2\Client\Provider\Google;

// 用戶端編號
$client_id 	= 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com';
// 用戶端密碼
$client_secret 	= 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// 從前一個步驟取得的 Token
$refresh_token  = '1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxg';
// OAuth 的信箱
$email		= 'xxxxxxx@xxxxxxx';

$mail = new PHPMailer(true);

$provider = new Google(
	[
		'clientId' => $client_id,
		'clientSecret' => $client_secret,
	]
);

$mail->setOAuth(
	new OAuth(
		[
			'provider'     => $provider,
			'clientId'     => $client_id,
			'clientSecret' => $client_secret,
			'refreshToken' => $refresh_token,
			'userName'     => $email,
		]
	)
);

$mail->isSMTP();

$mail->SMTPDebug 	= 3;				// 如果需要顯示debug內容,測完確定沒問題請改成 0
$mail->SMTPAuth 	= true; 
$mail->SMTPSecure 	= 'tls';
$mail->AuthType 	= 'XOAUTH2';
$mail->Host 		= "smtp.gmail.com";
$mail->Port 		= 587;
$mail->CharSet 		= "utf-8";

$mail->setFrom('寄件人信箱', '寄件人名稱');    // 寄件人
$mail->addAddress('收件人信箱', '收件人名稱'); // 收件人
$mail->isHTML(true);                         // 寄送 HTML 格式郵件

$mail->Subject = "郵件主旨";                  // 主旨
$mail->Body    = "&lt;Strong>測試&lt;/Strong>";     // 信件內容
$mail->AltBody = "測試";                      // 無法顯示 HTML 時的內容

$mail->send();

?></pre>



<p>一切就緒後連到該寄信的 PHP 頁面，就可以成功把信寄出囉！</p>


<div class="wp-block-image">
<figure class="aligncenter is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AP1GczMLz2nu2nJ4oC9XUvFKUNbJNFysGsEQYN9LUHzuBmiEiyxnq5avpPb8-nMINhWVug9WD-guXHdUyfrte1yUxdbLU9O4rRn0fxZZ59U1vF0-j_1XofQvqYgfIHzmGwr-hf9y1ccy9TBervTm_5lZ7Y6w=w630-h363-s-rw-gm" alt="PHPMailer + Gmail OAuth 寄信 19" style="width:400px" title="PHPMailer + Gmail OAuth2 寄信"></figure></div>


<pre class="wp-block-preformatted">延伸閱讀：<a href="https://noter.tw/10744/google-%e8%a9%a6%e7%ae%97%e8%a1%a8%e6%89%b9%e9%87%8f%e7%99%bc%e9%80%81-email-yamm/" data-wpel-link="internal">Google 試算表批量發送 Email (免費版每日 50 封)</a></pre>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/12780/phpmailer-gmail-oauth2-%e5%af%84%e4%bf%a1/" data-wpel-link="internal">PHPMailer + Gmail OAuth2 寄信</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/12780/phpmailer-gmail-oauth2-%e5%af%84%e4%bf%a1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>加密的階段作業 (SSL) Cookie 中遺漏安全屬性</title>
		<link>https://noter.tw/11094/%e5%8a%a0%e5%af%86%e7%9a%84%e9%9a%8e%e6%ae%b5%e4%bd%9c%e6%a5%ad-ssl-cookie-%e4%b8%ad%e9%81%ba%e6%bc%8f%e5%ae%89%e5%85%a8%e5%b1%ac%e6%80%a7/</link>
					<comments>https://noter.tw/11094/%e5%8a%a0%e5%af%86%e7%9a%84%e9%9a%8e%e6%ae%b5%e4%bd%9c%e6%a5%ad-ssl-cookie-%e4%b8%ad%e9%81%ba%e6%bc%8f%e5%ae%89%e5%85%a8%e5%b1%ac%e6%80%a7/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Tue, 14 Mar 2023 02:51:17 +0000</pubDate>
				<category><![CDATA[網頁前端]]></category>
		<category><![CDATA[加密的階段作業 (SSL) Cookie 中遺漏安全屬性]]></category>
		<category><![CDATA[cookieFlags]]></category>
		<category><![CDATA[cookie secure]]></category>
		<category><![CDATA[_ga]]></category>
		<category><![CDATA[Google Analytics]]></category>
		<category><![CDATA[Google 分析]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=11094</guid>

					<description><![CDATA[<p>加密的階段作業 (SSL) Cookie 中遺漏安全屬性 。小蛙負責的網站必須要通過資訊安全相關掃描，否則就會被下架，因此每隔一段時間就會掃描一次看看有沒有什麼問題，通常都是掃出一些不大不小的問題，例如這個 加密的階段作業 (SSL) Cookie 中遺漏安全屬性。</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/11094/%e5%8a%a0%e5%af%86%e7%9a%84%e9%9a%8e%e6%ae%b5%e4%bd%9c%e6%a5%ad-ssl-cookie-%e4%b8%ad%e9%81%ba%e6%bc%8f%e5%ae%89%e5%85%a8%e5%b1%ac%e6%80%a7/" data-wpel-link="internal">加密的階段作業 (SSL) Cookie 中遺漏安全屬性</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>小蛙負責的網站必須要通過資訊安全相關掃描，否則就會被下架，因此每隔一段時間就會掃描一次看看有沒有什麼問題，通常都是掃出一些不大不小的問題，例如這個 <strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">加密的階段作業 (SSL) Cookie 中遺漏安全屬性</mark></strong>。</p>



<span id="more-11094"></span>



<p>上一次掃出來的時候已經修正過了，這次加了一些功能後又出現，下圖是安全掃描報告，看到 _ga, _gid 之類的很快就知道是使用 Google 分析 (Google Analytics) 造成的。</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AMWts8CpWO6L1Qe5Nsh4CCbvsy2_g-i-g0NuZANVGH6eae8A8LugZUn96qHAZeKNYKrp-QNQ69xJnuT9e_4BrR3GNOSy9RxUVNpU9YFrCm7Obj0aWSBglx7jyqL0bfYdDFNZc12PNmovWU7tfBs5oHy8XvMD=w800-h288-rw" alt="加密的階段作業 (SSL) Cookie 中遺漏安全屬性" title="加密的階段作業 (SSL) Cookie 中遺漏安全屬性"></figure></div>


<p>要修正這個嚴重性被標示為「中」的問題很簡單，只要到載入及初始化 Google 分析的部份，例如小蛙找到其中一個頁面 Google 分析的載入有這段</p>



<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ga('create', 'UA-xxxxxxxx-x', 'auto');</pre>



<p>這時候要用到 Google 分析提供的 cookieFlags，把上述設定改成 </p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ga('create', 'UA-xxxxxxxx-x', {'cookieFlags': 'SameSite=None; Secure'});</pre>



<p>就可以囉 ~ 更多 cookieFlags 的設定可參考：<a href="https://www.ichdata.com/cookieflags-field-google-" target="_blank" rel="noreferrer noopener ugc nofollow external" data-wpel-link="external" class="wpel-icon-right">Google Analytics设置cookieFlags &#8211; GA 小站analytics.html<span class="wpel-icon wpel-image wpel-icon-6"></span></a>，F12 看看設定前跟設定後的差別。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AMWts8Cq_ziC0VswS6rCRJKZYvWfV4uGDD9mzuNKwnj--5cGPj5aJVFlKFGXnHTAqmn6JsHYY4sVIXQNct366HaThiUPcTUfy44LqKj2D7IKnrVfmEYJk5Umm8s_ng41YtLw5uoJP9IipOvVKs6O8M2LmfyO=w1151-h117-rw" alt="cookieFlags secure 設定前" title="加密的階段作業 (SSL) Cookie 中遺漏安全屬性"><figcaption>cookieFlags secure 設定前</figcaption></figure>



<figure class="wp-block-image size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AMWts8CEoTm4A21-ms2U2YLb7lzDJ7-x0LlwO20E_LIBk8pprJ0AHGoU_6JHjsAbbUsMbL_OLvX-5fW3BHKaPIZtpLfBBN-fCA7J78nzueG17knPiZXlhGM0N5bO_eKovzIKdAkFlEOJnY-bmJatVdhwTEuL=w1153-h117-rw" alt="cookieFlags secure 設定後" title="加密的階段作業 (SSL) Cookie 中遺漏安全屬性"><figcaption>cookieFlags secure 設定後</figcaption></figure>



<p>打完收工～！再次送掃！站上還有其他不錯的 <a href="https://noter.tw/category/share/trick/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">技巧分享</a> 喔～</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/11094/%e5%8a%a0%e5%af%86%e7%9a%84%e9%9a%8e%e6%ae%b5%e4%bd%9c%e6%a5%ad-ssl-cookie-%e4%b8%ad%e9%81%ba%e6%bc%8f%e5%ae%89%e5%85%a8%e5%b1%ac%e6%80%a7/" data-wpel-link="internal">加密的階段作業 (SSL) Cookie 中遺漏安全屬性</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/11094/%e5%8a%a0%e5%af%86%e7%9a%84%e9%9a%8e%e6%ae%b5%e4%bd%9c%e6%a5%ad-ssl-cookie-%e4%b8%ad%e9%81%ba%e6%bc%8f%e5%ae%89%e5%85%a8%e5%b1%ac%e6%80%a7/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>無法開啟 PHP ZipArchive 下載的 zip 檔</title>
		<link>https://noter.tw/10668/%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-php-ziparchive-%e4%b8%8b%e8%bc%89%e7%9a%84-zip-%e6%aa%94/</link>
					<comments>https://noter.tw/10668/%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-php-ziparchive-%e4%b8%8b%e8%bc%89%e7%9a%84-zip-%e6%aa%94/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Fri, 16 Dec 2022 13:19:02 +0000</pubDate>
				<category><![CDATA[網頁後端]]></category>
		<category><![CDATA[網頁前端]]></category>
		<category><![CDATA[技術相關]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ZipArchive]]></category>
		<category><![CDATA[PHP zip]]></category>
		<category><![CDATA[PHP 壓縮檔案]]></category>
		<category><![CDATA[ob_clean()]]></category>
		<category><![CDATA[網頁直接下載檔案]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=10668</guid>

					<description><![CDATA[<p>PHP 內有一個用來操控壓縮檔的 ZipArchive 模組，今天收到問題回報說下載的 zip 檔沒辦法開啟，當下直覺認為是對方操作錯誤，給幾個同事測試之後，有一個同事同樣無法開啟，竟然是因為檔頭多了一些空白造成，這篇記錄此問題的解法。</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/10668/%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-php-ziparchive-%e4%b8%8b%e8%bc%89%e7%9a%84-zip-%e6%aa%94/" data-wpel-link="internal">無法開啟 PHP ZipArchive 下載的 zip 檔</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>PHP 內有一個用來操控壓縮檔的 ZipArchive 模組，今天收到問題回報說下載的 zip 檔沒辦法開啟，當下直覺認為是對方操作錯誤，給幾個同事測試之後，有一個同事同樣無法開啟，竟然是因為檔頭多了一些空白造成，這篇記錄此問題的解法。</p>



<span id="more-10668"></span>



<h2 class="para wp-block-heading">PHP 壓縮檔案</h2>



<p>這邊是留給自己的 ZipArchive 用法的 memo，確定載入此模組後，只要使用以下方法就可以輕鬆把檔案壓縮成 zip</p>



<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 建立 ZipArchive 物件
$zip = new ZipArchive();
// 在指定路徑中建立一個 zip 檔
$res = $zip->open('tmp_callback/123.zip', ZipArchive::CREATE);
// 把 tmp_callback/file1 新增至 zip 內，並改名為 00000001.txt
$zip->addFile('tmp_callback/file1', '00000001.txt');
// 關閉 zip 物件
$zip->close();</pre>



<h2 class="para wp-block-heading">透過網頁下載 zip 檔</h2>



<p>透過以下的語法可以讓瀏覽器直接下載 zip 檔，一方面在之前做權限控管以及避免洩漏檔案真實路徑</p>



<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$path = "/var/www/html/uploads/";
$filename = '03082358.zip';
	
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($path . $filename));
@readfile($file);</pre>



<h2 class="para wp-block-heading">無法開啟壓縮檔的原因</h2>



<p>直到有使用者反應壓縮檔無法開啟，透過 <a href="https://unzip-online.com/en/zip" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">unzip-online.com<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 線上解壓縮也失敗</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img fetchpriority="high" decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEUmC2qlg12i3weQMfhMjRT7Fs36NTNVuXgAmZ14RgARuzc-f3A5P6-R6RVcbFWjuV5zg2buO7UDMNQzmuJq7Yjz6p6uhzi36eDw9VLmBYSYsFfAzZnhLX-h94Sj4Ha-4Vu11rgEEmMHazPjEb01fLom=w940-h463-no?authuser=1" alt="AL9nZEUmC2qlg12i3weQMfhMjRT7Fs36NTNVuXgAmZ14RgARuzc f3A5P6 R6RVcbFWjuV5zg2buO7UDMNQzmuJq7Yjz6p6uhzi36eDw9VLmBYSYsFfAzZnhLX h94Sj4Ha 4Vu11rgEEmMHazPjEb01fLom=w940 h463 no?authuser=1 無法開啟 PHP ZipArchive 下載的 zip 檔" width="470" height="232" title="無法開啟 PHP ZipArchive 下載的 zip 檔"></figure></div>


<p>使用 notepad++ 開啟後才發現 zip 檔開頭有三行空白，可是有些電腦可以正常開啟有些電腦卻不行</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVkfKvVTFLvsECju82DL81lA0cgTr9UbDAzMFQKa0EYoBTd7BkCwQVNsgf7pAeJbFT5wvfG7ovzLSIsHevoDTCTe6caIDyIJmvsoi6cgQzor9JIL0mvkwUAsczIPFE7Eq6i99yno-QHzmq8WTTKaliH=w208-h119-no?authuser=1" alt="AL9nZEVkfKvVTFLvsECju82DL81lA0cgTr9UbDAzMFQKa0EYoBTd7BkCwQVNsgf7pAeJbFT5wvfG7ovzLSIsHevoDTCTe6caIDyIJmvsoi6cgQzor9JIL0mvkwUAsczIPFE7Eq6i99yno QHzmq8WTTKaliH=w208 h119 no?authuser=1 無法開啟 PHP ZipArchive 下載的 zip 檔" width="156" height="89" title="無法開啟 PHP ZipArchive 下載的 zip 檔"></figure></div>


<p>只要把那三行空白刪除，就可以正常解壓縮了 &#8230;</p>



<h2 class="para wp-block-heading">無法開啟壓縮檔的解決方法</h2>



<p>一開始以為是 ZipArchive 模組的問題，做了各種嘗試後發現，產出來的 zip 是正常的，並沒有上面那三行空白，那問題就是出在下載這段了。</p>



<p>試了很多方法發現只要有<code>&lt;?php include_once "xxx.php" ?&gt;</code>就會有三行空白的狀況，一開始笨笨的試著把所有用到的 include 都拔進來放，後來覺得這樣實在是又累又蠢，最後終於找到簡單解法 &#8230; 只要在<code>readfile</code>之前呼叫<code>ob_clean();</code>就可以避免這個問題 &#8230; </p>



<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 以上省略
ob_clean();
@readfile($file);</pre>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/10668/%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-php-ziparchive-%e4%b8%8b%e8%bc%89%e7%9a%84-zip-%e6%aa%94/" data-wpel-link="internal">無法開啟 PHP ZipArchive 下載的 zip 檔</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/10668/%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-php-ziparchive-%e4%b8%8b%e8%bc%89%e7%9a%84-zip-%e6%aa%94/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CSS 段落文字縮排、凸排 (text-indent)</title>
		<link>https://noter.tw/10241/css-text-indent/</link>
					<comments>https://noter.tw/10241/css-text-indent/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Sat, 20 Aug 2022 06:10:53 +0000</pubDate>
				<category><![CDATA[網頁前端]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=10241</guid>

					<description><![CDATA[<p>小蛙在工作上常會遇到內容太長需要縮排的狀況，或有些文章的排版也是會用到首行縮排，這篇文章把 CSS text-indent 文字縮排 的簡單使用記下來。</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/10241/css-text-indent/" data-wpel-link="internal">CSS 段落文字縮排、凸排 (text-indent)</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>小蛙在工作上常會遇到內容太長需要縮排的狀況，或有些文章的排版也是會用到首行縮排，這篇文章把 CSS <code>text-indent</code> 文字縮排 的簡單使用記下來。</p>



<span id="more-10241"></span>



<h2 class="para wp-block-heading">text-indent 文字縮排</h2>



<p>小蛙無設定縮排的文字（由左至右、上至下排列），樣式如下</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEWJqlRi9kexGyXULP6B-OmieoNSreQP4c2m6bIYAEiYyi2FHdL90rGaG_4jB62Yzf3geTumER2c_ciUb3Z-zJrMscbAropH2clfqa54WJxX27CFsIIZ6rUPJ9FsFamzb2s1z7gvmdT4mTwR4ECKBXs-=w1271-h248-no?authuser=2" alt="網頁文字無縮排" width="636" height="124" title="CSS 段落文字縮排、凸排 (text-indent)"></figure></div>


<p>報章雜誌上常可見首行縮排兩個字的排版</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVnmVNwaCTW2TXYTVBS6ySozLentrVMybSKLevb78U5D-HJH-hrqc9jygBmiYJNOiv7Ovd9BOmwR6lRgBCZqX0UPQ4Iq1apE34VLvyA8JA2RpIZjRry4asKfA2eHDSHZmLimKAg3ZBiZboz___kKv1n=w1268-h265-no?authuser=2" alt="text-indent: 2em; 首頁縮排 2 字" width="634" height="133" title="CSS 段落文字縮排、凸排 (text-indent)"></figure></div>


<p>使用 <code>text-indent: 2em;</code> 即可輕鬆達成首行縮排兩個字，也可以透過 px 或其他單位進行調整</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;style>
.indent-2{
  text-indent: 2em;
}
&lt;/style>

&lt;h3>首行縮排二字&lt;/h3>
&lt;div class="indent-2">現在雲端儲存服務眾星雲集，例如：Google 雲端硬碟、Apple iCloud、Microsoft OneDrive、Dropbox、Box … 等，在共同編輯及使用的情況下，版本管理就非常重要了，這篇文章把 Google 雲端硬碟檔案版本管理的方法記下來。&lt;/div></pre>



<h2 class="para wp-block-heading">text-indent 還能做什麼</h2>



<p>小蛙一開始遇到 <code>text-indent</code> 是因為要做出下面這種排版</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVzD40zBbP7kymBWfdKPUZoz1gDhqPldsATaVdUGglEASJGIAIbhzdFvwi3kG9VkFGxMprRSb-e2Y3fhnp8dLl71-XDhjyIlayYbhQSieeJwL01dKMCgQ5oGlG-AVVBbTZ5T-jyDL1suGclFIoqc927=w1315-h168-no?authuser=2" alt="text-indent: 5em; margin-left: 5em; 首頁凸排 5 字，左邊邊界右推 5 字" width="658" height="84" title="CSS 段落文字縮排、凸排 (text-indent)"></figure></div>


<p>這個例子有 5 個字（包含一個全形：），我們先把 <code>2em</code> 改成 <code>5em</code>，剛剛我們設定 <code>text-indent: 2em;</code> 是首行往右邊縮 2 個字，那這個需求的理解是不是照理推改成 <code>text-indent: -5em;</code> 就可以變成往左邊的了？</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEUbQv2XuyVtLaqbQSaud7l6-RvR_Tw1hVysy5t8xTatNEz8mh2i5WAnE4_EsU7mnywtwaoqId__tYfnwhI2JkJeTxafXBZSUDAu-fk4v2ken9aooXniKNrN9R8u06aZUDcCBSzr3fzKKsn80JeahqWz=w1303-h155-no?authuser=2" alt="text-indent: 5em; 首頁凸排 5 字" width="652" height="78" title="CSS 段落文字縮排、凸排 (text-indent)"></figure></div>


<p>結果是 &#8230; 最左邊的字超出畫面，嗯，既然提到邊界，css 有一個左邊邊界的屬性 margin-left，加入 <code>margin-left: 5em;</code> 看看能不能把凸出去的內容再擠回來</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVzD40zBbP7kymBWfdKPUZoz1gDhqPldsATaVdUGglEASJGIAIbhzdFvwi3kG9VkFGxMprRSb-e2Y3fhnp8dLl71-XDhjyIlayYbhQSieeJwL01dKMCgQ5oGlG-AVVBbTZ5T-jyDL1suGclFIoqc927=w1315-h168-no?authuser=2" alt="text-indent: 5em; margin-left: 5em; 首頁凸排 5 字，左邊邊界右推 5 字" width="658" height="84" title="CSS 段落文字縮排、凸排 (text-indent)"></figure></div>


<p>效果看起來的確是我們要的了，統整使用到的 css，只要把 <code>text-indent</code> 擠出去的位移，再用 <code>margin-left</code> 推回來，不管後面幾行要縮排多少，都可以自己調整囉！</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;style>
.indent-title-5{
  text-indent: -5em;
  margin-left: 5em;
}
&lt;/style>

&lt;div class="indent-title-5">文章摘要：現在雲端儲存服務眾星雲集，例如：Google 雲端硬碟、Apple iCloud、Microsoft OneDrive、Dropbox、Box … 等，在共同編輯及使用的情況下，版本管理就非常重要了，這篇文章把 Google 雲端硬碟檔案版本管理的方法記下來。&lt;/div></pre>



<p><strong>CSS 相關文章：</strong></p>



<ol class="my-li bg-darkblue wp-block-list"><li><a href="https://noter.tw/10241/css-text-indent/" data-wpel-link="internal">CSS 段落文字縮排、凸排 (text-indent)</a></li></ol>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/10241/css-text-indent/" data-wpel-link="internal">CSS 段落文字縮排、凸排 (text-indent)</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/10241/css-text-indent/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Client 操作 Google APIs (1) 開啟與測試 API</title>
		<link>https://noter.tw/8940/google-apis-1-api-explorer/</link>
					<comments>https://noter.tw/8940/google-apis-1-api-explorer/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Fri, 16 Jul 2021 06:07:47 +0000</pubDate>
				<category><![CDATA[其他]]></category>
		<category><![CDATA[技巧分享]]></category>
		<category><![CDATA[技術相關]]></category>
		<category><![CDATA[程式開發]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[記下來]]></category>
		<category><![CDATA[Google APIs]]></category>
		<category><![CDATA[Google 服務]]></category>
		<category><![CDATA[API Console]]></category>
		<category><![CDATA[Google Search Console API]]></category>
		<category><![CDATA[Try this API]]></category>
		<category><![CDATA[教學]]></category>
		<category><![CDATA[Google Cloud Platform]]></category>
		<category><![CDATA[Enable Google API]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=8940</guid>

					<description><![CDATA[<p>應該很多人不知不覺中身邊就充滿了 Google 服務吧！舉凡是 Gmail、Google Maps、Google 雲端硬碟、Google 表單、Google 試算表、Google 文件 &#8230;&#46;&#46;&#46;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8940/google-apis-1-api-explorer/" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>應該很多人不知不覺中身邊就充滿了 <a href="https://www.google.com.tw" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">Google<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 服務吧！舉凡是 Gmail、Google Maps、Google 雲端硬碟、Google 表單、Google 試算表、Google 文件 &#8230; 等，這些服務除了透過對應的軟體或網頁使用外，Google 其實也提供了很多 API 服務，通稱 Google APIs ，讓使用者可以透過這些 API 來打造自己的 Google 服務。</p>



<span id="more-8940"></span>



<p>可能有很多人不懂 Google 提供的 APIs 能做什麼，以及有哪些 Google APIs，只要到  <a href="https://console.cloud.google.com/apis/library" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">Google Cloud Platform<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 就可以看到 Google 提供的所有 API，所有你想得到的 Google 服務，幾乎都有對應開放的 API，差別在於每個 API 使用的次數跟頻率有所限制，還有另一個是 Google 開放的 API 裡面「並不會」接露所有功能。</p>



<p>舉個 Google Maps 的例子，使用 Google Maps 時除了地圖、店家資訊及使用者評論等等，中間還有一個熱門時段，讓使用者可以清楚知道哪個時段會比較多人，就可以避開熱門時段，挑個比較少人的時間去，好友當時就是因為 Google Maps API 沒有提供這個功能，因此小蛙才幫忙用爬蟲的方式解決這個問題。</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWL_oDuen_qBzMNbYdZK5dhO7gPNmPDQkcPM0YOc_46CTNvh5cV6oVjYGXRHD7ekPEe1Lw5OmF94ZQQqDYMvSJxaujLwDnv92xvapzUX-BPmA4-8Louw_SVq-JArilQ06Rd6kKTpTX_xAFOxr4WdrajSQ=w407-h208-no?authuser=3" alt="Google Maps 熱門時段" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>連上 Google Cloud Platform 可以看到很多各式各樣你想得到或想不到的服務</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXUsdR4ePOskw_zEHJYvimSQypoWYO6SsQTD6GCfBn6TOnBp50Mu9kpk1sQsZjWsoek5gK97lV6STAFv6rbfXUxj0MX5odaaPe7FbgmLnK2DwaRwRa_FtH9SCpU4vzF0ngk26UUXoUV6b_iO_-QKmSmlA=w1236-h850-no?authuser=3" alt="Google Cloud Platform APIs" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<h2 class="para wp-block-heading">開啟 Google APIs</h2>



<p>小蛙這系列都會用 Google Search Console 來當作範例，其他 API 原理基本上都差不多，只是呼叫的方式跟屬性設定有所不同，開始吧！</p>



<p>首先連到 <a href="https://console.cloud.google.com/apis/library" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">Google Cloud Platform<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 從上方(1)選取一個要使用 API 的專案(2)，如果沒有專案的話可以建一個新的(3)</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWFT6CzR0i2Y5RvptdJooiHeuWYJLa7EbiOyChF7LY63w0QzMae8BYIKyPwP5Tcxn1ILM4PUPjALTw0CZuZaQnTGKF8jxIdyKugcy5svmcP6WmWXXyBjBGgOaBqAgPysbu_djST_i4EzwLzx6wdwsbdDg=w762-h377-no?authuser=3" alt="Google Cloud Platform Select a project" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>搜尋要開啟的 API (1)，點進去 (2)</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLW4Vne9XTUwgclQcPfh-XHpx00_pbkYywARPO_gNv-EVd2KrabcLVd0RdBz7rJjHp3oCkqgdNWZFvs352bKRPaiP0Oy4flXRGspXFdyalenpxAUT5VfDVvkAiXvtZ7a8PxYwwKL4FBkFhg_kb1TDqh3Hg=w919-h278-no?authuser=3" alt="Google Cloud Platform Keyword Search" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>點上面的 Enable 就可以開啟該 API 了，下面有一些 API 說明可以看看</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWi6McQq0WSsUZC7MEdkczS6MDRqTsVFeOBaGMVp-5VmydFdyy5-gQ_0KE5xp8LhKKVRuojmN1nL2OrbVIcPdKOagGyeiTqhTc4wkFlVE512UfAvFPrbTLYFGXu8P0r9Zy11TNJ8CvTyqNw9X4Q56Y_qg=w623-h327-no?authuser=3" alt="Google Cloud Platform Enable Google Search Console API" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>API 成功開啟後，左邊的 Credentials 可以設定 API 認證的部份，不過這裡小蛙到第三篇文章的時候才會提到</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXOt8yYkjQHUScF-vTgRlnDwHy29kzvsUPc67IWmku0F-PJw8x6DenuKslF_-aXSdWuyyIMA7hRdHMby60lryZki-FUsn8weuVBjpE4cfOfPFjOuVL2rCIU7bFpVcAD18TLZQxjZOKeWx_wVr6ls4OYMg=w261-h272-no?authuser=3" alt="Google Cloud Platform Side Menu" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<h2 class="para wp-block-heading">測試 API 說明</h2>



<p>回到剛剛啟動 Google Search Console API 的頁面，這次點選右邊 TRY THIS API</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXx468noxoeQwCFdKEpcfnNexiK7agGdBvKk5AcjoYvG3ZBvCBAriIh_E2yLQ3VwFrjfXEX7Zct9erixYXxQaFRUk0C9JQvy1uRNIAlsexI8SuNvrVBkfOk1OxuYcPcmP7kEJhX-NZwzqR0d2bMLNgZXQ=w627-h314-no?authuser=3" alt="Google Cloud Platform TRY THIS API" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>開出來的頁面是 Search Console URL Testing Tools API，不過小蛙這邊要用的不是這個 API，而是能取得 Google Search Console 裡面資料的 API，因此點選左上角 (1)</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXV055sMST_O7FgfVpcCjl9ww6oiyueSOOn2mHoM0f9-hN6JHFnaEmpEaBvzWHJYG5oKIp2BnhCmtHUB7MCat4URiREEfSY563bVnknM-A6KtBl_Iox4O4yVVhq6wQ5W9-_hTs-ahuujZTCNnfuDHauCA=w958-h468-no?authuser=3" alt="Google Search Console APIs &gt; URL Testing Tools API (Beta)" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>回到 Google Search Console APIs 首頁後進入 Search Console API</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXlQmNV8QP7dfkg7b7EaLqAbcQCEb_V_svlc2FIWesq5oQ5tPrQ-qT57-BJgddhP59FtcFshozITznDW5GnJc4iHE3KIG7NgUkaVvN1HmqFwhBeoVDm01PBNcgiZ1s6MvciXfjjUDME7t704kMSWT7OkA=w931-h573-no?authuser=3" alt="Google Search Console APIs" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>找到下面的 Try it in your browser</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLVMbYziFhx3dxR9F28UWx3wkZcDtQVnU14rkAAvySVGeMi2SYWSORxoKQMnfn3Tk1FTBeikKbnx3EptXdL3aV0O63oUIicb5md0O0_yST37E2gSzS99p_RchRGxak1URCTXXwv6LJCwx7UfYqIvkHN97A=w622-h449-no?authuser=3" alt="Try out the Google Search Console API" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>接著隨便點一個 api 功能</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXDc68UJJAMl16ASnRzF8bAdvriWeX0bDNvdn08Ix_acpps0V_WZqNOWLuELUoSTT142lzEx4xMyRsOzfcFWvJb3mcLFJ3s-CwPcusGSI_L_-1HsfosX2Fuc8HUJmKEcG_S3kzHaGxsdc3pXniJ4YhDdg=w942-h602-no?authuser=3" alt="Google Search Console APIs &gt; query" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>右邊會開啟一個側欄，這邊就是 Google API 的測試工具了，點選右上角的方框 (1) 可將螢幕放大顯示更多資訊</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXUAKERyhTBWcoSSDmfbzvN4NI5IyDm8Jmr4k9d7Xt6UKiNxcpH8DIHCLa2GTvUrI7ysY7btQVxiMNryzY9r0hQY3vgRCMn9AA1gzB484MlYDFPwIbo0LEbtSjLoM7a_L6U4b7G4eMaYcXtN0375WA27g=w399-h811-no?authuser=3" alt="Google Search Console API &gt; Try this API 1" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>Google Search Console 原本就是以「網站」為目標，因此必填欄位是在 Search Console 裡面的網址 (1)，Request body 的部份填入查詢的參數 (2)，輸入完成後點選 EXECUTE 執行 (3)，下圖 (4) 的地方是真正發送出的 request</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXfwzWrEja8fKmnA0PPyyLqnv0hqZIswmWbV97eqVRpX7fAk4vHt1JQBIu_i_3jIown7IT0Jr7P56sYOVnAk4yY7oEbq8OA1nFGdSc419FBdiu8gXA8DwggmMlcMlFS9nMJ28SOj5zH_OPzAUp-FllncQ=w1150-h697-no?authuser=3" alt="Google Search Console API &gt; Try this API 2" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<h2 class="para wp-block-heading">使用測試 API</h2>



<p>小蛙用記下來 https://noter.tw/ 測試一下 (1)，填入一些必要的參數 (2) 取得 2021-05-01 ~ 2021-07-01 區間的資料，只要回傳前 10 筆就好，點選 (3) 的驚嘆號會顯示有哪些屬性可以設定，確定沒問題後 EXECUTE 送出吧！注意：這邊的 siteUrl 一定要是自己的 Search Console 原本就有的資源才可以，也就是說不能去查別人的，不然就太恐怖了！</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXxhOOd5fA43lv7nABBi3L9YJ0TMd1_RH-6LG6nHEuiVKZvXEIYlEAro-ZsxEmcdprhPEv1131GnKUla5crI-SUTM1bZhSiEvoCdKkZODHyzCo6QI9CkQmxU_jrnVQOP82lhdS9e4XUDIltV50aDK4sNQ=w456-h734-no?authuser=3" alt="Google Search Console API &gt; Try this API 3" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>點了 EXECUTE 後瀏覽器跳出登入畫面，有些可以透過 API Key 來使用，有些則是必須透過 OAuth 2.0 才可以使用，選擇一個帳戶來使用吧～</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLViqpex5Vq1tiC-5zHF84HkfewOulCuvoms8WvDbmNC4WNlWfxr_sW0PPedt4Mu8xhel_4oidPPm9R2hXS72UjvzhglZyQkLW6Bxwets6MXnrEZDseuzGfqeJGoHbzMTB8bGlqWvCzMQrJUotyxb5P4Fg=w601-h660-no?authuser=3" alt="SSO Login" width="451" height="495" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>執行成功後，右上角會產生實際送出的 API</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWo34bRGyi15XFeIn6p1_egFpEDrUG3ikfHlhF965Ueu3yLUlrIWZ9b-wIeHxmnBBs8D_BAM0csiOvvRG7SNF_3h5bUP7ASDDHrayL9UipS_4SBTUCTqxLyOg9gVWWrxdSTnlZeLmaZQ9vv7OZBoQvhHg=w669-h144-no?authuser=3" alt="Google Search Console API &gt; Try this API curl" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>右下角則是顯示回傳結果，可以看見在這區間被點擊的次數為 46,812 次，搜尋曝光有 838,324 次 &#8230; 等</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWxy3xjsu-hb7cDZQIT8Rl9UDH1sK4KH2pHha0SQZJ2j-Gx0hoH17yjA9IcrgZ781Ni2TmIRYxVhQB6vCXA1o_hTPLtbz0NuoygsNvgDDE9UMYdddV9inyb_23hknSkRHRdPyjGaajrWZwL0Xi6lsXd-A=w305-h288-no?authuser=3" alt="Google Search Console API &gt; Try this API results 1" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p class="withcode">這看起來好像只能取到很基本的資料？不～來把參數做個調整，加上 <code>aggregationType</code> 及 <code>dimensions</code> 的設定回傳各個頁面的資訊～</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXhpOjk2WH7VGQn7KoPm1_AHInljAqKgu7x02VM2i2Ub_LHWQI_IP_LKNDX7kaq5xR-txJd7KZCqXLkWgR-fCfdwbdgmI_m1A4qbNn5Ib3YaglDKf9ebSdK0kHabszrHEXwQUtwp3UEf0DE2fEqbJr1CQ=w425-h332-no?authuser=3" alt="Google Search Console API &gt; Request body" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>查詢結果會根據區間內的點擊數排列，看看剛剛我們加的 dimensions 都在 keys 陣列裡面，包含搜尋字串、頁面、來源國家及日期</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWDbjBcT2NFEMd4IepyzdVoo5dVJG04OrpMRY4NLTX9sAsLa6Gb1P4jEuTDIaN_F-7oUr0e08VRkmnCgbazYvQIOKJwnu3vfhJHV0TxZR72RbFvDXm54KEJVP0ZDCZKCzhDL324dX-m4w7sf23b5dtDaw=w437-h322-no?authuser=3" alt="Google Search Console API &gt; Try this API results 2" title="PHP Client 操作 Google APIs (1) 開啟與測試 API"></figure></div>



<p>Google Search Console API 啟用跟測試就到這邊，下一篇將繼續介紹環境設定部份。</p>



<p id="block-c58a739f-b4e1-42ae-8530-5e20da347253"><strong>Google 系列文章：</strong></p>



<ul class="wp-block-list" id="block-2b1f3c8f-95c7-4eff-8105-53dea9d75e5e"><li><a href="https://noter.tw/8940/google-apis-1-api-explorer/" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a></li><li><a href="https://noter.tw/8944/google-apis-2-google-api-client-for-php/" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a></li><li><a href="https://noter.tw/8942/google-apis-3-service-account-credentials/" data-wpel-link="internal">PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</a></li><li><a href="https://noter.tw/9035/google-apis-4-google-search-console-apis/" data-wpel-link="internal">PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs</a></li><li><a href="https://noter.tw/8604/google-formranger/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 表單應用：設定選項次數上限</a></li><li><a href="https://noter.tw/9049/copy-a-google-form/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">複製 Google 表單 (Copy A Google Form)</a></li><li><a href="https://noter.tw/8358/google-adsense-tax-w8ben/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google Adsense: 向 Google 提交稅務資料 | 填寫 W-8BEN 稅務表單</a></li><li><a href="https://noter.tw/4946/google-%e9%9b%b2%e7%ab%af%e5%a4%96%e5%b8%b6%e6%9c%8d%e5%8b%99-takeout%ef%bc%9a%e8%bc%95%e9%ac%86%e6%89%93%e5%8c%85%e9%9b%b2%e7%ab%af%e7%a1%ac%e7%a2%9f%e3%80%81%e7%9b%b8%e7%b0%bf-%e7%ad%89/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 雲端外帶服務 (Takeout)：輕鬆打包雲端硬碟、相簿 … 等資料</a></li><li><a href="https://noter.tw/4394/google-%e7%9b%b8%e7%b0%bf%e5%8f%96%e5%be%97%e7%9c%9f%e5%af%a6%e5%9c%96%e7%89%87%e4%bd%8d%e7%bd%ae%ef%bc%88%e6%8f%92%e5%85%a5%e5%9c%96%e7%89%87%e5%88%b0%e6%96%87%e7%ab%a0%ef%bc%89/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 相簿取得真實圖片位置（插入圖片到文章）</a></li><li><a href="https://noter.tw/4267/gdirve-%e8%ae%93%e4%bd%a0%e5%9c%a8-linux-%e6%96%87%e5%ad%97%e4%bb%8b%e9%9d%a2%e4%b9%9f%e8%83%bd%e5%a5%bd%e5%a5%bd%e4%bd%bf%e7%94%a8-google-drive/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">gdirve 讓你在 Linux 文字介面也能好好使用 Google Drive</a></li><li><a href="https://noter.tw/4302/microsoft-office-word-%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-google-drive-%e8%b6%85%e9%80%a3%e7%b5%90/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Microsoft Office Word 無法開啟 Google Drive 超連結</a></li></ul>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8940/google-apis-1-api-explorer/" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/8940/google-apis-1-api-explorer/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</title>
		<link>https://noter.tw/8944/google-apis-2-google-api-client-for-php/</link>
					<comments>https://noter.tw/8944/google-apis-2-google-api-client-for-php/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Tue, 13 Jul 2021 07:40:57 +0000</pubDate>
				<category><![CDATA[程式開發]]></category>
		<category><![CDATA[技巧分享]]></category>
		<category><![CDATA[技術相關]]></category>
		<category><![CDATA[其他]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[記下來]]></category>
		<category><![CDATA[Google APIs]]></category>
		<category><![CDATA[Google Search Console API]]></category>
		<category><![CDATA[Google Cloud Platform]]></category>
		<category><![CDATA[Google Client]]></category>
		<category><![CDATA[教學]]></category>
		<category><![CDATA[Google APIs Client Library for PHP]]></category>
		<category><![CDATA[composer]]></category>
		<category><![CDATA[安裝]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=8944</guid>

					<description><![CDATA[<p>Google APIs 開啟的方法可參考上一篇文章：PHP Client 操作 Google APIs (1) 開啟與測試 API，這篇文章繼續記錄使用 Google API Client 來操作 G&#46;&#46;&#46;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8944/google-apis-2-google-api-client-for-php/" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>Google APIs 開啟的方法可參考上一篇文章：<a href="https://noter.tw/8940/google-apis-1/" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a>，這篇文章繼續記錄使用 Google API Client 來操作 Google Search Console APIs。</p>



<span id="more-8944"></span>



<p>上一篇文章我們示範了怎麼使用 Google 提供的 API 測試工具，透過這些工具產生相關 REST 指令，同時 Google 也提供 Java 及 Python 版本的使用範例，只要點進 <a href="https://developers.google.com/webmaster-tools/search-console-api-original/v3/quickstart/quickstart-python" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">Google Search Console APIs<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 就可以看到。</p>



<p>但是小蛙今天要使用的不是 Java 及 Python，而是 PHP 版本的 Client (beta)，只要點進 <a href="https://developers.google.com/webmaster-tools/search-console-api-original/v3/libraries#php" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">Install Client Libraries<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 拉到下面可以看到各種版本的 Client，將頁籤切換到 PHP，或是其它自己擅長的語言～點選 Get the latest&nbsp;<a href="https://github.com/google/google-api-php-client" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">Google Search Console API client library for PHP (beta)<span class="wpel-icon wpel-image wpel-icon-6"></span></a></p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLX9CBG9wuYorqrlOuCWgAYOQlWD82BHOBZepvZ2VXxMkut7YR6XT8G_1yJcbLGiPo5QQ7pMEOgs2dPkAVgPNkSQ4SBFdWRI6pPB9nJnwH2beMtZGnff-N_Hg24WyUPHNUVk_YaAX0ia77X13Okfa1xW4w=w877-h158-no?authuser=3" alt="Google API Client PHP " title="PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP"></figure></div>



<p>接著開啟 Github 頁面，PHP 版本需在 5.6.0 以上才可以使用，並且需要透過 composer 安裝，沒有 composer 的話可參考<a href="https://getcomposer.org/doc/00-intro.md" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">這邊<span class="wpel-icon wpel-image wpel-icon-6"></span></a>。安裝好後使用以下指令安裝</p>



<pre class="wp-block-preformatted withcode">composer require google/apiclient:^2.10</pre>



<p>安裝完 PHP Client 後，在要使用 Google APIs 的 PHP 最上面加上</p>



<pre class="wp-block-preformatted withcode">require_once '/path/to/your-project/vendor/autoload.php';</pre>



<p>就可以載入 Google API 的相關工具了，如果沒有或真的不想裝 composer 的話，其實也有提供直接下載的版本，只要到 <a href="https://github.com/googleapis/google-api-php-client/releases" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">這邊<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 去下載適合的版本，解壓縮後同樣在要使用 Google APIs 的 PHP 加入上面 require_once (注意路徑正確性) 就可以使用 Google API 了。</p>



<p>接著建一個 PHP 檔案，新增一個 Google_Client() 物件出來，如果下載或設定正確的話，畫面上應該空白不會噴任何錯誤</p>



<pre class="wp-block-preformatted withcode">&lt;?php
 ini_set('display_errors', '1');
 error_reporting(E_ALL);
 // 換成自己的實際路徑
 require_once '../googleapiclient/vendor/autoload.php';
 $client = new Google_Client();
?&gt;</pre>



<p>如果出現下面這種錯誤，可能是沒安裝正確或是路徑指定錯誤，再檢查看看是哪裡有問題。</p>



<pre class="wp-block-preformatted withcode"><strong>Fatal error</strong>: Uncaught Error: Class 'Google_Client' not found in /var/www/xxx.php:6 Stack trace: #0 {main} thrown in&nbsp;<strong>/var/www/test/xxx.php</strong>&nbsp;on line&nbsp;<strong>6</strong></pre>



<p>這篇文章比較短，只著重在 PHP 版本的安裝，下一篇文章再繼續介紹怎麼使用 OAuth 登入並查詢 Google Search Console APIs 的相關資料！</p>



<p id="block-c58a739f-b4e1-42ae-8530-5e20da347253"><strong>Google 系列文章：</strong></p>



<ul class="wp-block-list" id="block-2b1f3c8f-95c7-4eff-8105-53dea9d75e5e"><li><a href="https://noter.tw/8940/google-apis-1-api-explorer/" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a></li><li><a href="https://noter.tw/8944/google-apis-2-google-api-client-for-php/" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a></li><li><a href="https://noter.tw/8942/google-apis-3-service-account-credentials/" data-wpel-link="internal">PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</a></li><li><a href="https://noter.tw/9035/google-apis-4-google-search-console-apis/" data-wpel-link="internal">PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs</a></li><li><a href="https://noter.tw/8604/google-formranger/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 表單應用：設定選項次數上限</a></li><li><a href="https://noter.tw/9049/copy-a-google-form/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">複製 Google 表單 (Copy A Google Form)</a></li><li><a href="https://noter.tw/8358/google-adsense-tax-w8ben/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google Adsense: 向 Google 提交稅務資料 | 填寫 W-8BEN 稅務表單</a></li><li><a href="https://noter.tw/4946/google-%e9%9b%b2%e7%ab%af%e5%a4%96%e5%b8%b6%e6%9c%8d%e5%8b%99-takeout%ef%bc%9a%e8%bc%95%e9%ac%86%e6%89%93%e5%8c%85%e9%9b%b2%e7%ab%af%e7%a1%ac%e7%a2%9f%e3%80%81%e7%9b%b8%e7%b0%bf-%e7%ad%89/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 雲端外帶服務 (Takeout)：輕鬆打包雲端硬碟、相簿 … 等資料</a></li><li><a href="https://noter.tw/4394/google-%e7%9b%b8%e7%b0%bf%e5%8f%96%e5%be%97%e7%9c%9f%e5%af%a6%e5%9c%96%e7%89%87%e4%bd%8d%e7%bd%ae%ef%bc%88%e6%8f%92%e5%85%a5%e5%9c%96%e7%89%87%e5%88%b0%e6%96%87%e7%ab%a0%ef%bc%89/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 相簿取得真實圖片位置（插入圖片到文章）</a></li><li><a href="https://noter.tw/4267/gdirve-%e8%ae%93%e4%bd%a0%e5%9c%a8-linux-%e6%96%87%e5%ad%97%e4%bb%8b%e9%9d%a2%e4%b9%9f%e8%83%bd%e5%a5%bd%e5%a5%bd%e4%bd%bf%e7%94%a8-google-drive/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">gdirve 讓你在 Linux 文字介面也能好好使用 Google Drive</a></li><li><a href="https://noter.tw/4302/microsoft-office-word-%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-google-drive-%e8%b6%85%e9%80%a3%e7%b5%90/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Microsoft Office Word 無法開啟 Google Drive 超連結</a></li></ul>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8944/google-apis-2-google-api-client-for-php/" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/8944/google-apis-2-google-api-client-for-php/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</title>
		<link>https://noter.tw/8942/google-apis-3-service-account-credentials/</link>
					<comments>https://noter.tw/8942/google-apis-3-service-account-credentials/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Tue, 13 Jul 2021 07:37:41 +0000</pubDate>
				<category><![CDATA[技術相關]]></category>
		<category><![CDATA[網頁後端]]></category>
		<category><![CDATA[程式開發]]></category>
		<category><![CDATA[API key]]></category>
		<category><![CDATA[Service account]]></category>
		<category><![CDATA[教學]]></category>
		<category><![CDATA[Google 認證]]></category>
		<category><![CDATA[建立 Service Account]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[APIs & Services]]></category>
		<category><![CDATA[記下來]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Google APIs]]></category>
		<category><![CDATA[Google APIs Credentials]]></category>
		<category><![CDATA[Credentials 介紹]]></category>
		<category><![CDATA[說明]]></category>
		<category><![CDATA[OAuth client ID]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=8942</guid>

					<description><![CDATA[<p>總算沒偷懶已經來到第三篇了，還不清楚怎麼回事的朋友可以看前面兩篇 PHP Client 操作 Google APIs (1) 開啟與測試 API 及 PHP Client 操作 Google APIs&#46;&#46;&#46;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8942/google-apis-3-service-account-credentials/" data-wpel-link="internal">PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>總算沒偷懶已經來到第三篇了，還不清楚怎麼回事的朋友可以看前面兩篇 <a style="font-size: revert;" rel="noreferrer noopener" href="https://noter.tw/8940/google-apis-1/" target="_blank" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a> 及 <a style="font-size: revert;" rel="noreferrer noopener" href="https://noter.tw/8944/google-apis-2/" target="_blank" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a>，這篇記錄如何建立 Google APIs Credentials 進行認證，並說明各認證方式的差別。</p>



<span id="more-8942"></span>



<p class="withcode">這系列小蛙以 Google Search Console API 當作例子，其他的 Google APIs 大致上使用的流程類似，差別可能在於用到的物件不同 (Google 分析就要改成使用 <code>Google_Service_Analytics</code> 物件)、要設定的 Scope 不同 (<code>Google_Service_Analytics::ANALYTICS_READONLY</code>，這些在 Google APIs 文件上都會有)，可以使用的認證方式不同 (有的限定只能 OAuth) 等等。</p>



<h2 class="para wp-block-heading">認證方式 (Google APIs Credentials)</h2>



<p class="withcode pink-solid">Google 在 <a href="https://console.cloud.google.com/" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">API Console<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 裡提供了 3 種認證方式，分別是：<code>API Key</code>、<code>OAuth client ID</code> 及 <code>Service account</code>。如果不清楚該使用哪一種的話，可以點最下面的 Help me choose。</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWsr_HcF9xUg_K2uLOc0AIx60-NcmcdxJCRRuW8QHXPQTEheM7Ct1EEhDMCbhmmiAAbaMAOnIGqfSOea6Y2wlJt7avO-8tLs6B3er1BJlO5z5IcZhlknQeIAZXTnmUegsn7zlimWzmNVJnDLT8KW0yHwA=w448-h282-no?authuser=3" alt="Google APIs Credentials" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<h3 class="para wp-block-heading">API Key</h3>



<p>API Key 是最簡單也最單純的，如果使用到 Google APIs 的功能是支援 API Key 存取的話，這是一個非常簡單的好選項，只要設定完成後，會得到一組 API Key，接著設定在 API Client 內就可以使用了。</p>



<h3 class="para wp-block-heading">OAuth client ID</h3>



<p>透過自己的 Gmail 帳號登入後並授權後才可以使用，用來存取自己帳號相關的資料，例如：行事曆、Search Console 資料、Google Analytics 資料 &#8230; 等。第一篇測試 APIs Explorer 時跳出的視窗就是使用 OAuth 2.0 登入的畫面。</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLViqpex5Vq1tiC-5zHF84HkfewOulCuvoms8WvDbmNC4WNlWfxr_sW0PPedt4Mu8xhel_4oidPPm9R2hXS72UjvzhglZyQkLW6Bxwets6MXnrEZDseuzGfqeJGoHbzMTB8bGlqWvCzMQrJUotyxb5P4Fg=w601-h660-no?authuser=3" alt="OAuth 2.0 Login Screen" width="451" height="495" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<h3 class="para wp-block-heading">Service account</h3>



<p>主要在 Server 端的應用，有些 APIs 需要透過 OAuth 取得帳號相關資料，但是如果要讓它自動執行或是做成機器人 (robot) 的話，沒辦法每次跳出視窗讓你去按登入授權，因此這種狀況選擇 Service account 就對了。</p>



<h2 class="para wp-block-heading">建立 Service Account </h2>



<p>繼續到 GCP 左側選單，點擊 APIs &amp; Services -&gt; Credentials</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLVwRX1gGcTjFQpwDX1KvCbZGYS1ZuoR21ueYlqk2HL6NoqldMtZEF6EccUjnWQq4S7gRIQdV2Me8A0AGDIOnTwlUmBvymjHDai5_Wki8njdr-FeL-8iwFtWR5GnhJ-VcO2FW9RzdUBgQbEfkyFi8DrsxQ=w452-h508-no?authuser=3" alt="Google APIs Credentials 1" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>點擊上方 CREATE CREDENTIALS 後選擇 Service account</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWsr_HcF9xUg_K2uLOc0AIx60-NcmcdxJCRRuW8QHXPQTEheM7Ct1EEhDMCbhmmiAAbaMAOnIGqfSOea6Y2wlJt7avO-8tLs6B3er1BJlO5z5IcZhlknQeIAZXTnmUegsn7zlimWzmNVJnDLT8KW0yHwA=w448-h282-no?authuser=3" alt="Google APIs Credentials 2" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>輸入帳號名稱 (Service account name) 後會自動產生 (Service account ID)，輸入描述避免太多 Credentials 的時候可以弄清楚哪個是哪個，輸入完成後點擊 CREATE AND CONTINUE</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWPU0h72ZGCeUnCbvMw5BR9nzGpOVZNZk4hQb8HvNr_ch_quwpujdXI0EigqkXMLDEPe3RsOtocWpxk5UJLiNGcamxDjVoJpIW4NuS_ihuV14IyFoJ81n9KTYh5jCAwWZYANJbEKXzRg69BViOjj2CBbw=w540-h430-no?authuser=3" alt="Google APIs Credentials 3" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>這步驟是選用的，小蛙直接跳過</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLUtquEXwP7kq6E9-rw9Xm0c6q--rr8vhNmyOCDmRNUppTPV6oWsS_swrgUtqSVkRF_hVzvkIQLKvCzf2rct__mlHvYA0uxnbWowWoN6pKsteIPv5KAqhgN_Ti-3ZHqqEbZ04ICSh5AAl1MiymmxdZ61Ng=w523-h363-no?authuser=3" alt="Google APIs Credentials 4" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>這步驟也是選用的，直接跳過</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLX_uiCo_D0ASkeoSQO5zEM3K4_Y5BXmgxxONvPiGeKcT9Vb7GuzWNm8legvtzE6uVTgUt_BR-L6CgKMvIoUpQsqSYXK8QlvMULhqIkCSr1kSn96-Wnx-utTx3Gy6UE7DHvkNmOLfxiTiqphM-DKvrMwGQ=w527-h317-no?authuser=3" alt="Google APIs Credentials 5" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>回到主畫面後可以看到剛剛新增的帳號出現了，點選右邊的鉛筆進入修改畫面</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLU_OhP-_kwBeNqHbmhuo0Dn0uK_yH_ARM1RnSV3j1Ai2wJaCFfwRxWcnGFfdFoYsvyEV3dkxU_Fx3UA57Kt2Kgrhq8qW6YpniaDCFHfdaIMXM38Z8vSbFdwrlkDT2MUPV44j-31c5KHNtKoD4mrz9zR2g=w798-h172-no?authuser=3" alt="Create Credentials 6" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>上方切換到 KEYS 頁籤 (1)，點擊下方 ADD KEY (2)，選擇 Create new key (3)</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWS7pdvaQdNoJdCL4vHP1EBApz1KUFuhKUu9UF02boWTzM7l-NJbcKo0xy_P_DRUpk5tKulBfpqF7c96nfMWNt7gdzmZCsCyAe8WSOfkV2HPD0n8NX6CXSmNneQsZIK8DDlj61Cy0bveB3POdOsWAnbRg=w488-h460-no?authuser=3" alt="Create Credentials 7" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>選擇 Key type，Google 建議使用 JSON 格式 (1) 的就好，點擊右下角的 CREATE (2)</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLV-HbT8k_2SZOSa_lWjr-tFn9n4e6oQEK4R1u_xZw36u-Cm8XxxNrB1o9fN0_jawmUByCaTkOn-rnpemUIXG8xYVoBptRx0sL5Qd_YD03Qrf-TqrFngKejWBxzL7VBnn2O87QXoThjAWmIzFXdIvXHCgw=w530-h328-no?authuser=3" alt="Create Credentials 8" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>接著會開始下載 JSON，這個 JSON 一定要保存好喔，如果遺失了是沒辦法再下載的，只能把原本的刪除後再產生一個，一定要注意。</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLVto-2A2QceFHDryLAVFYIBxDiI8exjglSIhl03n8v7qDynqYwM1q6x0NHsrnVhPaawZ3W7AKdGSdiCchb_RBgVFZZMbQe_GKcfso0Cjz7yTuaFGDJmlrlRARv3u_xKx_yk5u-Sq1ckoKq5UpQ1OlHCkQ=w677-h131-no?authuser=3" alt="Create Credentials 9" title="PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹"></figure></div>



<p>到這邊 Service Account 就建立完成了，下一篇將會說明如何透過 PHP Client 操作 Google Search Console APIs 來取得相關數據。</p>



<p id="block-c58a739f-b4e1-42ae-8530-5e20da347253"><strong>Google 系列文章：</strong></p>



<ul class="wp-block-list" id="block-2b1f3c8f-95c7-4eff-8105-53dea9d75e5e"><li><a href="https://noter.tw/8940/google-apis-1-api-explorer/" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a></li><li><a href="https://noter.tw/8944/google-apis-2-google-api-client-for-php/" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a></li><li><a href="https://noter.tw/8942/google-apis-3-service-account-credentials/" data-wpel-link="internal">PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</a></li><li><a href="https://noter.tw/9035/google-apis-4-google-search-console-apis/" data-wpel-link="internal">PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs</a></li><li><a href="https://noter.tw/8604/google-formranger/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 表單應用：設定選項次數上限</a></li><li><a href="https://noter.tw/9049/copy-a-google-form/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">複製 Google 表單 (Copy A Google Form)</a></li><li><a href="https://noter.tw/8358/google-adsense-tax-w8ben/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google Adsense: 向 Google 提交稅務資料 | 填寫 W-8BEN 稅務表單</a></li><li><a href="https://noter.tw/4946/google-%e9%9b%b2%e7%ab%af%e5%a4%96%e5%b8%b6%e6%9c%8d%e5%8b%99-takeout%ef%bc%9a%e8%bc%95%e9%ac%86%e6%89%93%e5%8c%85%e9%9b%b2%e7%ab%af%e7%a1%ac%e7%a2%9f%e3%80%81%e7%9b%b8%e7%b0%bf-%e7%ad%89/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 雲端外帶服務 (Takeout)：輕鬆打包雲端硬碟、相簿 … 等資料</a></li><li><a href="https://noter.tw/4394/google-%e7%9b%b8%e7%b0%bf%e5%8f%96%e5%be%97%e7%9c%9f%e5%af%a6%e5%9c%96%e7%89%87%e4%bd%8d%e7%bd%ae%ef%bc%88%e6%8f%92%e5%85%a5%e5%9c%96%e7%89%87%e5%88%b0%e6%96%87%e7%ab%a0%ef%bc%89/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 相簿取得真實圖片位置（插入圖片到文章）</a></li><li><a href="https://noter.tw/4267/gdirve-%e8%ae%93%e4%bd%a0%e5%9c%a8-linux-%e6%96%87%e5%ad%97%e4%bb%8b%e9%9d%a2%e4%b9%9f%e8%83%bd%e5%a5%bd%e5%a5%bd%e4%bd%bf%e7%94%a8-google-drive/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">gdirve 讓你在 Linux 文字介面也能好好使用 Google Drive</a></li><li><a href="https://noter.tw/4302/microsoft-office-word-%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-google-drive-%e8%b6%85%e9%80%a3%e7%b5%90/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Microsoft Office Word 無法開啟 Google Drive 超連結</a></li></ul>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8942/google-apis-3-service-account-credentials/" data-wpel-link="internal">PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/8942/google-apis-3-service-account-credentials/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs</title>
		<link>https://noter.tw/9035/google-apis-4-google-search-console-apis/</link>
					<comments>https://noter.tw/9035/google-apis-4-google-search-console-apis/#comments</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Tue, 13 Jul 2021 07:30:16 +0000</pubDate>
				<category><![CDATA[其他]]></category>
		<category><![CDATA[網頁後端]]></category>
		<category><![CDATA[技術相關]]></category>
		<category><![CDATA[分享]]></category>
		<category><![CDATA[記下來]]></category>
		<category><![CDATA[Google APIs]]></category>
		<category><![CDATA[Google Cloud Platform]]></category>
		<category><![CDATA[Goolge Search Console APIs]]></category>
		<category><![CDATA[開發]]></category>
		<category><![CDATA[教學]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=9035</guid>

					<description><![CDATA[<p>這系列的最後一篇，原本以為已經寫完了，今天發現這篇躺在草稿裡 &#8230; 還不清楚怎麼回事的朋友可以看前面幾篇 PHP Client 操作 Google APIs (1) 開啟與測試 API 、 &#46;&#46;&#46;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/9035/google-apis-4-google-search-console-apis/" data-wpel-link="internal">PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>這系列的最後一篇，原本以為已經寫完了，今天發現這篇躺在草稿裡 &#8230; 還不清楚怎麼回事的朋友可以看前面幾篇 <a style="font-size: revert;" rel="noreferrer noopener" href="https://noter.tw/8940/google-apis-1/" target="_blank" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a> 、 <a style="font-size: revert;" rel="noreferrer noopener" href="https://noter.tw/8944/google-apis-2/" target="_blank" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a> 及 <a href="https://noter.tw/8942/google-apis-3-service-account-credentials/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</a>，這篇記錄如何使用 Google Search Console APIs。 </p>



<span id="more-9035"></span>



<p>我們在前幾篇裡面瞭解了 Google APIs、安裝 Client for PHP 以及申請了 Google APIs Credentials 裡的 Service Account 並下載了 JSON (一定要好好保存，遺失了只能刪除再產生一次，沒辦法再次下載喔)，開始使用！</p>



<h2 class="para wp-block-heading">Google APIs 大致流程</h2>



<p>使用 Google APIs 大致上的流程都差不多</p>



<ul class="my-li bg-darkblue wp-block-list"><li>載入 Google APIs Client for PHP (或其他語言版本)</li><li>加入 Service Account</li><li>建立並初始化物件</li><li>設定篩選條件</li><li>取得並解析結果</li></ul>



<p>只要把握這幾個原則，之後若要使用 Google APIs 就不會再霧沙沙了 (但 API 還是要查，每個 API 的 Scope 跟條件設定上會有很大的不同)</p>



<h2 class="para wp-block-heading">載入 Google APIs Client for PHP</h2>



<p>這個在前面  <a style="font-size: revert;" rel="noreferrer noopener" href="https://noter.tw/8944/google-apis-2/" target="_blank" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a>  的時候已經講過了，在程式碼裡面很簡單，開頭放上這行就可以了，為了更方便 debug，可以多設定兩行條件，只要使用上出現錯誤，就會直接在網頁上呈現</p>



<pre class="wp-block-preformatted withcode">// 顯示錯誤
ini_set('display_errors', '1');
// 所有錯誤都顯示
error_reporting(E_ALL);
// 載入 Google Client
require_once '../googleapiclient/vendor/autoload.php';</pre>



<h2 class="para wp-block-heading">加入 Service Account</h2>



<p>小蛙前面提到這次要做的事情是讓程式不經人工自動執行，而 Search Console 能使用的方式只有 OAuth 與 Service Account (忘記差別的這邊請 <a href="https://noter.tw/8942/google-apis-3-service-account-credentials/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</a>)，要不需人工介入只有 Service Account 了，而要讓 Service Account 存取 Search Console 資料的話，就必須將該帳號加入 Search Console 裡面囉！</p>



<p>首先進入 Search Console 要使用的資源，左邊選單下面找到「設定」</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXzIBu7ISSOmPScFy7qVA96IJdZN6hn_2sC8u8CAQo6ilW2TSsoE7VnZcq8sj1gRGhJqQ6wc_wjaC6MNwP3aY-rbsd89loU6C4h5Abb520EUYBS_0TD5NMys3eYYxweQuQwqvchEjMtFMJr9_65SCb-QA=w326-h243-no?authuser=2" alt="AM JKLXzIBu7ISSOmPScFy7qVA96IJdZN6hn 2sC8u8CAQo6ilW2TSsoE7VnZcq8sj1gRGhJqQ6wc wjaC6MNwP3aY rbsd89loU6C4h5Abb520EUYBS 0TD5NMys3eYYxweQuQwqvchEjMtFMJr9 65SCb QA=w326 h243 no?authuser=2 PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs" title="PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs"></figure></div>


<p>點擊「使用者與權限」</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXZUyX8xf-BHak-20EBkiO2TFTuTXL_INR2QUGFYk7pHLNsIPtwfApwqJ1xcShRkvqj2low9Iuh4ksHN79DSkUqUjylMiyEd1MMK0DO5P2U1X-ZfMa9lKLLv8EyrMp1-j3pXSYrSGxt8gh5MKlyxeN7ww=w817-h394-no?authuser=2" alt="AM JKLXZUyX8xf BHak 20EBkiO2TFTuTXL INR2QUGFYk7pHLNsIPtwfApwqJ1xcShRkvqj2low9Iuh4ksHN79DSkUqUjylMiyEd1MMK0DO5P2U1X ZfMa9lKLLv8EyrMp1 j3pXSYrSGxt8gh5MKlyxeN7ww=w817 h394 no?authuser=2 PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs" width="613" height="296" title="PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs"></figure></div>


<p>點擊「新增使用者」並把從 GCP Credentials 那邊得到的 Service Account 貼上，權限選擇「完整」</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXEF6Sy2MrLHsvzAKbOuqRzWdPB-kwXigP9LAgaaBdiHnlavX2vwEvWRLMW6NS4nJi_mkQlhcD0ldCVhN4KoVx_D_azli_64i9YbL1nDF7HEzeBvMSG5mS0gdHUv_YeADHYPcEgZqaWH6uKZFM4gwAj5A=w746-h385-no?authuser=2" alt="AM JKLXEF6Sy2MrLHsvzAKbOuqRzWdPB kwXigP9LAgaaBdiHnlavX2vwEvWRLMW6NS4nJi mkQlhcD0ldCVhN4KoVx D azli 64i9YbL1nDF7HEzeBvMSG5mS0gdHUv YeADHYPcEgZqaWH6uKZFM4gwAj5A=w746 h385 no?authuser=2 PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs" width="560" height="289" title="PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs"></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLVdteLUJJMU-tjYJzk3UQDQIhDk0n7Q36DgbhQKKbu3n57YYr2H1aPYTP0krgU5rBXxSGz8_z40ZmiJEOCaGJKCK-XhvCFhYdVza6Zs3z1Y2T5P7DAshqm-rDHu4jGLKaBkisrRs7fcjXUvlPo5Lh56mw=w1004-h385-no?authuser=2" alt="AM JKLVdteLUJJMU tjYJzk3UQDQIhDk0n7Q36DgbhQKKbu3n57YYr2H1aPYTP0krgU5rBXxSGz8 z40ZmiJEOCaGJKCK XhvCFhYdVza6Zs3z1Y2T5P7DAshqm rDHu4jGLKaBkisrRs7fcjXUvlPo5Lh56mw=w1004 h385 no?authuser=2 PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs" title="PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs"></figure></div>


<h2 class="para wp-block-heading">建立並初始化物件</h2>



<p>回到 PHP 的部份加入金鑰及設定 Scope</p>



<pre class="wp-block-preformatted withcode">// 設定金鑰
putenv('GOOGLE_APPLICATION_CREDENTIALS=存放金鑰的路徑/server_secrets.json');
session_start();

// 建立基本 Google Client 
$client = new Google_Client();
// 載入預設金鑰位置
$client-&gt;useApplicationDefaultCredentials();
// 設定 Scope
$client-&gt;addScope([Google_Service_Webmasters::WEBMASTERS_READONLY, Google_Service_Webmasters::WEBMASTERS]);
$client-&gt;setAccessType('offline');
// 建立基本 Google Service Webmasters 物件
$master = new Google_Service_Webmasters($client);</pre>



<h2 class="para wp-block-heading">設定篩選條件</h2>



<p>建立完要用的 Google Service Webmasters 物件之後，對！以前 Google Search Console 叫做 Google Webmaster，雖然我們說要操作 Search Console 但建立的物件跟設定的 Scope 可以看出都還是 Webmasters。</p>



<pre class="wp-block-preformatted withcode">$search = new Google_Service_Webmasters_SearchAnalyticsQueryRequest();
$search-&gt;setStartDate( '2021-07-06' );
$search-&gt;setEndDate( '2021-08-06' );
$search-&gt;setDimensions( ['QUERY'] );
$search-&gt;setAggregationType( 'BY_PROPERTY' );
$search-&gt;setRowLimit(10);</pre>



<p>至於有哪些條件或是要怎麼操作取得自己要的內容，就自己到 <a href="https://developers.google.com/webmaster-tools/search-console-api-original/v3/searchanalytics/query" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">API Explorer<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 測試，或到 <a href="https://noter.tw/8940/google-apis-1/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a> 複習吧！</p>



<h2 class="para wp-block-heading">取得並解析結果</h2>



<p>最後把他們全部串起來～丟回的結果可以先用 print_r 印出來看看，或是直接到 <a href="https://developers.google.com/webmaster-tools/search-console-api-original/v3/searchanalytics/query" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">這邊<span class="wpel-icon wpel-image wpel-icon-6"></span></a> 看回傳的結果 ~</p>



<pre class="wp-block-preformatted withcode">$data = $master-&gt;searchanalytics-&gt;query('https://noter.tw/', $search);

$result = [];
$rows = $data-&gt;rows;
foreach ($rows as $row){
    // 剖析資料
}

</pre>



<p>到這邊基本上就大功告成了，上面的功能是取得網站上特定區間的關鍵字資訊 ~ 需要其他資料的話，釣竿已經在手了，就要靠自己囉！Good Luck ~ </p>



<p id="block-c58a739f-b4e1-42ae-8530-5e20da347253"><strong>Google 系列文章：</strong></p>



<ul class="wp-block-list" id="block-2b1f3c8f-95c7-4eff-8105-53dea9d75e5e"><li><a href="https://noter.tw/8940/google-apis-1-api-explorer/" data-wpel-link="internal">PHP Client 操作 Google APIs (1) 開啟與測試 API</a></li><li><a href="https://noter.tw/8944/google-apis-2-google-api-client-for-php/" data-wpel-link="internal">PHP Client 操作 Google APIs (2) 安裝 Google API Client for PHP</a></li><li><a href="https://noter.tw/8942/google-apis-3-service-account-credentials/" data-wpel-link="internal">PHP Client 操作 Google APIs (3) Google APIs Credentials 介紹</a></li><li><a href="https://noter.tw/9035/google-apis-4-google-search-console-apis/" data-wpel-link="internal">PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs</a></li><li><a href="https://noter.tw/8604/google-formranger/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 表單應用：設定選項次數上限</a></li><li><a href="https://noter.tw/9049/copy-a-google-form/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">複製 Google 表單 (Copy A Google Form)</a></li><li><a href="https://noter.tw/8358/google-adsense-tax-w8ben/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google Adsense: 向 Google 提交稅務資料 | 填寫 W-8BEN 稅務表單</a></li><li><a href="https://noter.tw/4946/google-%e9%9b%b2%e7%ab%af%e5%a4%96%e5%b8%b6%e6%9c%8d%e5%8b%99-takeout%ef%bc%9a%e8%bc%95%e9%ac%86%e6%89%93%e5%8c%85%e9%9b%b2%e7%ab%af%e7%a1%ac%e7%a2%9f%e3%80%81%e7%9b%b8%e7%b0%bf-%e7%ad%89/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 雲端外帶服務 (Takeout)：輕鬆打包雲端硬碟、相簿 … 等資料</a></li><li><a href="https://noter.tw/4394/google-%e7%9b%b8%e7%b0%bf%e5%8f%96%e5%be%97%e7%9c%9f%e5%af%a6%e5%9c%96%e7%89%87%e4%bd%8d%e7%bd%ae%ef%bc%88%e6%8f%92%e5%85%a5%e5%9c%96%e7%89%87%e5%88%b0%e6%96%87%e7%ab%a0%ef%bc%89/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Google 相簿取得真實圖片位置（插入圖片到文章）</a></li><li><a href="https://noter.tw/4267/gdirve-%e8%ae%93%e4%bd%a0%e5%9c%a8-linux-%e6%96%87%e5%ad%97%e4%bb%8b%e9%9d%a2%e4%b9%9f%e8%83%bd%e5%a5%bd%e5%a5%bd%e4%bd%bf%e7%94%a8-google-drive/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">gdirve 讓你在 Linux 文字介面也能好好使用 Google Drive</a></li><li><a href="https://noter.tw/4302/microsoft-office-word-%e7%84%a1%e6%b3%95%e9%96%8b%e5%95%9f-google-drive-%e8%b6%85%e9%80%a3%e7%b5%90/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Microsoft Office Word 無法開啟 Google Drive 超連結</a></li></ul>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/9035/google-apis-4-google-search-console-apis/" data-wpel-link="internal">PHP Client 操作 Google APIs (4) 使用 Google Search Console APIs</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/9035/google-apis-4-google-search-console-apis/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程</title>
		<link>https://noter.tw/8623/received-fatal-alert-handshake_failure/</link>
					<comments>https://noter.tw/8623/received-fatal-alert-handshake_failure/#comments</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Fri, 25 Jun 2021 08:54:43 +0000</pubDate>
				<category><![CDATA[程式開發]]></category>
		<category><![CDATA[一般程式]]></category>
		<category><![CDATA[javax.net.ssl.SSLHandshakeException]]></category>
		<category><![CDATA[Received fatal alert: handshake_failure]]></category>
		<category><![CDATA[handshake_failure]]></category>
		<category><![CDATA[java 1.7]]></category>
		<category><![CDATA[java 1.8]]></category>
		<category><![CDATA[jre]]></category>
		<category><![CDATA[java ssl error]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[Eclipse]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=8623</guid>

					<description><![CDATA[<p>小蛙的工作常常要維護很多有年代的系統，這些系統有些已經找不到源頭或甚至沒有文件留下來了，前一段時間一直碰到抓 https 的時候噴錯的狀況，這篇把這段很痛苦的經歷記錄下來，希望能幫助遇到相同問題的朋友&#46;&#46;&#46;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8623/received-fatal-alert-handshake_failure/" data-wpel-link="internal">javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>小蛙的工作常常要維護很多有年代的系統，這些系統有些已經找不到源頭或甚至沒有文件留下來了，前一段時間一直碰到抓 https 的時候噴錯的狀況，這篇把這段很痛苦的經歷記錄下來，希望能幫助遇到相同問題的朋友。</p>



<span id="more-8623"></span>



<p>早期很多網站都是 http 的，到現在各家大廠的推波助瀾下，你沒有 https 就被當作是不合群的、有危險的、跟不上潮流的網站(?!)，總之當有人上你的網站，但你的網站沒有 https 的時候，各大瀏覽器就會很不客氣的提示：您與這個網站的連線不安全，有些人看到這些提示怕都怕死了根本不敢連～</p>



<p>小蛙這次要處理的系統是早期抓網站圖片的工具，由於是早期開發的，當時幾乎都是 http 為主的網站，這支程式也完全沒有考慮 https 的問題 (當時合作的單位完全沒有 https)，這一兩年來這個問題慢慢顯露出來，有些要抓的網站也在我們自己單位，因此可以透過一些特殊的方法來處理，但是今天遇到 &#8230; 要抓外面單位的網站，這 &#8230; 折騰了小蛙一整天啊。</p>



<p class="pre left green">小蛙給一個最簡單卻也是最困難的建議：更新到 JRE 1.8</p>



<p>小蛙試了超多網路上找到的方法，如果你已經在網路上找很久了，那小蛙用的方法你一定都試過，應該也都還是失敗 &#8230; (跟小蛙一樣)，最後決定直接安裝 JRE 1.8。來看一下這討人厭的錯誤訊息</p>



<pre class="wp-block-preformatted withcode">Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
     at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
     at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
     at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1959)
     at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1077)
     at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
     at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)
     at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323)
     at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)
     at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
     at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
     at TESTSSL.downloadRemoteImage(TESTSSL.java:37)
     at TESTSSL.downloadRemoteImage(TESTSSL.java:53)
     at TESTSSL.main(TESTSSL.java:74)</pre>



<p>直接看小蛙 Eclipse 的設定，Compiler 的地方使用 1.7 </p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/ACtC-3e9Jzy_2bG9HQE6aGNsNevI25pm7FJoS7MK_g-QL-g7O5Ho40-uzMk3igXTjhZYUpuQ9ja4_KPfJDxNq2Ei60AJbo6GWbhyk5GWis4JIZMNARbGNOWPta4CUK-uMW2VcqeDKyD1UdxsuyqdEUxCKI0Q8Q=w831-h788-no?authuser=2" alt="ACtC 3e9Jzy 2bG9HQE6aGNsNevI25pm7FJoS7MK g QL g7O5Ho40 uzMk3igXTjhZYUpuQ9ja4 KPfJDxNq2Ei60AJbo6GWbhyk5GWis4JIZMNARbGNOWPta4CUK uMW2VcqeDKyD1UdxsuyqdEUxCKI0Q8Q=w831 h788 no?authuser=2 javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程" title="javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程"></figure></div>



<p>Java Build Path 也是使用 jdk 1.7.0_45</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/ACtC-3f7rafyHQzMcgF3Od_mfpt9y8ZXQoDpnmU51K3zzzML-oZom0p3IkeC5ECLoWU2K2Ic6eiaECBVP6fpa9tjYLmGHAYXkWmkeorJi55ULGHsxLBafeg8IH3YRSZoxO6ncqvAmEyFi3upQFI_iKUEjC38OA=w831-h788-no?authuser=2" alt="ACtC 3f7rafyHQzMcgF3Od mfpt9y8ZXQoDpnmU51K3zzzML oZom0p3IkeC5ECLoWU2K2Ic6eiaECBVP6fpa9tjYLmGHAYXkWmkeorJi55ULGHsxLBafeg8IH3YRSZoxO6ncqvAmEyFi3upQFI iKUEjC38OA=w831 h788 no?authuser=2 javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程" title="javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程"></figure></div>



<p>Run Configurations 的 JRE 設定也是選擇 jdk 1.7.0_45</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLWHU1lL8wQEAIaIRR3gi-P47_XCOsGwV72aWXOkHX_G4e3NOgFI5uFLVBgmOxyOgp4pV36EiE-ZckUO9-AQHbp9ghuyxXFyljn1jn_kFzFQHUTiGMwtWgifDVcWhbB2DzsfTSsAvNpXZv5vCwjusEZrGA=w693-h296-no?authuser=2" alt="AM JKLWHU1lL8wQEAIaIRR3gi P47 XCOsGwV72aWXOkHX G4e3NOgFI5uFLVBgmOxyOgp4pV36EiE ZckUO9 AQHbp9ghuyxXFyljn1jn kFzFQHUTiGMwtWgifDVcWhbB2DzsfTSsAvNpXZv5vCwjusEZrGA=w693 h296 no?authuser=2 javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程" title="javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程"></figure></div>



<p>執行下去就是這個錯誤 &#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/ACtC-3f9jG0XqkmNx4fEHhjEK90x9RWkI-C3TYAjN7SxSuC5BPHiIOODq3mSyGK5YMRRwZ6Txt3q9yNt0R75LCOpFalfA74bFUUKtHOQ0KBSJAQ8mBvdHHbvvN1uUBWgdjX12jEetzKb0TapfRn7TC0K39ZwDw=w1357-h400-no?authuser=2" alt="ACtC 3f9jG0XqkmNx4fEHhjEK90x9RWkI C3TYAjN7SxSuC5BPHiIOODq3mSyGK5YMRRwZ6Txt3q9yNt0R75LCOpFalfA74bFUUKtHOQ0KBSJAQ8mBvdHHbvvN1uUBWgdjX12jEetzKb0TapfRn7TC0K39ZwDw=w1357 h400 no?authuser=2 javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程" title="javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程"></figure></div>



<p>如果手動安裝了 JRE 8，然後把 Run Configurations 的 JRE 設定成 JRE 8</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AM-JKLXwW-GbryLhs1UBdvpWDXYSb6Thx52bsxaVDbxndptJjpt_Vc2qrQ2zyx0ih1P6aSrZL2hNoUsH5P_ezZ7AAd5YR4x-yOITS2WcCkx-rT07BvWLix9BLbIOcs-v67eos6VLo3VsUxWOiwIlK3u6xkYi7Q=w686-h282-no?authuser=2" alt="AM JKLXwW GbryLhs1UBdvpWDXYSb6Thx52bsxaVDbxndptJjpt Vc2qrQ2zyx0ih1P6aSrZL2hNoUsH5P ezZ7AAd5YR4x yOITS2WcCkx rT07BvWLix9BLbIOcs v67eos6VLo3VsUxWOiwIlK3u6xkYi7Q=w686 h282 no?authuser=2 javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程" title="javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程"></figure></div>



<p>就打完收工了 &#8230; 真的是太敢動 (嗚嗚嗚)，原本小蛙一直想要在 1.7 的環境下想辦法處理，試了一大堆方法沒有一個可以動的，因為改來改去改到亂七八糟，就連把環境升上 1.8 也都還是不行 &#8230; 後來發現 Eclipse 執行的時候可以選擇運行環境，就決定專案的 Compiler 跟 Build Path 維持 1.7，Run Configurations 改成 JRE 1.8 &#8230; 不想繼續糾結下去 Orz &#8230; 但有一些 JSP 環境下沒辦法這樣處理的還是很頭大啊 >&lt;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8623/received-fatal-alert-handshake_failure/" data-wpel-link="internal">javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 抗戰歷程</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/8623/received-fatal-alert-handshake_failure/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Post 後上一頁 history.back() 發生文件已到期/認重新提交表單 ERR_CACHE_MISS</title>
		<link>https://noter.tw/8538/err_cache_miss/</link>
					<comments>https://noter.tw/8538/err_cache_miss/#comments</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Sat, 05 Jun 2021 06:41:57 +0000</pubDate>
				<category><![CDATA[網頁後端]]></category>
		<category><![CDATA[網頁前端]]></category>
		<category><![CDATA[重新提交表單]]></category>
		<category><![CDATA[上一頁]]></category>
		<category><![CDATA[文件已過期]]></category>
		<category><![CDATA[重新導向]]></category>
		<category><![CDATA[Cache-Control]]></category>
		<category><![CDATA[post]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[JSP]]></category>
		<category><![CDATA[ERR_CACHE_MISS]]></category>
		<category><![CDATA[history.back()]]></category>
		<category><![CDATA[送出表單]]></category>
		<guid isPermaLink="false">https://noter.tw/?p=8538</guid>

					<description><![CDATA[<p>原本可以正常使用的系統裡，經過資安調整之後出現了 ERR_CACHE_MISS 的問題，記錄一下解決的方法。 平常瀏覽網頁的時候也會遇到，當送出表單後，點選「上一頁」常可以看到文件已過期，或是重新傳送&#46;&#46;&#46;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8538/err_cache_miss/" data-wpel-link="internal">Post 後上一頁 history.back() 發生文件已到期/認重新提交表單 ERR_CACHE_MISS</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>原本可以正常使用的系統裡，經過資安調整之後出現了 ERR_CACHE_MISS 的問題，記錄一下解決的方法。</p>



<span id="more-8538"></span>



<p class="withcode">平常瀏覽網頁的時候也會遇到，當送出表單後，點選「上一頁」常可以看到文件已過期，或是重新傳送這些資料才可以正確顯示之類的錯誤訊息，記得以前會看到<code>ERR_CACHE_MISS</code>錯誤的字樣，小蛙這次遇到的只有顯示如下圖</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/ACtC-3eSvPTbFIORBjFcpEsPvn90wGzIaXNUCrp25i3s9QSent9yf6064q3zx3R1JpHozzATD2LhTwU1_d3al4waPQ5RX7JAmDlMN2Nf7eo-xfyuYanrDOQk5Aw-T6GKADAvVyk6SclvZGi7YVpD79o8o_lA7Q=w1314-h650-no?authuser=2" alt="ACtC 3eSvPTbFIORBjFcpEsPvn90wGzIaXNUCrp25i3s9QSent9yf6064q3zx3R1JpHozzATD2LhTwU1 d3al4waPQ5RX7JAmDlMN2Nf7eo xfyuYanrDOQk5Aw T6GKADAvVyk6SclvZGi7YVpD79o8o lA7Q=w1314 h650 no?authuser=2 Post 後上一頁 history.back() 發生文件已到期/認重新提交表單 ERR_CACHE_MISS" title="Post 後上一頁 history.back() 發生文件已到期/認重新提交表單 ERR_CACHE_MISS"><figcaption>Chrome 錯誤訊息：這個網頁需要使用你先前輸入的資料才能正確顯示。你可以重新傳送這些資料，不過這麼做會重複執行這個網頁先前執行過的任何動作。 (ERR_CACHE_MISS)</figcaption></figure>



<figure class="wp-block-image size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/ACtC-3f_qjJrM_VT4J6DVmyBUA8UnC4f3AzMssF_Ctxux1nsRzdsxZXI0prLeuH-01YNoQ3WYgIpG-BzUYaeDt3unvCruWrxGTIOnzNOcr3eynoV4gRC84NUQCEhzWcUTyRtEdBwsgZQn02TmqZD1j9-lkS8Cg=w826-h494-no?authuser=2" alt="ACtC 3f qjJrM VT4J6DVmyBUA8UnC4f3AzMssF Ctxux1nsRzdsxZXI0prLeuH 01YNoQ3WYgIpG BzUYaeDt3unvCruWrxGTIOnzNOcr3eynoV4gRC84NUQCEhzWcUTyRtEdBwsgZQn02TmqZD1j9 lkS8Cg=w826 h494 no?authuser=2 Post 後上一頁 history.back() 發生文件已到期/認重新提交表單 ERR_CACHE_MISS" title="Post 後上一頁 history.back() 發生文件已到期/認重新提交表單 ERR_CACHE_MISS"><figcaption>Firefox 錯誤訊息：文件已過期 此文件已不存在。您所請求的文件已不存在於 Firefox 的快取當中。為了您的安全，Firefox 將不會自動重新請求敏感文件。請點下重試以重新向網站請求取得文件。(ERR_CACHE_MISS)</figcaption></figure>



<p class="withcode">小蛙接手處理的這個系統則是原本點選上一頁沒有問題，經過資安掃描修正後發生的，Google 及跟處理過該狀況的同事討論過之後找到相同的答案，如果在 header 中加入<code>Cache-Control: private</code>的設定，就可以解決掉這個問題！</p>



<h2 class="para wp-block-heading">PHP</h2>



<pre class="wp-block-preformatted withcode">// 在 session_start(); 後加入
header("Cache-Control:private");</pre>



<h2 class="para wp-block-heading">JSP</h2>



<pre class="wp-block-preformatted withcode">response.setHeader("Cache-Control", "private");</pre>



<p>參考資料：</p>



<ul class="my-li bg-darkblue wp-block-list"><li>Chrome浏览器对于POST页面执行history.back返回或表单数据丢失的解决办法<br>(連結失效 https://www.unixso.com/Php/post-header.html)</li><li><a href="https://blog.csdn.net/A9925/article/details/42027229" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">ERR_CACHE_MISS 搜尋回上一頁空白或錯誤<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li><li><a href="https://www.cnblogs.com/houdj/p/9685571.html" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">ERR_CACHE_MISS 上一页提示确认重新提交表单<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li></ul>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/8538/err_cache_miss/" data-wpel-link="internal">Post 後上一頁 history.back() 發生文件已到期/認重新提交表單 ERR_CACHE_MISS</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/8538/err_cache_miss/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>
