WordPress 外掛 OAuth 2.0 架構重構實戰:從耦合邏輯到共用認證元件 - Mr. 蔡大痣數位轉型顧問 - WordPress 及 SEO 專家

文章分類/

WordPress 外掛 OAuth 2.0 架構重構實戰:從耦合邏輯到共用認證元件

221 瀏覽
2026-02-21 更新

當公司有多個 WordPress 外掛都需要串接 Google API 時,每個外掛各自實作一份 OAuth 2.0 認證邏輯,不但造成程式碼重複,更增加維護成本。

本文記錄我們將 orca-gsc-insight 外掛中的 OAuth 認證邏輯,經過三個階段的重構,最終抽取為 orca-admin 共用元件的完整過程。

1. 背景:原始架構的問

重構前,ORCA_GSC_OAuth_Api 這個類別身兼兩職:

  • Google OAuth 2.0 認證流程(取得授權 URL、處理回調、刷新 Token、撤銷授權)
  • Google Search Console API 資料查詢(熱門文章、關鍵字、連線測試)

這產生了幾個問題:

問題影響
OAuth 邏輯與 GSC 邏輯混在同一個類別無法複用於 Google Analytics 等其他服務
Option key 硬編碼在認證類別中不同服務無法使用各自的 key 前綴
每個外掛都要複製一份 OAuth 程式碼Bug 修復需要同步多個副本
快取清除混在 OAuth 撤銷邏輯中認證層不應知道業務層的 transient key

2. Phase 1 — 職責分離:抽取 OAuth 認證類別

第一步是將 OAuth 認證邏輯從 ORCA_GSC_OAuth_Api 中抽出,建立獨立的 ORCA_Google_OAuth 類別。

重構前的結構

ORCA_GSC_OAuth_Api.php
├── OAuth 認證流程(get_oauth_url, handle_callback, refresh_token...)
├── GSC API 呼叫(get_hot_posts, test_connection...)
└── 快取管理(clear_cache, delete_transient...)

重構後的結構

ORCA_Google_OAuth.php      ← 純 OAuth 認證(新檔案)
├── get_oauth_url()
├── handle_oauth_callback()
├── get_access_token()
├── refresh_access_token()
├── revoke_oauth()
├── is_authorized()
└── diagnose_oauth_setup()

ORCA_GSC_OAuth_Api.php     ← 純 GSC API 呼叫(瘦身後)
├── __construct(ORCA_Google_OAuth $oauth)
├── get_hot_posts()
├── test_connection()
├── get_keywords_data()
└── clear_cache()

關鍵決策:GSC 呼叫層透過建構子注入 ORCA_Google_OAuth 實例,而非自己建立。這遵循了依賴注入(Dependency Injection)原則。

// ORCA_GSC_OAuth_Api 接收 OAuth 實例,不自己建立
class ORCA_GSC_OAuth_Api {
    private $oauth;

    public function __construct( ORCA_Google_OAuth $oauth = null ) {
        $this->oauth = $oauth ?? self::create_oauth();
    }
}

3. Phase 2 — 深度解耦:建構子注入 + 工廠方法

Phase 1 抽出了類別,但 ORCA_Google_OAuth 內部仍硬編碼了 GSC 專用的 option key 前綴、scope 和 redirect URI。這讓它無法直接複用於其他 Google 服務。

問題:硬編碼的設定值

// ❌ 重構前:OAuth 類別硬編碼了 GSC 專用值
class ORCA_Google_OAuth {
    private $prefix = 'orca_gsc_insight_oauth';  // 寫死
    private $scope  = 'https://www.googleapis.com/auth/webmasters.readonly'; // 寫死
}

解法:所有參數由建構子注入

// ✅ 重構後:所有設定由外部注入
class ORCA_Google_OAuth {

    private $prefix;
    private $scope;
    private $redirect_uri;

    public function __construct(
        string $option_prefix,
        string $scope,
        string $redirect_uri
    ) {
        $this->prefix       = $option_prefix;
        $this->scope        = $scope;
        $this->redirect_uri = $redirect_uri;
    }

    // Option key 由前綴動態組合
    private function opt_client_id()     { return $this->prefix . '_client_id'; }
    private function opt_client_secret() { return $this->prefix . '_client_secret'; }
    private function opt_token()         { return $this->prefix . '_token'; }
    private function opt_refresh_token() { return $this->prefix . '_refresh_token'; }
    // ...
}

工廠方法:集中管理 GSC 專屬設定

既然 ORCA_Google_OAuth 不再知道自己是為 GSC 服務,GSC 專屬的設定就需要一個集中定義的地方。我們在 ORCA_GSC_OAuth_Api 中建立了靜態工廠方法 create_oauth()

class ORCA_GSC_OAuth_Api {

    // GSC 專屬常數
    private const OAUTH_PREFIX = 'orca_gsc_insight_oauth';
    private const OAUTH_SCOPE  = 'https://www.googleapis.com/auth/webmasters.readonly';

