新的对象模型

php中的对象处理部分已完全重写,具有更佳的性能和更多的功能。在先前的php版本中,对象被当做原始的简单类型

(如integer和string)来处理,这种方法的缺点是当变量被赋值或作为参数传递时,得到的是对象拷贝。而在新版本中,

对象是通过句柄来引用的,而不是通过对象的值(句柄想象为对象的标识符)。

很多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关键字(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 引入了类常量(per-class constants)定义:

<?php

class foo {

const constant = “constant”;

}

echo “foo::constant = ” . foo::constant . “\n”;

?>

php5允许常量中包含表达式,但在编译时常量中的表达式将被计算,

因此常量不能在运行中改变它的值。

<?php

class bar {

const a = 1<<0;

const b = 1<<1;

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

(zend引擎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()拦截函数(interceptor function)将被自动调

用。类名将作为__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);

?>