PEAR:创建中间的数据库应用层

function errorMessage($dbcode)

返回DB错误文本信息。

function &raiseError($code = DB_ERROR, $mode = false, $level = false,$debuginfo = false, $nativecode = false)

抛出一个错误。这个函数由DB来调用。

function setFetchMode($fetchmode)

设置缺省的fetch模式。

fetchmod有以下几种:

DB_FETCHMODE_DEFAULT :使用缺省的模式
DB_FETCHMODE_ORDERED :每条记录的数据列使用数字索引,从0开始
DB_FETCHMODE_ASSOC :每条记录的数据列使用字段名索引,同查询中的字段名一致
DB_FETCHMODE_FLIPPED:如果结果集是多维的,多条记录多个字段,一般来说返回一个2维数组,第一维是记录号,标明是第几条记录,第2维的数组则使用字段名或数字索引。DB_FETCHMODE_FLIPPED则会交换这个顺序,也就是说,第一层是字段名,第2维才是记录号.

function setOption($option, $value)

设置后端数据库选项。$options,$value分别是选项名和相应的值。

一般不用直接调用,在DB_Common及其子类的构建函数中会调用这个函数。

function getOption($option)

取得某个option的值

function prepare($query)

为execute()准备编译一个查询。对于某些后端数据库,这是通过仿真来实现的。返回编译后的查询句柄,出错则返回一个 DB_Error对象。

function execute($stmt, $data = false)

执行编译后的查询。$stmt是由prepare返回的句柄。$data是参数数据,如果你使用的是参数查询,$data将要包含每个?参数的值

例子:

<?php
/**
  * 连接到MYSQL数据库
  */
  $host   = "localhost";
 
  $user   = "root";
  $passwd = "";
  $persistent = 1;
 
  if ($persisternt){
     $conn = mysql_connect($host,$user,$passwd);
  }else {
     $conn = mysql_pconnect($host,$user,$passwd);
  }
?>

function executeEmulateQuery($stmt, $data = false)

返回一个实际的查询字符串,供仿真prepare,execute的时候使用,内部函数。如果出错,则返回DB_Error对象

function executeMultiple( $stmt, &$data )

在同一个查询句柄上执行多个查询。$data必需是一个从0开始的数组,这个函数将依次使用数组中的每一行数据来调用execute。这个函数一般用于参数查询。你可执行一次查询的编译,然后将不同的参数值放入$data数组,然后就可一次同时执行查询了。需要注意,如果中间某个查询出错,剩余的查询不会继续进行而是返回错误。

function modifyQuery($query)

内部函数,用于后端数据库修正查询,后端数据库实现这个函数,然后返回进行优化和修正后查询串。
例子:这是DB_mysql的实现,

<?php
function sql_exec($sql) {
    global $db_Name;
    $result = mysql_db_query($db_dbName,$sql);
    if (!$result) {
            echo mysql_errno(). ": ".mysql_error(). "<br>$sql<br>";
            exit();             
    }
    return $result;
}
$db_Name = "test";
$sql = "select * from users";
$result = sql_exec($sql);
while( $row = mysql_fetch_row($result) ){
    echo "姓名:$row[0] 性别:$row[1] 年龄 $row[2]<br>";
}
mysql_free_result($result);
?>

function &query($query)

执行一个查询,查询成功,如果是有结果集的查询,则回一个DB_Result对象,如果没有结果集的查询则返回 DB_OK。

出错则返回DB_Error对象。

function &getOne($query, $params = array())

执行查询,并返回结果集中第一行第一列的数据。$params是参数值,如果后端数据库支持,将调用prepare /execute来使用这些参数。

例子:

<?php
include_once "DB.php";
/*
* 连接到数据库
*/
   $db_host = "localhost";
   $db_user = "root";
   $db_passwd = "";
   $db_dbName = "test";
   $PersistentConnection = 1 ;
   $db_type ="mysql";
   $db_proto ="";
   $db = DB::connect("$db_type://$db_user@$db_passwd:$db_host/$db_dbName",$db_options);
   if( DB::isError($db) ){
       die "无法连接数据库,错误原因:".DB::errorMessage($db);
   }
function sql_exec($sql) {
    global $db;
    $result = $db->query($sql);
    if (DB::isError($result)){
        echo "发生数据库错误:".DB::errorMessage($result);
        exit();
    }
    return $result;
}   
/////////////////////////////////////////////
$sql = "select * from users";
$result = sql_exec($sql);
while( $row = $result->fetchRow() ){
    echo "姓名:$row[0] 性别:$row[1] 年龄 $row[2]<br>";
}
?>

function &getRow($query, $fetchmode = DB_FETCHMODE_DEFAULT, $params = array())

执行查询,请返回结果集的第一条记录。

$fetchmod 指定fetch模式,如果省略,使用缺省模式。

例子:

   $db_type ="mysql";
变成:
   $db_type ="pgsql";

function &getCol($query, $col = 0, $params = array())

执行查询,并返回包含结果集中指定字段列的值的数组。

$col是要返回的列的索引,可以是整数,或者是关键字段名。

例子:

$dsn = array(
       'phptype'  => 'mysql',
       'dbsyntax' => '',
       'protocol' => '',
       'hostspec' => 'localhost',
       'database' => 'test',
       'username' => 'root',
       'password' => ''
       )

function &getAssoc($query, $force_array = false, $params = array())

执行查询,并返回一个关联数组。

$force_arry 强制返回数组。如果true,那么即使你的结果集里只有2个字段,那么关键字段对应的值也是一个只有一个元素的
数组。如果false,那么关键字段对应的值是一个标量了。

注意,这个关联数组有些特别:
如果你查询的是”select userid,name,reg_date from user”,记录集是:

