测试和开发代码
单元测试就是用来将成分分割成不同的独立单元,并决定每一单元的行为是否和期望的一样。尽管 Drupal 没有一个核心的单元测试 API,但它拥有一个相关的学习小组(http://groups.drupal.org/unit-testing),而且它还有一些工具用来帮助开发 者创建更高质量的代码。其中最著名的一个就是第3方模块 — devel.module。
Devel 模块
Devel 模块,最初由 Moshe Weitzman 编写的,它是一个大杂烩,用来帮助开发者调试和检查你的代码各个零碎的方面。
你可以从 http://drupal.org/project/devel 获取这个模块(或者使用 CVS 检出并获得更酷的效果)。安装了 devel 模块后,一定要启用 devel 区块。下面是 devel 区块中比较模糊的一些链接以及其能做什么的列表:
Empty Cache(清空缓存):清空数据库的用来存储页面、菜单、节点、和变量缓存的缓存表。更明确一点就是,这些要被清空的表有 cache、cache_filter、cache_menu 和 cache_page。
注意:点击 Empty Cache 链接不能清空用户定制的缓存表。
Function reference(函数引用):使用 PHP 的 get_defined_functions() 得到在当前请求期间所用到用户函数列表。点击一个函数的名字可以查看它的文档。
Reinstall module(重装模块):通过运行 hook_install() 来重新安装一个模块。Schema 版本号将被设置成最近更新的版本号。在重新安装模块以前,一定要先手工的清理掉任何存在的由该模块生成的数据库表。
Reset menus(重置菜单):将所有的菜单项重置到它们的默认设置上,并且删除所有的定制菜单项。
Variable viewer(变量查看器):列出当前存储在在 variables 表中以及你的 settings.php 文件的 $conf 数组中的所有变量以及它们的值。一般可以使用 variable_get() 和 variable_set() 来访问这些变量。
Session viewer(会话查看器):展示你的 $_SESSION 变量的内容。
展示查询
打开页面 http://example.com/?q=admin/settings/devel,选住 Collect query info(收集查询信息)和 Display query log(展示查询日志)旁的复选框。
一旦你保存了这些设置,你将会看到,在每个页面的最底部,都有一列查询列表,这些查询就是在生成你当前页面所用到的所有的查询!此外,列表还会告诉你生成查询的函数,该查询所耗费的时间,以及查询被调用的次数。
在许多有见识的方式中你都可以使用这一信息。例如,如果同一查询在一个页面被调用了40次,那么你就需要检查一下你的代码看是否存在一个坏的循环结 构体。如果确实是这样的话,你可以考虑实现一个静态变量,在请求期间,来保存数据库插叙结果。下面的例子说明了这个设计模式的样子(来自 taxonomy.module):
function taxonomy_get_term($tid) {
static $terms = array();
if (!isset($terms[$tid])) {
$terms[$tid] = db_fetch_object(db_query(’SELECT * FROM {term_data} WHERE tid =
%d’, $tid));
}
return $terms[$tid];
}
我们创建了一个静态数组来保存结果集,这样如果查询已被执行过了,那么我们就已经有了这个值,这样就可以直接将其返回而不是再次查询数据库了。
处理耗费时间的查询
假定你已经编写了一个名为 task(任务)的定制节点模块,而且你使用 hook_load() 来向节点对象追加关于任务的额外信息。表的 schema 如下:
CREATE TABLE task (
nid int,
vid int,
percent_done int,
PRIMARY KEY (nid,vid),
KEY nid (nid)
);
在运行了 devel.module 和查看了查询日志以后,你注意到针对上表的查询拖累你的站点性能!注意超过5毫秒的查询就被默认是缓慢的。
毫秒 函数 查询
27.16 task_load SELECT * FROM task WHERE vid = 3
那么为什么这个查询这么耗时呢?如果它是一个使用多表关联的更复杂的查询,那么我们将考虑使用更好的方式来组织数据,但是它是一个非常简单的查询。 首先要做的是使用 SQL 的 EXPLAIN 语法来查看数据库是怎么解释这个查询的。当你在一个 SELECT 语句前面追加一个关键字 EXPLAIN 时,数据库将返回该查询执行计划的相关信息。
EXPLAIN SELECT * FROM task WHERE vid = 3
MySQL 给出下面的报告:
Id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE task system NULL NULL NULL NULL 1
在这里最重要的一列就是 key 列,它现在的值为 NULL。这告诉我们 MySQL 在获取结果集时没有使用任何的主键、UNIQUE 键、或者索引键;它需要一行一行的查找。所以加快这个查询速度的最好方式是向 vid 列添加一个 UNIQUE 键。
ALTER TABLE task ADD UNIQUE (vid);
更多关于 MySQL 的 EXPLAIN 信息,参看 http://dev.mysql.com/doc/refman/5.0/en/explain.html 。
Devel 模块的其它用途
Devel 模块还有一些其它一些方便函数,它们常被忽略却能增加你的开发效率的。
例如,你可以实时的切换当前查看 Drupal 页面的用户。这对于技术支持和调试其它角色非常有用。为了切换到另一个用户上,导航到URL http://example.com/?q=devel/switch/$uid,其中 $uid 是你想切换到的用户的 ID。另一种方式是,启用 Switch users(切换用户)区块,它提供了达到相同功能的一组链接。
你可以使用 dsm()、dvm()、dpr() 和 dvr() 函数来输出调试信息,这些信息对于其它用户是不可见的。
dsm() 向页面的消息区域输出一个简单变量(比如一个字符串或者一个整数)。
dvm() 向页面的消息区域输出一个 var_dump()。对于复杂的变量比如数组或者对象使用这个函数。
dpr() 使用一个递归函数在页面顶部输出一个复杂的变量(比如一个数组或者对象)。
dvr() 在页面顶部输出一个 var_dump()。
这些函数的输出对于没有 access devel information(访问 devel 信息)权限的用户是不可见的,这对于实时调试非常方便。
一个使用的例子如下所示:
dpr(node_load(5)); // Display the data structure of node 5.
dvr($user); // Display the $user variable.
模块构造器模块(The Module Builder Module)
在 http://drupal.org/project/module_builder 有一个伟大的模块,它能帮你很容易的构建出你自己的模块的骨架。它向你询问你想要创建的钩子并帮你创建它们,而且带有示例代码。接着你就可以下载这些文本并开始工作了!
应用剖析和调试
下面的 PHP 调试器和集成开发环境(IDE)是能够帮你快速找到 Drupal 瓶颈所在之处的有力工具;他们也在够帮你在你自己的模块内找出低效的算法:
- Zend Studio IDE: http://www.zend.com/
- Komodo IDE: http://www.activestate.com/Products/Komodo/
- Eclipse IDE: http://www.eclipse.org/
- Xdebug PHP Extension: http://www.xdebug.org/
在下面的图中,我们是用了 Zend Studio(拥有最漂亮的图形输出,这有争论)的截图,而其它的 IDEs 也能够生成相似的输出。图21-2展示了一个使用应用剖析器(application profiler)追踪 Drupal 请求处理,所得到的图形输出。结果显示了每个文件里面的函数所占用的相对时间。在这里,Drupal 看起来在 includes/bootstrap.inc 里花了一般的时间。
图21-2在 Zend IDE 中,一个 Drupal 请求的时间饼图
图21-3 在 Zend IDE 中,一个 Drupal 请求的调用追踪(Call trace)
在图21-3和21-4中,我们向下钻取,来查看哪些函数在一个请求期间耗费了相对较多的处理器时间。这一特性能方便的帮你决定哪些地方需要多些花工夫进行优化。
图21-4 在 Zend IDE 中,一个 Drupal 请求的函数统计
实时调试是 PHP 的特性而不是 Drupal 的,但是值得在这里讨论一下,这是由于如果你的笔记本上装有一个实时调试器的话,这就标志着你是一个 Drupal 高手。
使用一个 PHP 调试器可让你在运行时暂停 PHP 代码的执行(比如设置一个断点),并一步一步的检查都发生了些什么。熟悉一个 PHP 调试器是你作为开发者最应该掌握的一门技术。一帧一帧的追踪代码,就像电影中的慢动作,它是用来调试的非常好的方式,能让你在熟悉一个臭虫的同时逐步掌握 Drupal。
初学 Drupal 的开发着的成人礼是端上一杯沏好的茶,启动调试器,花一些小时一步步的追踪一个标准的 Drupal 请求的流程,这能够获得关于 Drupal 如何工作的第一手资料。
总结
读完本章后,你应该能够:
- 按照 Drupal 的代码规范编写代码
- 为你的代码添加注释,这样使用 API 模块来讲你的注释生成文档
- 使用 egrep 来搜索 Drupal 的代码
- 适用版本控制下载和更新 Drupal
- 干净的修改 Drupal 核心代码
- 使用统一的 diff 格式生成展示代码修改的补丁
- 使用别人创建的补丁
- 使用 devel.module 来增强你的开发效率
- 通过开发者的最佳时间来标识 Drupal 编程高手


