线上部署失败常被误判为“环境问题”,实则多因依赖冲突导致vendor混装不兼容版本,如laravel/framework v10.32.0与spatie/laravel-backup v8.0.0调用v11新增的cache::lock(),表面class not found,根源是lock文件未严格验证。

线上部署前必须用 composer install --no-dev + --ignore-platform-reqs 以外的严格模式验证,否则本地能过、线上崩是常态。
为什么线上部署失败常被误判为“环境问题”
报错如 Class 'GuzzleHttp\Client' not found 或 Call to undefined method Illuminate\Support\Facades\Cache::lock(),表面看是类没加载或方法不存在,实际多是依赖冲突导致 vendor 中混装了不兼容版本——比如 laravel/framework 被锁在 v10.32.0,但 spatie/laravel-backup v8.0.0 内部调用的是 v11 才引入的 Cache::lock(),而 Composer 因为某个间接依赖卡住了升级路径,最终装进来的是一套“看似满足约束、实则运行时报错”的组合。
这类问题在线上暴露,是因为:
- 本地开发时可能开了
require-dev,顺带装了高版本辅助包,掩盖了主依赖的不兼容 - CI/CD 流程用了
--ignore-platform-reqs跳过 PHP 版本校验,结果线上 PHP 版本低,某些包的 polyfill 没生效 -
composer.lock提交前没跑composer install --no-dev验证,导致 lock 文件里混入了 dev-only 包的依赖链
上线前必须跑的三步验证命令
别只信 composer install 成功就合代码。以下命令必须在与线上一致的 PHP 版本、扩展、--no-dev 条件下执行:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
-
composer install --no-dev --dry-run:确认无冲突且所有包能解出,不碰 vendor -
composer show --tree | grep -E "(laravel|guzzle|monolog)":检查关键包是否真按预期版本安装,尤其注意带(locked to x.y.z)的行 -
php -d extension=mbstring.so -d extension=openssl.so -d extension=curl.so -m | grep -E "(mbstring|openssl|curl)":验证线上启用的扩展和本地一致,避免因扩展缺失触发 polyfill 失效
如果 --dry-run 报错,立刻停发,回退到 composer why-not 定位;如果 show --tree 显示某包版本明显低于预期(比如你写了 "guzzlehttp/guzzle": "^7.5" 却显示 locked to 7.2.0),说明有隐性阻塞,需查 composer prohibits guzzlehttp/guzzle:7.5.0。
如何让线上部署不因 lock 文件冲突雪上加霜
多人并行改依赖时,composer.lock Git 冲突几乎必然发生。手动合并 lock 文件等于把 SAT 求解器的工作交给眼睛——99% 会出错。正确做法是:
- 禁止直接编辑
composer.lock,所有变更必须来自composer update或composer require - 冲突发生后,先
git checkout --ours composer.json(保留自己的声明),再git checkout --theirs composer.json(拉取对方的),手工合并两个require和require-dev区块 - 删掉本地
composer.lock和vendor/,运行composer update --no-dev重生成 lock 文件 - 提交新
composer.lock前,务必git diff确认只有目标包及其直系依赖变动,没有意外升级symfony/console或降级phpunit/phpunit
最易被忽略的一点:composer update --no-dev 不等于“只装 prod 包”,它仍会更新 require-dev 里被 prod 包间接依赖的项(比如 phpunit 被 laravel/pint 依赖)。真要隔离,得用 composer install --no-dev + 提前删掉 require-dev 块,或者用 COMPOSER_NO_DEV=1 环境变量控制。










