用过 Laravel 的人都知道它的任务调度有多优雅 -- 不用碰 crontab,一行代码搞定定时任务。但不是每个项目都用 Laravel,很多老项目、微服务、独立脚本还是靠手动维护 crontab,任务一多就成了灾难。php-cron-scheduler 把 Laravel 的任务调度思路提炼成了一个独立包,任何 PHP 项目都能用。

安装
composer require peppeocchi/php-cron-scheduler
整个库只有一个外部依赖:dragonmantank/cron-expression,用来解析 cron 表达式。核心代码就 3 个类,干净利落。

系统配置
只需要在 crontab 里加一条记录:
* * * * * /usr/bin/php /path/to/your/scheduler.php >> /dev/null 2>&1
这一行就够了。之后所有定时任务都在 PHP 代码里管理,再也不用碰 crontab 文件。
三种任务类型
php-cron-scheduler 支持三种方式注册任务:
执行 PHP 脚本
最常用的方式。独立进程执行,不会阻塞主调度器:
$scheduler = new \GO\Scheduler();
// 基本用法
$scheduler->php('/path/to/cleanup.php');
// 指定 PHP 解释器路径和参数
$scheduler->php(
'/path/to/report.php',
'/usr/bin/php8.2',
['-c' => '/path/to/php.ini'],
'daily-report' // 任务 ID,用于标识
);
执行 Shell 命令
直接跑系统命令:
// 数据库备份
$scheduler->raw('mysqldump -u root mydb > /backup/db.sql', [], 'db-backup');
// 清理日志
$scheduler->raw('find /var/log/app -name "*.log" -mtime +30 -delete');
执行闭包函数
在当前进程中直接执行,适合轻量级操作:
$scheduler->call(function () {
$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '');
$pdo->exec("DELETE FROM sessions WHERE last_activity < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 7 DAY))");
})->hourly();
闭包方式注意几点:它在当前进程中执行,不是后台任务,如果执行时间太长会阻塞后续任务。
时间调度
注册完任务后,用链式调用设置执行时间:
Cron 表达式
支持标准五段式 cron 表达式:
// 每天凌晨 2:30 执行
$scheduler->php('backup.php')->at('30 2 * * *');
// 每周一上午 9:00
$scheduler->php('weekly-report.php')->at('0 9 * * 1');
// 每月 1 号凌晨 3:00
$scheduler->php('monthly-report.php')->at('0 3 1 * *');
快捷方法
记不住 cron 表达式没关系,库提供了一堆语义化的方法:
// 按分钟
$scheduler->php('health-check.php')->everyMinute();
$scheduler->php('queue-worker.php')->everyMinute(5); // 每 5 分钟
// 按小时
$scheduler->php('cache-warm.php')->hourly(); // 每小时整点
$scheduler->php('sync-data.php')->hourly(30); // 每小时的第 30 分钟
// 按天
$scheduler->php('daily-job.php')->daily(); // 每天 00:00
$scheduler->php('evening-report.php')->daily(18, 30); // 每天 18:30
$scheduler->php('nightly.php')->daily('23:00'); // 每天 23:00
// 按星期
$scheduler->php('monday-task.php')->monday();
$scheduler->php('friday-report.php')->friday(18);
$scheduler->php('weekend-job.php')->sunday(12, 30);
// 按月份
$scheduler->php('monthly-invoice.php')->january(1);
$scheduler->php('yearly-review.php')->december(25, 20, 30);
这些方法可以链式调用,代码可读性比 crontab 好太多。
指定日期
还能在特定日期执行一次性任务:
$scheduler->php('launch.php')->date('2026-06-01 00:00');
$scheduler->php('event.php')->date(new DateTime('2026-12-25'));
防止重叠执行
定时任务最怕的就是上一次还没跑完,下一次又启动了。onlyOne() 方法用文件锁解决这个问题:
$scheduler->php('heavy-import.php')
->everyMinute(5)
->onlyOne();
// 自定义锁文件目录
$scheduler->php('heavy-import.php')
->everyMinute(5)
->onlyOne('/tmp/my-locks');
// 重叠时执行自定义逻辑
$scheduler->php('heavy-import.php')
->everyMinute(5)
->onlyOne(null, function ($lockFileTime) {
// 如果锁文件超过 1 小时,说明上次任务可能卡死了,允许执行
return (time() - $lockFileTime) > 3600;
});
原理很简单:任务开始时创建 .lock 文件,结束后删除。如果发现锁文件存在,就跳过本次执行。
条件执行
有些任务只在特定条件下才需要跑,用 when() 添加条件判断:
// 仅工作日执行
$scheduler->php('workday-report.php')
->daily(9)
->when(function () {
return date('N') < 6; // 1-5 是工作日
});
// 仅生产环境执行
$scheduler->php('production-sync.php')
->hourly()
->when(function () {
return getenv('APP_ENV') === 'production';
});
// 仅当数据库有新数据时执行
$scheduler->call(function () {
return processPendingOrders();
})->everyMinute()
->when(function () {
return getPendingOrderCount() > 0;
});
输出处理
任务的执行结果可以写入文件或发送邮件:
// 写入单个文件
$scheduler->php('report.php')
->output('/var/log/report-output.log');
// 写入多个文件
$scheduler->php('report.php')
->output(['/var/log/report.log', '/var/log/report-archive.log']);
// 追加模式(默认是覆盖)
$scheduler->php('report.php')
->output('/var/log/report.log', true);
// 发送邮件(需要安装 swiftmailer)
$scheduler->php('report.php')
->output('/tmp/report-output.log')
->email(['admin@example.com' => 'Admin']);
说实话,邮件功能我不推荐使用,因为它依赖的 SwiftMailer 已经废弃了。生产环境用钩子自己接通知渠道更靠谱。
前置和后置钩子
用 before() 和 then() 在任务执行前后插入自定义逻辑:
$logger = new Logger();
$scheduler->php('data-sync.php')
->before(function () use ($logger) {
$logger->info('data-sync 开始执行');
})
->then(function ($output) use ($logger) {
$logger->info('data-sync 执行完毕,输出: ' . $output);
});
实际项目中,我通常用后置钩子做失败通知 -- 接入企业微信或钉钉机器人,比邮件及时得多。
Worker 模式
调试阶段不想设 crontab,可以用 work() 方法直接在终端里跑:
$scheduler = new \GO\Scheduler();
$scheduler->php('some-job.php')->everyMinute();
$scheduler->work(); // 阻塞式循环,每分钟检查一次
也可以指定检查时间点:
// 在每分钟的第 0 秒和第 30 秒各检查一次
$scheduler->work([0, 30]);
这个模式主要用来开发调试,生产环境还是老老实实用 crontab。
完整示例
一个典型的调度文件 scheduler.php:
<?php
require __DIR__ . '/vendor/autoload.php';
use GO\Scheduler;
$scheduler = new Scheduler();
$bin = '/usr/bin/php8.2';
// 清理过期 session -- 每小时
$scheduler->call(function () {
$pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '');
$pdo->exec("DELETE FROM sessions WHERE updated_at < NOW() - INTERVAL 7 DAY");
})->hourly();
// 数据库备份 -- 每天凌晨 2:00
$scheduler->raw('mysqldump -u root app | gzip > /backup/app_$(date +\%Y\%m\%d).sql.gz')
->daily(2)
->onlyOne();
// 发送日报 -- 工作日 18:00
$scheduler->php(__DIR__ . '/jobs/daily-report.php', $bin)
->daily(18)
->when(fn() => date('N') < 6)
->output('/var/log/daily-report.log');
// 检查队列积压 -- 每 5 分钟
$scheduler->php(__DIR__ . '/jobs/check-queue.php', $bin)
->everyMinute(5)
->before(fn() => error_log('queue check start'))
->then(function ($output) {
if (trim($output) === 'ALERT') {
sendDingTalkNotification('队列积压告警');
}
});
$scheduler->run();
然后 crontab 里只加一条:
* * * * * /usr/bin/php /path/to/scheduler.php >> /dev/null 2>&1
全部搞定。新增任务改 PHP 文件就行,不用再碰 crontab。
失败处理
run() 执行后可以拿到失败的任务列表:
$scheduler->run();
foreach ($scheduler->getFailedJobs() as $failedJob) {
$job = $failedJob->getJob();
$exception = $failedJob->getException();
error_log("任务执行失败: " . $exception->getMessage());
}
不过要注意,这个库没有内置重试机制。任务失败了就是失败了,需要自己在钩子里实现重试逻辑。
有什么不足
说几个需要注意的点:
五年没发版了。 v4.0 发布于 2021 年,至今没出新版本。社区有 43 个 Open Issue,部分涉及 PHP 8.2+ 的 deprecation warnings。功能上是够用的,但如果你需要活跃维护的项目,要考虑这个风险。
不支持 Windows。 后台执行模式用了 | tee、> /dev/null 2>&1 & 这些 Unix 特有的写法,Windows 环境下只有闭包类型的任务能正常工作。
锁机制比较原始。 基于文件的 .lock 锁在单机场景够用,但如果你的应用跑在多台服务器上,需要自己加分布式锁。
没有重试机制。 任务失败后不会自动重试,需要自己在 then() 钩子里处理。
邮件功能过时。 依赖的 SwiftMailer 已经废弃,建议忽略邮件功能,用自己的通知方案。
总结
php-cron-scheduler 做到了一件事:把 Laravel 任务调度的核心体验剥离出来,给不使用 Laravel 的项目用。3 个类、1 个依赖,源码十分钟就能看完,没有学习成本。如果你的项目定时任务超过 3 个,还在手搓 crontab,用它会让生活轻松不少。唯一需要权衡的是维护活跃度 -- 功能成熟但作者不太活跃,如果你介意这一点,可以考虑 Crunz 作为替代。
原文标题: [peppeocchi/php-cron-scheduler]没有 Laravel 也能优雅管理定时任务
原文地址: https://phpreturn.com/index/a6a1682a494b4f.html
原文平台: PHP武器库
版权声明: 本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturn(PHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。