让PHP CLI脚本真正跑起来需明确处理输入、退出、错误和环境差异:先用php script.php测试,显式调用exit(),避免依赖$_SERVER超全局变量,用getopt()或symfony/console解析参数,输出区分STDOUT/STDERR,注意CLI与Web的配置、权限、时区等差异。

PHP CLI 脚本不是 Web 脚本的简单复制粘贴,直接在终端跑 php script.php 会失败,除非你明确处理了输入、退出、错误和环境差异。
怎么让 PHP 脚本在命令行里真正跑起来
关键不是加个 #!/usr/bin/env php 就完事——很多系统没配好 shebang 或 PATH,反而掩盖真实问题。先确保脚本能被 PHP 解释器正确加载:
- 文件开头不用写
#!/usr/bin/env php,先用php script.php测试;等稳定后再加 shebang 并chmod +x - 必须显式调用
exit()或die()结束,否则 CLI 模式可能因输出缓冲或未捕获异常静默退出 - 避免依赖
$_SERVER['DOCUMENT_ROOT']或$_SERVER['REQUEST_URI'],CLI 下它们不存在或为空 - 用
php -S启动的是内置 Web 服务器,和 CLI 模式无关;CLI 脚本不走 HTTP 协议栈,也不经过 Apache/Nginx
读取命令行参数:别硬解析 $argv
$argv 是原始数组,$argc 是长度,但手动判断 --help、-v、--input=file.txt 容易出错。推荐用标准方式:
- PHP 8.1+ 直接用
$_SERVER['argv'](和$argv等价),但别假设索引 0 是脚本名——某些 SAPI 下可能不同 - 简单场景用
getopt():比如getopt('hv', ['help', 'verbose', 'file:']),冒号表示该选项需参数 - 复杂 CLI 工具建议用
symfony/console,它自动处理帮助文本、子命令、参数绑定和类型校验,比手写健壮得多 - 注意:Windows 下长选项(
--option)可能被 cmd 截断,优先测试 PowerShell 或 WSL
输出与交互:别用 echo 和 print_r 直接怼
CLI 输出不是 HTML,没有浏览器渲染兜底。颜色、换行、进度条、用户输入都要主动控制:
立即学习“PHP免费学习笔记(深入)”;
- 用
fwrite(STDOUT, "text\n")替代echo,避免被输出缓冲干扰;错误信息统一走STDERR:fwrite(STDERR, "error\n") -
print_r($data, true)才返回字符串,直接print_r($data)会立即输出并可能破坏 JSON/CSV 等结构化输出 - 需要用户确认时,用
fgets(STDIN)读一行,记得rtrim()去掉换行符;不要用readline()除非已启用扩展 - 彩色输出靠 ANSI 转义序列,如
"\033[32mOK\033[0m",但先检测是否支持:function_exists('posix_isatty') && posix_isatty(STDOUT)
常见报错和兼容性坑
CLI 和 Web SAPI 的配置默认不同,很多“本地能跑线上挂”的问题根源在这里:
-
PHP Fatal error: Uncaught Error: Call to undefined function curl_init()—— CLI 使用独立的php.ini,路径通常是$(php --ini | grep "Loaded Configuration File" | cut -d' ' -f6),检查扩展是否启用 -
Warning: file_get_contents(): failed to open stream: Permission denied—— CLI 进程用户(如www-data或当前 shell 用户)对文件无读写权限,和 Web 用户身份无关 -
Allowed memory size of X bytes exhausted—— CLI 默认内存限制常为 -1(不限),但某些共享主机或容器会设低值,用ini_set('memory_limit', '512M')覆盖 - 日期相关函数(
date()、strtotime())在 CLI 下默认时区是 UTC,不是date.timezone配置值,务必调用date_default_timezone_set('Asia/Shanghai')
CLI 脚本最难的不是语法,而是脱离 Web 环境后对“运行上下文”的重新认知:没有请求生命周期、没有自动清理、没有隐式错误页面。每个 echo、每次 file_put_contents、每处未捕获的异常,都直接暴露给终端用户——这既是约束,也是可控性的来源。



















