- 論壇徽章:
- 1
|
從使用PEAR以來,使用得最多的是PEAR::DB,但是這么幾年來,PEAR::DB一直沒有什么新的變化。在最新的一個項目里,我開始嘗試使用 PEAR::MDB2,這個包現(xiàn)在已經(jīng)替代了PEAR::DB,而且提供了豐富的特性,從它的結(jié)構(gòu)上來看,也提供了更多的擴展可能。
可能是作者忙于開發(fā)或者這個包太新,相關(guān)的文檔沒有跟上,所以只能自己閱讀代碼來搞清楚它提供的各種方法。
如何使用最基本的連接、查詢等,在PEAR手冊中(http://pear.php.net/manual/en/package.database.mdb2.php)已經(jīng)提供了簡單的說明,在此我只討論MDB2如何調(diào)用各種模塊的機制。
除了基本的sql查詢功能外,MDB2還提供了幾個擴展模塊,比如Manager(操縱數(shù)據(jù)庫對象),這個模塊可以添加、刪除數(shù)據(jù)庫,添加、修改、刪除表。要使用這個模塊提供的方法有三種方式:
$mdb->loadModule('Manager');
$mdb->manager->createTable($name, $fields); // PHP4
或者
$mdb->loadModule('Manager');
$mdb->create($name, $fields); // PHP5
或者
$mdb->mgCreateTable($name, $fields); // PHP5
前兩種方式必須事先用loadModule('Manager')載入模塊,否則無法調(diào)用。
先來研究一下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};
- }
復(fù)制代碼
前邊一部分是根據(jù)模塊的名字載入對應(yīng)的文件,并且檢查文件中是否包含了正確的類,稍微有點點攪,不過熟悉目錄結(jié)構(gòu)之后就可以看懂。(看不明白也無所謂,只要不自己寫模塊)
關(guān)鍵在于實例化的部分:
$this->{$property} =& new $class_name($this->db_index);
$this->modules[$module] =& $this->{$property};
在實例化了Manager模塊包含的類之后,把這個實例作為當(dāng)前MDB2實例的同名屬性,另外再添加為modules屬性數(shù)組的元素。
所以在PHP4下,可以用$mdb->manager->createTable($name, $fields);調(diào)用模塊的方法。
調(diào)用的時候注意用模塊名字的小寫,因為$property = strtolower($module);
如果是PHP5的話,就可以使用另外兩種更加方便的調(diào)用方式,原因是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);
- }
復(fù)制代碼
當(dāng)調(diào)用不存在的mgCreateTable()和createTable()方法時,__call()方法會被調(diào)用。
preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match)
試圖把調(diào)用的方法名拆分成為"mg C reateTable"(mgCreateTable)三段
所以使用這種方法要注意大小寫,不然無法正確的拆分
isset($this->options['modules'][$match[1]])
第一段是模塊名的縮寫,把這個縮寫拿到$mdb->options屬性中去比較
默認(rèn)的,options屬性的內(nèi)容是
- $mdb->options['modules'] = array(
- 'ex' => 'Extended',
- 'dt' => 'Datatype',
- 'mg' => 'Manager',
- 'rv' => 'Reverse',
- 'na' => 'Native',
- 'fc' => 'Function',
- );
復(fù)制代碼
$module = $this->options['modules'][$match[1]];
當(dāng)?shù)谝欢蔚膬?nèi)容是mg時,對應(yīng)的模塊就是Manager了
$method = strtolower($match[2]).$match[3];
那么方法就是createTable了
if (!isset($this->modules[$module]) || !is_object($this->modules[$module]))
$result =& $this->loadModule($module);
在modules屬性中查看是否已經(jīng)有被調(diào)用模塊的實例,modules屬性是用來存放模塊實例的數(shù)組,剛才在loadModule()部分已經(jīng)有說明
如果沒有對應(yīng)的實例則loadModule()來創(chuàng)建實例,所以這種調(diào)用方式不用事先loadModule()
foreach ($this->modules as $key => $foo)
當(dāng)三段法無效的時候,遍歷當(dāng)前所有的模塊實例
if (is_object($this->modules[$key]) && method_exists($this->modules[$key], $method)
使用method_exists()函數(shù)尋找每個實例是否有被調(diào)用的方法
return call_user_func_array(array(&$this->modules[$module], $method), $params);
最后調(diào)用找到的模塊實例的對應(yīng)方法
從以上分析可以看出MDB2對它的擴展模塊的調(diào)用方式,只要遵守這些約定,我們也可以自行擴展出自己的模塊。
不過$mdb->createTable()這種調(diào)用方式有個問題,如果兩個模塊都具有相同的方法就可能出問題,寫自己的模塊需要注意這一點。
暫時先寫到這里,以后再繼續(xù)討論幾個內(nèi)置模塊的具體使用。
[ 本帖最后由 夜貓子 于 2006-7-12 02:00 編輯 ] |
|