Composer自定义installer-paths时依赖包autoload失效问题
现有项目的主composer.json配置如下,要求将自定义插件安装到指定自定义路径:
{ "name": "myself/main-project", "repositories": [ { "type": "git", "url": "https://github.com/myself/some-plugin.git" } ], "require": { "myself/some-plugin": "@dev" }, "extra": { "installer-paths": { "some-other-path/{$name}/": ["type:wordpress-plugin"] } } }
该插件存放在独立代码仓库,对应的composer.json配置如下,配置了Foo命名空间的自动加载规则:
{ "name": "myself/some-plugin", "type": "wordpress-plugin", "autoload": { "psr-4": { "Foo\\": "src" } }, "require": { "composer/installers": "^2.1" } }
插件在src/Bar.php路径下定义了带命名空间的类Bar:
<?php namespace Foo; class Bar { public function __construct() { echo 'Foobar!'; } }
当插件安装到自定义路径时,自动加载功能失效。在项目其他位置调用Bar类的代码如下:
<?php require_once(dirname(__DIR__) . '/vendor/autoload.php'); $bar = new Foo\Bar();
执行后抛出错误:Fatal error: Uncaught Error: Class "Foo\Bar" not found。
如果将主composer.json中的自定义installer-paths修改为默认vendor目录后重新安装依赖:
"installer-paths": { "vendor/{$name}/": ["type:wordpress-plugin"] }
此时功能运行正常,可以正常输出Foobar!。
- 检查生成的自动加载文件,确认已识别
Foo命名空间规则; - 使用Composer path类型仓库(
"type": "path")本地加载插件,问题仍然复现; - 尝试使用
\Foo\Bar等多种命名空间调用写法,均无效。
问题原因
这个问题是composer/installers的机制和Composer默认自动加载逻辑的冲突导致的:composer/installers仅负责将匹配类型的包移动到installer-paths配置的自定义目录,但不会同步修改Composer核心记录的包安装路径。Composer生成自动加载映射时,默认以vendor目录作为依赖包的根路径拼接命名空间对应的文件路径,当包被移动到vendor外的自定义目录时,自动加载规则里拼接出的文件路径和实际存储路径不匹配,哪怕规则里已经写了Foo命名空间,指向的也是不存在的错误地址,最终触发类找不到的错误。
当你把安装路径改回vendor目录下时,路径拼接逻辑和实际存储位置一致,所以可以正常加载。
解决方法
两种方案选其一即可:
- 安装扩展包
oomphinc/composer-installers-extender,这个扩展会在composer/installers移动包位置后,同步更新Composer记录的包路径,让自动加载生成的路径和实际安装位置对齐。安装完成后重新执行composer dump-autoload即可生效,不需要修改原有配置。 - 不安装额外扩展,直接在主项目的
composer.json中显式声明插件的命名空间路径,覆盖默认生成的错误映射:
{ "autoload": { "psr-4": { "Foo\\": "some-other-path/myself/some-plugin/src/" } } }
配置完成后执行composer dump-autoload重载自动加载规则即可正常使用。
内容的提问来源于stack exchange,提问作者kapoko

