製作一個使用服務 API 的 WordPress 插件,“從湯到堅果”
已發表: 2022-03-10越來越多的公開可用的 API 提供了強大的服務來擴展我們應用程序的功能。 WordPress 是一個令人難以置信的動態和靈活的 CMS,它支持從小型個人博客到主要電子商務網站以及介於兩者之間的所有內容。 使 WordPress 如此通用的部分原因在於其強大的插件系統,這使得添加功能變得異常容易。
我們將介紹我如何製作 GitHub Pipeline,這是一個允許您使用短代碼在 WordPress 頁面上顯示來自 GitHub API 的數據的插件。 我將給出具體的示例和代碼片段,但將此處描述的技術視為如何使用插件使用任何服務 API 的藍圖。
關於 SmashingMag 的進一步閱讀:
- WordPress Essentials:如何創建 WordPress 插件
- 如何使用瞬態在 GitHub 上部署 WordPress 插件
- 向插件添加可配置字段的三種方法
我們將從頭開始,但假設您對 WordPress 和插件開發有一定程度的熟悉,並且我們不會花時間在初學者主題上,例如安裝 WordPress 或 Composer。
你需要:
- PHP 環境,全新安裝 WordPress;
- 一個 GitHub 帳戶(或其他 API 提供商,如果您想即興發揮);
- 作曲家(推薦)。
選擇 API
編寫這種插件的第一步是選擇一個 API。 在本教程中,我們將使用 GitHub API。 如果您正在考慮使用其他 API,請考慮一些可能會影響您的項目回報的重要因素。
我首先要看的是 API 文檔的徹底性和質量。 如果它稀疏或過時,請準備好花時間篩選源代碼來回答您自己的問題。 此外,還要考慮 API 的成熟程度以及提供者對其版本的負責程度。 沒有什麼比花時間創造偉大的東西更糟糕的了,只是因為上游 API 的變化而破壞了它。 在端點 URL 中查找版本控制系統。
// good https://api.stable.com/v1/user/ // danger https://api.dodgy.com/user/
冒著明顯的風險,針對第三方 API 進行編程涉及信任關係,並且並非所有 API 都是平等的。
購買圖書館
知名品牌通常會分發庫或 SDK,以便更輕鬆地使用其 API。 如果不是這種情況,請記住在重新發明輪子之前研究其他人是否已經編寫了庫。 Google 和 GitHub 是您開始研究的兩個好地方。 GitHub 上的星數或分叉數很好地表明了庫的有效性。 最近提交的年齡和/或未解決問題的數量表明它的維護程度。 就我而言,我很幸運地找到了 KNP Labs 提供的可愛的 PHP GitHub API。
用 Guzzle 編寫自己的
如果您的提供者沒有令人滿意的庫,您仍然不應該從頭開始,因為像 Guzzle 這樣的工具可以更輕鬆地處理 HTTP 請求。 Guzzle 包裝了 PHP 的 cURL 庫,並消除了通常與手動配置和發出請求相關的許多令人頭疼的問題。 即使您只發出一兩個請求,我仍然建議您使用它,因為它會使您的代碼更加健壯,並且使用 Composer 安裝它是小菜一碟。
設置插件
我們將從一個最小的 WordPress 插件的基本框架開始,一個包含兩個文件的目錄。 選擇一個描述性和唯一的文件夾名稱對於避免與其他插件衝突很重要。 如果您的插件名稱有些通用,請考慮添加唯一前綴。
github-api/ readme.txt github-api.php
readme.txt
包含您的插件的元數據,如果您決定將其發佈在wordpress.org
上,該元數據將出現。 閱讀文檔中有關發布 WordPress 插件的信息,或查看綜合示例 readme.txt。
PHP 文件的標題中還包含一些元數據,這些元數據將用於在儀表板中顯示有關您的插件的信息。 從如下所示的標題開始:
<?php /** Plugin Name: GitHub API description: >- Add GitHub project information using shortcode Version: 1.0 Author: Your Name License: GPLv2 or later Text Domain: github-api */
出於安全原因,拒絕直接訪問文件也是一個好主意,如下所示:
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
此時,插件不會做任何事情,但是當您將文件複製到wp-content/plugins
時,它應該會出現在插件列表中並且您應該能夠激活它。

