yii2类自动加载严格按三步执行:先查$classmap,再解析命名空间别名,最后交由composer;顺序错误将导致unknownclassexception或静默加载错误。

Yii2 的类自动加载不是“猜路径”,而是有严格优先级的三步查找:先查 $classMap,再按命名空间解析别名,最后 fallback 失败 —— 顺序错了,就容易报 UnknownClassException 或静默加载错文件。
类名查找顺序:为什么 app\controllers\SiteController 会去 @app/controllers/SiteController.php?
Yii2 不靠目录扫描,也不依赖 PSR-4 自动推导,它用的是「确定性映射」:
- 第一步:查
Yii::$classMap—— 这是硬编码的类名到文件路径的数组(来自vendor/yiisoft/yii2/classes.php),命中就直接include,跳过后续所有逻辑; - 第二步:若类名含
\(如app\controllers\SiteController),则拼出别名路径@app/controllers/SiteController.php,再调用getAlias()解析真实路径; - 第三步:若类名不含
\(比如旧式写法MyHelper),autoload()直接return,不尝试解析 —— 这类类必须进$classMap或由 Composer 管理。
所以 app\controllers\SiteController 能加载成功,前提是:@app 别名已注册(通常在 Application::setBasePath() 中完成),且该路径下存在对应 PHP 文件。缺任意一环,就会卡在第二步并抛出 "Unable to find 'app\controllers\SiteController' in file:..."。
getAlias() 怎么把 @app 变成真实路径?别名不是字符串替换
getAlias() 看似简单,实则是带层级匹配的别名树查找,不是正则替换:
- 它先截取
@后第一个/前的部分作为根别名(如@app、@yii); - 再查
static::$aliases['@app']是否为字符串(单值)或数组(多路径注册); - 如果是数组(常见于 Advanced 模板中
@common多个注册点),它会做最长前缀匹配 —— 例如@common/models/User会优先匹配@common/models而非仅@common。
典型坑:Yii::setAlias('@app', '/var/www/myapp') 写成 Yii::setAlias('@app/', '/var/www/myapp')(多了斜杠)—— getAlias() 会因根别名不匹配而报 Invalid path alias。
什么时候该往 $classMap 里加条目?不是所有类都走别名
$classMap 是性能关键路径,适合三类场景:
- 框架核心类(如
yii\web\Application),已在classes.php预置,避免每次解析别名; - 高频使用但路径不标准的类(如第三方 SDK 的
AliyunOssClient放在@common/sdk/aliyun/下,不想改命名空间); - 需要绕过别名机制的类(比如某些生成器产出的类,命名空间与目录结构不一致)。
注意:$classMap 中的路径如果以 @ 开头(如 'MyClass' => '@common/helpers/MyClass.php'),autoload() 仍会调用 getAlias() 解析 —— 所以别名本身也得提前注册好,否则照样失败。
Composer autoloader 和 Yii autoloader 谁先执行?别被 spl_autoload_register 的 $prepend 搞晕
入口文件中这两行顺序和参数决定加载权:
require __DIR__ . '/../vendor/autoload.php'; // Composer 注册自己的 loader require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php'; // Yii 注册自己的 loader
关键在 Yii 的注册调用:spl_autoload_register(['Yii', 'autoload'], true, true) —— 第三个参数 true 表示“插到队首”,所以 Yii 的 autoload() 总是第一个被调用。
- 如果 Yii 找不到类,它
return,控制权交还给队列中的下一个(即 Composer loader); - 如果 Composer 先注册且没
$prepend=true,而你又把自定义类放进了composer.json的psr-4,那 Yii 根本不会介入 —— 类直接被 Composer 加载了; - 冲突场景:同一个类同时出现在
$classMap和 Composer 的autoload里,Yii 会优先加载$classMap版本 —— 这常被用来“热替换”调试版类。
真正容易忽略的是:getAlias() 查找不到时默认抛异常,但传 false 作为第二个参数(如 getAlias($alias, false))会静默返回 false,后续 is_file() 判定失败就直接 return —— 此时你得确认 Composer 是否兜底,否则类就真丢了。










