- 论坛徽章:
- 1
|
从使用PEAR以来,使用得最多的是PEAR::DB,但是这么几年来,PEAR::DB一直没有什么新的变化。在最新的一个项目里,我开始尝试使用 PEAR::MDB2,这个包现在已经替代了PEAR::DB,而且提供了丰富的特性,从它的结构上来看,也提供了更多的扩展可能。
可能是作者忙于开发或者这个包太新,相关的文档没有跟上,所以只能自己阅读代码来搞清楚它提供的各种方法。
如何使用最基本的连接、查询等,在PEAR手册中(http://pear.php.net/manual/en/package.database.mdb2.php)已经提供了简单的说明,在此我只讨论MDB2如何调用各种模块的机制。
除了基本的sql查询功能外,MDB2还提供了几个扩展模块,比如Manager(操纵数据库对象),这个模块可以添加、删除数据库,添加、修改、删除表。要使用这个模块提供的方法有三种方式:
$mdb->loadModule('Manager');
$mdb->manager->createTable($name, $fields); // PHP4
或者
$mdb->loadModule('Manager');
$mdb->create($name, $fields); // PHP5
或者
$mdb->mgCreateTable($name, $fields); // PHP5
前两种方式必须事先用loadModule('Manager')载入模块,否则无法调用。
先来研究一下loadModule()
- function &loadModule($module, $property = null, $phptype_specific = null)
- {
- if (!$property) {
- $property = strtolower($module);
- }
- if (!isset($this->{$property})) {
- $version = $phptype_specific;
- if ($phptype_specific !== false) {
- $version = true;
- $class_name = 'MDB2_Driver_'.$module.'_'.$this->phptype;
- $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
- }
- if ($phptype_specific === false
- || (!MDB2::classExists($class_name) && !MDB2::fileExists($file_name))
- ) {
- $version = false;
- $class_name = 'MDB2_'.$module;
- $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
- }
- $err = MDB2::loadClass($class_name, $this->getOption('debug'));
- if (PEAR::isError($err)) {
- return $err;
- }
- // load modul in a specific version
- if ($version) {
- if (method_exists($class_name, 'getClassName')) {
- $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index);
- if ($class_name != $class_name_new) {
- $class_name = $class_name_new;
- $err = MDB2::loadClass($class_name, $this->getOption('debug'));
- if (PEAR::isError($err)) {
- return $err;
- }
- }
- }
- }
- if (!class_exists($class_name)) {
- $err =& $this->raiseError(MDB2_ERROR_LOADMODULE, null, null,
- "unable to load module '$module' into property '$property'");
- return $err;
- }
- $this->{$property} =& new $class_name($this->db_index);
- $this->modules[$module] =& $this->{$property};
- if ($version) {
- // this will be used in the connect method to determine if the module
- // needs to be loaded with a different version if the server
- // version changed in between connects
- $this->loaded_version_modules[] = $property;
- }
- }
- return $this->{$property};
- }
复制代码
前边一部分是根据模块的名字载入对应的文件,并且检查文件中是否包含了正确的类,稍微有点点搅,不过熟悉目录结构之后就可以看懂。(看不明白也无所谓,只要不自己写模块)
关键在于实例化的部分:
$this->{$property} =& new $class_name($this->db_index);
$this->modules[$module] =& $this->{$property};
在实例化了Manager模块包含的类之后,把这个实例作为当前MDB2实例的同名属性,另外再添加为modules属性数组的元素。
所以在PHP4下,可以用$mdb->manager->createTable($name, $fields);调用模块的方法。
调用的时候注意用模块名字的小写,因为$property = strtolower($module);
如果是PHP5的话,就可以使用另外两种更加方便的调用方式,原因是PHP5的新特性__call()方法。
- function __call($method, $params)
- {
- $module = null;
- if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match)
- && isset($this->options['modules'][$match[1]])
- ) {
- $module = $this->options['modules'][$match[1]];
- $method = strtolower($match[2]).$match[3];
- if (!isset($this->modules[$module]) || !is_object($this->modules[$module])) {
- $result =& $this->loadModule($module);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- } else {
- foreach ($this->modules as $key => $foo) {
- if (is_object($this->modules[$key])
- && method_exists($this->modules[$key], $method)
- ) {
- $module = $key;
- break;
- }
- }
- }
- if (!is_null($module)) {
- return call_user_func_array(array(&$this->modules[$module], $method), $params);
- }
- trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method), E_USER_ERROR);
- }
复制代码
当调用不存在的mgCreateTable()和createTable()方法时,__call()方法会被调用。
preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match)
试图把调用的方法名拆分成为"mg C reateTable"(mgCreateTable)三段
所以使用这种方法要注意大小写,不然无法正确的拆分
isset($this->options['modules'][$match[1]])
第一段是模块名的缩写,把这个缩写拿到$mdb->options属性中去比较
默认的,options属性的内容是
- $mdb->options['modules'] = array(
- 'ex' => 'Extended',
- 'dt' => 'Datatype',
- 'mg' => 'Manager',
- 'rv' => 'Reverse',
- 'na' => 'Native',
- 'fc' => 'Function',
- );
复制代码
$module = $this->options['modules'][$match[1]];
当第一段的内容是mg时,对应的模块就是Manager了
$method = strtolower($match[2]).$match[3];
那么方法就是createTable了
if (!isset($this->modules[$module]) || !is_object($this->modules[$module]))
$result =& $this->loadModule($module);
在modules属性中查看是否已经有被调用模块的实例,modules属性是用来存放模块实例的数组,刚才在loadModule()部分已经有说明
如果没有对应的实例则loadModule()来创建实例,所以这种调用方式不用事先loadModule()
foreach ($this->modules as $key => $foo)
当三段法无效的时候,遍历当前所有的模块实例
if (is_object($this->modules[$key]) && method_exists($this->modules[$key], $method)
使用method_exists()函数寻找每个实例是否有被调用的方法
return call_user_func_array(array(&$this->modules[$module], $method), $params);
最后调用找到的模块实例的对应方法
从以上分析可以看出MDB2对它的扩展模块的调用方式,只要遵守这些约定,我们也可以自行扩展出自己的模块。
不过$mdb->createTable()这种调用方式有个问题,如果两个模块都具有相同的方法就可能出问题,写自己的模块需要注意这一点。
暂时先写到这里,以后再继续讨论几个内置模块的具体使用。
[ 本帖最后由 夜猫子 于 2006-7-12 02:00 编辑 ] |
|