滚动更新时不能直接运行 composer update,因其默认丢弃 composer.lock 重算整棵依赖树,导致新旧实例间依赖版本混搭、序列化不兼容及静默行为变更(如 symfony/http-foundation 补丁版修改返回类型引发 fatal error);必须指定包名并配合 --with-all-dependencies 限定影响范围,且每次执行前需 --dry-run 验证,禁用无目标的全量更新。

滚动更新时为什么不能直接跑 composer update
因为 composer update 默认丢弃 composer.lock,重新协商整棵树——它不管线上还在跑的旧实例是否兼容新依赖。比如 monolog/monolog 从 2.10.0 升到 2.10.3,symfony/http-foundation 同步升到 6.4.7,看似只是补丁版,但后者悄悄改了 Request::getPort() 的返回类型(int|null → int),旧代码里没做空判断,上线后某次请求就 fatal。这不是 Composer 的错,是滚动场景下“重算”和“复现”的根本冲突。
composer update 必须带包名 + --with-all-dependencies
只写 composer update guzzlehttp/guzzle 是危险的:它只更新该包及其直系依赖(如 psr/http-message),但间接依赖(如 ralouphie/getallheaders)仍卡在 composer.lock 旧版本里,导致类加载失败或方法不存在。真正可控的做法是:
- 先确认目标包已在
composer.json的require或require-dev中显式声明 - 执行
composer update guzzlehttp/guzzle --with-all-dependencies,强制刷新整个子图 - 若需连带升级配套组件(如 Laravel 升级),加多个包名:
composer update laravel/framework illuminate/* --with-all-dependencies - 绝对不要用
composer update --with-all-dependencies不带包名——等价于全量重算,和裸update无异
每次执行前必须 --dry-run + 检查 composer outdated
--dry-run 输出里出现 downgrading 或 removing 就得立刻停手:滚动中降级包(如把 phpunit/phpunit 从 10.5.0 降到 10.4.3)极易引发测试断言失效;移除包则可能让旧实例调用已不存在的类。更关键的是,composer outdated --direct 才是你该信任的依据:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 它只列出你显式
require的包,排除传递依赖干扰 - 输出带
!的行(如symfony/console 5.4 → 6.0 !)必须人工核验 CHANGELOG 和迁移指南 - 中文环境下常因镜像源缓存导致
outdated漏报,可临时切官方源:composer config -g repo.packagist composer https://repo.packagist.org
锁文件必须按环境拆分,且禁止跨环境复用
开发机 PHP 8.2、预发机 PHP 8.3、生产机 PHP 8.3(但启用了 opcache.enable_cli=1)——同一份 composer.lock 在三个环境里解析出的实际包版本可能不同。Composer 会按当前平台 PHP 版本过滤候选包,而 lock 文件只记录“当时算出来的结果”,不记录“当时用的平台条件”。所以:
- CI 流水线里必须用
composer install,不是update - 生产部署脚本里绝不能删
composer.lock再跑install,那等于放弃一致性控制 - 如果真要重刷依赖树,唯一可靠方式是删
composer.lock+composer install --no-cache,且必须在目标环境(如生产 PHP 版本)下执行
滚动发布最易被忽略的点:你以为只是升了一个包,实际是整棵依赖子图在不同实例间错位加载——composer.lock 不是缓存,是契约;--with-all-dependencies 不是捷径,是边界。










