剖析 `wp_schedule_single_event()` 函数的源码,它是如何将定时任务数据存储在 `wp_options` 表中并由 `wp-cron` 触发的?

咳咳,各位观众老爷们,欢迎来到今天的“WordPress定时任务内幕大揭秘”现场!今天咱不聊风花雪月,就来扒一扒WordPress里那个默默耕耘的定时任务调度员——wp_schedule_single_event() 函数的底裤,看看它是怎么把任务安排得井井有条,让 wp-cron 乖乖干活的。

准备好了吗?咱们这就开讲!

一、wp_schedule_single_event():定时任务的起点

首先,咱们先来认识一下今天的主角wp_schedule_single_event()。顾名思义,这个函数的作用是安排一个单次执行的定时任务。什么叫单次执行呢?就是说,这个任务只会被执行一次,执行完就拜拜了,下次想再执行,还得重新安排。

它的函数签名是这样的:

/**
 * Schedules a single event to run only once.
 *
 * @since 2.1.0
 *
 * @param int    $timestamp  Unix timestamp (UTC) of when to run the event.
 * @param string $hook       Action hook to execute when the event is run.
 * @param array  $args       Optional. Array of arguments to pass to the hook's function. Default empty array.
 * @return bool True if the event was successfully scheduled, false otherwise.
 */
function wp_schedule_single_event( int $timestamp, string $hook, array $args = array() ) {
    // ... 函数体 ...
}

参数解释:

  • $timestamp: 任务执行的时间戳,必须是Unix时间戳,精确到秒。比如,你想让任务在2024年1月1日零点执行,那这个值就是strtotime('2024-01-01 00:00:00')
  • $hook: 要执行的动作钩子(Action Hook)。简单来说,就是你想让哪个函数来干活。这个钩子会被 do_action() 函数触发。
  • $args: 传递给钩子函数的参数数组。你想让函数在执行的时候带点啥,就放在这里面。

二、wp_schedule_single_event() 的内部逻辑:拆解定时任务的“包装”

wp_schedule_single_event() 的核心工作就是把你的定时任务信息打包,然后塞到数据库里。 具体来说,它主要做了以下几件事情:

  1. 参数校验: 首先,它会对传进来的参数进行一些基本的校验,比如时间戳是否合法,hook是否为空等等。

  2. 构建键名: 它会根据 $hook$args 生成一个唯一的键名(key),用来标识这个定时任务。这就像给每个任务贴个标签,方便以后查找和删除。

    $key = _wp_cron_string( $hook, $args ); // 生成唯一键名

    _wp_cron_string() 函数的作用是将 $hook$args 序列化成一个字符串,保证唯一性。

  3. 获取现有定时任务列表: 它会从 wp_options 表中获取已经存在的定时任务列表,通常存储在 cron 这个 option 里。这个列表是一个数组,key是时间戳,value是该时间戳下需要执行的任务的数组。

    $crons = _get_cron_array(); // 获取现有定时任务列表

    _get_cron_array() 函数实际上是从 wp_options 表中读取 cron option 的值,如果不存在,就返回一个空数组。

  4. 检查任务是否已经存在: 它会遍历现有的定时任务列表,看看是否已经存在相同 hookargs 的任务。如果存在,就直接返回 false,避免重复添加。

    foreach ( $crons as $timestamp_existing => $cron ) {
        if ( isset( $cron[ $hook ][ $key ] ) ) {
            return false; // 已经存在,直接返回
        }
    }
  5. 添加任务到定时任务列表: 如果任务不存在,它会将任务添加到定时任务列表中。具体来说,就是以 $timestamp 为键,创建一个数组,数组的键是 $hook,值是一个包含任务信息的数组。

    $crons[ $timestamp ][ $hook ][ $key ] = array(
        'schedule' => false, // 对于单次任务,schedule 字段为 false
        'args'     => $args,
    );
  6. 更新 wp_options 表: 最后,它会将更新后的定时任务列表写回到 wp_options 表中,这样 wp-cron 才能找到并执行这些任务。

    _set_cron_array( $crons ); // 更新 wp_options 表

    _set_cron_array() 函数实际上是将 $crons 数组序列化后,更新到 wp_options 表的 cron option 中。

三、wp-cron:定时任务的执行者

wp-cron 并不是一个真正的守护进程(daemon),而是一个“伪”定时任务系统。它依赖于用户的访问来触发。每次有用户访问WordPress站点时,WordPress会检查是否有需要执行的定时任务,如果有,就执行它们。

wp-cron 的核心逻辑位于 wp-includes/cron.php 文件中。简单来说,它的工作流程如下:

  1. 检查是否需要运行 wp-cron: WordPress会在每次请求时,检查是否需要运行 wp-cron。这个检查主要基于 DISABLE_WP_CRON 常量和 doing_cron action。

    • 如果 DISABLE_WP_CRON 常量被定义为 true,则 wp-cron 不会运行。
    • 如果 doing_cron action 正在执行,则 wp-cron 不会再次运行,避免重复执行。
  2. 获取定时任务列表: wp-cron 会从 wp_options 表中读取 cron option 的值,获取需要执行的定时任务列表。

  3. 遍历定时任务列表: wp-cron 会遍历定时任务列表,找到所有需要执行的任务。

  4. 执行任务: 对于每个需要执行的任务,wp-cron 会根据任务的 hookargs,调用 do_action() 函数来触发相应的动作钩子。

    do_action( $hook, ...$args ); // 执行任务
  5. 清理过期任务: wp-cron 会清理已经过期的定时任务,避免 wp_options 表过于庞大。

