文章
| Oracle + PHP
部署 PHP 系列,第 4 部分: Oracle 和 PEAR::MDB2 数据库抽象层的结合使用 本文是介绍了 Oracle 和 MDB2 数据库抽象层间结合使用的过程,附带简单的示例。
PEAR - PHP扩展及应用资料库 (PHP Extension and Application Repository) 其中汇集了大量高质量 PHP 组件。这个资料库包含了众多 PHP 类,它们可用于从数据库验证到网络连接,到图像,到 XML 等几乎所有用户可想到的目的。 在部署 PHP 系列的这一篇文章中,我将详述如何使用 PEAR 中的高级别数据库抽象层 — MDB2 。(免责声明:PEAR::MDB2 目前被评定为“beta”质量。尽管它有一个经反复测试的稳定的代码基础,但其 API 可能会随时间而发生变化。因此,它在 PEAR 资料库中不能被评定为“稳定”质量。) 数据库抽象层的使用在部署 PHP 应用程序的许多情形下都是很有帮助的 — 尤其当您处理不同的数据库系统或计划将一个应用程序从一个数据库迁移到另一个数据库的时候。特别是当您想在应用程序中操作数据库结构时,该层对您将非常有用。其接口将为你带来极大的方便,你不必再手动创建所有必要的 SQL 语句,使用本文中示例之一提供的简单方法,您可以运行间即时创建表。MDB2 中的模式管理器使您可以在您的应用程序的更新版本第一次运行时自动更改数据库,这样您在安装期间就不必担心这一任务了。 PEAR 背景 在开始应用 MDB2 之前,您必须对 PEAR 有更进一步的了解,这是因为它不仅提供必需的程序包,还提供了独特的安装基础架构来管理这些程序包。因此,首先您需要了解本文所需程序包的安装过程。 从版本 4.3.0 起,每个 PHP 分发版本都附带有 PEAR 安装程序(除非禁用了该程序)。要测试 PEAR 是否正确安装,只要在 shell 中键入“pear”,然后按回车即可。如果您看到一个 PEAR 命令列表,就可以跳过下一步。 也许您有一个较老版本的 PHP 或者您(偶然或者有意地) 在安装过程中停用了 PEAR。在那些情况下,您可以从 Web 中启动 PEAR。 根据您的操作系统,您可以使用下列命令链(这在大多数 UNIX 或类 UNIX 的系统上都应该有效): lynx -source http://go-pear.org | php -q如果您没有运行该命令所需的环境,您可从 http://go-pear.org 下载这个源文件,将其保存在本地(可以命名为“pearbootstrap.php”),然后在其上运行您的 PHP CLI/CGI ( php -q pearbootstrap.php)。两种方法都会开始一个交互的自举过程,以安装 PEAR 基类和 PEAR 安装程序。 安装完毕后,您需要通过 php.ini、htaccess 文件,或者任何其他方法,确保将到 PEAR 的基路径加入到您的 include_path 指令中。如果您在安装 PEAR 或在准备安装程序过程中进展不顺,则可以使用安装程序命令 pear config-show 和 pear config-set,它们对您将有所帮助。如果您不得不使用代理服务器的话,请确保针对它进行相应的设置。还有一点要考虑到,就是升级到最新的 PEAR 版本,您可以通过执行 pear upgrade-all 完这一任务。
接下来,您需要安装必要的程序包。PEAR 安装程序为此提供了命令 pear install, 其后必须跟着一个程序包名称。然后安装程序将会从 PEAR Web 站点下载此程序包并完成安装。在我们的例中,您需要输入“pear install MDB2”。通常首次使用这种方法会失败,这是因为您的 PEAR 的初始安装的选项 preferred_state 设置为稳定。PEAR 安装程序不会安装稳定性低于 preferred_state 选项设置状态的程序包(为了不破坏您的应用程序)。因为 MDB2 目前在测试阶段,您必须使用 pear install MDB2-beta 进行强制安装。 MDB2 程序包已经被拆分成几个子程序包,您也将需要安装其中的一些了程序包。因此您需要运行 pear install MDB2_Schema-beta 来获取对数据库模式的支持 — 您将会在后面的示例中使用到这些数据库模式。每个数据库驱动程序驻留在其自己的子程序包中。因此,您需要使用 pear install MDB2_Driver_oci8-alpha 来分别安装 Oracle 驱动程序。您可以访问每个程序包各自的 Web 站点以了解更多详细信息,如要了解 Oracle 驱动程序的信息,请访问:pear.php.net/package/MDB2_Driver_oci。 成功安装这些程序包之后,我们就可以继续下一步,但是在开始之前,需先大致介绍一下数据库抽象层的概念。 数据库抽象 PHP 为所有常见的数据库和一些特别的数据库提供了扩展。对于不依赖于数据库的应用程序,数据库抽象扮演了最重要的角色。即便您不打算将你的应用程序用于不同的数据库后端,抽象也有意义。要想对进一步研究抽象层,我们先来了解一些类型的数据库抽象。
调用抽象。当您的开发人员必须处理不同的数据库时,强烈建议使用这类抽象。每个 PHP 数据库扩展提供一个完全不同的 API,如:Oracle 8 (OCI) 扩展使用 oci_execute()。调用抽象为所有数据库扩展提供了一个通用 API。 数据类型抽象。数据类型抽象是更高级别的抽象。每个数据库有其自己的数据类型和用于它们的规范(例如:日期和时间字段)。对那些数据类型进行抽象后,您能够以统一的方式来访问并使用不同类型的数据。 SQL 抽象。无论您把 SQL 抽象放在一个更高的级别或与数据类型抽象同一级别,当您从一个数据库迁移到另一个时,该项特性对你都非常有用。每个数据库都支持标准 SQL 的一些专用扩展。尽管大多数这些扩展都针对纯 SQL 的类似问题,但是它们使用了不同的语法。其他情况有如在一个数据库中实现某一特性,但在另一个数据库中却不提供该特性。SQL 抽象可用于解决这两种情况,它为那些特性提供了统一的 API,如果在特定数据库不提供某功能时,它可以模拟该功能。 关于 MDB2 MDB2 是 MDB 抽象层的第二版,MDB 抽象层开始是 Metabase(一种不依赖于 PEAR 的数据库抽象层)到 PEAR 的端口。PEAR 资料库提供了这两个版本,因此请使用 PEAR 安装程序。前面已经介绍了安装本文所必需的程序包的过程。
MDB2 不是 PEAR 中唯一的抽象。PEAR::DB 也提供了级别较低的抽象(调用抽象和一部份 SQL 抽象)。而 MDB2 提供了完整的数据类型抽象和更多的 SQL 抽象。此外,它还提供一些其他好的特性,本文后面将涉及这些内容。 本文提供的全部是 MDB2 与 Oracle 数据库一起使用的示例。但是,因为 MDB2 是一个抽象层,所以它与其他关系数据库系统的一起使用的过程也是类似的。 使用入门 如果您曾经使用过数据库抽象层,那么对 MDB2 你将会很快上手。MDB2 的基本使用与 PEAR::DB 的使用没有很大差别,PEAR::DB 是一个极其成熟的抽象层,而且有非常完善的文档记录。
部署。 可能您已经很熟悉 PDO(PHP 5.1 中的新抽象层)。MDB2 的 API 力图尽可能接近 PDO,MDB2 试图将 PDO 扩展到 PHP 用户群。 为初始化数据库连接,MDB2 使用一种被称为 DSN(data source name,数据源名称)的机制。DSN 可以用一个字符串表示,或者可以是一个相关数组。您需要的基本元素是“hostspec”,通常数据库主机要连接到它;还有“phptype”,数据库的类型(如 Oracle 数据库的“oci8”)。在数据库为 Oracle 的情况下,hostspec 是一个本地连接标识符。如果您在 MDB2 中省去它,MDB2 会试图通过特定环境变量 ORACLE_SID 取得 Oracle 系统 ID(System ID,SID)。此外,还需要一个用户名,在多数情况下还需要密码。数组表示使用元素名称作为关键字,字符串表示使用类似 URI 的格式: oci://username:password@hostspec我们的第一个示例 —名称为 init.inc.php 的文件(在示例代码中),精确演示了创建连接的过程。所有其他的示例脚本都包含有 init.inc.php,用于初始化数据库连接。
在代码中你会看到已经有很多注释,但在这里我们还是要对整个过程进行一下说明。 这个文件中的第一个内容是 DSN 数组的定义 — 如前面所述。(如果您想要试用这些脚本,这个部分是必须根据您的环境来自定义的。)定义 DSN 后,使用 MDB2::connect() 建立了连接。此处要注意的是 PEAR 错误处理的使用。 MDB2::connect() 或者返回一个数据库驱动程序对象,或者返回一个类型 PEAR_Error 的对象;后者可以用 PEAR::isError() 检查。如果出现错误,脚本会停止,错误消息在屏幕上显示。(在后面的示例中我们还将涉及这些错误处理。) 除了 connect() 方法,MDB2 还提供一种叫做 singleton() 的方法,如果您使用这种方法,它不会重新创建数据库连接而是重用一个现有连接(在你已经连接的前提下)。这使您能够在应用程序中的任何地方获得一个数据库连接,而无须使用参数传递它或直接访问全局(所谓的单态模式)。 如果您连接 Oracle 数据库有问题,可以使用 $db->getDebugInfo(); 来获取关于所出现错误的更详细的信息。在任何您遇到奇怪的行为或接收到错误的情况下,可调用 getDebugInfo(),他对您有帮助。 您现在已经建立了一个数据库连接,所以让我们了解一些一些简单的查询和基本的 MDB2 方法。所有示例在示例代码下载中都可以找到。 示例 1:简单查询 (example_01_simplecalls.php) 此示例中的第一个注释说明了我们所有示例使用的表结构。除此之外,还有两个 INSERT 语句用于加入一些数据。为了试用此示例,您需要创建在该处定义的表。接下来,需要包含必要的文件(MDB2 基文件和我们的 init.inc.php)。
在此示例后面的部分中,创建了若干简单的 SQL 语句并使用 $db->query() 将其提交到数据库。再次请注意 PEAR 错误处理的使用,因为 query() 在失败时返回一个 PEAR_Error 对象。在其他情况下, query() 将返回常量 MDB2_OK 或一个结果集对象(当选择数据时)。在用 select 查询数据库后,您将会看到两种不同的方法(其中一个是批注过的),它们从 query() 方法(行 43ff 和 52ff)返回的结果集中读取结果。在此处, fetchRow() 方法用于检索下一个结果记录。注释掉的代码使用默认的抓取模式,在此,返回的数组的字段是带编号的。未注释掉的方法使用不同的抓取模式 ( MDB2_FETCHMODE_ASSOC),它返回一个按列名称索引的数组。还有其它更多的读取方法,如 MDB2_FETCHMODE_OBJECT,它返回对象而非数组,这些数字将列作为其属性。 在完成“insert”和“delete”查询后,您将发现用于读取数据的更多高级的方法。例如, queryAll() 方法使用您的 SQL 语句查询数据库,并将所有检索到的结果返回至数组中,所有结果按数字进行索引。 MDB2 的基本使用现在应该已经清楚了,我们现在继续了解一些更为高级的特性。请注意,为了专注于更重要的特性,在这里我有意略去了一些特性,如 MDB2 的数据类型抽象 — 使用它在 PHP 中自动转换数据类型(如,日期)的过程。 示例 2:高级查询 (example_02_extendedcalls.php) 本文到这里为止,MDB2 所做的事情其他数据库抽象层也能做。在此示例中,您将看到一些更为高级的查询类型。
这一示例脚本初看上去好像与前面的脚本并无二样。但是在行 26,我们将一些更高级的功能加载到了数据库驱动程序中。这是 MDB2 中的最佳特性之一:模块化。如果您不需要高级别的抽象,而且只想所有类型的数据库都使用一个简单的统一 API,则 MDB2 将只加载这个功能(它是默认功能)。任何其他代码都可以按需加载。在我们的示例中,我们加载了“Extended”模块,这样我们可以使用更多的方法。 您将看到的第一个特性是 SQL 抽象的一部分。每个数据库都会执行一个功能用于限制由“select”查询返回的记录量,但就这一功能也有众多不同的 SQL 语法。 limitQuery() 调用方法先构建所用的数据库(在我们的示例中是 Oracle)所需的查询。 limitQuery 的第一个参数是要提交的查询,接下来您可以定义返回的数据类型(我们不使用该特性,所以在此我们传递 null)。随后在后面加入两个限制参数。第一个参数表明要读取的记录数,第二个给出从何处开始读取的偏移数。还可以使用这一种方法来达到选择一定数量的行的目的:首先调用 $db->setLimit(),然后运行前面所见的查询,如 query() 或 queryAll() 方法 。 我们在此示例中看到的另一个扩展特性是 getAssoc() 方法,它与 queryAll() 作用类似,但将按首列对返回的结果进行重新索引。也就是说,返回的结果记录数组不再按连续整数值(0、1、2 ……)索引,. .)而是按在 SELECT 语句中检索到的首列(应该是某种 ID)索引。请注意,如果所使用的列的索引符不是唯一的,后面的值会覆盖较早的值。 示例 3:管理数据库 (example_03_manager.php) 现在,您对 MDB2 有了一个总的了解,这将帮助你开始使用它及使用在 PEARWeb 上自动生成的 API 文档。您应该能够管理由 MDB2 提供的所有典型数据库抽象功能。现在,我们将了解数据库管理函数,在讨论应用程序部署时,它们是最值得注意的内容。
在示例 3 中所说明情况是按需创建数据库表。您的应用程序无需任何数据库安装脚本,因为它会在过程中即时完成这项工作。 首先,尝试查询数据库,对数据库表进行简单的选择查询。如果 MDB2 返回一个 MDB2_ERROR_NOSUCHTABLE 的错误代码,您就会知道要使用的表不存在。这意味着您必须创建此表,这可以通过使用一个“管理器”来完成。 此管理器是与我们在示例 2 中使用的“扩展的”模块类似的 MDB2 模块,所不同的是,它不对我们目前使用的数据库驱动程序的功能进行增强,而是创建一个新对象 — manager(行 22)。 使用这一管理器对象,我们现在能够轻松创建并操作数据库表,使用代码中的(行 28ff) $table 数组表示的简单的表模式。数组的每个关键字表示我们的表的一列,它是由另一个数组定义,作为该数组的值。这些列数组至少定义一个类型(前面描述的 MDB2 的抽象类型之一),也可以定义更多的属性 — 如代码中所示。这个数组的格式在 manager class 的 createTable() 方法中有完善的文档。调用 $manager->createTable() 后,接收表的名称来创建并定义数组。 此外,您要定义新表的主关键字。通过管理器也可以完成这一任务 — 尽管它本身并不支持“主关键字”。到目前为止管理器只支持唯一索引,而非使用真正的主关键字约束。如果这些索引的字段值显式设置为“非空”,它们的使用效果如主关键字一样。当然,这个解决方案是最常见的数据库特性之一,但是,在某种程度上来说,这是抽象的代价。 因此,我们先定义一个唯一索引作为主关键字。索引的定义像表一样 — 使用数组。一个索引定义数组必须至少有一个“fields”关键字,它被分配到一组字段名,这些字段名为索引的一部分。在我们的示例中,我们只有一个字段 — ID。对 createIndex() 的调用也非常简单。该方法接收创建索引所在的表的名称、索引的名称以及定义数组。 在大 IF 语句内的最后一个操作是再次使用 SELECT 语句,以运行大 IF 语句后接收有效结果对象用于错误检查。最后,再次检查结果(可能出现另一个错误如 MDB2_ERROR_NOSUCHTABLE,或创建表之后“select”仍返回一个错误)。 首次运行此脚本后,您应该查看一下您的数据库结构:打开 SQL*Plus 并运行如 "describe categories" 来查看新表是如何创建的。 示例 4:模式 (example_04_schema.xml) 管理表的另一种方法是在抽象模式中定义它们。该下载的示例代码中的是一个典型的 MDB2 架构定义,其中有很多注释。我们在此不会深入讨论这个定义,因为它是有完善的文档的。MDB2 数据类型在此又一次扮演了一个角色,但这些也是有非常完善的文档的。
我们要了解这些必要的 PHP 代码,以及模式的用途。首先,一个模式可以定义完整的数据库或只定义单个表。它定义了元素和数据的结构,结构与示例 2 中的管理器数组结构类似。 首先,初始化使用模式所需的模块。此模块是第一个从 MDB2 分离至其自身的子程序包中的模块。因此,语法与我们之前所使用的不同。MDB2_Schema 类(如您所见,必须显式要求)提供一个 factory 方法,此方法为我们的数据库连接创建一个模式管理器。 使用新创建的模式管理器,您可以使用 updateDatabase()(行 27)根据模式文件对数据库进行更新。如您在代码中所见,调用 updateDatabase() 方法时使用了用两个文件名参数。第二个参数是一个备份文件,其中存有最近的数据库模式。首先调用脚本时,即使存在此文件没有关系。(当首次调用时脚本备份文件不存在可能会更好,这意味着数据库尚不存在。)如果找不到备份文件,管理器假定表不存在,必须创建表。成功创建表后,管理器将实际的模式文件保存到备份位置。其原因很简单:每次程序运行到这一行代码时,就会比较两个模式文件。如果备份文件与新文件不匹配,管理器计算两者之间的不同并执行必要的 ALTER 语句。在部署应用程序新版本时,您的数据库将动态更新。 现在可以检查一下示例所完成的工作。只需使用 SQL*Plus 中的 describe maintainers,就将得到神奇的结果。 结论 现在您应该对 MDB2 的使用有了很好的了解。祝您 PHP 编码之旅充满快乐!
Tobias Schlitt [ tobias@schlitt.info] 从 2002 年以来一直是一名活跃的 PEAR 开发人员。他不但承担 PEAR 程序包的维护工作,还负责 PEAR Web 站点(主要是提案系统 PEPr),他还是 PEAR Core 质量保证小组的成员之一。 |