PHP-自動加載原理分析

提及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中的方法執行結果「
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

因而,PHP5實現了類的自動加載(Autoload)功能,這個功能最初是經過PHP的一個魔術方法__autoload()實現的。後來,PHP擴展SPL(Standard PHP Library 標準PHP類庫)又實現了更強大的自動加載機制。函數

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中的方法執行結果「
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

咱們在A文件中加了一個函數:__autoload(),而且本身在函數中編寫了相應的引入方法,運行以後一樣獲得告終果,沒有報錯。咱們須要明確 __autoload()函數PHP在找不到類的時候會自動執行,可是PHP內部並無定義這個函數,這個函數須要開發着本身定義,而且編寫內部邏輯,PHP只負責在須要的時候自動調用執行。並且在調用的時候會自動傳人要加載的類名做爲參數。spa

有了__autoload()函數,能夠看出,若是咱們如今須要引入100個其它文件,只須要訂好一個規則,編寫一個函數就能夠了。這比直接用require/inlude有了很大進步,可是一樣也有新的問題,在一個項目中,咱們只能編寫一個__autoload()函數,若是項目比較大,加載每一個文件都使用一樣的規則顯然是不現實的,那麼咱們可能就須要在__autoload()中編寫複雜的規則邏輯來知足加載不一樣文件的需求。這一樣會使得__autoload()函數變得複雜臃腫,難以維護管理。.net

因而,SPL(Standard PHP Library 標準PHP類庫)的自動加載機制就應時而生了。命令行

SPL 自動加載

首先,明確一點,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文件不變
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中的方法執行結果 ?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在這個小例子,能夠看到,經過 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() //註銷自動加載函數
  • 1
  • 2
  • 3
  • 4

參考資源: 
http://www.jb51.net/article/31279.htm

相關文章
相關標籤/搜索