第三章 版本约束的艺术:精确版本、范围约束、通配符、波浪号~与脱字符^的区别
版本约束这东西,说白了就是告诉 Composer:「你要装哪个版本的包,以及能容忍多大的变化。」
我刚用 Composer 那会儿,觉得随便写个 "monolog/monolog": "1.0" 就行了。结果呢?某天同事更新了依赖,整个项目炸了。嗯,从那以后我才认真研究版本约束的细节。
今天咱们就把这几种约束方式掰开揉碎,讲清楚。
一、精确版本:最保守的选择
写法很简单:
"monolog/monolog": "1.25.3"
意思就是:我只要 1.25.3 这个版本,多一个小版本号都不行。
什么时候用?
- 你明确知道某个版本是稳定的
- 你依赖的包 API 经常变,不敢乱升级
- 生产环境需要锁定版本
二、范围约束:给版本划个区间
用比较运算符来限定范围:
"vendor/package": ">=1.0 <2.0"
"vendor/package": ">=1.0, <2.0" // 逗号也行
还可以组合:
"vendor/package": ">=1.0, <1.1 || >=1.2"
这表示:1.0 到 1.1 之间(不含 1.1),或者 1.2 及以上。为什么跳过 1.1?因为那个版本有 bug。
我个人习惯用范围约束来「围堵」有问题的版本。比如某个包 2.3.0 有性能问题,我就写:
"vendor/package": ">=2.0, <2.3.0 || >=2.3.1"
三、通配符:偷懒但有效
"monolog/monolog": "1.*"
这表示:1.0、1.1、1.2……只要是 1.x 都行。相当于 >=1.0, <2.0。
也可以更细:
"monolog/monolog": "1.2.*"
只允许 1.2.x 系列。
通配符看着方便,但有个坑:它允许小版本升级。如果包作者在 1.2.5 里改了公共方法签名,你的代码可能就挂了。
四、波浪号 ~:锁定主版本和次版本
这是很多人容易搞混的地方。咱们直接看例子:
| 写法 | 等价范围 | 允许的变化 |
|---|---|---|
~1.2 |
>=1.2, <2.0 |
允许小版本升级(1.2 → 1.3) |
~1.2.3 |
>=1.2.3, <1.3 |
只允许补丁版本升级(1.2.3 → 1.2.4) |
说白了,波浪号的意思是:「别给我乱升大版本,小版本可以动,但别动次版本号。」
我记得有一次,团队里有人写了 ~2.0,结果 Composer 直接装了 2.9.9。他吓一跳,以为要出大事。其实没问题,因为 2.x 之间是向后兼容的——至少理论上如此。
五、脱字符 ^:最推荐的写法
这是 Composer 官方最推荐的方式。看例子:
| 写法 | 等价范围 | 允许的变化 |
|---|---|---|
^1.2.3 |
>=1.2.3, <2.0 |
允许次版本和补丁版本升级 |
^0.3 |
>=0.3.0, <0.4.0 |
0.x 版本特殊处理,只允许补丁升级 |
^0.0.3 |
>=0.0.3, <0.0.4 |
连补丁都不能升 |
脱字符的意思是:「我相信语义化版本。只要主版本不变,次版本和补丁版本应该都是向后兼容的。」
你想想看,如果每个包作者都遵守语义化版本规范,那 ^1.2.3 就是最省心的写法。既能拿到 bug 修复(补丁版本),也能拿到新功能(次版本),还不用担心 API 被改。
~更保守:锁定次版本,只允许补丁升级^更开放:锁定主版本,允许次版本和补丁升级
六、实战中的选择策略
我一般这样选:
- 新项目、新依赖:用
^。比如"laravel/framework": "^8.0"。这样能自动获取小版本更新。 - 对稳定性要求极高:用
~加三位版本号。比如"monolog/monolog": "~2.3.0"。只接受补丁更新。 - 有已知问题的版本:用范围约束排除。比如
"vendor/package": "^2.0, !=2.1.5"。 - 锁定生产环境:用
composer.lock文件。composer.json 里可以宽松些,但 lock 文件保证所有人装的一样。
^0.9 上吃过亏。0.x 版本在语义化版本里不保证稳定性,结果 0.10 直接改了接口。所以对于 0.x 的包,我建议用精确版本或者 ~ 约束。
七、一张表看懂所有约束
| 写法 | 含义 | 示例匹配 | 不匹配 |
|---|---|---|---|
1.2.3 |
精确版本 | 1.2.3 | 1.2.4, 1.3.0 |
>=1.2 |
大于等于 | 1.2, 2.0, 3.5 | 1.1.9 |
1.* |
通配符 | 1.0, 1.9.9 | 2.0 |
~1.2.3 |
波浪号 | 1.2.3, 1.2.9 | 1.3.0, 2.0 |
^1.2.3 |
脱字符 | 1.2.3, 1.9.9 | 2.0, 0.9.0 |
八、我的个人建议
如果你问我,新手最该记住什么?
记住两点:
- 大部分时候用
^,它最符合语义化版本的精神 - 遇到 0.x 的包,换成
~或者精确版本
至于通配符和范围约束,偶尔用用就好。它们太灵活,容易让依赖关系变得不可控。
嗯,版本约束这块就讲这么多。下一章咱们聊聊 composer.lock 文件——那个让团队保持一致的秘密武器。