最可靠的方式是使用 PHP_SAPI === 'cli' 或 php_sapi_name() === 'cli',二者均被官方保证且无误判风险;避免依赖 $_SERVER['argv']、$_SERVER['SHELL'] 等不可靠变量。

如何用 php_sapi_name() 判断 CLI 环境
最可靠的方式是检查 php_sapi_name() 的返回值,它直接反映 PHP 的运行接口类型。Web 服务器(如 Apache、Nginx+FPM)下通常返回 apache2handler、fpm-fcgi 或 cgi-fcgi;而 CLI 下固定返回 cli。
注意:不能只依赖 $_SERVER['SHELL'] 或 php_uname('s'),它们在某些容器或 Windows 环境下不可靠或根本不存在。
-
php_sapi_name() === 'cli'是唯一被 PHP 官方文档明确保证的判断依据 - 该函数无需额外扩展,PHP 默认启用,无兼容性风险
- 避免写成
strpos(php_sapi_name(), 'cli') !== false—— 某些嵌入式 SAPI(如cli-server)会误判
PHP_SAPI 常量是否更简洁?
PHP_SAPI 是编译时定义的常量,值与 php_sapi_name() 相同,但更轻量——不调用函数,无运行时开销。
它适合在配置加载、框架启动早期等对性能敏感的位置使用,比如判断是否跳过 Web 相关的路由初始化。
立即学习“PHP免费学习笔记(深入)”;
- 直接比较:
PHP_SAPI === 'cli',语义清晰且执行更快 - 注意:它在所有 PHP 版本中都可用(5.4+ 更稳定),但极老版本(如 5.2)可能未定义,需兜底
- 别和
__PHP_SAPI__混淆——后者是预处理器宏,不能在运行时用
为什么 isset($_SERVER['argv']) 不推荐
虽然 CLI 下 $_SERVER['argv'] 通常存在,但它在某些 CGI/FastCGI 场景下也可能被 Web 服务器注入(尤其是 Nginx 配置了 fastcgi_param SCRIPT_FILENAME $request_filename; 并透传参数时),导致误判。
更麻烦的是,部分 Docker 镜像或 CLI 封装脚本(如 Laravel Envoy)会主动 unset $_SERVER['argv'],反而让真实 CLI 环境返回 false。
- 这不是边缘情况——Laravel 的
artisan在某些部署中就触发过此问题 - 即使加上
!empty($_SERVER['argv']),也无法排除 Web 环境伪造参数的可能性 - 它无法区分
php -r "echo 1;"(无 argv)和真正的交互式 CLI
实际项目中怎么组织判断逻辑
多数框架或命令行工具会在入口文件(如 artisan、bin/console)顶部做一次判定,后续复用布尔变量,而不是反复调用判断函数。
示例:
$is_cli = PHP_SAPI === 'cli';
if ($is_cli) {
// 加载 CLI 专用配置
define('APP_ENV', $_ENV['APP_ENV'] ?? 'console');
} else {
// 拒绝 Web 请求访问敏感命令入口
http_response_code(404);
exit;
}
- 把判断结果赋给变量后,全程用
$is_cli,避免重复计算 - 不要在循环里或高频调用函数中做此判断——尽管开销极小,但语义上没必要
- 如果脚本既支持 CLI 又支持 Web API(如调试钩子),需额外检查
$_SERVER['REQUEST_METHOD']等上下文,单靠 SAPI 不够
TERM)交叉验证,而不是只信一个条件。



