    /**
     * 建立 GSC 專用的 OAuth 實例(靜態工廠方法)
     */
    public static function create_oauth(): ORCA_Google_OAuth {
        return new ORCA_Google_OAuth(
            self::OAUTH_PREFIX,
            self::OAUTH_SCOPE,
            admin_url( 'admin.php?page=orca-gsc-settings&action=oauth_callback' )
        );
    }
}

所有需要 GSC OAuth 實例的地方(Admin Page、設定頁面、Elementor Handler)統一呼叫 ORCA_GSC_OAuth_Api::create_oauth(),不再自行 new ORCA_Google_OAuth()

快取清除的分層

撤銷 OAuth 時,原本在 OAuth 類別裡直接清除 GSC 的 transient 快取(orca_gsc_insight_hot_posts)。這違反了關注點分離。重構後:

// ORCA_Google_OAuth::revoke_oauth()
// → 只撤銷 token,不清除任何業務快取

// ORCA_GSC_OAuth_Api::revoke_oauth()
// → 呼叫 OAuth 撤銷 + 清除 GSC 專屬快取
public function revoke_oauth(): bool {
    $result = $this->oauth->revoke_oauth();   // OAuth 層:撤銷 token
    $this->clear_cache();                      // GSC 層:清除業務快取
    return $result;
}

未來擴展範例

現在若要新增 Google Analytics 的 OAuth 串接,只需建立新的呼叫層,完全不用修改 OAuth 類別本身:

// 未來新增 GA 串接,只要寫一個新的呼叫層
class ORCA_GA_OAuth_Api {
    private const OAUTH_PREFIX = 'orca_ga_oauth';
    private const OAUTH_SCOPE  = 'https://www.googleapis.com/auth/analytics.readonly';

    public static function create_oauth(): ORCA_Google_OAuth {
        return new ORCA_Google_OAuth(
            self::OAUTH_PREFIX,
            self::OAUTH_SCOPE,
            admin_url( 'admin.php?page=orca-ga-settings&action=oauth_callback' )
        );
    }
}

4. Phase 3 — 跨外掛共用:移至 orca-admin

經過前兩階段,ORCA_Google_OAuth 已經是完全通用的 Google OAuth 2.0 認證類別,不包含任何 GSC 或其他服務的邏輯。這正是將它移至共用基底外掛 orca-admin 的最佳時機。

步驟一:移動檔案並更改命名空間

項目移動前移動後
檔案位置orca-gsc-insight/includes/api/ORCA_Google_OAuth.phporca-admin/includes/api/ORCA_Google_OAuth.php
命名空間OrcaGscInsightOrcaBiz
載入方式由 orca-gsc-insight 自行 require_once由 orca-admin Bootstrap 統一載入

步驟二:修正呼叫端引用(3 個檔案)

orca-gsc-insight 中,需要修正所有引用 ORCA_Google_OAuth 的地方:

(1)主檔案 orca-gsc-insight.php — 移除 require_once

// ❌ 移除(檔案已不在本專案中)
require_once ORCA_GSC_INSIGHT_DIR . 'includes/api/ORCA_Google_OAuth.php';

// ✅ 改為註解說明來源
// ORCA_Google_OAuth 由 orca-admin 外掛提供(OrcaBiz\ORCA_Google_OAuth)

(2)ORCA_GSC_OAuth_Api.php — 新增跨命名空間 use

namespace OrcaGscInsight;

// ✅ 新增:從 orca-admin 的命名空間引入
use OrcaBiz\ORCA_Google_OAuth;

class ORCA_GSC_OAuth_Api {
    // 型別提示 ORCA_Google_OAuth 會透過 use 正確解析
    public function __construct( ORCA_Google_OAuth $oauth = null ) { ... }
}

(3)settings-page.php — 更改 use 命名空間

// ❌ 舊的引用
use OrcaGscInsight\ORCA_Google_OAuth;

// ✅ 新的引用
use OrcaBiz\ORCA_Google_OAuth;

步驟三:刪除本地檔案

確認所有引用都已修正後,刪除 orca-gsc-insight 中的本地副本:

rm includes/api/ORCA_Google_OAuth.php

5. 外掛依賴檢查實作

既然 orca-gsc-insight 現在依賴 orca-admin 提供 OAuth 元件,就必須在啟用時檢查依賴是否滿足。我們在主檔案中加入了 admin_init hook:

add_action('admin_init', function () {
    if (!function_exists('is_plugin_active')) {
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }

    // 檢查 orca-admin 是否啟用
    if (!is_plugin_active('orca-admin/orca-admin.php') 
        && !class_exists('OrcaBiz\ORCA_Google_OAuth')) {
        
        // 自動停用本外掛
        deactivate_plugins(plugin_basename(ORCA_GSC_INSIGHT_FILE));

        // 顯示管理員錯誤通知
        add_action('admin_notices', function () {
            echo '<div class="notice notice-error"><p>';
            echo '<strong>ORCA GSC Insights</strong> 需要 ';
            echo '<strong>ORCA Admin</strong> 外掛才能運作。';
            echo '請先安裝並啟用 <code>orca-admin</code> 外掛。';
            echo '</p></div>';
        });

        if (isset($_GET['activate'])) {
            unset($_GET['activate']);
        }
    }
});

