WordPress插件開發(fā)教程手冊(cè) — 任務(wù)計(jì)劃 WP Cron
本文詳細(xì)介紹了WordPress中的WP-Cron系統(tǒng),解釋了其工作原理、與Unix Cron的區(qū)別以及如何使用它來調(diào)度基于時(shí)間的任務(wù)。文章還提供了創(chuàng)建自定義時(shí)間間隔和計(jì)劃周期性任務(wù)的代碼示例,幫助開發(fā)者更好地利用WP-Cron功能。
Cron 是基于時(shí)間的任務(wù)調(diào)度系統(tǒng),可以在 Unix 系統(tǒng)上使用,WP-Cron 是 WordPress 中處理任務(wù)的基于時(shí)間的任務(wù)調(diào)度系統(tǒng),WordPress 的幾個(gè)核心功能如檢查更新和定制發(fā)布功能,都使用了 WP-Cron 系統(tǒng)。
WP-Cron 的工作原理是,在頁面加載的時(shí)候,檢查計(jì)劃任務(wù)列表以查看需要運(yùn)行的任務(wù),計(jì)劃中的任務(wù)將在頁面加載時(shí)執(zhí)行。WP-Cron 和 Unix 系統(tǒng)的 Cron 運(yùn)行方式不一樣,他只在頁面加載時(shí)觸發(fā)。如果我們安排了一個(gè)任務(wù)在下午 2:00 運(yùn)行,而在下午 5:00 之前沒有發(fā)生任何頁面加載,則該計(jì)劃任務(wù)不會(huì)運(yùn)行。
為什么使用 WP-Cron
為什么使用 WP-Cron 呢?很多虛擬主機(jī)不允許用戶訪問系統(tǒng) Cron,但 WordPress 核心和許多插件需要一個(gè) Cron 系統(tǒng)來執(zhí)行基于時(shí)間的任務(wù)。Cron 是一個(gè)非常有用的工具,因此 WordPress 提供了通過頁面加載觸發(fā)的基于時(shí)間的 WP-Cron 系統(tǒng)。雖然該系統(tǒng)不一定會(huì)在特定的時(shí)間運(yùn)行,WP-Cron 也有一定的幾率完成我們的計(jì)劃任務(wù)。 使用 WordPress API 來設(shè)置 Cron 任務(wù)比在 WordPress 之外使用其他方法設(shè)置更加簡(jiǎn)單。
如果 Unix 系統(tǒng)的任務(wù)沒有在特定的時(shí)間執(zhí)行,他就永遠(yuǎn)不會(huì)執(zhí)行了。而 WP-Cron 將在下一次頁面加載的時(shí)候執(zhí)行過期而沒有運(yùn)行的任務(wù),不管這個(gè)任務(wù)過期過久。任務(wù)將一直在隊(duì)列中,直到有一個(gè)頁面加載時(shí)觸發(fā)他們,因此,通過 WP-Cron 設(shè)置的計(jì)劃任務(wù)不會(huì)丟失。
理解 WordPress 任務(wù)計(jì)劃
Unix 系統(tǒng)的 Cron 在特定的時(shí)間運(yùn)行(比如,每個(gè)小時(shí)的第五分鐘),與系統(tǒng) Cron 不同,WP-Cron 使用時(shí)間間隔來模擬系統(tǒng) Cron,首先給第一個(gè)任務(wù)一個(gè)運(yùn)行時(shí)間,然后設(shè)置一個(gè)時(shí)間間隔,以秒為單位,之后按照時(shí)間間隔重復(fù)任務(wù)。例如,我們安排了一個(gè)下午 2:00 開始,間隔 300 秒(5分鐘)的計(jì)劃任務(wù),該任務(wù)首先在下午 2:00 運(yùn)行一次,然后在下午 2:05 運(yùn)行,然后每隔 5 分鐘運(yùn)行一次。
為了簡(jiǎn)化計(jì)劃任務(wù),WordPress 提供了三個(gè)默認(rèn)時(shí)間間隔和一個(gè)添加自定義間隔的簡(jiǎn)單方法。我們可以創(chuàng)建一個(gè)過濾器來添加自定義時(shí)間間隔,如:
add_filter( 'cron_schedules', 'example_add_cron_interval' );
function example_add_cron_interval( $schedules ) {
$schedules['five_seconds'] = array(
'interval' => 5,
'display' => esc_html__( 'Every Five Seconds' ),
);
return $schedules;
}
此過濾器函數(shù)創(chuàng)建了一個(gè)新的時(shí)間間隔,允許我們每 5 秒鐘運(yùn)行一次 Cron 任務(wù)。
調(diào)度 WP Cron 事件
計(jì)劃周期性任務(wù)
為了讓我們的任務(wù)執(zhí)行,我們需要?jiǎng)?chuàng)建一個(gè)自定義鉤子,并掛載一個(gè)回調(diào)函數(shù)到這個(gè)鉤子上,這是非常重要的一步,忘了這一步,我們的任務(wù)計(jì)劃永遠(yuǎn)不會(huì)被執(zhí)行。
下面的示例中,我們將創(chuàng)建一個(gè)鉤子,第一個(gè)參數(shù)是鉤子的名稱,第二個(gè)參數(shù)是掛載到鉤子上的回調(diào)函數(shù)的名稱。
add_action( 'bl_cron_hook', 'bl_cron_exec' );
現(xiàn)在,我們來進(jìn)行實(shí)際的任務(wù)調(diào)度。另一個(gè)需要注意的事情是 WP-Cron 在調(diào)度任務(wù)時(shí)比較簡(jiǎn)單,沒有去重的功能,任務(wù)是由為任務(wù)提供的鉤子驅(qū)動(dòng)的,但是如果多次調(diào)用 wp_schedule_event() ,及時(shí)使用相同的鉤子名稱,該任務(wù)也會(huì)被多次調(diào)用。如果我們的代碼在每個(gè)頁面皆在時(shí)執(zhí)行任務(wù),可能導(dǎo)致該任務(wù)被執(zhí)行很多次,這可不是個(gè)好主意。WordPress 提供了一個(gè) wp_next_scheduled() 函數(shù)來幫助我們檢查一個(gè)特定的任務(wù)是否已經(jīng)被調(diào)度。
wp_next_scheduled() 接收一個(gè)參數(shù)——鉤子的名稱,返回一個(gè)包含下一個(gè)執(zhí)行時(shí)間戳的字符串,或 false 用來表示任務(wù)沒有被調(diào)度,使用方法如下:
wp_next_scheduled( 'bl_cron_hook' );
我們使用 wp_schedule_event() 來完成重復(fù)任務(wù)調(diào)度,該函數(shù)需要三個(gè)必需的參數(shù),一個(gè)附加參數(shù)。我們可以用附加參數(shù)傳遞給一個(gè)數(shù)組給執(zhí)行 WP-Cron 任務(wù)的回調(diào)函數(shù)。我們將重點(diǎn)介紹前三個(gè)參數(shù),參數(shù)如下:
- $timestamp – 首次執(zhí)行此任務(wù)的 Unix 時(shí)間戳
- $recurrence – 執(zhí)行任務(wù)的重復(fù)時(shí)間間隔的名稱
- $hook – 用來調(diào)用的自定義鉤子名稱
我們將使用 5 秒的時(shí)間間隔和我們之前創(chuàng)建的鉤子來演示一下:
wp_schedule_event( time(), 'five_seconds', 'bl_cron_hook' );
記住,我們需要首先確保該任務(wù)計(jì)劃尚未被安排,完整代碼如下:
if ( ! wp_next_scheduled( 'bl_cron_hook' ) ) {
wp_schedule_event( time(), 'five_seconds', 'bl_cron_hook' );
}
取消任務(wù)計(jì)劃調(diào)度
當(dāng)我們不再需要某個(gè)計(jì)劃任務(wù)時(shí),我們可以使用 wp_unschedule_event() 函數(shù)來取消任務(wù)計(jì)劃調(diào)度,該函數(shù)有兩個(gè)參數(shù):
- $timestamp – 下一次任務(wù)的時(shí)間戳
- $hook –用來調(diào)用的自定義鉤子名稱
這個(gè)函數(shù)不僅可以取消在每個(gè)時(shí)間戳執(zhí)行的計(jì)劃任務(wù),還可以取消未來所有的重復(fù)任務(wù)計(jì)劃。如果不知道下一個(gè)任務(wù)的時(shí)間戳,可以使用 wp_next_scheduled() 函數(shù)查找,該函數(shù)需要一個(gè)參數(shù):
- $hook –用來調(diào)用的自定義鉤子名稱
把上面的代碼放在一起,看起來應(yīng)該像下面這樣。
$timestamp = wp_next_scheduled( 'bl_cron_hook' );
wp_unschedule_event( $timestamp, 'bl_cron_hook' );
當(dāng)我們不再需要某些任務(wù)計(jì)劃的時(shí)候,及時(shí)取消這些任務(wù)計(jì)劃非常重要,因?yàn)?WordPress 會(huì)繼續(xù)嘗試執(zhí)行他們。取消計(jì)劃任務(wù)的重要時(shí)機(jī)就是插件禁用時(shí),而 WordPress.org 插件目錄中的很多插件都忘記了這一點(diǎn),不能自行清理不再需要的任務(wù)計(jì)劃。如果你發(fā)現(xiàn)了一個(gè)這樣的插件,可以嘗試聯(lián)系插件作者更新他們的代碼。WordPress 為我們提供了一個(gè)名為 register_deactivation_hook() 函數(shù),允許開發(fā)人員在插件停用的時(shí)候運(yùn)行一個(gè)函數(shù),設(shè)置起來非常簡(jiǎn)單,示例如下:
register_deactivation_hook( __FILE__, 'bl_deactivate' );
function bl_deactivate() {
$timestamp = wp_next_scheduled( 'bl_cron_hook' );
wp_unschedule_event( $timestamp, 'bl_cron_hook' );
}
掛載 WP-Cron 到系統(tǒng)任務(wù)調(diào)度器
如我們上面介紹的,WP-Cron 的執(zhí)行依賴于頁面加載,不一定會(huì)嚴(yán)格定期執(zhí)行,如果我們有關(guān)鍵任務(wù)需要必需按時(shí)執(zhí)行,這會(huì)是一個(gè)問題,幸運(yùn)的是,我們有一個(gè)簡(jiǎn)單的解決辦法來避免這個(gè)問題。只需將系統(tǒng)的任務(wù)計(jì)劃設(shè)置為按我們所需的時(shí)間間隔(或在特定的時(shí)間)運(yùn)行即可。最簡(jiǎn)單的解決辦法是使用工具向 wp-cron.php 文件發(fā)送 Web 請(qǐng)求。
在我們的系統(tǒng)上安排了任務(wù)計(jì)劃之后,WordPress 將不再需要在每個(gè)頁面加載時(shí)幫我們執(zhí)行任務(wù)計(jì)劃,我們可以禁用掉這個(gè)功能,來節(jié)約這個(gè)不必要的服務(wù)器開銷。WP-Cron 可以在 wp-config.php 中設(shè)置一個(gè)常量來禁用,在 wp-config.php 中添加以下代碼即可。
define('DISABLE_WP_CRON', true);
Windows 系統(tǒng)
Windows 系統(tǒng)中,基于時(shí)間的任務(wù)系統(tǒng)叫 Task Scheduler,我們可以通過控制面板中的 “管理工具” 找到這個(gè)系統(tǒng)。如何設(shè)置任務(wù)因服務(wù)器設(shè)置而異。一種方法是使用 PowerShell 和一個(gè)基本任務(wù),創(chuàng)建基本任務(wù)后,可以使用以下命令來調(diào)用 WordPress Cron 腳本。
powershell Invoke-WebRequest http://YOUR_SITE_URL/wp-cron.php
Mac OS X 和 Linux
Mac OS X 和 Linux 都使用 Cron 作為基于時(shí)間的調(diào)度系統(tǒng),他們通??梢允褂孟旅娴拿顝慕K端訪問。
crontab -e
應(yīng)該注意的是,任務(wù)將以常規(guī)用戶或根用戶的身份運(yùn)行,具體取決于運(yùn)行該命令的系統(tǒng)用戶。Cron 有一個(gè)特定的語法。
- 分鐘
- 小時(shí)
- 一月中的天數(shù)
- 日期
- 一周中的天數(shù)
- 需要執(zhí)行的命令