四、wp_options 表:定时任务的“储藏室”

wp_options 表是WordPress的核心数据表之一,用于存储各种配置信息。而定时任务的信息,就存储在这个表的 cron option 中。

cron option 的值是一个序列化的数组,其结构如下:

array(
    [timestamp] => array( // Unix 时间戳
        [hook] => array( // 动作钩子名称
            [key] => array( // 任务的唯一键名
                'schedule' => false, // 对于单次任务,为 false;对于重复任务,为 schedule 类型(hourly, daily, weekly 等)
                'args'     => array( // 传递给钩子函数的参数数组
                    // ... 参数 ...
                ),
            ),
            // ... 更多同 hook 的任务 ...
        ),
        // ... 更多 hook ...
    ),
    // ... 更多时间戳 ...
)
  • timestamp (时间戳): Unix 时间戳,表示任务应该执行的时间。
  • hook (动作钩子): 要执行的动作钩子名称。
  • key (任务键名): 通过 _wp_cron_string() 函数生成的唯一键名,用于区分相同 hook 和 args 的不同任务。
  • schedule (调度类型): 对于单次任务,这个值为 false。对于重复执行的任务,这个值表示任务的调度类型,例如 'hourly''daily''weekly' 等。
  • args (参数): 传递给钩子函数的参数数组。

五、代码示例:让任务在下周一早上8点执行

现在,咱们来用一个具体的例子,演示如何使用 wp_schedule_single_event() 函数来安排一个定时任务。

假设我们想让一个名为 my_custom_function() 的函数在下周一早上8点执行。我们可以这样写代码:

function schedule_my_task() {
    // 计算下周一早上8点的时间戳
    $next_monday_8am = strtotime( 'next monday 08:00:00' );

    // 安排定时任务
    wp_schedule_single_event(
        $next_monday_8am, // 时间戳
        'my_custom_hook', // 动作钩子
        array( 'arg1' => 'value1', 'arg2' => 'value2' ) // 参数数组
    );
}

// 在某个合适的时机调用 schedule_my_task() 函数,比如在插件激活时
add_action( 'init', 'schedule_my_task' );

// 定义 my_custom_hook 对应的函数
add_action( 'my_custom_hook', 'my_custom_function', 10, 2 );

function my_custom_function( $arg1, $arg2 ) {
    // 这里写你的任务逻辑
    error_log( 'My custom function is running! Arg1: ' . $arg1 . ', Arg2: ' . $arg2 );
}

这段代码做了以下几件事情:

  1. schedule_my_task() 函数: 这个函数负责计算下周一早上8点的时间戳,并使用 wp_schedule_single_event() 函数来安排定时任务。
  2. add_action( 'init', 'schedule_my_task' ): 在 WordPress 初始化时,调用 schedule_my_task() 函数,安排定时任务。
  3. add_action( 'my_custom_hook', 'my_custom_function', 10, 2 ):my_custom_function() 函数绑定到 my_custom_hook 动作钩子上。
  4. my_custom_function() 函数: 这个函数是实际执行的任务逻辑。这里只是简单地将一些信息写入错误日志。

六、避坑指南:使用定时任务的注意事项

  • 时间戳必须是 UTC 时间: wp_schedule_single_event() 函数期望的时间戳是 UTC 时间。如果你的服务器时区不是 UTC,你需要将时间戳转换为 UTC 时间。
  • 避免高频率的定时任务: wp-cron 依赖于用户的访问来触发,如果你的站点访问量很低,高频率的定时任务可能无法按时执行。
  • 使用 wp_unschedule_event() 删除任务: 如果你不再需要某个定时任务,可以使用 wp_unschedule_event() 函数来删除它,避免 wp_options 表过于庞大。
  • 善用日志记录: 在你的任务函数中添加日志记录,方便你调试和排查问题。
  • 考虑使用真正的 Cron: 如果你需要更可靠的定时任务系统,可以考虑使用服务器上的真正的 Cron 任务,而不是依赖于 wp-cron

七、总结:定时任务的艺术

今天,咱们深入剖析了 wp_schedule_single_event() 函数的源码,了解了它是如何将定时任务数据存储在 wp_options 表中,并由 wp-cron 触发的。希望通过今天的讲解,你对WordPress的定时任务系统有了更深入的理解。

记住,掌握定时任务的艺术,能让你在WordPress的世界里更加游刃有余,实现更多自动化和个性化的功能。

好了,今天的讲座就到这里,感谢大家的收听! 咱们下期再见! (挥手)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注