
# WP-Cron 替代方案

Queuety 可以替代内置的 WordPress cron 系统。启用后，`wp_schedule_event()` 调用会被拦截，并通过 Queuety 的调度器转发，而不是使用默认的 WP-Cron 机制。事件应由 Queuety 工作进程处理，从而使任务执行不受网站流量影响。

> **当前限制：** 桥接器会创建 Queuety 计划，但工作进程没有处理它们所生成的 `__queuety_cron_{hook}` 作业的处理程序，因此原始 WordPress 回调永远不会运行。每个作业都会失败、在退避后重试，最终被搁置（[#9](https://github.com/inline0/queuety/issues/9)）。在此问题修复之前，请勿在必须运行 cron 事件的网站上启用该桥接器。

## 启用替代方案

在插件或主题初始化早期调用 `Queuety::replace_wp_cron()`：

```php
use Queuety\Queuety;

Queuety::replace_wp_cron();
```

通常会在插件的主文件中或在 `plugins_loaded` 钩子中调用此函数。调用时必须已加载 WordPress（插件必须处于启用状态）。

## 拦截内容

cron 桥接器会挂接到三个 WordPress 过滤器：

| WordPress 函数 | 挂接的过滤器 | 行为 |
|---|---|---|
| `wp_schedule_event()` | `pre_schedule_event` | 创建间隔相同的 Queuety 计划 |
| `wp_unschedule_event()` | `pre_unschedule_event` | 移除相应的 Queuety 计划 |
| `wp_get_scheduled_event()` | `pre_get_scheduled_event` | 从 Queuety 返回计划数据 |

只有**重复执行**的事件（即具有 `schedule` 属性，例如 `hourly`、`twicedaily`、`daily` 的事件）会被拦截。使用 `wp_schedule_single_event()` 计划的一次性事件不会被拦截。

## 事件映射方式

插件调用 `wp_schedule_event()` 时，Queuety 会通过 `wp_get_schedules()` 解析计划名称，创建一个处理程序名称为 `__queuety_cron_{hook}` 的 Queuety 计划，并将原始 WordPress 钩子名称及参数存储在计划载荷中。

预期的行为是：处理该计划作业的 Queuety 工作进程会使用原始钩子名称和参数调用 `do_action()`，使现有的 WordPress 回调在 Queuety 的调度下运行。`CronBridge::process_cron_event()` 实现了这项调用，但目前还没有任何代码调用它（参见上文的限制）。计划也只会由长期运行的 `wp queuety work` 进程评估，大约每分钟一次（参见[调度](https://queuety.inline0.dev/zh/docs/features/scheduling)）。

### 间隔解析

桥接器会先通过 `wp_get_schedules()` 解析计划名称，然后回退到内置默认值：

| 计划名称 | 间隔 |
|---|---|
| `hourly` | 3,600 秒 |
| `twicedaily` | 43,200 秒 |
| `daily` | 86,400 秒 |
| `weekly` | 604,800 秒 |

只要 `wp_get_schedules()` 返回通过 `cron_schedules` 过滤器注册的自定义计划间隔，就支持这些间隔。

## 恢复 WP-Cron

要恢复使用默认的 WordPress cron 系统：

```php
Queuety::restore_wp_cron();
```

这会移除过滤器钩子，并让 WordPress 再次通过其内置机制处理 cron 事件。请注意，安装桥接器时会将 `DISABLE_WP_CRON` 定义为 `true`，且运行时无法取消定义，因此你可能需要从 `wp-config.php` 中移除该常量，或重启进程。

## 适用场景

当网站流量较低、需要比 WP-Cron 更可靠的定时机制，或者已经运行 Queuety 工作进程并希望让 cron 执行也纳入同一套运维系统时，替代方案最有用。它也有助于集中监控，因为此时 cron 事件会与其他队列任务一起显示在相同的日志和指标中。

## 限制

这里有几项重要限制。必须有一个正在运行的 Queuety 工作进程，否则计划事件永远不会运行。只有重复计划会被拦截；使用 `wp_schedule_single_event()` 创建的一次性事件仍在桥接器之外。`replace_wp_cron()` 还会为当前 PHP 进程的剩余生命周期定义 `DISABLE_WP_CRON`，因此 `restore_wp_cron()` 会移除钩子，但无法取消定义该常量。最后，只有在加载了 WordPress 时桥接器才会工作，因为它依赖 WordPress 过滤器和 `do_action()`。

## 示例：完整配置

```php
use Queuety\Queuety;

// In your plugin's main file.
add_action( 'plugins_loaded', function () {
    Queuety::replace_wp_cron();
} );
```

此处无需调用 `Queuety::init()`：已启用的 Queuety 插件会根据 WordPress 数据库凭据建立连接，并在 `plugins_loaded` 时初始化自身。`replace_wp_cron()` 只负责安装过滤器，因此不依赖上述初始化是否已完成。

完成此操作后，插件通过 `wp_schedule_event()` 注册的重复事件会作为 Queuety 计划存储。在 [#9](https://github.com/inline0/queuety/issues/9) 修复之前，这些事件的回调不会运行。