設(shè)置計(jì)劃任務(wù)時(shí),不考慮的時(shí)間段使用 * 號(hào)代替,例如,如果我們需要每隔 15 分鐘運(yùn)行一次命令,而不考慮小時(shí)、日期和月份,計(jì)劃任務(wù)設(shè)置如下:
15 * * * * command
許多服務(wù)器都自帶了 wget 工具,我們可以使用這個(gè)工具來訪問 WordPress 的 Cron 腳本。
wget http://YOUR_SITE_URL/wp-cron.php
每天凌晨觸發(fā)網(wǎng)站的 WordPress Cron 的 Unix Cron 設(shè)置如下:
0 0 * * * wget http://YOUR_SITE_URL/wp-cron.php
如何簡(jiǎn)單測(cè)試 WP-Cron
在本教程中,我們將創(chuàng)建一個(gè)插件,每 5 秒運(yùn)行一次任務(wù)并顯示一條消息。為了測(cè)試,我們直接在瀏覽器中加載 wp-cron.php 文件并將數(shù)據(jù)輸出到瀏覽器。否則我們將需要執(zhí)行一些其他操作,可能會(huì)需要操作數(shù)據(jù)庫,因?yàn)橛?jì)劃任務(wù)操作通常不會(huì)在站點(diǎn)上顯示輸出。讓我們快速完成初始步驟來獲取這一設(shè)置。
創(chuàng)建一個(gè)插件文件
在 wp-content/plugins 文件夾中創(chuàng)建一個(gè)文件夾 “my-wp-cron-test” 和 “my-wp-cron-test.php” 文件。當(dāng)然,也可以用其他名稱。打開我們剛才創(chuàng)建的 PHP 文件,開始編輯:
<?php
/*
Plugin Name: My WP-Cron Test
*/
上面的代碼設(shè)置了在 WordPress插件頁面中顯示的名稱,不要忘了啟用插件。
測(cè)試代碼
在瀏覽器中打開 YOUR_SITE_URL/wp-cron.php 頁面。
查看當(dāng)前所有已計(jì)劃的任務(wù)
WordPress 有一個(gè)未公開的函數(shù) _get_cron_array,該函數(shù)返回當(dāng)前所有計(jì)劃任務(wù)的數(shù)組,我們將使用 var_dump 函數(shù)來輸出所有計(jì)劃任務(wù)。在插件中加入以下代碼:
echo '<pre>'; var_dump( _get_cron_array() ); echo '</pre>';
放到一個(gè)容易調(diào)用的函數(shù)中如下:
function bl_print_tasks() {
echo '<pre>'; var_dump( _get_cron_array() ); echo '</pre>';
}