燕之庐网站建设 - 优质网站设计公司

Posts Tagged ‘php5’

php

2009/11/14

Andi Gutmans看 PHP 5、Oracle 的未来

Tags: ,

PHP 5 的发布经理对 PHP 5 一些新特性的概述及对其未来(对 Oracle 用户而言)的评述。

PHP 5(PHP:超文本预处理语言版本 5) 于 2004 年 7 月 13 日正式发布。毫不奇怪,由于 PHP 在 Web 应用程序市场中的领先地位,因此该版本获得了媒体的广泛报道。.NET 和 J2EE 等技术确实在曝光率和宣传报道方面超过 PHP,但易用性、高性能、与 Apache Web 服务器的紧密集成以及大量应用程序构建块使 PHP 成为领先的 Web 应用程序开发语言之一。

您可能会问自己,既然提供了 Zend Engine 功能的 PHP 4 已经如此成功,为什么还需要 PHP 5 和 Zend Engine II 呢?事实是,PHP 4 在某些方面并不擅长。这些方面中的大多数对于大型项目和公司(项目管理的结构化程度更高,且系统之间必须具备协同工作的能力)而言更为重要。PHP 5 解决了这些问题,使 PHP 不但对此类项目更具吸引力,而且还仍是 Web 应用程序开发的领先技术。

在本文中,我将介绍:

  • PHP 5 的幕后知识
  • 它的某些新特性的简短概述
  • 简要展望 PHP 和 Oracle 用户的未来。

Zend Engine II 新的面向对象的模型

背景随 着 PHP 使用范围的稳步增长,它在较大项目中的使用率也在不断上升。大型项目好像都使用面向对象 (OO) 的方法。并不是说您不能编写小型 OO 应用程序,而且即便不使用面向对象的编程 (OOP) 方法也完全可以编写令人印象深刻的大型应用程序。但人们往往在这些情况下选择 OO 范例 — 可能是因为 OOP 为功能和技术设计提供更多惯用的工具(UML — 统一建模语言)、为重复出现的问题提供重用解决方案(设计模式)以及 OO 语言本身的内置机制(帮助强化软件设计和合同)。

PHP 先前版本中的对象模型存在的主要问题是,将对象实现为具有与整数和字符串相似的副本语意的自带类型。这不但会因为 PHP 有时进行的意外隐式对象克隆而导致的某种非常令人混淆的行为,而且使我们无法实现某些基本功能,如取消对方法返回对象的引用的功能。

以下示例演示了这两个问题。
a) 隐式对象克隆:

<?php

class Person {
var $name;

function Person($name) {
$this->name = $name;
}

function setName($name) {
$this->name = $name;
}

function getName() {
return $this->name;
}
}

function lowerCaseName($obj)
{
$new_name = strtolower($obj->getName());
$obj->setName($new_name);
}

$obj = new Person("Andi");
lowerCaseName($obj);
print $obj->getName();

?>

大多数开发人员认为此示例会打印出“andi”。但令人吃惊的是,此示例在 PHP 4 中却打印出“Andi”。这是因为正如前面提到的,PHP 4 将对象作为常规自带类型处理,所以将$obj按值传递给lowerCaseName()实际是克隆该对象。lowerCaseName()$obj执行的最终操作在该对象的克隆版本上进行。此行为不但导致令人吃惊的结果,而且对于认识到此问题的开发人员而言,它将需要按引用传递和返回对象,由于开发人员必须在许多位置插入“&”(按引用传递、按引用返回和按引用赋值),因此将使代码的维护更加困难。

b) 无法取消对方法返回对象的引用:

$obj->getParentObject()->method();

如果您不熟悉 PHP 4,则可能认为此示例可以正常运行。但由于前面提到的隐式克隆问题,因此不具有取消对方法返回对象的引用的功能,从而无法实现这一功能。作为变通方法,很多 PHP 4 代码将如下所示:

$temp_obj &= $obj->getParentObject();
$temp_obj->method();

还有更多示例可以说明对象的基本结构如何在 PHP 4 变得存在缺陷,但这两个示例足以让您认识到这一点。

主要的新语言特性。PHP 5 中最基本、最重要的变化是使用了对象句柄(或 Id),而不是将其实现为自带数据类型。复制时实际上只复制句柄(Id 编号)本身;并不复制这些句柄所代表的对象。该语言在语义方面看似较小的这个变化却是催生 PHP 5 大多数新特性的主要动力。它允许添加新的语言特性和新的 PHP 扩展,如完全利用新语义的很棒的 SimpleXML。

下面列出了 PHP 5 中的新语言特性,但本文不对其进行详细介绍(否则本文就成一本书了)。

新的对象克隆语义正如所指出的,无论是对象赋值、按值传递对象,还是从函数中按值返回对象,脚本引擎均不会自动克隆 PHP 5 中的对象。如果需要克隆,则开发人员可以通过使用新的clone关键字(例如,clone $obj;)显式克隆对象。开发人员还可以在类中实现一个名为__clone()的 方法,当克隆操作复制了所有原始对象的属性后,将在得到的新对象中调用此方法。实现此回调不是必需的,但如果开发人员希望每个对象自身拥有特定资源的一个 副本(以便为客隆对象创建该资源的一个新版本,否则,这两个对象将使用相同的资源),则实现此回调很有用。此类资源的一个实例就是文件。

公共/私有/受保护的访问修饰符。PHP 5 支持其他面向对象的语言(如 C++ 和 Java)中通常都具有的 PPP(公共/私有/受保护的)访问修饰符。可以将这些访问修饰符用于属性和方法,以施加访问限制。

接口、抽象类和方法。我 们在 Zend Technologies 收到许多要求在 PHP 5 中提供多重继承 (MI) 的请求,因此我们决定在 PHP 5 中解决此问题。在比较了许多语言(主要是 C++ 和 Java)的实现,了解了哪种语言最容易适应 PHP 的动态本质后,我们决定使用 Java 样式的接口和抽象类为 MI 提供一个解决方案。

允许 PHP 扩展重载 PHP 对象语法。PHP 5 最重要的特性之一可能就是 Zend Engine II 在对象语法和其语义之间存在一个抽象层。此方法允许 PHP 扩展创建它们自己的对象,这些对象的行为与用户级 PHP 对象的行为不同。例如,新的 COM 扩展使用这些重载功能,以便使用常规 PHP 对象语法以一种对 PHP 开发人员而言比较自然的方式访问 COM 对象:

$ie = new COM("InternetExplorer.Application");
$ie->Visible = true;
$ie->Navigate("http://www.php.net/");

其他利用此功能的扩展包括 SimpleXML、SOAP 和 Perl 扩展。

其他新特性PHP 5 中大概有 12 个以上的新语言特性,如类常量、静态属性和方法、__autoload()以及instanceof运算符。您可以在http://www.zend.com/php5中找到一个更完整的列表。

