drupal_render()只是对theme()的调用做了包装,真正做任务的还是theme()。

function theme($hook, $variables = array()) {
... ...
}

theme()的开头检查了module_load_all()是否有执行。theme()只能在所有模块装入后才能执行。

// If called before all modules are loaded, we do not necessarily have a full
// theme registry to work with, and therefore cannot process the theme
// request properly. See also _theme_load_registry().
if (!module_load_all(NULL) && !defined('MAINTENANCE_MODE')) {
throw new Exception(t('theme() may not be called until all modules are loaded.'));
}

theme_get_registry()返回所有的theme hooks。

$hooks = theme_get_registry(FALSE);

参数$hook可以是一个数组,包含所有可用的备选theme hook。theme()会取第一个存在的theme hook。

// If an array of hook candidates were passed, use the first one that has an
// implementation.
if (is_array($hook)) {
foreach ($hook as $candidate) {
if (isset($hooks[$candidate])) {
break;
}
}
$hook = $candidate;
}

处理theme hook suggestion。什么是theme hook suggestion?以请求q=node/sports/5为例,可以优先匹配node__sports__5,再匹配node__sports,最后再匹配node。如果存在node__sports__5的theme hook,则后面的就不予考虑,后面的以此类推。注意theme hook suggestion是以双下划线分开的。

// If there's no implementation, check for more generic fallbacks. If there's
// still no implementation, log an error and return an empty string.
if (!isset($hooks[$hook])) {
// Iteratively strip everything after the last '__' delimiter, until an
// implementation is found.
while ($pos = strrpos($hook, '__')) {
$hook = substr($hook, 0, $pos);
if (isset($hooks[$hook])) {
break;
}
}
if (!isset($hooks[$hook])) {
// Only log a message when not trying theme suggestions ($hook being an
// array).
// 只有$hook是数组的时候才记录日志信息
if (!isset($candidate)) {
watchdog('theme', 'Theme hook %hook not found.', array('%hook' => $hook), WATCHDOG_WARNING);
}
return '';
}
}

$info保存当前hook信息。$theme_path是一个全局变量,在theme()执行过程中会替换为当前theme hook对应的路径,theme()后面会还原回来。

$info = $hooks[$hook];
global $theme_path;
$temp = $theme_path;
// point path_to_theme() to the currently used theme path:
$theme_path = $info['theme path'];

引入$info['includes']相关文件。

// Include a file if the theme function or variable processor is held
// elsewhere.
if (!empty($info['includes'])) {
foreach ($info['includes'] as $include_file) {
include_once DRUPAL_ROOT . '/' . $include_file;
}
}

重新整理$variables数组。

// If a renderable array is passed as $variables, then set $variables to
// the arguments expected by the theme function.
if (isset($variables['#theme']) || isset($variables['#theme_wrappers'])) {
$element = $variables;
$variables = array();
if (isset($info['variables'])) {
foreach (array_keys($info['variables']) as $name) {
if (isset($element["#$name"])) {
$variables[$name] = $element["#$name"];
}
}
}
else {
$variables[$info['render element']] = $element;
}
} // Merge in argument defaults.
if (!empty($info['variables'])) {
$variables += $info['variables'];
}
elseif (!empty($info['render element'])) {
$variables += array($info['render element'] => array());
}

定义theme hook时,允许定义theme hook执行时必需的变量和默认值。可以使用render element定义单个变量,也可以用variables定义多个变量。用render element定义单个变量的例子:

function practice_theme() {
return array(
'flash_messages' => array(
'render element' => 'messages',
'template' => 'flash_messages',
),
);
} // 整理后的$variables大致是这样:
$variables = array(
'messages' => $element,
);

用variables定义多个变量的例子:

function practice_theme() {
return array(
'flash_messages' => array(
'variables' => array(
'foo' => array(),
'bar' => array()
),
'template' => 'flash_messages',
),
);
} // 整理后的$variables大致是这样:
$variables = array(
'foo' => isset($element['foo']) ? $element['foo'] : array(),
'bar' => isset($element['bar']) ? $element['bar'] : array(),
);

如果$info['base hook']不为空,则后面调用的preprocess functions和process functions要是base hook的,不能是当前$hook的。但还是要保证theme_hook_suggestion是当前$hook。

