生产环境必须禁用 composer update,因其会重解析依赖树、破坏 composer.lock 一致性、引入未测试变更;部署应严格使用 composer install --no-dev --optimize-autoloader --no-interaction --locked。

生产环境必须禁用 composer update
线上项目一旦稳定,composer update 就该从部署流程里彻底消失。它不是“升级手段”,而是“破坏性操作”——哪怕只改一行 composer.json,也可能触发整棵依赖树重解析,导致 monolog/monolog 从 2.10 升到 3.0(BC break)、guzzlehttp/guzzle 切换 major 版本、甚至因某个间接依赖的 PHP 版本约束失败而中断构建。
真实风险点:
-
composer.lock被本地误更新但未提交,CI 拉取的 lock 文件和代码不匹配 - 某包发布新 patch 版本后,
^2.9约束自动拉取 2.9.5,结果该版本有未声明的 Redis 连接池 bug -
laravel/framework升级后,未同步更新laravel/octane,导致php artisan octane:start启动失败
正确做法:所有变更必须显式声明,且仅限最小范围。
- 只更新指定包:
composer update laravel/framework --with-dependencies - 安全审计优先:
composer audit查出高危漏洞后,再针对性composer require vendor/package:^x.y.z - 紧急修复时加
--dry-run预览变更,确认无意外升级再执行
锁文件不是摆设,是上线前的强制校验点
composer.lock 不是生成完就完事的快照,它是生产环境部署的准入凭证。部署脚本里漏掉 --locked,等于把门锁换成贴纸。
关键行为差异:
- 没加
--locked:即使composer.json里写"php": "^8.1",lock 里记的是"8.3.5",也能强行装下去 - 加了
--locked:lock 里任何包的版本若不满足composer.json的约束(比如 lock 记"3.0.0",json 写"^2.0"),直接报错退出
上线命令必须带这四个参数:composer install --no-dev --optimize-autoloader --no-interaction --locked。少一个,都可能让缓存失效、类加载变慢、或引入 dev-only 插件。
全局包别碰生产环境
你服务器上装的 composer global require laravel/installer 或 phpunit/phpunit,和线上应用完全无关。它们只影响本地开发体验,却常被误当成“系统级依赖”混进部署流程。
典型翻车现场:
- CI 机器上执行了
composer global update,结果把deployer/deployer升到 v7,而项目里写的 deploy.php 是 v6 语法,部署脚本直接挂掉 - 某运维同学为图省事,在生产机上全局装了
hirak/prestissimo,结果 PHP 8.2 下插件崩溃,composer install卡死在 DNS 解析
解决方案很直接:
- CI/CD 流水线里禁止任何
composer global命令 - 生产机上删掉
~/.composer目录(或至少清空vendor/bin) - 需要的 CLI 工具(如
phpunit、phpstan)全部改用项目内安装:composer require --dev phpunit/phpunit
PHP 版本升级时,autoload 优化会暴露隐藏问题
当你把 PHP 从 8.1 升到 8.2,再跑 composer install --optimize-autoloader,突然爆出一堆 Class not found,别急着 rollback。大概率是旧代码里一直靠“未优化 autoload”的容错机制侥幸运行——比如类文件没按 PSR-4 规则放,或 autoload-dev 里声明了测试类路径,但生产用了 --no-dev。
排查顺序:
- 先确认该类是否在
composer.json的autoload或autoload-dev里正确声明 - 检查类名和文件路径是否严格匹配(
App\Models\User必须对应app/Models/User.php) - 运行
composer dump-autoload -o --classmap-authoritative强制重建 classmap,看是否还报错 - 如果只在
--no-dev下出错,说明问题出在autoload-dev规则里,得把真正要用的类挪到autoload
最易忽略的一点:Laravel 11 默认启用 APP_URL 严格校验,php artisan storage:link 没跑,Storage::url() 就返回空——这种问题在未优化 autoload 时可能被其他逻辑掩盖,一开优化立刻暴露。


















