PHPV5.3中的新特性,第1部分:物件介面的變化

測試5555發表於2011-12-19

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),以及針對某些情況的特定佇列和堆實現(SplMinHeapSplMaxHeap 和 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 成為一種更加強大的物件導向程式語言。


相關文章