PHP關(guān)鍵字Self、Static和parent的區(qū)別詳解
簡(jiǎn)介
在使用PHP代碼時(shí),您可能經(jīng)常會(huì)遇到parent::、static::和self::。但是當(dāng)你第一次作為一個(gè)開(kāi)發(fā)人員開(kāi)始的時(shí)候,有時(shí)候你會(huì)很困惑,不知道它們是做什么的,以及它們之間的區(qū)別。
在我第一次作為開(kāi)發(fā)人員開(kāi)始工作后的很長(zhǎng)一段時(shí)間里,我認(rèn)為static::和self::是完全一樣的。
parent::是什么?
假設(shè)我們有一個(gè)BaseTestCase類(lèi),它有一個(gè)setUp方法:
class BaseTestCase { public function setUp(): void { echo 'Run base test case set up here...'; } } (new BaseTestCase())->setUp(); // Output is: "Run base test case set up here...';
正如我們所看到的,當(dāng)我們調(diào)用 setUp 方法時(shí),它按預(yù)期運(yùn)行并輸出文本。
現(xiàn)在,讓我們假設(shè)我們想要?jiǎng)?chuàng)建一個(gè)新的FeatureTest
類(lèi)來(lái)繼承BaseTestCase
類(lèi)。如果我們想運(yùn)行FeatureTest
類(lèi)的setUp
方法,我們可以這樣做:
class FeatureTest extends BaseTestCase { // } (new FeatureTest())->setUp(); // Output is: "Run base test case set up here...";
正如我們所看到的,我們沒(méi)有在FeatureTest中定義setUp方法,所以在BaseTestCase中定義的方法將被運(yùn)行。
現(xiàn)在,假設(shè)我們想在運(yùn)行FeatureTest中的setUp方法時(shí)運(yùn)行一些額外的邏輯。例如,如果這些類(lèi)是作為PhpUnit測(cè)試的一部分使用的測(cè)試用例,那么我們可能需要在數(shù)據(jù)庫(kù)中創(chuàng)建模型或設(shè)置測(cè)試值。
一開(kāi)始,你可能(錯(cuò)誤地)認(rèn)為你可以在你的FeatureTest方法中定義setUp方法,然后調(diào)用$this->setUp()。老實(shí)說(shuō),當(dāng)我第一次學(xué)習(xí)編程的時(shí)候,我總是陷入這個(gè)陷阱!
所以我們的代碼可能看起來(lái)像這樣:
class FeatureTest extends BaseTestCase { public function setUp(): void { $this->setUp(); echo 'Run extra feature test set up here...'; } } (new FeatureTest())->setUp();
但是,您會(huì)發(fā)現(xiàn),如果我們運(yùn)行這段代碼,我們最終會(huì)陷入一個(gè)循環(huán),導(dǎo)致您的應(yīng)用程序崩潰。這是因?yàn)槲覀冞f歸地要求setUp
一遍又一遍地調(diào)用它自己。你可能會(huì)得到類(lèi)似這樣的輸出:
Fatal error: Out of memory (allocated 31457280 bytes) (tried to allocate 262144 bytes) in /in/1MXtt on line 15 mmap() failed: [12] Cannot allocate memory mmap() failed: [12] Cannot allocate memory Process exited with code 255.
因此,我們需要告訴PHP在BaseTestCase
中使用setUp
方法,而不是使用$this->setUp()
。為了做到這一點(diǎn),我們可以像這樣用parent::setUp()
替換$this->setUp()
:
class FeatureTest extends BaseTestCase { public function setUp(): void { parent::setUp(); echo 'Run extra feature test set up here...'; } } (new FeatureTest())->setUp(); // Output is: "Run base test case set up here... Run extra feature test set up here...";
現(xiàn)在,正如你所看到的,當(dāng)我們?cè)贔eatureTest類(lèi)中運(yùn)行setUp方法時(shí),我們首先運(yùn)行BaseTestCase中的代碼,然后繼續(xù)運(yùn)行子類(lèi)中定義的其余代碼。
值得注意的是,您并不總是需要將parent::調(diào)用放在方法的頂部。實(shí)際上,您可以將其放置在方法中任何最適合代碼目的的位置。例如,如果你想先在FeatureTest類(lèi)中運(yùn)行你的代碼,然后在BaseTestCase類(lèi)中運(yùn)行,你可以像這樣將parent::setUp()調(diào)用移動(dòng)到方法的底部:
self::是什么?
假設(shè)我們有一個(gè)Model
類(lèi),它有一個(gè)靜態(tài)的connection
屬性和一個(gè)makeConnection
方法。我們還可以想象我們有一個(gè)User
類(lèi),它繼承了Model
類(lèi)并覆蓋了connection
屬性。
這兩個(gè)類(lèi)可能看起來(lái)像這樣:
class Model { public static string $connection = 'mysql'; public function makeConnection(): void { echo 'Making connection to: '.self::$connection; } } class User extends Model { public static string $connection = 'postgres'; }
現(xiàn)在讓我們?cè)趦蓚€(gè)類(lèi)上運(yùn)行makeConnection
方法,看看我們會(huì)得到什么輸出:
(new Model())->makeConnection(); // Output is: "Making connection to mysql" (new User())->makeConnection(); // Output is: "Making connection to mysql";
正如我們所看到的,這兩個(gè)調(diào)用都導(dǎo)致了Model類(lèi)的connection屬性被使用。這是因?yàn)閟elf使用了在方法所在的類(lèi)上定義的屬性。在這兩種情況下,makeConnection方法在Model類(lèi)上是打開(kāi)的,因?yàn)閁ser類(lèi)上不存在一個(gè)方法。
為了進(jìn)一步說(shuō)明這一點(diǎn),我們將向User類(lèi)添加makeConnection方法,如下所示:
class Model { public static string $connection = 'mysql'; public function makeConnection(): void { echo 'Making connection to: '.self::$connection; } } class User extends Model { public static string $connection = 'postgres'; public function makeConnection(): void { echo 'Making connection to: '.self::$connection; } }
現(xiàn)在,如果我們?cè)俅握{(diào)用這兩個(gè)方法,我們會(huì)得到以下輸出:
(new Model())->makeConnection(); // Output is: "Making connection to mysql" (new User())->makeConnection(); // Output is: "Making connection to postgres";
正如您所看到的,對(duì)makeConnection
的調(diào)用現(xiàn)在將使用User
類(lèi)上的connection
字段,因?yàn)檫@是該方法存在的地方。
static::是什么?
現(xiàn)在我們已經(jīng)知道了self::
的作用,讓我們來(lái)看看static::
。
為了更好地理解它的作用,讓我們更新上面的代碼示例,使用static::
而不是self::
,如下所示:
class Model { public static $connection = 'mysql'; public function makeConnection() { echo 'Making connection to: '.static::$connection; } } class User extends Model { public static $connection = 'postgres'; }
如果我們?cè)趦蓚€(gè)類(lèi)上運(yùn)行makeConnection
方法,我們會(huì)得到以下輸出:
(new Model())->makeConnection(); // Output is: "Making connection to mysql" (new User())->makeConnection(); // Output is: "Making connection to postgres";
正如我們所看到的,這個(gè)輸出與我們之前使用self::$connection
時(shí)不同。對(duì)User
類(lèi)上的makeConnection
方法的調(diào)用使用了User
類(lèi)上的connection
屬性,而不是Model
類(lèi)(該方法實(shí)際所屬的類(lèi))。這是由于PHP中一個(gè)名為“后期靜態(tài)綁定”的特性。
根據(jù)PHP文檔:這個(gè)特性被命名為“后期靜態(tài)綁定”,從內(nèi)部的角度考慮。“后期綁定”來(lái)自這樣一個(gè)事實(shí),即static::將不會(huì)使用定義方法的類(lèi)來(lái)解析,而是使用運(yùn)行時(shí)信息來(lái)計(jì)算。它也被稱(chēng)為“靜態(tài)綁定”,因?yàn)樗梢杂糜冢ǖ幌抻冢╈o態(tài)方法調(diào)用。"
因此,在我們的示例中,使用了User
類(lèi)上的connection
屬性,因?yàn)槲覀冊(cè)谕粋€(gè)類(lèi)上調(diào)用了makeConnection
方法。
然而,值得注意的是,如果connection
屬性在User
類(lèi)上不存在,它將回退到使用Model
類(lèi)上的屬性。
什么時(shí)候使用self::或 static::?
現(xiàn)在我們對(duì)self::
和static::
之間的區(qū)別有了一個(gè)大致的了解,讓我們快速介紹一下如何決定在自己的代碼中使用哪一個(gè)。
這一切都取決于您正在編寫(xiě)的代碼的用例。
一般來(lái)說(shuō),我通常會(huì)使用static::
而不是self::
,因?yàn)槲蚁M业念?lèi)是可擴(kuò)展的
例如,假設(shè)我想寫(xiě)一個(gè)類(lèi),我完全打算由子類(lèi)繼承(例如上面示例中的BaseTestCase
類(lèi))。除非我真的想防止子類(lèi)重寫(xiě)屬性或方法,否則我想使用static::
。
這意味著我可以有信心,如果我重寫(xiě)任何靜態(tài)方法或字段,我的子類(lèi)將使用我的重寫(xiě)。我無(wú)法告訴你有多少次我在代碼中遇到了bug,當(dāng)我在父類(lèi)中使用self::
時(shí),然后無(wú)法弄清楚為什么我的子類(lèi)沒(méi)有使用我的重寫(xiě)!
另一方面,一些開(kāi)發(fā)人員可能會(huì)爭(zhēng)辯說(shuō),你應(yīng)該堅(jiān)持使用self::
,因?yàn)槟悴粦?yīng)該真的從類(lèi)繼承。他們可能會(huì)建議你應(yīng)該遵循“組合優(yōu)于繼承”的原則。我不會(huì)深入研究這個(gè)話(huà)題,因?yàn)檫@是未來(lái)的另一篇博客文章。但從廣義上說(shuō),簡(jiǎn)單地說(shuō),這個(gè)原則指出,你應(yīng)該避免通過(guò)將所有邏輯放在父類(lèi)中來(lái)為類(lèi)添加功能,而是通過(guò)用許多更小的類(lèi)來(lái)構(gòu)建類(lèi)來(lái)添加功能。
這意味著如果你遵循這個(gè)原則,你就不需要使用static::
,因?yàn)槟阌肋h(yuǎn)不會(huì)擴(kuò)展你的父類(lèi)。如果你想確保類(lèi)不能被擴(kuò)展,你甚至可以更進(jìn)一步,在定義類(lèi)時(shí)使用final
關(guān)鍵字。使用final
關(guān)鍵字可以防止類(lèi)被繼承,所以它可以減少您對(duì)類(lèi)可能意外擴(kuò)展并引入任何潛在錯(cuò)誤的擔(dān)憂(yōu)。
一般來(lái)說(shuō),最好在編寫(xiě)代碼時(shí)根據(jù)具體情況決定應(yīng)該使用static::
還是self::
。
以上就是PHP關(guān)鍵字Self、Static和parent的區(qū)別詳解的詳細(xì)內(nèi)容,更多關(guān)于PHP關(guān)鍵字Self、Static和parent的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
PHP實(shí)現(xiàn)清除wordpress里惡意代碼
這篇文章主要介紹了PHP實(shí)現(xiàn)清除wordpress里惡意代碼的方法以及相關(guān)代碼和使用方法,有需要的小伙伴可以參考下。2015-10-10用php來(lái)改寫(xiě)404錯(cuò)誤頁(yè)讓你的頁(yè)面更友好
404錯(cuò)誤,很多人都知道,如果要訪(fǎng)問(wèn)的url不存在的時(shí)候就讀取顯示這個(gè)頁(yè)面;以往在處理404寫(xiě)幾行字,有心人對(duì)其稍加美化,首先我來(lái)說(shuō)明一下我的404究竟都能幫我做哪些事情以及PHP如何來(lái)改寫(xiě)你的404錯(cuò)誤頁(yè)2013-01-01smarty模板嵌套之include與fetch性能測(cè)試
使用模板時(shí)難免要碰到模板嵌套的時(shí)候,大家都知道有兩種方法可以實(shí)現(xiàn),以下是個(gè)人早期的一些使用感受。2010-12-12如何阻止網(wǎng)站被惡意反向代理訪(fǎng)問(wèn)(防網(wǎng)站鏡像)
最近有人用小站數(shù)據(jù),利用反向代理技術(shù),做了個(gè)小偷站。用戶(hù)訪(fǎng)問(wèn)的是他的網(wǎng)址,但實(shí)質(zhì)上內(nèi)容數(shù)據(jù)確是我的,這是一起惡意反向代理事件2014-03-03php中用foreach來(lái)操作數(shù)組的代碼
php中用foreach來(lái)操作數(shù)組的代碼,需要的朋友可以參考下。2011-07-07PHP文件讀寫(xiě)操作相關(guān)函數(shù)總結(jié)
這篇文章主要介紹了PHP文件讀寫(xiě)操作相關(guān)函數(shù)總結(jié),本文總結(jié)了fwrite()、fread()、fgets()、fgetc()、file()、readfile() 等函數(shù)的介紹及使用例子,需要的朋友可以參考下2014-11-11php判斷當(dāng)前操作系統(tǒng)類(lèi)型
這篇文章主要介紹了php判斷當(dāng)前操作系統(tǒng)類(lèi)型,分為兩種情況一種是服務(wù)器端,一種是客戶(hù)端,感興趣的小伙伴們可以參考一下2015-10-10php短網(wǎng)址和數(shù)字之間相互轉(zhuǎn)換的方法
這篇文章主要介紹了php短網(wǎng)址和數(shù)字之間相互轉(zhuǎn)換的方法,涉及php操作字符串的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03