composer install 是多环境一致的唯一保障,因其严格按 composer.lock 还原精确版本、哈希、URL 和依赖树;composer update 则重解析依赖,易引发版本跃迁与兼容问题,导致线上 Class not found 等故障。

用 composer install,不是 composer update——除非你明确要升级依赖。锁文件失效、环境行为不一致、线上报 Class not found,八成是因为有人在部署机上手抖敲了 composer update。
为什么 composer install 才是多环境一致的唯一保障
composer install 读取 composer.lock,逐字还原每个包的精确版本、哈希、dist URL 和嵌套依赖树;composer update 则完全忽略 lock 文件,重新跑一遍依赖解析器,哪怕只改了一个 ^8.0 到 ^8.1,也可能导致整个子依赖链重排——比如 guzzlehttp/guzzle 从 7.8.x 升到 8.0.0,而某个私有 SDK 并未适配。
常见错误现象:
- 本地开发能跑,CI 构建通过,但上线后 PHP Fatal error: Uncaught Error: Class "GuzzleHttp\Exception\ConnectException" not found
- Git 合并时手动编辑
composer.lock,结果composer install报JSON decode error或静默跳过某些包 - 不同成员执行
composer update后提交了各自版本的 lock,导致团队里实际运行着至少三种不同的monolog/monolog行为
composer.lock 冲突了,别手改,直接重建
composer.lock 是依赖图的二进制快照,不是普通配置文件。它包含 packages 数组顺序、dist.sha256 校验值、version_normalized 归一化字段——任何手动删
正确重建流程:
- 先确保
composer.json已合并完成(比如已拉取origin/main并解决其冲突) - 删掉本地
vendor/和composer.lock - 运行
composer update --no-install:只生成新 lock,不装包,可快速检查是否出现意外降级或跳过 - 确认输出无异常后,
git add composer.lock并提交,描述中注明变更点(如 “lock: bump league/flysystem to ^3.24 for PHP 8.3 compat”)
生产部署必须加 --no-dev,但不是为了“删掉测试代码”
composer install --no-dev 的作用是跳过 require-dev 里声明的包(如 phpunit/phpunit、laravel/pint),但它**不阻止 autoload-dev 自动加载规则生效**。如果业务代码里写了 use Tests\TestCase; 或硬引用了 Orchestra\Testbench 类,运行时照样报错。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
真正隔离 dev 依赖的方式只有两种:
- 代码层:把测试基类、辅助断言等全部移到
tests/目录下,且不被autoload规则覆盖 - 部署层:Dockerfile 中固定写
RUN COMPOSER_DEV_MODE=0 composer install --no-dev --optimize-autoloader,避免 CI 脚本漏参数把 debugbar 打进镜像
注意:--no-dev 不等于“禁用测试类自动加载”,它只是不安装那些包。autoload-dev 的映射仍存在,只要类被加载就会触发 fatal。
依赖冲突卡住时,优先用 composer why-not 定位,别急着删 lock
当 composer update 卡在 Resolving dependencies 或报 Conclusion: don't install laravel/framework v10.32.0,说明约束无交集。此时盲目删 vendor/ 或 composer.lock 只会让问题更难复现。
实操步骤:
- 运行
composer why-not guzzlehttp/guzzle:^8.0,看哪几个包在阻断它(比如spatie/laravel-backuprequireguzzlehttp/guzzle:^7.0) - 再用
composer why spatie/laravel-backup追一层依赖链,确认它是直接 require 还是被其他包间接拉入 - 检查
composer show guzzlehttp/guzzle,看当前实际安装的是哪个版本,再对比它的require列表是否与冲突源矛盾 - 若确定要升,优先改
composer.json中可控包的约束(如把"spatie/laravel-backup": "^8.0"改成"^9.0"),而不是对guzzlehttp/guzzle强加^8.0约束
最易被忽略的一点:conflict 字段是全局生效的。哪怕你没 require laravel/framework,只要某个已装包(比如 orchestra/testbench)间接拉入了它,且你的 composer.json 里写了 "conflict": {"laravel/framework": ">=11.0"},Composer 就会拒绝解析——这和你想“提醒自己别升级”完全不是一回事。

















