在PHP5之前,各個PHP框架如果要實現類的自動加載,一般都是按照某種約定自己實現一個遍歷目錄,自動加載所有符合約定規則的文件的類或函數。 當然,PHP5之前對面向對象的支持並不是太好,類的使用也沒有現在頻繁。 在PHP5後,當加載PHP類時,如果類所在文件沒有被包含進來,或者類名出錯,Zend引擎會自動調用__autoload 函數。此函數需要用戶自己實現__autoload函數。 在PHP5.1.2版本後,可以使用spl_autoload_register函數自定義自動加載處理函數。當沒有調用此函數,默認情況下會使用SPL自定義的spl_autoload函數。 看下面兩個例子:
1、 __autoload示例:
代碼如下 復制代碼function __autoload($class_name) {
echo '__autload class:', $class_name, '<br />';
}
new Demo();
以上的代碼在最後會輸出:__autload class:Demo。
並在此之後報錯顯示: Fatal error: Class 'Demo' not found
2、spl_autoload_register示例:
代碼如下 復制代碼function classLoader($class_name) {
echo 'SPL load class:', $class_name, '<br />';
}
spl_autoload_register('classLoader');
new Demo();
以上的代碼在最後會輸出:SPL load class:Demo。
並在此之後報錯顯示: Fatal error: Class 'Demo' not found
以上的兩個示例表明:當類不存在時(即需要的類不在類符號表),Zend引擎會將再調用一次用戶定義的函數,如__autoload或spl_autoload_register注冊的函數。 如果這兩個方法同時存在,那麼程序會調用哪一個呢?還是說兩個都調用?看下面一個示例,你覺得會輸出什麼呢?
代碼如下 復制代碼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 Demo();
__set、__tostring等類的魔法方法的常量定義在源碼級別是一起的, 可是它並不是專屬於某個類的魔法方法。它是所有的類共用的自動加載魔術方法。 它將作為一個全局函數存在。那麼Zend引擎是如何在類沒有找到時調用這個方法的呢?
不管是使用new關鍵字創建類的實例,還是使用implement實現接口,或者繼承某個類, 所有的這些操作都有可能調用__autoload函數。這幾個操作在源碼層都有一個共同點,它們在執行的時候都需要獲取類的信息(接口在本質上也是一個類)。 它們在最終都會調用 zend_fetch_class (Zend/zend_execute_API.c)函數,這個函數本身沒有多少內容,關鍵是它調用了zend_lookup_class_ex(Zend/zend_execute_API.c)函數, 這個函數就是類的自動加載的真相所在。
在zend_lookup_class_ex函數中,我們看到程序會首先查詢類符號表,如果存在類直接返回。如果不存在,就會執行我們所說的自動加載了。這裡針對__autoload函數和spl相關的函數都做了處理,並且以第一參數和第二參數傳遞給Zend引擎的函數調用函數zend_call_function。
在zend_call_function函數中,它會判斷第二參數是否存在函數,如果存在函數則只會調用第二個參數傳遞的函數(這裡指SPL注冊的函數)。如果第二個函數沒有值,則執行第一個參數傳遞過來的函數(這裡指用戶定義的__autoload函數)。到這裡,我想前面提到的兩個方法同時存在的情況應該就有答案了,這也算是一篇基於的php教程了。