詳解PHP后期靜態(tài)綁定分析與應(yīng)用
基礎(chǔ)知識(shí)
1. 范圍解析操作符 (::)
- 可以用于訪問(wèn)靜態(tài)成員,類常量,還可以用于覆蓋類中的屬性和方法。
- self,parent 和 static 這三個(gè)特殊的關(guān)鍵字是用于在類定義的內(nèi)部對(duì)其屬性或方法進(jìn)行訪問(wèn)的。
- parent用于調(diào)用父類中被覆蓋的屬性或方法(出現(xiàn)在哪里,就將解析為相應(yīng)類的父類)。
- self用于調(diào)用本類中的方法或?qū)傩裕ǔ霈F(xiàn)在哪里,就將解析為相應(yīng)的類;注意與$this區(qū)別,$this指向當(dāng)前實(shí)例化的對(duì)象)。
- 當(dāng)一個(gè)子類覆蓋其父類中的方法時(shí),PHP 不會(huì)調(diào)用父類中已被覆蓋的方法。是否調(diào)用父類的方法取決于子類。
2. PHP內(nèi)核將類的繼承實(shí)現(xiàn)放在了"編譯階段"
<?php class A{ const H = 'A'; const J = 'A'; static function testSelf(){ echo self::H; //在編譯階段就確定了 self解析為 A } } class B extends A{ const H = "B"; const J = 'B'; static function testParent(){ echo parent::J; //在編譯階段就確定了 parent解析為A } /* 若重寫testSelf則能輸出“B”, 且C::testSelf()也是輸出“B” static function testSelf(){ echo self::H; } */ } class C extends B{ const H = "C"; const J = 'C'; } B::testParent(); B::testSelf(); echo "\n"; C::testParent(); C::testSelf();
運(yùn)行結(jié)果:
AA
AA
結(jié)論:
self::和parent::出現(xiàn)在某個(gè)類X的定義中,則將被解析為相應(yīng)的類X,除非在子類中覆蓋父類的方法。
3.Static(靜態(tài))關(guān)鍵字
作用:
- 在函數(shù)體內(nèi)的修飾變量的static關(guān)鍵字用于定義靜態(tài)局部變量。
- 用于修飾類成員函數(shù)和成員變量時(shí)用于聲明靜態(tài)成員。
- (PHP5.3之后)在作用域解析符(::)前又表示靜態(tài)延遲綁定的特殊類。
例子:
定義靜態(tài)局部變量(出現(xiàn)位置:局部函數(shù)中)
特征:靜態(tài)變量?jī)H在局部函數(shù)域中存在,但當(dāng)程序執(zhí)行離開(kāi)此作用域時(shí),其值并不丟失。
<?php function test() { static $count = 0; $count++; echo $count; if ($count < 10) { test(); } $count--; }
定義靜態(tài)方法,靜態(tài)屬性
a)聲明類屬性或方法為靜態(tài),就可以不實(shí)例化類而直接訪問(wèn)。
b)靜態(tài)屬性不能通過(guò)一個(gè)類已實(shí)例化的對(duì)象來(lái)訪問(wèn)(但靜態(tài)方法可以)
c)如果沒(méi)有指定訪問(wèn)控制,屬性和方法默認(rèn)為公有。
d)由于靜態(tài)方法不需要通過(guò)對(duì)象即可調(diào)用,所以偽變量 $this 在靜態(tài)方法中不可用。
e)靜態(tài)屬性不可以由對(duì)象通過(guò) -> 操作符來(lái)訪問(wèn)。
f)用靜態(tài)方式調(diào)用一個(gè)非靜態(tài)方法會(huì)導(dǎo)致一個(gè) E_STRICT 級(jí)別的錯(cuò)誤。
g)就像其它所有的 PHP 靜態(tài)變量一樣,靜態(tài)屬性只能被初始化為文字或常量,不能使用表達(dá)式。所以可以把靜態(tài)屬性初始化為整數(shù)或數(shù)組,但不能初始化為另一個(gè)變量或函數(shù)返回值,也不能指向一個(gè)對(duì)象。
a.靜態(tài)方法例子(出現(xiàn)位置: 類的方法定義)
<?php class Foo { public static function aStaticMethod() { // ... } } Foo::aStaticMethod(); $classname = 'Foo'; $classname::aStaticMethod(); // 自PHP 5.3.0后,可以通過(guò)變量引用類 ?>
b.靜態(tài)屬性例子(出現(xiàn)位置:類的屬性定義)
<?php class Foo { public static $my_static = 'foo'; public function staticValue() { return self::$my_static; //self 即 FOO類 } } class Bar extends Foo { public function fooStatic() { return parent::$my_static; //parent 即 FOO類 } } print Foo::$my_static . "\n"; $foo = new Foo(); print $foo->staticValue() . "\n"; print $foo->my_static . "\n"; // Undefined "Property" my_static print $foo::$my_static . "\n"; $classname = 'Foo'; print $classname::$my_static . "\n"; // As of PHP 5.3.0 print Bar::$my_static . "\n"; $bar = new Bar(); print $bar->fooStatic() . "\n"; ?>
c.用于后期靜態(tài)綁定(出現(xiàn)位置: 類的方法中,用于修飾變量或方法)
下面詳細(xì)分析
后期靜態(tài)綁定(late static binding)
自 PHP 5.3.0 起,PHP 增加了一個(gè)叫做后期靜態(tài)綁定的功能,用于在繼承范圍內(nèi)引用靜態(tài)調(diào)用的類。
1.轉(zhuǎn)發(fā)調(diào)用與非轉(zhuǎn)發(fā)調(diào)用
轉(zhuǎn)發(fā)調(diào)用 :
指的是通過(guò)以下幾種方式進(jìn)行的靜態(tài)調(diào)用:self::,parent::,static:: 以及 forward_static_call()。
非轉(zhuǎn)發(fā)調(diào)用 :
明確指定類名的靜態(tài)調(diào)用(例如Foo::foo())
非靜態(tài)調(diào)用(例如$foo->foo())
2.后期靜態(tài)綁定工作原理
原理:存儲(chǔ)了在上一個(gè)“非轉(zhuǎn)發(fā)調(diào)用”(non-forwarding call)中的類名。意思是當(dāng)我們調(diào)用一個(gè)轉(zhuǎn)發(fā)調(diào)用的靜態(tài)調(diào)用時(shí),實(shí)際調(diào)用的類是上一個(gè)非轉(zhuǎn)發(fā)調(diào)用的類。
例子分析:
<?php class A { public static function foo() { echo __CLASS__."\n"; static::who(); } public static function who() { echo __CLASS__."\n"; } } class B extends A { public static function test() { echo "A::foo()\n"; A::foo(); echo "parent::foo()\n"; parent::foo(); echo "self::foo()\n"; self::foo(); } public static function who() { echo __CLASS__."\n"; } } class C extends B { public static function who() { echo __CLASS__."\n"; } } C::test(); /* * C::test(); //非轉(zhuǎn)發(fā)調(diào)用 ,進(jìn)入test()調(diào)用后,“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為C * * //當(dāng)前的“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為C * public static function test() { * A::foo(); //非轉(zhuǎn)發(fā)調(diào)用, 進(jìn)入foo()調(diào)用后,“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為A,然后實(shí)際執(zhí)行代碼A::foo(), 轉(zhuǎn) 0-0 * parent::foo(); //轉(zhuǎn)發(fā)調(diào)用, 進(jìn)入foo()調(diào)用后,“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為C, 此處的parent解析為A ,轉(zhuǎn)1-0 * self::foo(); //轉(zhuǎn)發(fā)調(diào)用, 進(jìn)入foo()調(diào)用后,“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為C, 此處self解析為B, 轉(zhuǎn)2-0 * } * * * 0-0 * //當(dāng)前的“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為A * public static function foo() { * static::who(); //轉(zhuǎn)發(fā)調(diào)用, 因?yàn)楫?dāng)前的“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為A, 故實(shí)際執(zhí)行代碼A::who(),即static代表A,進(jìn)入who()調(diào)用后,“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名依然為A,因此打印 “A” * } * * 1-0 * //當(dāng)前的“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為C * public static function foo() { * static::who(); //轉(zhuǎn)發(fā)調(diào)用, 因?yàn)楫?dāng)前的“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為C, 故實(shí)際執(zhí)行代碼C::who(),即static代表C,進(jìn)入who()調(diào)用后,“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名依然為C,因此打印 “C” * } * * 2-0 * //當(dāng)前的“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為C * public static function foo() { * static::who(); //轉(zhuǎn)發(fā)調(diào)用, 因?yàn)楫?dāng)前的“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名為C, 故實(shí)際執(zhí)行代碼C::who(),即static代表C,進(jìn)入who()調(diào)用后,“上一次非轉(zhuǎn)發(fā)調(diào)用”存儲(chǔ)的類名依然為C,因此打印 “C” * } */ 故最終結(jié)果為: A::foo() A A parent::foo() A C self::foo() A C
3.更多靜態(tài)后期靜態(tài)綁定的例子
a)Self, Parent 和 Static的對(duì)比
<?php class Mango { function classname(){ return __CLASS__; } function selfname(){ return self::classname(); } function staticname(){ return static::classname(); } } class Orange extends Mango { function parentname(){ return parent::classname(); } function classname(){ return __CLASS__; } } class Apple extends Orange { function parentname(){ return parent::classname(); } function classname(){ return __CLASS__; } } $apple = new Apple(); echo $apple->selfname() . "\n"; echo $apple->parentname() . "\n"; echo $apple->staticname(); ?> 運(yùn)行結(jié)果: Mango Orange Apple
b)使用forward_static_call()
<?php class Mango { const NAME = 'Mango is'; public static function fruit() { $args = func_get_args(); echo static::NAME, " " . join(' ', $args) . "\n"; } } class Orange extends Mango { const NAME = 'Orange is'; public static function fruit() { echo self::NAME, "\n"; forward_static_call(array('Mango', 'fruit'), 'my', 'favorite', 'fruit'); forward_static_call('fruit', 'my', 'father\'s', 'favorite', 'fruit'); } } Orange::fruit('NO'); function fruit() { $args = func_get_args(); echo "Apple is " . join(' ', $args). "\n"; } ?> 運(yùn)行結(jié)果: Orange is Orange is my favorite fruit Apple is my father's favorite fruit
c)使用get_called_class()
<?php class Mango { static public function fruit() { echo get_called_class() . "\n"; } } class Orange extends Mango { // } Mango::fruit(); Orange::fruit(); ?> 運(yùn)行結(jié)果: Mango Orange
應(yīng)用
前面已經(jīng)提到過(guò)了,引入后期靜態(tài)綁定的目的是:用于在繼承范圍內(nèi)引用靜態(tài)調(diào)用的類。
所以, 可以用后期靜態(tài)綁定的辦法解決單例繼承問(wèn)題。
先看一下使用self是一個(gè)什么樣的情況:
<?php // new self 得到的單例都為A。 class A { protected static $_instance = null; protected function __construct() { //disallow new instance } protected function __clone(){ //disallow clone } static public function getInstance() { if (self::$_instance === null) { self::$_instance = new self(); } return self::$_instance; } } class B extends A { protected static $_instance = null; } class C extends A{ protected static $_instance = null; } $a = A::getInstance(); $b = B::getInstance(); $c = C::getInstance(); var_dump($a); var_dump($b); var_dump($c); 運(yùn)行結(jié)果: E:\code\php_test\apply\self.php:37: class A#1 (0) { } E:\code\php_test\apply\self.php:38: class A#1 (0) { } E:\code\php_test\apply\self.php:39: class A#1 (0) { }
通過(guò)上面的例子可以看到,使用self,實(shí)例化得到的都是類A的同一個(gè)對(duì)象
再來(lái)看看使用static會(huì)得到什么樣的結(jié)果
<?php // new static 得到的單例分別為D,E和F。 class D { protected static $_instance = null; protected function __construct(){} protected function __clone() { //disallow clone } static public function getInstance() { if (static::$_instance === null) { static::$_instance = new static(); } return static::$_instance; } } class E extends D { protected static $_instance = null; } class F extends D{ protected static $_instance = null; } $d = D::getInstance(); $e = E::getInstance(); $f = F::getInstance(); var_dump($d); var_dump($e); var_dump($f); 運(yùn)行結(jié)果: E:\code\php_test\apply\static.php:35: class D#1 (0) { } E:\code\php_test\apply\static.php:36: class E#2 (0) { } E:\code\php_test\apply\static.php:37: class F#3 (0) { }
可以看到,使用static可以解決self時(shí)出現(xiàn)的單例繼承問(wèn)題。
相關(guān)文章
uni-app路由配置文件pages.json平臺(tái)化拆分
這篇文章主要為大家介紹了uni-app路由配置文件pages.json平臺(tái)化拆分示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01高性能JavaScript循環(huán)語(yǔ)句和條件語(yǔ)句
這篇文章主要為大家介紹了高性能JavaScript循環(huán)語(yǔ)句和條件語(yǔ)句,感興趣的小伙伴們可以參考一下2016-01-01iframe自適應(yīng)寬度、高度 ie6 7 8,firefox 3.86下測(cè)試通過(guò)
近期需要一個(gè)iframe自適應(yīng)高度的東西,在網(wǎng)上找了很多,都不能用……一看大體的日期都是大概 2008年前后的其他近期的基本都是以前的轉(zhuǎn)載,所以只好自己動(dòng)手了。2010-07-07JS循環(huán)發(fā)送請(qǐng)求時(shí)控制請(qǐng)求并發(fā)數(shù)實(shí)例
這篇文章主要介紹了JS循環(huán)發(fā)送請(qǐng)求時(shí)控制請(qǐng)求并發(fā)數(shù)實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12javascript實(shí)現(xiàn)網(wǎng)頁(yè)中涉及的簡(jiǎn)易運(yùn)動(dòng)(改變寬高、透明度、位置)
這篇文章主要介紹了javascript實(shí)現(xiàn)網(wǎng)頁(yè)中涉及的簡(jiǎn)易運(yùn)動(dòng),比如改變寬高、透明度、位置等,感興趣的小伙伴們可以參考一下2015-11-11JS常見(jiàn)面試試題總結(jié)【去重、遍歷、閉包、繼承等】
這篇文章主要介紹了JS常見(jiàn)面試試題,總結(jié)分析了javascript去重、遍歷、閉包、繼等相關(guān)算法與操作技巧,需要的朋友可以參考下2019-08-08jsonp的簡(jiǎn)單介紹以及其安全風(fēng)險(xiǎn)
JSONP原理就是動(dòng)態(tài)插入帶有跨域url的script標(biāo)簽,然后調(diào)用回調(diào)函數(shù),把我們需要的json數(shù)據(jù)作為參數(shù)傳入,通過(guò)一些邏輯把數(shù)據(jù)顯示在頁(yè)面上,這篇文章主要給大家介紹了關(guān)于jsonp的簡(jiǎn)單介紹以及其安全風(fēng)險(xiǎn)的相關(guān)資料,需要的朋友可以參考下2022-01-01