必须写"dev-main"而非"*"或"^1.0":path源只认git分支名,忽略version字段;需确保本地包已git init并提交,分支名与require严格一致,repositories须置于主项目composer.json顶层。

require 里必须写 dev-main 而不是 * 或 ^1.0
本地 path 源不走语义化版本解析,它只认 Git 分支名。即使你本地包的 composer.json 里写了 "version": "1.0.0",Composer 也完全忽略。写 "acme/utils": "*" 会触发全量依赖解析,可能绕过 path 源去远程找包;写 "^1.0" 则直接报错或 fallback 到 packagist。
正确做法是显式指定分支:默认分支是 main 就写 "dev-main",是 develop 就写 "dev-develop"。大小写、连字符、拼写必须和 git branch 输出完全一致。
- 检查本地包是否已初始化 Git:
git init && git add . && git commit -m "init"(无 commit 时 Composer 直接跳过) - 确认分支存在:
cd /path/to/local/pkg && git branch --show-current - 主项目
composer.json的require字段必须是"acme/utils": "dev-main",不能带空格、中文逗号或末尾空格
repositories 必须放在主项目 composer.json 顶层
很多人把 repositories 塞进本地包自己的 composer.json 里,结果毫无作用。Composer 只读取执行命令时所在目录(即你 cd 进去的那个项目)的根 composer.json 中顶层的 repositories 字段。
错误示例:"require": { "repositories": [...] }(嵌套在 require 下)或放在本地包目录下——这不会被加载。
- 正确位置:主项目
composer.json根层级,与require、autoload并列 - 类型固定为
"type": "path",不是"vcs"或"git" - 路径值支持相对路径(如
"../my-pkg")或绝对路径(如"/var/www/my-pkg"),但不能含~或$HOME - 路径必须可访问:PHP 进程得能
is_dir()到该目录;Docker 容器内需挂载对应 volume,SELinux 限制也会导致静默失败
vendor 里看到软链不是失败,而是 path 源生效了
执行 composer install 后,vendor/acme/utils 显示为箭头(->)指向外部目录,这是正常行为,不是 bug。这是 path 源的核心机制:热更新——改本地包代码,主项目立刻生效,无需反复 install。
Linux/macOS 下用 ls -l vendor/acme/utils 看是否显示类似 acme/utils -> /path/to/my-pkg;如果是个普通文件夹,说明配置未生效,Composer fallback 到了其他源。
- 验证是否生效:
composer show acme/utils输出中source字段应为path - Windows 用户注意:需启用“开发者模式”或以管理员权限运行终端,否则 Composer 会退化为复制(不报错但失去热更新能力)
- CI/CD 场景慎用 path 源——构建机通常没那个本地路径,应改用 dist + archive 或私有 Satis
name 字段大小写和分隔符必须完全匹配
Composer 不做模糊匹配。name 字段必须和 require 中写的完全一致:大小写、短横线 -(不能用下划线 _ 或大写字母)、vendor 名全部对齐。路径也一样——url 指向的是含 composer.json 的目录,不是文件,且路径相对于主项目 composer.json 的位置。
常见错误现象:Could not find package acme/utils,但 composer show --all 里根本没出现该包名。
- 检查本地包
composer.json中的"name": "acme/utils"是否和主项目require里写的完全一致 - 路径末尾不可加
/(如"../my-pkg/"是错的,应为"../my-pkg") - 本地包目录下必须有合法的
composer.json,且其中name字段不能为空或非法格式(如含空格、中文)
实际调试时最容易被忽略的是 Git 初始化状态和 Windows 权限——没 commit 的本地包、没开开发者模式的 Windows 终端,都会让 path 源看起来“失效”,但其实只是被静默跳过。











