关于 Zend Framework 和国产 FXXX 在类加载性能上的测试

 之前,FXXX 的主要开发者 L 兄曾经在 phpchina 上同我做过一些讨论。当然讨论的结果似乎不能令人满意。FXXX 对于我一直所诟病的数据库支持对于 L 兄来说似乎不是什么大的问题。而 Zend Framework 对于他来说似乎也是一直拥有效率黑洞,以及大而不精等诸多糟粕。Zend Framework 即将成为 PEAR2 是 L 兄一个比较精辟的形容。

项目中最初推翻 FXXX 转而使用 Zend Framework 主要是由于 Orcale 的支持问题。但是这个问题似乎是 FXXX 的软肋,不能碰,一碰就招致狂风暴雨般的质问。当然,质问的内容不是框架的数据库支持。而是触碰这个软肋的人的技术水平问题。而 Zend Framework 的性能黑洞,也让我带着意思疑虑。Zend Framework 的性能是否真的不可调和,这个框架是否真的又臃肿,又庞大?带着这个问题,我做了以下试验:

试验内容:为了检测 Zend Framework 的性能到底如何,我必须选择一个参照对象。很遗憾,优秀的 FXXX 被选中了。因为这个性能的疑虑正是 L 兄带给我的,所以………….解铃还需系铃人。

那么对两个框架所有的部分都进行测试,似乎不太可能。这绝对是超出我能力范围的。那么我选择了 Loader 作为测试的内容。两个框架都有各自的加载类的方法。而这个方法又是在所有使用框架的开发中需要使用的。

Zend_Loader::loadClass() vs FXXX::loadClass()

我首先在Doc Root  目录中创建了 Zend 和 FXXX 目录,用于存放两个框架必要的文件。既然是测试 Loader 那么只要存放必要的 Loader 文件就可以了。顺便可以测试一下两个框架的耦合度。 Continue reading 关于 Zend Framework 和国产 FXXX 在类加载性能上的测试

手册中 Zend_Db_Table 的表关系例子的 E-R 图及部分解释

在 Zend Framework 的 9.8. Zend_Db_Table Relationships 章节中,介绍了使用 Zend_Db_Table 关联表的例子。我听过不少人都觉得这部分相当难理解。我觉得手册如果能给出例子中所用数据表的E-R图,那么能帮助大家更清晰的理解这个例子。

E-R 图:

Zend_Db_Table 例子中的 E-R 图

建表用 SQL 语句 (MYSQL 5.0):


— 表的结构 `accounts`

