必须在主项目composer.json根级配置repositories,仅改require无效;本地包name、路径、版本须严格匹配,否则Composer静默跳过;启用symlink需本地包内设"options":{"symlink":true}并管理员权限执行update。

本地包必须用 path repository,不能只改 require
Composer 安装本地包失败,90% 是因为只在 require 里写了包名,却没在根级 composer.json 配 repositories。它根本不会去读你本地目录——不是“找不到文件”,是压根不扫描。
正确做法是:在主项目 composer.json 的最外层(和 require 同级)写:
{
"repositories": [
{
"type": "path",
"url": "./packages/my-utils"
}
],
"require": {
"acme/utils": "dev-main"
}
}
-
url必须是相对于主项目composer.json的路径,不能带file://或绝对路径 -
acme/utils必须和本地包自己composer.json里的"name"字段**逐字一致**(大小写、斜杠、短横线全敏感) - 版本必须写
"dev-main",尤其本地包还没打 Git tag 时;写"*"或"^1.0"会静默跳过
循环依赖报错时,先确认是不是 symlink 没生效
当你看到 Class not found 或 require_once(): Failed opening required,但 vendor/acme/utils 目录明明存在——大概率是 Composer 没走 symlink,而是复制了一份静态副本。改本地代码后 vendor 里没变化,就是这个原因。
关键点不在主项目,而在本地包自己的 composer.json:
- 必须在本地包的
composer.json里加:"options": {"symlink": true} - Windows 用户必须用管理员权限运行终端,否则创建 junction 失败且无提示
- 验证是否真链接成功:
ls -la vendor/acme/utils(macOS/Linux)应显示->;Windows 上dir vendorcmeutils应显示JUNCTION
循环依赖实际发生时,Composer 会直接拒绝安装
如果 A 包 require B,B 包又 require A,Composer 解析阶段就会报错:Root package 'a/a' cannot require itself 或类似 circular dependency 提示。这不是 autoload 或路径问题,是依赖图构建失败。
真实场景中,这种硬循环极少手动写出,常见于:
- 两个本地包互相 require,且都用
dev-main—— Composer 无法推断谁先装 - 其中一个包的
require写错了 name,指向了自己(比如拼写错误导致匹配到自身) - 用了
path+package混合仓库类型,触发了非预期解析路径
解决方式只有两个:拆开耦合(推荐),或把其中一个包的依赖改成 require-dev 并确保它不参与生产 autoload。
autoload 映射不生效?别忘了 dump-autoload 是独立步骤
即使 vendor/acme/utils 已正确 symlink,use AcmeUtilsHelper 仍报错,往往是因为 autoload 没注册。Composer 不会自动把本地包的 autoload 段落合并进主项目的自动加载器。
必须手动执行:
- 确保本地包
composer.json里有合法autoload声明(如"psr-4": {"Acme\Utils\": "src/"}) - 在主项目根目录运行
composer dump-autoload(不是install或update) - 检查生成的
vendor/composer/autoload_psr4.php,确认你的命名空间已写入
最容易被忽略的是:本地包改了 autoload 配置后,很多人以为 composer update 会顺带刷新 autoload —— 实际不会。必须显式 dump-autoload。


