userid name reg_date
test testor 2001-07-05
test2 teest2 2001-07-06

那么返回的数组是这样的:

<?php
include_once "DB.php";
if ( DB::assertExtension("php_oci8") ){
    echo "oracle 8扩展加载成功!"}else {
    die "无法加载oracle 8扩展"}
?>

例子:

function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null) 
//$code是DB错误代码,$mode决定错误处理的模式,缺省是返回,$level 错误级别, 
//$debuginfo是附加的调式信息,如刚刚执行的SQL语句等等。
 
 //DB_Warning 
//类似DB_Error。
 
//DB_result 
//这是非常重要的类。

function &getAll($query, $fetchmode = DB_FETCHMODE_DEFAULT, $params = array())

返回包含结果集中全部记录的数组。注意,如果你的结果集很大,不要使用这个函数。

例子:

function fetchRow($fetchmode = DB_FETCHMODE_DEFAULT)

function autoCommit($onoff=false)

指定是否自动提交事务。有的后端数据库不支持。

function commit()

提交当前事务

function rollback()

回滚当前事务

function numRows($result)

返回结果集中的记录数

function affectedRows()

返回上一次查询操作的记录数

function nextId($seq_name, $ondemand = true)

返回指定的sequence的下一个值

function createSequence($seq_name)

创建一个Sequence

function dropSequence($seq_name)

删除一个Sequence

五、更进一步,创建你自己的中间数据库应用层

到此,我们对于DB类的功能已经有了更深的了解。我们可以基于DB类来创建你自己的数据库应用层了。也许你会问,我为什么还要创建新的类,直接在我的应用程序中使用DB不好么?答案是,当然可以,但是我不推荐。

首先,虽然DB的功能很强大,但是仍然是过于底层的,你的类应该是面向应用的。你的这个数据库层应该屏蔽不需要使用的功能和函数,同时,也要提供一些更’高级’的方法,比如,你的应用程序不应该去联接数据库,释放资源,这些应该是透明的。那么这些工作就要由你的这个类来完成了。

其次,DB仍有一些缺陷,一旦找到比它更好的,你可以迅速地升级。你所要改的只是这个类的方法如何实现,你的应用程序的其他模块不会为此受到影响。

在你设计自己的类的时候,希望能够一些准则:

提供基本的自由的查询接口。包括query,execute.分别对应有结果集和无结果集的查询。

不要使用特定数据库的某些特性,即使因为放弃使用这些特征会给你增加不少的代码量。
尽量屏蔽一些数据库的操作细节,比如连接数据库,释放资源等等。

六、 DB的不足

上面我们讨论了DB的优点和一些使用的方法与技巧。但是,任何事物都不是十全十美的,DB类更是如此,由于DB乃至PEAR的开发时间并不长,因此DB并没有最终全部完成,其中也或多或少地存在一些BUG或者设计上的问题,需要我们在使用中去发现和修正。

MYSQL的问题

问题1:前段我在开发一个项目中,发现DB的MYSQL类有一个问题,那就是当你connect的时候,MYSQL类自动将当前数据库设置为$DSN中的数据库名。以后使用query的时候,都是使用当前数据库。
下面是原来connect的代码:

function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT)

这导致了一个后来令我费解的问题:

我的项目需要我连接2个数据,假设分别是user和test。test是我的主要数据库,但是我要从第一个数据库中user中取得用户信息,同时test中保存用户的权限信息。我为此做了一个中间的类CV_DB,用来实现我的数据库层。在 CV_DB的创建函数中,我连接到指定的数据库,从而我可以这样使用:

function numCols()

后面当我执行,查询某个用户的信息的时候,出现了”该表不存在”的DB错误。但实际上这个表是存在的。最后,我发现原来是DB/mysql.php的问题,因为它的查询使用的是,mysql_query,而不是 mysql_db_query,当我连接到第2个数据库的时候,MYSQL的当前数据库变成了第2个,这时我再执行针对第一个数据库的查询,当然会出错。

解决方式有2个,在创建CV的时候不连接到数据库,查询的时候连接,查询完毕后断开。或者,修改DB /Mysql.php。我选择后者,我将上面几行注释,然后将SimpleQuery中的mysql_query 替换成mysql_db_query.

问题2:mysql没有execute,因此它继承使用了 DB_common中模拟方式,但是不幸地是,这带来了新的问题,在一些更新查询中,所要更新的数据有? &这2个特殊字符的时候,会出现问题,因为prepare认为这是参数查询的字符,将进行分析,导致无法更新数据。

解决方式也有2个:替换?和&,但是这样要考虑的事情很多。或者:直接使用 simpleQuery或者query。

我选择后者,由于我的其他类只和CV――我这个中间数据库应用类打交道,于是,我在CV的execute方法中做了判断,如果是后端数据库是mysql,那么我直接调用simpleQuery,否则,调用后端数据库的 prepare和execute。这样,实际的后端数据库对于我项目中的其他应用类是透明的,我可以简单地做相应的调整,这也是我设计数据库应用层的初衷。

DB的开发状态


DB目前仍在不断地开发当中,在DB/下面有一个文件STATUS,它描述了DB类的功能和各个后端数据库的实现情况,下面是 PHP4.0.6这个发布中的开发情况:

function numRows()

七、参考资源

ADODB另一个非常好操纵数据库的PHP程序,DB的绝好的替代者

关于作者
潘凡(Night Sailer):北京赛迪数据有限公司工程师。主要研究兴趣是Perl,PHP与XML的应用,PHP的MVC开发模式,PERL-GTK的使用。您可以通过 E-mail:[email protected] 跟他联系。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据