15: 任务
Phlo 内置了跨应用的 cron 运行器。每个应用的系统 cron 条目每分钟触发一次 tasks::run;tasks 资源以声明方式与 %app->tasks 匹配。您的应用中没有 cron 语法,也没有外部调度程序。
15.1: 设置
三个步骤。
1. 在 data/app.json 中激活资源:
{
"resources": [..., "tasks"]
}
2. 在 app.phlo 中描述你的任务:
prop tasks => arr(
cleanup: arr(do: 'account::cleanup', every: '5 minutes'),
poll: arr(do: fn() => external::pull(), every: 'minute'),
backup: arr(do: 'backup::run', daily: '03:00'),
report: arr(do: 'report::weekly', weekly: 'monday 09:00'),
)
3. 每个应用一个 cron 条目,使用绝对路径:
* * * * * php-zts <app>/www/app.php tasks::run
将其放置在 /etc/cron.d/example-tasks(系统,6 个字段包括用户)或通过 crontab -u <user>(每用户,5 个字段)。
15.2: 日程安排
每个任务选择一个调度键:
| 键 | 格式 | 示例 |
|---|---|---|
every: |
PHP 可读的持续时间字符串 | 'minute','5 minutes','2 hours','1 day' |
daily: |
'HH:MM' |
'03:00' |
weekly: |
'<weekday> HH:MM' |
'monday 09:00' |
every: 'minute'(没有前导数字)在内部变为'1 minute'。通过 strtotime("+$every", 0) 进行解析。
15.3: 可调用的 (`do:`)
do: 字段接受三种形式:
| 类型 | 示例 | 被调用为 |
|---|---|---|
| Closure | fn() => external::pull() |
直接 |
'Class::method' |
'account::cleanup' |
account::cleanup() |
| 资源名称 | 'backup' |
phlo('backup') |
与正常请求不同,任务在 HTTP 生命周期之外运行:没有 %req,没有 %session。编写任务时请确保其自包含。
15.4: 磁盘上的状态 (`data/tasks/`)
tasks::run 会自动创建 data/tasks/ 并用三个文件保护每个任务:
| 文件 | 内容 | 何时 |
|---|---|---|
<name>.last |
原始 Unix 时间戳 | 每次成功运行时,用于到期检查 |
<name>.json |
{schedule, return} 用于 Control Center |
每次成功运行时 |
<name>.lock |
空(mtime 计数) | 在运行期间,TTL 1 小时 |
锁定防止慢任务自我重叠。TTL 故意设置为 1 小时:失败的任务会被停放,直到锁定过期;其他任务会照常运行。
15.5: 错误流程
在 tasks::run 中没有 try/catch。一个 Throwable 会冒泡到 Phlo 的框架异常处理程序,并写入 data/errors.json,就像构建错误一样。Phlo Control Center 在任务选项卡中显示它们。
15.6: Phlo 控制中心
Phlo 控制中心自动检测 data/tasks/:
- 在导航中出现一个 任务标签(仅当目录存在时),紧接在首页之后。
- 每个任务:调度(来自 JSON)、上次运行时间、返回值(类型感知:标量 / 数组 / 字符串)、锁定状态。
- 控制中心对
tasks资源和应用程序完全无关,它仅从data/tasks/读取。调度信息来自<name>.json,而不是通过应用程序路由(这会触发 HTTP 响应并干扰控制中心的渲染)。
15.7: 示例
prop tasks => arr(
heartbeat: arr(do: 'app::heartbeat', every: 'minute'),
)
static heartbeat => file_put_contents(data.'heartbeat.log', date('Y-m-d H:i:s').' tasks::run fired'.lf, FILE_APPEND | LOCK_EX)
Cron entry:
* * * * * php-zts <app>/www/app.php tasks::run
每分钟调用一次 tasks::run,检查 heartbeat 是否为 every: 'minute' 且 lastRun < 60s 之前,然后运行 app::heartbeat()。文件 data/tasks/heartbeat.last、.json 和 .lock 会被更新;在任务运行时,中间的 cron 触发会跳过该任务。