新的物件模型
PHP中的物件處理部分已完全重寫,具有更佳的效能和更多的功能。在先前的PHP版本中,物件被當做原始的簡單類型
(如integer和string)來處理,這種方法的缺點是當變數被賦值或作為參數傳遞時,得到的是物件拷貝。而在新版本中,
物件是透過句柄來引用的,而不是透過物件的值(句柄想像為物件的識別碼)。
許多PHP程式設計師可能未意識到舊的物件模型的“copying quirks“,因此以前的大多數PHP程式將不需要做任何更改
即可運行,或只做很少的改動。
私有和保護成員
PHP 5引進了私有和保護成員變量,它們可以定義可視化的類別屬性。
範例
保護成員變數能在該類別的子類別中被訪問,而私有成員變數只能在所屬類別中被存取。
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() {
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->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)中,私有方法和保護方法也被引入。
範例:
private function aPrivateMethod() {
echo "Foo::aPrivateMethod() called.n";
}
protected function atected( {
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 {
}
class ImplementedClass extends AbstractClass {
}
class ImplementedClass extends AbstractClass {
public) >echo "ImplementedClass::test() called.n";
}
}
$o = new ImplementedClass;$o->test();
?>
抽象類別不能實例化。
舊的程式碼中的使用者自訂類別或方法中雖未定義"abstract」關鍵字,但無需修改即可運行。
介面(Interfaces)
ZEND引擎2.0引入了介面。一個類別可以實現任意的介面列表。
例如:
舊的程式碼中的使用者定義類別或方法中雖然沒有定義"interface」關鍵字,但無需修改即可正常運作。
類別類型提示(Class Type Hints)
在保留類別無需定義類型的同時,PHP 5引入了類別類型提示來聲明,以期望把物件的類別透過參數傳遞給一個方法。
例如:
a($b);$a->b($b );?>
這些類別類型提示不是像一些需要類型定義的語言那樣在編譯中進行檢查,而是在執行時進行檢查。這表示:
is equivalent to:
這種語法只用於物件或類,不適用於內建(built-in)類型。
Final關鍵字(final)
PHP 5引入了「final」關鍵字以定義在子類別中不能被覆寫的成員或方法。
例:
class Foo { final function bar() { // ... }}?>
先前所寫程式碼中的使用者自訂類別或方法中雖未定義"final"關鍵字,但無需修改就可以運行了。
物件複製(Object Cloning)
PHP 4在物件被複製時,使用者無法判斷執行那個拷貝建構子。在複製時,PHP 4根據物件的屬性
一位一位地複製一個相同的複製品。
每次都要建立一個完全一樣的複製品並不總是我們想要的。一個很好的複製構造例子是,當有
一個代表一個GTK窗口的對象,它擁有該窗口的所有資源,當你建立一個拷貝時,你可能需要一
個新的窗口,它擁有原始視窗的所有屬性,但需要擁有新視窗的資源。另外一個例子是你有一個
對象引用了另一個對象,當你複製父對象時,你希望建立那個引用對象的新實例,以使復製品有一個單獨的拷貝。
對一個物件的拷貝透過呼叫物件的__clone()方法完成:
$copy_of_object = $object->__clone();
?>
當開發者要求建立一個物件的新的拷貝時,ZEND引擎會檢查是否已經定義了__clone()方法。如果未定義
的話,它會呼叫一個預設的__clone()方法來複製該物件的所有屬性。如果定義了該方法,則該方法會負責
在拷貝中設定必要的屬性。為使用方便,引擎會提供一個函數從來源物件匯入所有的屬性,這樣它就可
以先得到一個具有值的來源物件拷貝,然後只需要對需要改變的屬性進行覆寫即可。
例:
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()來定義。
示例:
class BaseClass {
function __construct() {
print "In BaseClass constructorn";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructorn";
}
}
$obj = new BaseClass();
$obj = new SubClass();
?>
為向後相容,當PHP5類別無法找到__construct()方法時,會經過舊的方法也就是類別名稱
來找出建構方法。這意味著唯一可能產生相容性問題的是在先前的程式碼中已經使用了
一個名為__construct()的方法名稱。
析構法
定義析構法是十分有用的。析構方法可以記錄調試信息,關閉資料庫連接,還有做其它的掃尾
工作。 PHP4中並無此機制,儘管PHP已支援註冊在請求結束時需要執行的函數。
PHP5引入了與其它物件導向語言如Java語言相似的析構方法:當最後一個該物件的參考被清除時,
系統將會在該物件從記憶體中釋放前呼叫名為__destruct ()的析構方法。
範例:
class MyDestructableClass {
function __construct() {
print "In constructorn";
$this->name = "MyDestructableClass";
}
function __destruct() {
print "Destroying " . $this->name . "n";
}
}
$obj = new MystructDestructable( );
?>
和構造方法相似,引擎將不調用父類的析構方法,為調用該方法,你需要在子
類的析構方法中通過parent: :__destruct()語句進行呼叫。
常數
PHP 5 引入了類別常數(per-class constants)定義:
class Foo {
const constant = "constant";
}
echo "Foo::constant = " . Foo::constant . "n";
?>
PHP5允許常數中包含表達式,但在編譯時常數中的表達式將被計算,
因此常數不能在運行中改變它的值。
class Bar {
const a = 1const b = 1const c = a | b;
}
?>
以前程式碼中的用戶自訂類別或方法中雖然未定義"const」關鍵字,
但無需修改就可以運行。
異常(Exceptions)
PHP4中沒異常處理,PHP5引入了與其它與語言相似的異常處理模型。
class MyExceptionFoo extends Exception {
function __construct($exception) {
parent::__construct($exception);
}
parent::__construct($exception);
}
}
}
try {
throw new MyExceptionFoo("Hello");
} catch (MyExceptionFoo $exception) {
print $exception->getMessage();
}
print $exception->getMessage();
}
>
>
先前程式碼中的使用者自訂類別或方法中雖未定義'catch', 'throw' 和'try'關鍵字,但無需修改
就可以運行。
函數返回物件值
在PHP4中,函數不可能返回物件的值並對返回的物件進行方法調用,隨著Zend Engine 2
(ZEND引擎2)的出現,以下呼叫成為可能:
class Circle {
function draw() {
print "Circlen";
}
}
class Square {
function draw() {
print "Squaren";
}
}
function ShapeFactoryMethod($shape) {
switch ($shape) { 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'來定義一個靜態方法,這樣可以從物件外呼叫。
例如:
class Foo {
public static function aStaticMethod() {
// ...
}
}
Foo::aStaticMethod();
?>
虛擬變數$this在定義為靜態(static)的方法中無效。
Instanceof
PHP5引入了 「instanceof「關鍵字來決定一個物件是否是某一個物件的實例,或某一個物件的衍生,或使用了某一個介面。
範例:
class baseClass { }
$a = new baseClass;
if ($a instanceof basicClass) {of basicClass) {of basicClass) {of basicClass) {of basicClass)
echo "Hello World";
}
?>
靜態函數變數(Static function variables) 所有的靜態變數現在在編譯時進行編譯時進行處理,這允許開發者透過引用來指定靜態變數。這個變化提高了效率但意味著不可能對靜態變數進行間接引用。
函數中透過引用方式傳遞的參數允許有預設值
例如:
function my_function(&$var = null) {
if ($var === null) {
die("$var needs to have a value");
}
}
?>
__autoload ()
在初始化一個未定義的類別時,__autoload()攔截函數(interceptor function)將會自動調
用。類別名稱將作為__autoload()攔截函數唯一參數傳遞給它。
例如:
function __autoload($className) {
include_once $className . ".php";
}
$object = new ClassName
?>
方法和屬性呼叫的重載
所有方法呼叫和屬性存取都可以通用__call(), __get() 和__set()方法來重載。
例: __get() 且__set()
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: $rn";
return $r;
} else {
print "Nothing!n";
}
}
function __set($nm, $val) {
print "Setting [$nm] to $ valn";
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()
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);
?>