CREATE TABLE `accounts` (
`account_name` varchar(50) NOT NULL,
PRIMARY KEY (`account_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


— 表的结构 `bugs`

CREATE TABLE `bugs` (
`bug_id` varchar(50) NOT NULL,
`reported_by` varchar(50) NOT NULL,
`assigned_to` varchar(50) NOT NULL,
`verified_by` varchar(50) NOT NULL,
PRIMARY KEY (`bug_id`),
KEY `reported_by` (`reported_by`),
KEY `assigned_to` (`assigned_to`),
KEY `verified_by` (`verified_by`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; Continue reading 手册中 Zend_Db_Table 的表关系例子的 E-R 图及部分解释

Zend Framework 代码分析——Zend_Loader

Zend_Loader 应该是 Zend Framework 中相对简单的类。所有方法都为静态方法,用于加载(loading)类或文件(loadClass、loadFile、autoload、registerAutoload 方法),以及加载类或文件时进行的必要判断(isReadable 方法)。

Zend_Loader 从 3834 版本被加入 Zend Framework 并没有太多的改动。除了 4259 版本添加了 registerAutoload 方法。

public static function loadClass($class, $dirs = null)

方法是高效的加载类的方法。类的文件命名必须是 {$className}.php。当第二个参数 $dirs 为文件夹名或元素是文件夹名的数组时,将从这些文件夹中找到第一个匹配的文件进行加载。而如果 $dirs 为 null,则将类名的下划线作为分隔,划分为文件夹。例如 Zend_Foo_Bar,将会加载 Zend/Foo/Bar.php这个文件。若无法在上述所有地方找到可用的文件加载类,则将对 include_path进行搜索。

if (class_exists($class, false) || interface_exists($class, false)) {

return;

}

首先判断是否已经加载,这里使用了 interface_exists,在 php 中 interface 可以被认为是一种特殊的类。

if ((null !== $dirs) && !is_string($dirs) && !is_array($dirs)) {

require_once 'Zend/Exception.php';
throw new Zend_Exception('Directory argument must be a string or an array');

}
if (
null === $dirs) {

$dirs = array();

}
if (
is_string($dirs)) {

$dirs = (array) $dirs;

}

若类和接口没有被加载,则对 $dirs 进行验证,判断是否为合法的类型:null、字符串、数组,否则抛出异常。而最终 $dirs 都将转换为数组处理。 Continue reading Zend Framework 代码分析——Zend_Loader

申请到了 Zend Framework 中文文档翻译资格

大概是之前提交的两个 issue 的好处,Bill 这老先生亲自回了一封热情洋溢的信(不过我估计每个通过申请的人收到的内容都差不太多)。并且在我没有想到的短的时间内批准了我的申请。

调整好 cygwin,编译了一下中文手册。configure.in报了两个错误,第 2 行和第 72 行。

line 2: AC_COPYRIGHT([Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)])

line 72: AC_CONFIG_FILES([Makefile])

将这两行注释掉, 73行改为:

line 73: AC_OUTPUT([Makefile])

编译通过。

最后发现:原来是 autoconf版本我安装的是2.13,而使用2.61是可以通过的。看来文档中关于编译工具版本的说明应该更详细一些。

Zend_Db_Adapter_Pdo_Oci关于字符集设置的解决方法

默认没有提供在 Zend_Db_Adapter_Pdo 中设置 charset 的方法,使用下面的补丁可以解决这个问题:

Index: Oci.php
==========================================
— Oci.php (revision 267)
+++ Oci.php (working copy)
@@ -67,7 +67,12 @@
$tns .= ‘/’;
}
$tns .= $dsn[‘dbname’];

+
+ if (isset($dsn[‘charset’]))
+ {
+ $tns .= ‘;charset=’ . $dsn[‘charset’];
+ }
+
return $this->_pdoType . ‘:’ . $tns;
}

这样 Zend_Db::factory 方法将支持这样的设置:

<?php

require_once ‘Zend/Db.php’;

$db = Zend_Db::factory(‘Pdo_Oci’, array(
‘host’ => ‘127.0.0.1’,
‘username’ => ‘webuser’,

‘password’ => ‘xxxxxxxx’,
‘dbname’ => ‘test’,
‘charset’ => ‘utf8’
));

这个问题我已经提交到了 Zend Framework 的 Issue 中。

http://framework.zend.com/issues/browse/ZF-1541?page=all

Zend_Db_Select 在 join 系列方法上的注意

大概很多人都跟我一样,在没有遇到问题的时候不去仔细阅读手册,问题如下:

当对一个使用 join 方法的 select 使用 COUNT(*) 统计时,join 总是默认添加字段在 FROM 前。

代码如下:

$select->from($this->addStatement(‘CBBS_USER’), new Zend_Db_Expr(‘COUNT(*)’))
->join($this->addStatement(‘CBBS_POWER’), ‘CBBS_USER.USER_ID = CBBS_POWER.USER_ID’)
->join($this->addStatement(‘CBBS_ROLE’), ‘CBBS_POWER.ROLE_ID = CBBS_ROLE.ROLE_ID’)
->where(‘CBBS_USER.DEL_FLAG = 0’);

这个产生的 SQL 语句:

SELECT COUNT(*), “CBBS_POWER”.*, “CBBS_ROLE”.* FROM “ACEMACHINE”.”CBBS_USER” INNER JOIN “ACEMACHINE”.”CBBS_POWER” ON CBBS_USER.USER_ID = CBBS_POWER.USER_ID INNER JOIN “ACEMACHINE”.”CBBS_ROLE” ON CBBS_POWER.ROLE_ID = CBBS_ROLE.ROLE_ID WHERE (CBBS_USER.DEL_FLAG = 0)

实际上只需要:

$select->from($this->addStatement(‘CBBS_USER’), new Zend_Db_Expr(‘COUNT(*)’))
->join($this->addStatement(‘CBBS_POWER’), ‘CBBS_USER.USER_ID = CBBS_POWER.USER_ID’, array())
->join($this->addStatement(‘CBBS_ROLE’), ‘CBBS_POWER.ROLE_ID = CBBS_ROLE.ROLE_ID’, array())
->where(‘CBBS_USER.DEL_FLAG = 0’);

就可以产生正确的 SQL 语句:

SELECT COUNT(*) FROM “ACEMACHINE”.”CBBS_USER” INNER JOIN “ACEMACHINE”.”CBBS_POWER” ON CBBS_USER.USER_ID = CBBS_POWER.USER_ID INNER JOIN “ACEMACHINE”.”CBBS_ROLE” ON CBBS_POWER.ROLE_ID = CBBS_ROLE.ROLE_ID WHERE (CBBS_USER.DEL_FLAG = 0)

其实 Zend Framework 手册上清楚的写着:

If you give an empty array as the columns argument, no columns from the respective table are included in the result set. See a code example under the section on the join() method.

Zend_Db_Adapter_Pdo_Oci 的 Bug

这个 Bug 在 Zend Framework 的 RC1、RC2 中都存在。2007-06-13日的SVN已经更新了这个Bug。ZF 对 Oracle 的支持又上升到一个层次。

我的 patch文件

Index: Oci.php
===============================
— Oci.php (revision 5221)
+++ Oci.php (working copy)
@@ -261,7 +261,7 @@
if ($primaryKey) {
$sequenceName .= “_$primaryKey”;
}
– $sequenceName .= ‘_seq’;
+ $sequenceName .= ‘_SEQ’;
return $this->lastSequenceId($sequenceName);
}
return $this->_connection->lastInsertId($tableName);

As Golang beyond PHP