// If the hook is a suggestion of a base hook, invoke the variable processors of
// the base hook, but retain the suggestion as a high priority suggestion to
// be used unless overridden by a variable processor function.
if (isset($info['base hook'])) {
$base_hook = $info['base hook'];
$base_hook_info = $hooks[$base_hook];
// Include files required by the base hook, since its variable processors
// might reside there.
if (!empty($base_hook_info['includes'])) {
foreach ($base_hook_info['includes'] as $include_file) {
include_once DRUPAL_ROOT . '/' . $include_file;
}
}
if (isset($base_hook_info['preprocess functions']) || isset($base_hook_info['process functions'])) {
$variables['theme_hook_suggestion'] = $hook;
$hook = $base_hook;
$info = $base_hook_info;
}
}

调用preprocess functions和process functions。这两类函数有两个目的,一是处理$variables,二是处理theme hook suggestion。theme_hook_suggestion的优先级大于theme_hook_suggestions(注意是suggestion复数),theme_hook_suggestions再安装FILO先进后出的原则匹配,最后加入的优先级最高。

// Invoke the variable processors, if any. The processors may specify
// alternate suggestions for which hook's template/function to use.
if (isset($info['preprocess functions']) || isset($info['process functions'])) {
$variables['theme_hook_suggestions'] = array();
foreach (array('preprocess functions', 'process functions') as $phase) {
if (!empty($info[$phase])) {
foreach ($info[$phase] as $processor_function) {
if (function_exists($processor_function)) {
// We don't want a poorly behaved process function changing $hook.
$hook_clone = $hook; // 不要让某些人将$hook搞坏了
$processor_function($variables, $hook_clone);
}
}
}
}
// If the preprocess/process functions specified hook suggestions, and the
// suggestion exists in the theme registry, use it instead of the hook that
// theme() was called with. This allows the preprocess/process step to
// route to a more specific theme hook. For example, a function may call
// theme('node', ...), but a preprocess function can add 'node__article' as
// a suggestion, enabling a theme to have an alternate template file for
// article nodes. Suggestions are checked in the following order:
// - The 'theme_hook_suggestion' variable is checked first. It overrides
// all others.
// - The 'theme_hook_suggestions' variable is checked in FILO order, so the
// last suggestion added to the array takes precedence over suggestions
// added earlier.
$suggestions = array();
if (!empty($variables['theme_hook_suggestions'])) {
$suggestions = $variables['theme_hook_suggestions'];
}
if (!empty($variables['theme_hook_suggestion'])) {
$suggestions[] = $variables['theme_hook_suggestion'];
}
foreach (array_reverse($suggestions) as $suggestion) {
if (isset($hooks[$suggestion])) {
$info = $hooks[$suggestion];
break;
}
}
}

产生输出可以用函数也可以用模版。$info['function']表示调用函数产生输出。

if (isset($info['function'])) {
if (function_exists($info['function'])) {
$output = $info['function']($variables);
}
}

$info['template']表示使用模板产生输出。theme_render_template()是默认的模板输出函数,.tpl.php是默认的模板文件扩展名。

// Default render function and extension.
$render_function = 'theme_render_template';
$extension = '.tpl.php';

不同的theme engine允许有不同的模板输出函数和模板文件扩展名。

// The theme engine may use a different extension and a different renderer.
global $theme_engine;
if (isset($theme_engine)) {
if ($info['type'] != 'module') {
if (function_exists($theme_engine . '_render_template')) {
$render_function = $theme_engine . '_render_template';
}
$extension_function = $theme_engine . '_extension';
if (function_exists($extension_function)) {
$extension = $extension_function();
}
}
}

通过template_preprocess()为$varialbes添加默认变量。使用$variables['directory']判断template_preprocess()有没有执行过。

// In some cases, a template implementation may not have had
// template_preprocess() run (for example, if the default implementation is
// a function, but a template overrides that default implementation). In
// these cases, a template should still be able to expect to have access to
// the variables provided by template_preprocess(), so we add them here if
// they don't already exist. We don't want to run template_preprocess()
// twice (it would be inefficient and mess up zebra striping), so we use the
// 'directory' variable to determine if it has already run, which while not
// completely intuitive, is reasonably safe, and allows us to save on the
// overhead of adding some new variable to track that.
if (!isset($variables['directory'])) {
$default_template_variables = array();
template_preprocess($default_template_variables, $hook);
$variables += $default_template_variables;
}

调用模板输出函数$render_function产生输出内容。

// Render the output using the template file.
$template_file = $info['template'] . $extension;
if (isset($info['path'])) {
$template_file = $info['path'] . '/' . $template_file;
}
$output = $render_function($template_file, $variables);

Drupal使用PHPTemplate作为默认的主题引擎。PHPTemplate的输出函数theme_render_template()很简单,include模板文件就OK。

