第一章 Drupal的工作原理

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

 在本书中,我们首先介绍Drupal的基本原理,接着对Drupal开发中,所涉及到的基本技术和常见技巧,进行详细的描述。在很多的技术图书中,都会包含与软件安装相关的章节,由于本书主要是讲解Drupal模块开发的,我们在这里不会讲解Drupal的安装及相关模块的配置,对于Drupal的安装,由于相对来讲,是很简单的,并且网络上有很多这样的文档,所以在这里就不会为安装配置浪费笔墨了。

 

  在本章中,我们将为大家介绍什么是DrupalDrupal相关技术、Drupal核心术语、引导指令流程、Drupal特有的钩子机制。


Drupal版本:

1什么是Drupal

     作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com。

Drupal从2001年诞生到现在,经过了不断的版本演化和市场检验以后,日趋成熟和完善。Drupal已经超越一个传统意义上的CMS范畴,越来越多的程序员把它做看作一个内容管理框架(CMF),总之Drupal正在演化为一门平台性质的技术。我们可以从三个方面来理解Drupal:

Drupal是一个基于GPL协议开源的内容管理系统。Drupal7包含了CMS的各种标准功能。比如文章的发布、文件图片的管理、用户帐号管理、菜单导航、频道分类等等。我们使用Drupal,可以搭建各种网站,比如个人的博客,企业的宣传网站,B2C门户网站,视频网站。

Drupal是一个内容管理框架,它提供了各种方式,允许开发者使用可插拔的模块来定制Drupal的功能。Drupal的开发者和使用者,可以从drupal.org上面下载各种模块,来扩展现有的功能,并且可以按照Drupal的规范,自己开发模块以满足需求。

Drupal背后有一个开放的社区,Drupal的成功,与社区的活跃程度是分不开的。在2011年1月Drupal7发布时,全球有800多个社区成员向Drupal核心贡献了代码。有数以千计的开发者,向社区贡献了7000多个模块,还有更多的人,在测试、文档、用户支持、翻译等方面为Drupal作出了贡献。

 

    对于Drupal的理解,仁者见仁,智者见智。我们在这里,就不用过多的纠结于Drupal到底是一个CMS,还是一个CMF这样的争论了,我们只需要了解Drupal能够做什么,我们需要它做什么就可以了。Drupal把很多复杂的事情,简单化了;同时Drupal也把很多简单的事情,复杂化了。这也是我们为什么对Drupal又爱又恨的原因。


Drupal版本:

2drupal的技术堆栈

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com


我们首先来看一下,在Drupal中,都会用到哪些技术。

