生产环境部署必须强制使用 composer install --no-dev --optimize-autoloader:前者杜绝 phpunit、debug-bundle 等 dev 依赖引入的安全风险与 autoload 冲突,后者通过预生成类映射将类加载耗时从 0.8–2.1ms 降至 0.03–0.07ms。

composer install --no-dev 是生产部署的硬性要求
不加 --no-dev 就上线,等于把 phpunit、larastan、symfony/debug 这类开发工具直接扔进生产环境——既拖慢 autoload,又可能因扩展缺失(如 xdebug)或配置泄露(如 debugbar 的路由)引发安全问题。
这个参数只控制「是否安装」,不影响 composer.json 里 require-dev 的存在,也不改变后续 composer update 的默认行为。它只是告诉 Composer:别碰 require-dev 里的包。
- Docker 构建时必须显式写
composer install --no-dev --optimize-autoloader - CI 脚本里跑测试前要
composer install(不带--no-dev),跑完测试再部署时必须切回--no-dev - 漏掉它,是线上 PHP 应用最常踩的依赖类故障来源之一
require-dev 不是“开发专用”,而是“非生产可选”
require-dev 字段本身没有环境语义,它只是个标记区。Composer 不会读取 APP_ENV 或 COMPOSER_DEV_MODE 去决定装不装——它只认命令行参数 --no-dev。
所以别指望“本地设 APP_ENV=local 就自动装 dev 包”,也别在代码里写 class_exists('PHPUnit\Framework\TestCase') 然后上线还指望它存在。一旦用了 --no-dev,这些类就真的不存在。
- 如果某个包在开发时需要、运行时也需要(比如
symfony/var-dumper用于日志调试),就不能放require-dev,得挪到require并配运行时开关 -
autoload-dev里的 PSR-4 规则,在--no-dev下不会被写入vendor/autoload.php,硬引用会直接Class not found - CI 报错
Class 'PHPUnit\Framework\TestCase' not found,大概率是缓存复用了上一次--no-dev的vendor/
config.platform 是解决环境差异的真正杠杆
当你的本地 PHP 是 8.3、生产是 8.1,或者本地有 ext-xdebug、线上没有,composer update 可能因为依赖声明冲突失败。这时靠改 composer.json 的 require 或拆配置文件没用——config.platform 才是让 Composer “假装”在目标环境解析依赖的正确方式。
它不装包,也不改行为,只影响依赖解析阶段的兼容性判断。
- 写死版本:
"php": "8.1.0",不能写"^8.1",否则无效 - 模拟扩展:
"ext-xdebug": "3.1.5",让 Composer 忽略扩展缺失报错 - 上线构建时更干净的做法是加
COMPOSER_PLATFORM_CHECK=0,比硬塞 platform 更可控
别碰多 composer.json 方案
用 composer.prod.json + COMPOSER=xxx composer install 听起来隔离彻底,实际破坏 composer.lock 的一致性。你本地 composer update 生成的 lock 文件,和线上用不同 json 解析出的依赖树根本对不上,轻则版本漂移,重则 Class not found 或方法不存在。
Composer 的设计哲学是“一套声明,多种安装”。所有环境都该基于同一份 composer.json 和已提交的 composer.lock 运行 composer install,靠参数和平台配置区分行为,而不是靠分裂配置。
真正难处理的,永远不是怎么装包,而是怎么让代码在 --no-dev 下不崩、在 config.platform 模拟下不误判、在 CI 缓存里不复用错 vendor —— 这些地方没日志、不报错、只静默失效。


















