自 PHP 5.3.0 起,PHP 增長了一個叫作後期靜態綁定的功能,用於在繼承範圍內引用靜態調用的類。 雖然也能夠調用非靜態方法,可是不會在運行時綁定。php
static 再也不只是簡單的靜態修飾關鍵字。而是還能夠調用類的靜態方法,非靜態方法,爲何靜態非靜態要分開說呢,由於調用的效果是不同的。函數
class A { protected $name = 'A'; static $alias = 'a'; const HASH = 'md5'; public function dd() { echo 'name:' . $this->name . PHP_EOL; echo 'self-alias:' . self::$alias . PHP_EOL; echo 'static-alias:' . static::$alias . PHP_EOL; // 後期靜態綁定 echo 'self-hash:' . self::HASH . PHP_EOL; echo 'static-hash:' . static::HASH . PHP_EOL; // 後期靜態綁定 var_dump(new self); var_dump($this); var_dump(new static); } public static function who () { echo __CLASS__ ; echo ' [ This is A ]'; echo PHP_EOL; } public static function test () { static:: who (); // 後期靜態綁定從這裏開始 } public static function test2 () { self:: who (); } } class B extends A { protected $name = 'B'; static $alias = 'b'; const HASH = 'sha1'; public static function who () { echo __CLASS__ ; echo ' [ This is B ]'; echo PHP_EOL; } } (new B)->dd()
結果輸出:this
name:B self-alias:a static-alias:b self-hash:md5 static-hash:sha1 object(admin\controllers\A) protected 'name' => string 'A' (length=1) object(admin\controllers\B) protected 'name' => string 'B' (length=1) object(admin\controllers\B) protected 'name' => string 'B' (length=1)
執行:code
B::who(); B::test(); B::test2();
輸出:對象
B [ This is B ] B [ This is B ] A [ This is A ]
self
和 __CLASS__
,都是對當前類的靜態引用
,取決於定義當前方法所在的類。也就是說,self 寫在哪一個類裏面, 它引用的就是誰。繼承
$this
指向的是實際調用時的對象,也就是說,實際運行過程當中,誰調用了類的屬性或方法,$this
指向的就是哪一個對象。但 $this
不能訪問類的靜態屬性和常量,且 $this
不能存在於靜態方法中。md5
static
關鍵字除了能夠聲明類的靜態成員(屬性和方法)外,還有一個很是重要的做用就是後期靜態綁定。get
parent
,是對當前類的父類的靜態引用。string
self
能夠用於訪問類的靜態屬性、靜態方法和常量,但 self
指向的是當前定義所在的類,這是 self
的限制。hash
$this
指向的對象所屬的類和 static
指向的類相同。
static
能夠用於靜態或非靜態方法中,也能夠訪問類的靜態屬性、靜態方法、常量和非靜態方法,但不能訪問非靜態屬性。
靜態調用時,static
指向的是實際調用時的類;非靜態調用時,static
指向的是實際調用時的對象所屬的類。
後期靜態綁定(也叫延遲靜態綁定),可用於在繼承範圍內引用靜態調用的類,也就是代碼運行時最初調用的類。
確切地說,static 後期靜態綁定的工做原理是存儲了上一個非轉發調用(non-forwarding call)的類名。
當進行靜態方法調用時,該類名(static指向的類名)爲明確指定的那個(一般是 :: 運算符的左側部分),即實際調用時的類。
如:上面例子中的
A::test(); //A::test() 調用的是 static::who(),這裏static指向的即是A,因此執行的就是A::who(); B::test(); //A::test() 調用的是 static::who(),這裏static指向的即是B,因此執行的就是B::who();
static指向的類名,指向的就是實際調用的類
self 能夠用於訪問類的靜態屬性、靜態方法和常量,但 self 指向的是當前定義所在的類,這是 self 的限制。
static 也能夠用於訪問類的靜態屬性、靜態方法和常量,static 指向的是實際調用時的類。當進行非靜態方法調用時,該類名(static指向的類名)爲該對象所屬的類,即實際調用時的對象所屬的類。
所謂的轉發調用(forwarding call)指的是經過如下幾種方式進行的靜態調用:self::,parent::,static:: 以及 forward_static_call() 。
可用 get_called_class() 函數來獲取被調用的方法所在的類名。
如下四種形式的調用,都是轉發調用:
self:: parent:: static:: forward_static_call()
除此以外的調用,就是非轉發調用。
後期靜態綁定的工做原理是存儲了上一個非轉發調用(non-forwarding call)的類名。
經過具體的類名或具體的對象進行的調用都是非轉發調用。
非靜態環境下的私有方法的查找順序
在非靜態環境下,在類的非靜態方法中,使用 $this 和 static 調用類的私有方法時,執行方式有所不一樣。
具體來講,$this 會先到所在定義範圍內尋找私有方法,再到它指向的對象所屬的類中尋找私有方法,而後尋找公有方法,最後到所在定義範圍內尋找公共方法。只要找到了匹配的方法,就調用,並中止查找。
而 static 則是先到它指向的類中尋找私有方法,再尋找共有方法;而後到所在定義範圍內尋找私有方法,再尋找共有方法。只要找到了匹配的方法,就調用,並中止查找。
下面是一個例子:
<?php class A { private function foo () { var_dump($this); echo '--'; var_dump(new static); echo '--'; echo __CLASS__; echo '--'; echo get_called_class(); echo '<br>'; } public function test () { $this -> foo (); static:: foo (); echo '<br>'; } } class B extends A { } class C extends A { private function foo () { echo 'this is C'; } } (new B())->test(); (new C())->test(); 輸出結果爲: object(B)#1 (0) { } --object(B)#2 (0) { } --A--B object(B)#1 (0) { } --object(B)#2 (0) { } --A--B object(C)#1 (0) { } --object(C)#2 (0) { } --A--C Fatal error: Uncaught Error: Call to private method C::foo() from context 'A'
後期靜態綁定的解析會一直到取得一個徹底解析了的靜態調用爲止。若是靜態調用使用了 parent:: 或者 self:: 等轉發調用的形式,將會轉發調用信息。
<?php 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 (); static::foo(); forward_static_call(['A', 'foo']); echo '<br>'; } public static function who () { echo __CLASS__ . "\n" ; } } class C extends B { public static function who () { echo __CLASS__ . "\n" ; } public static function test2() { self::test(); } } class D extends C { public static function who () { echo __CLASS__ . "\n" ; } } B::foo(); B::test(); C::foo(); C::test(); D::foo(); D::test2();
以上的輸出結果爲:
B A B B B B C A C C C C D A D D D D
static 後期靜態綁定的工做原理是存儲了上一個非轉發調用(non-forwarding call)的類名。請記住這句話。
下面的例子是非轉發調用。
A::foo(); // 輸出 A B::foo(); // 輸出 B C::foo(); // 輸出 C
後期靜態綁定 static ,是定義在了 foo() 方法中,哪一個類經過非轉發調用的形式調用 foo() 方法, foo() 方法中的 static 指向的就是哪一個類。
可是,若是經過轉發調用的形式,調用 foo() 方法,如:
parent :: foo (); self :: foo (); static::foo(); forward_static_call(['A', 'foo']);
那麼,就以轉發調用代碼所在的方法 test() 爲準,哪一個類經過非轉發調用的形式調用 test() 方法, foo() 方法中的 static 指向的就是哪一個類。
假如調用 test() 方法時,也採用了轉發調用的形式,如:
public static function test2() { self::test(); }
那麼,就以 test2() 方法爲準 ... 依次類推。
也就是說,在使用了後期靜態綁定的基類中,後期靜態綁定所在的方法若是被轉發調用,則 static 的指向,會一直向上追溯,直到遇到非轉發調用的形式。