解析_theme_build_registry()和_theme_process_registry()
Drupal使用_theme_build_registry()和_theme_process_registry()两个函数构建theme registry。theme registry是theme hook的集合组数。这里以practice模块定义两个theme hook为例,说明一下theme registry的构建过程。
环境:
1. cool_breadcrumbs:定义在practice_theme()中,用function实现。
2. cool_messages:定义在practice_theme()中,用template实现。
3. 使用默认PHPTemplate主题引擎。
4. 使用sub_bartik主题,该主题继承自bartik。
_theme_build_registry()的函数原型:
function _theme_build_registry($theme, $base_theme, $theme_engine) {
... ...
}
_theme_build_registry()调用了_theme_process_registry(),而且还调用了很多次:
1. 所有实现了hook_theme()钩子的模块,每个模块都要调用一次:
foreach (module_implements('theme') as $module) {
// 注意调用时$name=模块名称, $type='module', $theme=模块名称, $path=模块路径
_theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));
}
2. 每个base theme都要调用一次。Drupal中的theme是可以继承的,而且可以多级继承。
// 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) {
// 注意调用时$name=theme engine name, $type='base_theme_engine', $theme=base theme name, $path=base theme path
// 对theme engine调用时,$theme都是对应的theme name, $path都是对应的theme path
_theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);
}
// 注意调用时$name=base theme name, $type='base_theme', $theme=base theme name, $path=base theme path
_theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);
}
3. 对theme engine调用一次。
// And then the same thing, but for the theme.
if ($theme_engine) {
// 注意调用时$name=theme engine name, $type='theme_engine', $theme=theme name, $path=theme path
// 对theme engine调用时,$theme都是对应的theme name, $path都是对应的theme path
_theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));
}
4. 对theme调用一次。
// Finally, hooks provided by the theme itself.
// 注意调用时$name=theme name, $type='theme', $theme=theme name, $path=theme path
_theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));
_theme_process_registry()的函数原型:
function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
... ... // 注意这里的参数$cache是传址的
}
从上面的_theme_build_registry()知道,_theme_build_registry()会在多种情况下被执行:
1.module
2. base theme engine
3. base theme
4. theme engine
5. theme
每种情况被执行时_theme_build_registry()都会找是否有hook_theme()钩子存在,在hook_theme返回的结果被处理后,合并到$cache,注意$cache参数是传址的。
$variable_process_phases = array(
'preprocess functions' => 'preprocess',
'process functions' => 'process',
); $hook_defaults = array(
'variables' => TRUE,
'render element' => TRUE,
'pattern' => TRUE,
'base hook' => TRUE,
); // Invoke the hook_theme() implementation, process what is returned, and
// merge it into $cache.
$function = $name . '_theme';
if (function_exists($function)) {
$result = $function($cache, $type, $theme, $path);
foreach ($result as $hook => $info) {
// When a theme or engine overrides a module's theme function
// $result[$hook] will only contain key/value pairs for information being
// overridden. Pull the rest of the information from what was defined by
// an earlier hook. // Fill in the type and path of the module, theme, or engine that
// implements this theme function.
$result[$hook]['type'] = $type;
$result[$hook]['theme path'] = $path; // 注意这里的type和theme path,这和传入的参数密切相关
// 当type=module时,theme path=module path
// 当type=base theme engine时,因为传入的是base theme path,所以theme path=base theme path
// 当type=base them时,theme path=base theme path
// 当type=theme engine时,因为传入的是current theme path,所以theme path=current theme path
// 当type=theme时,theme path=current theme path // If function and file are omitted, default to standard naming
// conventions.
if (!isset($info['template']) && !isset($info['function'])) {
$result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name . '_') . $hook;
} // 在hook_theme()定义theme hook时,一般会设置template或者function
// 如果两者都没有设置,则默认地type=module时为theme_$hook(),或者$name_$hook()
// 例如type=theme,则可能是bartik_cool_messages()
// 或者type=theme engine,则可能是phptemplate_cool_engine() if (isset($cache[$hook]['includes'])) {
$result[$hook]['includes'] = $cache[$hook]['includes'];
} // $cache[$hook]是什么意思?
// 在后面会看到这样一句代码:$cache = $result + $cache;
// $cache合并了当前的hook_theme()结果$result,并确保$result优先$cache
// 举例来说,可能在practice模块中定义了cool_messsages,
// 当_theme_process_registry()在type=module被执行完成后,$cache就应该有了cool_messages。
// 后面的bartik又修改了cool_messages的定义,当type=theme执行完成后,
// $cache的就是两者合并后的结果,并且后面定义的优先于前面定义的。 // If the theme implementation defines a file, then also use the path
// that it defined. Otherwise use the default path. This allows
// system.module to declare theme functions on behalf of core .include
// files.
if (isset($info['file'])) {
$include_file = isset($info['path']) ? $info['path'] : $path;
$include_file .= '/' . $info['file'];
include_once DRUPAL_ROOT . '/' . $include_file;
$result[$hook]['includes'][] = $include_file;
} // 可以定义一个专门的theme function file,用$info['file']说明
// 这个theme function file可能不属于当前模块,例如可能是system模块用到core模块里面的文件,
// 这时候需要用$info['path']说明这个theme function file所在的目录 // If the default keys are not set, use the default values registered
// by the module.
if (isset($cache[$hook])) {
$result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
} // The following apply only to theming hooks implemented as templates.
if (isset($info['template'])) {
// Prepend the current theming path when none is set.
if (!isset($info['path'])) {
// 将template转成完整路径
$result[$hook]['template'] = $path . '/' . $info['template'];
}
} // Allow variable processors for all theming hooks, whether the hook is
// implemented as a template or as a function.
foreach ($variable_process_phases as $phase_key => $phase) {
// Check for existing variable processors. Ensure arrayness.
if (!isset($info[$phase_key]) || !is_array($info[$phase_key])) {
$info[$phase_key] = array();
$prefixes = array();
if ($type == 'module') {
// Default variable processor prefix.
$prefixes[] = 'template';
// Add all modules so they can intervene with their own variable
// processors. This allows them to provide variable processors even
// if they are not the owner of the current hook.
$prefixes += module_list();
}
elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
// Theme engines get an extra set that come before the normally
// named variable processors.
$prefixes[] = $name . '_engine';
// The theme engine registers on behalf of the theme using the
// theme's name.
$prefixes[] = $theme;
}
else {
// This applies when the theme manually registers their own variable
// processors.
$prefixes[] = $name;
}
foreach ($prefixes as $prefix) {
// Only use non-hook-specific variable processors for theming hooks
// implemented as templates. See theme().
if (isset($info['template']) && function_exists($prefix . '_' . $phase)) {
$info[$phase_key][] = $prefix . '_' . $phase;
}
if (function_exists($prefix . '_' . $phase . '_' . $hook)) {
$info[$phase_key][] = $prefix . '_' . $phase . '_' . $hook;
}
} // variable processor,变量处理器,这是一个数组
// 变量处理器分两种:preprocess function和皮肉process function
// 当type=module时,按照下列顺序查找(以cool_messages为例):
// template_preprocess()
// template_preprocess_cool_messages()
// moduleA_preprocess()
// moduleA_preprocess_cool_messages()
// ... ...
// moduleZ_preprocess()
// moduleZ_preprocess_cool_messages()
// ---
// template_process()
// template_process_cool_messages()
// moduleA_process()
// moduleA_process_cool_messages()
// ... ...
// moduleZ_process()
// moduleZ_process_cool_messages()
//
// 当type=theme_engine/base_theme_engine时,按照下列顺序查找:
// phptemplate_engine_preprocess()
// phptemplate_engine_preprocess_cool_messages()
// bartik_preprocess()
// bartik_preprocess_cool_messages()
// ---
// phptemplate_engine_process()
// phptemplate_engine_process_cool_messages()
// bartik_process()
// bartik_process_cool_messages()
//
// 当type=theme/base_theme时,按照下列顺序查找:
// bartik_preprocess()
// bartik_preprocess_cool_messages()
// ---
// bartik_process()
// bartik_process_cool_messages() }
// Check for the override flag and prevent the cached variable
// processors from being used. This allows themes or theme engines to
// remove variable processors set earlier in the registry build.
if (!empty($info['override ' . $phase_key])) {
// Flag not needed inside the registry.
unset($result[$hook]['override ' . $phase_key]);
// 色泽override preprocess functions这样的参数,
// 可以忽略其它情况下的变量处理器
}
elseif (isset($cache[$hook][$phase_key]) && is_array($cache[$hook][$phase_key])) {
$info[$phase_key] = array_merge($cache[$hook][$phase_key], $info[$phase_key]);
// 这里将$cache[$hook][$phase_key]和$info[$phase_key]做了合并,
// 也就是说可能出现这样的情况:
// practice_preprocess()/practice_preproces_cool_messages() 模块practice的缺省处理器
// other_preproces_cool_messages() 模块other定义的cool_messages处理器
// phptemplate_engine_preprocess_cool_messages() 主题引擎定义的cool_messages处理器
// bartik_preprocess_cool_message() 主题bartik定义的cool_messages处理器
// sub_bartik_preprocess_cool_message() 主题sub_bartik定义的cool_messages处理器
// 这些变量处理器是没有顺序的,这一点要特别注意
}
$result[$hook][$phase_key] = $info[$phase_key];
}
} // Merge the newly created theme hooks into the existing cache.
// 合并$result到$cache,这一句代码很重要
// _theme_build_registry()执行完成后,$cache就是最后的theme registry
$cache = $result + $cache;
}
当theme/base theme被执行时,_theme_process_registry()会自动关联在theme里面定义的变量处理器:
// $result是hook_theme()返回的结果
// phptemplate_theme()做了两件事情:
// 一是搜索get_defined_functions()中所有的theme function,
// 二是搜索主题目录下所有以.tpl.php结尾的theme template,
// 然后返回到$result,再保存到$cache.
// 例如,theme目录中可以存在一个cool_sideleft.tpl.php这样一个theme template,
// 不需要用hook_theme()定义这个theme hook,phptemplate会自动搜索这个theme template注册到$cache。
//
// phptemplate执行在前,theme执行在后,
// 当theme执行时,$cache已经包含了them中定义所有theme function和theme template。
// 这时候,可以在theme定义变量处理器,例如bartik_preprocess_cool_sideleft()。
// 执行下面的代码可以将bartik_preprocess_cool_sideleft()关联到cool_sideleft。 // Let themes have variable processors even if they didn't register a
// template.
if ($type == 'theme' || $type == 'base_theme') {
foreach ($cache as $hook => $info) {
// Check only if not registered by the theme or engine.
if (empty($result[$hook])) {
foreach ($variable_process_phases as $phase_key => $phase) {
if (!isset($info[$phase_key])) {
$cache[$hook][$phase_key] = array();
}
// Only use non-hook-specific variable processors for theming hooks
// implemented as templates. See theme().
if (isset($info['template']) && function_exists($name . '_' . $phase)) {
$cache[$hook][$phase_key][] = $name . '_' . $phase;
}
if (function_exists($name . '_' . $phase . '_' . $hook)) {
$cache[$hook][$phase_key][] = $name . '_' . $phase . '_' . $hook;
$cache[$hook]['theme path'] = $path;
}
// Ensure uniqueness.
$cache[$hook][$phase_key] = array_unique($cache[$hook][$phase_key]);
}
}
}
}
情境A:接文章开头假设的例子,我们用practice_theme()定义两个theme hook:
function practice_theme() {
return array(
'cool_breadcrumbs' => array(
'render element' => 'breadcrumbs',
'function' => 'practice_cool_breadcrumbs',
),
'cool_messages' => array(
'render element' => 'messages',
'template' => 'cool_messages',
),
);
}
要完成上面的定义,需要建立一个名为practice_cool_breadcrumbs的theme function:
function practice_cool_breadcrumbs($variables) {
return '<div>Demo Breadcrumbs</div>';
}
还需要建立一个名为cool_messages.tpl.php的theme template:
<?php
// sites/all/modules/practice/cool_messages.tpl.php
print '<div>Demo Messages</div>';
到这里,practice模块新增的两个theme hook就完成了。当_theme_process_registry($type='module', $name='practice')执行完成后,对应的$result结果如下:
$result = array(
'cool_breadcrumbs' => array(
'render element' => 'breadcrumbs',
'function' => 'practice_cool_breadcrumbs',
'type' => 'module',
'theme path' => 'sites/all/modules/practice',
'preprocess functions' => array(),
'process functions' => array(),
),
'cool_messages' => array(
'render element' => 'messages',
'template' => 'sites/all/modules/practice/cool_messages', // template转成了完整路径
'type' => 'module',
'theme path' => 'sites/all/modules/practice',
'preprocess functions' => array( // 用template实现的theme hook,默认的会加上template_preprocess/process()
0 => 'template_preprocess',
),
'process functions' => array(
0 => 'template_process',
),
),
);
因为没有在其它地方修改这两个theme hook,所有最后的$cache和$result是一样的。
情境B:续情境A。theme hook可以定义变量处理器preprocess function和process function。实质上,情境A中的template_preprocess()和template_process()也是变量处理器。
function practice_preprocess(&$variables, $hook) { ... }
function practice_process(&$variables, $hook) { ... }
_theme_process_registry($type='module', $name='practice')执行完成后,$result结果如下:
$result = array(
'cool_breadcrumbs' => array(
... ...
'preprocess functions' => array(), // 不带theme hook名称的变量处理器不适用于function
'process functions' => array(),
),
'cool_messages' => array(
... ...
'preprocess functions' => array(
0 => 'template_preprocess',
1 => 'practice_preprocess', // 不带theme hook名称的变量处理器只针对template
),
'process functions' => array(
0 => 'template_process',
1 => 'practice_preprocess',
),
),
);
上面的变量处理器不带theme hook名称,这样的处理器适用与所有用template实现的theme hook。带名称的处理器只适用于指定名称的theme hook:
function practice_preprocess_cool_breadcrumbs(&$variables, $hook) { ... } $result = array(
'cool_breadcrumbs' => array(
'preprocess functions' => array(
0 => 'practice_preprocess_cool_breadcrumbs', // 带名称的处理器只适用于指定名称的theme hook
),
'process functions' => array(),
),
... ...
);
情境C:续情境A。情境B是在theme hook定义所在的模块practice建立处理器,在其它模块也是可以的。其它模块定义处理器也可以分通用(不带theme hook名称,适用与所有的template)和专用(带theme hook名称,只适用于指定名称)两种。
function other_preprocess(&$variables, $hook) { ... }
function other_preprocess_cool_messages(&$variables, $hook) { ... } $result = array(
'cool_messages' => array(
'preprocess functions' => array(
0 => 'template_preprocess',
1 => 'other_preprocess',
2 => 'other_preprocess_cool_messages',
),
'process functions' => array(
0 => 'template_process',
),
),
);
情境D:续情境A。在theme中也可以为theme hook定义处理器。这部分的处理是在_theme_process_registry($type='theme/base_theme')的最后实现的:
if ($type == 'theme' || $type == 'base_theme') {
foreach ($cache as $hook => $info) { // 只针对在$cache中已经注册过的theme hook
// Check only if not registered by the theme or engine.
if (empty($result[$hook])) { // empty($result[$hook])表明是没有在theme的hook_theme()钩子中定义
foreach ($variable_process_phases as $phase_key => $phase) {
if (!isset($info[$phase_key])) {
$cache[$hook][$phase_key] = array();
}
// Only use non-hook-specific variable processors for theming hooks
// implemented as templates. See theme().
if (isset($info['template']) && function_exists($name . '_' . $phase)) {
$cache[$hook][$phase_key][] = $name . '_' . $phase;
}
if (function_exists($name . '_' . $phase . '_' . $hook)) {
$cache[$hook][$phase_key][] = $name . '_' . $phase . '_' . $hook;
$cache[$hook]['theme path'] = $path;
}
// Ensure uniqueness.
$cache[$hook][$phase_key] = array_unique($cache[$hook][$phase_key]);
}
}
}
}
例如,情境A中的cool_messages,可以在theme中再定义一个处理器:
function bartik_preprocess_cool_messages(&$variables) { ... } // _theme_process_registry($type='module', $name='practice')执行完毕后的$cache
$cache = array(
'cool_messages' => array(
'preprocess functions' => array(
0 => 'template_preprocess',
),
'process functions' => array(
0 => 'template_process',
),
),
); // _theme_process_registry($type='theme', $name='bartik')执行完毕后的$cache
$cache = array(
'cool_messages' => array(
'preprocess functions' => array(
0 => 'template_preprocess',
1 => 'bartik_preprocess_cool_messages', // bartik中定义的处理器
),
'process functions' => array(
0 => 'template_process',
),
),
);
情境E:续情境A。hook_theme()定义的theme hook可以在主题theme中被重载。这里的重载有几个意思:
1. theme function重载。例如,cool_breadcrumbs默认由practice_cool_breadcrumbs()输出,可以在主题中定义bartik_cool_breadcrumbs()实现重载。
2. theme template重载。例如,cool_messages默认由practice/cool_messages.tpl.php输出,可以在主题中定义cool_messages.tpl.php实现重载。
3. theme suggestion重载。例如,名为page的theme hook,默认可能是在某个模块中顶一顶,可以在主题中定义bartik_page()实现函数重载,定义bartik_page__node()/bartik_page__node__1()这样的函数实现theme suggestion函数重载;也可以在主题中定义page.tpl.php实现模板重载,定义page__node.tpl.php/page__node__1.tpl.php这样的模板实现theme suggestion模板重载。
function phptemplate_theme($existing, $type, $theme, $path) {
$templates = drupal_find_theme_functions($existing, array($theme));
$templates += drupal_find_theme_templates($existing, '.tpl.php', $path);
return $templates;
} function drupal_find_theme_functions($cache, $prefixes) {
$implementations = array();
$functions = get_defined_functions(); // 查找以theme名称开头的theme functions
// 注意这里只处理$cache中已注册的theme hook
// $prefix指的是theme名称, 例如bartik foreach ($cache as $hook => $info) {
foreach ($prefixes as $prefix) { // Find theme functions that implement possible "suggestion" variants of
// registered theme hooks and add those as new registered theme hooks.
// The 'pattern' key defines a common prefix that all suggestions must
// start with. The default is the name of the hook followed by '__'. An
// 'base hook' key is added to each entry made for a found suggestion,
// so that common functionality can be implemented for all suggestions of
// the same base hook. To keep things simple, deep hierarchy of
// suggestions is not supported: each suggestion's 'base hook' key
// refers to a base hook, not to another suggestion, and all suggestions
// are found using the base hook's pattern, not a pattern from an
// intermediary suggestion. // 这里查找的是theme suggestion functions
// 例如,在$cahce中已存在名为page的theme hook
// 可以在theme中定义一系列的theme suggestion functions:
// bartik_page__node()
// bartik_page__node__1()
//
// theme suggestion会做为一个新的theme hook被注册,例如
// $cache['page__node__1'] = ...
//
// theme suggesion hook的base hook被标记为$hook $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
if (!isset($info['base hook']) && !empty($pattern)) {
$matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $functions['user']);
if ($matches) {
foreach ($matches as $match) {
$new_hook = substr($match, strlen($prefix) + 1);
$arg_name = isset($info['variables']) ? 'variables' : 'render element';
$implementations[$new_hook] = array(
'function' => $match,
$arg_name => $info[$arg_name],
'base hook' => $hook,
);
}
}
} // Find theme functions that implement registered theme hooks and include
// that in what is returned so that the registry knows that the theme has
// this implementation. // 如果存在bartik_page()函数,则覆盖hook_theme()定义的page实现
//
// 覆盖是在_theme_process_registry()处理的, $result是phptemplate_theme()返回的结果:
// if (isset($cache[$hook])) {
// $result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
// } if (function_exists($prefix . '_' . $hook)) {
$implementations[$hook] = array(
'function' => $prefix . '_' . $hook,
);
}
}
} return $implementations;
} function drupal_find_theme_templates($cache, $extension, $path) {
$implementations = array(); // Collect paths to all sub-themes grouped by base themes. These will be
// used for filtering. This allows base themes to have sub-themes in its
// folder hierarchy without affecting the base themes template discovery.
$theme_paths = array();
foreach (list_themes() as $theme_info) {
if (!empty($theme_info->base_theme)) {
$theme_paths[$theme_info->base_theme][$theme_info->name] = dirname($theme_info->filename);
}
}
foreach ($theme_paths as $basetheme => $subthemes) {
foreach ($subthemes as $subtheme => $subtheme_path) {
if (isset($theme_paths[$subtheme])) {
$theme_paths[$basetheme] = array_merge($theme_paths[$basetheme], $theme_paths[$subtheme]);
}
}
}
global $theme;
$subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : array(); // Escape the periods in the extension.
$regex = '/' . str_replace('.', '\.', $extension) . '$/';
// Get a listing of all template files in the path to search.
$files = drupal_system_listing($regex, $path, 'name', 0); // 在theme目录下查找所有以.tpl.php结尾的模板文件:
// page.tpl.php
// page__node.tpl.php
// page__node__1.tpl.php // Find templates that implement registered theme hooks and include that in
// what is returned so that the registry knows that the theme has this
// implementation.
foreach ($files as $template => $file) {
// Ignore sub-theme templates for the current theme.
if (strpos($file->uri, str_replace($subtheme_paths, '', $file->uri)) !== 0) {
continue;
}
// Chop off the remaining extensions if there are any. $template already
// has the rightmost extension removed, but there might still be more,
// such as with .tpl.php, which still has .tpl in $template at this point.
if (($pos = strpos($template, '.')) !== FALSE) {
$template = substr($template, 0, $pos);
}
// Transform - in filenames to _ to match function naming scheme
// for the purposes of searching.
$hook = strtr($template, '-', '_');
if (isset($cache[$hook])) {
// 只处理在$cache中注册的theme hook
// 如果存page.tpl.php模板, 则覆盖hook_theme()定义的page实现
$implementations[$hook] = array(
'template' => $template,
'path' => dirname($file->uri),
);
}
} // Find templates that implement possible "suggestion" variants of registered
// theme hooks and add those as new registered theme hooks. See
// drupal_find_theme_functions() for more information about suggestions and
// the use of 'pattern' and 'base hook'.
$patterns = array_keys($files);
foreach ($cache as $hook => $info) { // 循环处理在$cache中注册的theme hook
$pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
if (!isset($info['base hook']) && !empty($pattern)) {
// Transform _ in pattern to - to match file naming scheme
// for the purposes of searching.
$pattern = strtr($pattern, '_', '-'); // 这里查找的是theme suggestion templates
// theme suggestion会做为一个新的theme hook被注册,例如
// $cache['page__node__1'] = ...
//
// theme suggesion hook的base hook被标记为$hook $matches = preg_grep('/^' . $pattern . '/', $patterns);
if ($matches) {
foreach ($matches as $match) {
$file = substr($match, 0, strpos($match, '.'));
// Put the underscores back in for the hook name and register this
// pattern.
$arg_name = isset($info['variables']) ? 'variables' : 'render element';
$implementations[strtr($file, '-', '_')] = array(
'template' => $file,
'path' => dirname($files[$match]->uri),
$arg_name => $info[$arg_name],
'base hook' => $hook,
);
}
}
}
}
return $implementations;
}
情境F:独立情境。除了在module中用hook_theme()钩子定义theme hook外,也可以在theme和theme engine用hook_theme()钩子theme hook。
function bartik_theme() {
return array(
'cool_sideleft' => array(
'render element' => 'sideleft',
'template' => 'cool_sideleft',
),
);
} function bartik_preprocess_cool_sideleft(&$variables) { ... } $result = array(
'cool_sideleft' => array(
'render element' => 'sideleft',
'template' => 'themes/bartik/cool_sideleft',
'type' => 'theme',
'theme path' => 'themes/bartik',
'preprocess functions' => array(
0 => 'bartik_preprocess_cool_sideleft', // 没有template_preprocess(). 处理器要以bartik_开头。
),
'process functions' => array(),
),
);
解析_theme_build_registry()和_theme_process_registry()的更多相关文章
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- .NET Core中的认证管理解析
.NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...
- Html Agility Pack 解析Html
Hello 好久不见 哈哈,今天给大家分享一个解析Html的类库 Html Agility Pack.这个适用于想获取某网页里面的部分内容.今天就拿我的Csdn的博客列表来举例. 打开页面 用Fir ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- Asp.Net WebApi核心对象解析(下篇)
在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...
- 【知识必备】内存泄漏全解析,从此拒绝ANR,让OOM远离你的身边,跟内存泄漏say byebye
一.写在前面 对于C++来说,内存泄漏就是new出来的对象没有delete,俗称野指针:而对于java来说,就是new出来的Object放在Heap上无法被GC回收:而这里就把我之前的一篇内存泄漏的总 ...
- SQL Server 数据加密功能解析
SQL Server 数据加密功能解析 转载自: 腾云阁 https://www.qcloud.com/community/article/194 数据加密是数据库被破解.物理介质被盗.备份被窃取的最 ...
随机推荐
- [转]Android网格视图(GridView)
GridView的一些属性: 1.android:numColumns=”auto_fit” //GridView的列数设置为自动,也可以设置成2.3.4…… 2.android:columnWi ...
- SqlServer Base64解码中文
最近在做一个和拥有TurboCRM相关的项目,其中需要取出客户信息的联系人字段,经过查看,这个字段在存入时用Base64加密过了 这个功能在应用层实现是很方便的,但是由于一些特殊原因,只能放到SqlS ...
- VMware 11.0 简体中文版|附永久密钥
20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送) 国内私募机构九鼎控股打造,九鼎投资是在全国股 ...
- HTTP和HTTPS的区别,以及各自的优缺点
转自 https://www.cnblogs.com/wqhwe/p/5407468.html 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容 ...
- 【javaw.exe 两个】启动了两个javaw.exe 相关
大家开发 肯定见过 两条并存的现象吧! 关闭方式1: 在任务管理器中,挑选 内存占用较小的 那个 关闭即可!! 关闭方式2: 1.查找对应的端口占用的进程:netstat -aon|findstr ...
- Appium+python自动化9-SDK Manager
前言 SDK Manager到有哪些东西是必须安装的呢? 一.SDK Manager 1.双击打开SDK Manager界面
- 在Spark程序中使用压缩
当大片连续区域进行数据存储并且存储区域中数据重复性高的状况下,数据适合进行压缩.数组或者对象序列化后的数据块可以考虑压缩.所以序列化后的数据可以压缩,使数据紧缩,减少空间开销. 1. Spark对压缩 ...
- C++对象赋值的四种方式
1. 引用作为参数的方式传递. GetObject(Object& obj) { obj.value = value1; } 特点: 在外部构造一个对象. 把该对象以引用的方式传递到函数中. ...
- AJAX前台传过来的中文在后台获取是乱码问题
前台传值时加上encodeURI $.SaveForm({ url: "${basePath}/soft/mergeSoftAction_add.do?ids="+ids+&quo ...
- 文本相似性计算总结(余弦定理,simhash)及代码
最近在工作中要处理好多文本文档,要求找出和每个文档的相识的文档.通过查找资料总结如下几个计算方法: 1.余弦相似性 我举一个例子来说明,什么是"余弦相似性". 为了简单起见,我们先 ...