您的位置:首页 > 编程语言 > PHP开发

PHP5.3的新特性(一):对象接口的变化

2011-12-15 13:41 337 查看
PHPV5和面向对象编程

2004年PHPV5发布,和PHPV4相比,它在面向对象编程(OOP)和设计方面有了巨大的进步,增加了一些必要的特性,如类的可见性、正确的构造函数与析构函数、类型提示和类反射API。PHP5为高级面向对象编程开启了方便之门,使得开发者可以更简单地实现各种设计模式,设计出更好的类和API。

在PHPV5.3中,继续大幅加强OOP能力,这些改进包括语法的进化和性能的改善等方面。首先让我们来看静态方法和属性相关的新特性。

改进后的静态方法与属性的处理方式

PHPV5中一个实用的新增特性是可以指定类的一个方法或属性为静态成员(PHPV4支持静态地访问类的方法和属性,但是无法指定它们是为静态访问而设计的)。静态访问在实现单例模式时非常有用,此时,类只存在一个实例。

PHPV5.3增加了若干特性,以加强对类的静态方法和属性的支持。首先,我们来分析新增的魔术方法:
__callStatic()。


__
callStatic()魔术方法


PHPV5包含了若干个可以用于类内部的魔术方法。如果在类中定义了这些方法,它们可以提供特殊的功能,从而实现重载(允许一个方法接收不同类型的参数)和多态(允许不同数据类型使用相同的接口)功能。同时,它们也让PHP中的面向对象编程(OOP)和设计模式的实现变得更加简单。

在PHPV5.3中,新增了一个魔术方法:
__callStatic()。它和
__call()魔术方法类似,后者用于处理类中不存在或不可见的方法调用。而
__callStatic()用于处理类似的静态方法调用,方便开发者实现方法的重载。下面是一个如何使用的例子。


Listing1.Exampleofusing
__callStatic()
vs.
__call()


PHP代码

classFoo
{
publicstaticfunction__callStatic(
$name,
$args
)
{
echo"Calledmethod$namestatically";
}

publicfunction__call(
$name,
$args
)
{
echo"Calledmethod$name";
}
}

Foo::dog();//输出"Calledmethoddogstatically"
$foo=newFoo;
$foo->dog();//输出"Calledmethoddog"

需要注意,PHP要求
__callStatic()方法必须是
public,而且是static。类似的,
__call()魔术方法也要求定义为
public,其实所有的魔术方法都要求定义为public。

动态的静态调用

PHP中有一个实用的特性是变量的变量。它意味着你可以用一个变量的字符串值作为另外一个变量的名字。换句话说,你可以完成下面的功能。

Listing2.Variablevariables

PHP代码

x='y';
$$x='z';
echo$x;//输出'y'
echo$$x;//输出'z'

同样的概念可以用于函数,甚至类的成员函数,如下例所示。

Listing3.Variablefunctionandclassmethodnames

PHP代码

classDog
{
publicfunctionbark()
{
echo"Woof!";
}
}

$class='Dog'
$action='bark';
$x=new$class();//实例化类'Dog'
$x->$action();//输出"Woof!"

在PHPV5.3中增加了一个功能,静态调用中的类名可以通过一个变量来指定。由此引入了一些新的编程方法,如下所示。

Listing4.Variableclassnaming

PHP代码

classDog
{
publicstaticfunctionbark()
{
echo"Woof!";
}
}

$class='Dog';
$action='bark';
$class::$action();//输出"Woof!"

这个新增功能使得PHP中变量的变量这个特性成为一个完备特性,可用于各种环境。

接下来让我们关注另外一个更加实用的关于静态方法和属性方面的功能改善:静态调用的晚绑定。

静态调用的晚绑定(膘叔:是否叫后绑定或者延迟绑定较好?)

PHPV5.3之前一个令人恼火的事情是静态方法和属性的处理。到目前为止,静态引用,包括使用self或__CLASS__,都是在函数定义所在类的范围内解析。当继承一个类,并在子类中调用静态函数时,将会发生解析错误。PHPV5.3中新增的静态调用晚绑定特性解决了这个问题。为了更好的说明这个问题,我们创建如下一个包含静态方法的类。

Listing5.Class
Foo
withstaticmethod
test()



PHP代码

classFoo
{
protectedstatic$name='Foo';

publicstaticfunctiontest()
{
returnself::$name;
}
}

然后继承这个类,并在子类中重定义$name属性。

Listing6.Childclass
Bar
thatextendsparentclass
Foo


PHP代码

classBarextendsFoo
{
protectedstatic$name='Bar';
}

最后进行一个静态调用。

Listing7.Staticmethodcall
test()


这个调用的输出将是字符串
Foo。因为test()方法中的
self::$name引用是在

Foo这个类中解析。而PHP解释器这么做是因为test()方法是在类Foo中定义的。


PHPV5.3通过使用static关键字,使得该引用可以在当前类中解析。将上面的类Foo改写为如下定义(译注:注意红色部分),输出将变为Bar。

Listing8.Usingthestatickeyword

PHP代码

classFoo
{
protectedstatic$name='Foo';

publicstaticfunctiontest()
{
returnstatic::$name;
}
}

classBarextendsFoo
{
protectedstatic$name='Bar';
}

echoBar::test();//输出'Bar'