接下來,我們要包含處理 API 請求的庫。 在下面的代碼示例中,我們包含了 KNP Labs 的 PHP GitHub API,但您可以通過將knplabs/github-api
替換為您正在使用的包來包含任何依賴項。
$ cd wp-content/plugins/github-api $ composer require knplabs/github-api
現在,您的文件結構應如下所示:
github-api/ composer.json composer.lock github-api.php readme.txt vendor/
現在文件已經就位,我們需要安裝它時創建的vendor/autoload.php
。
require_once 'vendor/autoload.php';
如果一切設置正確,您應該能夠從庫中實例化一個類,而不會引發致命錯誤。
$testing = new \Github\Client();
但這只是測試您的依賴項是否可用的一種快速方法。 我建議定義一個擴展庫的新類。
class MyGithub extends \Github\Client {};
這並不重要,但它使您的代碼在幾個方面更加靈活。 最明顯的方式是您可以根據需要更改或添加新功能。 但是,如果您將來想要切換庫,它也會讓生活變得更輕鬆,因為您只需要在一次地方進行更改,而不是在整個代碼中進行更改。
WordPress 簡碼
現在是時候設置我們的第一個短代碼了,它是一個特殊的片段,允許您通過將其放置在帖子或頁面中來生成內容。 如果您對簡碼完全陌生,或者您想更深入地了解它們的工作原理,請查看官方簡碼文檔。 在我的示例中,我將在作者放置此短代碼的任何位置顯示 GitHub 問題列表: [github_issues]
正如我們之前所做的那樣,讓我們從一個最小的示例開始,以確保在我們進行 API 調用之前正確註冊短代碼。
function github_issues_func( $atts ) { return "Hello world!"; } add_shortcode( "github_issues", "github_issues_func" );
現在,如果您發布一個包含[github_issues]
的頁面,您應該在訪問該頁面時看到“Hello world”。 現在這可以正常工作了,讓我們設置 API 調用。
在上一節中,我們為我們的 GitHub 庫設置了自動加載,並定義了我們自己的類來擴展它。 現在,我們可以開始使用它來進行 API 調用了。 因此,我們將其添加到短代碼的回調函數中。 作為概念證明,讓我們獲取 GitHub Pipeline 存儲庫中的所有問題。 此方法的文檔可用。
function github_issues_func( $atts ) { // Instantiate our class $gh = new MyGithub(); // Make the API call to get issues, passing in the GitHub owner and repository $issues = $gh->api('issue')->all('TransitScreen', 'wp-github-pipeline'); // Handle the case when there are no issues if ( empty($issues) ) return "<strong>" . __("No issues to show", 'githup-api') . "</strong>"; // We're going to return a string. First, we open a list. $return = "<ul>"; // Loop over the returned issues foreach( $issues as $issue ) { // Add a list item for each issue to the string // (Feel free to get fancier here) // Maybe make each one a link to the issue issuing $issue['url] ) $return .= "<li>{$issue['title']}</li>"; } // Don't forget to close the list $return .= "</ul>"; return $return; } add_shortcode( 'github_issues', 'github_issues_func' );
現在您應該能夠查看我們上面用來測試簡碼的同一頁面,這一次您應該看到一個無序列表的問題。 可是等等! 在繼續之前,讓我們進行一項優化,以便我們的代碼更容易測試。 (你是在測試,對吧?!)不要在我們的函數中使用new
來實例化 GitHub 庫類,讓我們將它作為參數傳入並僅在必要時實例化它。
function github_issues_func( $atts, $gh=null ) { // Conditionally instantiate our class $gh = ( $gh ) ? $gh : new MyGithub(); …
這種修改類似於依賴注入模式,這使得我們的函數更容易測試。 WordPress 單元測試超出了本教程的範圍,但很容易看出這個新版本如何讓我們輕鬆地將“假”API 數據傳遞給函數,以便我們的測試斷言準確地知道會發生什麼。
到目前為止,我們所做的對於僅在單個存儲庫上使用的內部項目來說非常好,但如果用戶可以配置從哪個存儲庫獲取問題,它將更加有用。 讓我們設置一下。
首先,我們將使用 WordPress 掛鉤來註冊我們的新設置頁面,回調函數將定義菜單的標籤和標題。 我們將使用相同的增量方法來使其工作。
// Register the menu. add_action( "admin_menu", "gh_plugin_menu_func" ); function gh_plugin_menu_func() { add_submenu_page( "options-general.php", // Which menu parent "GitHub", // Page title "GitHub", // Menu title "manage_options", // Minimum capability (manage_options is an easy way to target administrators) "github", // Menu slug "gh_plugin_options" // Callback that prints the markup ); } // Print the markup for the page function gh_plugin_options() { if ( !current_user_can( "manage_options" ) ) { wp_die( __( "You do not have sufficient permissions to access this page." ) ); } echo "Hello world!"; }
現在您應該能夠登錄儀表板並在“設置”下看到新的 GitHub 菜單,然後單擊它以查看帶有“Hello world!”的空白設置頁面

接下來,我們將用表單的實際標記替換Hello word
。 我會給你一個準系統的例子,你可以隨心所欲地設計。
?> <form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="update_github_settings" /> <h3><?php _e("GitHub Repository Info", "github-api"); ?></h3> <p> <label><?php _e("GitHub Organization:", "github-api"); ?></label> <input class="" type="text" name="gh_org" value="<?php echo get_option('gh_org'); ?>" /> </p> <p> <label><?php _e("GitHub repository (slug):", "github-api"); ?></label> <input class="" type="text" name="gh_repo" value="<?php echo get_option('gh_repo'); ?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Save", "github-api"); ?>" /> </form> <?php
我包含的 CSS 類與 WordPress 核心使用的類相匹配,這是一種無需額外努力即可讓您的自定義選項頁面看起來不錯的好方法。


關於這個表格,有兩件重要的事情需要理解。 首先,它提交的端點必須是/wp-admin/admin-post.php
。 您可以硬編碼此路徑,但如果網站安裝在子目錄中,它將無法工作。 因此,我們使用內置的admin_url()
來動態創建它。
其次,注意名為action
的隱藏輸入。 這個字段是 WordPress 知道如何處理表單提交的發布請求的方式。 該value
對應於我們將用於設置回調函數的操作掛鉤的名稱。 我們將掛鉤的操作的名稱將是這個值,前綴為admin post
。 所以,在我們的例子中,我們需要添加這個:
add_action( 'admin_post_update_github_settings', 'github_handle_save' );
我發現這是創建自定義管理菜單的一個更古怪、更不直觀的方面,但是一旦你習慣了它,它就會變得非常輕鬆。 正如您可能已經猜到的那樣, add_action()
的第二個參數是我們的回調函數的名稱,它實際上會將值保存到數據庫中。
function github_handle_save() { // Get the options that were sent $org = (!empty($_POST["gh_org"])) ? $_POST["gh_org"] : NULL; $repo = (!empty($_POST["gh_repo"])) ? $_POST["gh_repo"] : NULL; // Validation would go here // Update the values update_option( "gh_repo", $repo, TRUE ); update_option("gh_org", $org, TRUE); // Redirect back to settings page // The ?page=github corresponds to the "slug" // set in the fourth parameter of add_submenu_page() above. $redirect_url = get_bloginfo("url") . "/wp-admin/options-general.php?page=github&status=success"; header("Location: ".$redirect_url); exit; }
這個例子也非常少。 在生產環境中,您可能想要添加一些驗證。 此外,在此示例中,我們會自動將status=success
傳遞回 URL。 表單的標記實際上還沒有使用它。 要有條件地顯示成功消息,請在gh_plugin_options()
的表單上方添加如下內容:
if ( isset($_GET['status']) && $_GET['status']=='success') { ?> <div class="updated notice is-dismissible"> <p><?php _e("Settings updated!", "github-api"); ?></p> <button type="button" class="notice-dismiss"> <span class="screen-reader-text"><?php _e("Dismiss this notice.", "github-api"); ?></span> </button> </div> <?php }
如果您添加驗證,請使用相同的模式傳回不同的狀態和消息,以便用戶知道他們提交表單是否失敗以及為什麼失敗。
現在您應該能夠保存新的所有者和存儲庫值。 通過輸入 GitHub 上任何公共項目的所有者和存儲庫來測試它。 最後一步是返回短代碼回調函數github_issues_func()
並替換硬編碼的所有者和存儲庫值。
… $issues = $gh->api("issue")->all(get_option("gh_org"), get_option("gh_repo")); …
一旦到位,請重新訪問您添加短代碼的頁面。 您現在應該看到您設置的任何項目的問題。
獎勵回合:OAuth 2.0 身份驗證
我們上面使用的方法非常適用於公共存儲庫,但是如果我們想將此插件與需要身份驗證的私有存儲庫一起使用怎麼辦? GitHub API 使用 OAuth 2.0 協議進行身份驗證,這是您現在使用最流行的 API 時會遇到的。 基本的 OAuth 2.0 工作流程如下所示:
- 您向提供者註冊一個應用程序(有時稱為“客戶端”)並收到一個唯一的 ID 和密鑰。
- 您的應用程序向提供者的身份驗證端點發出請求,傳遞上述憑據以及提供者用來將請求重定向回應用程序端點的重定向 URL。
- 然後提示用戶接受您的訪問請求。 如果他們這樣做了,提供商會使用您隨請求發送的 URL 以及臨時代碼將用戶重定向回您的應用程序。
- 您的應用程序捕獲此代碼,然後發出第二個請求,將此代碼傳遞回提供者。 提供者使用訪問令牌進行響應,然後您的應用程序使用該令牌向提供者進行身份驗證。
我們將從在 GitHub 上註冊一個應用程序開始。 與大多數流行的 API 一樣,此部分位於網站的開發人員區域。

這裡最重要的是授權回調 URL。 第三步中傳遞的重定向 URL 必須與您在此處輸入的內容相匹配,或者包含它。 所以,在開發過程中,我一般會進入網站的首頁。 這樣,我的應用程序可以重定向到任何路徑。
接下來,我們將在gh_plugin_options()
中的設置頁面中添加第二個表單,用於提交應用程序的客戶端 ID 和密碼。
<form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="oauth_submit" /> <h3>Oauth 2.0</h3> <p> <label><?php _e("GitHub Application Client ID:", "github-api"); ?></label> <input class="" type="text" name="client_id" value="<?php echo get_option('client_id')?>" /> </p> <p> <label><?php _e("GitHub Application Client Secret:", "github-api"); ?></label> <input class="" type="password" name="client_secret" value="<?php echo get_option('client_secret')?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Authorize", "github-api"); ?>" /> </form>
為了讓生活更輕鬆,我將使用 The League of Extraordinary Packages 的 GitHub Provider for OAuth 2.0 Client。 所以,首先,讓我們再次使用 Composer 來添加依賴:
composer require league/oauth2-github
請注意,來自 KNP Labs 的 GitHub 庫也內置了 OAuth 2.0 支持。因此,在我的具體示例中,這有點多餘。 但我想介紹這個庫,因為它屬於一組特定於提供商的 OAuth 2.0 客戶端庫,它們都擴展了由強大的非凡包聯盟維護的相同框架。 您可以查看支持的提供程序的完整列表或閱讀有關如何擴展框架以支持新提供程序的說明。
讓我們創建一個函數來在用戶提交表單時請求並保存令牌。 因為 GitHub 是已經支持的提供商之一,所以我可以將示例複製到其文檔中,只需進行一些修改。
function handle_oauth() { // If the form was just submitted, save the values // (Step 1 above) if ( isset($_POST["client_id"]) && isset($_POST["client_secret"]) ) { update_option( "client_id", $_POST["client_id"], TRUE ); update_option("client_secret", $_POST["client_secret"], TRUE); } // Get the saved application info $client_id = get_option("client_id"); $client_secret = get_option("client_secret"); if ($client_id && $client_secret) { $provider = new League\OAuth2\Client\Provider\Github([ "clientId" => $client_id, "clientSecret" => $client_secret, "redirectUri" => admin_url("options-general.php?page=github"), ]); } // If this is a form submission, start the workflow // (Step 2) if (!isset($_GET["code"]) && $_SERVER["REQUEST_METHOD"] === "POST") { // If we don't have an authorization code, then get one $authUrl = $provider->getAuthorizationUrl(); $_SESSION["oauth2state"] = $provider->getState(); header("Location: ".$authUrl); exit; // Check given state against previously stored one to mitigate CSRF attack // (Step 3 just happened and the user was redirected back) } elseif (empty($_GET["state"]) || ($_GET["state"] !== $_SESSION["oauth2state"])) { unset($_SESSION["oauth2state"]); exit("Invalid state"); } else { // Try to get an access token (using the authorization code grant) // (Step 4) $token = $provider->getAccessToken("authorization_code", [ "code" => $_GET["code"] ]); // Save the token for future use update_option( "github_token", $token->getToken(), TRUE ); } }
而且,就像我們對其他表單所做的那樣,我們需要添加操作掛鉤,以便在保存表單時調用該函數。
add_action( "admin_post_oauth_submit", "handle_oauth" );
保存表單現在應該會將您發送到 API 提供者的授權頁面。 授權後,您的應用程序可以使用保存的令牌從您有權訪問的私有存儲庫中請求數據。 我在 KNP Labs 使用的庫有一個方便的方法。
$gh = new MyGithub(); $gh->authenticate( get_option("github_token"), NULL, Github\Client::AUTH_HTTP_TOKEN);
庫在處理身份驗證的方式上會有所不同,但是通過一種或另一種方式,您將傳入令牌,然後它將代表用戶發出經過身份驗證的請求。
結論
我們已經涵蓋了很多領域,並且我嘗試盡可能減少示例,以便整個工作流程保持清晰。 本教程的完整源代碼可用。 我希望您現在對創建使用第三方服務 API 的 WordPress 插件所涉及的移動部分有一個清晰的了解,並希望您受到啟發來編寫自己的 WordPress API 插件。
最後的筆記
- WordPress 法典(文檔)
- GitHub API(文檔)
- OAuth 2.0(文檔)
- 作曲家(文檔)
- 非凡禮包聯盟
- Guzzle 文檔