當(dāng)前位置:首頁 > 芯聞號 > 充電吧
[導(dǎo)讀](1) autoload機(jī)制概述?? 在使用PHP的OO模式開發(fā)系統(tǒng)時,通常大家習(xí)慣上將每個類的實(shí)現(xiàn)都存放在一個單獨(dú)的文件里,這樣會很容易實(shí)現(xiàn)對類進(jìn)行復(fù)用,同時將來維護(hù)時也很便利。這也是OO設(shè)計(jì)的基本

(1) autoload機(jī)制概述??
在使用PHP的OO模式開發(fā)系統(tǒng)時,通常大家習(xí)慣上將每個類的實(shí)現(xiàn)都存放在一個單獨(dú)的文件里,這樣會很容易實(shí)現(xiàn)對類進(jìn)行復(fù)用,同時將來維護(hù)時也很便利。這也是OO設(shè)計(jì)的基本思想之一。在PHP5之前,如果需要使用一個類,只需要直接使用include/require將其包含進(jìn)來即可。下面是一個實(shí)際的例子:??

/* Person.class.php */???
<?php????
class Person {????
? var $name, $age;????
? function __construct ($name, $age)????
? {????
?? $this->name = $name;????
?? $this->age = $age;????
? }????
}????
?>

/* no_autoload.php */???
<?php????
require_once (”Person.class.php”);????
$person = new Person(”Altair”, 6);????
var_dump ($person);????
?>

在這個例子中,no-autoload.php文件需要使用Person類,它使用了require_once將其包含,然后就可以直接使用Person類來實(shí)例化一個對象。

但隨著項(xiàng)目規(guī)模的不斷擴(kuò)大,使用這種方式會帶來一些隱含的問題:如果一個PHP文件需要使用很多其它類,那么就需要很多的require/include語句,這樣有可能會造成遺漏或者包含進(jìn)不必要的類文件。如果大量的文件都需要使用其它的類,那么要保證每個文件都包含正確的類文件肯定是一個噩夢。

PHP5為這個問題提供了一個解決方案,這就是類的自動裝載(autoload)機(jī)制。autoload機(jī)制可以使得PHP程序有可能在使用類時才自動包含類文件,而不是一開始就將所有的類文件include進(jìn)來,這種機(jī)制也稱為lazy loading。

下面是使用autoload機(jī)制加載Person類的例子:

/* autoload.php */???
<?php????
function __autoload($classname) {????
? require_once ($classname . “class.php”);????
}????
$person = new Person(”Altair”, 6);????
var_dump ($person);????
?>????
通常PHP5在使用一個類時,如果發(fā)現(xiàn)這個類沒有加載,就會自動運(yùn)行__autoload()函數(shù),在這個函數(shù)中我們可以加載需要使用的類。在我們這個簡單的例子中,我們直接將類名加上擴(kuò)展名”.class.php”構(gòu)成了類文件名,然后使用require_once將其加載。從這個例子中,我們可以看出autoload至少要做三件事情,第一件事是根據(jù)類名確定類文件名,第二件事是確定類文件所在的磁盤路徑(在我們的例子是最簡單的情況,類與調(diào)用它們的PHP程序文件在同一個文件夾下),第三件事是將類從磁盤文件中加載到系統(tǒng)中。第三步最簡單,只需要使用include/require即可。要實(shí)現(xiàn)第一步,第二步的功能,必須在開發(fā)時約定類名與磁盤文件的映射方法,只有這樣我們才能根據(jù)類名找到它對應(yīng)的磁盤文件。

因此,當(dāng)有大量的類文件要包含的時候,我們只要確定相應(yīng)的規(guī)則,然后在__autoload()函數(shù)中,將類名與實(shí)際的磁盤文件對應(yīng)起來,就可以實(shí)現(xiàn)lazy loading的效果。從這里我們也可以看出__autoload()函數(shù)的實(shí)現(xiàn)中最重要的是類名與實(shí)際的磁盤文件映射規(guī)則的實(shí)現(xiàn)。???
但現(xiàn)在問題來了,如果在一個系統(tǒng)的實(shí)現(xiàn)中,如果需要使用很多其它的類庫,這些類庫可能是由不同的開發(fā)人員編寫的,其類名與實(shí)際的磁盤文件的映射規(guī)則不盡相同。這時如果要實(shí)現(xiàn)類庫文件的自動加載,就必須在__autoload()函數(shù)中將所有的映射規(guī)則全部實(shí)現(xiàn),這樣的話__autoload()函數(shù)有可能會非常復(fù)雜,甚至無法實(shí)現(xiàn)。最后可能會導(dǎo)致__autoload()函數(shù)十分臃腫,這時即便能夠?qū)崿F(xiàn),也會給將來的維護(hù)和系統(tǒng)效率帶來很大的負(fù)面影響。在這種情況下,難道就沒有更簡單清晰的解決辦法了吧?答案當(dāng)然是:NO! 在看進(jìn)一步的解決方法之前,我們先來看一下PHP中的autoload機(jī)制是如何實(shí)現(xiàn)的。

