1. 程式人生 > 其它 >PHP 後期靜態繫結

PHP 後期靜態繫結

在看一些框架原始碼或者是某個專案的程式碼時,經常能看到後期靜態繫結的用法
。比如下面這段:

public static function getInstance()
{
    if (is_null(static::$instance)) {
        static::$instance = new static;
    }
    return static::$instance;
}

這裡用到的就是後期靜態繫結。那麼,什麼是後期靜態繫結?

“後期繫結” 的意思是說,static:: 不再被解析為定義當前方法所在的類,而是在實際執行時計算的。

這裡要先說兩個概念,一個是轉發呼叫,另一個是非轉發呼叫。

轉發呼叫
所謂的 “轉發呼叫”(forwarding call)指的是通過以下幾種方式進行的靜態呼叫:self::, parent::, static:: 以及 forward_ static _call ()。即在進行靜態呼叫時未指名類名的呼叫屬於轉發呼叫。

非轉發呼叫
非轉發呼叫其實就是明確指定類名的靜態呼叫(foo::bar ())和非靜態呼叫 ($foo->bar ())。即明確地指定類名的靜態呼叫和非靜態呼叫。

顧名思義,非轉發呼叫前面有類名所以呼叫的函式一定是屬於 “這個類的”,不需要轉到別的類。轉發呼叫就是由於前期的靜態繫結導致在後面呼叫靜態方法時可能 “轉發到其他的類”

在 PHP 的官方文件裡,對於後期靜態繫結是這樣說的:後期靜態繫結工作原理是儲存了在上一個 “非轉發呼叫”(non-forwarding call)中的類名。意思是當我們呼叫一個轉發呼叫的靜態呼叫時,實際呼叫的類是上一個非轉發呼叫的類。

來看兩個例子:

例 1:

class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // 後期靜態繫結從這裡開始
    }
}
class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}
B::test();

以上程式碼會輸出

B
例 2:

class A {
    public static function foo() {
        static::who();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}

class B extends A {
    public static function test() {
        A::foo();
        parent::foo();
        self::foo();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}
class C extends B {
    public static function who() {
        echo __CLASS__."\n";
    }
}

C::test();

以上程式碼會輸出

A
C
C
在這裡主要分析下例 2。

1.C::test (),這是一個非轉發呼叫,因為::前面有類名 C。
2. 進入 test () 方法,有三個靜態呼叫 A::foo (),parent::foo (),self::foo (), 對於這三個靜態呼叫來說,他們的非轉發呼叫類就是 C。
3. 現在執行 A::foo (), 這是一個非轉發呼叫。A::foo () 中的程式碼是 static::who (), 這是一個轉發呼叫,對於這個轉發呼叫來說他的非轉發呼叫類就是不再是 C 而是 A(因為之前執行了 A::foo ())。因此執行的結果為 A
4. 現在執行 parent::foo (), 這是一個轉發呼叫,轉發到哪裡呢?就是它的上一個非轉發呼叫的類,也就是類 C(在步驟 2 中提到的)。在這裡一定要注意雖然在這之前執行了 A::foo (), 但是 parent::foo () 的上一個非轉發呼叫的類任然是類 C。因此執行的結果是 C.
5. 現在執行 self::foo (), 這個和 parent::foo () 一樣都是轉發呼叫,因此也輸出 C。

使用後期靜態繫結的好處

後期靜態繫結目前我看到較多的是用於物件例項化中,在例項化物件時,static 會根據執行時呼叫的類來決定例項化物件,而 self 則是根據所在位置的類來決定例項化物件。當我們只想例項化子類,並且不希望後續在對子類的使用中由於父類的變化對子類產生影響時,後期靜態繫結就能發揮它的作用了。

————————————————
原文作者:JeffreyC
轉自連結:https://learnku.com/articles/8964/understanding-of-static-binding-at-the-later-stage-of-php
版權宣告:著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請保留以上作者資訊和原文連結。