Composer不扫描软链接目录的autoload配置,需用path仓库类型声明真实路径并require包名,再执行composer update;同时phpunit.xml需显式包含vendor下软链接模块的tests目录,CI中应检测路径存在或回退到git clone。

软链接模块在 Composer 中的加载路径问题
Composer 默认不扫描软链接目录下的 autoload 配置,即使你用 ln -s 把本地模块链接进 vendor/,composer dump-autoload 也不会自动识别它的 autoload 规则(比如 "psr-4" 映射)。结果就是:类找不到,phpunit 直接报 Class not found。
根本原因在于 Composer 的 autoloader 生成逻辑只遍历 vendor/composer/autoload_psr4.php 等静态文件,而软链接模块的 composer.json 根本没被读取过。
- 别指望
composer require --dev path/to/local/module自动生效——它只对真实路径有效,不处理软链接 - 手动改
vendor/composer/autoload_psr4.php是临时方案,下次dump-autoload就被覆盖 - 最稳的方式是让 Composer “认为”这个软链接是正式的开发依赖
用 path 仓库类型强制 Composer 加载软链接
在项目根目录的 composer.json 中显式声明一个 path 类型仓库,指向你的软链接目标路径(不是链接本身,是它指向的真实目录):
{
"repositories": [
{
"type": "path",
"url": "../my-local-package"
}
],
"require": {
"my-vendor/my-local-package": "*"
}
}
注意:url 必须是绝对路径或相对于 composer.json 的相对路径,且必须指向软链接**最终解析后的实际目录**;require 的包名要和该模块 composer.json 里的 "name" 完全一致。
- 执行
composer update my-vendor/my-local-package后,Composer 会把../my-local-package软链接进vendor/,并正确读取它的autoload配置 - 此时
phpunit就能正常加载被测类了 - 如果模块里有
autoload-dev,它的测试类路径也会被加入 autoloader,无需额外配置
phpunit.xml 里别漏掉软链接模块的测试目录
即使类能加载了,PHPUnit 默认只运行 tests/ 下的用例。如果你的软链接模块自带 tests/ 目录(比如在 ../my-local-package/tests),得在项目根目录的 phpunit.xml 里显式包含它:
<testsuites><testsuite name="Project"><directory>tests/</directory><directory>vendor/my-vendor/my-local-package/tests/</directory></testsuite></testsuites>
否则 phpunit 根本不会发现那些测试文件。
- 路径必须和
vendor/下的实际结构一致(即vendor/{vendor}/{name}/tests) - 如果模块用了自定义命名空间前缀,确保它的
autoload-dev已生效,否则测试类里的use语句仍会失败 - 执行
phpunit --list-tests可验证是否识别到了软链接模块的测试用例
CI 环境下软链接可能失效,得用条件回退
在 GitHub Actions、GitLab CI 这类环境里,ln -s 创建的软链接常因工作目录权限或挂载方式失效,导致 path 仓库无法解析。这时候不能硬扛。
- CI 脚本里先判断路径是否存在:
test -d ../my-local-package || git clone https://github.com/my-vendor/my-local-package.git ../my-local-package - 或者直接跳过
path方式,在 CI 中走常规composer require my-vendor/my-local-package:dev-main - 更稳妥的是:把模块测试单独抽成独立 job,用
composer install+phpunit在模块自身目录下运行,不依赖主项目的 vendor 结构
本地开发靠软链接提效,但 CI 的稳定比路径“优雅”重要得多——这点容易被忽略,直到流水线突然挂掉。