(2) PHP的autoload機(jī)制的實(shí)現(xiàn)

我們知道,PHP文件的執(zhí)行分為兩個獨(dú)立的過程,第一步是將PHP文件編譯成普通稱之為OPCODE的字節(jié)碼序列(實(shí)際上是編譯成一個叫做zend_op_array的字節(jié)數(shù)組),第二步是由一個虛擬機(jī)來執(zhí)行這些OPCODE。PHP的所有行為都是由這些OPCODE來實(shí)現(xiàn)的。因此,為了研究PHP中autoload的實(shí)現(xiàn)機(jī)制,我們將autoload.php文件編譯成opcode,然后根據(jù)這些OPCODE來研究PHP在這過程中都做了些什么:

/* autoload.php 編譯后的OPCODE列表,是使用作者開發(fā)的OPDUMP工具?
???? */????
??? 1: <?php????
??? 2:? // require_once (”Person.php”);????
??? 3:??
??? 4:? function __autoload ($classname) {????
??????????? 0? NOP????????????????
??????????? 0? RECV??????????????? 1????
??? 5:?? if (!class_exists($classname)) {????
??????????? 1? SEND_VAR??????????? !0????
??????????? 2? DO_FCALL??????????? ‘class_exists’ [extval:1]????
??????????? 3? BOOL_NOT??????????? $0 =>RES[~1]?????
??????????? 4? JMPZ??????????????? ~1, ->8????
??? 6:??? require_once ($classname. “.class.php”);????
??????????? 5? CONCAT????????????? !0, ‘.class.php’ =>RES[~2]?????
??????????? 6? INCLUDE_OR_EVAL???? ~2, REQUIRE_ONCE????
??? 7:?? }????
??????????? 7? JMP???????????????? ->8????
??? 8:? }????
??????????? 8? RETURN????????????? null????
??? 9:??
?? 10:? $p = new Person(’Fred’, 35);????
??????????? 1? FETCH_CLASS???????? ‘Person’ =>RES[:0]?????
??????????? 2? NEW???????????????? :0 =>RES[$1]?????
??????????? 3? SEND_VAL??????????? ‘Fred’????
??????????? 4? SEND_VAL??????????? 35????
??????????? 5? DO_FCALL_BY_NAME???? [extval:2]????
??????????? 6? ASSIGN????????????? !0, $1????
?? 11:??
?? 12:? var_dump ($p);????
??????????? 7? SEND_VAR??????????? !0????
??????????? 8? DO_FCALL??????????? ‘var_dump’ [extval:1]????
?? 13: ?>

在autoload.php的第10行代碼中我們需要為類Person實(shí)例化一個對象。因此autoload機(jī)制一定會在該行編譯后的opcode中有所體現(xiàn)。從上面的第10行代碼生成的OPCODE中我們知道,在實(shí)例化對象Person時,首先要執(zhí)行FETCH_CLASS指令。我們就從PHP對FETCH_CLASS指令的處理過程開始我們的探索之旅。

通過查閱PHP的源代碼(我使用的是PHP 5.3alpha2版本)可以發(fā)現(xiàn)如下的調(diào)用序列:

ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, …) (zend_vm_def.h 1864行)???
=> zend_fetch_class (zend_execute_API.c 1434行)????
? =>zend_lookup_class_ex (zend_execute_API.c 964行)????
?? => zend_call_function(&fcall_info, &fcall_cache) (zend_execute_API.c 1040行)

在最后一步的調(diào)用之前,我們先看一下調(diào)用時的關(guān)鍵參數(shù):

/* 設(shè)置autoload_function變量值為”__autoload” */???
fcall_info.function_name = &autoload_function;? // Ooops, 終于發(fā)現(xiàn)”__autoload”了????
…????
fcall_cache.function_handler = EG(autoload_func); // autoload_func !

