PHP5/ZendEngine2的改进

* 本文是对《Classes and Objects in
PHP5》系列文章的补充和修正,介绍了PHP5对象体系的总体框架,但有些特性没有具体介绍。强烈建议在读过《Classes
and Objects in PHP5》后阅读本文。
PHP5推出的对象体系相信是大家最为期待的。PHP5借鉴了Java2的对象模型,提供了较为强大的面向对象编程支持,使用PHP来实现OO将变得轻松和自然。
对象传递 PHP5使用了Zend引擎II,对象被储存于独立的结构Object
Store中,而不像其它一般变量那样储存于Zval中(在PHP4中对象和一般变量一样存储于Zval)。在Zval中仅存储对象的指针而不是内容(value)。当我们复制一个对象或者将一个对象当作参数传递给一个函数时,我们不需要复制数据。仅仅保持相同的对象指针并由另一个zval通知现在这个特定的对象指向的Object
Store。由于对象本身位于Object
Store,我们对它所作的任何改变将影响到所有持有该对象指针的zval结构—-表现在程序中就是目标对象的任何改变都会影响到源对象。.这使PHP对象看起来就像总是通过引用(reference)来传递,因此PHP中对象默认为通过“引用”传递,你不再需要像在PHP4中那样使用&来声明。
垃圾回收机制
某些语言,最典型的如C,需要你显式地要求分配内存当你创建数据结构。一旦你分配到内存,就可以在变量中存储信息。同时你也需要在结束使用变量时释放内存,这使机器可以空出内存给其它变量,避免耗光内存。
PHP可以自动进行内存管理,清除不再需要的对象。PHP使用了引用计数(reference
counting)这种单纯的垃圾回收(garbage
collection)机制。每个对象都内含一个引用计数器,每个reference连接到对象,计数器加1。当reference离开生存空间或被设为NULL,计数器减1。当某个对象的引用计数器为零时,PHP知道你将不再需要使用这个对象,释放其所占的内存空间。
例如: ?php class Person{ } function sendEmailTo(){ } $haohappy = new
Person( ); // 建立一个新对象: 引用计数 Reference count = 1 $haohappy2 =
$haohappy; // 通过引用复制: Reference count = 2 unset($haohappy); //
删除一个引用: Reference count = 1 sendEmailTo($haohappy2); //
通过引用传递对象: // 在函数执行期间: // Reference count = 2 //
执行结束后: // Reference count = 1 unset($haohappy2); // 删除引用:
Reference count = 0 自动释放内存空间 ?
以上是PHP5在内存管理上的变化,也许大家不怎么感兴趣。下面我们来看看PHP5中的对象模型和PHP4有什么具体的不同之处:
★ 新增功能 ★ 改进功能 1) ★ Private and Protected Members
私有和保护类成员 2) ★ Abstract Classes and Methods 抽象类和抽象方法 3) ★
Interfaces 接口 4) ★ Class Type Hints 类型指示 = 5) ★ final final关键字
= 6) ★ Objects Cloning 对象复制 = 7) ★ Constructors and Destructors
构造函数和析构函数 8) ★ Class Constants 类常量 = 9) ★ Exceptions
异常处理 10) ★ Static member 静态类成员 11) ★__METHOD__ constant
__METHOD__常量 = 12) ★ Reflection 反射机制
第1、2、3、7、10请自行查阅本文末尾的《Classes and Objects in
PHP5》系列,其中已有详细介绍,本文中不再讲解。第9点异常处理和第12点反射机制内容较为丰富,限于篇幅亦不在文中介绍,请关注即将推出的《PHP
& More》电子杂志第二期,会专门撰文介绍。
以下向大家介绍第4、5、6、8、11点语言特性: 4) ★ Class Type Hints
类型指示
大家都知道,PHP是一种弱类型的语言。在使用变量前不需要定义,不需要声明变量的数据类型。这在编程中带来很多便利,但也带了一些隐患,特别当变量的类型变化时。在PHP5增加了类型指示,可以在执行过程中自动对类方法的参数类型进行判断。这类似于Java2中的RTTI,配合reflection可以让我们很好地控制对象。
?php interface Foo { function a(Foo $foo); } interface Bar { function
b(Bar $bar); } class FooBar implements Foo, Bar { function a(Foo $foo) {
// … } function b(Bar $bar) { // … } } $a = new FooBar; $b = new
FooBar; $a-a($b); $a-b($b); ?
在强类型语言中,所有变量的类型将在编译时进行检查,而在PHP中使用类型指示来对类型的检查则发生在运行时。如果类方法参数的类型不对,将会报出类似“Fatal
error: Argument 1 must implement interface Bar…”这样的错误信息。
以下代码: ?php function foo(ClassName $object) { // … } ? 相当于:
?php function foo($object) { if (!($object instanceof ClassName)) {
die(“Argument 1 must be an instance of ClassName”); } } ? 5) ★ final
final关键字
PHP5中新增加了final关键字,它可以加在类或类方法前。标识为final的类方法,在子类中不能被覆写。标识为final的类,不能被继承,而且其中的方法都默认为final类型。
Final方法: ?php class Foo { final function bar() { // … } } ?
Final类: ?php final class Foo { // class definition } //
下面这一行是错误的 // class Bork extends Foo {} ? 6) ★ Objects Cloning
对象复制
前面在内存管理部份说过,PHP5中默认通过引用传递对象。像使用$object2=$object1这样的方法复制出的对象是相互关联的。如果我们确实需要复制出一个值与原来相同的对象而希望目标对象与源对象没有关联,那么就需要使用clone关键字。如果还希望在复制的同时变动源对象中的某些部份,可以在类中定一个__clone()函数,加入操作。
?php //对象复制 class MyCloneable { static $id = 0; function
MyCloneable() { $this-id = self::$id++; } /* function __clone() {
$this-address = “New York”; $this-id = self::$id++; } */ } $obj = new
MyCloneable(); $obj-name = “Hello”; $obj-address = “Tel-Aviv”; print
$obj-id . “/n”; $obj_cloned = clone $obj; print $obj_cloned-id . “/n”;
print $obj_cloned-name . “/n”; print $obj_cloned-address . “/n”; ?
以上代码复制出一个完全相同的对象。 然后请把function
__clone()这一个函数的注释去掉,重新运行程序。则会复制出一个基本相同,但部份属性变动的对象。
8) ★ Class Constants 类常量 PHP5中可以使用const关键字来定义类常量。 ?php
class Foo { const constant = “constant”; } echo “Foo::constant = ” .
Foo::constant . “/n”; ? 11) ★__METHOD__ constant __METHOD__常量
__METHOD__ 是PHP5中新增的“魔术”常量,表示类方法的名称。
魔术常量是一种PHP预定义常量,它的值可以是变化的,PHP中的其它已经存在的魔术常量有__LINE__、__FILE__、__FUNCTION__、__CLASS__等。
?php class Foo { function show() { echo __METHOD__; } } class Bar
extends Foo { } Foo::show(); // outputs Foo::show Bar::show(); //
outputs Foo::show either since __METHOD__ is // compile-time
evaluated token function test() { echo __METHOD__; } test(); //
outputs test ?

新的对象模型
PHP中的对象处理部分已完全重写,具有更佳的性能和更多的功能。在先前的PHP版本中,对象被当做原始的简单类型
来处理,这种方法的缺点是当变量被赋值或作为参数传递时,得到的是对象拷贝。而在新版本中,
对象是通过句柄来引用的,而不是通过对象的值。
很多PHP程序员可能未意识到老的对象模型的“copying
quirks“,因此以前的大多数PHP程序将不需要做任何更改
即可运行,或只做很少的改动。 私有和保护成员 PHP
5引进了私有和保护成员变量,它们可以定义可视化的类属性。 示例
保护成员变量能在该类的子类中被访问,而私有成员变量只能在所属类中被访问。
?phpclass MyClass { private $Hello = “Hello, World!/n”; protected $Bar =
“Hello, Foo!/n”; protected $Foo = “Hello, Bar!/n”; function printHello()
{ print “MyClass::printHello() ” . $this-Hello; print
“MyClass::printHello() ” . $this-Bar; print “MyClass::printHello() ” .
$this-Foo; } } class MyClass2 extends MyClass { protected $Foo; function
printHello() { MyClass::printHello(); /* Should print */ print
“MyClass2::printHello() ” . $this-Hello; /* Shouldn’t print out
anything */ print “MyClass2::printHello() ” . $this-Bar; /* Shouldn’t
print (not declared)*/ print “MyClass2::printHello() ” . $this-Foo; /*
Should print */ } } $obj = new MyClass(); print $obj-Hello; /*
不输出任何内容,以下类同 */ print $obj-Bar; /* Shouldn’t print out
anything */ print $obj-Foo; /* Shouldn’t print out anything */
$obj-printHello(); /* Should print */ $obj = new MyClass2(); print
$obj-Hello; /* Shouldn’t print out anything */ print $obj-Bar; /*
Shouldn’t print out anything */ print $obj-Foo; /* Shouldn’t print out
anything */ $obj-printHello(); ? 私有和保护方法 PHP
5(ZEND引擎2)中,私有方法和保护方法也被引入。 示例: ?phpclass Foo {
private function aPrivateMethod() { echo “Foo::aPrivateMethod()
called./n”; } protected function aProtectedMethod() { echo
“Foo::aProtectedMethod() called./n”; $this-aPrivateMethod(); } } class
Bar extends Foo { public function aPublicMethod() { echo
“Bar::aPublicMethod() called./n”; $this-aProtectedMethod(); } } $o = new
Bar; $o-aPublicMethod(); ?
以前代码中的用户自定义类或方法中虽然没有定义”public,” “protected” 或
“private”等关键字,但无需修改即可运行。 抽象类和方法 PHP
5还引入了抽象类和方法。抽象方法只声明方法的”符号”,而不提供它的实现。一个包含抽象方法的类需要声明为”abstract”。
例如: ? phpabstract class AbstractClass { abstract public function
test(); } class ImplementedClass extends AbstractClass { public function
test() { echo “ImplementedClass::test() called./n”; } } $o = new
ImplementedClass;$o-test(); ? 抽象类不能实例化。
旧的代码中的用户自定义类或方法中虽未定义”abstract”关键字,但无需修改就可以运行。
接口(Interfaces) ZEND引擎2.0引入了接口。一个类可以实现任意的接口列表。
例如: ?phpinterface Throwable { public function getMessage();}class
Exception implements Throwable { public function getMessage() { // …}?
旧的代码中的用户定义类或方法中虽然没有定义”interface”关键字,但无需修改就可以正常运行。
类类型提示(Class Type Hints) 在保留类无需定义类型的同时,PHP
5引入了类类型提示来声明,以期望把对象的类通过参数传递给一个方法。 例如:
?phpinterface Foo { function a(Foo $foo);}interface Bar { function b(Bar
$bar);}class FooBar implements Foo, Bar { function a(Foo $foo) { // …
} function b(Bar $bar) { // … }}$a = new FooBar;$b = new
FooBar;$a-a($b);$a-b($b);?
这些类类型提示不是象一些需要类型定义的语言那样在编译中进行检查,而是在运行时进行检查。这就意味着:
?phpfunction foo(ClassName $object) { // …}? is equivalent to:
?phpfunction foo($object) { if (!($object instanceof ClassName)) {
die(“Argument 1 must be an instance of ClassName”); }}?
这种语法只用于对象或类,不适用于内建(built-in)类型。 Final关键字 PHP
5引入了“final”关键字以定义在子类中不能被覆盖的成员或方法。 例: ?php
class Foo { final function bar() { // … }}?
以前所写代码中的用户自定义类或方法中虽未定义”final”关键字,但无需修改就可以运行了。
对象克隆(Object Cloning) PHP
4在对象被复制时,用户不能判断运行那个拷贝构造函数。在复制时,PHP
4根据对象的属性 一位一位地复制一个同样的复制品。
每次都要建立一个完全一样的复制品并不总是我们想要的。一个很好的复制构造例子是,当有
一个代表一个GTK窗口的对象,它拥有该窗口的所有资源,当你建立一个拷贝时,你可能需要一
个新的窗口,它拥有原窗口的所有属性,但需要拥有新窗口的资源。另外一个例子是你有一个
对象引用了另外一个对象,当你复制父对象时,你希望建立那个引用对象的新实例,以使复制品有一个单独的拷贝。
对一个对象的拷贝通过调用对象的__clone()方法完成: ?php
$copy_of_object = $object-__clone(); ?
当开发者请求建立一个对象的新的拷贝时,ZEND引擎会检查是否已经定义了__clone()方法。如果未定义
的话,它会调用一个默认的__clone()方法来复制该对象的所有属性。如果定义了该方法,该方法会负责
在拷贝中设置必要的属性。为使用方便,引擎会提供一个函数从源对象中导入所有的属性,这样它就可
以先得到一个具有值的源对象拷贝,然后只需要对需要改变的属性进行覆盖即可。
例: ?php class MyCloneable { static $id = 0; function MyCloneable() {
$this-id = self::$id++; } function __clone() { $this-name =
$that-name; $this-address = “New York”; $this-id = self::$id++; } } $obj
= new MyCloneable(); $obj-name = “Hello”; $obj-address = “Tel-Aviv”;
print $obj-id . “/n”; $obj = $obj-__clone(); print $obj-id . “/n”;
print $obj-name . “/n”; print $obj-address . “/n”; ? 统一的构造方法
ZEND引擎允许开发者定义类的构造方法。具有构造方法的类在新建时会首先调用构造方法,构造
方法适用于在正式使用该类前进行的初始化。
在PHP4中,构造方法的名称与类名相同。由于在派生类中调用父类的作法比较普遍,因此导致在
PHP4中当类在一个大型的类继承中进行移动时,处理方式有点笨拙。当一个派生类被移动到一个不同
的父类中时,父类的构造方法名必然是不同的,这样的话派生类中的有关调用父类构造方法的语句需要改写。
PHP5引入了一个定义构造方法的标准方式,通过调用它们的__construct()来定义。
示例: ?php class BaseClass { function __construct() { print “In
BaseClass constructor/n”; } } class SubClass extends BaseClass {
function __construct() { parent::__construct(); print “In SubClass
constructor/n”; } } $obj = new BaseClass(); $obj = new SubClass(); ?
为向后兼容,当PHP5类不能找到__construct()方法时,会通过老的方法也就是类名
来查找构造方法。这意味着唯一可能产生兼容性问题的是在以前的代码中已经使用了
一个名为__construct()的方法名。 析构方法
定义析构方法是十分有用的。析构方法可以记录调试信息,关闭数据库连接,还有做其它的扫尾
工作。PHP4中并无此机制,尽管PHP已支持注册在请求结束时需要运行的函数。
PHP5引入了与其它面向对象语言如Java语言相似的析构方法:当最后一个该对象的引用被清除时,
系统将会在该对象从内存中释放前调用名为__destruct()的析构方法。 示例:
?php class MyDestructableClass { function __construct() { print “In
constructor/n”; $this-name = “MyDestructableClass”; } function
__destruct() { print “Destroying ” . $this-name . “/n”; } } $obj = new
MyDestructableClass(); ?
和构造方法相似,引擎将不调用父类的析构方法,为调用该方法,你需要在子
类的析构方法中通过parent::__destruct()语句进行调用。 常量 PHP 5
引入了类常量定义: ?php class Foo { const constant = “constant”; } echo
“Foo::constant = ” . Foo::constant . “/n”; ?
PHP5允许常量中包含表达式,但在编译时常量中的表达式将被计算,
因此常量不能在运行中改变它的值。 ?php class Bar { const a = 10; const b
= 11; const c = a | b; } ?
以前代码中的用户自定义类或方法中虽然未定义”const”关键字,
但无需修改就可以运行。 异常(Exceptions)
PHP4中没异常处理,PHP5引入了与其它与语言相似的异常处理模型。 ?php class
MyExceptionFoo extends Exception { function __construct($exception) {
parent::__construct($exception); } } try { throw new
MyExceptionFoo(“Hello”); } catch (MyExceptionFoo $exception) { print
$exception-getMessage(); } ?
以前代码中的用户自定义类或方法中虽未定义’catch’, ‘throw’ 和
‘try’关键字,但无需修改 就可以运行。 函数返回对象值
在PHP4中,函数不可能返回对象的值并对返回的对象进行方法调用,随着Zend
Engine 2 的出现,以下调用成为可能: ?php class Circle { function draw()
{ print “Circle/n”; } } class Square { function draw() { print
“Square/n”; } } function ShapeFactoryMethod($shape) { switch ($shape) {
case “Circle”: return new Circle(); case “Square”: return new Square();
} } ShapeFactoryMethod(“Circle”)-draw();
ShapeFactoryMethod(“Square”)-draw(); ? 静态类中的静态成员变量可初始化
例如: ?php class foo { static $my_static = 5; } print
foo::$my_static; ? 静态方法(Static Methods)
PHP5引入了关键字’static’来定义一个静态方法,这样可以从对象外进行调用。
例如: ?php class Foo { public static function aStaticMethod() { // …
} } Foo::aStaticMethod(); ?
虚拟变量$this在被定义为静态(static)的方法中无效。 Instanceof PHP5引入了
“instanceof“关键字来确定一个对象是否是某一个对象的实例,或某一个对象的派生,或使用了某一个接口。
示例: ?php class baseClass { } $a = new baseClass; if ($a instanceof
basicClass) { echo “Hello World”; } ? 静态函数变量(Static function
variables)
所有的静态变量现在在编译时进行处理,这允许开发者通过引用来指定静态变量。这个变化提高了效率但意味着不可能对静态变量进行间接引用。
函数中通过引用方式传递的参数允许有默认值 例如: ?php function
my_function(&$var = null) { if ($var === null) { die(“$var needs to
have a value”); } } ? __autoload()
在初始化一个未定义的类时,__autoload()拦截函数将被自动调
用。类名将作为__autoload()拦截函数唯一参数传递给它。 例如: ?php
function __autoload($className) { include_once $className . “.php”; }
$object = new ClassName; ? 方法和属性调用的重载
所有方法调用和属性访问都可以通用 __call(), __get() 和
__set()方法来重载。 例: __get() 和 __set() ?php class Setter {
public $n; public $x = array(“a” = 1, “b” = 2, “c” = 3); function
__get($nm) { print “Getting [$nm]/n”; if (isset($this-x[$nm])) {
$r = $this-x[$nm]; print “Returning: $r/n”; return $r; } else { print
“Nothing!/n”; } } function __set($nm, $val) { print “Setting [$nm]
to $val/n”; if (isset($this-x[$nm])) { $this-x[$nm] = $val; print
“OK!/n”; } else { print “Not OK!/n”; } } } $foo = new Setter(); $foo-n =
1; $foo-a = 100; $foo-a++; $foo-z++; var_dump($foo); ? 示例: __call()
?php class Caller { var $x = array(1, 2, 3); function __call($m, $a) {
print “Method $m called:/n”; var_dump($a); return $this-x; } } $foo =
new Caller(); $a = $foo-test(1, “2”, 3.4, true); var_dump($a); ?

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图