config.platform是唯一能稳定控制Composer依赖解析目标环境的机制,必须严格写在composer.json的config对象内,如"config": {"platform": {"php": "8.1.25", "ext-mbstring": "*"}},仅影响install/update阶段依赖选择,不改变运行时行为;写错位置、漏配扩展或未删lock/vendor均导致失效。

config.platform 是唯一能稳定控制 Composer 依赖解析目标环境的机制,必须写在项目 composer.json 的 config 对象内,否则完全不生效。
platform 必须嵌套在 config 对象里,写错位置就等于没写
很多人把 platform 直接写在 composer.json 根层级,比如和 require 并列,结果 CI 构建失败、本地 install 成功但线上报错。原因很简单:Composer 只识别 "config": { "platform": { ... } } 这一精确路径。
- ✅ 正确结构:
"config": { "platform": { "php": "8.1.25", "ext-mbstring": "*" } } - ❌ 错误写法:
"platform": { "php": "8.1.25" }(顶层,被忽略) - ❌ 错误写法:
"config": { "platform.php": "8.1.25" }(键名非法) - ❌ 错误写法:
"config": { "platform": "8.1.25" }(值必须是对象,字符串静默失效)
验证是否生效:运行 composer config --list | grep platform,看到带 (local) 标记才算对。
只配 php 版本远远不够,扩展必须手动列全
config.platform 不读你本地的 php.ini 或 php -m 输出,它只认你明确写进去的项。漏一个扩展,composer install 就可能中断,或装上运行时根本不存在的包。
- 常见必须补全的扩展:
"ext-mbstring": "*"、"ext-openssl": "*"、"ext-json": "*"、"ext-sodium": "*"、"ext-pdo": "*" - 扩展名严格为
ext-xxx,大小写敏感:ext-curl✅,ext-CURL❌ - 版本号填
*多数够用,但某些包(如symfony/cache)会检查扩展版本 ≥ 某值,这时建议写死,如"ext-redis": "5.3.7"
注意:"php": "8.1.25" 不代表 ext-sodium 自动“存在”;如果某包声明了 "ext-sodium": "*",而你没写,composer install 就会报错退出。
改完 platform 后,必须删 lock 和 vendor 才能真正生效
composer.lock 缓存了上次解析的依赖树,哪怕你改了 config.platform,composer install 默认仍复用旧记录——等于白改。
- 最稳妥做法:删掉
composer.lock和vendor/,再运行composer install - 或强制重解:运行
composer update --with-all-dependencies - 别用
--ignore-platform-reqs应对解析失败——它绕过所有约束,可能装上 PHP 8.2 才有的函数调用,部署到 8.1 环境直接Fatal error
CI/CD 中尤其要注意:脚本里若存在 --ignore-platform-reqs,config.platform 形同虚设。
platform 只影响 install/update 阶段,和运行时完全无关
这是最容易被忽略的一点:config.platform 是编译期约束,不是运行时模拟。它不安装任何扩展,也不改变 phpversion() 返回值,更不会让 match 表达式在 PHP 7.4 上跑起来。
- 哪怕你设成
"php": "7.4.0",本地 PHP 8.3 依然能跑,但只要代码用了match,部署到真 7.4 环境就必然崩 -
config.platform只决定“哪些包能进依赖树”,不决定“代码能不能执行” - 宝塔、Docker 等多 PHP 环境下,
which php和对应/www/server/php/82/bin/php -m才决定函数是否存在——platform再准也救不了真实环境缺失











