必须写1.2.3,不能带v前缀;composer只认纯语义化版本,v1.2.3会报“invalid version string”错误;^1.2.3等价于>=1.2.3 =1.2.3

composer.json 里写 v1.2.3 还是 1.2.3?
必须写 1.2.3,不能带 v 前缀。Composer 解析版本时只认纯语义化格式,v1.2.3 会被当作非法字符串报错:Invalid version string "v1.2.3"。Packagist 和 Git tag(如 v1.2.3)是发布侧的事,require 侧约束必须剥离 v。
^1.2.3 和 ~1.2.3 在 tag 场景下实际行为差异
两者都依赖真实存在的 Git tag,但升级边界完全不同:
-
^1.2.3等价于>=1.2.3 :只要远程有 <code>1.12.0、1.99.99这类 tag,composer update就可能拉进来——哪怕中间跳了 10 个次版本 -
~1.2.3等价于>=1.2.3 :只允许修订号(PATCH)变,<code>1.2.4、1.2.99合法,1.3.0直接被排除 - 关键陷阱:某包在
1.8.0废弃了一个方法,你写的是^1.2.3,composer update仍会升到1.8.0——它不查 CHANGELOG,只比对 tag 字符串
想锁死到某个 tag,但又怕未来打新 tag 撞上约束怎么办?
直接用精确版本最稳妥:
- 运行
composer require vendor/package:1.2.3(注意冒号,且用引号包裹) - 它会写入
"vendor/package": "1.2.3"到composer.json,并把对应 commit hash 写进composer.lock - 后续
composer install严格按 lock 文件还原,不受新 tag 影响 - 别用
^1.2.3或~1.2.3代替——它们本质是“允许升级”,不是“锁定”
为什么删了 composer.lock 后 composer install 装出来的不是你想要的 tag?
因为 composer install 只读 composer.lock;没 lock 文件时,它退化为 composer update --lock,重新解析 composer.json 中的约束,并按当前所有可用 tag 计算最新兼容版本。常见后果:
- 你本意是用
~1.2.0,但远程新打了1.2.99,install 就装1.2.99 - 你写了
^1.2.3,结果作者发了1.12.0,install 就装1.12.0 - 想确保每次部署都一模一样,
composer.lock必须提交进 Git,且禁止手动修改
真正决定运行时行为的不是 composer.json 里的符号,而是 composer.lock 里记下的那个 commit hash——标签只是人类可读的别名,Composer 最终靠 hash 定位代码。











