Composer install 不报版本冲突是因为它只校验 composer.lock 完整性而不重解析依赖;实际校验应使用 composer update --dry-run(可加 --with-dependencies 和 --no-dev),配合 composer validate --strict 与静态分析工具。

Composer install 时为什么没报版本冲突?
因为默认情况下 composer install 只校验 composer.lock 文件的完整性,不重新解析依赖树,也不检查已锁定版本是否仍满足 composer.json 中的约束。哪怕你手动改过 composer.json 的版本号,只要 lock 文件存在且校验通过,install 就会静默照装。
用 composer update --dry-run 检查实际会升级哪些包
这是最贴近“自动校验”的实操方式:它会完整走一遍依赖解析流程,但不写入 composer.lock 或安装文件,只输出差异。适合 CI 或本地预检。
- 运行
composer update --dry-run,看是否有包被标记为 “would update” 或报could not be resolved - 加
--with-dependencies可递归检查子依赖是否仍满足约束(尤其重要,否则可能漏掉间接冲突) - 若项目有多个
require-dev包,记得加--no-dev或保留它,取决于你校验的范围 - 注意 PHP 版本不匹配时,
--dry-run也会提前暴露:比如php: ^8.2要求下,却锁定了仅支持 8.1 的包版本
启用 composer validate + 自定义脚本校验 lock 文件时效性
composer validate 本身只检查 JSON 格式和字段合法性,不碰版本逻辑。但你可以补一层简单校验:确认 composer.lock 的 content-hash 是否与当前 composer.json 内容一致——不一致说明 json 被改过但没重生成 lock。
- 手动触发:
composer update --lock会强制重生成lock并校验一致性,失败即报错 - CI 中建议用:
composer install --no-scripts --no-autoloader && composer validate --strict,后者加--strict会检查平台配置兼容性 - 注意
composer.lock里的packages和packages-dev是分开存储的,修改require-dev后必须重新生成整个lock,不能只靠 diff
为什么 vendor/autoload.php 加载成功 ≠ 依赖版本安全?
自动加载器只管文件存在、类名映射正确,完全不验证版本语义。比如 "monolog/monolog": "^2.0" 锁定在 2.10.0,但你代码里用了 Monolog\Logger::setTimezone()(该方法 2.12 才加入),运行时才报 Call to undefined method。
- 这种问题无法靠 Composer 自动发现,必须结合静态分析工具如
phpstan或psalm配合 stubs 或 baseline - 更隐蔽的是 BC-breaking 的接口变更:比如某包从返回
array改为返回Traversable,类型声明没变,但 foreach 行为不同 - 所以“自动校验”永远只是第一道防线;真正的版本可信度,得靠 lock 文件+测试覆盖率+明确的 semver 使用约定











