後期靜態繫結在PHP中的使用

airland發表於2021-09-09

什麼叫後期靜態繫結呢?其實我們在之前的文章[PHP中的static]中已經說過這個東西了。今天我們還是再次深入的理解一下這個概念。

首先,我們透過一段程式碼來引入後期靜態繫結這一概念:

class A
{
    public static function who()
    {
        echo __CLASS__, PHP_EOL;
    }
    public static function test()
    {
        self::who();
    }
}

class B extends A
{
    public static function who()
    {
        echo __CLASS__, PHP_EOL;
    }
}

B::test(); // A

在這段程式碼中,我們使用了self關鍵字,當使用B類呼叫test()靜態方法時,self指向的是A類的who()方法,因此,輸出的是A。別激動,這是普通的靜態繫結。self關鍵字呼叫的內容取決於它定義時所在的類。也就是說不管怎麼繼承,用哪個子類來呼叫test()方法,self關鍵字都會呼叫的是A類的who()方法。

而後期靜態繫結呢?其實就有點像例項化的類物件,每個例項化的物件,呼叫的都是自身,而不是父類的屬性方法。普通的靜態呼叫可不是這樣,但是現實中我們又有這樣的需求,就像例項化物件的呼叫方式一樣來呼叫靜態屬性方法,這時,我們就可以使用static關鍵字來實現後期靜態繫結。

class C
{
    public static function who()
    {
        echo __CLASS__, PHP_EOL;
    }
    public static function test()
    {
        static::who();
    }
}

class D extends C
{
    public static function who()
    {
        echo __CLASS__, PHP_EOL;
    }
}

D::test(); // D

當使用static關鍵字後,這裡D類呼叫的test()方法內部呼叫的who()就是D類自己了。

官方文件中的定義如下:

當進行靜態方法呼叫時,該類名即為明確指定的那個(通常在 :: 運算子左側部分);當進行非靜態方法呼叫時,即為該物件所屬的類。

該功能從語言內部角度考慮被命名為“後期靜態繫結”。“後期繫結”的意思是說,static:: 不再被解析為定義當前方法所在的類,而是在實際執行時計算的。也可以稱之為“靜態繫結”,因為它可以用於(但不限於)靜態方法的呼叫。

除了self和static關鍵字外,我們還有一個parent關鍵字,這個關鍵字的意義就很明顯了,呼叫父類的靜態內容。我們同時用三個關鍵字一起來進行測試:

class E
{
    public static function who()
    {
        echo __CLASS__, PHP_EOL;
    }
    public static function test()
    {
        self::who();
        static::who();
    }
}

class F extends E
{
    public static function who()
    {
        echo __CLASS__, PHP_EOL;
    }
}

class G extends F
{
    public static function who()
    {
        parent::who();
        echo __CLASS__, PHP_EOL;
    }
}

G::test();

// E
// F
// G

最後,我們再來看兩個PHP的方法,一個是get_called_class()方法,用來獲取當前呼叫的是哪個類。在靜態方法中可以根據呼叫方式判斷當前類是哪個類來進行其他的業務邏輯操作。另一個是forward_static_call()方法,用於靜態方法的呼叫。

class H
{
    public static function who()
    {
        echo __CLASS__ . ':' . join(',', func_get_args()), PHP_EOL;
    }
    public static function test()
    {
        echo get_called_class(), PHP_EOL;
        forward_static_call('who', 'a', 'b'); // xxx:a,b
        forward_static_call(['I', 'who'], 'c', 'd'); // I:c,d
        forward_static_call_array(['H', 'who'], ['e', 'f']); // H:e,f
    }
}

class I extends H
{
    public static function who()
    {
        echo __CLASS__ . ':' . join(',', func_get_args()), PHP_EOL;
    }
}

function who()
{
    echo 'xxx:' . join(',', func_get_args()), PHP_EOL;
}

H::test(); // H
// xxx:a,b
// I:c,d
// H:e,f
I::test(); // I
// xxx:a,b
// I:c,d
// H:e,f

注意,如果forward_static_call()不指定類名的話,將呼叫全域性的方法。forward_static_call_array()則是將引數使用陣列進行傳遞。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/964/viewspace-2827023/,如需轉載,請註明出處,否則將追究法律責任。

相關文章