composer outdated --direct 能暴露三类关键风险:安全更新、主版本跳变(如monolog从v1→v3)、约束失效导致包未被检测到;它仅检查composer.json中显式声明的依赖,避免传递依赖干扰。

composer outdated --direct 能看出啥关键信息
它只检查 composer.json 里你亲手写的 require 和 require-dev,不被传递依赖带节奏。真实项目里,这一步能立刻暴露三类风险:
- 哪些包有安全更新(标着
security标签) - 哪些已跳主版本(比如
monolog/monolog显示从v1.26.1→v3.0.0) - 哪些被约束卡死根本没出现在列表里(常见于用了
replace或conflict的包)
常见错误现象:composer outdated 空输出,但其实是因为配置了 "minimum-stability": "stable" + "prefer-stable": true,把候选更新全过滤掉了。临时删掉这两行再跑一次,才看得真。
如果项目用了 config.platform,outdated 的结果是按模拟平台算的,不是你本地 PHP 版本的真实兼容性——别信它,得在目标环境上再验。
升级前必须验证测试套件是否 clean exit
80% 的“升级失败”其实是测试本身早就不通过了,只是你没发现。别跳这步:
- 先跑
vendor/bin/phpunit(或php artisan test、./vendor/bin/phpspec),确认它能正常结束,不报Class 'Tests\TestCase' not found这类 autoload 错误 - 检查
phpunit.xml里的bootstrap是否指向vendor/autoload.php或项目自定义入口 - Laravel 项目务必确认
APP_ENV=testing已设,且config/database.php中testing数据库连接可用 - 不确定 autoload?先
composer dump-autoload再试
用 --with-dependencies 控制影响范围
直接 composer update 是最危险的操作,连 phpunit/phpunit 都可能升到不兼容版,测试直接挂。应该锁死范围:
- 想升级
guzzlehttp/guzzle?就跑composer update guzzlehttp/guzzle --with-dependencies,让 Composer 自动拉起它必需的关联包(比如psr/http-client),但不动其他分支 - 执行前加
--dry-run看预估变更;升级后对比composer.lock,理想变更 ≤ 5 行;若上百行,说明波及面太大,得暂停、分拆 - 绝对别用
composer update --lock:它只重写 lock 文件,不装包,完全暴露不了运行时类加载失败或接口变更问题
回归验证要盯住 composer.lock 变更和快照断言
升级后光跑测试不够,还得确认契约没被悄悄破坏:
-
git diff composer.lock必看:有没有意外升级了symfony/console这种基础组件?有没有 dev 包混进生产依赖? - 对关键逻辑做快照断言(例如用
spatie/phpunit-snapshot-assertions),比对升级前后 JSON 响应结构是否一致,防接口签名静默变更 - CI 中强制加
--no-cache,并记录每次composer outdated --minor-only的输出,方便回溯哪次引入了破坏性变更
最容易被忽略的是:升级框架大版本(比如 Laravel 9 → 11)前,composer.json 里 config.platform.php 如果还写着 "8.1",outdated 就不会提醒你某些包已要求 PHP 8.2+——这种隐性不兼容,要手动核对 changelog 才能发现。


















