zoukankan      html  css  js  c++  java
  • 解析theme()

    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;
  • 相关阅读:
    【设计模式】第九篇:组合模式解决层级关系结构问题
    【设计模式】第八篇:喝豆浆就是装饰者模式吗?
    【设计模式】第七篇:和我一起简单认识桥接模式
    【设计模式】第六篇:来康康适配器模式
    【设计模式】第五篇:什么是原型模式?浅提浅拷贝和深拷贝
    【计算机网络】学习笔记,第三篇:数据链路层(谢希仁版)
    一篇搞定工厂模式【简单工厂、工厂方法模式、抽象工厂模式】
    单例模式的几种实现And反射对其的破坏
    缓存穿透、击穿、雪崩什么的傻傻分不清楚?看了这篇文后,我明白了
    图文并茂,带你深入了解AQS的源码
  • 原文地址:https://www.cnblogs.com/eastson/p/3700718.html
Copyright © 2011-2022 走看看