2.1 PHP

  Drupal使用的编程语言是PHPhttp://php.net)。PHP是一个流行的、跨平台的、服务器端执行的脚本语言。所以熟悉PHP,对于学习Drupal开发很有帮助。但是这并不是说,熟悉PHP 是必须的,由于Drupal本身,在PHP的基础上,又做了大量的封装,很多功能只需要调用自己的API即可实现,所以其它语言的程序员,转学Drupal,并不比PHP程序员转学Drupal更加困难。

 

  因为PHP易于入门,所以大量的PHP代码都是由新手编写的。而新手的水平大家也知道,他们的代码总是存在这样或者那样的问题,这就给PHP的名声带来了比较坏的影响。不过, PHP也可以用于构建严谨的程序。Drupal核心中的所有代码都遵守了严格的编码规范(http://drupal.org/nodes/318),通过开源,Drupal代码也经过了成千上万人的锤炼。对于Drupal来讲,PHP的入门门槛比较低,这就意味着有更多的人能够为Drupal贡献代码,通过开源,会有很多人对这些代码进行检查,这样就保证了代码的质量。

 

  最后,需要注意的是,Drupal7所需的最低PHP版本是PHP5.2。由于在PHP5.2及后续版本中,面向对象编程正占据着主流地位,因而尽管Drupal本身是面向过程的,但是Drupal核心中的一些子系统,以及很多第3方模块,都广泛采用了面向对象编程技术。

 

2.2 web服务器

    最常用的web服务器就数Apache了,所以一开始Drupal就对Apache提供了内置支持。当然,这并不是说,Drupal不能运行在其它web服务器上。随着Drupal的流行,对其它web服务器的支持,也越来越完善了,比如IIS、lighttpd、nginx。最近两年,在Drupal的高性能应用实践中,越来越多的Drupal程序员把Nginx作为首选服务器,用以提升性能。

   我们在本书中不会涉及太多与web服务器相关的知识。这里值得一提的是,在Drupal中,简洁URL用到了web服务器的相关设置。有关简洁URL的相关配置,可以参看相关的文档。

 

2.3 数据库

  最初,Drupal对MySQL提供了内置支持,在Drupal的后续版本中,增加了对PostgreSQL的支持。在Drupal7中,内置支持了MySQL、PostgreSQL、SQLite三个数据库系统。由于Drupal7的数据库API,是完全基于PHP5的PDO,而PDO能够支持各种各样的数据库,比如Oracle、SQL Server、DB2,所以通过第3方模块,就可以实现Drupal7对Oracle、SQL Server,DB2的支持。Drupal 对商业数据库的支持,能够让Drupal 在更广泛的领域中得以应用。

 

2.4操作系统

  操作系统位于Drupal相关技术堆栈的最下面的一层,由于Drupal是基于PHP编写的,而PHP语言也具有跨操作系统的特点,所以只要操作系统能够支持PHP,我们就可以使用它来运行DrupalWindowsLinuxMac OS等等,在这些主流的操作系统上,都可以运行Drupal。操作系统层,主要负责网站相关的最底层的任务,比如网络连接,文件的权限。如果你是在linux下面安装运行Drupal,你经常会遇到文件夹权限不可写,这样的权限问题。

  本书的作者,使用的是Vista操作系统,所用的环境是XAMPP,所有的代码都是在这个操作系统下面编写的,相信这些程序也能够应用于其它操作系统及相关环境下。

 

2.5 HTMLCSSJavaScript

  Drupal系统中最终返回给浏览器的是HTML,而在HTML中,CSS是用来定义页面样式的,JavaScript则是浏览器客户端的脚本语言。需要注意的是,DrupalJavaScript的支持,是通过jQuery实现的。jQuery是一个优秀的轻量级的JavaScript框架 (压缩后只有21k) ,它兼容CSS3,还兼容各种浏览器。作为一个Drupal开发者,我们今后肯定会涉及到这三种技术。


Drupal版本:

3Drupal文件夹结构

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

我们从drupal.org上下载最新的Drupal7版本,把它结压缩后,放到本地的web文档目录下,这是我们就会看到Drupal核心文件夹下的目录结构了。

 

 

               Drupal7核心的默认文件夹结构

1.png

 

    通过了解Drupal默认安装的目录结构,我们能够了解一些最佳实践,比如下载的第3方模块和主题的放置位置,以及如何拥有不同的Drupal安装轮廓。我们来进一步的了解一下Drupal的文件夹目录:

 

3.1 includes 

  这个文件夹下面包含了Drupal通用的函数库,比如ajax、批处理、表单API、数据库API、本地化API等等。这些通用函数库,是Drupal程序运行的基石。

 

3.2 misc

这个文件夹下面包含了JavaScript文件,和其它各种图标和图片文件。其中JavaScript文件包括jquery.jsjquery.form.jsjquery.cookie.jsdrupal.jsajax.j、jQuery UI等等。

 

3.3 modules

   这个文件夹下面包含了所有核心模块,一个模块对应一个文件夹,总共40个模块。最好不要乱动这个文件夹(以及除profiles和sites外的其它文件夹)下面的任何东西。对于第3方模块、主题,或者自定义的模块、主题,应该放到sites目录下

 

3.4 profiles 

    这个文件夹下面包含一个站点的不同安装轮廓(profile)。Drupal7自带了两个profile,一个是标准化安装(standard),一个是最小化安装(minimal)。安装轮廓的主要目的是,用来自动的启用核心的或者第3方的模块,并作一些初始化设置。比如rszrama为了方便大家测试commerce模块,就提供了一个commercedev安装轮廓(https://github.com/rszrama/commercedev),使用这个profile,用户就能够方便的搭建一个电子商务测试站点。

 

3.5 scripts

    这个文件夹下面包含了许多脚本,这些脚本可用于语法检查、清理代码、从命令行运行Drupal、使用cron处理特定情况、以及运行单元测试等等。在Drupal自身程序运行过程中,调用不到这些脚本;这里面都是一些shellPerl的实用脚本。

 

3.6 sites

    这个文件夹下面用来放置Drupal的配置文件、第3方模块与主题、自定义模块与主题等等。你从第3方模块库中下载的模块,通常都放在sites/all/modules/standard下面;而你自己编写的模块,则放在sites/all/modules/custom目录下面。我们对Drupal所进行的任何修改,基本上都放在这个文件夹下进行。

   在sites下面有一个名为default的文件夹,里面包含了Drupal默认配置文件--- default.settings.php。在Drupal安装过程中,系统将会基于你提供的数据库帐号信息和这个默认文件,为你自动创建一个settings.php文件。对于多站点安装,配置文件通常位于sites/www.example.com/settings.php

 

    另外sites/default/files通常用作Drupal文件系统所在的目录。Web服务器需要具有这个子目录的读写权限。默认情况下,Drupal在安装时会自动为你创建这个文件夹,并检查是否设置了相应的权限。

 

3.7 themes

   这个文件夹下面包含了Drupal的模板引擎和默认主题。这里的默认主题有bartikgarlandseven等等。你下载的第3方主题以及自己创建的主题,不能放在这个位置,而应该放在sites/all/themes目录下面。

 

3.8 authorize.php

   这个PHP文件里面包含了运行认证文件操作的管理脚本。通过settings.php中的全局变量killswitch以及'administer software updates'权限,可以控制对这个文件中脚本的访问。

 

3.9 cron.php

   这个PHP文件用于执行定时任务,比如清理过期的缓存数据,以及计算统计信息。Drupal7在运行定时任务时,首先会检查cron_key是否正确,从而避免cron.php被恶意的调用执行。

 

3.10 index.php

   这个PHP文件是Drupal处理http请求的主入口程序。它就相当于一个路由器,用来将程序的执行控制权分发给合适的处理器上,而后者会输出相应的页面内容。

 

3.11 install.php 

   这个PHP文件是Drupal安装器的主入口程序。

 

3.12 update.php

    这个PHP文件是Drupal升级时的主入口程序。通过设置settings.php中的全局变量update_free_access,可以绕过升级时的权限检查。

 

3.13 xmlrpc.php 

    这个PHP文件用来接收XML-RPC请求,如果你的网站没有用到XML-RPC,那么可以将这个文件从中删除。

 

3.14 robots.txt

    这个文件是搜索引擎爬虫排除标准的默认实现。在这个文件中,你可以定义搜索引擎爬虫能够访问哪些页面,不能访问哪些页面。

 

  此外,.htaccess文件是apache的相关配置文件;而web.config则是IIS的配置文件,它是Drupal7中新增的一个文件。其余文件则是相关的文档文件。

 


Drupal版本:

4Drupal核心概念

  作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

  通过了解Drupal自身的文件夹结构,我们对Drupal有了初步的认识。我们还需要对Drupal中的常用概念,或者说专有术语,有更好的界定。这能够帮助我们更好的学习使用Drupal。常用的术语有模块、钩子、主题、节点、菜单、区块、字段、实体等等。Drupal相关术语不仅仅是这里所列的这么几个,还有更多的相关术语。有兴趣的读者,可以参看http://drupal.org/node/19828



Drupal版本:

4.1 模块

   作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com 
   我们浏览Drupal核心文件夹的modules目录,就会发现这里包含了许多子文件夹,正如前文中所述的那样,每个下面都有一个模块。而每一个模块,都实
现了特定的功能。通过模块的安装与卸载,我们就可以为Drupal站点添加或者删除特定功能。Drupal作为一个框架,其最大的一个优势,就是完全的模
块化。当我们建设一个站点时,我们只需要根据需求组装相应的模块就可以了;当然,这里所说的只是大多数时候。

 

    Drupal7的模块我们大致可以分为4类:核心必选模块、核心可选模块、第3方模块、自定义的模块。核心必选模块,位于modules目录,有字段(Field)、字段SQL存储(Field SQL storage) 、文本字段(Text)、过滤器(Filter)、节点(Node)、系统(System)、用户(User),这里需要注意的是modules/field目录下,包含了多个模块,其中3个模块是核心必选模块。modules目录中,除去必选模块以外,剩余的就是核心可选模块,核心可选模块的info文件中,不包含 “required = TRUE
这句话。第3方模块,就是由Drupal的社区成员,贡献到drupal.org上的模块,目前(2011年5月),drupal.org上有7000+
多个第3方模块,而且这个数量正在稳步增加。自定义模块,就是为了实现网站的特殊需求,程序员自己开发的模块,通常没有上传到drupal.org上。

 

    Drupal
本身是不向下兼容的,每个主版本之间,差别往往很大。随着主版本的升级,一些模块,原来是Drupal核心可选模块,后来变成了第3方模块,比如
drupal.module;有一些模块,原来是核心必选模块,后来变成了核心可选模块,比如block.module;也有一些模块,原来是第3方模
块,后来变成了核心可选或者必选模块,比如
simpletest,cck。如果你自己定义的模块,贡献到了drupal.org上,那么这个模块就变成了第3方模块。Drupal的模块,就像大自然一样,是在不断演化的,而且也存在优胜劣汰这样的自然法则。



Drupal版本:

4.2 钩子

作者:老 葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

  谈到模块,就不得不提到钩子这个概念。我有时也喜欢把钩子称作钩子机制,我们可以把钩子看做Drupal的内部事件。有时也可以钩子看作是特殊的回调函数。
模块就是通过钩子,与Drupal核心系统无缝整合在一起了。钩子是一个很抽象的概念,我们通过代码来理解一下钩子机制。

function module_invoke_all() {

  $args = func_get_args();

  $hook = $args[0];

  unset($args[0]);

  $return = array();

  foreach (module_implements($hookas $module) {

    $function = $module . '_' . $hook;

    if (function_exists($function)) {

      $result = call_user_func_array($function$args);

      if (isset($result) && is_array($result)) {

        $return = array_merge_recursive($return$result);

      }

      elseif (isset($result)) {

        $return[] = $result;

      }

    }

  }

 

  return $return;

}

 

    代码参考地址:

http://api.drupal.org/api/drupal/includes--module.inc/function/module_invoke_all

    函数module_invoke_all是理解钩子的关键,而这里面,foreach循环中,函数module_implements是理解钩子的又一关键,它用来获取所有实现了钩子$hook的模块。而在这个循环体代码里面,第一句给出了钩子的命名规范。模块在实现钩子的时候,必须采用“模块名_钩子名”形式。其余代码的含义,就是如果这个具体的钩子函数存在(function_exists($function)),那么就调用这个钩子函数(call_user_func_array($function, $args))。

 

    Drupal中的钩子大致可以分为3类,采用module_invoke_all调用的钩子是一类,也是最常见的;采用module_invoke调用的钩子是一类,这种钩子在Drupal核心中经常出现;还有一个就是主题钩子,比如theme_item_list,这里的item_list有时也被称为主题钩子。

 

    上面提到了module_invoke,让我们看一下它的代码:

<?php

function module_invoke() {

  $args = func_get_args();

  $module = $args[0];

  $hook = $args[1];

  unset($args[0], $args[1]);

  if (module_hook($module$hook)) {

    return call_user_func_array($module . '_' . $hook$args);

  }

}

?>

    在这个函数中,call_user_func_array函数只调用了一次,而在前面的module_invoke_all函数的代码中,它被循环调用了多次。这就是两类钩子之间的区别。

    有关钩子的更多信息,可以参看api.drupal.org上的在线文档,http://api.drupal.org/api/drupal/includes--module.inc/group/hooks/7。此外,如果一个模块对外提供了钩子,那么在这个模块的文件夹下面,通常会有一个modulename.api.php这样的文件,里面包含了钩子的具体说明,比如在用户模块下面,就有一个这样的文件user.api.php,里面包含了用户模块对外提供的所有钩子。

 

 


Drupal版本:

4.3 主题

  作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

  我们在前面的Drupal文件结构的分析中,提到过themes文件夹,这里面放置的就是Drupal核心自带的主题。Drupal中的主题主要负责把原始
数据转化为格式化的HTML输出。通常一个Drupal主题,由info文件、模板文件、template.php、CSS文件、JS文件、图片构成。我
们打开一个Drupal主题,比如garland,就会看到在模板文件中,里面包含了html代码片断和PHP变量。主题对外观的控制,就是通过这样的模
板文件和主题函数实现的。除了Drupal核心自带的这些主题外,
Drupal.org还有很多第3方的主题可用,比较常用的有Zen、fusion、tao等等(http://drupal.org/project/themes)。

 

    前面所说的主题,指的是具体的主题。在Drupal中,主题系统,所包含的含义就会更加广泛一些。除了上面的所说的具体的主题外,它还包含Drupal的主
题机制,包含在Drupal核心模块中、第3方模块中的各种模板文件和主题函数,以及Drupal的主题覆写。Drupal通过自己的主题系统,将逻辑层
与表现层作了分离;将逻辑与外观分离,这也是Drupal的最佳实践之一。在
includes文件夹下,有一个theme.inc文件,里面的代码包含了Drupal的主题覆写机制,也包含了各种预处理函数、处理函数、还有常用的主题函数,阅读这些代码,有助于大家熟悉Drupal的主题系统。

 

Drupal
通过主题系统来控制网站的外观,我们可以通过定制自己的主题,来实现自己的具体外观。在定制主题的过程中,对于Drupal核心或者第3方模块的默认输
出,我们通常有两种定制方式:一种就是使用CSS的覆写机制,重新定义CSS规则,保留原有的markup输出;这种方式的优点是,简单方便,缺点就是有
大量的垃圾html输出,在后续过程中,不易复用。另一种方式就是在自己的主题中通过覆写模板与主题函数实现,这样可以完全使用自己的markup输出、
使用自己的CSS规则;这种方式的优点是html代码干净、浏览器兼容性比较好,易于复用,缺点就是比较复杂,前期成本高。


Drupal版本:

4.4 区块

   作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

   区块一般指的是放置在模板中的边栏、页首、或者页尾中,主内容外的内容片断。通过区块管理界面,可以启用或者禁用这些信息。举例来说,我们通常在站点的页脚
处,显示的“版权信息”,就可以处理成区块;常常显示在站点边栏的“热门内容”,每个站点的主导航链接,都可以处理成区块;比如“用户登录”和“我的帐
号”功能,可以结合在一起,处理成区块显示,这样匿名用户看到的就是“用户登录”表单,而注册用户看到的就是“我的帐号”链接。

    我们可以控制单个区块的显示位置,比如显示在特定页面,显示给特定角色的用户,显示在特定节点类型的页面中,显示在特定的主题区域下。区块通常是放在区域中的,而区域的定义则位于主题的info文件中。


4.5 菜单

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

    在Drupal中,菜单有两层含义:一种是处理请求的路由系统,它会将页面请求所提供的URL映射到Drupal内部的回调函数上,这是Drupal程序员所关心的;另一种就是页面上的导航,它负责组织站点的内容关系。

 

    菜单具有层级的树形结构,一个菜单项下面可以有多个子菜单项,子菜单项下面又可以包含菜单项。需要注意的是Drupal菜单的层级,最多可以有9级,超过了9级,系统就不能正常工作了,而在实际的站点导航中,很难遇到包含9级的导航。

 


Drupal版本:

4.6 用户

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

    对于你站点的每位访问者,无论他拥有你站点的一个账号,或者是匿名访问,在Drupal中,都会将其处理成用户。每个用户都具有一个ID,注册用户还具有用
户名、电子邮件等信息。用户本身是实体的一个具体实现,所以我们可以为用户添加更多的字段;此外,使用profile2模块,就可以定义不同的
profile类型,并将其与用户关联起来。

 

    ID为0的用户为匿名用户,我们可以为匿名用户启用缓存。ID为1的用户,是Drupal站点的超级管理员,具有站点操作的各种权限。Drupal通过角色来
管理不同注册用户的权限。一个角色就代表着一组权限的集合。Drupal自带了3中角色:匿名用户、注册用户、管理员。根据站点的需求,可以添加更多的角
色,比如“站内编辑”。


Drupal版本:

4.7 字段与实体

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

    字段和实体,是Drupal7中新引入的两个概念。在Drupal7以前,人们使用CCK模块来扩展节点类型,为节点类型添加各种字段。这种方式渐渐的演变成为了主流方式,并最终在Drupal7中进入了Drupal内核。

 

    以
前,人们尝试着,将所有的内容都统一到节点上去,比如区块,评论,分类,用户profile,都存在相应的第3方模块将其实现为相应的节点类型。在
Drupal7中,核心开发者将这方面的努力做了进一步的抽象,把核心中的节点、分类、评论、用户都抽象成为了实体。实体具有相同的增删改查,可以为实体
添加更多的字段。

 

    第3方模块,Entity API正在成为Drupal7中的CCK模块,基于这个模块,我们可以方便的定义出来新的实体类型,比如在Ubercart和Commerce模块中,就基于这个模块将订单实现成为了一种新的实体类型。


Drupal版本:

5Drupal执行流程

  作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

  Drupal是如何处理一个HTTP页面请求呢?如果对于这样的请求处理,我们能够理解其基本的流程结构,那么对于今后的学习,将会很有帮助。如果你想追踪Drupal的代码执行过程,那么首先可以搭建好调试环境,从index.php文件开始,逐步设置断点,这样就可以快速的了解Drupal的基本原理了。让我们以一个简单的例子,来分析一下常用请求的处理流程。假定一个注册用户访问我的站点http://zhupou.cn,并浏览文章“Drupal入围2008全球开源CMS大奖赛决赛”,也就是访问路径http://zhupou.cn/node/88(我这里假定zhupou.cn已经升级到Drupal7)。

 

    1、首先,我们启用了简洁URLWeb服务器收到请求后,会按照URL重写把用户看到的URL,转换为系统可以理解的URL,在这里就是将http://zhupou.cn/node/88 转换为了http://zhupou.cn/index.php?node/88ApacheIISNginx都支持URL重写。

 

    2PHP开始执行Drupalindex.php文件,Drupal获取到内部路径“node/88.

 

    3Drupal启动完整地引导指令流程,完成资源的初始化,加载所有启用的模块后,将内部路径“node/88”映射到了节点模块的对应回调函数node_page_view上。

 

    4、节点模块执行node_page_view函数,经过node_page_view -à node_show à node_view_multipleànode_viewànode_build_content这样的持续回调,它从数据库中读取ID88的节点,并将返回的数据封装成一个drupal_render可以识别的数组。在node_viewnode_build_content函数中,先后触发了以下钩子函数:hook_field_prepare_view,hook_field_formatter_prepare_viewhook_entity_prepare_viewhook_field_formatter_viewhook_node_viewhook_entity_viewhook_node_view_alterhook_entity_view_alter

 

    5、主题系统获取节点88的数据信息,并将其使用html代码进行封装,同时应用CSS。主题系统获取与节点88相关的其它页面元素数据信息,并分别将其使用html代码进行封装,同时应用CSS

 

    6Drupal完成所有的处理后,把最终封装好的HTMLCSS数据传送给用户的浏览器。浏览器将这些数据显示成web页面,呈现给最终用户。

 

    这里重点需要理解的就是Drupal的引导指令,和相关的钩子函数调用。让我们对这两点进一步的解释。

1e30e924b899a901947c2b8d1d950a7b0308f5c4.jpg


Drupal版本:

5.1 引导指令

 作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

 我们在前面提到,Drupal会启动一个完整的引导指令,实际上,对于每个由Drupal处理的页面请求,Drupal都会启用自己的引导指令。Drupal的引导指令,定义在bootstrap.inc文件中,它包含八个阶段:

 

 

初始化配置阶段

在这一阶段,通过include_once()来解析settings.php文件,提取该文件中保存的键值信息,将会初始化Drupal的内部配置数组,并建立站点的基路径($base_url),获取HTTP请求的内部路径,初始化一些全局变量。

 

页面缓存阶段

在有些情况下,我们需要更高性能的站点,因此就需要不经过数据库就调用缓存系统。在页面缓存阶段,系统会加载自带的缓存处理器,并尝试加载第3方模块(比如memcached)的缓存处理器,如果启用了页面缓存,那么系统将会在本阶段直接返回缓存的页面,并终止引导指令后续阶段的执行。

 

初始化数据库阶段

在初始化数据库阶段,将会初始化数据库系统。需要注意的是,数据库连接只有在实际调用时,才会正式初始化;同时会注册自动加载的函数,这样我们就可以方便的访问系统中定义的类和接口了。

 

初始化变量系统阶段

 

在这个阶段,将会初始化Drupal的锁系统;加载所有的系统变量,注意这里没有覆写settings.php中的变量;加载引导指令模块。

 

初始化会话处理阶段

Drupal采用了PHP内置的会话处理机制,并在在此基础上,实现了自己用户层级的会话存储处理器,使用session_set_save_handler函数重载了SESSION存储方式,这样就可以使用数据库来存储会话信息了。这里我们需要注意的是,Drupal的会话信息不是存储在内存中的,而是存储在数据库中的。在本阶段,将会初始化或者重新构建会话。代表当前用户的全局对象$User也会在这一阶段初始化,不过出于效率的考虑,并不是对象的所有属性都是可用的(当需要时,可以通过明确的调用函数user_load()来加载这些属性)。

 

设立页面头部阶段

在这个阶段,会使用bootstrap_invoke_all触发hook_boot钩子,系统会使用这个钩子来设置一些全局参数,以供后面调用;同时还会初始化锁系统;发送默认的HTTP头部。需要注意的是,在调用hook_boot钩子时,大多数的模块和许多通用函数库还没有加载进来,它是处理页面请求所调用的第一个钩子,比hook_init还要早。如果你所用的模块实现了这个钩子,那么会在“性能”管理页面,提示你这个模块与激进缓存模式不兼容。

 

语言判定阶段

    在这个阶段,会初始化所有已定义的语言类型。如果站点启用了多语言特性,系统会基于语言协定设置,为每个给定类型选择一个语言;同时在多语言环境下,完成了语言系统初始化后,还会调用hook_language_init钩子。

 

完成

该阶段是引导指令的最后一个阶段,它包括加载一些通用函数库,比如path.inctheme.incpager.incmenu.incfile.incform.incajax.inctoken.inc等等。在这里将设置Drupal定制的错误处理器,并加载所有启用了的模块。同时还会初始化Drupal内部路径,初始化主题系统。最后Drupal调用hook_init钩子,这样在对请求正式处理以前,为相应模块提供一个交互的机会。

 


Drupal版本:

5.2 钩子的执行顺序

  作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

  前面我们在Drupal概念中讲到钩子,现在进一步的对其进行分析,掌握了Drupal的钩子机制,熟悉各种常用的钩子,这是Drupal开发过程中的必备条件。

 

    钩子的触发,通常使用module_invoke_all来完成,这是一种常用的方式,但是还存在其它的方式。比如在引导指令阶段,就使用了bootstrap_invoke_all触发hook_boot钩子。代码如下:

   bootstrap_invoke_all('boot');

   

    而在node_build_content函数中,hook_node_view_alterhook_entity_view_alter钩子的触发,则是使用drupal_alter完成的,代码如下:

    drupal_alter(array('node_view', 'entity_view'), $build, $type);

 

   而hook_field_prepare_viewhook_field_formatter_prepare_view钩子的触发,使用的代码分别是:

_field_invoke_multiple('prepare_view', $entity_type, $prepare);

_field_invoke_multiple_default('prepare_view', $entity_type, $prepare, $view_mode);

 

    注意,这两个都是内部函数,专门用于field相关的模块。后者对前者作了封装,最终将使用field_default_prepare_view()来触发hook_field_formatter_prepare_view钩子。

 

    这些触发钩子的函数,尽管名字各不相同,但是里面的核心代码,是一致的。

 

 foreach ($modules as $module) {  

    $function = $module . '_hookname';

    if (function_exists($function)) {

      $function();

    }

  }  

   

    如果多个函数都实现相同的钩子,那么这些钩子之间的执行顺序是怎么决定的呢?Drupal首先会使用模块的重量进行排序,按照顺序依次执行。重量越小,越靠前;重量越大,越靠后。但是通常情况下,模块的重量都默认为0。在重量相同的情况下,则按照模块名字的字母顺序进行排列。比如,我们使用form_alter模块修改特定表单,如果有多个模块同时修改了该表单,那么form_alter钩子的执行顺序将会对结果产生影响。而默认的按模块名字的字母顺序执行,有时候并不能得到我们想要的结果,这个时候我们可以调整模块本身的重量。示例代码如下(这里假定模块名字为module_name):

db_update('system')  

  ->fields(array(    

    'weight' => 999,      

  ))  

  ->condition('name', 'module_name')  

  ->execute();

 

    我画了一张钩子执行顺序的流程图,希望能够方便大家理解构字的执行顺序。

1.png 

                   钩子执行顺序图

 

 

总结

读完本章以后,你应该能够

理解Drupal是什么,

Drupal核心的文件结构、

Drupal常用术语,

Drupal处理http请求的大致流程,

引导指令流程,

钩子回调流程。