Yii配置合并无自动规则,必须手动用ArrayHelper::merge()递归处理嵌套结构;main.php须先被web.php加载,main-local.php需显式判断加载;环境变量须在配置加载前读取并手动回退。

Yii 的配置数组不是“自动覆盖”,而是你写哪行代码、用哪个函数,就按那个逻辑合并——没默认规则,只有你手写的那几行决定最终结果。
ArrayHelper::merge() 是唯一靠谱的递归覆盖方式
Yii 官方模板和文档推荐 yii\helpers\ArrayHelper::merge(),因为它能正确处理嵌套结构:同名字符串键深度覆盖,同名数字键不重排,数组值递归合并,标量值直接右覆盖左。
常见错误是误用 array_merge() 或 + 运算符:
-
array_merge()会重置数字键,导致'components' => ['log', 'db']合并后顺序错乱甚至丢失 -
+只做一层合并,'components' => ['db' => [...]]整个子数组会被右边完全替换,而不是字段级合并 -
array_merge_recursive()会把同名键强行转成数组(如'debug' => true和'debug' => false变成['debug' => [true, false]]),直接让应用崩溃
main.php 和 web.php 的加载顺序不能颠倒
典型写法是 web.php 里先 require 'main.php',再调用 ArrayHelper::merge() 加载自身配置。如果反过来,main.php 去 require web.php,会导致 $config 初始为空或结构错位,后续组件注册失败。
更关键的是:框架本身不介入这个过程。入口脚本(如 web/index.php)只执行你 require 的那一行,其余全靠你手动组织。
所以必须确认:
-
web.php开头有$config = require __DIR__ . '/main.php'; - 后面紧跟
$config = yii\helpers\ArrayHelper::merge($config, [...局部配置...]); - 最后
return $config;,不能漏掉
main-local.php 不是自动生效,得看 main.php 里有没有那段判断
很多人以为只要放个 main-local.php 就能覆盖,其实它是否生效,完全取决于 main.php 文件末尾是否保留了这段代码:
$localConfig = __DIR__ . '/main-local.php';
if (file_exists($localConfig)) {
$config = yii\helpers\ArrayHelper::merge($config, require $localConfig);
}
删了它,或者把 main-local.php 放到别的目录(比如 config/local/),它就彻底失效。另外注意:
-
file_exists()检查失败时不会报错,但配置也不会加载——静默丢弃,很难排查 - Docker 部署时容易因 COPY 权限问题导致
main-local.php不可读,require报failed to open stream - 不要用
include_once替代require,后者失败会中断启动,前者可能只是返回false导致空配置
环境变量和配置文件混用时,覆盖优先级要手动控制
比如数据库配置既想从 params-local.php 读,又想支持 getenv('DB_DSN'),就不能依赖文件合并顺序来“覆盖”,而要在具体组件定义里显式取值:
'db' => [
'class' => 'yii\db\Connection',
'dsn' => getenv('DB_DSN') ?: $params['db']['dsn'],
'username' => getenv('DB_USER') ?: $params['db']['username'],
],
否则一旦 $params['db'] 本身为空,ArrayHelper::merge() 会把它整个删掉,而不是留空字段供 getenv 回退。
真正容易被忽略的点是:**配置合并只发生在应用启动初期,所有 getenv() 调用必须在 require 配置文件之前完成,否则变量还没读到,配置就已经固定了**。


















