大家好,欢迎来到今天的“WordPress定时炸弹”讲座!别害怕,这里的“炸弹”不是真炸弹,而是我们今天的主角——WordPress 的定时任务。 今天我们要深入探讨 wp_schedule_single_event()
和 wp_schedule_event()
这两个“定时炸弹”的源码和工作原理,保证让你听完之后,也能成为 WordPress 定时任务的高手。
一、为什么我们需要定时任务?
想象一下,你有个博客,每天想定时发布一篇新的文章,或者每天凌晨定时清理一下数据库垃圾数据。如果每次都要手动操作,那得多累啊!这时候,定时任务就派上用场了。WordPress 的定时任务允许我们在指定的时间执行特定的代码,解放我们的双手,让 WordPress 自动完成一些重复性的工作。
二、WordPress 的定时任务机制:WP-Cron
在深入 wp_schedule_single_event()
和 wp_schedule_event()
之前,我们需要了解 WordPress 定时任务的大管家——WP-Cron。
WP-Cron 并不是一个真正的系统级别的 cron 任务。它更像是一个“伪 cron”,或者说是一个“计划任务模拟器”。 它的工作方式是:当有用户访问你的 WordPress 网站时,WP-Cron 会检查是否有需要执行的定时任务。如果有,它就会执行这些任务。
所以,如果你的网站访问量很低,WP-Cron 可能就无法按时执行任务。这就是为什么有些时候,你的定时任务会延迟执行的原因。
三、wp_schedule_single_event()
:一次性定时炸弹
wp_schedule_single_event()
函数用于安排一个 只执行一次 的定时任务。 它的用法很简单:
wp_schedule_single_event( $timestamp, $hook, $args );
$timestamp
:任务执行的时间戳(Unix 时间戳)。$hook
:与该任务关联的钩子(action hook)。当任务执行时,WordPress 会触发这个钩子。$args
:传递给钩子函数的参数(数组)。
现在,让我们深入源码,看看 wp_schedule_single_event()
到底做了什么:
function wp_schedule_single_event( $timestamp, $hook, $args = array() ) {
$crons = _get_cron_array(); // 获取当前已计划的所有任务
$key = md5( $hook . serialize( $args ) ); // 生成任务的唯一标识符
if ( isset( $crons[ $timestamp ][ $key ] ) ) {
return false; // 如果该任务已经存在,则直接返回 false
}
$crons[ $timestamp ][ $key ] = array(
'hook' => $hook,
'args' => $args,
'schedule' => false // 标记为单次事件
);
uksort( $crons, 'strnatcmp' ); // 按照时间戳排序
return _set_cron_array( $crons ); // 保存更新后的任务列表
}
-
_get_cron_array()
:获取任务列表这个函数负责从 WordPress 的
options
表中获取已计划的所有任务。任务数据存储在cron
选项中,以数组的形式存储。function _get_cron_array() { $crons = get_option( 'cron' ); if ( ! is_array( $crons ) ) { return array(); } foreach ( $crons as $timestamp => $cronhooks ) { foreach ( (array) $cronhooks as $hook => $keys ) { if ( preg_match( '/^[A-Za-z0-9_-]+$/', $hook ) === 0 ) { unset( $crons[ $timestamp ][ $hook ] ); } } if ( empty( $crons[ $timestamp ] ) ) { unset( $crons[ $timestamp ] ); } } return $crons; }
get_option( 'cron' )
:从数据库中获取cron
选项的值。- 代码检查任务的 hook 是否有效,无效的则删除。
- 如果某个时间戳下没有任何任务,则删除该时间戳。
-
md5( $hook . serialize( $args ) )
:生成唯一标识符为了防止重复添加相同的任务,
wp_schedule_single_event()
会根据$hook
和$args
生成一个 MD5 散列值作为任务的唯一标识符。这样,即使你多次调用wp_schedule_single_event()
并且参数相同,也只会添加一个任务。 -
$crons[ $timestamp ][ $key ] = ...
:添加任务到数组将新的任务添加到
$crons
数组中。 数组的结构是这样的:$crons = array( $timestamp => array( // 时间戳 $key => array( // 任务的唯一标识符 'hook' => $hook, // 钩子名称 'args' => $args, // 参数 'schedule' => false // 标记为单次事件 ) ) );
-
uksort( $crons, 'strnatcmp' )
:按时间戳排序uksort()
函数使用strnatcmp()
函数对$crons
数组的键(时间戳)进行排序。strnatcmp()
函数执行的是“自然排序”,可以正确地处理包含数字的时间戳。 -
_set_cron_array( $crons )
:保存任务列表这个函数负责将更新后的任务列表保存到 WordPress 的
options
表中。function _set_cron_array( $crons ) { return update_option( 'cron', $crons ); }
update_option( 'cron', $crons )
:将$crons
数组保存到数据库的cron
选项中。
四、wp_schedule_event()
:周期性定时炸弹
wp_schedule_event()
函数用于安排一个 周期性 的定时任务。 它的用法如下:
wp_schedule_event( $timestamp, $recurrence, $hook, $args );
$timestamp
:第一次执行任务的时间戳(Unix 时间戳)。$recurrence
:任务的执行周期(字符串)。可以是 WordPress 预定义的周期,也可以是自定义的周期。$hook
:与该任务关联的钩子(action hook)。当任务执行时,WordPress 会触发这个钩子。$args
:传递给钩子函数的参数(数组)。
WordPress 预定义的周期包括:
周期 | 描述 |
---|---|
hourly | 每小时一次 |
twicedaily | 每天两次 |
daily | 每天一次 |
weekly | 每周一次 |
同样,让我们深入源码,看看 wp_schedule_event()
做了什么:
function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array() ) {
$crons = _get_cron_array(); // 获取当前已计划的所有任务
$key = md5( $hook . serialize( $args ) . $recurrence ); // 生成任务的唯一标识符
if ( isset( $crons[ $timestamp ][ $key ] ) ) {
return false; // 如果该任务已经存在,则直接返回 false
}
$crons[ $timestamp ][ $key ] = array(
'hook' => $hook,
'args' => $args,
'schedule' => $recurrence // 存储任务的执行周期
);
uksort( $crons, 'strnatcmp' ); // 按照时间戳排序
return _set_cron_array( $crons ); // 保存更新后的任务列表
}
wp_schedule_event()
的代码与 wp_schedule_single_event()
非常相似,主要区别在于以下两点:
-
唯一标识符的生成
在
wp_schedule_event()
中,唯一标识符的生成包含了$recurrence
参数,这样可以区分相同 hook 和 args 的不同周期的事件。$key = md5( $hook . serialize( $args ) . $recurrence );
-
'schedule' => $recurrence
在
wp_schedule_event()
中,'schedule'
键的值是$recurrence
,表示任务的执行周期。
五、WP-Cron 如何执行任务?
当有用户访问你的 WordPress 网站时,wp-cron.php
会被触发(实际上是通过 HTTP 请求触发)。 wp-cron.php
文件会检查是否有需要执行的定时任务。
wp-cron.php
文件的核心代码如下:
if ( ! defined( 'DOING_CRON' ) ) {
define( 'DOING_CRON', true );
}
// 加载 WordPress 核心文件
require_once( dirname( __FILE__ ) . '/wp-load.php' );
// 检查是否禁用 WP-Cron
if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
return;
}
// 检查是否正在运行其他 WP-Cron 进程
if ( wp_doing_cron() ) {
return;
}
// 设置超时时间
if ( ! defined( 'WP_CRON_LOCK_TIMEOUT' ) ) {
define( 'WP_CRON_LOCK_TIMEOUT', 60 ); // 默认 60 秒
}
// 获取当前时间
$time_now = time();
// 获取需要执行的任务列表
$crons = _get_cron_array();
if ( empty( $crons ) ) {
return;
}
// 遍历任务列表,执行到期的任务
foreach ( $crons as $timestamp => $cronhooks ) {
if ( $timestamp > $time_now ) {
break; // 任务还未到期,退出循环
}
foreach ( (array) $cronhooks as $hook => $keys ) {
foreach ( (array) $keys as $key => $data ) {
if ( ! has_action( $data['hook'] ) ) {
continue; // 如果钩子不存在,则跳过
}
do_action_ref_array( $data['hook'], $data['args'] ); // 执行钩子函数
// 如果是周期性任务,则重新计划任务
if ( $data['schedule'] ) {
$new_timestamp = wp_next_scheduled( $hook, $data['args'] );
if ( $new_timestamp ) {
wp_unschedule_event( $timestamp, $hook, $data['args'] );
wp_schedule_event( $new_timestamp, $data['schedule'], $hook, $data['args'] );
}
} else {
// 如果是单次任务,则取消计划
wp_unschedule_event( $timestamp, $hook, $data['args'] );
}
}
}
}
-
_get_cron_array()
:获取任务列表与
wp_schedule_single_event()
和wp_schedule_event()
中使用的_get_cron_array()
函数相同,用于从数据库中获取任务列表。 -
遍历任务列表,执行到期的任务
wp-cron.php
遍历任务列表,如果任务的执行时间戳小于当前时间戳,则执行该任务。 -
do_action_ref_array( $data['hook'], $data['args'] )
:执行钩子函数do_action_ref_array()
函数用于触发与任务关联的钩子函数。$data['hook']
是钩子的名称,$data['args']
是传递给钩子函数的参数。 -
重新计划周期性任务
如果任务是周期性的,
wp-cron.php
会使用wp_schedule_event()
函数重新计划该任务,以便在下一个周期继续执行。 -
取消计划单次任务
如果任务是单次的,
wp-cron.php
会使用wp_unschedule_event()
函数取消计划该任务,以防止重复执行。
六、wp_unschedule_event()
:取消定时炸弹
wp_unschedule_event()
函数用于取消已计划的定时任务。 它的用法如下:
wp_unschedule_event( $timestamp, $hook, $args );
$timestamp
:任务执行的时间戳(Unix 时间戳)。$hook
:与该任务关联的钩子(action hook)。$args
:传递给钩子函数的参数(数组)。
让我们看看 wp_unschedule_event()
的源码:
function wp_unschedule_event( $timestamp, $hook, $args = array() ) {
$crons = _get_cron_array(); // 获取当前已计划的所有任务
$key = md5( $hook . serialize( $args ) ); // 生成任务的唯一标识符
if ( ! isset( $crons[ $timestamp ][ $key ] ) ) {
return false; // 如果该任务不存在,则直接返回 false
}
unset( $crons[ $timestamp ][ $key ] ); // 从任务列表中移除该任务
if ( empty( $crons[ $timestamp ] ) ) {
unset( $crons[ $timestamp ] ); // 如果该时间戳下没有任何任务,则移除该时间戳
}
return _set_cron_array( $crons ); // 保存更新后的任务列表
}
wp_unschedule_event()
函数首先获取任务列表,然后根据 $timestamp
、$hook
和 $args
生成唯一标识符,找到要取消的任务,并将其从任务列表中移除。最后,将更新后的任务列表保存到数据库中。
七、自定义定时任务周期
WordPress 允许我们自定义定时任务的周期。我们可以使用 cron_schedules
过滤器来添加自定义的周期。
add_filter( 'cron_schedules', 'my_custom_cron_schedule' );
function my_custom_cron_schedule( $schedules ) {
$schedules['every_five_minutes'] = array(
'interval' => 300, // 5 分钟,单位为秒
'display' => __( 'Every 5 Minutes' )
);
return $schedules;
}
'every_five_minutes'
:自定义周期的名称。'interval' => 300
:周期的间隔时间,单位为秒。'display' => __( 'Every 5 Minutes' )
:周期在 WordPress 后台显示的名称。
添加了自定义的周期后,我们就可以在 wp_schedule_event()
函数中使用它了:
wp_schedule_event( time(), 'every_five_minutes', 'my_custom_hook' );
八、一些注意事项
- WP-Cron 的可靠性问题: 由于 WP-Cron 依赖于用户访问来触发,因此如果你的网站访问量很低,定时任务可能会延迟执行。为了解决这个问题,你可以使用系统级别的 cron 任务来定期访问
wp-cron.php
文件。 - 任务的唯一性: 在添加定时任务时,要确保任务的
$hook
和$args
参数能够唯一标识该任务,避免重复添加相同的任务。 - 任务的执行时间: 要合理设置任务的执行时间,避免在网站访问高峰期执行耗时较长的任务,影响用户体验。
- 错误处理: 在编写定时任务的钩子函数时,要做好错误处理,避免因为一个任务的错误导致整个 WP-Cron 进程崩溃。可以使用
try...catch
语句来捕获异常,并记录错误日志。 - 调试定时任务: 可以使用插件,或者修改
wp-config.php
文件,增加define('WP_DEBUG', true);
和define('WP_DEBUG_LOG', true);
来开启调试模式,查看错误日志。
九、总结
wp_schedule_single_event()
和 wp_schedule_event()
是 WordPress 定时任务的核心函数。它们允许我们安排单次和周期性的任务,让 WordPress 自动完成一些重复性的工作。
通过深入了解这两个函数的源码和 WP-Cron 的工作原理,我们可以更好地利用 WordPress 的定时任务机制,提高我们的开发效率。
今天的讲座就到这里。希望大家对 WordPress 的定时任务有了更深入的了解。 记住,定时任务虽然强大,但也要谨慎使用,避免滥用导致性能问题。 感谢大家的参与!