Composer对path仓库默认复制而非链接,是为保障构建可重现性;需在本地包composer.json中显式配置"options": {"symlink": true}并执行composer update vendor/name才能启用符号链接,且须验证系统权限、Git初始化及Composer≥2.2。

用 path 类型仓库 + 统一命名空间 + symlink 显式启用,是唯一能保证多模块本地协同调试不掉链子的方式。其他方案要么改代码不生效,要么 autoload 断掉,要么 CI 上直接报错。
为什么 composer install 后 vendor 里还是复制不是链接
Composer 默认对 path 仓库走 copy 模式,不是 bug,是设计——它优先保障可重现性。但开发时你要的是热更新,所以必须显式打开 symlink。
- 在本地包(即被引用的子模块)的
composer.json中加:"options": {"symlink": true} - 或全局启用:
composer config --global path.repo.symlink true -
composer update vendor/package-name才会触发 symlink 重建;install不管这个开关 - 验证是否真链接:
ls -l vendor/vendor/package-name(Linux/macOS)或dir vendorendorpackage-name(Windows),看到->或才算成功 - 常见静默 fallback 原因:本地包没
git init+ 至少一个 commit;Windows 下没开“开发者模式”;权限不足导致 Composer 自动降级为 copy
多个本地包怎么避免重复写 repositories
每个项目都手写一堆 repositories 配置,不仅累,还容易路径写错、版本不一致。全局配置一次,所有项目自动继承。
- 把常用本地包统一放在一个父目录下,比如
~/dev/myorg/,里面放utils/、api-client/、logger/等子目录 - 执行:
composer config -g repositories.myorg-path '{"type":"path","url":"~/dev/myorg/*"}' - 注意:
*是通配符,会匹配~/dev/myorg/下所有含合法composer.json的子目录 - 主项目
composer.json里就只留require,不用再写repositories—— Composer 会自动从全局配置里找 - 团队协作时慎用全局配置,建议用
composer.json的config.repos字段做项目级覆盖,更可控
autoload 不生效、Class not found 怎么排查
90% 的 “找不到类” 不是代码问题,是 autoload 映射和命名空间没对齐。尤其多模块时,每个 composer.json 的 autoload 和 autoload-dev 必须独立且正确。
- 子模块的
composer.json必须有autoload,例如:"psr-4": {"MyOrg\Utils\": "src/"};末尾斜杠不能少 - 测试类在
tests/下,就得在autoload-dev里单独配:"psr-4": {"MyOrg\Tests\": "tests/"};如果写成"MyOrg\Tests": "tests/"(缺最后反斜杠),就会加载失败 - 无命名空间的辅助文件(如
tests/bootstrap.php)必须用autoload-dev.files引入,PSR-4 不处理这类文件 - 改完 autoload 配置后,必须手动运行:
composer dump-autoload --dev,否则vendor/composer/autoload_dev.php不更新 - 别只看
vendor/目录是否存在,用php -r "echo class_exists('MyOrg\Utils\Helper') ? 'ok' : 'fail';"实测才靠谱
最易被忽略的一点:所有模块的 name 字段必须和主项目 require 中的包名完全一致,包括大小写、连字符、vendor 名——错一个字符,Composer 就当它是另一个包,symlink 和 autoload 全部失效,且错误提示极其模糊。











