PHP自动加载机制
概述
首先,為什么PHP需要自動(dòng)加載呢?
在PHP面向?qū)ο?OO)編程中,為了方便管理,我們都會(huì)把一個(gè)類寫在一個(gè)單獨(dú)的文件中,那么如果想在A類中使用B類的功能,就需要把B類加載到A類。對(duì)于這樣的需求在最原始的時(shí)候,我們是通過(guò)require 和 include 語(yǔ)法實(shí)現(xiàn)將文件加載到另一個(gè)文件中,include 和 require 是PHP中引入文件的兩個(gè)基本方法。
在小規(guī)模開(kāi)發(fā)中直接使用 include 和 require 不會(huì)有什么不妥,但在大型項(xiàng)目中會(huì)造成大量的 include 和 require 堆積。這樣的代碼既不優(yōu)雅,執(zhí)行效率也很低,而且維護(hù)起來(lái)也相當(dāng)困難。
PHP的自動(dòng)加載功能,框架實(shí)現(xiàn)自動(dòng)加載的包括PHP規(guī)范中的PSR0和PSR4原則,Composer的自動(dòng)加載功能等等。
php加載文件方式:
include()與require()
簡(jiǎn)單的文件加載方法:
require()
包含的意思,找不到文件時(shí),會(huì)報(bào)warning的錯(cuò)誤,然后程序繼續(xù)往下執(zhí)行。include語(yǔ)句只有在被執(zhí)行時(shí)才會(huì)讀入要包含的文件。在錯(cuò)誤處理方便,使用include語(yǔ)句,如果發(fā)生包含錯(cuò)誤,程序?qū)⑻^(guò)include語(yǔ)句,雖然會(huì)顯示錯(cuò)誤信息但是程序還是會(huì)繼續(xù)執(zhí)行!php處理器會(huì)在每次遇到include()語(yǔ)句時(shí),對(duì)它進(jìn)行重新處理,所以可以根據(jù)不同情況的,在條件控制語(yǔ)句和循環(huán)語(yǔ)句中使用include()來(lái)包含不同的文件。
include()
必須的意思,找不到文件時(shí),會(huì)報(bào)fatal error(致命錯(cuò)誤),程序停止往下執(zhí)行。在php文件被執(zhí)行之前,php解析器會(huì)用被引用的文件的全部?jī)?nèi)容替換require語(yǔ)句,然后與require語(yǔ)句之外的其他語(yǔ)句組成個(gè)新的php文件,最好后按新的php文件執(zhí)行程序代碼。
require_once()
類似于include(),系統(tǒng)會(huì)進(jìn)行判斷,如果已經(jīng)包含,則不會(huì)再包含第二次。
include_once()
類似于require(),系統(tǒng)會(huì)進(jìn)行判斷,如果已經(jīng)包含,則不會(huì)再包含第二次。
– 共同點(diǎn):能包含位于獨(dú)立文件中的代碼,可以減少代碼的重復(fù),實(shí)現(xiàn)代碼結(jié)構(gòu)的模塊化,方便調(diào)用。
注意事項(xiàng):
加載文件格式
include/require 包含進(jìn)來(lái)的文件必須要加<?php ?>因?yàn)樵诎瑫r(shí),首先理解文件內(nèi)容是普通字符串,碰到<?php ?>標(biāo)簽時(shí),才去解釋。
路徑要求
可以用絕對(duì)路徑,也可以用相對(duì)路徑;windows下正反斜線都可以,linux下只認(rèn)正斜線,所以最好用正斜線。
如何選擇
比如是系統(tǒng)配置,缺少了,網(wǎng)站不讓運(yùn)行,自然用require,如果是某一段統(tǒng)計(jì)程序,少了,對(duì)網(wǎng)站只是少統(tǒng)計(jì)人數(shù)罷了,不是必須要的,可以用include而加不加once是效率上的區(qū)別,加上once,雖然系統(tǒng)幫你考慮了只加載一次,但系統(tǒng)的判斷會(huì)是效率降低,因此,更應(yīng)該在開(kāi)發(fā)之初,就把目錄結(jié)構(gòu)調(diào)整好,盡量不要用_once的情況。
特殊用法
利用include/require返回被包含頁(yè)面的返回值
1
2a.php頁(yè)面中: ..... return $value;
b.php頁(yè)面中:$v = include("a.php");
__autoload()自動(dòng)加載
PHP5及之后的版本,使用尚未定義的類時(shí)會(huì)自動(dòng)調(diào)用__autoload函數(shù),所以我們可以通過(guò)編寫__autoload函數(shù)來(lái)讓php自動(dòng)加載類,而不必寫一個(gè)長(zhǎng)長(zhǎng)的包含文件列表。
需明確的是對(duì)于__autoload()函數(shù),PHP在找不到類的時(shí)候會(huì)自動(dòng)執(zhí)行,但是PHP內(nèi)部并沒(méi)有定義這個(gè)函數(shù)。
這個(gè)函數(shù)需要開(kāi)發(fā)者自定義,并且編寫內(nèi)部邏輯,PHP只負(fù)責(zé)在需要的時(shí)候自動(dòng)調(diào)用執(zhí)行。而且在調(diào)用的時(shí)候會(huì)自動(dòng)傳人要加載的類名作為參數(shù)。
用法:首先需要在需要加載文件的代碼中,定義__autoload()函數(shù),并且編寫內(nèi)部邏輯。PHP在找不到類的時(shí)候會(huì)自動(dòng)執(zhí)行__autoload()函數(shù)。下面是A.php需要加載B.php的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//文件 B.php 不做修改
//文件 A.php
<?php
class A{
public function test(){
$b_object = new B();
$b_object->echo_info();
}
}
function __autoload($classname){
require $classname.'.php';//include 'b.php';
}
$a_object = new A();
$a_oject->test();
?>
命令行輸入:#php a.php
輸出: “我是class B中的方法執(zhí)行結(jié)果“缺陷:一個(gè)項(xiàng)目中僅能有一個(gè)這樣的 __autoload() 函數(shù),因?yàn)?PHP 不允許函數(shù)重名,也就是說(shuō)你不能聲明2個(gè)__autoload()函數(shù)文件,否則會(huì)報(bào)致命錯(cuò)誤。
如果項(xiàng)目比較大,加載每個(gè)文件都使用同樣的規(guī)則顯然是不現(xiàn)實(shí)的,那么我們可能就需要在__autoload()中編寫復(fù)雜的規(guī)則邏輯來(lái)滿足加載不同文件的需求。
這同樣會(huì)使得__autoload()函數(shù)變得復(fù)雜臃腫,難以維護(hù)管理。
SPL 自動(dòng)加載
PHP在實(shí)例化一個(gè)對(duì)象時(shí)(實(shí)際上在實(shí)現(xiàn)接口,使用類常數(shù)或類中的靜態(tài)變量,調(diào)用類中的靜態(tài)方法時(shí)都會(huì)如此),首先會(huì)在系統(tǒng)中查找該類(或接口)是否存在,如果不存在的話就嘗試使用autoload機(jī)制來(lái)加載該類。而autoload機(jī)制的主要執(zhí)行過(guò)程為:
- 檢查執(zhí)行器全局變量函數(shù)指針autoload_func是否是NULL;
- 如果 autoload_func==NULL ,則查找系統(tǒng)是否定義 __autoload() 函數(shù),如果定義了,則執(zhí)行并返回加載結(jié)果。如果沒(méi)有定義,則報(bào)錯(cuò)并退出;
- 如果 autoload_func 不等于NULL,則直接執(zhí)行 autoload_func 指向的函數(shù)加載類,此時(shí)并不檢查 __autoload() 函數(shù)是否定義。
通過(guò)上述PHP自動(dòng)加載流程,可知PHP實(shí)際上提供了兩種方法來(lái)實(shí)現(xiàn)自動(dòng)裝載機(jī)制:(1)使用用戶定義的__autoload()函數(shù),這通常在PHP源程序中來(lái)實(shí)現(xiàn);(2)設(shè)計(jì)一個(gè)函數(shù),將autoload_func指針指向它,這通常使用C語(yǔ)言在PHP擴(kuò)展中實(shí)現(xiàn),即 SPL autoload機(jī)制,即本節(jié)中的SPL自動(dòng)加載。如果兩種方式都實(shí)現(xiàn)了,也就是 autoload_func 不等于NULL,程序只會(huì)執(zhí)行第二種方式,__autoload() 函數(shù)是不會(huì)被執(zhí)行的。
用法
通過(guò)spl_autoload_register('my_autoload'),實(shí)現(xiàn)了 當(dāng)程序執(zhí)行找不到類B時(shí),會(huì)執(zhí)行 自定義的 my_autoload()函數(shù),加載B類。實(shí)際上 spl_autoload_register('my_autoload') 的作用就是 把a(bǔ)utoload_func 指針指向 my_autoload()。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25B.php文件不變
A.php
<?php
class A{
public function test(){
$b_object = new B();
$b_object->echo_info();
}
}
function __autoload($classname){
require $classname.'.php';//include 'b.php';
}
function my_autoload($classname){
require $classname.'.php';//include 'b.php';
echo 'my_autoload ';
}
spl_autoload_register('my_autoload');
$a_object = new A();
$a_object->test();
結(jié)果:my_autoload 我是class B中的方法執(zhí)行結(jié)果
?>SPL 自動(dòng)加載整個(gè)過(guò)程
針對(duì)上述的示例,假如把spl_autoload_register('my_autoload')改成 spl_autoload_register()不添加任何參數(shù),B類也能被加載。
為什么呢?
因?yàn)镾PL擴(kuò)展內(nèi)部自己定義了一個(gè)自動(dòng)加載函數(shù) spl_autoload(),實(shí)現(xiàn)了自動(dòng)加載的功能,如果我們不定義自己的自動(dòng)加載函數(shù),并且程序里寫了spl_autoload_register()(如果不傳參數(shù),必須是第一次執(zhí)行才會(huì)有效)或者 spl_autoload_register('spl_autoload'),那么autoload_func 指針就會(huì)指向內(nèi)部函數(shù) spl_autoload()。程序執(zhí)行的時(shí)候如果找不到相應(yīng)類就會(huì)執(zhí)行該自動(dòng)加載函數(shù)。
SPL 是怎么實(shí)現(xiàn)autoload_func 指針指向不同的函數(shù)呢?
在SPL內(nèi)部定義了 一個(gè)函數(shù) spl_autoload_call() 和 一個(gè)全局變量autoload_functions。autoload_functions本質(zhì)上是一個(gè)HashTable,不過(guò)我們可以將其簡(jiǎn)單的看作一個(gè)鏈表,鏈表中的每一個(gè)元素都是一個(gè)函數(shù)指針,指向一個(gè)具有自動(dòng)加載類功能的函數(shù)。
spl_autoload_call()的作用就是按順序遍歷 autoload_functions,使得autoload_func指向每個(gè)自動(dòng)加載函數(shù),如果加載成功就停止,如果不成功就繼續(xù)遍歷下個(gè)自動(dòng)加載函數(shù),直到加載成功或者遍歷完所有的函數(shù)。
autoload_functions 這個(gè)列表是誰(shuí)來(lái)維護(hù)的呢?
由spl_autoload_register() 這個(gè)函數(shù)維護(hù)。我們說(shuō)的自動(dòng)加載函數(shù)的注冊(cè),其實(shí)就是通過(guò)·spl_autoload_register()·把自動(dòng)加載函數(shù)加入到autoload_functions 列表。
相關(guān)SPL自動(dòng)加載函數(shù)
1
2spl_autoload_functions() //打印autoload_functions列表
spl_autoload_unregister() //注銷自動(dòng)加載函數(shù)
參考:
總結(jié)
- 上一篇: 【软件测试】等价类划分
- 下一篇: C++编译错误C2365