~1.2.3 表示 >=1.2.3 且 <1.3.0;~1.2 表示 >=1.2.0 且 <1.3.0;~1 表示 >=1.0.0 且 <2.0.0;~0.8.2 表示 >=0.8.2 且 <0.9.0。

波浪号 ~ 到底允许多大范围升级?
它不是“差不多就行”,而是有明确截断规则的版本锁:写到哪一位,就只允许那一位及之后变动。~1.2.3 表示 >=1.2.3 且 ;<code>~1.2 等价于 >=1.2.0 且 ;<code>~1 就是 >=1.0.0 且 。关键在“最后一位非零数字”——你写了三位,它就卡死在次版本号;你只写两位,它就放开修订号任你升到 <code>1.99.999。
- 想只修 bug、不加新功能?用
~1.2.3,它绝不会装1.3.0 - 想稳住整个
1.x大版本但又接受小更新?该用^1.2.3,不是~1.2 - 写成
~1.2.0和~1.2效果一样,Composer 会自动补.0
为什么生产环境常写 ~2.7.4 而不是 ^2.7.4?
因为 ^2.7.4 允许升到 2.99.99,而 ~2.7.4 只能到 2.7.x —— 这在中间件兼容性敏感的场景里就是命门。比如某支付 SDK 明确只测过 monolog/monolog 的 2.7.x 系列,但 ^2.7.4 在 CI 构建时悄悄拉了 2.8.0,结果日志格式变更导致对账失败。
- 已知
2.7.5有内存泄漏,又不想锁死=2.7.4失去后续补丁?~2.7.4是最优解 -
composer show monolog/monolog -i查真实安装版本,别只信composer.json里写的约束 - CI 流水线中加
composer prohibits monolog/monolog:2.8.0,快速确认谁在拖版本后腿
~ 能不能只升安全补丁?
不能。~ 不识别 CVE,也不区分“修复漏洞”和“新增接口”,它只按 SemVer 位数做机械截断。你写 "monolog/monolog": "~2.8",composer update 可能装 2.9.0(含 CVE 修复),也可能跳过直接装 2.10.0(带新特性但没修那个漏洞)。
- 真正管安全的是
composer audit,不是版本符号:运行它会告诉你当前composer.lock里哪些包有已知漏洞 - 看到提示
Upgrade to ^2.10.0,得手动改约束为"monolog/monolog": "^2.10.0"或更保守的"~2.10.0" -
composer update --dry-run必须跑一遍,确认升级结果是否真落在你预期的区间内
0.x 版本下 ~ 和 ^ 的行为差异
0.x 是语义化版本里的“实验区”,^0.8.2 实际等价于 >=0.8.2 ,它不会升到 <code>0.9.0 —— 这和 ^1.8.2(可升到 1.99.99)完全相反。很多人踩坑,就是因为没注意包还在 0.x 阶段,却误以为 ^ 代表“放心升”。这时 ~0.8.2 和 ^0.8.2 行为一致,都只放行 0.8.x。
- 想明确支持
0.9.x和0.10.x?必须显式写"~0.9.0 || ~0.10.0",^帮不上忙 - 查一个包是否还在
0.x:看它的最新稳定 tag,不是看你自己写的约束 -
composer require some/package:0.9.0默认生成的约束是"^0.9.0",但实际效果是锁死在0.9.x,这点极易被忽略