zend_call_function是Zend Engine中最重要的函數(shù)之一,其主要功能是執(zhí)行用戶在PHP程序中自定義的函數(shù)或者PHP本身的庫函數(shù)。zend_call_function有兩個重要的指針形參數(shù)fcall_info, fcall_cache,它們分別指向兩個重要的結(jié)構(gòu),一個是zend_fcall_info, 另一個是zend_fcall_info_cache。zend_call_function主要工作流程如下:如果fcall_cache.function_handler指針為NULL,則嘗試查找函數(shù)名為fcall_info.function_name的函數(shù),如果存在的話,則執(zhí)行之;如果fcall_cache.function_handler不為NULL,則直接執(zhí)行fcall_cache.function_handler指向的函數(shù)。

現(xiàn)在我們清楚了,PHP在實(shí)例化一個對象時(實(shí)際上在實(shí)現(xiàn)接口,使用類常數(shù)或類中的靜態(tài)變量,調(diào)用類中的靜態(tài)方法時都會如此),首先會在系統(tǒng)中查找該類(或接口)是否存在,如果不存在的話就嘗試使用autoload機(jī)制來加載該類。而autoload機(jī)制的主要執(zhí)行過程為:

(1) 檢查執(zhí)行器全局變量函數(shù)指針autoload_func是否為NULL。???
(2) 如果autoload_func==NULL, 則查找系統(tǒng)中是否定義有__autoload()函數(shù),如果沒有,則報(bào)告錯誤并退出。????
(3) 如果定義了__autoload()函數(shù),則執(zhí)行__autoload()嘗試加載類,并返回加載結(jié)果。????
(4) 如果autoload_func不為NULL,則直接執(zhí)行autoload_func指針指向的函數(shù)用來加載類。注意此時并不檢查__autoload()函數(shù)是否定義。

真相終于大白,PHP提供了兩種方法來實(shí)現(xiàn)自動裝載機(jī)制,一種我們前面已經(jīng)提到過,是使用用戶定義的__autoload()函數(shù),這通常在PHP源程序中來實(shí)現(xiàn);另外一種就是設(shè)計(jì)一個函數(shù),將autoload_func指針指向它,這通常使用C語言在PHP擴(kuò)展中實(shí)現(xiàn)。如果既實(shí)現(xiàn)了__autoload()函數(shù),又實(shí)現(xiàn)了autoload_func(將autoload_func指向某一PHP函數(shù)),那么只執(zhí)行autoload_func函數(shù)。

(3) SPL autoload機(jī)制的實(shí)現(xiàn)

SPL是Standard PHP Library(標(biāo)準(zhǔn)PHP庫)的縮寫。它是PHP5引入的一個擴(kuò)展庫,其主要功能包括autoload機(jī)制的實(shí)現(xiàn)及包括各種Iterator接口或類。SPL autoload機(jī)制的實(shí)現(xiàn)是通過將函數(shù)指針autoload_func指向自己實(shí)現(xiàn)的具有自動裝載功能的函數(shù)來實(shí)現(xiàn)的。SPL有兩個不同的函數(shù)spl_autoload, spl_autoload_call,通過將autoload_func指向這兩個不同的函數(shù)地址來實(shí)現(xiàn)不同的自動加載機(jī)制。

spl_autoload是SPL實(shí)現(xiàn)的默認(rèn)的自動加載函數(shù),它的功能比較簡單。它可以接收兩個參數(shù),第一個參數(shù)是$class_name,表示類名,第二個參數(shù)$file_extensions是可選的,表示類文件的擴(kuò)展名,可以在$file_extensions中指定多個擴(kuò)展名,護(hù)展名之間用分號隔開即可;如果不指定的話,它將使用默認(rèn)的擴(kuò)展名.inc或.php。spl_autoload首先將$class_name變?yōu)樾懀缓笤谒械膇nclude path中搜索$class_name.inc或$class_name.php文件(如果不指定$file_extensions參數(shù)的話),如果找到,就加載該類文件。你可以手動使用spl_autoload(”Person”, “.class.php”)來加載Person類。實(shí)際上,它跟require/include差不多,不同的它可以指定多個擴(kuò)展名。

怎樣讓spl_autoload自動起作用呢,也就是將autoload_func指向spl_autoload?答案是使用spl_autoload_register函數(shù)。在PHP腳本中第一次調(diào)用spl_autoload_register()時不使用任何參數(shù),就可以將autoload_func指向spl_autoload。

