自定义 UrlRule 必须继承 yii\web\UrlRule 并同时重写 createUrl 和 parseRequest 方法,确保正反向逻辑一致;需在 config/web.php 中以对象形式注册,显式声明 pattern 和 route,并注意参数处理、正则验证及缓存清理。

UrlRule 类继承和重写 createUrl 与 parseRequest
Yii2 的 UrlRule 默认只支持简单路由映射和参数占位符,真要实现动态前缀、多语言路径、或权限级 URL 过滤,必须自定义类。核心是继承 yii\web\UrlRule,然后重写两个关键方法:createUrl(生成 URL 时调用)和 parseRequest(解析请求时调用)。别只重写一个——否则正向生成和反向解析会不一致,导致 Url::to() 出错或 404。
常见错误现象:URL 生成正常,但访问时 404;或者能访问,但 Url::to(['post/view', 'id' => 123]) 返回空字符串。大概率是两个方法逻辑没对齐,比如 parseRequest 从 /zh-CN/article/123 提取了 lang 和 id,但 createUrl 没把 lang 当作必要参数参与拼接。
- 必须在
createUrl中显式检查并保留所有影响路由匹配的参数(如lang、version),否则生成的 URL 缺失关键段 -
parseRequest返回数组格式固定:[[$route, $params], ...],第一个元素是路由名,第二个是参数键值对;返回false表示不匹配 - 如果规则依赖运行时状态(如当前用户语言),避免在构造函数里缓存,而应在两个方法中实时获取
配置里怎么注册自定义 UrlRule
自定义类写好后,不能直接塞进 urlManager.rules 数组里当字符串用。必须实例化,并传入必要配置项。否则 Yii2 会尝试用默认构造函数初始化,导致 pattern、route 等属性为空,规则失效。
正确做法是在 config/web.php 的 urlManager 配置中,把规则写成对象形式:
'rules' => [
[
'class' => 'app\components\MultiLangUrlRule',
'pattern' => '<lang:\w+>/<controller:\w+>/<action:\w+>',
'route' => '<controller>/<action>',
'suffix' => '',
],
]
注意:pattern 和 route 仍需声明,即使你的自定义类内部已硬编码——因为 Yii2 初始化时会先读这两个字段做基础校验。漏掉会导致 Invalid Configuration 错误。
- 不要在
rules数组里混用字符串规则和对象规则,容易触发 PHP 类型判断异常 - 如果多个规则有重叠 pattern(比如都匹配
/api/*),顺序很重要:靠前的规则优先匹配,后面的一概忽略 - 调试时可在
parseRequest开头加Yii::debug($request->getPathInfo(), __METHOD__),看实际进来的 path 是什么
为什么 createUrl 里要用 $params 而不是 $this->defaults
新手常误以为 defaults 是兜底参数,能在 createUrl 中直接拿来拼 URL。错。Yii2 在调用 createUrl 前,已经把 defaults 合并进 $params 了,你拿到的 $params 就是最终参数集。如果还手动去读 $this->defaults,反而可能覆盖用户传的实际值。
典型场景:你定义了 'defaults' => ['lang' => 'en'],但用户调用 Url::to(['post/index', 'lang' => 'zh-CN'])。此时 $params 已是 ['lang' => 'zh-CN', ...]。若你在 createUrl 里又写 $params['lang'] = $this->defaults['lang'],就强行回退到 en,URL 生成错误。
-
$params是唯一可信输入,所有逻辑基于它展开 - 若需 fallback 行为(比如 lang 缺失时补默认值),应先判断
isset($params['lang']),再决定是否插入 -
$this->defaults只在初始化和规则匹配阶段起作用,运行时不参与 URL 构造
调试失败时最该查的三个地方
自定义 UrlRule 不生效,90% 的问题出在这三处,而不是逻辑本身。
- 检查
urlManager.enablePrettyUrl是否为true,且showScriptName设为false;否则所有自定义规则被绕过,走默认index.php?r=xxx模式 - 确认
urlManager.rules数组里没有语法错误(比如漏逗号、引号不闭合),PHP 解析失败会导致整个 rules 数组为空,静默降级 - 打开
urlManager.cache时,修改规则后必须清空 runtime/cache 目录下的urlManager*文件,否则旧规则缓存一直生效
真正麻烦的是 pattern 正则写错但没报错——比如 <id:\d+> 写成 <id:d+>,它不会抛异常,只是永远不匹配。建议在 parseRequest 开头加一行 if (preg_match($this->pattern, $pathInfo)) { Yii::info('matched', __METHOD__); } 快速验证正则是否生效。


















