生产环境崩溃主因是Composer未真正锁定版本:^2.4.1允许升至2.99.99,composer.lock被.gitignore过滤或缺失会触发fallback为update;应使用精确版本如"2.11.0"、强制提交lock文件、CI校验其存在,并用composer show验证单版本。

生产环境崩溃,90%以上不是代码写错了,而是composer install没真锁住版本——你以为写了^2.4.1就安全了,其实它允许装到2.99.99;你以为提交了composer.lock就万事大吉,结果它被.gitignore悄悄过滤掉了。
为什么composer install有时还是升了版本?
因为它根本没执行“安装”,而是 fallback 成了composer update——这不是 bug,是 Composer 的默认退化逻辑:
-
composer.lock文件缺失、被.gitignore忽略,或git status显示已修改但未提交 →install自动重算依赖 -
composer.json新增了 require 项,但没运行composer update更新 lock → install 会报错或强行求解 - CI 脚本里误用了
composer install --no-lock(该参数已废弃,但旧流水线还在用) - PHP 版本或扩展不匹配,触发 platform check 失败,Composer 放弃 lock 直接重解析
怎么写composer.json才算真正锁定?
别信“推荐用^”的教程。生产环境要的是确定性,不是兼容性:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 精确版本号必须不带任何符号:
"monolog/monolog": "2.11.0",等价于==2.11.0 - 绝对不用
^2.11.0(允许2.99.99)、~2.11(允许2.11.99)、*或dev-main - 如果必须保留语义化升级空间(如内部工具库),用
~2.11.0而非^2.11,它至少能卡死次版本(2.11.x) -
conflict比require更硬:"conflict": { "symfony/console": ">=6.0.0" }会直接阻止安装,哪怕其他包依赖它
composer.lock不是缓存,是部署契约
它记录的不只是版本号,还有每个包的 SHA-256 hash、下载 URL、安装路径和平台约束。它的存在本身,就是锁定生效的前提:
- 必须
git add composer.lock并 commit —— 不是“可选”,是强制 - CI 流水线第一行加:
test -f composer.lock || (echo "LOCK MISSING" >&2; exit 1) - 验证是否真锁住了:
composer show monolog/monolog | grep versions输出应只有一行2.11.0,不能有逗号分隔的多个版本 - 本地改完
composer.json后,别直接composer update——先composer update monolog/monolog --dry-run看它会不会动其他包
团队协作中最容易被忽略的一点
不是怎么写版本号,而是谁在什么时候动了composer.lock。Git 提交前没人检查 diff,导致 lock 文件里php字段写着"8.1.10",而服务器跑的是8.1.22,结果composer install静默失败,日志只报platform requirements not met,连具体哪条不满足都不说。盯住git status composer.lock,比记住所有命令都管用。

















