說起PHP的自動加載,很多同學可能都會想到各種框架的自動加載功能,PHP規範中的PSR0和PSR4原則,Composer的自動加載功能等等,這些都為我們的開發提供了很大的方便。
那麼PHP自動載入的前因後果到底是什麼? PHP的內部原理又是怎麼樣的呢?接下來我就根據自己的理解來進行分析總結:
為什麼會有自動載入?
在PHP物件導向(OO)程式設計中,為了方便管理,我們都會把一個類別寫在一個單獨的檔案中,那麼如果想在A類別中使用B類的功能,就需要把B類載入到A類。對於這樣的需求在最原始的時候,我們是透過require 和 include 語法實現的,這2種語法結果基本上一樣,執行流程有一些區別,這裡不解釋。例如:
//文件 B.php <?php class B{ public function echo_info(){ echo "我是class B中的方法执行结果"; } } ?> //文件 A.php <?php require 'b.php';//include 'b.php'; class A{ public function test(){ $b_object = new B(); $b_object->echo_info(); } } $a_object = new A(); $a_oject->test(); ?> 命令行输入:#php a.php 输出: “我是class B中的方法执行结果“
於是,PHP5實作了類別的自動載入(Autoload)功能,這個功能最初是透過PHP的一個魔術方法__autoload()實現的。後來,PHP擴充SPL(Standard PHP Library 標準PHP類別函式庫)又實作了更強大的自動載入機制。
php原始自動載入
#首先,先介紹下__autoload()方法。還是剛剛的例子,使用__autoload()可以做如下修改:
//文件 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中的方法执行结果“
我們在A文件中加了一個函數:__autoload(),並且自己在函數中編寫了相應的引入方法,運行之後同樣得到了結果,沒有報錯。我們需要明確__autoload()函數PHP在找不到類別的時候會自動執行,但是PHP內部並沒有定義這個函數,這個函數需要開發著自己定義,並且編寫內部邏輯,PHP只負責在需要的時候自動調用執行。而且在呼叫的時候會自動傳人要載入的類別名稱作為參數。
有了__autoload()函數,可以看出,如果我們現在需要引入100個其它文件,只需要訂好一個規則,編寫一個函數就可以了。這比直接用require/inlude有了很大進步,但是同樣也有新的問題,在一個專案中,我們只能寫一個__autoload()函數,如果專案比較大,載入每個檔案都使用相同的規則顯然是不切實際的,那麼我們可能需要在__autoload()中編寫複雜的規則邏輯來滿足載入不同檔案的需求。這同樣會使得__autoload()函數變得複雜臃腫,難以維持管理。
於是,SPL(Standard PHP Library 標準PHP類別函式庫)的自動載入機制就應時而生了。
SPL自動載入
#首先,明確一點,PHP在實例化一個物件時(實際上在實現接口,使用類別常數或類別中的靜態變量,調用類別中的靜態方法時都會如此),首先會在系統中查找該類別(或接口)是否存在,如果不存在的話就嘗試使用autoload機制來加載該類。而autoload機制的主要執行過程為:
1、檢查執行器全域變數函數指標autoload_func是否是NULL;
2.如果autoload_func==NULL ,則查找系統是否定義__autoload() 函數,如果定義了,則執行並傳回載入結果。如果沒有定義,則報錯並退出;
3、如果 autoload_func 不等於NULL,則直接執行 autoload_func 指向的函數載入類別,此時並沒有檢查 __autoload() 函數是否定義。
透過對PHP自動載入流程的了解,可以看到PHP實際上提供了兩種方法來實現自動裝載機制:
一種我們前面已經提到過,是使用用戶定義的__autoload()函數,這通常在PHP原始程式中來實現;
另外一種是設計一個函數,將autoload_func指標指向它,這通常使用C語言在PHP擴充中實現,即SPL autoload機制。
如果兩種方式都實作了,也就是 autoload_func 不等於NULL,程式只會執行第二種方式,__autoload() 函數是不會被執行的。
先看一個SPL 自動載入範例:
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'; } 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(); 结果:my_autoload 我是class B中的方法执行结果 ?>
在這個小例子,可以看到,透過spl_autoload_register('my_autoload'),實作了當程式執行找不到類別B時,會執行自訂的my_autoload()函數,載入B類。實際上 spl_autoload_register(’my_autoload’) 的作用就是 把autoload_func 指標指向 my_autoload()。現在,整個PHP 自動載入過程就明白了。
詳細分析SPL自動載入過程
#首先還是剛剛的小例子,假如把spl_autoload_register('my_autoload') 改成spl_autoload_register()不添加任何參數,B類能被載入嗎?答案是:YES。
為什麼呢?
因为SPL扩展内部自己定义了一个自动加载函数 spl_autoload(),实现了自动加载的功能,如果我们不定义自己的自动加载函数,并且程序里写了 spl_autoload_register()(如果不传参数,必须是第一次执行才会有效)或者 spl_autoload_register(’spl_autoload’),那么autoload_func 指针就会指向内部函数 spl_autoload()。程序执行的时候如果找不到相应类就会执行该自动加载函数。
那么,SPL 是怎么实现autoload_func 指针指向不同的函数呢?
原来,在SPL内部定义了 一个函数 spl_autoload_call() 和 一个全局变量autoload_functions。autoload_functions本质上是一个HashTable,不过我们可以将其简单的看作一个链表,链表中的每一个元素都是一个函数指针,指向一个具有自动加载类功能的函数。
spl_autoload_call()的作用就是按顺序遍历 autoload_functions,使得autoload_func指向每个自动加载函数,如果加载成功就停止,如果不成功就继续遍历下个自动加载函数,直到加载成功或者遍历完所有的函数。
那么,autoload_functions 这个列表是谁来维护的呢?就是 spl_autoload_register() 这个函数。我们说的自动加载函数的注册,其实就是通过spl_autoload_register()把自动加载函数加入到 autoload_functions 列表。
到此为止,整个自动加载的流程就是分析结束了。
相关SPL自动加载函数: spl_autoload_functions() //打印autoload_functions列表 spl_autoload_unregister() //注销自动加载函数
以上便是php自动加载原理的全部介绍,想了解更多相关内容请访问PHP中文网:PHP视频教程
以上是詳細介紹php自動載入原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!