這是兩篇參考文章
事實上,thinkphp 框架用的是 composer 的自動載入
public/index.php 中第一句
namespace think;
require __DIR__ . '/../vendor/autoload.php';
接下來 vender/autoload.php
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit00730f6ba716ad33a51233e55145c868::getLoader();
這裡引入的 autoload_real.php 中定義的 ComposerAutoloaderInit 類名後面帶了一串雜湊值,可以這樣做的原因可能是為了保證類名的唯一性,防止和使用者自定義的類名衝突
2.1 單例
public static function getLoader()
{
// 單例模式,這個很好理解
if (null !== self::$loader) {
return self::$loader;
}
...
}
2.2 例項化ClassLoader核心類
public static function getLoader()
{
...
spl_autoload_register(array('ComposerAutoloaderInit00730f6ba716ad33a51233e55145c868', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit00730f6ba716ad33a51233e55145c868', 'loadClassLoader'));
...
}
loadClassLoader 方法
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
這裡它先將本類的 loadClassLoader 註冊為自動載入方法,用完後又登出掉,而 loadClassLoader 方法唯一的用處就是引入 ClassLoader.php 檔案,為什麼不直接引入而要繞這麼一圈呢?
前面介紹 laravel自動載入的文章中有對這裡做了解釋,但是我還是看不明白
2.3 將autoload_static.php中定義的自動載入對映關係合併到ClassLoader類中
ClassLoader 類的 prefixLengthsPsr4、prefixDirsPsr4 等屬性是私有的,要操作這些私有屬性,這裡有兩種方式,第一種是使用匿名函式繫結 Closure::bind,第二種是呼叫 ClassLoader 類的公有方法
第二種方式很好理解,我們來看下第一種方式
require_once __DIR__ . '/autoload_static.php';
// ComposerStaticInit00730f6ba716ad33a51233e55145c868::getInitializer 方法返回一個匿名函式物件,call_user_func 執行這個匿名函式
call_user_func(\Composer\Autoload\ComposerStaticInit00730f6ba716ad33a51233e55145c868::getInitializer($loader));
看下 getInitializer 方法
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit00730f6ba716ad33a51233e55145c868::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit00730f6ba716ad33a51233e55145c868::$prefixDirsPsr4;
$loader->fallbackDirsPsr0 = ComposerStaticInit00730f6ba716ad33a51233e55145c868::$fallbackDirsPsr0;
}, null, ClassLoader::class);
}
關於 Closure::bind 方法,文件中給的解釋是:複製一個閉包,繫結指定的$this物件和類作用域。不太好理解
第一個引數是要繫結的匿名函式,這個容易理解
第二個引數是匿名函式中 $this 指代的物件,如果需要的話,方式是 new ClassName(),不需要就如文中傳 null
第三個引數指定匿名函式的類作用域,簡單來說就是如果匿名函式中有用到 private 或 protected 的屬性或方法時,這裡傳入對應的類名,如$loader->prefixLengthsPsr4 是私用的,這裡傳的就是 $loader 例項對應的類名。如果將prefixLengthsPsr4
改為公有的,ComposerStaticInit...中的屬性改為私有的,那這個引數就要填ComposerStaticInit...的類名了
我們知道,類物件的傳遞都是引用傳遞,所以這裡匿名函式中修改 $loader 的屬性值,ComposerAutoloaderInit...中定義的 $loader也修改了
2.4 PSR0 和 PSR4 的區別
在類名中使用下劃線沒有任何特殊含義
名稱空間與檔案目錄的對映方法有所調整,假如我們有一個名稱空間: Foo/class ,Foo 是頂級名稱空間,其存在著使用者定義的與目錄的對映關係: "Foo/"=>"src/" 按照PSR0標準,對映後的檔案目錄是:src/Foo/class.php, 但是按照 PSR4標準,對映後的檔案目錄就會是:src/class.php
PSR4的名稱空間最後一位必須是\
PSR0 的最後一個\後如果有_,將會轉化為分隔符
2.5 將ClassLoader類的loadClass方法註冊為自動載入函式
public function register($prepend = false)
{
// sql_autoload_register 註冊的自動載入函式是一個佇列的形式,如果你有自己的自動載入方法,需要在 composer的自動載入找不到的情況下呼叫,就用 sql_autoload_register 註冊你的,第三個引數傳false
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
2.6 loadClass方法中最主要的findFileWithExtension方法
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
// 先獲取首字母,查詢prefixLengthsPsr4中是否有匹配
// 擷取最後一個分隔符之前的部分,查詢prefixDirsPsr4中是否有匹配,這裡用的while迴圈,沒有找到的話就繼續擷取上一個分隔符前面部分
// 問題:為什麼不是從prefixLengthsPsr4首字母對應的值中匹配?難道是因為prefixDirsPsr4的數量不多的原因麼?
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
// 如果上面沒有找到,就去extend資料夾下去找(對這裡的fallbackDirsPsr4 來說就是這樣的),TP的官方文件中也有說在extend中的每一個資料夾都是一個自定義根名稱空間
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// 後面的是PSR0的匹配,就不看了
.
.
return false;
}
2.7 includeFile
注意下面的註釋部分,這個函式被放在了類的外面,以防止引入的檔案中呼叫$this或self
include和require的區別中有一點,require放在程式的最前面,include可以放在任意位置,所以這裡需要用 include
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結