在PHP開發過程當中,若是但願從 外部引入一個class,一般會使用include和require方法,去把定義這個class的文件包含進來。這個在小規模開發的時候,沒什麼大問 題。但在大型的開發項目中,這麼作會產生大量的require或者include方法調用,這樣不因下降效率,並且使得代碼難以維護,何況 require_once的代價很大。php
在PHP5以前,各個PHP框架若是要實現類的自動加載,通常都是按照某種約定本身實現一個遍歷 目錄,自動加載全部符合約定規則的文件的類或函數。 固然,PHP5以前對面向對象的支持並非太好,類的使用也沒有如今頻繁。 在PHP5後,當加載PHP類時,若是類所在文件沒有被包含進來,或者類名出錯,Zend引擎會自動調用__autoload 函數。此函數須要用戶本身實現__autoload函數。 在PHP5.1.2版本後,可使用spl_autoload_register函數自定義自動加載處理函數。當沒有調用此函數,默認狀況下會使用SPL 自定義的spl_autoload函數。數組
spl_autoload函數的默認實現 若是不使用任何參數調用 autoload_register() 函數,則之後在進行 __autoload() 調用時會自動使用此函數。php框架
SPL有兩個不一樣的函數 spl_autoload, spl_autoload_call,經過將autoload_func指向這兩個不一樣的函數地址來實現不一樣的自動加載機制。安全
spl_autoload 是SPL實現的默認的自動加載函數,它的功能比較簡單。它能夠接收兩個參數,第一個參數是$class_name,表示類名,第二個參 數$file_extensions是可選的,表示類文件的擴展名" title="擴展名">擴展名,能夠在$file_extensions中指定多個擴展名" title="擴展名">擴展名,護展名之間用分號隔開即 可;若是不指定的話,它將使用默認的擴展名" title="擴展名">擴展名.inc或.php。spl_autoload首先將$class_name變爲小寫,而後在全部的 include path中搜索$class_name.inc或$class_name.php文件(若是不指定$file_extensions參數的話),若是找 到,就加載該類文件。你能夠手動使用spl_autoload(」Person」, 「.class.php」)來加載Person類。實際上,它跟require/include差很少,不一樣的它能夠指定多個擴展名" title="擴展名">擴展名。框架
怎 樣讓spl_autoload自動起做用呢,也就是將autoload_func指向spl_autoload?答案是使用 spl_autoload_register函數。在PHP腳本中第一次調用spl_autoload_register()時不使用任何參數,就能夠將 autoload_func指向spl_autoload。函數
經過上面的說明咱們知道,spl_autoload的功能比較簡單,並且它是在SPL擴展中實現的,咱們沒法擴充它的功能。若是想實現本身的更靈活的自動加載機制怎麼辦呢?這時,spl_autoload_call函數閃亮登場了。ui
我 們先看一下spl_autoload_call的實現有何奇妙之處。在SPL模塊內部,有一個全局變量autoload_functions,它本質上是 一個HashTable,不過咱們能夠將其簡單的看做一個鏈表,鏈表中的每個元素都是一個函數指針,指向一個具備自動加載類功能的函數。 spl_autoload_call自己的實現很簡單,只是簡單的按順序執行這個鏈表中每一個函數,在每一個函數執行完成後都判斷一次須要的類是否已經加載, 若是加載成功就直接返回,再也不繼續執行鏈表中的其它函數。若是這個鏈表中全部的函數都執行完成後類尚未加載,spl_autoload_call就直接 退出,並不向用戶報告錯誤。所以,使用了autoload機制,並不能保證類就必定能正確的自動加載,關鍵仍是要看你的自動加載函數如何實現。spa
在php5中的標準庫方法spl_autoload至關於實現本身的__autoload指針
<?php function __autoload($classname){ if(is_file($classname.'.php'){ include $classname.'.php'; } elseif(is_file($classname.'.inc'){ include $classname.'.inc'; } }
它會在註冊目錄下自動尋找與$classname同名的.php/.inc文件。固然,你也能夠指定特定類型的文件,方法是註冊擴展名code
<?php spl_autoload_extensions('.php,.inc,.some');
這樣,它也會搜索.some文件。默認,php是不會啓動spl_autoload的,那麼怎樣才能自動讓spl_autoload生效呢呢?方法是
<?php spl_autoload_register();
spl_autoload_register有一個$callback參數,若是不指定,它就會自動註冊spl_autoload,爲了能搜尋更多的自動加載目錄,能夠在這些代碼前面設置自動加載目錄
<?php set_include_path(get_include_path() . PATH_SEPARATOR . 'some/path' . DIRECTORY_SEPARATOR);
這樣,當php找不到指定的類時,就會在set_include_path指定的目錄下尋找。
這些方法經常使用在php框架中。好比把上面的介紹串連起來:
<?php set_include_path(get_include_path() . PATH_SEPARATOR . 'some/path' . DIRECTORY_SEPARATOR); spl_autoload_extensions('.php,.inc,.some'); spl_autoload_register();
當你要加載some/path下面的classA類時,它會在目錄下尋找classa.php或classa.inc或classa.some,這樣你就能夠放心地運用new classA或extends classA
<?php ClassB extends ClassA { // code.. } $a = new ClassA; $b = new ClassB;
spl_autoload
spl_autoload_register 註冊一個自動加載函數 函數參數一個當類不存在時自動加載的函數
spl_autoload_call嘗試調用全部已註冊的自動加載函數來裝載請求類
spl_autoload_extensions註冊並返回spl_autoload函數使用的默認文件擴展名。當不使用任何參數調用此函數時,它返回當前的文件擴展名的列表,不一樣的擴展名用逗號分隔。要修改文件擴展名列表,用一個逗號分隔的新的擴展名列表字符串來調用本函數便可。中文注:默認的spl_autoload函數使用的擴展名是".inc,.php"。實例:spl_autoload_extensions(".inc, .php, .lib, .lib.php ");
spl_autoload_unregister註銷已註冊的自動加載函數 函數參數註冊時的函數名
spl_autoload_functions 返回全部已註冊的z自動加載函數函數。
一、 __autoload示例:
function __autoload($class_name) { echo '__autload class:', $class_name, '<br />'; } new Demo();
以上的代碼在最後會輸出:__autload class:Demo。
並在此以後報錯顯示: Fatal error: Class ‘Demo’ not found
咱們通常使用_autoload自動加載類以下:
<?php function __autoload($class_name) { require_once ($class_name . 「class.php」); } $memo= new Demo();
咱們能夠看出_autoload至少要作三件事情,第一件事是根據類名肯定類文件名,第二件事是 肯定類文件所在的磁盤路徑(在咱們的例子是最簡單的狀況,類與調用它們的PHP程序文件在同一個文件夾下),第三件事是將類從磁盤文件中加載到系統中。第 三步最簡單,只須要使用include/require便可。要實現第一步,第二步的功能,必須在開發時約定類名與磁盤文件的映射方法,只有這樣咱們才能 根據類名找到它對應的磁盤文件。
所以,當有大量的類文件要包含的時候,咱們只要肯定相應的規則,而後在__autoload() 函數中,將類名與實際的磁盤文件對應起來,就能夠實現lazy loading的效果。從這裏咱們也能夠看出__autoload()函數的實現中最重要的是類名與實際的磁盤文件映射規則的實現。
但如今問題來了,假如在一個系統的實現中,假如須要使用不少其它的類庫,這些類庫多是由不一樣的 開發工程師開發,其類名與實際的磁盤文件的映射規則不盡相同。這時假如要實現類庫文件的自動加載,就必須在__autoload()函數中將全部的映射規 則所有實現,所以__autoload()函數有可能會很是複雜,甚至沒法實現。最後可能會致使__autoload()函數十分臃腫,這時即使可以實 現,也會給未來的維護和系統效率帶來很大的負面影響。在這種狀況下,一種新的解決方案,即spl_autoload_register()函數。
二、此函數的功能就是把函數註冊至SPL的__autoload函數棧中,並移除系統默認的__autoload()函數。下面的例子能夠看出:
function __autoload($class_name) { echo '__autload class:', $class_name, '<br />'; } function classLoader($class_name) { echo 'SPL load class:', $class_name, '<br />'; } spl_autoload_register('classLoader'); new Test();//結果:SPL load class:Test
語法:bool spl_autoload_register ( [callback $autoload_function] ) 接受兩個參數:一個是添加到自動加載棧的函數,另一個是加載器不能找到這個類時是否拋出異常的標誌。第一個參數是可選的,而且默認指向 spl_autoload()函數,這個函數會自動在路徑中查找具備小寫類名和.php擴展或者.ini擴展名,或者任何註冊到 spl_autoload_extensions()函數中的其它擴展名的文件。
<?php class CalssLoader { public static function loader($classname) { $class_file = strtolower($classname).".php"; if (file_exists($class_file)){ require_once($class_file); } } } // 方法爲靜態方法 spl_autoload_register('CalssLoader::loader'); $test = new Test();
一旦調用spl_autoload_register()函數,當調用未定義類時,系統會按順序調用註冊到spl_autoload_register()函數的全部函數,而不是自動調用__autoload()函數。若是要避免這種狀況,需採用一種更加安全的spl_autoload_register()函數的初始化調用方法:
if(false === spl_autoload_functions()){ if(function_exists('__autoload')){ spl_autoload_registe('__autoload',false); } }
spl_autoload_functions()函數會返回已註冊函數的一個數組,若是SPL自動加載棧尚未被初始化,它會返回布爾值false。然 後,檢查是否有一個名爲__autoload()的函數存在,若是存在,能夠將它註冊爲自動加載棧中的第一個函數,從而保留它的功能。以後,能夠繼續註冊 自動加載函數。
還能夠調用spl_autoload_register()函數以註冊一個回調函數,而不是爲函數提供一個字符串名稱。如提供一個如array('class','method')這樣的數組,使得可使用某個對象的方法。
下一步,經過調用spl_autoload_call('className')函數,能夠手動調用加載器,而不用嘗試去使用那個類。這個函數能夠和函數 class_exists('className',false)組合在一塊兒使用以嘗試去加載一個類,而且在全部的自動加載器都不能找到那個類的狀況下失 敗。
f(spl_autoload_call('className') && class_exists('className',false)){ } else { }
SPL 自動加載功能是由spl_autoload() ,spl_autoload_register(), spl_autoload_functions() ,spl_autoload_extensions()和spl_autoload_call()函數提供的。