Composer 不支持限定包仅在 CLI 下安装,因其安装过程始终运行于 PHP CLI 模式且无法感知 Web SAPI;运行时环境控制需通过 PHP\_SAPI 判断、require-dev 分离或扩展依赖间接约束实现。

Composer 本身不支持限定包仅在 CLI 下安装
你无法在 composer.json 中写一条规则,让某个包“只装在 CLI 环境、不装在 Apache/FPM”,因为 Composer 的依赖解析和安装过程完全运行在 PHP CLI 模式下——它根本不知道你的 Web SAPI 是什么。所谓“限定运行环境”,实际是运行时控制,不是安装时控制。
想让某包只在 CLI 下生效,得靠 autoloading + 运行时判断
典型场景:你用 symfony/console 写命令行工具,但不希望它被 Web 请求加载(避免 autoload 开销或意外触发)。这不是 Composer 能管的,而是靠 PHP 自身逻辑拦截:
- 不要把该包放进
require,改放require-dev——它不会出现在生产环境的vendor/里(除非你运行composer install --no-dev时漏了) - 在入口文件(如
bin/my-command)开头加判断:if (PHP_SAPI !== 'cli') { die('This script only runs in CLI mode.'); } - 如果必须进
require(比如包同时提供 CLI 和 Web 功能),就在具体类或函数里检查:if (!in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { throw new RuntimeException('Not allowed in web context'); }
为什么 config.platform.php 不能模拟 SAPI 类型
Composer 的 platform 配置只支持 php、ext-xxx 和极少数系统变量(如 lib-* ),它不识别 php-sapi 或 PHP_SAPI。试图写 "platform": {"php-sapi": "cli"} 会被忽略,既不报错也不生效。
有些项目会误以为通过 config.platform.ext-posix 或 ext-pcntl 就能间接约束 CLI,但这是错的:这些扩展在 FPM 下也可能启用(比如用于信号处理),且它们的存在 ≠ 当前是 CLI。
立即学习“PHP免费学习笔记(深入)”;
真正影响 SAPI 兼容性的点,藏在扩展声明里
你声明 "ext-posix": "*" 或 "ext-pcntl": "*",本质是在说“本项目代码调用了这些扩展的函数”。而这些函数在大多数 Web SAPI(如 FPM、Apache)中默认禁用或行为受限(例如 pcntl_fork() 在 FPM 中直接失败)。所以,这类扩展的 presence 实际上成了 CLI 的软性门槛——不是 Composer 限定的,是 PHP 运行时拦住的。
容易踩的坑:
- 写了
"ext-pcntl": "*",但没意识到它在 Web 环境下根本不可用,结果上线后Call to undefined function pcntl_fork()直接 fatal - 用
composer install --ignore-platform-reqs绕过检查,导致 vendor 装上了,但一跑就崩 - 本地 CLI 有
ext-posix,CI 环境没装,测试通过但线上挂掉——这种差异比 PHP 版本更隐蔽
最稳妥的做法:把真正依赖 SAPI 行为的逻辑(如 fork、readline、signal 处理)抽成独立服务或命令,并明确文档标注「仅限 CLI」,别指望 Composer 替你做运行时守门。



















