• 解析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;
  • 相关阅读:
    Ajax基础:3.Json
    Head First Design Patterns State Pattern
    Head First Design Patterns Template Method Pattern
    Articles For CSS Related
    Head First Design Patterns Decorator Pattern
    代码审查工具
    How To Be More Active In A Group
    Head First Design Patterns Factory Method Pattern
    Head First Design Patterns Composite Pattern
    Tech Articles
  • 原文地址:https://www.cnblogs.com/eastson/p/3700718.html
Copyright © 2020-2023  润新知