1. 自動加載與 Psr0陈哑、Psr4 和 Composer 的關(guān)系容诬?
說起PHP的自動加載,我們可能馬上想到的是各種框架的自動加載功能碟渺,PHP規(guī)范中的PSR0和PSR4原則, Composer 的自動加載功能等等突诬。PSR0和PSR4原則只是為我們定義自動加載的類提供了統(tǒng)一的規(guī)范苫拍,例如:命名空間芜繁,而 Composer 同樣是利用了這個統(tǒng)一規(guī)范加入了自動加載構(gòu)成一個完整的組件包,為我們的開發(fā)提供了很大的方便绒极。
2. 為什么會有自動加載
在PHP面向?qū)ο?OO)編程中骏令,為了方便管理,我們都會把一個類寫在一個單獨的文件中垄提,那么如果想在A類中使用B類的功能榔袋,就需要把B類加載到A類。對于這樣的需求在最原始的時候铡俐,我們是通過require 和 include 語法實現(xiàn)的摘昌。
PHP5實現(xiàn)了類的自動加載(Autoload)功能,減少傳統(tǒng)方式的文件引入方式帶來的復(fù)雜性高蜂,如:文件嵌套依賴聪黎、方法名沖突等。
3. __autoload()自動加載
PHP5實現(xiàn)了類的自動加載(Autoload)功能备恤,這個功能最初是通過PHP的一個魔術(shù)方法__autoload()實現(xiàn)的稿饰。后來,PHP擴展SPL(Standard PHP Library 標(biāo)準(zhǔn)PHP類庫)又實現(xiàn)了更強大的自動加載機制露泊。
我們需要明確__autoload()函數(shù)PHP在找不到類的時候會自動執(zhí)行喉镰,但是PHP內(nèi)部并沒有定義這個函數(shù),這個函數(shù)需要開發(fā)著自己定義惭笑,并且編寫內(nèi)部邏輯侣姆,PHP只負(fù)責(zé)在需要的時候自動調(diào)用執(zhí)行。而且在調(diào)用的時候會自動傳人要加載的類名作為參數(shù)沉噩。
4. __autoload()自動加載與SPL自動加載的關(guān)系
有了__autoload()函數(shù)捺宗,可以看出,如果我們現(xiàn)在需要引入100個其它文件川蒙,只需要訂好一個規(guī)則蚜厉,編寫一個函數(shù)就可以了。這比直接用require/inlude有了很大進步畜眨,但是同樣也有新的問題昼牛,在一個項目中,我們只能編寫一個__autoload()函數(shù)康聂,如果項目比較大贰健,加載每個文件都使用同樣的規(guī)則顯然是不現(xiàn)實的,那么我們可能就需要在__autoload()中編寫復(fù)雜的規(guī)則邏輯來滿足加載不同文件的需求恬汁。這同樣會使得__autoload()函數(shù)變得復(fù)雜臃腫伶椿,難以維護管理。
于是,SPL(Standard PHP Library 標(biāo)準(zhǔn)PHP類庫)的自動加載機制就應(yīng)時而生了悬垃。
5. SPL自動加載原理
首先游昼,明確一點甘苍,PHP在實例化一個對象時(實際上在實現(xiàn)接口尝蠕,使用類常數(shù)或類中的靜態(tài)變量,調(diào)用類中的靜態(tài)方法時都會如此)载庭,首先會在系統(tǒng)中查找該類(或接口)是否存在看彼,如果不存在的話就嘗試使用autoload機制來加載該類。而autoload機制的主要執(zhí)行過程為:
1囚聚、檢查執(zhí)行器全局變量函數(shù)指針autoload_func是否是NULL靖榕;
2、如果 autoload_func==NULL ,則查找系統(tǒng)是否定義 **__autoload() **函數(shù)顽铸,如果定義了茁计,則執(zhí)行并返回加載結(jié)果。如果沒有定義谓松,則報錯并退出星压;
3、如果 autoload_func 不等于NULL鬼譬,則直接執(zhí)行 autoload_func 指向的函數(shù)加載類娜膘,此時并不檢查 __autoload() 函數(shù)是否定義。
通過對PHP自動加載流程的了解优质,可以看到PHP實際上提供了兩種方法來實現(xiàn)自動裝載機制:
a. 一種我們前面已經(jīng)提到過竣贪,是使用用戶定義的__autoload()函數(shù),這通常在PHP源程序中來實現(xiàn)巩螃;
b. 另外一種就是設(shè)計一個函數(shù)演怎,將autoload_func指針指向它,這通常使用C語言在PHP擴展中實現(xiàn)避乏,即 SPL autoload機制颤枪。
如果兩種方式都實現(xiàn)了,也就是 autoload_func 不等于NULL淑际,程序只會執(zhí)行第二種方式畏纲,__autoload() 函數(shù)是不會被執(zhí)行的。
詳細(xì)分析SPL自動加載過程
假如把spl_autoload_register(’my_autoload’) 改成 spl_autoload_register()不添加任何參數(shù)春缕,B類能被加載嗎盗胀?答案是:YES。
為什么呢锄贼?
因為SPL擴展內(nèi)部自己定義了一個自動加載函數(shù) spl_autoload(),實現(xiàn)了自動加載的功能票灰,如果我們不定義自己的自動加載函數(shù),并且程序里寫了 spl_autoload_register()(如果不傳參數(shù),必須是第一次執(zhí)行才會有效)或者 spl_autoload_register(’spl_autoload’)屑迂,那么autoload_func 指針就會指向內(nèi)部函數(shù) spl_autoload()浸策。程序執(zhí)行的時候如果找不到相應(yīng)類就會執(zhí)行該自動加載函數(shù)。
那么惹盼,SPL 是怎么實現(xiàn)autoload_func 指針指向不同的函數(shù)呢庸汗?
原來,在SPL內(nèi)部定義了 一個函數(shù) spl_autoload_call() 和 一個全局變量autoload_functions手报。autoload_functions本質(zhì)上是一個HashTable蚯舱,不過我們可以將其簡單的看作一個鏈表,鏈表中的每一個元素都是一個函數(shù)指針,指向一個具有自動加載類功能的函數(shù)掩蛤。
spl_autoload_call()的作用就是按順序遍歷 autoload_functions枉昏,使得autoload_func指向每個自動加載函數(shù),如果加載成功就停止揍鸟,如果不成功就繼續(xù)遍歷下個自動加載函數(shù)兄裂,直到加載成功或者遍歷完所有的函數(shù)。
那么阳藻,autoload_functions 這個列表是誰來維護的呢晰奖?就是 spl_autoload_register() 這個函數(shù)。我們說的自動加載函數(shù)的注冊稚配,其實就是通過spl_autoload_register()把自動加載函數(shù)加入到 autoload_functions 列表畅涂。