http://www.ibm.com/developerworks/cn/opensource/os-php-5.3new1/
PHP V5 和面向对象编程
与 PHP V4 提供的特性相比,2004 年发布的 PHP V5 在面向对象编程(OOP)和设计方面向前迈出了很大的一步。它提供了一些必要的改进,例如类可见性、合适的构造函数和解构函数、输入提示和类反射(class-reflection)API。它为在 PHP 中进行高级的面向对象编程敞开了大门,并允许实现更加简单的设计模式,以及更好的设计类和 API。
PHP V5.3 在 OOP 方面提供了大量渐进式补充。这些改进一直集中在语法补充和性能改进方面。首先,我们将查看静态方法和成员方面的新特性。
--------------------------------------------
回页首
改进静态方法和成员处理
PHP V5 中的一个有用补充就是能够将一个方法或类成员指定为静态的(PHP V4 确实支持对方法和类成员的静态访问,但是不能够将方法或成员指定为专门用于静态访问)。静态访问特别适合实现单一设计模式,在这种模式中只存在一个类实例。
PHP V5.3 提供一些特性来增强对类的静态成员和方法的支持。我们将查看最近添加的一种魔术方法:__callStatic()。
_callStatic() 魔术方法
PHP V5 提供了一些可用于类内部的特别定义的方法,称为魔术方法。当在类内部定义时,这些方法可以提供特殊的功能,并支持重载(允许一种方法接受不同类型的参数)和多态(允许不同数据类型使用相同的接口)。它们还允许通过 PHP 轻松地使用不同类型的 OOP 编程方法和设计模式。
在 PHP V5.3 中,添加了一种新的魔术方法:__callStatic()。它的工作方式类似于 __call() 魔术方法,后者的设计意图是处理那些没有在类中定义或对类不可见的方法的调用。然而,__callStatic() 是为了处理静态方法调用,这使我们能够更好地设计方法重载。下面给出了一个使用该方法的示例。
清单 1. 使用 __callStatic() 和 __call() 的示例
class Foo
{
public static function __callStatic(
$name,
$args
)
{
echo "Called method $name statically";
}
public function __call(
$name,
$args
)
{
echo "Called method $name";
}
}
Foo::dog(); // outputs "Called method dog statically"
$foo = new Foo;
$foo->dog(); // outputs "Called method dog"
需要注意,PHP 确实加强了对 __callStatic() 方法的定义;它必须是公共的,并且必须被声明为静态的。同样,__call() 魔术方法必须被定义为公共的,所有其他魔术方法都必须如此。
--------------------------------------------
回页首
动态的静态调用
PHP 的一个优秀特性是可变变量。这表示可以使用某个变量的字符串值指定另一个变量的名称。换句话说,可以执行与下面类似的操作。
清单 2. 可变变量
x = 'y';
$$x = 'z';
echo $x; // outputs 'y'
echo $$x; //
outputs 'z'
这也适用于函数,甚至是类方法,如下所示。
清单 3. 可变函数和类方法名
class Dog
{
public function bark()
{
echo "Woof!";
}
}
$class = 'Dog'
$action = 'bark';
$x = new $class(); // instantiates the class 'Dog'
$x->$action(); // outputs "Woof!"
PHP V5.3 的一个新特性就是在进行静态调用时,能够使指定的类名成为一个变量。这提供了一些新的机会,如下所示。
清单 4. 可变的类命名
class Dog
{
public static function bark()
{
echo "Woof!";
}
}
$class = 'Dog';
$action = 'bark';
$class::$action(); //outputs "Woof!"
这一补充完善了 PHP 的可变变量特性,允许将它们应用到涉及 PHP 的所有情形。
让我们查看一个有关静态方法和成员应用的更有用的增强:延迟静态绑定(late static binding)。
--------------------------------------------
回页首
延迟静态绑定
在 V5.3 以前,PHP 存在的麻烦问题是如何处理静态方法和成员。到目前为止,使用自身或 __CLASS__ 进行的静态引用都是在定义函数的类作用域中解析的。问题在于,如果类进行了扩展并且调用来自新的子类,那么解析将是错误的。PHP V5.3 添加了延迟静态绑定来解决这个问题。为了更好地进行解释,我们在下面将创建一个具有静态方法的类。
清单 5. 使用静态方法 test() 的 Foo 类
class Foo
{
protected static $name = 'Foo';
public static function test()
{
return self::$name;
}
}
让我们对这个类进行扩展。我们将在子类中重新定义成员 $name。
清单 6. 子类 Bar 扩展了父类 Foo
class Bar
{
protected static $name = 'Bar';
}
我们在清单 7 中进行了静态调用。
清单 7. 静态方法调用 test()
echo Bar::test();
该调用的输出是字符串 Foo。这是因为在 test() 方法中进行的引用 self::$name 是在 Foo 类中完成的。这样绑定的原因是:函数是在 Foo 类中定义的。
PHP V5.3 添加了关键字 static 以允许针对当前类进行引用。因此将修改上面的 Foo 类以在清单 8 中使用该关键字,我们将看到输出的内容变成了 Bar。
清单 8. 使用 static 关键字
class Foo
{
protected static $name = 'Foo';
public static function test()
{
return static::$name;
}
}
class Bar
{
protected static $name = 'Bar';
}
echo Bar::test(); // outputs 'Bar'
有关 static 关键字需要注意一点,它的工作方式与在非静态上下文中的工作方式不同。这意味着普通的继承规则没有应用到静态调用中。静态关键字将仅仅尝试在当前类中解析调用,而不是在定义函数的类中执行。这一点值得注意。
现在您已经了解了有关静态方法和成员的增强,现在让我们看一看 PHP V5 中新添的类,它们构成了非常有用的部分:标准 PHP 库。
--------------------------------------------
回页首
标准 PHP 库
标准 PHP 库(Standard PHP Library,SPL)是 PHP V5 中新增的接口和类的集合,旨在解决标准问题。这些问题包括实现可迭代的对象,使对象具有数组的行为或实现一个链接的列表。这些类和方法的优点是它们是原生的 PHP,这意味用 PHP 本身实现它们会获得更快的速度。在很多情况下,这些类和方法还允许内部 PHP 函数直接使用这些对象,就像 Iterator 接口允许您使用 foreach 结构迭代对象一样。
PHP V5.3 向 SPL 添加了更多的类。我们前面提到一个类就是在 SPL 类 SplDoublyLinkedList 中实现的双重链接列表。它供其他两个新 SPL 类使用:SplStack(实现一个栈)和 SplQueue(实现一个队列)。
让我们看一看如何使用 SplStack 类实现一个栈。
清单 9. 使用 SplStack
$stack = new SplStack();
// push a few new items on the stack
$stack->push('a');
$stack->push('b');
$stack->push('c');
// see how many items are on the stack
echo count($stack); // returns 3
// iterate over the items in the stack
foreach ( $stack as $item )
echo "[$item],";
// the above outputs: [c],[b],[a]
// pop an item off the stack
echo $stack->pop(); // returns 'c'
// now see how many items are on the stack
echo count($stack); // returns 2
SqlQueue 也采取类似的方式,但是它像队列那样工作(先进先出;而不是像栈一样最后一个项进栈,第一个项出栈)。此外,还存在堆实现(SplHeap),以及针对某些情况的特定队列和堆实现(SplMinHeap、SplMaxHeap 和 SplPriorityQueue)。
另一个有用的补充是 SplFixedArray 类,顾名思义,这是一个固定大小的数组实现。然而,它的性能非常快 — 实际上它在基准测试中要比 PHP 内置数组实现快 10% 至 30%。造成这种速度优势的原因是数组是固定大小的,而默认的 PHP 数组是可变大小的,并且不允许非数值型索引。清单 10 显示了它的使用方法。
清单 10. SplFixedArray
$array = new SplFixedArray(3);
$array[0] = 'dog';
$array[1] = 'cat';
$array[2] = 'bird';
$a->setSize(4); // increase the size on the fly
$array[3] = 'mouse';
foreach ( $array as $value )
echo "[$value],";
Output:
[dog],[cat],[bird],[mouse]
此外,添加了一些新的迭代器类:FilesystemIterator 和 GlobIterator。它们与 PHP 中的其他迭代器类使用相同的工作方式,但是它们分别针对不同的情况。
SPL 的另一个改变是现在的 PHP V5.3 通常启用 SPL。在以前的 PHP V5 版本中,可以在编译时禁用 SPL,但是 PHP V5.3 不能禁用 SPL。
SPL 中的新补充?? PHP 添加了一些有用的并且易于使用的功能,以及数据结构的实现,例如双重链接列表、栈、堆和队列。这些类可用于替换用户空间实现,这将改进速度并更好地集成各种 PHP 函数和构造。
现在我们已经了解了 SPL 中的一些新内容,让我们看一看 PHP V5.3 中的 OOP 如何通过循环垃圾收集获得显著的性能和内存使用改善。
--------------------------------------------
回页首
循环垃圾收集
垃圾收集是 PHP 开发人员在性能方面遇到的一个问题。PHP 有一个非常简单的垃圾收集器,它实际上将对不再位于内存范围(scope)中的对象进行垃圾收集。垃圾收集的内部方式是使用一个引用计数器,因此当计数器达到 0 时(意味着对该对象的引用都不可用),对象将被当作垃圾收集并从内存中删除。
这种方式工作得很好,但是如果一个对象使用父子关系引用另一个对象,那就会引发问题。在这种情况下,这些对象的引用计数器没有被收集,因此这些对象使用的内存仍然属于未引用的内存,并且直到完成请求后才能够进行分配。下面看一下关于这种问题的例子。
清单 11. PHP V5.2 及之前版本不能恰当地对父子类关系进行垃圾收集
class Parent
{
public function __construct()
{
$this->child = new Child($this);
}
}
class Child
{
public function __construct(
Parent $parent
)
{
$this->parent = $parent;
}
}
在这种情况下,每当创建 Parent 类的实例并且该实例随后超出内存范围时,内存不会被释放,因此脚本在内存使用中不断增加。有一些用户空间解决方案可以解决这个问题。例如为父类创建一个解构函数将直接释放子对象。这种解构器必须在解除父类引用之前进行调用。但是执行这些工作会使您的代码也变得非常复杂。
在 PHP V5.3 中,垃圾收集器将检测这些循环引用,并且能够释放它们所占用的内存,因此在执行脚本时 PHP 内存使用情况将保持平稳。当 Parent 类的每个引用被删除后,Parent 类中的 Child 类引用也将会被当作垃圾收集。
--------------------------------------------
回页首
结束语
PHP 在支持面向对象编程方面经历了长期的发展。PHP V4 时期的支持是比较弱的,但在 PHP V5 中得到显著的改善,并且后续版本还会调整。现在,PHP V5.3 提供了一些令人兴奋的改进,包括语法增强,例如新 __callStatic() 魔术方法、动态的静态调用、延迟静态绑定、静态方法和成员支持。它为 SPL 添加了新的内容,包括双重链接表、栈、堆和队列的实现,使您获得了一些常见的数据结构并且可以轻松使用它们。最后,期待已久的循环垃圾收集器是一个经过改进的垃圾收集器,它恰当地为这些循环实例释放内存,解决了自引用类的内存和性能问题。所有这些特性使 PHP V5.3 成为一种更加强大的面向对象编程语言。