设计模式正如前面所讨论的,能够在大型(通常也可以是小型)PHP 软件项目中使用设计模式非常重要。虽然可以在 PHP 4 中利用此类模式,但由于缺少重要的语言特性(如静态属性和方法、PPP 访问修饰符和接口,因此通常很难推行这些模式的所有语义。

单实例模式经常使用并且非常出色的是单实例模式。尽管它是一个比较简单的模式,但要完整地实现它,必须使用静态属性和方法以及 PPP 访问修饰符。

例如:

<?php

class MySingleton {
static private $instance = NULL;

private function __construct() {
}

private function __clone() {
}

static public function Instance() {
if (self::$instance == NULL) {
self::$instance = new MySingleton();
}
return self::$instance;
}
// ... Additional code for the MySingleton class.
}

此实现利用了 PHP 5 新特性,从而获得了一个比较整洁并且不易出错的单实例实现。例如,将构造函数和客隆方法声明为private的功能可以防止开发人员不小心多实例化一个MySingleton类的副本,这是因为只有该类自己才可以访问这些方法。使用静态属性支持是为了实现一个可以全局访问的属性 (self::$instance),该属性引用类的单一实例。将属性声明为private可以确保只有该类自己才可以使用该属性。

不可变对象模式另一个不太常用的设计模式是不可变对象模式。此模式通常用于对相对较少的值进行大量引用的应用程序。它允许通过使对象不可变(禁止其状态变化)并强制实现此目的代码创建类的一个新实例来使应用程序代码共享对象。

以下示例演示了如何创建一个表示 SQL 查询的类。该组合的查询语句本身可能用在应用程序的多个位置。要更改该查询的值的代码可以使用changeStmt()方法实现此目的,该方法返回代表指定查询字符串的新对象的句柄。

<?php

final class ImmutableQueryStatement {
private $stmt;

public function __construct($stmt) {
$this->stmt = $stmt;
}

public function getStmt() {
return $this->stmt;
}

public function changeStmt($stmt) {
return new ImmutableQueryStatement($stmt);
}
}

此示例利用了几个 PHP 5 新语言特性。首先,它使用final关键字,以使该类不会再有“子类”。该方法使开发人员不能再继承和实现可变类。此外,changeStmt()利用新对象句柄并按值返回新建的对象(在 PHP 4 中,必须按引用返回新对象实例,从而使实现复杂化)。最后(但不是最次要的),与上一个示例相似,访问修饰符用于指定该类应遵守的访问合同。

PHP 5 中的 XML 和 Web 服务

背景在过去的几年里,XML 变得越来越重要,它允许不同的应用程序和系统使用标准工具和方法协同处理数据。这是 Oracle 及其他供应商坚定不移地支持和采用该技术的原因之一。在每个公司中,数据都是组织的核心部分。

PHP 4 中的 XML 支持非常混乱。尽管它支持 SAX(用于 XML 的简单 API)、DOM(文档对象模型)和 XSL(可扩展样式表语言),但却没有统一、符合标准的实现。SAX 实现基于逐渐过时的 Expat XML 分析器,DOM 扩展的命名惯例不符合标准,并且该扩展仍处于试验状态。此外,对 XSL 的支持还要使用称为 Sablotron 的 XML 库。

因此决定在 PHP 5 中重新编写 XML 支持。来自 PHP 社区的几位开发人员将此工作变为了现实。第一个也是最重要的决定是所有 XML 功能将基于Gnome 项目优秀的 libxml2 库。依照此原则,重新编写了现有的三个扩展。最重要的是,修改了 DOM 扩展并将其接口重新设计为符合 W3C。当 PHP 5 发布时,DOM 已脱离了试验阶段,变得功能完备且稳定。

SimpleXML除 了对现有的 XML 扩展进行重要的重新编写和统一以外,还出现了一个新的 XML 扩展。此扩展名为 SimpleXML,它允许开发人员能像访问自带 PHP 对象那样访问 XML 文件。回顾本文的前几个部分,这之所以成为可能是因为新的 Zend Engine II 使扩展能够重载面向对象的语法。

考虑以下 XML 文件:

<clients>
<client>
<name>John Doe</name>
<account_number>87234838</account_number>
</client>
<client>
<name>Janet Smith</name>
<account_number>72384329</account_number>
</client>
</clients>

以下 PHP 5 代码迭代 XML 文件,打印客户的名称和帐号:

<?php

$clients = simplexml_load_file('clients.xml');

foreach($clients->client as $client) {
print "$client->name account:$client->account_number\n";
}

运行此示例脚本将获得以下输出:

John Doe account: 87234838
Janet Smith account: 72384329

有 了 SimpleXML,访问 XML 文件变得非常容易。我相信,SimpleXML 将使 PHP 开发人员在处理 XML 文件时获得革命性的易用性。如果 SimpleXML 无法执行某些操作,则由于 SimpleXML 和 DOM 扩展都使用同一基础库,因此可以将 SimpleXML 对象转换为 DOM 树,在 DOM 中可以执行更高级的 XML 操作。SimpleXML 与 DOM 之间的这种相互转换是零复制的,也就是它既不花费时间也不占用额外的内存。

SOAP回顾前面的介绍,我曾将互操作性看作是大公司的主要问题。使用 Web 服务(更具体地说是 SOAP 协议)解决两个或更多系统之间的互操作性问题已变得越来越普遍。

由于 PHP 4 的默认发行套件未提供自带集成的 SOAP 支持,因此我们认为在 PHP 5 中必须解决此问题。因此,我们为 SOAP(客户端以及服务器 API)创建了一个新的自带实现,它允许 PHP 开发人员轻松创建和使用 Web 服务。

以下示例演示了从 PHP 中调用 SOAP 服务是何等简单。您可能注意到,此扩展使用了与前面相同的面向对象的重载功能。

<?php

$client =
new SoapClient("http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl");

print($client->getQuote("ORCL"));

编写本文档时,运行此示例将打印11.23

未来和 Oracle

总论2004 年 7 月发布的 PHP 5 诞生时间很短。尽管如此,在 PHP 发展过程中却发生了很多令人关注的事情。在改进脚本引擎的性能方面已经做了大量的工作,而对 Oracle 读者而言,最重要的是有很多与数据库相关的新革新措施。在 PHP 社区中,Oracle 的使用范围很广,很多 Zend 客户都是 Oracle 用户。他们使用 Oracle 的方式虽然与其他数据库有所不同,但通常都是一个非常明智的选择,这与 Oracle 的公认跟踪记录、高级特性,以及(往往)Oracle 基础架构中的现有投资相关。

脚本引擎性能开发 PHP 5 时,Zend 和社区更专注于功能而不是性能。因此,除了几个个别情况以外,PHP 4 与 PHP 5 的脚本引擎性能差别并不大。

在大多数 PHP 应用程序中,PHP 的原始执行性能不是主要瓶颈。最常见的瓶颈与 I/O 相关,并通常与数据库相关。尽管如此,我们仍相信改进脚本引擎本身的性能将肯定为 PHP 用户带来好处。因此,我们决定投入大量资源来改进 PHP 5.1.x 的性能。

自 从 PHP 5 发布以来,我们投入了大量资源来调整脚本引擎。许多构思都出自由 Thies Arntzen 和 Sterling Hughes 在大约一年前发布的性能补丁。其他构思出自 Zend 和 PHP 开发人员社区的内部。最终我们设计出一种引擎,它在综合基准测试(不包括 I/O 和实际代码的基准测试)中的速度通常为 PHP 4.0 和 PHP 5.0 的两倍以上。

改进非常引人注目,并且当 PHP 5.1.0 发布时,所有 PHP 用户都可以使用它,而不必对他们的源代码进行任何更改。我认为,PHP 5.1.0 将在 2005 年第一个季度之初发布,但对于开放源项目来说,谁也不敢保证。

SQL Relay。SQL Relay(是一个非常有趣的第三方项目。它是一个为 SQL 连接(包括 Oracle)实现代理中介的项目,允许使用 PHP 实现数据库连接池。

该项目提供了它自己的 PHP 数据库扩展(您必须更改您的 PHP 数据库代码)。此扩展与 SQL Relay 中介通信,后者在扩展与数据库之间传递查询和结果。

SQL Relay 的某些优点:

  • 使用连接池,您可以限制打开的数据库连接数。
  • 在 PHP 持久连接无法用在您环境的情形,此解决方案可以解决初始化 Oracle 数据库连接时连接时间较长这一问题。
  • 该项目还支持其他编程语言。如果您使用的是一个混合环境,则可以利用从 PHP 中使用的同一个 SQL Relay 后台程序。除 PHP 以外,还支持 C、Java、Perl 以及许多其他语言。
  • 启动和运行 SQL Relay 非常简单。我还要指出,SQL Relay 作者对我的问题的反应非常积极。

SQL Relay 的某些缺点:

  • 必须使用一个不同于 PHP Oracle 扩展的 API。
  • 结果集被复制两次:首先复制到 SQL Relay 中介,然后复制到 PHP。
  • 没有哪个 API 像 PHP 自带的 oci8 扩展一样功能丰富。

我认为,如果您的项目确实需要 Oracle 数据库连接池,则您最好试一试 SQL Relay。它可能并不尽善尽美,但在一个更好的解决方案推出之前可能需要一段时间,且此解决方案确实有效。

PDO除了现有的 oci8 PHP 扩展(具有一个自带的 Oracle 数据库接口)以外,PHP 社区还一直致力于开发一个新的数据库抽象层。由于 Oracle 技术网已经提供了一篇深入介绍PHP 数据对象 (PDO)的 文章(作者 Wez Furlong),因此我们完全可以认为 PDO 值得期待。PHP 在很长时间以来一直期待一个好的自带数据库抽象。我认为,PDO 就是我们一直期待的解决方案。PDO 的设计者是 PHP 社区的一些高水平的开发人员,我很喜欢他们开发 PDO 的方法。以下是他们的设计目标清单(已写入 PDO README 文件中:

  1. 是轻型的。
  2. 为常见的数据库操作提供通用 API。
  3. 性能很高。
  4. 将大多数 PHP 特定代码置于 PDO 内核(如持续性资源管理)中;驱动程序只应关注如何获取数据而不是 PHP 的内部结构。

一方面,它为使用数据库提供通用 API。但它还允许每个驱动程序添加自己的附加功能,因此 PDO 不但支持最常见的数据库 API,而且还使您能够使用数据库提供的所有特性。我们都知道 Oracle 提供了很多特性。

PropelPropel是 一个对象持久性和查询框架。它采用对象/关系映射 (ORM) 模型并基于 Apache Torque 项目,该项目对于 Java 执行相同操作。与 PDO 不同,Propel 是一个非常高级的数据库抽象层,它重新定义了您查询、创建和操作持久性对象的方式。正如对 OO/RDBMS 映射系统所期望的,Propel 还处理数据库模式创建。

此类系统有很多优点。对于新手,开发人员可以集中大部分时间编写业务逻辑,并且不必处理数据库的复杂性 — 无论是模式管理还是编写精致的 SQL 语句。数据库操作非常自然,这是因为开发人员只使用常规对象,而持久层处理更新数据库中相应字段和行的低级细节。

缺 点是您失去了某些控制。OO 模型到关系数据库的自动映射并非总是很顺利。它不但使得手动编写精致、强大的查询很困难,而且也不允许您这样做 — 您破坏了抽象,并且微小的映射更新就可能破坏应用程序。因此,使用这样的系统意味着您必须遵守工具的规则。在大多数情况下,这样的代价是可以接受的,这是 因为提高的生产率有助于缩短开发时间并提高代码质量。但在某些情况下,您可能绝对需要这样的控制。

Propel 是一个非常有趣的项目,一定会派上用场。此外,它是基于名为Creole的数据库抽象层构建的。与 PDO 不同,此抽象层尝试尽可能地模仿 JDBC,并且在您将现有 Java 代码转换为 PHP 的情况下更易于使用。也就是说,如果 PDO 成为主流并作为标准 PHP 的一部分发行,那么最好遵守它。

Java 集成一年前,Zend 和 Sun Microsystems 启动了Java 规范请求 (JSR) 223, 用以定义 PHP 和 Java 的接口标准。如今,JSR 的专家组由许多软件供应商(包括 Oracle)组成。尽管 JSR 提到了所有脚本语言,但最初的兴趣却在于 PHP 以及主要是看能否从 PHP 中调用 Java 代码。您可以猜测此类连接的主要动机之一是将前端 PHP 服务器连接到后端 J2EE 应用服务器,更具体地说,是能够从 PHP 代码直接调用 Enterprise Java Bean (EJB)。

以下是使用 Java 接口实现的 Oracle JDBC 查询示例:

图 1:连接 PHP 和 Java:Oracle JDBC 查询

您可以看到您将可以在 PHP 中编写 Java 代码。这会使您能够调用您可能拥有的任何 Java 业务逻辑(特别是 EJB)。

此连接支持为已经投资了后端业务逻辑但希望利用 PHP 的快速开发时间和特性的 Oracle 应用服务器用户提供了新的可能性。

结论

PHP 5 无疑是 PHP 和 PHP 社区的重大进步。在 O’Reilly 开放源代码会议上,一位记者询问某些 PHP 社区领导者:PHP 5 是否是我们翘首期望的一切?回答是一致的;PHP 5 远远超过了我们最初的计划和期望。

更具体地说,我认为 Oracle 用户有很多值得期望的事情。就 Oracle发布的有 关在未来的 Oracle 应用服务器版本中包含 PHP 的发展方向声明而言,显而易见,公司已经认识到了 PHP 技术的重要性。我相信,在认识到 PHP 技术的重要性之后将出现各种提高 Oracle/PHP 生产率和灵活性(两者在如今不断变化的市场上的需求程度很高)的解决方案。即将推出的 Oracle 10g版本中的初始 PHP 绑定以及用于 Oracle JDeveloper 的 PHP 扩展是 Oracle 支持广泛流行的 PHP 的重要先期步骤。


Andi Gutmans是 Zend Technologies 的创办人和副总裁。1997 年之后,他一直致力于开发 PHP,并与 Zeev Suraski 携手,先后创建了 PHP 3 和 PHP 4。最近,Andi 为即将推出的 PHP 5 版本领导了 Zend Engine II 的面向对象的改进,并参与编写了PHP 5 强大编程(Prentice Hall) 一书。欢迎您将有关 PHP 5 的建议发送到andi@zend.com

php

PHP 5 数据对象 (PDO) 抽象层与 Oracle

Tags: , ,

一名新 PHP 数据对象 (PDO) 数据抽象层的原始开发人员为您简要介绍该抽象层,重点讲述与 Oracle 一起运行的情况。

需要 PHP:5.0
需要其他:Oracle 8 或更高版本客户端库
下载用于 Oracle 的 PDO (Windows):php_pdo.dll,php_pdo_oci.dll
下载用于 Oracle 的 PDO (Unix):pdo,pdo_oci
下载示例代码

PDO 简介

PHP 主要是由志愿者完成的项目;尽管有少数一些固定的“核心”开发人员,但是我们没有一个人在全职受薪的开发 PHP。除此之外,我们分别位于世界不同地方,您可以想象长期开发的协调工作是何等困难。因此,PHP 主要是基于突发奇想的个人短期需求来发展的,其原因也多种多样,有的是试验,有的则是因为“明天有活要交”。尽管这样通常每一步都会改善 PHP,但从长远来看则是缺乏完整性 - 数据库扩展就是一个重要的例子。

在各种不同的数据扩展(oci、mysql、 postgresql、mssql 等)之间根本没有真正的一致性,甚至在某些情况下,在这些扩展内部也没有真正的一致性。几乎所有这些扩展都在使用与基础数据库 API 紧密相连的不同代码完成着相同种类的任务。而且因为我们(PHP 核心开发人员和扩展开发人员)的人手非常有限,因此这就造成了代码更加难以维护,从而为 PHP 带来了很大的问题。

由于 PHP 越来越受欢迎并不断成功,因此主要 PHP 数据库扩展的维护者们参加了在德国举行的 LinuxTag 2003 大会,在会上我们交换了对 PHP 前景的看法。在讨论 PHP 发展的随机性时,我们确定了在 PHP 中进行数据库访问的一些目标:

  • 提供一种轻型、清晰、方便的 API
  • 统一各种不同 RDBMS 库的共有特性,但不排除更高级的特性。
  • 通过 PHP 脚本提供可选的较大程度的抽象/兼容性。

我们之所以提出了这种 PHP 数据对象 (PDO) 的概念,是因为我们希望通过采用 Zend Engine 2(PHP 5 的核心)先进的面向对象特性获得该 API 的一些更优秀的性能。

PHP 中的数据抽象层概念一点都算不上新;在 Google 中查询“PHP database abstraction”会找到大约 83,200 个匹配项。它几乎是许多 PHP 开发人员梦寐以求的,而其产生则部分归因于我们不完整的 API。如果您曾经尝试过使用第三方抽象层来完成任何真正重要的工作,通常会发现这些抽象层对于手头的工作来说设计的功能过于强大了 - 或者表现为在使用前需要进行大量学习,或者表现为接口速度缓慢,参数需要经过多层脚本函数调用才能到达数据库自有的 API;通常是存在上述两种表象。

为什么这些抽象层会存在这种问题?这些抽象层总是在试图完成太多的任务,甚至可能是不可能的任务。我们决定以实用为目标,仅将一些最常见的数据库 API 特性作为我们的基础,并使得 PDO 驱动程序能够将它们特定于产品的特性暴露为常规扩展函数。

为什么使用 PDO?

听 过有关数据库抽象扩展谣传的大多数人会立刻对 PDO 的扩展方面产生疑惑 - 我们是否要分析 SQL,将其转换为相应的后端方言呢?我们如何处理特性 X 或特性 Y,等等。因此,当您听说我们在 PDO 中根本不用为此而担忧时可能会大吃一惊;我们不希望使所有内容都完全统一,因为要使得这种统一成为可能,只能是将自己限制在最低的通用标准。

如果 PDO 不是一个整体的抽象层,那还有什么别的原因值得您考虑使用它吗?

  • 性能。PDO 从一开始就吸取了现有数据库扩展成功和失败的经验教训。因为 PDO 的代码是全新的,所以我们有机会重新开始设计性能,以利用 PHP 5 的最新特性。
  • 能力。PDO 旨在将常见的数据库功能作为基础提供,同时提供对于 RDBMS 独特功能的方便访问。
  • 简单。PDO 旨在使您能够轻松使用数据库。API 不会强行介入您的代码,同时会清楚地表明每个函数调用的过程。
  • 运 行时可扩展。PDO 扩展是模块化的,使您能够在运行时为您的数据库后端加载驱动程序,而不必重新编译或重新安装整个 PHP 程序。例如,PDO_OCI 扩展会替代 PDO 扩展实现 Oracle 数据库 API。还有一些用于 MySQL、PostgreSQL、ODBC 和 Firebird 的驱动程序,更多的驱动程序尚在开发。

您可能想了解 PDO 与其他常用的抽象层的对比情况,例如PEAR DBADODB。无论在 API 方面还是在性能方面,PDO 都比其他常见抽象层要轻型,但是涉及到在各个数据库后端之间提供统一性方面,则不如那些抽象层,例如用于处理大量可移植性问题的PEAR MDB 2抽象层。

在哪里可以获得 PDO?

PDO 是通过 PECL(发音为“pee-kle”,欧洲语言风格),即 PHP 扩展库提供的。如果您在运行 Linux 计算机,请按照下面的说明进行设置;稍后是在 Windows 上安装的详细信息。

请注意,PDO 及其驱动程序当前处于“alpha”状态;这就意味着我们会合理保证没有重大缺陷,但是该程序包功能并不完善 - 我们还要添加很多功能。虽然我们鼓励您测试该程序包,但是实在不推荐在现阶段将其用于生产。

Unix/Linux 安装

如果您以前尚未尝试过 PHP 5,则请花一点时间来通读一下“新闻”和各种声明。在 UNIX 计算机上,您可能要安装或升级 libxml2;如果没有 libxml2,“pear”程序包管理工具就无法运行,您安装 PDO 时就会遇到很多困难。获取PHP 5,并将其编译和安装。确保指定的前缀不是 /usr/local/,这样它就不会与 PHP 4 安装发生冲突了:

% ./configure –prefix=/usr/local/php5 –with-zlib [此处指定其他选项]
% make install

现 在您就可以使用“pear”工具获取并安装 PDO 以及用于 PDO 的 Oracle 驱动程序了。因为 PDO 当前标记为 alpha,所以默认情况下 pear 工具不会下载该程序包。在该程序包名称后面添加后缀“-alpha”,通知该 pear 工具可以安装 alpha 版本:

% PATH=”/usr/local/php5/bin:$PATH”
% pear install PDO-alpha

您需要告知 PHP 从专用于 PHP 5 的 php.ini 文件加载 PDO 驱动程序。如果您使用的前缀与我使用的一样,PHP 则会在 /usr/local/php5/lib/php.ini 中查找 php.ini 文件。向该文件中添加以下行:

extension=pdo.so

现在您需要获取数据库特定的驱动程序;对于 Oracle,此特定程序称为 PDO_OCI。在 shell 中,键入:

% pear install PDO_OCI-alpha

此驱动程序也需要从 php.ini 文件加载;将下行添加到前面添加的那行之后

extension=pdo_oci.so

现在检查一下,确保它能够运行:

% php -m

在模块列表中,您应该会看到 PDO 和 PDO_OCI。

防火墙碍事了?

如果您位于防火墙的后面,则在使用 pear 安装程序获取程序包时可能会遇到一些问题。如果发生这种情况,则可以按照下列说明手动下载并安装这些程序包:

% wget http://pecl.php.net/get/PDO
% pear install PDO-0.1.1.tgz
[ 将 extension=pdo.so 添加到 php.ini ]

% wget http://pecl.php.net/get/PDO_OCI
% pear install PDO_OCI-0.1.tgz
[ 将 extension=pdo_oci.so 添加到 php.ini ]

在上述两种情况下,都需要首先调用“pear install”(后跟下载的真正程序包);上述示例中的版本号在本文编写之时是最新的,但随着开发的继续进行会发生变化。

Windows 安装

如果您正在运行 Windows,则请按照下列说明执行:

  1. http://www.php.net/downloads.php#v5获取 PHP 5,将其解压缩到 C:\php5。
  2. http://snaps.php.net/win32/PECL_5_0/php_pdo.dllhttp://snaps.php.net/win32/PECL_5_0/php_pdo_oci.dll分 别获取 PDO 和 PDO_OCI,将其放入 C:\php5\ext。或者,您可以从 PHP 5 下载页上列出的“用于 PHP 5.0.0 的 PECL 模块集合”zip 文件中找到所有这些 PDO 驱动程序,以及所有 PECL 程序包的所有 Windows 版本。
  3. 编辑 C:\php5\php.ini 文件,并添加下列内容:extension=php_pdo.dll
    extension=php_pdo_oci.dll

编辑 php.ini 文件时,有一点很重要,即要在任何其他 PDO 驱动程序之前先加载 PDO 扩展,否则就不能正确初始化(在这种情况下会出错)。

如 果在 Windows 目录中有一个 PHP 4 的全局 php.ini 文件,则可能会遇到问题。最好的解决方法是,移动该 php.ini 文件,使其与 PHP 4 SAPI 位于相同的文件夹中,以隔离 PHP 4 安装;例如,将其移动到与 php4apache.dll 相同的文件夹中。请注意,PHP 5 程序中并非所有文档都是最新的;推荐的安装过程如上面所述 - 如 install.txt 文件所声明的,请勿将任何 DLL 复制到 windows 文件夹或 system 文件夹中 - 任何内容都是自包含的。如果您运行的是 apache,并且遇到无法加载 DLL 的错误,则检查一下是否将 C:\php5 添加到了 PATH 中。另外,还要注意 PHP 5 的 CGI 版本现在的名称为 php-cgi.exe。

连接 PDO

首 先创建 PDO 类的一个实例,将其用作数据库句柄。使用哪个基础驱动程序并不重要;您总要使用 PDO 类名。构造函数的第一个参数为数据源名称 (DSN),第二个参数为用户名,第三个参数为该用户名的口令。DSN 的 PDO 命名惯例为 PDO 驱动程序的名称,后面一个冒号,再后面是可选的驱动程序特定的信息。在我们的示例中,会加载 OCI 驱动程序但不指定任何其他信息;这样会使用默认的数据库。对于其他驱动程序,如 ODBC 驱动程序,第一个冒号后面的所有内容都将被用作 ODBC DSN。MySQL 驱动程序会同样以不同的方式解释它的 DSN。

如果无法加载该驱动程序,或者发生了连接失败,则会抛出一个 PDOException,以便您可以决定如何最好地处理该故障。

<?php
try {
$dbh = new PDO(”OCI:”, “scott”, “tiger”);
} catch (PDOException $e) {
echo “Failed to obtain database handle ” .$e->getMessage();
}
?>

在连接字符串中,您可以指定两个可选参数;第一个是数据库名称,第二个是字符集;这些参数与可选的第三个和第四个参数相对应,后两个参数您可能在 oci8 扩展函数ociconnect()ociplogon()中使用过。要使用特定的字符集连接一个特定的数据库,则可以执行下列操作:

<?php
try {
$dbh = new PDO(”OCI:dbname=accounts;charset=UTF-8″, “scott”, “tiger”);
} catch (PDOException $e) {
echo “Failed to obtain database handle ” .$e->getMessage();
}
?>

省略 try..catch 控制结构并无裨益。如果在应用程序的较高级别没有定义异常处理,则在无法建立数据库连接的情况下,该脚本会终止。

连接管理

目前,PDO 完全没有执行自己的任何连接管理,因此每个“新 PDO”调用都会建立一个新的数据库连接。该连接在 $dbh 变量越界时,或者当您为其指定 NULL 值时会被释放。

<?php
try {
$dbh = new PDO(”OCI:dbname=accounts;charset=UTF-8″, “scott”, “tiger”);
} catch (PDOException $e) {
echo “Failed to obtain database handle ” .$e->getMessage();
exit;
}
// 在此处对数据库执行一些操作
// …

// 现在完成,释放该连接
$dbh = null;
?>

计划在不久的将来为 PDO 增加连接缓存功能;就当前的 oci8 扩展而言,会重用与现有服务器的连接,并且在这些连接中,还会重用闲置的登录。当在缓存连接模式中运行时,如上面的代码段所示释放 $dbh 时会将该登录标记为可由其他连接重用。

如果您使用 ODBC 驱动程序访问 Oracle,则可能会很高兴地注意到,默认情况下 PDO_ODBC 驱动程序支持 ODBC 连接池。

使用 PDO

了解一个编程 API 的最好方式就是使用它,因此我们来看一下附带的这个演示,以了解如何进行批次更新(请参见示例代码)。

既 然我们已经成功连接到了 Oracle,那么现在就可以创建一个表来保存一些数据了。对于此示例,我们使用一些 PHP 扩展及其作者,并将这些内容输入一个数据库中。数据库句柄对象的 exec() 方法可用来发出不会返回结果集的快速一次性查询,因此我们在这里使用该方法来发出 CREATE TABLE 查询。

为了使得示例更自然,我从 PHP 源代码中抽取了扩展及其作者的信息,并将其存储到了一个 CSV 文件中(请参见“credits.csv”)。这就代表一个常见情形:从 CSV 文件批次导入数据。在我们的示例中,我们充分利用了 Oracle 的预处理语句和绑定参数,以获得一个高效的数据导入脚本。在讲述该示例之前,有必要了解一下 PDO 处理事务的方式。

PDO 中的事务处理

Oracle 具有一个敏感的默认操作模式:当您进行连接时,将会位于一个隐式事务处理中,在提交事务之前其中的更改不会完全生效。除了事务处理的标准优点(原子性、一 致性、隔离性、可持久性 - ACID)之外,数据库服务器在执行每次更新之后还不需要重新构建索引和其他内部结构;它可以延迟到提交之后进行。这样会加速代码的执行。Oracle 这点确实很好。

但不幸的是,并非每个数据库供应商都支持事务处理,并且因为 PDO 旨在以一种相对可移植的方式支持这些事务处理,所以它默认情况下以自动提交模式运行。启用自动提交模式后,数据库驱动程序会隐式提交每个成功的更新。当您调用$dbh->beginTransaction()时,就会请求关闭自动提交,直到调用$dbh->commit()或者$dbh->rollBack()才会重新启用,具体取决于您的代码是怎样编写的。如果基础驱动程序不支持事务处理,则会抛出一个 PDOException。

如果发生了问题并且 PHP 出错,您的脚本将退出并且事务处于待批状态;或者您关闭数据库句柄时,PDO 会自动针对任何待批的事务调用$dbh->rollBack()。此行为会减少向数据库中提交可能未定义或者已损坏数据的可能性,这是用于处理已放弃事务的标准语义。

预处理语句、存储过程

PDO 支持使用 Oracle 样式命名的占位符语法将变量帮定到 SQL 中的预处理语句(与 oci8 扩展中的ocibindbyname()类 似)。PDO 还为其他数据库(如 ODBC)提供了命名占位符模拟,甚至可以为生来就不支持该概念的数据库(如 MySQL)模拟预处理语句和绑定参数。这是 PHP 向前迈进的积极一步,因为这样可以使开发人员能够用 PHP 编写“企业级”的数据库应用程序,而不必特别关注数据库平台的能力。

使用 PDO 预处理语句非常简单,调用数据库句柄的prepare()方法即可。它会返回一个语句句柄对象,然后您可以使用该对象来绑定参数和执行语句。在此示例中,我们将要定义两个命名占位符,“:extension”和“:name”,这两个占位符分别与 .CSV 文件中的 PHP 扩展名称和其中一个作者的姓名相对应。

$stmt = $dbh->prepare(”INSERT INTO CREDITS (extension, name) VALUES (:extension, :name)”);

预处理了语句之后,我们使用bindParam()方法来将这些命名参数分别与 PHP 变量名称“$extension”和“$name”相关联(这与ocibindbyname()类似)。我们还会通知 Oracle,这些数据将要格式化为字符串,最大长度为 64 个字符。

$stmt->bindParam(’:extension’, $extension, PDO_PARAM_STR, 64);
$stmt->bindParam(’:name’, $name, PDO_PARAM_STR, 64);

我们现在即准备好插入数据了 - 我们只需要打开该 CSV 文件,并从中获取数据即可。通过使用fopen()fgetcsv()函数可以相当简单地完成此操作。然后,我们可以使用 PHP list() 构造函数直接将 CSV 的列指定给变量“$extension”和“$name”。因为这些变量已经绑定到了语句中,所以我们现在要做的只是调用该语句对象的execute()方法使其执行插入。这种方式既方便又快捷 - 在事务处理时每个迭代循环只有两行。到达文件尾时,我们就可以立即使用数据库句柄的commit()方法来提交这些更改了。

如果您只是要传递输入参数,并且有许多这样的参数要传递,那么您会觉得下面所示的快捷方式语法非常有帮助;此语法使您能够省去对$stmt->bindParam()的调用。

$stmt = $dbh->prepare(”INSERT INTO CREDITS (extension, name) VALUES (:extension, :name)”);
$stmt->execute(array(’:extension’ => $extension, ‘:name’ => $name));

您还可以使用bindParam来为存储过程设置输入/输出参数;语法是完全相同的,只是查询有所不同。下面的代码演示如何调用一个名为“sp_add_item”的存储过程;其目的是要针对输入设置$item_name,然后该存储过程将在返回时更新$error_code

$stmt = $dbh->prepare(”begin sp_add_item(:item_name, :error_code); end”);
$stmt->bindParam(’:item_name’, $item_name, PDO_PARAM_STR, 12);
$stmt->bindParam(’:error_code’, $error_code, PDO_PARAM_STR, 12);
$stmt->execute();

抓取数据

使用 PDO 抓取数据与进行插入或更新相似,只是您执行完查询之后,将要重复调用fetch()方法来获取结果集的下一行。进行获取的最简单情况如下所示,值得注意的一点是,您还可以将参数绑定到查询,以控制如 WHERE 子句这样的内容;执行此操作的语法与我们已经看到的bindParam()代码完全相同。

$stmt = $dbh->prepare(”SELECT extension, name from CREDITS”);
if ($stmt->execute()) {
while ($row = stmt->fetch()) {
print_r($row);
}
}

PDO 支持一些不同的抓取策略,这些策略在方便性和性能方面有所差别;通过将下列选项之一指定为fetch()方法的参数,您可以更改其返回值以适应您的语法:

  • PDO_FETCH_NUM- 每个行抓取返回一个按照列位置索引的数组,并且以 0 为基数(第一列是第 0 个元素)。while ($row = $stmt->fetch(PDO_FETCH_NUM)) {
    printf(”Extension %s, by %s<br>”, $row[0], $row[1]);
    }
  • PDO_FETCH_ASSOC- 每个行抓取根据行集中的列名,返回一个按列名索引的数组。while ($row = $stmt->fetch(PDO_FETCH_ASSOC)) {
    echo “Extension $row[EXTENSION] by $row[NAME]<br>”;
    }
  • PDO_FETCH_BOTH- 每个行抓取返回一个既按照列位置又按照列名索引的数组。也就是上述两种情况的直接组合。如果没有指定抓取模式,则该模式为默认模式。
  • PDO_FETCH_OBJ- 每个行抓取返回一个匿名对象,其属性名与列名对应。while ($row = $stmt->fetch(PDO_FETCH_ASSOC)) {
    echo “Extension {$row->EXTENSION} by {$row->NAME}<br>”;
    }
  • PDO_FETCH_LAZY- 每个行抓取返回一个引用语句对象的重载对象。这“看起来”好像是PDO_FETCH_OBJPDO_FETCH_BOTH的组合,只是只有当您在脚本中访问 PHP 变量时才创建这些变量。
  • PDO_FETCH_BOUND- 抓取每行,返回 TRUE。在使用绑定输出列时这种方式非常有用,它可以避免创建不需要的任何数组或对象。(请参见下面的示例)。

无论您使用哪种抓取策略,当没有其他行可抓取时,fetch()方法将会返回 FALSE。

现 在我要讲述一些技巧,如果您需要最后再调整一下脚本性能的话,这些技巧可能会对您有所帮助。但先给你一个忠告:要像躲避瘟疫一样避免不成熟的优化。您应该 总是首选最清晰、可维护性最好的解决方案。请记住,在一个典型的 Web 应用程序中,您不能衡量各种抓取模式间的区别,除非脚本要处理很多行。我再重复一遍:抓取模式间的性能区别非常小 - 请使用最适合您代码的模式。

请记住,使用PDO_FETCH_NUM的花销最小,因为访问列数据只是一个简单的数值查询。PDO_FETCH_OBJ使您能够使用 OO 语法将数据集的列作为对象的属性来访问,但是每个属性访问都涉及一个附加的散列查询,使得使用它的花销基本上与PDO_FETCH_ASSOC相同。每个这样的模式都会复制整行,从而占用稍多的内存。

很多数据库驱动程序都会代表您预先抓取并缓存一定数量的行。PHP 每次访问其中一个这样行中的列时,它都需要将其复制到自己的专用内存区域中。如果您的查询涉及很多行,而只需要基于某种复杂的逻辑访问给定行的特定列,则您会发现PDO_FETCH_LAZY是一种避免使用很多内存的有用方法,因为它只有在您访问给定列时才复制该列。使用此方式时要注意,从某个给定语句为每个fetch()抓取的“惰性对象”是每次迭代时使用的同一对象(以减少每次创建/销毁它的开销)。这就暗示着您不能只是简单地存储该对象用于以后的比较,因为它仍然会引用该语句的当前行 - 您需要手动复制所需要的部分。

最后一种模式为PDO_FETCH_BOUND, 该模式会告知 PDO 您已经将所有列绑定到了 PHP 变量,并且除了要它在到达行集的末尾时通知您外不需要它执行别的任何操作。绑定输出列在概念上与绑定输入参数相似,只是绑定输出列可以用于所有数据库驱动 程序。您可以将 PHP 变量绑定到命名列,PDO 将在每次调用execute()时对其进行更新。此技术可用来剃去结果集中每列、每行的一些虚拟机器操作码(这种代码速度比原生码要慢)。这种技术的缺点在于,可能会使您的代码难以跟踪(也称为 WTF 系数较高),您使用变量名称时需要倍加小心。下面的代码说明了绑定输出列的使用。请注意,您不必指定PDO_FETCH_BOUND即可使用$stmt->bindColumn();PDO_FETCH_BOUND只是一个对于您了解只能使用绑定值的情况的一种优化。

$stmt = $dbh->prepare(”SELECT extension, name from CREDITS”);
if ($stmt->execute()) {
$stmt->bindColumn(’EXTENSION’, $extension);
$stmt->bindColumn(’NAME’, $name);
while ($stmt->fetch(PDO_FETCH_BOUND)) {
echo “Extension:$extension, Author:$name\n”;
}
}

可移植性

区分大小写的列

PDO 旨在令使用可移植 SQL 的脚本运行良好、可移植。本文中提及的所有查询(调用存储过程除外)在使用任何 PDO 驱动程序时其运行性能应该相同 - 包括所有绑定输入变量和绑定输出列。

但有一个转换问题 - 当您使用PDO_FETCH_ASSOC抓 取数据时,不同的驱动程序会以不同的方式返回列名 - 某些会将列名转化为大写,某些转换为小写,某些则会使其呈查询中指定的样式。这对于 PHP 脚本来说是一个潜在的问题,因为数组键区分大小写。PDO 提供了一个兼容性属性来帮助规范脚本的结果。下面的小代码段是上面PDO_FETCH_BOUND示例的可移植版本,因为setAttribute()方法调用会指导 PDO 将抓取返回的列名全部转换为大写:

$dbh = new PDO(’OCI:’, ’scott’, ‘tiger’);
$dbh->setAttribute(PDO_ATTR_CASE, PDO_CASE_UPPER);
stmt = $dbh->prepare(”SELECT extension, name from CREDITS”);
if ($stmt->execute()) {
$stmt->bindColumn(’EXTENSION’, $extension);
$stmt->bindColumn(’NAME’, $name);
while ($stmt->fetch(PDO_FETCH_BOUND)) {
echo “Extension:$extension, Author:$name\n”;
}
}

除了PDO_CASE_UPPER之外,还有PDO_CASE_LOWER(它会将列名转换为小写)和PDO_CASE_NATURAL(它是默认选项:使列保持数据库驱动程序返回的形式)。

错误和错误处理

可 移植脚本的另一个难题是处理从各种数据库处理程序返回的各种不同的错误消息;某些数据库对于程序化处理错误的支持能力很差,而其他一些数据库则具有非常丰 富的错误代码。只要可行,PDO 将为您的脚本提供一个统一的错误代码,从而使您不必为应对可移植性的这个方面所累。当然,PDO 还会为驱动程序提供原生错误代码和错误消息,以防您需要用它来进行诊断,或者错误代码映射不完整。

另一个困扰 PHP 数据库扩展的一致性问题是错误处理策略的一致性:某些扩展会返回的错误代码需要您手动抓取错误字符串,而其他一些扩展则只是发出 PHP 警告。PDO 允许您从下列三种不同的错误处理策略中选择一种:

  • PDO_ERRMODE_SILENT
    这是默认模式;它只是使用语句和数据库句柄对象的 errorCode() 和 errorInfo() 方法为您设置要检查的错误代码。if (!$dbh->exec($sql)) {
    echo $dbh->errorCode() .”<BR>”;
    $info = $dbh->errorInfo();
    // $info[0] == $dbh->errorCode() 统一的错误代码
    // $info[1] 是驱动程序特定的错误代码
    // $info[2] 是驱动程序特定的错误字符串
    }
  • PDO_ERRMODE_WARNING
    除了设置错误代码之外,PDO 还会发出 PHP 警告,您可以使用常规的 PHP 错误处理程序捕获该警告,并集中应用您准备好用于应用程序的任何错误处理/记录策略,或者只是使该错误显示在浏览器中(在内部测试过程中非常有用)。
  • PDO_ERRMODE_EXCEPTION
    除了设置错误代码之外,PDO 还会抛出一个 PDOException,并将其属性设置为包含该错误代码和信息。然后,您可以在代码的较高级别捕获该异常,使用全局异常处理程序捕获该异常,或者不对其进行处理而终止脚本(此时将回滚任何未决的事务)。try {
    $dbh->exec($sql);
    } catch (PDOException $e) {
    // 显示警告消息
    print $e->getMessage();
    $info = $e->errorInfo;
    // $info[0] == $e->code; unified error code
    // $info[1] 是驱动程序特定的错误代码
    // $info[2] 是驱动程序特定的错误字符串
    }

请注意,与警告或异常相比,静默模式针对运行时错误使用的资源最少,但是为了获得该速度,您牺牲了一些简单性,而变得有一点复杂。

统一错误代码表当前包括下列常量:PDO_ERR_NONE、 PDO_ERR_CANT_MAP、PDO_ERR_SYNTAX、PDO_ERR_CONSTRAINT、PDO_ERR_NOT_FOUND、 PDO_ERR_ALREADY_EXISTS、PDO_ERR_NOT_IMPLEMENTED、PDO_ERR_MISMATCH、 PDO_ERR_TRUNCATED、PDO_ERR_DISCONNECTED。

这些常量所代表的意思字面即可推知,但是 PDO_ERR_CANT_MAP 代码除外;这是一个 PDO 特定的代码,也就是说它无法将驱动程序特定的代码映射到统一的错误代码,因此您应该查询 errorInfo() 方法返回的驱动程序特定代码来获得更多信息。

数据类型

PDO 在某种程度上类型不可知,因此它喜欢将数据表示为字符串,而不是将其转换为整数或双精度类型。此时您可能对此有些迷惑,但是原因非常简单:字符串类型是最 精确的类型,在 PHP 中具有最广泛的应用范围;过早地将数据转换为整数或者双精度类型可能会导致截断或舍入错误。通过将数据以字符串抽出,PDO 为您提供了一些脚本控制,您可以使用普通的 PHP 类型转换工具(如数学运算过程中的转换和隐式)来控制如何进行转换以及何时进行转换。

NULL

如 果结果集中的某列包含一个 NULL 值,PDO 则会将其映射为 PHP null 值。Oracle 在将数据返回 PDO 时会将空字符串转换为 NULL,但是 PHP 支持的任何其他数据库都不会这样处理,从而导致了可移植性问题。PDO 提供了一个驱动程序级属性PDO_ATTR_ORACLE_NULLS,该属性会为其他数据驱动程序模拟此行为:

$dbh = new PDO(’OCI:’, ’scott’, ‘tiger’);
$dbh->setAttribute(PDO_ATTR_ORACLE_NULLS, true);
// 现在从此 $dbh 打开的任何语句中的
// 空字符串都将被转换为 NULL

POD 的现状和未来

PDO 现在仍相当不成熟,但是会快速成熟起来。在编写本文之时,我在本文中提到的任何内容都能够通过 PDO_OCI 驱动程序适用于 Oracle 8 或更高版本(在 Oracle 8.0 和 9.2 上测试过)。

已经计划增加以下主要特性,在不久将可以使用:

  • 使用 PHP 流的 LOB 支持。使 用绑定参数,您能够将任何流资源(如文件、套接字、HTTP 资源、压缩/筛选的流)作为输入或输出参数传递到在 LOB 上运行的查询中。与之相似,类型为 LOB 的输出参数将表现为 PHP 流,因此您可以使用 fread()、fwrite()、fseek() 和其他流函数来访问这些参数。此时,在 PDO 中根本没有 LOB 支持。
  • 持久性连接和缓存的预处理语句。持久性连接使您能够避免在每个页面命中时打开和关闭数据库服务器连接。缓存的预处理语句又前进了一步,它使您能够持久保持查询的预处理版本以及数据库句柄。
  • 游标。目前,PDO 只提供前向只读游标,但是将来会提供可滚动游标(需要基础驱动程序支持)、REF-CURSOR、使用游标进行定位更新,以及可更新滚动游标。

我 们希望在 PHP 5.1 中默认启用 PHP 扩展(距此目标尚远),但是在此之前,我们希望能让 PDO 在 PHP 5.0 发布时稳定运行,但是我们日常工作中的压力稍稍拖延了这些工作。同时,通过 PECL 发布 PDO 使我们能够在收到问题报告时做出回应,并根据不同于 PHP 5.0 发布时间表的时间表发布修复版本,因此您在 PHP 5.1 发布前即可使用 PDO。

我们需要您的反馈

如果您试用了 PDO,并且发现了问题,请务必使用我们的错误跟踪软件将其报告给我们。如果您使用的是 Oracle 驱动程序,则请使用此页:

http://pecl.php.net/bugs/report.php?package=PDO_OCI

如果您使用的是其他驱动程序,则请用其名称替换该 URL 中 PDO_OCI。

如果您使用 PDO 时遇到问题,或者针对某些特性存在疑问,或者具有特性请求,请联系pecl-dev@lists.php.net。如果您愿意,当然还可以直接联系我 (wez@php.net),但是请注意,我每天都会收到大量有关 PHP 的电子邮件;您可能会发现如果首先与前面的邮件列表联系会更快得到答复。


Wez Furlong是 Brain Room Ltd. 的技术总监,他在该公司不但使用 PHP 用于 Web 开发,还将其用作 Linux 和 Windows 应用程序和系统的嵌入式脚本引擎。Wez 是 PHP 的核心开发人员,经常向 SQLite、COM/.Net、ActivePHP、mailparse 和 Streams API 等投稿,他是 PECL 即 PHP 扩展社区库的“头儿”。他的咨询公司的网页为http://www.thebrainroom.net

网站建设

产业新闻

2009/11/13

使用PHP 5.0 轻松解析XML文档

Tags: ,

用sax方式的时候,要自己构建3个函数,而且要直接用这三的函数来返回数据, 要求较强的逻辑。 在处理不同结构的xml的时候, 还要重新进行构造这三个函数,麻烦!

用dom方式,倒是好些,但是他把每个节点都看作是一个node,操作起来要写好多的代码, 麻烦!

网上有好多的开源的xml解析的类库, 以前看过几个,但是心里总是觉得不踏实,感觉总是跟在别人的屁股后面.

这几天在搞java, 挺累的,所以决定换换脑袋,写点php代码,为了防止以后xml解析过程再令我犯难,就花了一天的时间写了下面一个xml解析的类,于是就有了下面的东西。

实现方式是通过包装”sax方式的解析结果”来实现的. 总的来说,对于我个人来说挺实用的,性能也还可以,基本上可以完成大多数的处理要求。

功能:

1、 对基本的xml文件的节点进行 查询 / 添加 / 修改 / 删除 工作.

2、导出xml文件的所有数据到一个数组里面.

3、整个设计采用了oo方式,在操作结果集的时候, 使用方法类似于dom

缺点:

1、 每个节点最好都带有一个id(看后面的例子), 每个“节点名字”=“节点的标签_节点的id”,如果这个id值没有设置,程序将自动给他产生一个id,这个id就是这个节点在他的上级节点中的位置编号,从0开始。

2、 查询某个节点的时候可以通过用“|”符号连接“节点名字”来进行。这些“节点名字”都是按顺序写好的上级节点的名字。

使用说明:

运行下面的例子,在执行结果页面上可以看到函数的使用说明

代码是通过php5来实现的,在php4中无法正常运行。

由于刚刚写完,所以没有整理文档,下面的例子演示的只是一部分的功能,代码不是很难,要是想知道更多的功能,可以研究研究源代码。

目录结构:

test.php
      test.xml
      xml / SimpleDocumentBase.php
      xml / SimpleDocumentNode.php
      xml / SimpleDocumentRoot.php
      xml / SimpleDocumentParser.php

文件:test.xml

<?xml version="1.0" encoding="GB2312"?>
<shop>
<name>华联</name>
<address>北京长安街-9999号</address>
<desc>连锁超市</desc>
<cat id="food">
<goods id="food11">
<name>food11</name>
<price>12.90</price>
</goods>
<goods id="food12">
<name>food12</name>
<price>22.10</price>
<desc creator="hahawen">好东西推荐</desc>
</goods>
</cat>
<cat>
<goods id="tel21">
<name>tel21</name>
<price>1290</price>
</goods>
</cat>
<cat id="coat">
<goods id="coat31">
<name>coat31</name>
<price>112</price>
</goods>
<goods id="coat32">
<name>coat32</name>
<price>45</price>
</goods>
</cat>
<special id="hot">
<goods>
<name>hot41</name>
<price>99</price>
</goods>
</special>
</shop>

文件:test.php

<?php
    require_once "xml/SimpleDocumentParser.php";
require_once "xml/SimpleDocumentBase.php";
require_once "xml/SimpleDocumentRoot.php";
require_once "xml/SimpleDocumentNode.php";
$test = new SimpleDocumentParser();
$test->parse("test.xml");
$dom = $test->getSimpleDocument();
echo "<pre>";
echo "<hr><font color=red>";
echo "下面是通过函数getSaveData()返回的整个xml数据的数组";
echo "</font><hr>";
print_r($dom->getSaveData());
echo "<hr><font color=red>";
echo "下面是通过setValue()函数,给给根节点添加信息,添加后显示出结果xml文件的内容";
echo "</font><hr>";
$dom->setValue("telphone", "123456789");
echo htmlspecialchars($dom->getSaveXml());
echo "<hr><font color=red>";
echo "下面是通过getNode()函数,返回某一个分类下的所有商品的信息";
echo "</font><hr>";
$obj = $dom->getNode("cat_food");
$nodeList = $obj->getNode();
foreach($nodeList as $node){
$data = $node->getValue();
echo "<font color=red>商品名:".$data[name]."</font><br>";
print_R($data);
print_R($node->getAttribute());
}
echo "<hr><font color=red>";
echo "下面是通过findNodeByPath()函数,返回某一商品的信息";
echo "</font><hr>";
$obj = $dom->findNodeByPath("cat_food|goods_food11");
if(!is_object($obj)){
echo "该商品不存在";
}else{
$data = $obj->getValue();
echo "<font color=red>商品名:".$data[name]."</font><br>";
print_R($data);
print_R($obj->getAttribute());
}
echo "<hr><font color=red>";
echo "下面是通过setValue()函数,给商品\"food11\"添加属性, 然后显示添加后的结果";
echo "</font><hr>";
$obj = $dom->findNodeByPath("cat_food|goods_food11");
$obj->setValue("leaveword", array("value"=>"这个商品不错",
"attrs"=>array("author"=>"hahawen", "date"=>date('Y-m-d'))));
echo htmlspecialchars($dom->getSaveXml());
echo "<hr><font color=red>";
echo "下面是通过removeValue()/removeAttribute()函数,
给商品\"food11\"改变和删除属性, 然后显示操作后的结果";
echo "</font><hr>";
$obj = $dom->findNodeByPath("cat_food|goods_food12");
$obj->setValue("name", "new food12");
$obj->removeValue("desc");
echo htmlspecialchars($dom->getSaveXml());
echo "<hr><font color=red>";
echo "下面是通过createNode()函数,添加商品, 然后显示添加后的结果";
echo "</font><hr>";
$obj = $dom->findNodeByPath("cat_food");
$newObj = $obj->createNode("goods", array("id"=>"food13"));
$newObj->setValue("name", "food13");
$newObj->setValue("price", 100);
echo htmlspecialchars($dom->getSaveXml());
echo "<hr><font color=red>";
echo "下面是通过removeNode()函数,删除商品, 然后显示删除后的结果";
echo "</font><hr>";
$obj = $dom->findNodeByPath("cat_food");
$obj->removeNode("goods_food12");
echo htmlspecialchars($dom->getSaveXml());
?>

php

2009/11/07

Winxp下php5,mysql5详细安装教程

Tags: ,

现在php和Mysql的黄金组合已经是开发动态网站的一个非常好的选择了,尤其对于我这样一个开源文化的支持者。经过长达两天的安装,屡错屡勇,终于安装成功。

下面是我根据自己的实际安装过程,整理的一个详细安装说明,希望对广大的LAMP爱好者有所帮助,避免把宝贵的时间浪费在没有任何意义的安装过程上面。,本文的最新版本请查阅我的个人网站:www.webgrin.com

本文实际测试平台说明:

Windows XP home 版。说明:一般来说,windows2000和Windows XP的安装大同小异,唯一的区别可能就是下文中所述的系统目录的位置不同,windows2000的系统目录应该是/WINNT,Windows XP系统目录应该是/WINdows,在复制相关文件时请注意将要复制的目标文件夹的位置不要弄错。:)

APACHE2.0.58 (注意,笔者安装时,最新版本是apache_2.2.2,但是此版本好像不支持php5的模块化安装,笔者最终没有安装成功)

PHP5.1.4

MYSQL5.0.22

phpMyAdmin-2.8.1

对应的安装文件分别是

apache_2.0.58-win32-x86-no_ssl.msi

php-5.1.4-Win32.zip,pecl-5.1.4-Win32.zip

mysql-5.0.22-win32.zip

phpMyAdmin-2.8.1.zip

请到官方网站下载对应版本的软件.

一、Mysql安装

Mysql在windows平台下的安装是我们熟悉标准的安装欢迎程序。

如不需要改变默认安装目录,一直点击next即可完成安装。

下面按照安装顺序解释一下安装过程中的选项。

1、默认是Typical,建议改成Custom

这一步需要选择组件及更改文件夹位置,组件使用默认即可,选择Change改变安装目录,注意:安装mysql的路径中,不能含有中文!

2、程序提示是否要在mysql.com上注册,可以选择跳过,即Skip Sign-Up。

3、安装结束,选中现在开始配置mysql服务器的选择框。

4、配置开始,选择第一项 Detailed Configuration

5、 选择mysql应用于何种类型,第一种是开发服务器,将只用尽量少的内存,第二种是普通WEB服务器,将使用中等数量内存,最后一种是这台服务器上面只跑 mysql数据库,将占用全部的内存,根据自己的需求,选择其中之一。我搭建的是开发平台,并不是实际应用的数据库服务器,所以选择第一种。

6、 选择数据库用途,第一种是多功能用途,将把数据库优化成很好的innodb存储类型和高效率的myisam存储类型,第二种是只用于事务处理类型,最好的 优化innodb,但同时也支持myisam,最后一种是非事务处理类型,适合于简单的应用,只有不支持事务的myisam类型是被支持的。一般选择第一 种多功能的,我选择的是第一种。

7、选择InnodDB的数据存放位置,默认即可。

8、选择mysql允许的最大连接数,可以根据自己的需要选择,默认即可

9、选择数据库监听的端口,一般默认是3306,如果改成其他端口,以后连接数据库的时候都要记住修改的端口,否则不能连接mysql数据库,默认即可

10、设置mysql的默认编码,可以根据需要,选择gb2312或gbk,如果已有数据库文件,要和原来已有的数据库的编码一致,否则可能会出现乱码。

11、选择是否要把mysql设置成windows的服务,建议选择设成服务,这样以后就可以通过服务中启动和关闭mysql数据库了。推荐把下面的复选框也勾选上,这样,在cmd模式下,不必非到mysql的bin目录下执行命令。

12、设置mysql的超级用户密码,超级用户对mysql拥有全部的权限,下面的复选框选择是否允许远程机器用root用户连接到你的mysql服务器上面,根据需要选择。

13、点击Execute,如果前面的设置没有问题的话很快就可以成功安装了。

二、安装apache

安装Apache同样是我们熟悉标准的安装程序。非常直观。

安 装过程中会提示输入主机名,管理员信箱等信箱,这些信息安装完以后可以修改的,这里可以直接按照安装程序中给出的示例填写即可,如 somenet.com,www.somenet.com,webmaster@somenet.com。注意,空着这几项也可以安装成功,但安装完成后 启动apache服务器会提示缺少管理员信箱的错误,所以建议全部填写,虽然是虚构的,:( 。

安装完成后在任务栏的右侧,应该有一个类似 apache那个小羽毛的图标,这就是Apache2的服务管理器。如果图标的状态有显示绿色小三角,说明服务器已经安装成功,如果是红色的,则说明 Apache没有安装成功,你需要进一步去找一下失败的根源。一般的错误原因是apache的配置文件不正确等,比如某一个模块没有正确加载。

三,安装 PHP5

将下载的php-5.0.3RC2-Win32.zip 解压缩并复制到你选择的安装目录中,比如 c:\php5。以下步骤中假定 PHP 位于 c:\php5 中。建议不要用中间有空格的路径,例如:C:\Program Files\PHP5。

将下载的pecl-5.1.4-Win32.zip 解压缩并复制到你选择的安装目录的ext子目录中。比如c:\php5\ext

复制 C:\PHP5\目录下的 php5ts.dll 文件到c:\winnt\system32目录中。

复制 C:\PHP5\ 目录下的libmysql.dll文件到 C:\windows\system32\ 目录中。

复制 C:\PHP5\ 目录下的php.ini-dist文件到 C:\windows\ 目录中,并改名为 php.ini。

说 明:C:\PHP5中包括两个 ini 文件,php.ini-dist 和 php.ini-recommended。其中 php.ini-recommended对默认设置作了性能和安全上的优化,安装实际运行的服务期推荐使用这个配置文件。我们安装的一般是开发平台,所以 在这里选择php.ini-dist。注意:如果使用了 NTFS文件系统,确保运行 webserver 的用户对 php.ini 有读取的权限(例如使其对 Everyone 可读)。

用记事本打开编辑刚才复制到C:\windows\ 目录中php.ini文件。

找到下面两行

; Directory in which the loadable extensions (modules) reside.

extension_dir = “./”

修改成:

; Directory in which the loadable extensions (modules) reside.

extension_dir = “c:\php5\ext\”

再找到下面两行:

;Windows Extensions

;Note that ODBC support is built in, so no dll is needed for it.

将其中的一行;extension=php_mysql.dll前面的分号去掉改成:extension=php_mysql.dll

然后保存文件并退出即可使PHP5支持MySQL数据库

四:让 Apache 解析 php 页面

用 你喜欢的文本编辑器打开httpd.conf文件,让我们加几行进去,这样我们Apache 就可以解析 .php 和.phps文件了。我们需要注意conf文件的两个部分,第一部分是 Dynamic Shared Object (DSO) Support list,第二部分是 cgi-bin directory

首先让我们找到如下所示的 DSO 列表.

# Dynamic Shared Object (DSO) Support

#

# to be able to use the functionality of a module which was built as a DSO you

# have to place corresponding `LoadModule’ lines at this location so the

# directives contained in it are actually available _before_ they are used.

# Statically compiled modules (those listed by `httpd -l’) do not need

# to be loaded here.

#

# Example:

# LoadModule foo_module modules/mod_foo.so

这还有许多,一直找到最后一行:

#LoadModule ssl_module modules/mod_ssl.so

上面一行是该列表的最后一行,按回车产生一个空的行,在这个空行内加入下面这行:

LoadModule php4_module c:/php/sapi/php4apache2.dll

在关闭conf文件之前,我们需要再加入两行。找到CGI-BIN目录,为了节省时间,可以这样找:按CTRL+F,(译者注:如果你是在记事本而不是写字板里编辑这个文本,那么,应该用快捷键F3)查找,在第二个后面两次回车,以便加入下面两行:

AddType application/x-httpd-php .php .phtml .php3 .php4

AddType application/x-httpd-php-source .phps

五:安装phpAdmin

1,解压,将压缩包解压到php可以运行的目录,一般就是apache的根目录

2,设置,拷贝{phpMyAdmin安装目录}/libraries/config.default.php 文件到phpMyAdmin安装目录,并且重命名为config.inc.php

修改config.inc.php文件

$cfg[’Servers’][$i][’host’] = ‘localhost’; // MySQL的主机名称或者IP地址

$cfg[’Servers’][$i][’port’] = ‘3306′; // MySQL端口 – 空白将用默认端口3306

$cfg[’Servers’][$i][’auth_type’] = ‘cookie’; // 认证方式 (config, http or cookie based)?

$cfg[’Servers’][$i][’user’] = ‘root’; // MySQL用户,如 root

$cfg[’Servers’][$i][’password’] = ‘root’; // MySQL用户密码

启用php.ini中的字符设置扩展功能

extension=php_mbstring.dll

注意,编辑onfig.inc.php文件应使用winxp自带写字板程序,不知道为什么用记事本打开显示的是无格式的文档。

六、后记

PHP,MYSQL,APACHE 的安装本来就是一件很简单的事情,特别是后来所讲的以模块化安装PHP的过程。由于最新版本的软件互相间的支持不是很好,尽量选择本文选择的经过实际测试 可以安装成功的版本。大家有时间都不妨自己试一下,这样你才能体验到成功的喜悦。最后祝大家都能顺利的安装AMP。