本文主要理解什麼是自動加載,爲什麼需要自動加載,自動加載的前世今生。
PHP-自動加載【自我概念理解】
PHP的自動加載,常見於各種框架的自動加載功能,PHP規範中的PSR0和PSR4原則,Composer的自動加載功能等等,這些都爲我們的開發提供了很大的方便。
在PHP面向對象(OO)編程中,爲了方便管理,我們都會把一個類寫在一個單獨的文件中。項目開發中我們難免要加載各種各樣的類,如果我們需要一個類,就require/include一個文件,這樣做的後果是效率非常低且文件頭太過累贅。由於require/include的做法不利於大型軟件開發,也就自然而然的引入了自動加載的方法。命名空間與自動加載賦予了PHP新的活力。讓PHP更具最新的軟件工程思想。
PHP5.2版本之前主要是通過require或者include載入依賴的文件資源。當項目變大的時候一個文件要依賴多個類就得在代碼之前寫很多行require/include來引入。PHP5.2之後,提供了類的自動載入功能,通過語法糖(魔術方法)function __autoload($class){require __DIR__.'/'.$class.'php;},在源碼運行上下文中一旦調用的類不存在,則會自動調用這個函數,這個函數會把調用的類名自動匹配項目路徑資源類文件,然後自動載入。可以理解爲__autoload魔術方法是require/include的自動化。
PHP7.2完全棄用了__autoload魔術方法,取而代之的是升級版的自動化載入:在php5.3之後,官方提供了一個spl_autoload_register()函數來取代__autoload()函數。__autoload魔術方法存在一個缺陷,當項目工程需要多個框架來搭建時。每個框架都有可能載入相同的類文件。這樣也就產生了重複加載錯誤。也就是說__autoload不夠足夠的智能化,__autoload在運行上下文中也只能定義一次。spl_autoload_register()正是解決__autoload的缺陷而誕生的。【PHP5實現了類的自動加載(Autoload)功能,這個功能最初是通過PHP的一個魔術方法__autoload()實現的。後來,PHP擴展SPL(Standard PHP Library 標準PHP類庫)又實現了更強大的自動加載機制。】
自動加載原理分析【轉載:晨風99的博客】
PHP原始自動加載
//文件 B.php
<?php
class B
{
public function echo_info()
{
echo "我是class B中的方法執行結果";
}
}
?>
//文件 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 自動加載【轉載:晨風99的博客】
首先,明確一點,PHP在實例化一個對象時(實際上在實現接口,使用類常數或類中的靜態變量,調用類中的靜態方法時都會如此),首先會在系統中查找該類(或接口)是否存在,如果不存在的話就嘗試使用autoload機制來加載該類。而autoload機制的主要執行過程爲:
- 檢查執行器全局變量函數指針autoload_func是否是NULL;
- 如果 autoload_func==NULL ,則查找系統是否定義 __autoload() 函數,如果定義了,則執行並返回加載結果。如果沒有定義,則報錯並退出;
- 如果 autoload_func 不等於NULL,則直接執行 autoload_func 指向的函數加載類,此時並不檢查 __autoload() 函數是否定義。
- 通過對PHP自動加載流程的瞭解,可以看到PHP實際上提供了兩種方法來實現自動裝載機制:
- 一種我們前面已經提到過,是使用用戶定義的__autoload()函數,這通常在PHP源程序中來實現;
- 另外一種就是設計一個函數,將autoload_func指針指向它,這通常使用C語言在PHP擴展中實現,即 SPL autoload機制。
如果兩種方式都實現了,也就是 autoload_func 不等於NULL,程序只會執行第二種方式,__autoload() 函數是不會被執行的。
先看一個 SPL 自動加載例子:
//文件 B.php
<?php
class B
{
public function echo_info()
{
echo "我是class B中的方法執行結果";
}
}
?>
//文件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 自動加載的整個過程。【轉載:晨風99的博客】
首先還是剛剛的小例子,假如把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() //註銷自動加載函數
————————————————
版權聲明:本節爲CSDN博主「晨風99」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_37356656/article/details/78642520
【注意】SPL自動加載需要注意的地方
因爲命名空間的規範,我們處理引用路徑時需要特別注意。命名空間增加了自動加載的路徑方便性,但同時需要處理一下命名空間帶來的路徑字符處理。當我們使用命名空間時:use語句。當前上下文中我們要創建的加載類名,其實就是命名空間名。也就是說,在處理require_once($file)時,$class正是命名空間。
我們看一下SPL自動加載時路徑的變化:
// sdk/MyPrint.php
namespace sdk;
class MyPrint
{
private $codechar = "河南省開軟網絡科技有限公司:KaiRuanSoft";
public function doprint()
{
echo $this->codechar;
}
}
//index.php
use sdk\MyPrint;
function my_autoload($class)
{
$file =__DIR__ . '\\' . $class . '.php';
echo '<H1>引用命名空間後$class的真實值就是命名空間:'.$class.'</H1>';
echo '<h1>這是$file存儲的的路徑:'.$file.'</H1>';
if(is_file($file))
{
require_once($file);
}
else
{
throw new Exception('Load File Error!');
}
}
try
{
spl_autoload_register('my_autoload');
$display = new MyPrint();
$display->doprint();
}
catch(Throwable $e)
{
echo '<p>'.$e->getMessage();
}
運行程序後的結果:
引用命名空間後$class的真實值就是命名空間:sdk\MyPrint
這是$file存儲的的路徑:E:\www\php\sdk\MyPrint.php
河南省開軟網絡科技有限公司:KaiRuanSoft
運行程序後,創建的自動加載類MyPrint()的類名已經通過命名空間替換爲:sdk\MyPrint。
所以我們在組合類文件真實路徑時是這樣的:$file =__DIR__ . '\\' . $class . '.php';
命名空間和自動加載的關係
命名空間採用use來區分類、方法的作用域。相同的類和方法不一定是相同的。說白了就是防止同名污染。自動加載是引用類。命名空間和自動加載是相輔相成達到引用類資源的目的。但命名空間和自動加載本身而言沒有半毛錢關係。use不會觸發自動加載,只有在new時才觸發自動加載。