~1.2.3 表示最小版本兼容,锁定在 1.2.x 范围内,允许修订号任意增长;^1.2.3 则锁定在 1.x.x 范围内,允许次版本和修订号增长。
波浪号
和折线号
在 Composer 的
版本约束中行为完全不同,选错一个符号可能导致依赖升级失败、意外降级,甚至生产环境崩溃。
为什么
实际锁定在
范围内
表示“最小版本兼容”,它按「主版本.次版本.修订号」三级结构做保守允许:只要主版本和次版本不变,就允许修订号任意增长。所以
等价于
(缺修订号)等价于
常见误用:写成
却期望它能升到
—— 不会,它连
都允许,但坚决卡死在
之前
适合场景:你明确依赖某个次版本的 API 兼容性,比如 Laravel 的
,只接受
的安全补丁
注意:如果包没遵循语义化版本(例如把
当作破坏性更新发布),
无法防护这种违规
为什么
可能跳到
甚至
表示“向后兼容升级”,它的规则是:允许主版本不变的前提下,次版本和修订号任意增长;但如果主版本为 0,则只允许修订号增长(即
≡
关键边界:对于
,它等价于
、
都合法,但
不行
危险点:如果某包把
当作重大变更发布(违反 semver),
仍会装它,Composer 不校验实际变更内容
真实案例:曾有团队因
拉入
,结果该版本悄悄废弃了一个方法,CI 通过但线上报
时它们如何影响实际安装行为
这两个符号只在运行
或首次
时起作用;一旦
写入具体版本,后续
就完全忽略约束,只认 lock 文件。
执行
:仅更新该包,按其
中定义的
或
规则找最新匹配版本
执行
(无参数):所有包都按各自约束重新评估,可能批量升级——这时
的包更容易“跨次版本”跃进
陷阱:如果你本地
文件里是
,但
写的是
,别人
时拿到的可能是
,哪怕你本地从未跑过
什么时候该用
,什么时候必须换
没有绝对正确,只有上下文适配。核心判断依据是:你是否信任该包的维护者严格遵守语义化版本,以及你的项目对变更的容忍度。
用
:内部工具库、小众包、历史包袱重的项目、CI/CD 流水线要求极稳(例如金融类定时任务)
用
:主流框架生态(Laravel、Symfony)、活跃开源项目、你主动参与 issue 跟踪且能快速响应 breaking change
禁用自动升级:直接写死版本如
(不带符号),适用于审计强控或合规场景
最容易被忽略的一点:PHP 自身版本约束也受此影响,比如
允许装
,但绝不允许
—— 这条常被漏看,导致
在 CI 上突然失败
~^composer.json~1.2.31.2.x~~1.2.3>=1.2.3 ;而 ~1.2>=1.2.0 。~1.2.31.3.01.2.101.3.0~9.52.09.52.x1.2.10~^1.2.31.9.02.0.0^^0.1.2>=0.1.2 )。^1.2.3>=1.2.3 —— 所以 1.9.01.10.52.0.01.10.0^^2.4.02.9.0Call to undefined methodcomposer updatecomposer updatecomposer installcomposer.lockinstallcomposer update vendor/packagecomposer.json^~composer update^lock1.2.5composer.json~1.2.0install1.2.9update~^~^"monolog/monolog": "2.10.0""php": "^8.1"8.1.998.2.0composer install