composer dump-autoload出现Ambiguous class resolution警告,项目仍能启动,所以一直被忽略。后来预生产正常、生产报方法不存在,才发现两个扩展都打包了Vendor\Library\Client类;哪个类被加载取决于扫描顺序和已生成的classmap。这个警告不是“无害提示”,而是部署不确定性。
先让Composer告诉你冲突双方
composer dump-autoload -o -vvv 2>&1 | tee /tmp/autoload.log grep -n "Ambiguous class resolution" /tmp/autoload.log
再从生成的classmap确认当前选择。
php -r '$m=require "vendor/composer/autoload_classmap.php"; echo $m["Vendor\\Library\\Client"]??"missing",PHP_EOL;' composer why vendor/library composer why-not vendor/library 2.0.0
判断是哪一种重复
- 两个Composer包各自带了一份同名库
- app/code中的模块又复制了一份vendor依赖
- 旧扩展目录升级后未删除,新旧目录同时存在
- 补丁工具生成了备份.php文件并被PSR-4扫描
- 大小写不同的路径在开发机不冲突,到Linux后表现不同
正确修复是只保留一个所有者
如果两个模块依赖同一库,让根composer.json统一require兼容版本,并从模块包里移除私有副本。若厂商扩展把依赖硬复制进自己的目录,优先升级厂商版本或联系厂商提供scoped依赖;不要长期靠调整autoload顺序决定谁赢。
短期无法改第三方包时,可以用replace/conflict或patch移除重复autoload,但必须写清楚版本边界并锁定composer.lock。exclude-from-classmap只能让警告消失,不会解决两个模块期待不同API的问题。
清理后要删除旧生成物
rm -rf generated/code/* generated/metadata/*
composer install --no-dev --prefer-dist --optimize-autoloader
bin/magento setup:di:compile
php -r 'require "vendor/autoload.php"; $r=new ReflectionClass("Vendor\\Library\\Client"); echo $r->getFileName(),PHP_EOL;'
Reflection输出必须指向预期包。所有节点、构建容器和CLI环境都执行同一检查,防止某台机器还有旧vendor目录。
兼容性回归
- 运行冲突类相关模块的主要API调用
- 比较两个扩展声明的依赖版本范围
- 检查生产使用--no-dev后是否少了被错误依赖的dev包
- 重新构建镜像,确认没有依赖本地未跟踪目录
- CI把Ambiguous class resolution当成失败而不是warning忽略
autoload顺序不是公共契约。只要两个文件声明同一个完全限定类名,项目就处于不可重复构建状态。