設計重點:

  • 使用 is_plugin_active() 搭配 class_exists() 雙重檢查,避免路徑不同造成的誤判
  • 不滿足時自動 deactivate_plugins(),防止因缺少類別定義導致 PHP Fatal Error
  • 清除 $_GET['activate'] 以避免 WordPress 顯示「外掛已啟用」的誤導訊息

6. 最終架構總覽

┌─────────────────────────────────────────────────────────────┐
│  orca-admin(基底外掛,命名空間 OrcaBiz)                     │
│                                                             │
│  ORCA_Google_OAuth                                          │
│  ├── __construct($prefix, $scope, $redirect_uri)            │
│  ├── get_oauth_url()                                        │
│  ├── handle_oauth_callback($code, $state)                   │
│  ├── get_access_token()        ← 自動處理 Token 過期刷新     │
│  ├── refresh_access_token()                                 │
│  ├── revoke_oauth()            ← 只處理 Token,不碰業務快取   │
│  ├── is_authorized()                                        │
│  └── diagnose_oauth_setup()                                 │
└──────────────────────────┬──────────────────────────────────┘
                           │ use OrcaBiz\ORCA_Google_OAuth
                           │
┌──────────────────────────▼──────────────────────────────────┐
│  orca-gsc-insight(GSC 外掛,命名空間 OrcaGscInsight)        │
│                                                             │
│  ORCA_GSC_OAuth_Api                                         │
│  ├── create_oauth()            ← 靜態工廠,注入 GSC 設定      │
│  ├── get_hot_posts()                                        │
│  ├── test_connection()                                      │
│  ├── revoke_oauth()            ← 撤銷 Token + 清 GSC 快取    │
│  └── clear_cache()                                          │
│                                                             │
│  ORCA_Admin_Page                                            │
│  └── 所有 OAuth 操作 → ORCA_GSC_OAuth_Api::create_oauth()    │
└─────────────────────────────────────────────────────────────┘

資料流:OAuth 授權流程

使用者點擊「Google 授權」
    │
    ▼
Admin Page 呼叫 ORCA_GSC_OAuth_Api::create_oauth()
    │  ← 注入 prefix='orca_gsc_insight_oauth', scope, redirect_uri
    ▼
ORCA_Google_OAuth::get_oauth_url()
    │  ← 產生 state、儲存到 wp_options(key = {prefix}_state)
    ▼
導向 Google 授權頁面 → 使用者同意 → 回調
    │
    ▼
ORCA_Google_OAuth::handle_oauth_callback()
    │  ← 驗證 state、交換 code 取得 Token
    │  ← 儲存到 wp_options(key = {prefix}_token 等)
    ▼
授權完成,GSC API 可正常呼叫

Option Keys 對照表

所有 option key 由工廠方法注入的 OAUTH_PREFIXorca_gsc_insight_oauth)動態組合:

wp_options Key用途
orca_gsc_insight_oauth_client_idOAuth Client ID
orca_gsc_insight_oauth_client_secretOAuth Client Secret
orca_gsc_insight_oauth_tokenAccess Token
orca_gsc_insight_oauth_refresh_tokenRefresh Token
orca_gsc_insight_oauth_token_expiryToken 過期時間
orca_gsc_insight_oauth_stateCSRF State
orca_gsc_insight_oauth_state_timeState 建立時間

7. 經驗總結與設計原則

遵循的設計原則

原則實踐方式
單一職責原則(SRP)OAuth 類別只管認證,GSC 呼叫層只管 API 查詢
依賴注入(DI)OAuth 實例透過建構子注入到 GSC 呼叫層
開放封閉原則(OCP)新增 Google 服務只需建新的呼叫層,不修改 OAuth 類別
工廠方法模式create_oauth() 集中管理服務專屬設定
關注點分離快取清除從 OAuth 層移至業務層

版本演進時間線

版本內容
v1.3.0Phase 1 — 抽取 ORCA_Google_OAuth 類別,職責分離
v1.3.1Phase 2 — 建構子注入所有參數、工廠方法、快取分層
v1.3.2Phase 3 — 移至 orca-admin,跨外掛共用,依賴檢查

給同仁的注意事項

  1. 不要直接 new ORCA_Google_OAuth() — 請一律使用對應呼叫層的 create_oauth() 工廠方法,確保設定值集中管理。
  2. 新增 Google 服務時 — 建立 ORCA_<服務名>_OAuth_Api.php,定義自己的 OAUTH_PREFIXOAUTH_SCOPE 常數。
  3. 撤銷授權時 — 呼叫呼叫層的 revoke_oauth()(會同時清除業務快取),不要直接呼叫 OAuth 類別的 revoke_oauth()
  4. 外掛依賴 — 任何使用 OrcaBiz\ORCA_Google_OAuth 的外掛,都需要加入 orca-admin 的依賴檢查。

本文記錄 2026-02-21 的重構工作。如有疑問,請聯繫開發團隊。

✦ 虎鯨 OrcaBiz SEO 優化專業團隊 ✦

專業 SEO 公司幫助你將流量累積成看得見的業績,成為長期有效的最強業務!

載入中…
沒有更多相關文章可閱讀