Composer本身不支持按操作系统条件安装包,唯一可控方式是通过config.platform模拟目标环境扩展能力并配合上游包规范声明的ext-xxx依赖来实现伪条件安装。

Composer 本身不支持按操作系统条件安装包
直接说结论:composer.json 里没有 os、platform-os 或类似字段能实现“只在 Linux 安装 ext-inotify”这种逻辑。Composer 的依赖解析器不读取 PHP_OS、php_uname('s') 或环境变量,它只看 require、conflict、platform 和包自身的 composer.json 声明。
所谓“某包只在特定系统安装”,实际是以下三种情况之一:
- 包作者在自己的
composer.json中写了"require": {"ext-posix": "*"},而该扩展在 Windows 上默认不可用 → Composer 在 Windows 上找不到满足条件的版本,自然跳过 - 包提供多个发行版(如 PHAR + 平台二进制),通过
bin脚本或 install hook 在运行时下载对应平台文件(例如spatie/browsershot) - 你手动用
config.platform模拟目标环境,让 Composer 主动剔除不兼容分支(这是唯一可控的“伪条件安装”)
用 config.platform 模拟目标 OS 扩展能力
这是最接近“限定安装”的做法,原理是告诉 Composer:“我最终部署在 Linux,所以请按 Linux 的扩展可用性来选包”,而不是“我现在正在 Linux 上运行”。
关键点:
-
config.platform必须写在composer.json的config对象下,不是根级 - 只对声明了
ext-xxx依赖的包生效 —— 如果上游包没写"require": {"ext-pcntl": "*"},platform就完全不起作用 - 写法示例(锁定为 Linux 环境):
{
"config": {
"platform": {
"php": "8.2.12",
"ext-posix": "8.2.12",
"ext-pcntl": "8.2.12",
"ext-sysvsem": "8.2.12"
}
}
}
这样配置后:
- 即使你在 Windows 上执行
composer install,也不会因缺失ext-pcntl报错 - Composer 会跳过所有 require 了
ext-win32service或其他 Windows-only 扩展的包版本 - 但注意:这不保证运行时可用 —— 如果你真在 Windows 上跑这段代码,
pcntl_fork()还是会失败
为什么不能靠 post-install-cmd 脚本删包?
有人想在 post-install-cmd 里判断 PHP_OS 然后 rm -rf vendor/some-linux-only-package,这看似可行,实则危险:
-
vendor/autoload.php已生成,类映射可能包含已被删除包的 PSR-4 规则,导致Class not found - 如果该包被其他已安装包硬依赖(比如
A → B → linux-only-package),Composer 不会校验其存在性,但运行时 autoload 失败 -
composer update会重新拉取,无法持久化“排除”效果 - CI/CD 流水线中多节点并行执行时,脚本执行顺序不可控,容易漏删或误删
真正需要 OS 隔离的逻辑,应该下沉到代码层:用 extension_loaded('pcntl') 或 function_exists('pcntl_fork') 运行时判断,而不是靠安装阶段硬砍。
替代方案:require-dev + CI 分离安装
如果你只是想让某些工具(比如 inotifywait 封装、systemd 控制器)只在 Linux CI 中生效,更稳妥的做法是:
- 把它们放进
require-dev,而非require - 在 Linux CI 脚本中运行
composer install --no-interaction(默认装 dev) - 在 Windows 开发机或跨平台构建中运行
composer install --no-dev - 配合
config.platform统一 PHP 版本,避免因 CLI/FPM 配置差异导致扩展检测不一致
这个组合不依赖 Composer 的“条件安装”能力,而是靠人工控制安装范围,稳定、可审计、无副作用。
真正的难点不在怎么写配置,而在于确认上游包是否规范声明了 ext-xxx 依赖 —— 很多包用 if (defined('HHVM_VERSION')) 或 class_exists() 做运行时兜底,却不写 require,这时 platform 就完全失效。


















