一、PEAR DB模块简介
  作为主流的Web编程语言,PHP在编写使用数据库的脚本方面做了大量的简化工作,因为现在的动态web页面越来越多地需要访问后端的数据库内容,尤其是当我们需要为访问者提供即时信息的时候。然而,虽然PHP易于使用,但是却没有包含通用的数据库访问接口,相反,对于每种数据库系统它都提供了不同的一种函数作为接口。例如,MySQL有一组函数接口,InterBase有另一组函数接口,而PostgreSQL也有它自己的函数接口。
  PHP之所以流行,是与它支持广泛的数据库引擎分不开的,因为这给人们提供了更多的选择余地。另一方面,由于不同的数据库使用不同的一组函数接口,所以移植PHP脚本的时候,必须对源代码做相应的修改。举例来说,用于提交SQL语句的函数可能是mysql_query()、ibase_query()或者pg_exec(),这要看您使用的是MySQL、InterBase或者PostgreSQL数据库。所以,当我们的脚本移植到其他类型的数据库引擎上的时候,或者从使用不同数据库引擎的人那里获得了有关的脚本的时候,必须对这些函数名进行相应的修改。
  但是自从PHP 4及更高版本后,这个问题已经可以通过PEAR中的一个数据库模块解决了。此PEAR DB模块支持一个两级体系结构的数据库访问:
上层提供了一个抽象接口,用以隐藏特定数据库的细节,所以,对于PEAR DB支持的所有数据库而言,脚本开发者无需考虑使用哪一组函数了。
下层由一些不同的驱动程序构成。每个驱动程序支持一个特定的数据库引擎,并将脚本开发者使用的抽象接口转换成数据库引擎对应的专用数据库接口。这提高了编程的灵活性,因为开发者无需考虑使用的是哪种数据库引擎的接口函数,从而隐去了很多不必要的细节。
  这些主要得益于这种体系结构所提供的抽象数据库接口。实际上,这种体系结构已经被应用于多种编程语言,例如Perl和Ruby语言中的DBI,Python语言的DB-API,Java语言的JDBC数据库访问接口等。然而,PEAR是包含在PHP发行版本中的,并且是默认安装的,所以对于较新的PHP版本,我们可以直接使用它,根本不用考虑安装问题。[url=http://www.ttplay8.cn]qq表情[/url]
  PEAR DB使用类和对象来表示面向对象的接口,所以,如果您对PHP的面向对象的程序设计还不太熟悉的话,请参考PHP手册的有关内容。
  PEAR DB体系结构主要通过两个用于所有的数据库引擎的文件以及一个特定于某个数据库引擎的文件来提供对数据库的支持的:
DB.php文件实现了创建数据库连接对象的DB类,并提供了一些实用例行程序。
DB/common.php实现了DB_common类,该类是数据库访问的基础。这个文件还包含了一些实现默认数据库访问方法和其它实用例行程序的公共代码。这些默认的方法会在后面根据需要换成特定驱动程序有关的方法。
 第三个文件包含驱动程序,它是根据您所选择的数据库而定的。每个驱动程序文件都有一个类似于DB/driver.php这样的名字,并实现了名为DB_driver的一个类,该类是从基类DB_common派生的。就MySQL而言,实际上有两个驱动程序,一个实现了对PHP扩展MySQL的支持,另一个实现了对PHP扩展mysqli的支持。DB/mysql.php实现类DB_mysql,它继承自DB_common,并提供了针对MySQL服务器的一些访问方法。这个驱动程序会访问mysql_xxx()函数。DB/mysql.php从PHP 5开始提供。它与之类似,但是实现的是DB_mysqli类,并且访问mysqli_xxx()函数。
  上面这些文件都可以在您的安装目录下面找到。通常情况下,一个脚本只会引用DB.php来获得对DB类的访问。然后,当您调用DB类的connect()方法来连接到数据库服务器的时候,这个类会确定您想使用哪种类型的服务器,并读取相应的驱动程序文件。随后,这个驱动程序文件又会引入DB/common.php。这个与Perl或者Ruby DBI 脚本仅仅引用顶层的DBI模块非常类似;我们需要的具体的低层驱动程序石油顶层的模块提供的connect()方法来确定的,我们不用操心。

  二、准备工作
  若要使用PEAR DB编写访问MySQL的脚本,必须满足下列要求:
您必须使用PHP 4或者更高版本。准确地说,应该是4.0.4版本以上。因为PHP 4之前的发行版本没有附带PEAR。此外,PHP还包含了一个称为PEAR的程序,我们可以通过它来检查安装了哪些PEAR模块,或者遗漏了哪些模块。举例来说,若要显示已经安装的模块,并且在遗漏DB模块的时候安装该模块的话,可以使用下面所示的命令: 
% pear lib % pear install DB
  注意,执行第二条命令的时候,需要以 root 身份进行。
  如果您安装了PHP 5,不仅可以使用MySQL驱动程序,而且还可以使用mysqli驱动程序。
 你的PHP版本必须包含用于MySQL的客户端程序库,否则你的脚本无法连接到MySQL服务器。如果希望使用mysqli驱动程序的话,这个客户端程序库必须是MySQL 4.1.2或更新的版本。
PHP初始化文件(PHP.ini)应该做必要的设置,使变量include_path的值为PEAR安装目录的路径名。举例来说,如果PEAR文件被安装到了/usr/local/lib/php目录下,那么就应当将include_path设置成:
include_path = "/usr/local/lib/php"        如果PEAR文件被安装到了Windows系统的C:\php\pear目录中的话,可以将include_path设置成:
include_path = "C:\php\pear"        Include_path变量也设置成其他目录,如果想让它的值包含多个目录,需要将这些目录路径名分割开来,对于UNIX操作系统可以用冒号作分割,对于Windows系统可以用分号作分割。
  如果把PHP作为一个Apache模块使用的话,每当修改了php.ini文件的时候,就需要重新启动Apache。 否则,PHP不会注意到这些改动。
  如果您是一个终端用户,没有权限修改Include_path变量,则需要联系管理员,让他把PEAR的安装目录添加到Include_path中。

  三、编写PEAR DB脚本
  当脚本使用PEAR DB接口访问MySQL的时候,通常需要经过下列步骤:
引用DB.php文件以便访问PEAR DB模块。
 通过调用connect()连接到MySQL服务器,并获得一个连接对象。
使用这个连接对象提交sql语句,并取得生成的对象
使用结果对象检索语句返回的信息
当连接对象不再需要的时候,关闭服务器连接。
  在后面,我们会对这些步骤进行更详细的讲解。

  四、引用PEAR DB
  使用任何一种PEAR DB调用之前,我们的脚本必须先引入DB.php文件。假定我们的Include_path已经设置了PEAR安装目录的名称,则可以像下面这样来引用这个文件:
require_once "DB.php";   实际上,我们可以使用任何一种文件包含语句,例如include语句或require语句皆可,但是require_once语句能够防止脚本中的其它用到DB.php的文件重复引入该文件。

  五、连接MySQL服务器
  为了连接到MySQL服务器,必须规定一个存放连接参数的数据源名称(DSN)。这个DSN是一个URL式样的字符串,用以表明数据库驱动程序(对于mysql扩展来说是mysql;对于“mysql improved”扩展来说则是mysqli),运行该服务器的主机名,访问您MySQL帐户的用户名和密码,以及想要使用的数据库的名称。DSN的一般语法如下所示:
mysqli://user_name:password@host_name/db_name   本文使用了mysqli驱动程序为例进行讲解。如果您想使用更旧的mysql驱动程序的话,可以把本文中的mysqli替换为mysql即可。
  该DSN会传递给DB类的connect()方法,举例来说,若要连接到本地主机上的MySQL服务器的测试数据库,用户名和密码为testuser和testpass,则可以使用下面的代码进行连接:
$dsn = "mysqli://testuser:testpass@localhost/test";
   $conn =& DB::connect ($dsn);
   if (DB::isError ($conn))
     die ("Cannot connect: " . $conn->getMessage () . "\n");
   如果connect()调用失败,$conn会指向一个错误对象,所以在退出之前可以利用该对象打印相应的出错信息。如果connect()调用成功,$conn指向一个连接对象,我们可以通过该对象提交语句,直到关闭该连接为止。注意,一定要检查connect()调用的结果,否则,如果返回值指出调用失败了,您还尝试用它来提交语句的话,只能得到更多的错误信息。一个替代处理错误的方法是,当PEAR 发生错误的时候让PEAR自动地终止脚本。这会在后面详细介绍。
  规定连接参数的另一种方法是将参数放到一个单独的文件中,然后从脚本中引用该文件即可。举例来说,您可以建立一个文件,将其命名为testdb_params.php,其内容如下所示:
<?php
   #用于连接数据库test的参数
   $driver = "mysqli";
   $user = "testuser";
   $password = "testpass";
   $host = "localhost";
   $db = "test";
   #用参数构造的DSN
   $dsn = "mysqli://testuser:testpass@localhost/test";
   ?>
   然后,把这个文件包含到我们的脚本中,这样就可以使用从连接参数构造的$dsn变量了,如下所示:
require_once "testdb_params.php";

   $conn =& DB::connect ($dsn);
   if (DB::isError ($conn))
     die ("Cannot connect: " . $conn->getMessage () . "\n");
  在不同的脚本中使用相同的参数时,这种方法格外简便,因为它无需将这些值分别插入各个脚本中;如果参数需要修改的话,只需改动testdb_params.php文件即可。它还使您可以把该参数文件传送到Web服务器文档树之外,也就是说,即使服务器配置出错而把PHP脚本本身作为响应返回给客户端,参数的字面值也不会显示出来。[url=http://www.wan128.cn]qq分组[/url]

  六、提交语句
  通过调用connect()获取连接对象之后,就可以通过把语句字符串传递给对象的query()方法来提交sql语句了,如下所示:
$stmt = "some SQL statement";
$result =& $conn->query ($stmt);
  返回值$result 有三种形式:
如果发生错误,DB::isError($result)将为true。这时,$result只能用来显示错误信息。
如果该字符串为诸如INSERT或者UPDATE之类的操纵数据而非返回结果集合的语句,则成功时$result的值为DB_OK。这时,我们可以通过调用$conn->affectedRows()来弄清该语句到底修改了多少行数据。
如果该字符串一个诸如SELECT之类的生成一个结果集合的语句,当该语句成功执行的时候$result为一个结果集合对象。这时,我们可以通过该对象确定结果集中的行数和列数,并能利用它来检索数据行。 当不再使用结果集的时候,可以通过调用$result->free()方法来销毁它。
  为了演示各种不同类型的语句的处理方式,我们首先讨论如何创建和填充一个表,这需要用到CREATE TABLE和INSERT语句,这两个语句都不会返回结果集合。然后,使用SELECT生成一个结果集合。一般而言,各语句的处理顺序如下所示:
调用query()执行语句。
检查语句是否成功执行。
如果语句失败,则报告有关错误消息。
如果语句取得成功,就可以检索期待的信息了,例如内容的行数等。

  七、提交不返回结果集合的语句
  以下代码将提交一个语句,该语句将创建一个简单的表animal,这个表共两列,分别为动物的名称和类别:
$result =& $conn->query ("CREATE TABLE animal
                             (name CHAR(40), category CHAR(40))");
   if (DB::isError ($result))
     die ("CREATE TABLE failed: " . $result->getMessage () . "\n");
  建好数据表之后,就可以给它填充数据了。下面的示例代码调用query()方法来提交一个INSERT语句,该语句将向表animal中载入一个数据集:
$result =& $conn->query ("INSERT INTO animal (name, category)
                             VALUES
                               ('snake', 'reptile'),
                               ('frog', 'amphibian'),
                               ('tuna', 'fish'),
                               ('racoon', 'mammal')");
   if (DB::isError ($result))
     die ("INSERT failed: " . $result->getMessage () . "\n");
   为了弄清楚一条成功的数据操纵语句对多少记录产生了影响,可以使用连接对象的affectedRows()方法,如下所示:
printf ("Number of rows inserted: %d\n", $conn->affectedRows ());   对于前面的INSERT语句, affectedRows()返回的值为4。

  八、提交返回结果集的语句
  现在,我们的表建好了,还存入了一些记录,所以可以使用SELECT语句来检索数据了,代码如下所示:
$result =& $conn->query ("SELECT name, category FROM animal");
   if (DB::isError ($result))
     die ("SELECT failed: " . $result->getMessage () . "\n");
   printf ("Result set contains %d rows and %d columns\n",
           $result->numRows (), $result->numCols ());
   while ($row =& $result->fetchRow ())
     printf ("%s, %s\n", $row[0], $row[1]);
   $result->free ();
  一个成功的query()调用将返回一个对象$result,它可以用于对结果集进行各种操作。能够从$result获取的信息包括,结果集的行数和列数,以及这些记录的内容等。当您不再该结果集合的时候,可以通过调用free()方法将其销毁。之后,$result就会失效,所以也不能再用它来访问结果集合了。

  九、获取结果集的其他方法
  FetchRow()有一个可选参数,该参数用来指示值的返回类型。默认情况下,fetchRow()将结果集的下一行(有时候也称为当前行)作为一个数组返回,数组中存放的是SELECT语句中指定的列所对应的内容,它们可以通过从0开始的数字索引来进行访问。它等价于使用下面所示的DB_FETCHMODE_ORDERED参数调用fetchRow():
$row =& $result->fetchRow (DB_FETCHMODE_ORDERED);   但是FetchRow()可以返回一个关联数组,这样的话,我们就可以通过列名来引用数组元素。为此,可以传递给fetchRow()一个参数DB_FETCHMODE_ASSOC,如下所示:
while ($row =& $result->fetchRow (DB_FETCHMODE_ASSOC))
     printf ("%s, %s\n", $row["name"], $row["category"]);
  若要将数据行作为对象来获取,可以使用DB_FETCHMODE_OBJECT模式。这样一来,我们就可以像对待对象属性那样来访问数据列的值(即记录的属性值)了,如下所示:
while ($obj =& $result->fetchRow (DB_FETCHMODE_OBJECT))
     printf ("%s, %s\n", $obj->name, $obj->category);
  当我们调用fetchRow()时传递了覆盖了默认的数据获取模式的话,可以通过调用setFetchMode()来恢复默认的模式,如下所示:
$result =& $conn->query ($stmt1);
   while ($row =& $result->fetchRow (DB_FETCHMODE_ASSOC)) ...
   ...
   $result =& $conn->query ($stmt2);
   while ($row =& $result->fetchRow (DB_FETCHMODE_ASSOC)) ...
   ...
  此外,我们还可以:
$conn->setFetchMode (DB_FETCHMODE_ASSOC);
   $result =& $conn->query ($stmt1);
   while ($row =& $result->fetchRow ()) ...
   ...
   $result =& $conn->query ($stmt2);
   while ($row =& $result->fetchRow ()) ...
   ...
  十、确定语句的类型
  通常提交语句的时候,我们都知道是否会从这个语句获得一个结果集。然而,在某种情况下这是不现实的,例如编写的脚本所执行的语句是从一个文件中随机读入的。 但是,只有知道了一个语句是否返回结果集才能进行正确的处理,为此,我们可以使用isManip()来检查语句字符串。 IsManip()是DB类的一个方法,如果语句是修改数据行的,不会返回结果集,那么它的返回值为true;如果语句是检索数据行的,会返回一个结果集,那么它的返回值为false,如下所示:
if (DB::isManip ($stmt))
   {
     #该语句将修改数据;并且不应预期返回结果集
   }
   else
   {
     #该语句将检索数据;将返回结果集
   }
 
  十一、断开服务器的连接
  使用连接完成所有任务后,应该立即关闭它:
$conn->disconnect ();   调用disconnect()方法之后,$conn就不能再作为连接对象使用了。

  十二、更多的错误处理
  PEAR为脚本开发者提供了控制PEAR错误处理的能力。默认情况下,PEAR调用返回的错误对象。一方面,这使您可以用错误信息做任何事情,例如显示一个错误信息;但是另一方面也将检查所有调用的结果的任务都甩给了我们。不过,我们还有其他处理方式。举例来说,如果我们不想测试所有调用的结果,那么可以将错误处理模式设为PEAR_ERROR_DIE,这样的话,当发生错误时,PEAR会自动显示一个错误信息和终止脚本。为了给连接对象设置错误处理模式,需要调用setErrorHandling(),代码如下所示:
$conn->setErrorHandling (PEAR_ERROR_DIE);  设置好错误处理模式之后,只要提交语句的时候发生错误,我们的脚本就会退出。换言之,我们可以假设,如果$conn->query()返回的话,表明它成功了,所以无需测试结果。需要注意的是,这里setErrorHandling()是作为连接对象的方法来调用的,所以在持有一个有效的连接的时候才能调用它;换句话说,我们无法使用它来捕获在尝试连接期间发生的错误。如果希望俘获所有的PEAR错误,包括connect()调用失败的错误,那么就需要将setErrorHandling()作为PEAR类的方法来调用,如下所示:
PEAR::setErrorHandling (PEAR_ERROR_DIE);   十三、移植注意事项
  将PEAR DB脚本移植到其它类型的数据库的时候,最理想的情形就是只需修改传给connect()调用的DSN字符串。举例来说,如果我们有一个MySQL脚本,现在想移植到PostgreSQL上,只把DSN字符串改为符合PostgreSQL驱动程序的要求就行了。 然而,PEAR DB对无法移植到其他数据库引擎的SQL却无能为力。举例来说,如果我们使用了MySQL的AUTO_INCREMENT特性来生成序列数,那么这种SQL语法是无法移植到其它数据库的。 有时候,可以使用PEAR级别的构造来避免SQL级别的不可移植性。就序号生成而言,PEAR DB模块提供了一个生成序号的工具,而该工具根本不涉及SQL。其底层实现细节都被驱动程序隐藏了起来。
  通过在与特定引擎有关的函数之上添加一个抽象层,PEAR DB为开发人员隐藏了所有与特定数据库有关的细节,从而为开发人员提供了一个统一接口。这极大的简化了开发人员的工作,但是也为底层实现增加了复杂性。所以,在使用PEAR DB时,其效率要比直接使用本地数据库访问函数低一些。