波浪号~表示最小版本兼容,即~1.2.3等价于>=1.2.3 <1.3.0;折号^表示向后兼容升级,即^1.2.3等价于>=1.2.3 <2.0.0。

波浪号 ~ 是「最小版本兼容」,不是「最新补丁版」
很多人以为 ~1.2.3 表示“装 1.2.x 中最新的补丁版”,其实它等价于 >=1.2.3 —— 也就是说,只要主版本(<code>1)和次版本(2)不变,补丁号(3)可以升,但次版本绝不能动。哪怕 1.2.99 发布了,~1.2.3 也能装;但 1.3.0 一出,立刻被拦住。
常见错误现象:composer update 没升级到预期的 1.2.10,却卡在 1.2.3 —— 很可能是你本地已锁死 composer.lock 中的版本,或依赖树里其他包强制约束了更低范围。
-
~1.2等价于>=1.2.0 (注意:没写补丁号时,会自动补 <code>.0) -
~1等价于>=1.0.0 ,不是 <code>>=1.0.0 - 如果真想允许次版本升级(比如接受
1.3.x),该用^1.2.3,不是~1.2.3
折折号 ^ 是「向后兼容升级」,但受语义化版本规则严格约束
^1.2.3 的意思是“允许次版本和补丁号自由升级,但主版本不能变”,等价于 >=1.2.3 。但它真正生效的前提是:目标包遵守 <a href="https://www.php.cn/link/8ddc98fe6483c3ecddcb687eb2d30d37">SemVer</a> 规范——即 <code>1.2.3 → 1.3.0 是兼容的,而 1.2.3 → 2.0.0 是不兼容的。
问题常出在非标准包上:有些包把 0.x.y 当稳定版用,但按 SemVer,0.1.0 → 0.2.0 是不兼容变更;此时 ^0.1.0 实际只允许 >=0.1.0 ,极易误判。
-
^0.1.0≡>=0.1.0 (零版本下,次版本变动视为破坏性) -
^1.0.0≡>=1.0.0 ,但若包没发 <code>1.0.0,只发了1.0.1,那^1.0.0仍能匹配(Composer 会找满足条件的最低可用版) - 不推荐对
dev-master或dev-feature/x用^,分支名不参与 SemVer 解析,^在这种场景下无意义
写错范围导致依赖冲突时,composer why-not 比报错更管用
当你执行 composer require foo/bar:^2.0 却失败,报一堆 can only install one of,别急着删 vendor 或改 composer.lock。先用 composer why-not foo/bar:2.0.0 查谁在拦路。
这个命令会列出所有已安装包中,与 foo/bar:2.0.0 冲突的版本约束,包括间接依赖(比如 your/project 依赖 baz/bam,而 baz/bam 锁死了 foo/bar:^1.5)。
- 输出里带
[required]的行,是直接声明的冲突约束 - 带
[required by]的行,是传递依赖引入的限制 - 如果看到某包约束为
foo/bar:1.9.*,而你想升到2.0,说明它还没适配,得等上游更新或自己 fork 修复
锁定生产环境版本,别只靠 composer.json 范围写法
composer.json 里的版本范围再严谨,也挡不住 composer install 时拉取新发布的补丁版(只要符合范围)。真正锁定行为靠的是 composer.lock —— 它记录了每个包的确切版本、哈希和依赖树快照。
容易被忽略的一点:composer install 默认只读 composer.lock,但如果你删了它,或加了 --no-lock,或运行 composer update,就会重新解析范围并可能升级。
- CI/CD 流水线中必须确保
composer install前存在有效的composer.lock,且不带--no-lock - 团队协作时,
composer.lock必须提交进 Git,否则每人本地install结果可能不同 - 用
composer show foo/bar可查当前装的是哪个精确版本,比看composer.json更真实
~ 和 ^ 的区别,而是某个小版本更新悄悄改了函数签名,又没改主版本号——这时候范围写得再准也没用,得靠测试覆盖和 composer outdated 主动巡检。