需要注意的是,static关键字的这种用法在非静态上下文中无效。也就是说普通的继承规则对静态调用无效。static关键字将简单的去尝试在当前类中进行解析,而不是函数定义所在的类。这是一个需要我们注意的事情。

在分析了一些有关静态方法和属性方面的改善之后,我们来看看PHPV5中非常重要的部分:标准PHP库中新增的一些类。

标准PHP库

PHPV5中新增的标准PHP库是用于解决标准问题的一组类和接口。这些问题包括可迭代对象、数组对象和链表等。使用这些类和接口的好处是:它们是PHP的原生方法,比用PHP实现的效率要高。同时,它们使得许多PHP内置函数和语言结构可以直接使用这些对象,比如你可以通过foreach结构遍历一个实现了迭代器接口的对象。

PHPV5.3在SPL中增加了若干个类。如早前提到过的双向链表SplDoublyLinkedList。SPL中新增的栈和队列类:SplStack和SplQueue的实现中都使用了该类。

下面看一下如何使用
SplStack
类实现一个栈。

Listing9.Using
SplStack





PHP代码

$stack=newSplStack();

//pushafewnewitemsonthestack

$stack->push('a');

$stack->push('b');

$stack->push('c');

//seehowmanyitemsareonthestack

echocount($stack);//returns3

//iterateovertheitemsinthestack

foreach($stackas$item)

echo"[$item],";

//theaboveoutputs:[c],,[a]

//popanitemoffthestack

echo$stack->pop();//returns'c'

//nowseehowmanyitemsareonthestack

echocount($stack);//returns2

SplQueue类与SplStack相似,实现了队列功能(先进先出,而栈是先进后出)。此外,还实现了堆(SplHeap)以及一些特殊的队列和堆实现,如(
SplMinHeap、
SplMaxHeap
SplPriorityQueue)。

另外新增的一个实用类是
SplFixedArray,如名字暗示的那样,它是一个固定大小的数组实现。不过,它的速度比PHP内置的数组要快20-30%,原因是它是固定大小的,而内置数组是变长的,并且它只允许数字索引。下面演示了它的用法。


[b]Listing10.
SplFixedArray


PHP代码

$array=newSplFixedArray(3);
$array[0]='dog';
$array[1]='cat';
$array[2]='bird';
$a->setSize(4);//increasethesizeonthefly
$array[3]='mouse';
foreach($arrayas$value)
echo"[$value],";

Output:
[dog],[cat],[bird],[mouse]

此外,还增加了两个迭代器类:
FilesystemIterator
GlobIterator。它们的行为与PHP中的其它迭代器一致,只不过它们是为特定目的而定制的。


另外一个变化是SPL在PHPV5.3中将强制启用。以前的PHPV5版本,你可以在编译的时候禁止SPL,但是这个选项现在没有了。

SPL的改善提高了PHP的易用性,并实现了数据结构如双向链表、栈、堆和队列等。它们可以替换用户自己实现的这些数据结构,这样不但可以提高效率,而且还可以促进PHP函数和语言结构的整合。

讨论完SPL的改进之后,我们看看PHPV5.3如何通过采用循环垃圾回收技术来提高OOP的性能与内存消耗。

循环垃圾回收

PHP开发者经常碰到的一个性能瓶颈是垃圾回收。PHP有一个非常简单的垃圾回收器,用于回收不在作用域内的对象。它的原理是通过引用计数,当计数到达0时(意味着不再有引用指向该对象),该对象被回收,释放占用的内存。

这种方式简单高效,但是在父子关系的对象引用中会产生问题。此时,这些对象的引用计数器不为0,所以不会被回收,一直存在到请求的结束。下面我们来看这个问题的一个例子。

Listing11.Parent-childclassrelationshipnotproperlygarbagecollectedinPHPV5.2andearlier

PHP代码

classParent
{
publicfunction__construct()
{
$this->child=newChild($this);
}
}

classChild
{
publicfunction__construct(
Parent$parent
)
{
$this->parent=$parent;
}
}

for($i=0;$i<1000;$i++){
newParent();
}

在这个例子中,你每次创建一个Parent对象,然后该对象不再被引用,但是内存不会被释放,所以脚本的内存消耗持续增加。在用户空间,存在一些方法来解决该问题,比如为Parent类提供一个析构函数,直接释放引用的子对象。但是你必须在释放Parent对象前显式调用该析构函数,如果一直这么做,将使你的代码变得非常复杂。

在PHPV5.3中,垃圾回收器将会检测这种循环引用并释放它们,从而使该脚本的内存使用保持在正常状态。当Parent对象的引用计数为0时,Parent内部引用的Child对象也将被回收。

总结

PHP在支持面向对象编程方面走过了漫漫长路,从PHPV4时的弱支持到PHPV5中的飞跃以及后续版本的改进。现在PHPV5.3又加入了一些令人兴奋的改进,包括语法上的改进,如新的
__callStatic()魔术方法、动态的静态调用、静态调用的晚绑定和静态方法和属性的支持。SPL上则增加了双向链表、栈、堆和队列的实现,为开发者提供了现成的常用数据结构并提高它们的易用性。最后,期盼已久的循环垃圾回收器修复了自引用类导致的内存和性能问题,通过这个大幅改进的垃圾回收器,正确释放循环引用的对象。上述
的特性,让
PHPV5.3成为一个更强大的面向对象编程语言。

原文链接:http://www.ibm.com/developerworks/opensource/library/os-php-5.3new1/index.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: