文章總結(jié)了PHP的自動(dòng)加載機(jī)制,包含魔法函數(shù) __autoload()和spl autoload函數(shù)。
緣起
PHP開(kāi)發(fā)過(guò)程中,需要通過(guò)include、include_once篡石、require或require_once引入定義的類(lèi),如圖:
<?php
require_once('./ClassA.php');
require_once('./ClassB.php');
require_once('./ClassC.php');
...
當(dāng)需要引入的類(lèi)比較少的時(shí)候西采,沒(méi)有什么大問(wèn)題夏志,但是當(dāng)需要引入的類(lèi)非常多時(shí),就會(huì)出現(xiàn)大量的include或require語(yǔ)句苛让,這樣會(huì)導(dǎo)致遺漏或者包含不必要的類(lèi)文件沟蔑。因此,類(lèi)的自動(dòng)加載(autoload)機(jī)制應(yīng)運(yùn)而生狱杰。
autoload機(jī)制主要用來(lái)自動(dòng)加載類(lèi)瘦材,該機(jī)制使PHP程序有可能在使用類(lèi)時(shí)才自動(dòng)引入類(lèi)文件,而不是一開(kāi)始就將所有的類(lèi)文件引入仿畸。
自動(dòng)加載函數(shù)__autoload()
PHP程序中食棕,當(dāng)我們要使用的類(lèi)未導(dǎo)入時(shí),會(huì)自動(dòng)調(diào)用__autoload()函數(shù)错沽,此函數(shù)是我們?cè)诔绦蛑凶远x的簿晓,通過(guò)這個(gè)函數(shù)可以加載需要引入的類(lèi)。
為了更好的說(shuō)明__autoload()函數(shù)千埃,下面舉個(gè)例子進(jìn)行說(shuō)明憔儿,以下是項(xiàng)目的目錄:
+ first
+-- ClassA.php
+ second
+-- ClassB.php
- autoload.php
- index.php
首先我們來(lái)看ClassA.php:
<?php
//test/first/ClassA.php
class ClassA
{
public function fun()
{
echo "ClassA is running...\n";
}
}
ClassB和ClassA類(lèi)似,只有一個(gè)簡(jiǎn)單的函數(shù)fun():
<?php
//test/second/ClassB.php
class ClassB
{
public function fun()
{
echo "ClassB is running...\n";
}
}
require放可、include函數(shù)
如果采用原始的require或者include方法谒臼,需要分別引入ClassA朝刊、ClassB文件,程序正常打印結(jié)果蜈缤。
<?php
//test/index.php
require_once __DIR__.'./first/ClassA.php';
require_once __DIR__.'./second/ClassB.php';
$classA = new ClassA();
$classB = new ClassB();
$classA->fun();
$classB->fun();
__autoload()函數(shù)
為了使用__autoload()函數(shù)拾氓,我們需要單獨(dú)新建一個(gè)文件,這里我們新建了一個(gè)autoload.php文件底哥,然后在index.php文件里調(diào)用include或require引入該文件咙鞍,此時(shí)當(dāng)有未引入的類(lèi)時(shí),程序會(huì)自動(dòng)調(diào)用__autoload()函數(shù)趾徽,并將類(lèi)名作為參數(shù)傳入奶陈。
__autoload()函數(shù),首先需要確定類(lèi)名對(duì)應(yīng)的文件名附较,然后通過(guò)調(diào)用is_readable()函數(shù)判斷該文件是否可讀,如果可讀則調(diào)用require_once引入相應(yīng)的類(lèi)文件潦俺。
<?php
//test/autoload.php
function __autoload($className)
{
if($className == 'ClassA'){
$classpath = __DIR__.'\\first\\'.$className.'.php';
}else{
$classpath = __DIR__.'\\second\\'.$className.'.php';
}
if (is_readable($classpath)) {
require_once($classpath);
}
}
此時(shí)拒课,需要修改index.php文件,該文件刪掉了ClassA和ClassB的引入事示,同時(shí)引入了autoload.php文件早像。這里只需要一行即可引入所需的類(lèi),當(dāng)所需的類(lèi)達(dá)到數(shù)百個(gè)時(shí)肖爵,__autoload()函數(shù)的優(yōu)勢(shì)就會(huì)彰顯無(wú)疑卢鹦。另外,__autoload()函數(shù)只有在需要相應(yīng)的類(lèi)時(shí)才會(huì)主動(dòng)引入劝堪,假設(shè)這里不需要ClassB類(lèi)冀自,那么程序不會(huì)引入。如果是采用require或include函數(shù)時(shí)秒啦,必須在程序的開(kāi)始交代清楚熬粗,否則會(huì)報(bào)錯(cuò)。
<?php
//test/index.php
require_once __DIR__.'./autoload.php';
$classA = new ClassA();
$classB = new ClassB();
$classA->fun();
$classB->fun();
但是余境,autoload()函數(shù)存在一個(gè)嚴(yán)重的缺點(diǎn):__autoload() 是全局函數(shù)且只能定義一次驻呐,不夠靈活。另外芳来,所有類(lèi)名和文件名的對(duì)應(yīng)關(guān)系都在__autoload()函數(shù)中實(shí)現(xiàn)含末,這會(huì)導(dǎo)致該函數(shù)非常臃腫。
spl autoload
SPL是Standard PHP Library(標(biāo)準(zhǔn)PHP庫(kù))的縮寫(xiě)即舌。它是PHP引入的一個(gè)擴(kuò)展庫(kù)佣盒,其主要功能包括autoload機(jī)制的實(shí)現(xiàn)及包括各種Iterator接口或類(lèi)。其有以下幾個(gè)函數(shù):
spl_autoload_register:注冊(cè) _autoload() 函數(shù)
spl_autoload_unregister:注銷(xiāo)已注冊(cè)的_autoload()函數(shù)
spl_autoload_functions:返回所有已注冊(cè)的_autoload()函數(shù)
spl_autoload_call:嘗試所有已注冊(cè)的_autoload()函數(shù)來(lái)加載類(lèi)
spl_autoload :_autoload()函數(shù)的默認(rèn)實(shí)現(xiàn)
spl_autoload_extionsions: 注冊(cè)并返回 spl_autoload 函數(shù)使用的默認(rèn)文件擴(kuò)展名顽聂。
spl_autoload_register()函數(shù)實(shí)現(xiàn)了一個(gè)autoload調(diào)用堆棧沼撕,我們可以向這個(gè)堆棧注冊(cè)多個(gè)__autoload()函數(shù)宋雏,當(dāng)PHP找不到類(lèi)名時(shí),就會(huì)調(diào)用autoload堆棧务豺,一個(gè)一個(gè)地去調(diào)用自定義的__autoload()函數(shù)磨总,實(shí)現(xiàn)自動(dòng)加載功能。
舉例說(shuō)明spl_autoload_register()函數(shù)的用法笼沥,以下是項(xiàng)目目錄
+ first
+-- ClassA.php
+ second
+-- ClassB.php
- autoloadA.php
- autoloadB.php
- index.php
項(xiàng)目增加了autoloadA.php蚪燕、autoloadB.php文件,并且修改了index.php文件奔浅。autoloadA.php文件用于加載first文件夾下的類(lèi)馆纳,autoloadB.php用于加載second文件夾下的類(lèi)。通過(guò)調(diào)用spl_autoload_register()函數(shù)可以注冊(cè)多個(gè)__autoload()函數(shù)汹桦。
<?php
//test/autoloadA.php
function __autoloadA($className)
{
$classpath = __DIR__.'\\first\\'.$className.'.php';
if (is_readable($classpath)) {
require_once($classpath);
}
}
spl_autoload_register('__autoloadA');
<?php
//test/autoloadB.php
function __autoloadB($className)
{
$classpath = __DIR__.'\\second\\'.$className.'.php';
if (is_readable($classpath)) {
require_once($classpath);
}
}
spl_autoload_register('__autoloadB');
文件index.php修改如下鲁驶,此時(shí)即可以實(shí)現(xiàn)注冊(cè)多個(gè)__autoload()函數(shù)。此時(shí)會(huì)發(fā)現(xiàn)舞骆,與最開(kāi)始使用require或include引入文件一樣需要兩行才能實(shí)現(xiàn)所有類(lèi)的引入钥弯,貌似并沒(méi)有更簡(jiǎn)單。但是假設(shè)first督禽、second文件夾下各有100個(gè)類(lèi)需要引入脆霎,則使用require或include函數(shù)引入需要200行,而使用spl_autoload_register()函數(shù)引入根本不需要改變代碼狈惫。
<?php
//test/index.php
require_once __DIR__ . './autoloadA.php';
require_once __DIR__ . './autoloadB.php';
$classA = new ClassA();
$classB = new ClassB();
$classA->fun();
$classB->fun();