通過上面的說明我們知道,spl_autoload的功能比較簡單,而且它是在SPL擴(kuò)展中實(shí)現(xiàn)的,我們無法擴(kuò)充它的功能。如果想實(shí)現(xiàn)自己的更靈活的自動加載機(jī)制怎么辦呢?這時,spl_autoload_call函數(shù)閃亮登場了。

我們先看一下spl_autoload_call的實(shí)現(xiàn)有何奇妙之處。在SPL模塊內(nèi)部,有一個全局變量autoload_functions,它本質(zhì)上是一個HashTable,不過我們可以將其簡單的看作一個鏈表,鏈表中的每一個元素都是一個函數(shù)指針,指向一個具有自動加載類功能的函數(shù)。spl_autoload_call本身的實(shí)現(xiàn)很簡單,只是簡單的按順序執(zhí)行這個鏈表中每個函數(shù),在每個函數(shù)執(zhí)行完成后都判斷一次需要的類是否已經(jīng)加載,如果加載成功就直接返回,不再繼續(xù)執(zhí)行鏈表中的其它函數(shù)。如果這個鏈表中所有的函數(shù)都執(zhí)行完成后類還沒有加載,spl_autoload_call就直接退出,并不向用戶報(bào)告錯誤。因此,使用了autoload機(jī)制,并不能保證類就一定能正確的自動加載,關(guān)鍵還是要看你的自動加載函數(shù)如何實(shí)現(xiàn)。

那么自動加載函數(shù)鏈表autoload_functions是誰來維護(hù)呢?就是前面提到的spl_autoload_register函數(shù)。它可以將用戶定義的自動加載函數(shù)注冊到這個鏈表中,并將autoload_func函數(shù)指針指向spl_autoload_call函數(shù)(注意有一種情況例外,具體是哪種情況留給大家思考)。我們也可以通過spl_autoload_unregister函數(shù)將已經(jīng)注冊的函數(shù)從autoload_functions鏈表中刪除。

上節(jié)說過,當(dāng)autoload_func指針非空時,就不會自動執(zhí)行__autoload()函數(shù)了,現(xiàn)在autoload_func已經(jīng)指向了spl_autoload_call,如果我們還想讓__autoload()函數(shù)起作用應(yīng)該怎么辦呢?當(dāng)然還是使用spl_autoload_register(__autoload)調(diào)用將它注冊到autoload_functions鏈表中。

現(xiàn)在回到第一節(jié)最后的問題,我們有了解決方案:根據(jù)每個類庫不同的命名機(jī)制實(shí)現(xiàn)各自的自動加載函數(shù),然后使用spl_autoload_register分別將其注冊到SPL自動加載函數(shù)隊(duì)列中就可了。這樣我們就不用維護(hù)一個非常復(fù)雜的__autoload函數(shù)了。

(4) autoload效率問題及對策

使用autoload機(jī)制時,很多人的第一反應(yīng)就是使用autoload會降低系統(tǒng)效率,甚至有人干脆提議為了效率不要使用autoload。在我們了解了autoload實(shí)現(xiàn)的原理后,我們知道autoload機(jī)制本身并不是影響系統(tǒng)效率的原因,甚至它還有可能提高系統(tǒng)效率,因?yàn)樗粫⒉恍枰念惣虞d到系統(tǒng)中。???
那么為什么很多人都有一個使用autoload會降低系統(tǒng)效率的印象呢?實(shí)際上,影響autoload機(jī)制效率本身恰恰是用戶設(shè)計(jì)的自動加載函數(shù)。如果它不能高效的將類名與實(shí)際的磁盤文件(注意,這里指實(shí)際的磁盤文件,而不僅僅是文件名)對應(yīng)起來,系統(tǒng)將不得不做大量的文件是否存在(需要在每個include path中包含的路徑中去尋找)的判斷,而判斷文件是否存在需要做磁盤I/O操作,眾所周知磁盤I/O操作的效率很低,因此這才是使得autoload機(jī)制效率降低的罪魁禍?zhǔn)?

因此,我們在系統(tǒng)設(shè)計(jì)時,需要定義一套清晰的將類名與實(shí)際磁盤文件映射的機(jī)制。這個規(guī)則越簡單越明確,autoload機(jī)制的效率就越高。

結(jié)論:autoload機(jī)制并不是天然的效率低下,只有濫用autoload,設(shè)計(jì)不好的自動裝載函數(shù)才會導(dǎo)致其效率的降低。

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