PHP V5.3 中的新特性,第 一 部分: 對象介面的變化
來源:互聯網
上載者:User
PHP V5.3 中的新特性,第 1 部分: 對象介面的變化
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 成為一種更加強大的物件導向程式設計語言。