优先选 psr-4:它自动映射命名空间与路径、IDE 支持好、无需手动更新;classmap 仅适用于无命名空间的零散旧文件,且易因漏执行 dump-autoload 导致 CI 失败。

autoload-dev 中的 psr-4 和 classmap 怎么选?

测试辅助类通常放在 tests/Supporttests/Helpers 下,不参与生产代码加载。用 psr-4 最稳妥:它按命名空间自动映射路径,IDE 支持好,且 Composer 自动处理文件变动;classmap 虽能强制加载任意文件,但需手动执行 composer dump-autoload,CI 环境容易漏掉,还可能意外把测试类打进生产 autoloader。

常见错误是把 autoload-dev 写成 autoload,导致测试类被生产环境加载——这会增大内存占用,还可能因依赖未安装(如 phpunit)而报 Class not found 错误。

  • psr-4 适合有统一命名空间的辅助类,例如 "Tests\Support\\": "tests/Support/"
  • classmap 仅用于零散、无命名空间的旧式 .php 文件(极少见)
  • 别在 autoload-dev 里重复定义 autoload 已声明的路径,Composer 不会合并,而是完全隔离

composer.json 中 autoload-dev 的标准写法

必须严格放在 autoload-dev 键下,与 autoload 并列。命名空间末尾的反斜杠不能省略,否则 PSR-4 解析失败;路径必须以 / 结尾(Windows 下也用正斜杠),否则 Composer 可能忽略。

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Support\\": "tests/Support/",
            "Tests\\Feature\\": "tests/Feature/",
            "Tests\\Unit\\": "tests/Unit/"
        }
    }
}

执行 composer dump-autoload 后,vendor/autoload.php 会同时加载 autoloadautoload-dev 的规则,但仅当该文件被引入时才生效(例如 PHPUnit 启动时自动 require 它)。

为什么 PHPUnit 运行时找不到我的 Helper 类?

最常见原因是命名空间与文件路径不匹配。比如文件 tests/Support/DatabaseHelper.php 声明了 namespace Tests\Helpers;,但 autoload-dev 里写的是 "Tests\\Support\\": "tests/Support/" —— 此时命名空间和目录名不一致,Composer 根本不会扫描这个文件。

  • 检查 namespace 声明是否与 autoload-dev 中的键完全对应(包括大小写)
  • 确认文件实际路径是否真在指定目录下,Composer 不递归扫描子目录
  • 运行 composer show --platform 验证 autoloader 是否已注册;或临时加一行 var_dump(class_exists('Tests\Support\DatabaseHelper')); 在测试中调试
  • 如果用了符号链接,确保 composer dump-autoload -o(优化模式)未启用,它会跳过 symlink 目录

测试类能否访问 src 下的内部类?

可以,但要注意 visibility。autoload-dev 只控制「加载」,不控制「可见性」。如果 src/Service/InternalProcessor.php 里有个 private function,测试类哪怕成功加载了这个类也无法调用它;此时应改用 protected + setAccessible(true)(PHP 8.1+ 推荐用 ReflectionMethod::invoke() 替代)。

另一个易忽略点:某些辅助类依赖 phpunit/phpunit 中的类(如 PHPUnit\Framework\TestCase),若在 autoload-dev 中提前加载了这些类,而当前环境未安装 PHPUnit(如生产部署后运行 composer install --no-dev),就会触发 fatal error。因此,所有只在测试中使用的 use 语句,务必确保其所在文件**只通过测试入口加载**,不要被 autoload 规则意外覆盖。