Andi Gutmans看 PHP 5、Oracle 的未来

日期:2006-09-21  作者:喜腾小二  来源:PHPChina


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) 隐式对象克隆:


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 访问修饰符。

例如:


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() 方法实现此目的,该方法返回代表指定查询字符串的新对象的句柄。


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 文件:



John Doe
87234838


Janet Smith
72384329


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


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

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

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

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 服务是何等简单。您可能注意到,此扩展使用了与前面相同的面向对象的重载功能。


$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

<<<返回技术中心

技术文章

站内新闻

我要啦免费统计