tp5底層源碼分析之——tp5.1類的自動載入機制

  • 2019 年 11 月 1 日
  • 筆記

tp框架作為中國主流框架,目前已經發布了6.0版本,相當於3.*版本是進行了重構,今天我們從源碼的角度來研究下tp5.1自動載入的實現

    作為單入口框架,從入口文件看起,入口文件在public/下,那麼為什麼大多數框架要把入口文件放到子文件夾下面呢?

 第一,為了動靜分離,因為現在的tp框架一般都是單入口,既然是單入口,那麼必然要做rewrite,如果把靜態文件和程式文件放到一起。

框架路由勢必要對每一個請求進行篩選,所以這些框架不約而同的把資源文件和程式文件區分開來,放在了不同的文件夾下面,所以從整體

來看,也就是為什麼入口會在子目錄了。

   第二,為了安全,linux下的許可權劃分非常嚴格,分貝氛圍讀,寫,執行。在這個基礎上又分為文件所有組,所在組,其他組。這樣劃分可以

更好的對文件許可權進行梳理,避免上傳漏洞(用戶上傳php文件被執行)等等。

    1.我們來看下入口文件:

 

    2. tp5.1入口文件引入載入了base.php文件,然後base.php文件中載入了loader.php類,並且執行了Loader::register()靜態方法,我們來看看register方法內部執行了什麼?

 2.1)在第79行也就是register()方法中執行了內置函數apl_autoloader_register(),此函數的第一個參數接收一個匿名函數,或者回調方法,作用是每當php

調用了不存在的類時就會執行此函數當中的回調函數,且攜帶一個參數,值是引入的未存在的帶命名空間的類名(如果有類名空間),如在base.php20行註冊異常機制,那麼這是攜帶的參數值是:thinkError.

  

 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中的類,返回值是:ComposerAutoloadComposerStaticInit3ec0ccb9b30037c3270e4e4566239878

2.5)91行,循環將剛才獲得到的類中 成員屬性 複製到本類Loader中, 在商法的類中存在兩個靜態成員屬性:$prefixLengthsPsr4、$prefixDirsPsr4。形式如下圖:

 這兩個成員屬性是根據psr-4規範規則 而生成,不懂的可自己百度了解。這裡將成員屬性複製到本類後,後面載入文件時查找類的文件路徑會用到,下面再講。

3)我們來看下註冊命名空間定義:註冊think和tratis兩個兩個文件夾路徑,調用self::addNamespace方法,主要做的事情就是將這兩個命名空間、文件路徑以psr-4規範形式 加入到上面所提的兩個成員屬性中$prefixLengthsPsr4、$prefixDirsPsr4。$prefixLengthsPsr4規則:將命名空間首字母當做第一維數組的鍵,將命名空間當做第二維數組的鍵,將此命名空間字元串長度當做第二維數組的值。$prefixDirsPsr4規則:將命名空間當做第一維數組的鍵,將對應的文件絕對路徑當做第二維數組的值,第二位數組的鍵是自增的索引值。此時本類中的靜態成員屬性$prefixLengthsPsr4、$prefixDirsPsr4的值如下圖:

 4).106行載入類庫映射文件,

它會查找項目根目錄下runtimeclassmap.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異曲同工