要在 Yii2.0.48 中让定时任务真正自动执行,必须完成控制台命令编写和系统 crontab 配置两件事,缺一不可;只写 PHP 逻辑不配 crontab,任务永远不会触发;只配 crontab 不写可调用的命令类,会直接报错“Unknown command”。

要在 Yii2.0.48 中让定时任务真正自动执行,必须完成控制台命令编写 + 系统 crontab 配置两件事,缺一不可;只写 PHP 逻辑不配 crontab,任务永远不会触发;只配 crontab 不写可调用的命令类,会直接报错“Unknown command”。
创建可被调用的控制台命令
第一步:在 app/commands/ 目录下新建一个 PHP 类文件,比如 CleanController.php,确保命名符合 PSR-4 规范且文件名与类名一致。
第二步:类必须继承 yii\console\Controller,方法名以 action 开头(如 actionOldLogs),否则无法通过 php yii clean/old-logs 调用。
第三步:在方法体内写清理逻辑,例如删除 30 天前的日志文件。不要在 init() 或构造函数里做 DB 查询或目录扫描——这些操作可能阻塞命令启动,导致 crontab 超时失败。
第四步:添加日志记录,用 Yii::info('Start cleaning old logs');,并确认 config/console.php 中已配置 log 组件,且 logFile 指向 @app/runtime/logs/cron.log 可写路径。
验证命令能否手动运行
切换到项目根目录,执行 php yii clean/old-logs。
如果看到输出或日志中出现预期信息,说明命令本身没问题;如果报错 Unknown command,检查类文件是否放在 app/commands/、命名是否带 Controller 后缀、命名空间是否为 app\commands。
这一步必须成功,否则 crontab 里写的全是无效指令。
配置 crontab 实现自动执行
方法一:使用绝对路径 + flock 防并发
运行 crontab -e,新增一行:
flock -n /tmp/clean_old_logs.lock -c "/usr/bin/php /var/www/myapp/yii clean/old-logs > /dev/null 2>&1"
其中 /usr/bin/php 是服务器上 PHP 的真实路径,可用 which php 查;/var/www/myapp/yii 是你项目根目录下的 yii 入口脚本,不是 yii.php 也不是 yii.bat。
方法二:cd 进入项目根目录再执行(更稳妥)
在 crontab 中写成:
0 2 * * * cd /var/www/myapp && /usr/bin/php yii clean/old-logs >> /var/www/myapp/runtime/logs/cron.log 2>&1
这样能避免因工作目录错误导致的相对路径加载失败,比如 @app 别名解析异常、配置文件找不到等问题。
【必须重定向输出,否则 cron 默认发邮件,没配 MTA 就会积压失败任务】
方法三:加 timeout 防卡死
如果清理逻辑可能耗时过长(比如扫描数万文件),在命令前加超时限制:
0 2 * * * timeout 600 /usr/bin/php /var/www/myapp/yii clean/old-logs >> /var/www/myapp/runtime/logs/cron.log 2>&1
单位是秒,这里设为 10 分钟,超时后进程强制终止,防止下次调度进来时两个实例同时跑。
测试 crontab 是否生效
第一步:先设一个临时高频任务,比如每分钟执行一次:
* * * * * cd /var/www/myapp && /usr/bin/php yii clean/old-logs >> /var/www/myapp/runtime/logs/test-cron.log 2>&1
第二步:等一分钟后检查 test-cron.log 是否有新日志生成;没有就说明 crontab 没生效或路径写错。
第三步:用 crontab -l 确认刚加的行确实在列表里;用 ps aux | grep cron 确认 cron 服务正在运行。
第四步:删掉测试行,换回正式时间表达式,比如 0 2 * * * 表示每天凌晨 2 点执行。


















