PHP V5 迁移指南
Jack D. Herrington, 资深软件工程师, Leverage Software Inc.<chsdate w:st="on" year="2006" month="12" day="04" islunardate="False" isrocdate="False"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial; mso-font-kerning: 0pt">2006 </span><span style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">年</span><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial; mso-font-kerning: 0pt"> 12 </span><span style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">月</span></chsdate> 04 日
使用 PHP V5 的新语言特性,可以明显地提高代码的可维护性和可靠性。通过阅读本文,您将了解如何利用这些新特性将用 PHP V4 开发的代码迁移到 PHP V5。
PHP V5 在 PHP V4 基础上做了重大改进。新语言特性使构建可靠的类库和维护类库更加容易。另外,重写标准库帮助使 PHP 更符合其同一 Web 语系,例如 Java™ 编程语言。让我们来看一些 PHP 新的面向对象特性,并了解如何将现有 PHP V4 代码迁移到 PHP V5。
首先,先来了解新语言特性及 PHP 的创建程序怎样更改了用 PHP V4 创建对象的方法。用 V5 的想法是要创建一种工业级语言用于 Web 应用程序开发。那意味着要了解 PHP V4 的限制,然后从其他语言中 (例如 Java、C#、C++、Ruby 和 Perl 语言) 抽取已知优秀语言架构并将这些架构并入 PHP 中。
第一个也是最重要的新特性是针对类的方法和实例变量的访问保护 —— public、protected 和 private 关键字。 这个新特性使类设计人员可以保证对类的内在特性的控制,同时告诉类的使用者哪些类可以而哪些类不可以触及。
在 PHP V4 中,所有代码都是 public 的。在 PHP V5 中,类设计人员可以声明哪些代码是对外部可见的 (public) 而哪些代码仅对类内部可见 (private) 或仅对类的子类可见 (protected)。如果没有这些访问控制,则在大型团队中开发代码或将代码分布为库的工作会受阻,因为那些类的使用者很可能使用错误的方法或访问应当为 private 成员变量的代码。
另一个较大的新功能是关键字 interface 和 abstract,这两个关键字允许进行契约编程。契约编程 意味着一个类向另一个类提供一张契约 —— 换言之: “这是我要做的工作,你不需要知道它是怎样完成的”。 实现 interface 的所有类都遵循该契约。interface 的所有使用者都同意仅使用 interface 中指定的方法。abstract 关键字使得使用接口十分容易,我稍后将加以说明。
这两个主要特性 —— 访问控制和契约编程 —— 允许大型编码人员团队更顺畅地使用大型代码库。这些特性还使 IDE 可以提供更丰富的语言智能特性集。本文不但说明了若干个迁移问题,而且还花了一些时间说明如何使用这些新主要语言特性。
访问控制
为了演示新语言特性,我使用了一个名为 Configuration 的类。这个简单的类中含有用于 Web 应用程序的配置项 —— 例如,指向图片目录的路径。在理想的情况下,此信息将驻存在一个文件或数据库里。清单 1 显示了一个简化的版本。
清单 1. access.php4
<?php
class Configuration
{
var $_items = array();
function Configuration() {
$this->_items[ 'imgpath' ] = 'images';
}
function get( $key ) {
return $this->_items[ $key ];
}
}
$c = new Configuration();
echo( $c->get( 'imgpath' )."\n" );
?>
这是一个完全正统的 PHP V4 类。成员变量保存配置项的列表,构造程序装入项,然后名为 get() 的访问方法返回项的值。
运行脚本后,以下代码将显示在命令行中:
% php access.php4
images
%
很好!这个结果意味着代码运行正常并且正常设定和读取了 imgpath 配置项的值。
将这个类转换为 PHP V5 的第一步是要将构造程序重命名。在 PHP V5 中,初始化对象 (构造程序) 的方法称为 __construct。这次小改动如下所示。
清单 2. access1.php5
<?php
class Configuration
{
var $_items = array();
function __construct() {
$this->_items[ 'imgpath' ] = 'images';
}
function get( $key ) {
return $this->_items[ $key ];
}
}
$c = new Configuration();
echo( $c->get( 'imgpath' )."\n" );
?>
这次改动并不大。只是移至 PHP V5 约定。下一步是添加对类的访问控制以确保类的使用者无法直接读写 $_items 成员变量。这次改动如下所示。
清单 3. access2.php5
<?php
class Configuration
{
private $_items = array();
public function __construct() {
$this->_items[ 'imgpath' ] = 'images';
}
public function get( $key ) {
return $this->_items[ $key ];
}
}
$c = new Configuration();
echo( $c->get( 'imgpath' )."\n" );
?>
如果这个对象的使用者都要直接访问项阵列,访问将被拒绝,因为该阵列被标记为 private。幸运的是,使用者发现 get() 方法可以提供广受欢迎的读取权限。
为了说明如何使用 protected 权限,我需要另一个类,该类必须继承自 Configuration 类。我把那个类称为 DBConfiguration,并假定该类将从数据库中读取配置值。此设置如下所示。
清单 4. access3.php
<?php
class Configuration
{
protected $_items = array();
public function __construct() {
$this->load();
}
protected function load() { }
public function get( $key ) {
return $this->_items[ $key ];
}
}
class DBConfiguration extends Configuration
{
protected function load() {
$this->_items[ 'imgpath' ] = 'images';
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."\n" );
?>
这张清单显示了 protected 关键字的正确用法。基类定义了名为 load() 的方法。此类的子类将覆盖 load() 方法把数据添加到 items 表中。load() 方法对类及其子类是内部方法,因此该方法对所有外部使用者都不可见。如果关键字都是 private 的,则 load() 方法不能被覆盖。
我并不十分喜欢此设计,但是,由于必须让 DBConfiguration 类能够访问项阵列而选用了此设计。我希望继续由 Configuration 类来完全维护项阵列,以便在添加其他子类后,那些类将不需要知道如何维护项阵列。我做了以下更改。
清单 5. access4.php5
<?php
class Configuration
{
private $_items = array();
public function __construct() {
$this->load();
}
protected function load() { }
protected function add( $key, $value ) {
$this->_items[ $key ] = $value;
}
public function get( $key ) {
return $this->_items[ $key ];
}
}
class DBConfiguration extends Configuration
{
protected function load() {
$this->add( 'imgpath', 'images' );
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."\n" );
?>
现在,项阵列可以是 private 的,因为子类使用受保护的 add() 方法将配置项添加到列表中。Configuration 类可以更改存储和读取配置项的方法而不需要考虑它的子类。只要 load() 和 add() 方法以同样的方法运行,子类就应当不会出问题。
对于我来说,增加了访问控制是考虑移至 PHP V5 的主要原因。难道就因为 Grady Booch 说 PHP V5 是四大面向对象的语言之一么?不,因为我曾经接受了一个任务来维护 100KLOC C++ 代码,在这些代码中所有方法和成员都被定义为 public 的。我花了三天时间来清除这些定义,并在清除过程中,明显地减少了错误数并提高了可维护性。为什么?因为没有访问控制,就不可能知道对象怎样使用其他对象,也就不可能在不知道要突破什么难关的情况下做任何更改。使用 C++,至少我还有编译程序可用。PHP 没有配备编译程序,因此这类访问控制变得愈加重要。
契约编程
从 PHP V4 迁移到 PHP V5 时要利用的下一个重要特性是支持通过接口、抽象类和方法进行契约编程。清单 6 显示了一个版本的 Configuration 类,在该类中 PHP V4 编码人员尝试了构建基本接口而根本不使用 interface 关键字。
清单 6. interface.php4
<?php
class IConfiguration
{
function get( $key ) { }
}
class Configuration extends IConfiguration
{
var $_items = array();
function Configuration() {
$this->load();
}
function load() { }
function get( $key ) {
return $this->_items[ $key ];
}
}
class DBConfiguration extends Configuration
{
function load() {
$this->_items[ 'imgpath' ] = 'images';
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."\n" );
?>
清单开始于一个小型 IConfiguration 类,该类定义所有 Configuration 类或派生类所提供的接口。此接口将在类与其所有使用者之间定义契约。契约声明了实现 IConfiguration 的所有类必须配有 get() 方法并且 IConfiguration 的所有使用者都必须坚持仅使用 get() 方法。
下面的这段代码是在 PHP V5 中运行的,但最好使用提供的接口系统,如下所示。
清单 7. interface1.php5
<?php
interface IConfiguration
{
function get( $key );
}
class Configuration implements IConfiguration
{
...
}
class DBConfiguration extends Configuration
{
...
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."\n" );
margin: 0cm 0cm
页:
[1]