您的 WordPress API 開發人員未使用的五種久經考驗的技術
已發表: 2022-03-11至少在您的客戶眼中,提升您作為 WordPress 開發人員的地位的最佳方法之一就是熟練使用 API。 這是 WordPress API 實現的一個常見場景:您的客戶要求您向他們的站點添加一個小部件——例如,一個電子郵件訂閱小部件。 您從他們的第三方電子郵件服務中獲取一些代碼(可能是腳本標籤或iframe ),將其粘貼到頁面中,然後回复您的客戶,“知道了!”
不幸的是,您正在與要求更高的客戶打交道,他們注意到以下缺陷:
- 儘管該小部件與網站的其他部分一樣具有無襯線字體,但它並不完全正確。 該小部件使用 Helvetica 而不是您安裝的自定義字體。
- 小部件的訂閱表單會觸發新的頁面加載,如果將其放置在文章的中途,這可能會造成乾擾。
- 在頁面的其餘部分之後,小部件似乎需要額外的時間來加載,這感覺很刺耳而且很便宜。
- 客戶希望根據訂閱者訂閱的帖子使用元數據標記訂閱者,並且該小部件不提供與此功能類似的任何東西。
- 客戶發現他們現在必須管理兩個儀表板(wp-admin 和電子郵件服務的管理區域)很煩人。
在這一點上,可能會發生兩件事中的一件。 您可以將這些項目聲明為“不錯”,並向您的客戶保證 80/20 解決方案的優點,或者您可以滿足這些要求。 根據我的個人經驗,我發現交付此類請求(即展示對第三方服務的掌握程度)是讓客戶相信您是某種 WordPress 嚮導的可靠方法。 另外,這通常很有趣。
在過去的十年中,我使用 WordPress 作為 API 消費平台,可能有 50 種不同的 API。 一些最常見的 API 是 MailChimp、Google Analytics、Google Maps、CloudFlare 和 Bitbucket。 但是,如果您需要做更多事情,如果您需要定制解決方案怎麼辦?
如何開發 WordPress API 客戶端
在本文中,我將針對通用的“電子郵件服務”API 進行開發,盡我最大的努力使事情盡可能不可知。 但是,我確實認為我們正在處理 JSON REST API 是合理的。 以下是一些背景主題,可能會幫助您享受本文中的技術要點:
- WordPress HTTP 系列函數
- JSON
- 休息
如果您發現自己對這些主題稍微熟悉並且有興趣深入挖掘,請立即暫停並下載出色的 Postman 應用程序。 它允許您在不編寫代碼的情況下與 API 進行通信。
但是,如果您根本不熟悉這些內容,請繼續閱讀。 具有一定程度 WordPress 經驗的技術讀者將從本文中獲得最大收益,但我會注意以不太技術化的方式解釋每種技術的價值。 非技術讀者將使本文能夠在讚助之前評估每個點的投資回報率,並在交付後判斷實施質量。
注意:如果您需要快速復習課程,可以查看我們的 WordPress REST API 指南。
在沒有進一步的序言的情況下,請允許我與您分享一些不同的技術,我發現自己在與我合作的大多數 API、項目和團隊中都很欣賞這些技術。
瞬態:何時握住它們,何時折疊它們
在我的開頭段落中,我注意到客戶發現跨越兩個管理區域很煩人:wp-admin 和他們的電子郵件服務的儀表板。 解決這個問題的一個好方法是在 wp-admin 中為他們提供一個儀表板小部件,用於顯示他們最近訂閱者活動的摘要。
但是話又說回來,這可能需要向遠程 API(電子郵件服務提供的 API)發出多個 HTTP 請求,從而導致頁面加載時間過長。 此性能問題的解決方案是將 API 調用存儲為瞬態。 這篇 Codex 文章提供了一個很好的解釋,你一定要閱讀,但我會總結一下:
- 從遠程 API 獲取數據。
- 使用
set_transient()存儲它,並根據您自己對性能、速率限制以及在此特定應用程序中顯示過時數據的錯誤餘量的判斷來選擇到期時間。 - 繼續你的業務邏輯——處理數據,返回一個值,不管是什麼情況。
- 當您再次需要數據時,例如在下一頁加載時,請在確定您需要從 API 獲取數據之前,使用
get_transient()在瞬態緩存中檢查它。
我認為這是一個有用且可行的基礎,但如果您考慮一下 REST 動詞,您可以更進一步。 在五種最常用的方法(GET、POST、PATCH、PUT、DELETE)中,只有一種屬於您的瞬態緩存。 你能猜出是哪一個嗎? 這是獲取。 在我的插件中,我幾乎總是有一個 PHP 類專門用於抽像對相關遠程 API 的調用,並且在實例化該類時的一個參數是 HTTP 方法。 如果它不是 GET 調用,那麼我根本不會調用任何緩存層。
此外,如果它不是一個 GET 調用,那麼有理由認為我正在採取一些行動以某種方式更改遠程數據,可能是通過添加、編輯或刪除電子郵件訂閱者。 這可能是通過delete_transient()使該資源的現有緩存失效的好時機。
回到我們的 WordPress 電子郵件訂閱 API 示例,以下是它在實踐中的工作方式:
- 用於顯示最近訂閱者的儀表板小部件將通過 GET 請求調用
/subscribers的 API 端點。 因為它是一個 GET 請求,所以它被存儲在我的臨時緩存中。 - 用於訂閱電子郵件列表的側邊欄小部件將通過 POST 請求調用
/subscribers的 API 端點。 因為它是一個 POST 請求,它不僅會避免我的瞬態緩存,還會促使我刪除我的瞬態緩存的相關部分,以便儀表板小部件反映這個新訂閱者。 - 在命名瞬變時,我經常通過在我調用的遠程 API URL 之後字面上命名它們來組織它們。 這是識別要刪除的正確瞬態的便捷方法。 如果它是一個帶有參數的端點,我會將它們連接成一個字符串並將它們也添加到瞬態名稱中。
作為客戶或其他技術含量較低的利益相關者,您應該在應用程序從遠程服務中提取數據的任何時候專門請求臨時緩存(或至少對其進行討論)。 您應該熟悉出色的 Query Monitor 插件,以了解瞬態是如何工作的。 它將為您提供一個界面,用於瀏覽哪些數據作為瞬態存儲、存儲頻率和存儲時間。
有時瞬態還不夠好
一些高級 WordPress 託管服務實際上不允許您在生產中使用瞬態。 他們運行的代碼可能以 MU 插件或其他腳本的形式運行,這將攔截您嘗試使用瞬態 API 並通過對象緩存存儲該信息。 WP-Engine 在其最常見的配置中就是一個典型的例子。
如果您只是簡單地存儲和檢索數據,您實際上不必關心這一點,甚至可能永遠不會注意到它正在發生。 整個*_transient()函數係列將為您提供相同的最終結果,只是過濾為使用對象緩存而不是瞬態緩存。 但是,在嘗試刪除瞬變時,您可能會遇到問題。 這就是為什麼。
如果您的 API 集成足夠複雜,值得擁有自己的設置頁面,您可能希望包含一個 UI 以允許管理員用戶清除插件的整個臨時緩存。 此按鈕最常見的用途是客戶端直接在遠程服務上更改某些數據,並希望使我們存儲在 WordPress 中的緩存無效。 如果客戶端更改帳戶憑據、API 密鑰或只是作為“恢復出廠設置”按鈕進行調試,此按鈕也可能會派上用場。
即使您足夠聰明地命名所有臨時鍵,以便您有希望為delete_transient()識別它們中的每一個,最好的情況可能仍然涉及原始 SQL,我在 WordPress 中總是盡量避免:
<?php // Purge all the transients associated with our plugin. function purge() { global $wpdb; $prefix = esc_sql( $this -> get_transient_prefix() ); $options = $wpdb -> options; $t = esc_sql( "_transient_timeout_$prefix%" ); $sql = $wpdb -> prepare ( " SELECT option_name FROM $options WHERE option_name LIKE '%s' ", $t ); $transients = $wpdb -> get_col( $sql ); // For each transient... foreach( $transients as $transient ) { // Strip away the WordPress prefix in order to arrive at the transient key. $key = str_replace( '_transient_timeout_', '', $transient ); // Now that we have the key, use WordPress core to the delete the transient. delete_transient( $key ); } } ?> 不方便,效率不高。 相反,這種情況需要對象緩存,因為對象緩存為我們提供了一種將緩存值組合在一起的便捷方式。 這樣,當您需要清空與插件相關的所有緩存值時,只需對wp_cache_delete( $key, $group )進行簡單的單行調用。
我會這樣總結所有這些:如果您還不是管理數據緩存的專家,那麼您就不能成為使用 API 的專家。
作為客戶端,需要注意的關鍵是暫存環境和生產環境之間的異常緩存行為。 換句話說,儘管在暫存中測試一批新工作始終是一個好習慣,但緩存在生產中也必須同樣小心地進行測試。
遠程 API 可以幫助通知您的 PHP 類層次結構
在為我的插件設計各種 PHP 類時,我經常發現模仿 API 端點的定義方式很有幫助——例如,以下端點似乎有什麼共同點?

