Composer 的自动加载器在查找类时,会严格按照 files → classmap → psr-4 → psr-0 的顺序依次检索:
1.首先检查 files 配置中手动指定的文件(如果有),这些文件会被直接加载。
若未找到,会去 classmap 中查找类名与文件路径的映射,找到则直接加载对应文件。
2.若 classmap 中没有该类的记录,再按 psr-4 规范的命名空间与目录映射关系去查找文件。
3.最后才会检查 psr-0 规范(较旧的自动加载方式,现在较少使用)。
- 以下是在
composer.json中同时配置files、classmap、psr-4、psr-0四种自动加载规则的示例,清晰展示各自的用法和场景:
{
"autoload": {
"files": [
"src/functions/global_helpers.php", // 全局工具函数(如格式化、日志函数)
"src/config/constants.php" // 项目常量定义(如版本号、路径常量)
],
"classmap": [
"src/legacy/library/", // 旧代码目录(非PSR规范,需扫描类映射)
"src/plugins/payment/lib/" // 第三方支付插件的类文件(结构不规则)
],
"psr-4": {
"App\\": "src/app/", // 命名空间 App\ 对应 src/app/ 目录
"Modules\\": "src/modules/", // 命名空间 Modules\ 对应 src/modules/ 目录
"Utils\\": "src/utils/" // 命名空间 Utils\ 对应 src/utils/ 目录
},
"psr-0": {
"Legacy\\": "src/legacy/old_lib/" // 旧PSR-0规范:Legacy_Class 对应 src/legacy/old_lib/Legacy/Class.php
}
},
"autoload-dev": {
"files": [
"tests/helpers/test_helpers.php" // 仅开发环境加载的测试工具函数
],
"psr-4": {
"Tests\\": "tests/" // 测试类命名空间 Tests\ 对应 tests/ 目录
}
}
}
各配置说明:
-
files- 用于加载无类定义的全局资源(函数、常量等),会在项目启动时自动
require。 - 示例中加载了生产环境的工具函数和常量,以及开发环境专用的测试辅助函数。
- 用于加载无类定义的全局资源(函数、常量等),会在项目启动时自动
-
classmap- 用于非PSR规范的旧代码或结构不规则的类文件,Composer 会扫描指定目录并生成类与文件的映射。
- 示例中针对旧代码库和第三方插件目录,避免手动维护路径关系。
-
psr-4(推荐优先使用)- 现代PHP项目最常用的规范,命名空间与目录结构严格对应,且命名空间前缀不包含在目录路径中。
- 例如:
App\Service\UserService类对应文件src/app/Service/UserService.php。 - 开发环境的测试类也通过
psr-4映射,方便测试代码组织。
-
psr-0(逐步淘汰,兼容旧项目)- 早期的自动加载规范,命名空间前缀会作为目录的一部分,且类名可能包含下划线(映射为目录分隔符)。
- 例如:
Legacy\OldClass类对应文件src/legacy/old_lib/Legacy/OldClass.php(注意命名空间前缀Legacy\\对应到了目录Legacy/)。
生效方式:
配置后需执行 composer dump-autoload 生成自动加载文件,生产环境可加 -o 优化(composer dump-autoload -o),会预生成更高效的类映射。
实际项目中,建议优先使用 psr-4,仅在兼容旧代码时使用 classmap 或 psr-0,files 仅用于必要的全局资源。
举例说明:
- 在vendor/composer/autoload_psr4.php 文件中,例如
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'app\\' => array($baseDir . '/app', $vendorDir . '/thinkcmf/cmf-app/src'),
当在/app和/thinkcmf/cmf-app/src中存在一个命名空间的控制器时,查找顺序是按照数组中目录的先后顺序执行的:
1.首先会在第一个目录(vendorDir . '/thinkcmf/cmf-app/src'),以此类推。
这一机制类似 “优先级覆盖”:靠前的目录可以覆盖靠后的目录中同名的类(按命名空间和类名匹配)。在你的示例中,/app 目录的优先级高于 thinkcmf/cmf-app/src,因此如果两者存在同名的控制器类(如 app\controller\Index),会优先加载 /app 目录下的版本。