PHP自动加载机制
概述
首先,為什么PHP需要自動加載呢?
在PHP面向對象(OO)編程中,為了方便管理,我們都會把一個類寫在一個單獨的文件中,那么如果想在A類中使用B類的功能,就需要把B類加載到A類。對于這樣的需求在最原始的時候,我們是通過require 和 include 語法實現將文件加載到另一個文件中,include 和 require 是PHP中引入文件的兩個基本方法。
在小規模開發中直接使用 include 和 require 不會有什么不妥,但在大型項目中會造成大量的 include 和 require 堆積。這樣的代碼既不優雅,執行效率也很低,而且維護起來也相當困難。
PHP的自動加載功能,框架實現自動加載的包括PHP規范中的PSR0和PSR4原則,Composer的自動加載功能等等。
php加載文件方式:
include()與require()
簡單的文件加載方法:
require()
包含的意思,找不到文件時,會報warning的錯誤,然后程序繼續往下執行。include語句只有在被執行時才會讀入要包含的文件。在錯誤處理方便,使用include語句,如果發生包含錯誤,程序將跳過include語句,雖然會顯示錯誤信息但是程序還是會繼續執行!php處理器會在每次遇到include()語句時,對它進行重新處理,所以可以根據不同情況的,在條件控制語句和循環語句中使用include()來包含不同的文件。
include()
必須的意思,找不到文件時,會報fatal error(致命錯誤),程序停止往下執行。在php文件被執行之前,php解析器會用被引用的文件的全部內容替換require語句,然后與require語句之外的其他語句組成個新的php文件,最好后按新的php文件執行程序代碼。
require_once()
類似于include(),系統會進行判斷,如果已經包含,則不會再包含第二次。
include_once()
類似于require(),系統會進行判斷,如果已經包含,則不會再包含第二次。
– 共同點:能包含位于獨立文件中的代碼,可以減少代碼的重復,實現代碼結構的模塊化,方便調用。
注意事項:
加載文件格式
include/require 包含進來的文件必須要加<?php ?>因為在包含時,首先理解文件內容是普通字符串,碰到<?php ?>標簽時,才去解釋。
路徑要求
可以用絕對路徑,也可以用相對路徑;windows下正反斜線都可以,linux下只認正斜線,所以最好用正斜線。
如何選擇
比如是系統配置,缺少了,網站不讓運行,自然用require,如果是某一段統計程序,少了,對網站只是少統計人數罷了,不是必須要的,可以用include而加不加once是效率上的區別,加上once,雖然系統幫你考慮了只加載一次,但系統的判斷會是效率降低,因此,更應該在開發之初,就把目錄結構調整好,盡量不要用_once的情況。
特殊用法
利用include/require返回被包含頁面的返回值
1
2a.php頁面中: ..... return $value;
b.php頁面中:$v = include("a.php");
__autoload()自動加載
PHP5及之后的版本,使用尚未定義的類時會自動調用__autoload函數,所以我們可以通過編寫__autoload函數來讓php自動加載類,而不必寫一個長長的包含文件列表。
需明確的是對于__autoload()函數,PHP在找不到類的時候會自動執行,但是PHP內部并沒有定義這個函數。
這個函數需要開發者自定義,并且編寫內部邏輯,PHP只負責在需要的時候自動調用執行。而且在調用的時候會自動傳人要加載的類名作為參數。
用法:首先需要在需要加載文件的代碼中,定義__autoload()函數,并且編寫內部邏輯。PHP在找不到類的時候會自動執行__autoload()函數。下面是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中的方法執行結果“缺陷:一個項目中僅能有一個這樣的 __autoload() 函數,因為 PHP 不允許函數重名,也就是說你不能聲明2個__autoload()函數文件,否則會報致命錯誤。
如果項目比較大,加載每個文件都使用同樣的規則顯然是不現實的,那么我們可能就需要在__autoload()中編寫復雜的規則邏輯來滿足加載不同文件的需求。
這同樣會使得__autoload()函數變得復雜臃腫,難以維護管理。
SPL 自動加載
PHP在實例化一個對象時(實際上在實現接口,使用類常數或類中的靜態變量,調用類中的靜態方法時都會如此),首先會在系統中查找該類(或接口)是否存在,如果不存在的話就嘗試使用autoload機制來加載該類。而autoload機制的主要執行過程為:
- 檢查執行器全局變量函數指針autoload_func是否是NULL;
- 如果 autoload_func==NULL ,則查找系統是否定義 __autoload() 函數,如果定義了,則執行并返回加載結果。如果沒有定義,則報錯并退出;
- 如果 autoload_func 不等于NULL,則直接執行 autoload_func 指向的函數加載類,此時并不檢查 __autoload() 函數是否定義。
通過上述PHP自動加載流程,可知PHP實際上提供了兩種方法來實現自動裝載機制:(1)使用用戶定義的__autoload()函數,這通常在PHP源程序中來實現;(2)設計一個函數,將autoload_func指針指向它,這通常使用C語言在PHP擴展中實現,即 SPL autoload機制,即本節中的SPL自動加載。如果兩種方式都實現了,也就是 autoload_func 不等于NULL,程序只會執行第二種方式,__autoload() 函數是不會被執行的。
用法
通過spl_autoload_register('my_autoload'),實現了 當程序執行找不到類B時,會執行 自定義的 my_autoload()函數,加載B類。實際上 spl_autoload_register('my_autoload') 的作用就是 把autoload_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();
結果:my_autoload 我是class B中的方法執行結果
?>SPL 自動加載整個過程
針對上述的示例,假如把spl_autoload_register('my_autoload')改成 spl_autoload_register()不添加任何參數,B類也能被加載。
為什么呢?
因為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自動加載函數
1
2spl_autoload_functions() //打印autoload_functions列表
spl_autoload_unregister() //注銷自動加載函數
參考:
總結
- 上一篇: 【软件测试】等价类划分
- 下一篇: PHP开发API