- https://api.example-email-service.com/v1/subscribers.json
- https://api.example-email-service.com/v1/lists.json
- https://api.example-email-service.com/v1/campaigns.json
它們都返回collections ,我的意思是 GET 請求的結果,返回零對多的結果,其中每個結果都是數組的成員。 這聽起來很明顯,但我發現它對我的 PHP 代碼中的以下類結構很有幫助:
-
class.collection.php,一個抽像類 class.subscribers.php擴展了抽像類Collection。-
class.lists.php擴展了抽像類Collection。 -
class.campaigns.php擴展了抽像類Collection。
抽像類將一個查詢參數數組作為其唯一參數:諸如分頁、排序列、排序順序和搜索過濾器之類的東西。 它將具有用於常見任務的方法,例如調用遠程 API、處理錯誤,以及可能將結果轉換為 HTML <select>菜單或 jQueryUI AutoSuggest。 實例化抽像類的類可能很短,可能只是指定要在*.json API 端點 URL 中使用的字符串。
同樣,以下端點有什麼共同點?
- https://api.example-email-service.com/v1/subscribers/104abyh4.json
- https://api.example-email-service.com/v1/lists/837dy1h2.json
- https://api.example-email-service.com/v1/campaigns/9i8udr43.json
他們都返回一個item ,我的意思是一個特定的、唯一的集合成員:比如一個特定的電子郵件訂閱者、一個電子郵件列表或一個電子郵件活動。 因此,我喜歡在我的 PHP 代碼中使用以下結構:
-
class.item.php,一個抽像類 class.subscriber.php擴展了抽像類Item。-
class.list.php擴展了抽像類Item。 -
class.campaign.php擴展了抽像類Item。
抽像類將使用一個字符串作為其唯一參數,以標識所請求的特定項目。 再一次,被實例化的類可能很短,可能只是指定要在*/duy736td.json中使用的字符串。
構建類繼承的方法有很多,但即使您採用與我上面概述的方法不同的方法,我敢打賭遠程 API 的結構很有可能有助於了解應用程序的結構。
作為客戶,糟糕架構的一個常見症狀是當您發現自己不得不在應用程序中一遍又一遍地請求相同的更改。 例如,如果您要求報告每頁返回 100 個結果而不是 10 個,並且您必須不斷重複該請求以獲取訂閱者報告、活動報告、取消訂閱報告等,您可能會檢測到糟糕的類架構。 在這種情況下,值得詢問您的團隊是否會從重構週期中受益:一組工作的目標不是改變產品的行為,而是改進底層代碼,以便更容易改變行為未來的產品。
WP_Error的完美用例
我很尷尬地承認,在我的代碼中正確開始使用WP_Error系列函數花了我數年的時間。 我傾向於只編寫自己的方式,或者假設永遠不會有錯誤值得以編程方式關注,或者根據具體情況處理它們。 使用遠程 API 就像激光束一樣打破了這種心態,因為它為使用WP_Error提供了一個極其方便和強大的用例。
回想一下,我之前提到過我經常有一個 PHP 類,其目的是向遠程 API 發出 HTTP 請求。 當您剝離所有樣板、所有數據操作、所有次要問題時,該類實際上歸結為調用wp_remote_request()以便從 API 獲取 HTTP 響應對象。 方便的是,如果調用由於某種原因無法執行, wp_remote_request()將改為返回WP_Error ,但如果調用成功返回了不受歡迎的 HTTP 響應呢?
例如,也許我們調用了/lists.json端點,但這個特定帳戶還沒有設置任何列表。 這將返回一個有效的 HTTP 響應,但狀態代碼為 400。雖然這本身並不是一個致命錯誤,但從一些希望將此 API 調用轉換為下拉菜單的前端代碼的角度來看,400 可能也是一個WSOD! 因此,我發現對wp_remote_request()的結果進行一些額外的解析很有幫助,畢竟可能會返回一個WP_Error :
<?php function call() { $response = wp_remote_request( $this -> url, $this -> args ); $code = wp_remote_retrieve_response_code( $response ); $first_digit = $code[0]; $good_responses = array( 2, 3 ); if( ! in_array( $first_digit, $good_responses ) { $body = wp_remote_retrieve_body( $response ); $out = new WP_Error( $code, $body ); } else { $out = $response; } return $out; } ?> 這種模式可以幫助簡化調用調用者類的代碼,因為我們知道在繼續輸出之前我們可以安全地依賴is_wp_error() 。
作為客戶,你應該偶爾扮演惡意用戶、困惑用戶和不耐煩用戶的角色。 以不應該使用的方式使用該應用程序。 做你的開發人員似乎不希望你做的事情。 注意會發生什麼。 您是否收到有用的錯誤消息? 您是否收到任何錯誤消息? 如果不是這樣,可能值得贊助一些圍繞更好的錯誤處理的工作。
ob_get_clean()的美麗調試能力
現代可編程網絡,幾乎每個站點都使用其他站點的 API,並且本身通過自己的 API 使用,已經成為一個非常強大的代碼舞台。 但正是這種品質也讓它變得相當緩慢。
遠程 HTTP 請求通常是給定頁面加載中最耗時的部分。 出於這個原因,許多 API 驅動的組件通過 Ajax 或 cron 執行。 例如,搜索電子郵件訂閱者列表的自動建議可能應該在每次擊鍵時按需 ping 遠程數據源,而不是在頁面加載時加載 DOM 中的所有 100,000 個訂閱者。 如果這不是一個選項,也許大型查詢可以在每晚的 cron 任務上同步,以便可以從本地鏡像而不是遠程 API 中提取結果。
這種方法的問題在於它可能難以調試。 不是簡單地打開WP_DEBUG並讓錯誤消息滾動到瀏覽器窗口中,而是卡在瀏覽器網絡控制台中,或者在 cron 任務(希望如此?)正在執行時跟踪日誌文件。 我覺得這很不舒服。
改善這種情況的一種方法是對error_log()進行謹慎和戰略性的調用。 但話說回來,日誌記錄的一個常見問題是,對於大型或繁忙的應用程序,錯誤日誌可能會變得過大或增長過快,無法用於監控或解析。 因此,我們必須對我們記錄的內容進行選擇性處理,就像對待我們的實際應用程序邏輯一樣多考慮。 很遺憾花時間記錄一些奇異的邊緣情況錯誤,這些錯誤似乎只在一些不常見的 cron 任務中間歇性發生,只是為了意識到錯誤的真實性質再次避開了你,因為你沒有記錄一些特定的數組成員,比如說,有問題的價值。
因此,我的理念變成了,我並不總是記錄,但當我這樣做時,我會記錄所有內容。 換句話說,在識別出一個特別令人擔憂的函數之後,我會用盡可能寬的網絡記錄它:
<?php function debug( $bug ) { ob_start(); var_dump( $bug ); $out = ob_get_clean(); error_log( $out ); } ?> 這相當於var_dump()將整個錯誤值放入錯誤日誌文件中的單個條目中。
作為客戶端,值得定期檢查應用程序的總文件內存使用情況。 如果您發現自己突然違反了託管帳戶中的存儲限制,則很有可能是錯誤日誌失控了。 您的開發人員將從專注於更好日誌記錄的工作週期中受益——您的客戶也將受益!
不完全是點擊誘餌,但它會做
請原諒本文的列表結構。 我無法將這些觀點強加到更統一的文章主題中,因為這些模式非常通用:它們適用於任何 JSON REST 端點和任何 WordPress 輸出。
它們是我反复看到的模式,無論遠程 API 是什麼,或者我們在 WordPress 中使用它做什麼。 我已經將所有這些類型的原則收集到一個插件樣板中,這極大地加速了我的工作。 您是否為每個項目保留了相似的點? 請分享它們,以便我可以竊取它們並將它們添加到我的樣板中!
