提及PHP的自動加載,不少同窗可能都會想到各類框架的自動加載功能,PHP規範中的PSR0和PSR4原則,Composer的自動加載功能等等,這些都爲咱們的開發提供了很大的方便。php
那麼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類庫)又實現了更強大的自動加載機制。函數
首先,先介紹下__autoload()方法。仍是剛剛的例子,使用__autoload()能夠作以下修改:ui
//文件 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只負責在須要的時候自動調用執行。並且在調用的時候會自動傳人要加載的類名做爲參數。spa
有了__autoload()函數,能夠看出,若是咱們如今須要引入100個其它文件,只須要訂好一個規則,編寫一個函數就能夠了。這比直接用require/inlude有了很大進步,可是一樣也有新的問題,在一個項目中,咱們只能編寫一個__autoload()函數,若是項目比較大,加載每一個文件都使用一樣的規則顯然是不現實的,那麼咱們可能就須要在__autoload()中編寫複雜的規則邏輯來知足加載不一樣文件的需求。這一樣會使得__autoload()函數變得複雜臃腫,難以維護管理。.net
因而,SPL(Standard PHP Library 標準PHP類庫)的自動加載機制就應時而生了。命令行
首先,明確一點,PHP在實例化一個對象時(實際上在實現接口,使用類常數或類中的靜態變量,調用類中的靜態方法時都會如此),首先會在系統中查找該類(或接口)是否存在,若是不存在的話就嘗試使用autoload機制來加載該類。而autoload機制的主要執行過程爲:設計
經過對PHP自動加載流程的瞭解,能夠看到PHP實際上提供了兩種方法來實現自動裝載機制:指針
若是兩種方式都實現了,也就是 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_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() //註銷自動加載函數