tp框架做爲國內主流框架,目前已經發布了6.0版本,至關於3.*版本是進行了重構,今天咱們從源碼的角度來研究下tp5.1自動加載的實現php
做爲單入口框架,從入口文件看起,入口文件在public/下,那麼爲何大多數框架要把入口文件放到子文件夾下面呢?linux
第一,爲了動靜分離,由於如今的tp框架通常都是單入口,既然是單入口,那麼必然要作rewrite,若是把靜態文件和程序文件放到一塊兒。數組
框架路由勢必要對每個請求進行篩選,因此這些框架不約而同的把資源文件和程序文件區分開來,放在了不一樣的文件夾下面,因此從總體安全
來看,也就是爲何入口會在子目錄了。composer
第二,爲了安全,linux下的權限劃分很是嚴格,分貝氛圍讀,寫,執行。在這個基礎上又分爲文件全部組,所在組,其餘組。這樣劃分能夠框架
更好的對文件權限進行梳理,避免上傳漏洞(用戶上傳php文件被執行)等等。函數
1.咱們來看下入口文件:spa
2. tp5.1入口文件引入加載了base.php文件,而後base.php文件中載入了loader.php類,而且執行了Loader::register()靜態方法,咱們來看看register方法內部執行了什麼?命令行
2.1)在第79行也就是register()方法中執行了內置函數apl_autoloader_register(),此函數的第一個參數接收一個匿名函數,或者回調方法,做用是每當php3d
調用了不存在的類時就會執行此函數當中的回調函數,且攜帶一個參數,值是引入的未存在的帶命名空間的類名(若是有類名空間),如在base.php20行註冊異常機制,那麼這是攜帶的參數值是:think\Error.
2.2)繼續往下看,Loader類中的80跟81行,分別是得到本項目的絕對路徑以及得到vender目錄下composer文件夾的絕對路徑,咱們打印輸出看下
2.3)85行後面,判斷是否存在composer文件夾,是否存在autoload_static.php 文件,由於5.1版本後,php官網再也不提供下載版本,只支持經過composer下載,因此這個文件必定是存在的。而後加載了這個類文件。
2.4)89行執行了 get_declared_classes() 函數,此函數功能是獲取由當前腳本中已定義類的名字組成的數組(包括本身引入的類,和php內置的一些類)。而後90行取出此數組中最後一個元素,也就是剛剛引入的autoload_static.php中的類,返回值是:Composer\Autoload\ComposerStaticInit3ec0ccb9b30037c3270e4e4566239878
2.5)91行,循環將剛纔得到到的類中 成員屬性 複製到本類Loader中, 在商法的類中存在兩個靜態成員屬性:$prefixLengthsPsr四、$prefixDirsPsr4。形式以下圖:
這兩個成員屬性是根據psr-4規範規則 而生成,不懂的可本身百度瞭解。這裏將成員屬性複製到本類後,後面加載文件時查找類的文件路徑會用到,下面再講。
3)咱們來看下注冊命名空間定義:註冊think和tratis兩個兩個文件夾路徑,調用self::addNamespace方法,主要作的事情就是將這兩個命名空間、文件路徑以psr-4規範形式 加入到上面所提的兩個成員屬性中$prefixLengthsPsr四、$prefixDirsPsr4。$prefixLengthsPsr4規則:將命名空間首字母當作第一維數組的鍵,將命名空間當作第二維數組的鍵,將此命名空間字符串長度當作第二維數組的值。$prefixDirsPsr4規則:將命名空間當作第一維數組的鍵,將對應的文件絕對路徑當作第二維數組的值,第二位數組的鍵是自增的索引值。此時本類中的靜態成員屬性$prefixLengthsPsr四、$prefixDirsPsr4的值以下圖:
4).106行加載類庫映射文件,
它會查找項目根目錄下\runtime\classmap.php文件,將此文件中的一維數組值賦值到本類成員屬性$classMap。這個文件是經過執行tp5.1命令行命令:php think optimize:autoload 生成的。生成的文件中包含了全部將要引入的類的 類名與文件絕對路徑 的映射,此文件會提升尋找類文件的效率,通常項目完成時生成,若是後續有新建的類的話,此文件須要從新生成才能尋找到新的類文件。後面會講到爲什麼會提升加載類的效率。默認是沒有此文件的。
5)此方法的最後一行118行,自動加載extend目錄,調用self::addAutoLoadDir()方法,作的事情是:將項目根目錄下的extend目錄絕對路徑放到 成員屬性 $fallbackDirsPsr4中。
6)Loader::autoload自動加載時執行的方法
上面說到 spl_autoload_register()函數,若是調用不存在的類時將執行此函數中的第一個參數方法。那麼將調用本類中的autoload方法。此方法代碼:
上面說了,此方法的參數$class的值的帶有命名空間的類名,127行判斷本類中的成員屬性$classAlias中是否存在此類名,若是存在,則將此類名設置別名,開始此屬性值是空數組。繼續往下,調用了self::findFile方法並將類名當作參數傳入,返回$file值,下方140行引入此文件。咱們來看看findFile方法作了哪些事情:
6.1)143行,首先判斷成員屬性$classmap中是否存在此類名的鍵,若是存在則直接返回。咱們上方說到此成員屬性值是執行tp5.1命令行命令生成的,因此生成文件後就再也不往下執行 循環判斷文件是否存在,直接返回文件路徑,因此能夠提升文件查找效率。
6.2)
if中執行的邏輯是根據psr-4規範查找類文件,具體是這樣:取出這個帶有命名空間類的第一個字母,判斷成員屬性$prefixLengthsPsr4是否存在此首字母的鍵值,而後循環這個鍵值,判斷命名空間有沒有是否存在$prefixLengthsPsr4二維的鍵,若是存在,根據$prefixDirsPsr4屬性取出命名空間對應的絕對路徑,而後拼接文件名,判斷文件是否存在,若是存在則返回文件的絕對路徑。
6.3)
循環$fallbackDirsPsr4屬性下全部值拼接文件名,返回絕對路徑並判斷文件是否存在,若存在則返回。
6.4):
根據psr-0規則判斷文件是否存在並返回,跟上方的psr-4殊途同歸