真正锁死依赖需在composer.json中写纯数字三位版本号如"monolog/monolog": "2.9.1",执行composer update monolog/monolog更新lock文件,提交composer.lock至git,并在部署时仅运行composer install;缺一不可。

依赖管理不是“装上就行”,版本控制也不是“提交 lock 就完事”——关键在 composer.json 的写法、lock 文件的生成时机、以及 Git 协作中的实际行为差异。
composer.json 里怎么写版本号才算真正锁死
写 "monolog/monolog": "2.9.1"(纯数字,无符号)才是锁死;写 "^2.9" 或 "~2.9.0" 都只是范围约束,composer update 仍会升级到 2.9.2 或 2.10.0(只要语义化版本允许)。改完必须立刻执行 composer update monolog/monolog,否则 composer.lock 不会更新,新机器上 install 还是可能装错版本。
常见错误:
- 误以为
"2.9"是精确版本 —— 不同 Composer 版本解析不一致,有的当^2.9.0,有的报错 - 只改
composer.json却没运行update,导致lock文件滞后 - CI 流水线用
composer update --no-dev覆盖了已锁定的包
为什么 composer.lock 必须提交,且不能手动改
composer.lock 是整个依赖树的哈希快照:记录每个包的确切版本、完整依赖路径、ZIP 包校验值。它不是配置文件,而是安装事实的“证据”。
不提交 lock,composer install 就退化成按 composer.json 重新解析 —— 即便你写了 "2.9.1",镜像源缓存了新版本或上游包更新了其 require,就可能装出 2.9.2。
手动修改 lock 文件无效且危险:Composer 不读取你改的字段,下次 update 或 install 会直接覆盖;若哈希值对不上,还会报 Invalid lock file 错误。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
如何防止间接依赖偷偷升级
你没 require psr/log,但 slim/slim 和 guzzlehttp/guzzle 都依赖它,最终装了 psr/log 3.0.0 导致 Class not found —— 这是间接依赖漂移的典型现象。
靠 conflict 字段硬控最可靠:
"conflict": {
"psr/log": ">=3.0.0",
"guzzlehttp/guzzle": ">=8.0.0"
}
原理是在依赖解析阶段就拒绝掉不兼容版本,而不是等装完再补救。注意:
-
conflict不影响已安装的包,只作用于下一次install或update - 别用
require-dev“假装”需要某个包来锁版本 —— 会引入不必要的 autoloading 和 dev-only 类 -
composer require --with-all-dependencies实际加剧漂移,不是解决方案
CI/CD 和多环境部署最容易忽略的点
CI 流水线必须用 composer install --no-dev --no-interaction --locked。其中 --locked 是关键:它强制校验 composer.lock 中每个包是否仍满足 composer.json 的约束,不满足直接失败,避免“本地能跑、CI 报错”的尴尬。
另外要注意平台一致性:
- Windows 和 Linux 对
ext-sockets等扩展默认状态不同,应在composer.json的config.platform中统一声明,例如:"php": "8.1.0" - Git 换行符不一致会导致
composer.lock反复变更,用.gitattributes固定为lf -
vendor/必须进.gitignore,但composer.json和composer.lock必须提交
真正的麻烦往往不出现在写代码时,而出现在某次 composer update 后没人检查 lock 是否同步、或 CI 没加 --locked 校验 —— 这些细节一旦漏掉,排查成本远高于预防成本。










