Drupal 渲染注册流程剖析
昨天晚上遇到一个很纠结的问题,是关于 Drupal Theme 的 Template 文件路径问题。原则上只要 Theme 主题目录下存在同名的 tpl.php 的话,Drupal 将不再使用 Module 目录下的默认模板,出现的问题则不同,Drupal 找不到 Theme 目录下的文件而总是用原默认模板。问题代码如下
function taxonpage_theme()
{
return array(
'taxonpage_element' => array(
'arguments' => array('content' => ''),
'template' => 'taxonpage',
),
);
}
渲染 taxonpage_element 后,Drupal 主题引擎会找 Module 目录下的 taxonpage.tpl.php,而在 garland 主题下实现了新的同名模板文件 taxonpage.tpl.php 并更新缓存后,新的模板文件并无起作用,Devel 拦截发现装载的依然是原路径上的模板文件。
借此机会,下午细看了 Drupal 的 theme.inc,问题关键定位在 theme registry 的结果,在 _theme_build_registry 函数内,程序按照四个步骤顺序生成 registry,分别是 module 级 (就是 Module 内实现的 [moduleName]_theme),base_theme 级 (父类主题,与本文问题无关,不讨论),theme_engine 级 (模板引擎,本文仅讨论基于 Drupal 的 phptemplate 模板引擎) 和 theme 级 (就是 Theme 内实现的 [themeName]_theme,与本文问题无关,不讨论)。
module 级步骤根据 [moduleName]_theme 的定义,找相应的渲染行为,若声明了 'function' 则用指定函数渲染,若声明了 'template' 则用指定模板文件渲染,缺省则调用 theme_hook 函数渲染。本例中声明为
'template' => 'taxonpage',
因此 registry 结果中 'theme path' 为 Module 所在路径,'template' 为 taxonpage。
theme_engine 级步骤则根据 phptemplate_theme 方法来修正由 module 级产生的 registry。若渲染行为声明为 'function' 时,系统会检查是否实现了 phptemplate_hook (如果是其它非 phptemplate 模板引擎,函数名为 [engineName]_hook) 和 [themeName]_hook 函数,优先级为 [themeName]_hook > [engineName]_hook > theme_hook;若声明为 'template' 时,系统会检查在当前主题下是否能找到指定的模板文件,详细可以阅读 theme.inc 内的 drupal_find_theme_templates 方法,该方法会找出所有主题根目录下的 tpl.php 文件,然后检查文件名是否 registry 里已存在的 hook,如果匹配则修正 registry 的结果,下面是关键代码
if (($pos = strpos($template, '.')) !== FALSE) {
$template = substr($template, 0, $pos); // 截取去除 .tpl.php 后缀
}
$hook = strtr($template, '-', '_'); // 用文件名作 hook 名
if (isset($cache[$hook])) {
// 在 registry 里找 hook,问题在此出现,因本例中 hook 跟 template 名不相同
$templates[$hook] = array(
'template' => $template,
'path' => dirname($file->filename),
);
}
显然,由于本例中 hook 名为 taxonpage_element,而在主题目录中找到的 template 名为 taxonpage,因此 registry 并无修改结果。由于 Drupal 使用 hook 作为 registry 的索引,通过 template 名找 hook 需要遍历整个 registry,将会造成非常严重的性能损耗,且 template 跟 hook 也很可能是一对多的关系,因此 Drupal 这个做法也是无奈之举,这也是使我跟同事纠结很久的原因。
解决办法很简单,把 'template' 设置成 hook 同名,或主题目录下的模板文件使用 [hook].tpl.php。
杯具啊~~~~~~
看完本文应该会对 Drupal 的 theme 流程有更深入了解,下图是 Drupal Theme Flow
