PHP CLI脚本需用绝对路径调用cron,因cron不继承shell环境且工作目录不确定;所有%须转义为\%;脚本应设时区、错误报告并用flock()防并发。

PHP CLI脚本必须用绝对路径调用
cron 不继承你的 shell 环境,php 命令可能找不到,./script.php 也可能因工作目录不确定而失败。直接写 /usr/bin/php /var/www/app/scripts/cleanup.php 最稳妥。
常见错误现象:Command not found 或脚本报 require_once(): Failed opening required 'config.php' —— 其实是当前工作目录不是你预期的。
- 用
which php查真实路径,别信php - 所有
require/include用绝对路径,或在脚本开头加chdir(__DIR__) - 避免用
~或./,cron 不展开波浪号,也不以 crontab 所在目录为基准
cron 表达式里 % 要转义
PHP 脚本里如果用了 date('Y-m-d H:i:s') 这类含 % 的格式,在 cron 中不转义会直接被 shell 当作注释截断,导致命令只执行一半。
使用场景:日志文件名带时间戳,比如 /usr/bin/php /path/to/script.php >> /var/log/script_$(date +\%Y\%m\%d).log 2>&1。
立即学习“PHP免费学习笔记(深入)”;
- 所有
%必须写成\%(反斜杠转义) - 哪怕只是 echo 一个字符串,只要含
%就得转 - 建议把复杂逻辑封装进 PHP 脚本内部,cron 只负责触发,避免在 crontab 行里写 shell 扩展
PHP 脚本要显式处理时区和错误报告
CLI 模式的 php.ini 和 Web 模式往往不同,date.timezone 可能为空,error_reporting 可能关着,结果定时任务静默失败,连错在哪都不知道。
性能影响:不设时区会导致每次调用 date() 都触发警告(虽不影响执行,但日志刷屏);不设错误级别会让致命错误直接中断,无任何记录。
- 脚本开头加
date_default_timezone_set('Asia/Shanghai'); - 加
error_reporting(E_ALL); ini_set('display_errors', 0); ini_set('log_errors', 1); - 务必用
ini_set('error_log', '/var/log/php-cron.log');指定独立错误日志路径
避免并发冲突:加锁不能只靠文件存在判断
两个 cron 实例几乎同时启动,都判断 !file_exists('lock') 为 true,然后都写入 lock 文件 —— 经典竞态条件。PHP 的 flock() 是唯一靠谱方案。
容易踩的坑:用 touch 或 file_put_contents('lock', '') 创建锁文件,根本挡不住并发。
- 打开文件后立即
flock($fp, LOCK_EX | LOCK_NB),失败就exit - 锁文件必须保持打开状态,脚本结束前才
fclose() - 别用
sleep()重试,cron 本身就有间隔控制,重复调度比等锁更可控
复杂点在于:flock 在 NFS 上不可靠,如果脚本部署在共享存储,得换用数据库行锁或 Redis SETNX。但对绝大多数单机部署,flock 就够用,也最容易被忽略。



















