分享

从头学习Drupal--基本架构四 | Drupal China

 gaofrank 2008-10-17

从头学习Drupal--基本架构四

xeopn 于 周三, 2008-06-25 00:48 提交。

主题(Theme)
主题是什么呢? 其实这个词已经被用的很泛了, 所有涉及外观定制的地方都充斥着这个词, 比如我们最常见到的windows桌面主题, QQ的界面皮肤等. 说白了主题就是外观, 可定制主题就是允许用户自定义应用的外观.
Drupal的外观也是由其主题(Theme)来表现的, 一个CMS只有强大的业务逻辑处理和扩展能力, 而没有好的外表, 一样会流失大量的用户. 所幸Drupal的外观表现机制同样十分强大, 它允许网站开发人员为其网站重新设计开发个性化主题, 同时Drupal社区还有大量共享的主题, 下载下来后, 解压到站点对应的主题目录(sites/all/themes,或sites/指定站点/themes)下即完成安装, 在管理界面直接热插拔应用主题, 非常方便.

主题机制(Theme System)
为了支持这么灵活,方便又强大的可插拔主题, Drupal有一整套设计实现, 我们叫它主题机制(Theme System), 它被设计用来分离界面元素(比如HTML, CSS等)与核心业务逻辑. 通过这种分离, 有效的减少界面元素对核心代码的影响, 从而能使核心代码尽快稳定; 同时更换新的界面也不需要去动复杂的业务逻辑, 便于分配给专业的界面设计人员去开发, 从而降低用户界面这种需求易变性模块的开发复杂性和依赖性. 下面我们就到Drupal的主题机制内部去探索一番.
Drupal的主题机制(Theme System)有由三大部分组成: 主题(Themes), 主题引擎(Theme Engines)主题api(includes/theme.inc).
主题(Themes)主要包含一组文件, 系统可以利用这些文件能展示对应的外观, 以获得不同的用户感受。一个主题主要由以下内容组成:模板文件, 样式表(CSS), 图像(images), Javascript文件等; 对于一个web设计者来说, 样式表什么的应该都比较熟悉, 但模板文件是什么呢? 模板就是一个HTML片断模子, 我们可以用它来生成有具体含义的HTML片断. 举个例子, 就好像一张空的贺卡, 你填上"xx生日快乐"就成了生日贺卡, 你写上"情人节一起happy吧"那就成了节日祝福贺卡. 因为一般人都比较聪明, 所以空的贺卡大家也知道哪该写姓名, 哪该写祝福语; 万一碰到个比较傻的要写怎么办, 于是聪明人可以先用铅笔在该写的作些提示标记, 这样傻子虽然不聪明但在有提示的地方把原来的铅笔标记替换成他自己的话还是能做到的. OK, 这个带有铅笔标记的贺卡我们就可以称之为一张贺卡模板.
Drupal的主题模板文件也 是同样道理, 它是一种带有标记(Tags)的HTML模板, 系统能够识别标记, 并根据标记进行内容替换, 生成实际的HTML页面. 那我靠什么识别标记? 当然就是看标记所用的语言了. 话说如果是美国傻子, 那我们就用英语写标记, 日本傻子则用日语. 回到Drupal世界, 写它的模板文件中标记的语言我们称之为模板语言(Template Language), Drupal6.x目前支持三种模板语言: php(默认), smarty, Xtemplate; 光有语言还不行, 还要有傻子的大脑来能识别这些语言啊, 那么系统中对应解释这些语言的工具就是主题引擎(Theme Engine).

Drupal默认的主题引擎是phptemplate, 它使用PHP语言, 直接内签在Drupal中, 不存在任何外部依赖. 下面看一个phptemplate的模板例子:

<?php
// $Id: block.tpl.php,v 1.3 2007/08/07 08:39:36 goba Exp $
?>
;
<div id="block-<?php print $block->module .‘-‘. $block->delta; ?>;" class="clear-block block block-<?php print $block->module ?>">

<?php if (!empty($block->subject)): ?>;
  <h2><?php print $block->subject ?></h2>
<?php endif;?>;

  <div class="content"><?php print $block->content ?></div>
</div>

因为phptemplate引擎使用的模板语言是PHP, 则里面php代码部分都是标记, 如果我们传入blcok变量{0,xx区块,xx区块内容}, 则经过主题机制(Theme system)处理后,上面这段模板代码将生成如下HTML片断:
<div id="block-book-0" class="clear-block block-book">
  <h2>xx区块</h2>
  <div class="content">xx区块的内容</div>
</div>

