作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com
如果,我们提供了breadcrumb.tpl.php,但是却无法在当前主题下面覆写的话,这个模板文件的作用就大打折扣了。从发现这个问题以后,我就多次尝试去解决它。首先是借助于Google,有什么技术问题,先问Google。这是我在Google上使用的搜索词“hook_theme_registry_alter template file could not be override”。
很幸运,有人遇到了同样的问题http://drupal.org/node/1424048,不幸的是,没有解决办法,如果你现在浏览这个问题的话,你会发现下面已经给出了解决办法,这个解决办法就是老葛给出来的。
我找到了一个类似的:
http://www.metachunk.com/blog/adding-module-path-drupal-7-theme-registry
在这篇文章里面,作者解决一个类似的问题,和我这里解决的问题还不完全一样,但是却为我们解决问题指明了方向。它是想在自己的模块目录下面,为模板文件提供一个模板建议,比如block--module--delta.tpl.php。这篇文章在解决这个问题的时候,也是花费了很大功夫的。它是这样解决的:
/**
* Implements hook_theme_registry_alter()
**/
function mymodule_theme_registry_alter(&$theme_registry) {
$mod_path = drupal_get_path('module', 'mymodule');
$theme_registry_copy = $theme_registry; // munge on a copy
_theme_process_registry($theme_registry_copy, 'phptemplate', 'theme_engine', 'pow', $mod_path);
$theme_registry += array_diff_key($theme_registry_copy, $theme_registry);
$hooks = array('node');
foreach ($hooks as $h) {
_mymodule_insert_after_first_element($theme_registry[$h]['theme paths'], $mod_path);
}
}
/**
* Helper function for re-ordering arrays (needed by theme_registry_alter)
*/
function _mymodule_insert_after_first_element(&$a, $element) {
if(is_array($a)) {
$first_element = array_shift($a);
array_unshift($a, $first_element, $element);
}
}
在hook_theme_registry_alter调用_theme_process_registry这种方式,让我眼前一亮,我的直觉告诉我,这种方式也能够解决我们的问题。开始的时候,我并没有看到它的代码的具体含义,但是我却做了这样的一件事情,认真的阅读了_theme_process_registry这个方法,以及_theme_build_registry,这两个函数里面的代码我读了很多遍,每个函数都认真的读过3遍以上的,弄清楚了函数里面每行代码的作用。我们来看一下_theme_build_registry的代码:
function _theme_build_registry($theme, $base_theme, $theme_engine) {
$cache = array();
// First, process the theme hooks advertised by modules. This will
// serve as the basic registry. Since the list of enabled modules is the same
// regardless of the theme used, this is cached in its own entry to save
// building it for every theme.
if ($cached = cache_get('theme_registry:build:modules')) {
$cache = $cached->data;
}
else {
foreach (module_implements('theme') as $module) {
_theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));
}
// Only cache this registry if all modules are loaded.
if (module_load_all(NULL)) {
cache_set('theme_registry:build:modules', $cache);
}
}
// Process each base theme.
foreach ($base_theme as $base) {
// If the base theme uses a theme engine, process its hooks.
$base_path = dirname($base->filename);
if ($theme_engine) {
_theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);
}
_theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);
}
// And then the same thing, but for the theme.
if ($theme_engine) {
_theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));
}
// Finally, hooks provided by the theme itself.
_theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));
// Let modules alter the registry.
drupal_alter('theme_registry', $cache);
// Optimize the registry to not have empty arrays for functions.
foreach ($cache as $hook => $info) {
foreach (array('preprocess functions', 'process functions') as $phase) {
if (empty($info[$phase])) {
unset($cache[$hook][$phase]);
}
}
}
return $cache;
}
这是用来构建主题的注册表的,大致的流程如下:
而注册的具体操作,则是委托给了_theme_process_registry。走到这里的时候,我做了一个非常大胆的尝试,我修改了这里的流程,这是我修改后的流程:
这是我修改后的代码:
function _theme_build_registry($theme, $base_theme, $theme_engine) {
$cache = array();
// First, process the theme hooks advertised by modules. This will
// serve as the basic registry. Since the list of enabled modules is the same
// regardless of the theme used, this is cached in its own entry to save
// building it for every theme.
if ($cached = cache_get('theme_registry:build:modules')) {
$cache = $cached->data;
}
else {
foreach (module_implements('theme') as $module) {
_theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));
}
// Only cache this registry if all modules are loaded.
if (module_load_all(NULL)) {
cache_set('theme_registry:build:modules', $cache);
}
}
// Let modules alter the registry.
drupal_alter('theme_registry', $cache);
// Process each base theme.
foreach ($base_theme as $base) {
// If the base theme uses a theme engine, process its hooks.
$base_path = dirname($base->filename);
if ($theme_engine) {
_theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);
}
_theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);
}
// And then the same thing, but for the theme.
if ($theme_engine) {
_theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));
}
// Finally, hooks provided by the theme itself.
_theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));
// Optimize the registry to not have empty arrays for functions.
foreach ($cache as $hook => $info) {
foreach (array('preprocess functions', 'process functions') as $phase) {
if (empty($info[$phase])) {
unset($cache[$hook][$phase]);
}
}
}
return $cache;
}
然后,我清除缓存,测试,竟然可以了。我竟然通过这种方式把问题解决掉了。这个时候,我认识到了一个问题,“基主题的主题函数/模板文件注册”这个流程前面,应该放置drupal_alter('theme_registry', $cache);这段代码,或者放置一段这样的代码:
drupal_alter('theme', $cache);
为什么这么说呢? 我们知道,主题引擎、主题,都是位于模块的上层的,虽然在Drupal7里面,模块可以使用hook_theme,主题引擎、主题也可以使用hook_theme。但是由于主题本身位于最上层,所以模板是没有必要通过hook_theme_registry_alter修改主题里面提供的特有的主题函数/模板文件,如果想要修改主题里面的,只需要在主题层直接修改就是了。而主题层的_theme_process_registry,还包含了对模块层已有主题函数/模板文件的覆写功能,这也是包含了的。
在此之前,我还做过这样的调试:
function breadcrumb2_theme_registry_alter(&$theme_registry) {
//print debug($theme_registry['breadcrumb']);
if (isset($theme_registry['breadcrumb'])) {
$path = drupal_get_path('module', 'breadcrumb2');
//$path = path_to_theme();
$theme_registry['breadcrumb']['path'] = $path;
$theme_registry['breadcrumb']['theme path'] = $path;
$theme_registry['breadcrumb']['template'] = 'breadcrumb';
$theme_registry['breadcrumb']['function'] = NULL;
unset($theme_registry['breadcrumb']['function']);
}
// print debug($theme_registry['breadcrumb']);
//print debug($theme_registry['node']);
print debug($theme_registry);
}
就是直接将$theme_registry的结构在前台输出出来,然后观察里面每一元素的结构,同时重点研究了($theme_registry['node']、($theme_registry['page']、($theme_registry[' breadcrumb ']的输出。这是没有修改核心代码时的输出:
array (
'variables' =>
array (
'breadcrumb' => NULL,
),
'type' => 'module',
'theme path' => 'sites/all/modules/breadcrumb2',
'preprocess functions' =>
array (
0 => 'template_preprocess_breadcrumb',
),
'process functions' =>
array (
),
'path' => 'sites/all/modules/breadcrumb2',
'template' => 'breadcrumb',
)
Debug:
array (
'template' => 'page',
'path' => 'themes/seven',
'type' => 'theme_engine',
'theme path' => 'themes/seven',
'render element' => 'page',
'preprocess functions' =>
array (
0 => 'template_preprocess',
1 => 'template_preprocess_page',
2 => 'contextual_preprocess',
3 => 'overlay_preprocess_page',
4 => 'shortcut_preprocess_page',
5 => 'seven_preprocess_page',
),
'process functions' =>
array (
0 => 'template_process',
1 => 'template_process_page',
2 => 'rdf_process',
),
)
我们看到,这里的数组,里面包含的信息远比hook_theme里面包含的多,而对于$theme_registry['page'],从它里面的结构可以看出,已经被当前主题seven给覆写了。