不要重複自己:使用 WP-CLI 自動執行重複任務
已發表: 2022-03-11你有沒有發現自己進入 WordPress 管理區域來更新主題、插件和 WP 核心? 你當然有。 您是否被問過,“您可以創建/更新/刪除此 CSV 文件上的所有用戶嗎?” 我相信你也遇到過。 您是否嘗試過遷移網站並希望有一個插件或第三方工具可以讓您完成這項工作? 我知道我有!
有一個非常強大的工具可以幫助您完成這些任務等等。 在我告訴你之前,我想建立一個簡短的軼事。
問題:在最近的一個項目中,我需要定期重複幾個程序化任務。 其中一項任務尤其涉及根據會員級別購買或訂閱的證據更新用戶級別權限。 如果公司找不到用戶為特定會員級別支付的款項,他們希望從用戶中刪除會員級別。 為什麼需要這個? 也許一個成員停止了訂閱,但沒有觸發一個事件,因此即使他們沒有為此付費,該成員仍然可以訪問(哎呀!)。 或者也許有人在試用優惠,但該優惠已過期,客戶仍然有訂閱(也很糟糕!)。
解決方案:我沒有進入管理面板並手動刪除數百個(可能是數千個)訂閱,而是選擇使用我最喜歡的 WordPress 工具之一 WP-CLI,它通過幾次擊鍵解決了問題。
在這篇文章中,我想向您介紹 WP-CLI(假設您還不是親密的朋友),引導您完成我為這種特殊情況編寫的一個簡單的自定義命令,並為您提供一些使用 WP-CLI 的想法和資源自己的發展。
什麼是 WP-CLI?
如果您以前從未聽說過 WP-CLI,那麼您並不孤單。 該項目雖然已有幾年曆史,但似乎在 WordPress 雷達下飛行了一段時間。 以下是官方網站上關於 WP-CLI 是什麼以及做什麼的簡要說明:
WP-CLI 是一組用於管理 WordPress 安裝的命令行工具。 您無需使用網絡瀏覽器即可更新插件、設置多站點安裝等等。
以下命令向您展示了 WP-CLI 的強大功能:
-
wp plugin update --all
更新所有可更新的插件。 -
wp db export
導出數據庫的 SQL 轉儲。 -
wp media regenerate
為附件重新生成縮略圖(例如,在您更改主題大小之後)。 -
wp checksum core
驗證 WordPress 核心文件沒有被篡改。 -
wp search-replace
搜索並替換數據庫中的字符串。
如果您在此處探索更多命令,您將看到每個 WordPress 開發人員或站點維護人員每天或每週執行的重複性任務都有大量可用命令。 這些命令在一年中為我節省了無數小時的指向、單擊和等待頁面重新加載的時間。
你確信嗎? 準備好開始了嗎? 偉大的!
您將需要在您的 WordPress 中安裝 WP-CLI(或在您的本地計算機上全局安裝)。 如果您尚未在本地開發環境中安裝 WP-CLI,可以在此處的網站上找到安裝說明。 如果您使用 Varying Vagrant Vagrants (VVV2),則包括 WP-CLI。 許多託管服務提供商的平台上也包含 WP-CLI。 我會假設你已經成功安裝了這個。
使用 WP-CLI 解決問題
為了解決重複性任務的問題,我們需要為我們的 WordPress 安裝提供一個自定義的 WP-CLI 命令。 向任何站點添加功能的最簡單方法之一是創建插件。 我們將在本例中使用插件主要有以下三個原因:
- 如果我們不需要它,我們將能夠關閉自定義命令
- 我們可以輕鬆地擴展我們的命令和子命令,同時保持模塊化。
- 我們可以維護跨主題甚至其他 WordPress 安裝的功能。
創建插件
要創建插件,我們需要在wp-content
目錄中的/plugins
目錄中添加一個目錄。 我們可以將此目錄toptal-wpcli
。 然後在該目錄中創建兩個文件:
-
index.php
,應該只有一行代碼:<?php // Silence is golden
-
plugin.php
,這是我們的代碼所在的地方(你可以隨意命名這個文件。)
打開plugin.php
文件並添加以下代碼:
<?php /** * Plugin Name: TOPTAL WP-CLI Commands * Version: 0.1 * Plugin URI: https://n8finch.com/ * Description: Some rando wp-cli commands to make life easier... * Author: Nate Finch * Author URI: https://n8finch.com/ * Text Domain: toptal-wpcli * Domain Path: /languages/ * License: GPL v3 */ /** * NOTE: THIS PLUGIN FILE WILL NOT WORK IN PRODUCTION AS IS AND IS ONLY FOR DEMONSTRATION PURPOSES! * You can of course take the code and repurpose it:-). */ if ( !defined( 'WP_CLI' ) && WP_CLI ) { //Then we don't want to load the plugin return; }
前幾行有兩個部分。
首先,我們有插件頭。 此信息被拉入 WordPress 插件管理頁面,並允許我們註冊我們的插件並激活它。 只有插件名稱是必需的,但我們應該為可能想要使用此代碼的任何人(以及我們未來的自己!)包括其餘的名稱。
其次,我們要檢查是否定義了 WP-CLI。 也就是說,我們正在檢查是否存在 WP-CLI 常量。 如果不是,我們想保釋而不運行插件。 如果它存在,我們就可以清楚地運行我們的其餘代碼。
在這兩個部分之間,我添加了一條註釋,即不應在生產中“按原樣”使用此代碼,因為某些函數是實際函數的佔位符。 如果您將這些佔位符函數更改為真實的活動函數,請隨時刪除此註釋。
添加自定義命令
接下來,我們要包含以下代碼:
class TOPTAL_WP_CLI_COMMANDS extends WP_CLI_Command { function remove_user() { echo "\n\n hello world \n\n"; } } WP_CLI::add_command( 'toptal', 'TOPTAL_WP_CLI_COMMANDS' );
這段代碼為我們做了兩件事:
- 它定義了類
TOPTAL_WP_CLI_COMMANDS
,我們可以將參數傳遞給它。 - 它將命令
toptal
分配給類,因此我們可以從命令行運行它。
現在,如果我們執行wp toptal remove_user
,我們會看到:
$ wp toptal hello hello world
這意味著我們的命令toptal
已註冊並且我們的子命令remove_user
正在工作。
設置變量
由於我們正在批量處理刪除用戶,因此我們要設置以下變量:
// Keep a tally of warnings and loops $total_warnings = 0; $total_users_removed = 0; // If it's a dry run, add this to the end of the success message $dry_suffix = ''; // Keep a list of emails for users we may want to double check $emails_not_existing = array(); $emails_without_level = array(); // Get the args $dry_run = $assoc_args['dry-run']; $level = $assoc_args['level']; $emails = explode( ',', $assoc_args['email'] );
每個變量的意圖如下:
-
total_warnings
:如果電子郵件不存在,或者電子郵件與我們要刪除的會員級別無關,我們將發出警告。 -
$total_users_removed
:我們想要統計在此過程中刪除的用戶數量(請參閱下面的警告)。 -
$dry_suffix
:如果這是一次試運行,我們想在最終成功通知中添加措辭。 -
$emails_not_existing
:存儲不存在的電子郵件列表。 -
$emails_without_level
:存儲沒有指定級別的電子郵件列表。 -
$dry_run
:一個布爾值,用於存儲腳本是否正在執行空運行 (true) 或不 (false)。 -
$level
:表示要檢查和可能刪除的級別的整數。 -
$email
:要檢查給定級別的電子郵件數組。 我們將遍歷這個數組
設置好變量後,我們就可以實際運行該函數了。 以真正的 WordPress 方式,我們將運行一個循環。
編寫函數本身
我們首先創建一個foreach
循環來循環遍歷$emails
數組中的所有電子郵件:
// Loop through emails foreach ( $emails as $email ) { // code coming soon } // end foreach
然後,我們添加一個條件檢查:
// Loop through emails foreach ( $emails as $email ) { //Get User ID $user_id = email_exists($email); if( !$user_id ) { WP_CLI::warning( "The user {$email} does not seem to exist." ); array_push( $emails_not_existing, $email ); $total_warnings++; continue; } } // end foreach
此檢查確保我們有一個註冊用戶使用我們正在檢查的電子郵件。 它使用email_exists()
函數來檢查是否有用戶使用該電子郵件。 如果它沒有找到使用該電子郵件的用戶,它會發出警告,以便我們在終端屏幕上知道未找到該電子郵件:
$ wp toptal remove_user [email protected] --dry-run Warning: The user [email protected] does not seem to exist.
然後將電子郵件存儲在$emails_not_existing
數組中以供稍後顯示。 然後我們將總警告加一併繼續循環到下一封電子郵件。
如果電子郵件確實存在,我們將使用$user_id
和$level
變量來檢查用戶是否有權訪問該級別。 我們將生成的布爾值存儲在$has_level
變量中:
// Loop through emails foreach ( $emails as $email ) { //Get User ID $user_id = email_exists($email); if( !$user_id ) { WP_CLI::warning( "The user {$email} does not seem to exist." ); array_push( $emails_not_existing, $email ); $total_warnings++; continue; } // Check membership level. This is a made up function, but you could write one or your membership plugin probably has one. $has_level = function_to_check_membership_level( $level, $user_id ); } // end foreach
與此示例中的大多數函數一樣,此function_to_check_membership_level()
函數是虛構的,但大多數會員插件應該具有幫助函數來獲取此信息。

現在,我們將繼續進行主要操作:從用戶那裡移除關卡。 我們將使用if/else
結構,如下所示:
foreach ( $emails as $email ) { // Previous code here... // Check membership level. This is a made up function, but you could write one or your membership plugin probably has one. $has_level = function_to_check_membership_level( $level, $user_id ); if ( $has_level ) { if ( !$dry_run ) { // Deactivate membership level. This is a made up function, but you could write one or your membership plugin probably has one. function_to_deactivate_membership_level( $level, $user_id, 'inactive' ); } WP_CLI::success( "Membership canceled for {$email}, Level {$level} removed" . PHP_EOL ); $total_users_removed++; } else { WP_CLI::warning( "The user {$email} does not have Level = {$level} membership." ); array_push( $emails_without_level, $email ); $total_warnings++; } // We could echo something here to show that things are processing... } // end foreach
如果$has_level
的值是“truthy”,這意味著用戶可以訪問會員級別,我們希望運行一個函數來刪除該級別。 在此示例中,我們將使用function_to_deactivate_membership_level()
函數來執行此操作。
但是,在我們真正從用戶那裡移除關卡之前,我們希望將該函數包含在條件檢查中,以查看這是否實際上是一個dry-run
。 如果是,我們不想刪除任何東西,只報告我們做了。 如果它不是dry-run
,那麼我們將繼續從用戶那裡刪除級別,將我們的成功消息記錄到終端,並繼續循環瀏覽電子郵件。
另一方面,如果$has_level
的值為“falsey”,這意味著用戶無權訪問會員級別,我們希望向終端記錄警告,將電子郵件推送到$emails_without_level
數組,然後繼續循環瀏覽電子郵件。
完成和報告
循環完成後,我們要將結果記錄到控制台。 如果這是一次試運行,我們想在控制台中記錄一條額外的消息:
if ( $dry_run ) { $dry_suffix = 'BUT, nothing really changed because this was a dry run:-).'; }
這個$dry-suffix
將附加到我們接下來記錄的警告和成功通知中。
最後,我們希望將結果記錄為成功消息,將警告記錄為警告消息。 我們將這樣做:
WP_CLI::success( "{$total_users_removed} User/s been removed, with {$total_warnings} warnings. {$dry_suffix}" ); if ( $total_warnings ) { $emails_not_existing = implode(',', $emails_not_existing); $emails_without_level = implode(',', $emails_without_level); WP_CLI::warning( "These are the emails to double check and make sure things are on the up and up:" . PHP_EOL . "Non-existent emails: " . $emails_not_existing . PHP_EOL . "Emails without the associated level: " . $emails_without_level . PHP_EOL ); }
請注意,我們使用的是WP_CLI::success
和WP_CLI::warning
輔助方法。 這些由 WP-CLI 提供,用於將信息記錄到控制台。 您可以輕鬆地記錄字符串,這就是我們在這裡所做的,包括我們的$total_users_removed
、 $total_warnings
和$dry_suffix
變量。
最後,如果我們確實在整個腳本運行時產生了任何警告,我們希望將該信息打印到控制台。 運行條件檢查後,我們將$emails_not_existing
和$emails_without_level
數組變量轉換為字符串變量。 我們這樣做是為了可以使用WP_CLI::warning
輔助方法將它們打印到控制台。
添加描述
我們都知道評論對其他人以及我們未來的自己有幫助,這些評論將在幾週、幾個月甚至幾年後回到我們的代碼中。 WP-CLI 提供了一個短描述(shortdesc)和長描述(longdesc)的接口,它允許我們註釋我們的命令。 在定義TOPTAL_WP_CLI_COMMANDS
類之後,我們將放在命令的頂部:
/** * Remove a membership level from a user * * ## OPTIONS * --level=<number> * : Membership level to check for and remove * * --email=<email> * : Email of user to check against * * [--dry-run] * : Run the entire search/replace operation and show report, but don't save changes to the database. * * ## EXAMPLES * * wp toptal remove_user --level=5 [email protected],[email protected], [email protected] --dry-run * * @when after_wp_load */
在 longdesc 中,我們定義了我們期望自定義命令接收的內容。 shortdesc 和 longdesc 的語法是 Markdown Extra。 在## OPTIONS
部分下,我們定義了我們期望接收的參數。 如果需要一個參數,我們將它包裝在< >
中,如果它是可選的,我們將它包裝在[ ]
中。
這些選項在命令運行時被驗證; 例如,如果我們省略了所需的電子郵件參數,我們會收到以下錯誤:
$ wp toptal remove_user --level=5 --dry-run Error: Parameter errors: missing --email parameter (Email of user to check against)
## EXAMPLES
部分包含一個示例,說明該命令在被調用時的外觀。
我們的自定義命令現已完成。 你可以在這裡看到最後的要點。
警告和改進空間
回顧我們在這裡所做的工作以了解如何改進、擴展和重構代碼非常重要。 這個腳本有很多改進的地方。 以下是一些關於可以改進的意見。
有時,我發現此腳本不會刪除它記錄為“已刪除”的所有用戶。 這很可能是由於腳本運行速度快於查詢的執行速度。 您的體驗可能會有所不同,具體取決於運行腳本的環境和設置。 解決此問題的快速方法是使用相同的輸入重複運行; 它最終會歸零並報告沒有用戶被刪除。
可以改進腳本以等待並驗證用戶已被刪除,然後再將用戶記錄為實際刪除。 這會減慢腳本的執行速度,但會更準確,您只需運行一次。
類似地,如果發現這樣的錯誤,腳本可能會拋出錯誤以提醒用戶尚未刪除關卡。
改進腳本的另一個方面是允許一次從一個電子郵件地址中刪除多個級別。 該腳本可以自動檢測是否有一個或多個級別以及一封或多封電子郵件要刪除。 我按級別獲得了 CSV 文件,因此我一次只需要運行一個級別。
我們還可以重構一些代碼以使用三元運算符,而不是我們目前擁有的更冗長的條件檢查。 為了演示,我選擇讓它更易於閱讀,但您可以隨意製作您自己的代碼。
在最後一步中,我們還可以自動將它們導出為 CSV 或純文本文件,而不是在最後一步將電子郵件打印到控制台
最後,沒有檢查以確保我們得到$level
變量的整數或$emails
變量中的電子郵件或逗號分隔的電子郵件列表。 目前,如果有人要包含字符串而不是整數,或者用戶登錄名而不是電子郵件,則腳本將無法運行(並且不會引發錯誤)。 可以添加對整數和電子郵件的檢查。
進一步自動化和進一步閱讀的想法
如您所見,即使在這個特定的用例中,WP-CLI 也非常靈活和強大,足以幫助您快速高效地完成工作。 您可能想知道,“我如何才能在我的每日和每週開發流程中開始實施 WP-CLI?”
您可以通過多種方式使用 WP-CLI。 以下是我的一些最愛:
- 無需進入管理面板即可更新主題、插件和 WP 核心。
- 如果我想測試 SQL 查詢,導出數據庫進行備份或執行快速 SQL 轉儲。
- 遷移 WordPress 網站。
- 使用虛擬數據或自定義插件套件設置安裝新的 WordPress 站點。
- 對核心文件運行校驗和以確保它們沒有受到損害。 (實際上有一個項目正在進行中,將其擴展到 WP 存儲庫中的主題和插件。)
- 編寫您自己的腳本來檢查、更新和維護站點主機(我在這裡寫過)。
WP-CLI 的可能性幾乎是無限的。 這裡有一些資源可以讓您繼續前進:
- 主要的 WP-CLI 站點:http://wp-cli.org
- WP-CLI 命令:https://developer.wordpress.org/cli/commands/
- 官方 WP-CLI 博客:https://make.wordpress.org/cli/
- WP-CLI 手冊:https://make.wordpress.org/cli/handbook/
- 進入 WooCommerce? 查看 WC-CLI:https://github.com/woocommerce/woocommerce/wiki/WC-CLI-Overview#woocommerce-commands
- 項目維護者 Daniel Bachhuber 的播客訪談:https://howibuilt.it/episode-28-daniel-bachhuber-wp-cli/