/**
* Renders a system default template, which is essentially a PHP template.
*
* @param $template_file
* The filename of the template to render.
* @param $variables
* A keyed array of variables that will appear in the output.
*
* @return
* The output generated by the template.
*/
function theme_render_template($template_file, $variables) {
// Extract the variables to a local namespace
extract($variables, EXTR_SKIP); // Start output buffering
ob_start(); // Include the template file
include DRUPAL_ROOT . '/' . $template_file; // End buffering and return its contents
return ob_get_clean();
}

最后,theme()还原$theme_path,返回输出$output。

// restore path_to_theme()
$theme_path = $temp;
return $output;

解析theme()的更多相关文章

  1. Theme皮肤文件(json解析、多文件管理)

    一  官方教程 http://developer.egret.com/cn/github/egret-docs/extension/EUI/skin/theme/index.html 二 thm主题文 ...

  2. Activity设置全屏显示的两种方式及系统自带theme属性解析

    转载说明:原贴地址:http://blog.csdn.net/a_running_wolf/article/details/50480386 设置Activity隐藏标题栏.设置Activity全屏显 ...

  3. android之xmlpullparse解析器

    Pull解析和Sax解析很相似,都是轻量级的解析,在Android的内核中已经嵌入了Pull,所以我们不需要再添加第三方jar包来支持Pull.Pull解析和Sax解析不一样的地方有(1)pull读取 ...

  4. Android Service完全解析,关于服务你所需知道的一切(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

  5. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q40-Q44)

    Question 40 You have a social networking site in SharePoint 2010 that allows users to post content f ...

  6. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q45-Q48)

    Question 45 You are designing a branding strategy for a customer with a new SharePoint 2010 server f ...

  7. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q52-Q55)

    Question 52You are responsible for rebranding the My Sites section of a corporate SharePoint 2010 fa ...

  8. 【凯子哥带你学Framework】Activity界面显示全解析

    前几天凯子哥写的Framework层的解析文章<Activity启动过程全解析>,反响还不错,这说明“写让大家都能看懂的Framework解析文章”的思想是基本正确的. 我个人觉得,深入分 ...

  9. SpringMVC解析3-DispatcherServlet组件初始化

    在spring中,ContextLoaderListener只是辅助功能,用于创建WebApplicationContext类型实例,而真正的逻辑实现其实是在DispatcherServlet中进行的 ...

随机推荐

  1. ArrayList源码阅读----JDK1.8

    //定义一个默认的长度10 private static final int DEFAULT_CAPACITY = 10; //定义空的数组 private static final Object[] ...

  2. wampserver -- 取消PHP页面Warning和Notice级别的报错

    Learn from:http://yige.org/p/91 一般遇到这样的问题,有两个方法:1.如果有服务器权限,直接把服务器上的php.ini的配置改了,改成不输出Warning和Notice级 ...

  3. Codeforces Beta Round #4 (Div. 2 Only) A. Watermelon 水题

    A. Watermelon 题目连接: http://www.codeforces.com/contest/4/problem/A Description One hot summer day Pet ...

  4. uoj 48 核聚变反应强度 次小公因数

    [UR #3]核聚变反应强度 Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/48 Description 著名核 ...

  5. Hive集合数据类型

    Hive的列除了支持基本的数据类型外,还支持使用Struct.Map和Array三种集合数据类型. 假设某表有如下一行,我们用JSON格式来表示其数据结构.在Hive下访问的格式为 { "n ...

  6. Hiho----微软笔试题《Combination Lock》

    Combination Lock 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Finally, you come to the interview room. You ...

  7. MySQL之char、varchar和text的设计

    最近有表结构设计中出现了varchar(10000)的设计引起了大家的讨论,我们下面就来分析分析. 首先我们先普及一下常识: 1.char(n)和varchar(n)中括号中n代表字符的个数,并不代表 ...

  8. CentOS下KVM克隆完成后修改MAC地址/VMware复制虚拟机修改MAC地址

    克隆完成之后可能mac地址会有冲突,进入KVM删除/etc/udev/rules.d/70-persistent-net.rules中的eth0的配置,接着把eth1改成eth0,并且修改/etc/s ...

  9. Add margining capability to a dc/dc converter

    You can easily add margining capability—that is, the ability to digitally adjust the output voltage— ...

  10. Delphi插件创建、调试与使用应用程序扩展

    Delphi插件创建.调试与使用应用程序扩展 翻译 : MiracleZ  有没有使用过Adobe Photoshop?如果用过,你就会对插件的概念比较熟悉.对外行人来说,插件仅仅是从外部提供给应用程 ...