主题api
主题api提供的强大机制使得分离出去的主题层能有机地和核心协同工作, 完成内容的表现. 该机制又是通过钩子(hooks)技术实现的, 看来hooks不仅仅用于模块(Module)与核心的交互, 它被应用到所有需要与核心交互的场景. 与普通的业务逻辑类的钩子不同, 主题钩子(theme hooks)具备以下的特性:
  1. 主题钩子是按显示组件(components)来定义的, 即一个显示组件定义一个钩子名;
  2. 主题钩子不仅能靠注册函数实现, 还能靠模板形式实现, 通过模板文件名的命名规约, 能实现与函数钩子类似的效果
  3. 主题钩子不仅是只能在模块中实现, 而且在表现层的主题和主题引擎都能实现; 这里注意了, 前面我们只说了主题中的模板文件, 这些文件是无法实现钩子函数, 而表现层中的钩子函数在哪里实现呢? 对了, 在主题目录下的template.php文件中实现.
  4. 主题钩子必须有默认实现, 要么在模块中实现, 即模块默认实现, 要么直接使用主题api中的theme_HOOK(), 它定义了核心默认的显示组件的外观.
  5. 主题钩子是纵向的, 是覆盖式的, 也就是说只有最上层的能起作用, 底下的都被屏蔽了, 这种钩子实现是不是更像函数重载呢


上面这张图是从网站下 载的, 它清楚地描述了主题机制的核心架构. 整个网页由几部分组成, 它们的不同颜色代表从不同钩子输出, 其中a点输出代表默认主题实现(核心和模块的默认钩子实现, 对应两种青灰的颜色); b点是主题引擎的钩子重载点, 主题引擎钩子实现的输出为褐色; c点则是主题的钩子重载点, 它的钩子输出为红色, 这三层的输出最终形成了给终端用户的网页.

主题钩子的注册是在模块的hook_theme()中实现的(一个业务钩子), 而主题钩子有两种形式: 回调函数和模板文件, 但主题层钩子形式必须与业务逻辑层(Core和Module)的形式保持一直, 下面我们分开进行阐述(下面的描述都基于PHPTemplate引擎):
(1)模板文件形式
模板文件允许你仅用css和的修改预定义的模板即可变换站点的外观, 非常简介和直接, 是我们自定义外观的主要方式. 常用的模板文件主要有以下几个:page.tpl.php; node.tpl.php; block.tpl.php; comment.tpl.php; box.tpl.php;

page.tpl.php

 

该模板描述一个HTML页面的主要元素, 包括, 和 元素, 它非常复杂, 大概能使用30多个Tag变量

 

node.tpl.php

 

该模板负责node内容的显示, 它的Tag变量都与node的属性相关, 而page.tpl.php的$content变量实际就代表它

 

block.tpl.php

 

该模板负责block的显示, 它有一个变量$block

 

box.tpl.php

 

该模板负责画一个简单的盒子, 原来好像被用作搜索结果和form, 但这个版本已经很少被使用了, 它与block有什么关系我到目前还没搞清楚

 

comment.tpl.php

 

该模板负责comment的显示, 其他没啥好说的

当处理页面显示请求时, 区块的内容, 节点的内容, 评论的内容等等都会先被放到区域(Region)中, 然后通过page.tpl.php中的区域变量$content, $header, $left, $right, and $footer等拼接成实际页面. 所以为了减少显示数据的生成, 可以禁用区域内的block而不是仅仅修改page模板.

前面说了, 模块也可以注册模板钩子, 主题也可以注册模板钩子, 那到底哪一个钩子生效呢? 这是由主题机制的调度函数分发的. 下图描述了几种关键模板的查顺序, 它们完全依赖模板名字的特殊命名约定来实现, 如果文件名相同, 则优先选择上层的模板.

更详细的关于模板文件内的Tag变量, 可以参考《Pro_Drupal_Development》和Drupal.org.

(2)回调函数形式
回调函数形式是一种用coding来处理自定义界面的方式, 说白了就是表现层没剥离完全的那部分, 留了一个后门, 以便处理更特殊的显示需求. 主题和主题引擎的钩子函数都在template.php文件中实现, 它们的命名规范是mytheme_hook()和themeengine_hook(). 尽管采用回调函数, 比模板方式提高5倍的速度, 但我们在定制外观的时候, 还是建议优先考虑定制模板钩子. 如果确实没有模板可用, 也尽量在主题引擎层实现函数钩子, 这样子主题也可以共享该函数钩子.

最后, 整个上面的流程, 不论是模板还是回调函数, 全部都是靠主题api中的theme()函数来实现的, 该函数堪称Drupal主题机制的中场发动机.

编后语:
我花了三天时间来看主题, 每多看一次都会对自己的否定几次, 确实太复杂, 而且Drupal6主题变化很大, 每天都不停的修改这篇文章, 真的以为这篇文章发不出来了, 但是为了给我的学习进程做个见证, 在我还没有完全理解它之前, 还是post出来了, 虽然不是很系统, 但它阐明我目前对主题机制的理解, 希望大家的指正.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多