composer不支持按操作系统条件安装依赖,硬写ext-inotify等os专属扩展到require会导致跨平台安装失败;应通过config.platform模拟目标环境并配合运行时extension_loaded()判断实现兼容。

Composer 本身不支持按操作系统(OS)条件安装依赖,所谓“Linux 才装 ext-inotify、Windows 才装 ext-com”这类需求,不能靠 composer.json 声明实现。硬写 "ext-inotify": "*" 到 require,会在 Windows/macOS 上直接报错退出,不是配置问题,而是设计如此。
为什么 ext-xxx 不能放进 require 做 OS 限定?
因为 Composer 的 platform-check 机制会在 install 或 update 前校验当前 PHP 环境是否满足所有 require 中声明的扩展。而 ext-inotify 在 Windows 上根本无法加载,php -m 里压根没有它——Composer 检测不到就拒绝解析依赖树,报错类似:
Your requirements could not be resolved to an installable set of packages. ext-inotify is missing from your system.
这不是 bug,是预期行为。它不区分“开发用”还是“运行时用”,只要声明了,就要求环境具备。
-
ext-posix、ext-pcntl、ext-sysvshm等同理,都是 Unix-only 扩展 - 哪怕你只在 Linux 容器里跑,本地 Windows 开发机执行
composer install也会失败 - 把它们挪到
require-dev只能缓解开发场景,但若包本身在require里写了这些,你仍会被拦住
config.platform 是唯一可控的“模拟”手段
config.platform 不是让 Composer “识别系统”,而是让它“假装目标环境有某些扩展”,从而绕过 platform-check,让依赖解析能继续。但它只影响 composer update 阶段的依赖选择,对 composer install 无效——除非你先在目标平台生成 composer.lock。
- 必须嵌套在
"config": {}下,不能放顶层 - 扩展名必须带
ext-前缀,且大小写、连字符与php -m输出完全一致(ext-xdebug✅,ext-xdebg❌) - 值可以是
true、"1.0.0"等非空字符串,Composer 只检查“是否存在”,不校验版本 - 改完后必须运行
composer update --lock,否则composer.lock仍是旧规则锁定的
示例(告诉 Composer:“就当我有 ext-posix,别拦我”):
"config": {
"platform": {
"ext-posix": "1.0.0"
}
}
真正该做的事:把 OS 差异下沉到代码层
依赖层面无法做 OS 条件安装,那就别强求 Composer 做它不该做的事。OS 相关逻辑应该由代码自己判断,而不是靠安装时“必须存在”。
- 用
extension_loaded('inotify')或function_exists('inotify_init')替代无条件调用 - Linux 专用功能封装成独立服务类,启动时检测并 fallback 到通用实现
- CLI 工具中避免硬编码
cp、rm -rf,改用symfony/filesystem这类跨平台抽象 - 二进制依赖(如 Chrome、FFmpeg)不要靠 Composer 安装,改用
spatie/browsershot这类包的 runtime 下载机制,或通过 Docker 统一提供
CI/CD 和多平台协作中最容易被忽略的点
很多人以为设了 config.platform 就万事大吉,结果 CI 构建失败或线上运行报错。问题往往出在:composer.lock 是谁生成的、在哪生成的、有没有被提交。
- Windows 开发者提交的
composer.lock,很可能不含 Linux 才能装的包分支(比如依赖ext-gmp的某个版本) -
--ignore-platform-reqs可临时跳过检查,但绝不能进 CI 脚本或提交到仓库 - Docker 构建时,如果
php -m和宿主机不一致(如 CLI vs FPM),composer install结果可能和运行时不匹配 - 最稳的做法:所有
composer.lock必须在目标部署环境(或镜像内)生成并提交











