程式地址
index.php
require __DIR__.'/../vendor/autoload.php';
跟蹤:index.php > /vendor/autoload.php
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit148a9da910429c7c016377bec97d275e::getLoader();
到達 composer 工作區
什麼是自動載入?
新下載的 laravel5.5 ,App 下預設有 User.php
,是 User 模型(Model)
$userModel = new App\User();
或
use App\User;
$userModel = new User();
如此方便得益於 composer 的自動載入。
composer 自動載入的流程
(一)類檔案歸納 驅動:composer
根據載入標準(files\classmap\psr-4\psr-0),將對映關係分門別類寫入不同的檔案,以陣列儲存
當在控制檯執行 composer require | update
引入一個元件時包,讀取組建包的 composer.json
中的 "autoload"
配置,分別按照每個組建包配置的自動載入規則,掃描元件中的檔案,將其寫入不同載入標準的php檔案陣列中。
那麼composer.json 中的自動載入通常有下面幾種方式 :(這不是真實的 autoload 配置,這是從 laravel5.5 各個組建包的 composer.json 中拼在一起的)
"autoload": {
這是 laravel/framework 組建包的 composer.json 中的
"files": [
"src/Illuminate/Foundation/helpers.php",
"src/Illuminate/Support/helpers.php"
],
這是 hamcrest/hamcrest-php 組建包的 composer.json 中的
"classmap": [
"hamcrest"
]
這是專案的 composer.json 中的
"psr-4": {
"App\\": "app/"
},
這是 mockery/mockery 組建包的 composer.json 中的
"psr-0": {
"Mockery": "library/"
}
},
files : 檔案預載入,框架啟動時便被 include ,通常檔案中提供一些函式方法方便我們使用,如經常用的 dd()
。"files:["src/Illuminate/Foundation/helpers.php"]"
寫入 /composer/autoload_files.php
,
autoload_files.php >
<?php
return array(
...
'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
);
classmap : 直接對映檔案真實路徑。 這是簡單粗暴的,因此這種方式效率是最高的。"classmap": ["hamcrest"],
寫入 /composer/autoload_classmap.php
autoload_classmap.php >
return array(
'Hamcrest\\Arrays\\IsArray' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArray.php',
'Hamcrest\\Arrays\\IsArrayContaining' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContaining.php',
'Hamcrest\\Arrays\\IsArrayContainingInAnyOrder' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingInAnyOrder.php',
...// hamcrest 資料夾下所有檔案
);
當 use Hamcrest\\Arrays\\IsArray;
時,可直接在 classmap 的陣列中找到它,是不是很粗暴呢。
psr-4 :這是最常用的載入標準。"psr-4": {"App\\": "app/"}
寫入 autoload_psr4.php
/composer/autoload_psr4.php >
return array(
...
'App\\' => array($baseDir . '/app'),// App 稱作 prefix
);
只要是 app/ 目錄下的檔案,且名命空間符合 psr-4 標準的類檔案都能被自動載入。如use App\Test
=> app/Test.php
。而 classmap 方式不可以,這樣你可以在 app/ 下自由的增加/刪除類檔案了。
這裡提一下,前面說 classmap 方式是最高效的,而 composer dump-autoload
可以將通過 psr-4 規範載入的檔案 “落盤”,即寫入 autoload_classmap.php 。從而起到加速的作用,官方建議生產環境時執行 composer dump-autoload --optimize
來優化專案的自動載入速度。
為了說明 composer dump-autoload
的作用,我在 app 目錄下建立 Test.php
<?php
namespace App;
class Test{
}
執行 composer dump-autoload
後,發現在autoload_classmap.php
與 autoload_static.php
(稍後再提及)中找到了它。
/composer/autoload_classmap.php && autoload_static.php >
return array(
...
'App\\Test' => __DIR__ . '/../..' . '/app/Test.php',
'App\\User' => $baseDir . '/app/User.php',
)
而 composer dump-autoload --optimize
的作用是進行優化(optimize),清理無效索引空間另外在 /composer
生成了 user
快取檔案。
psr-0 :和 psr-4 類似,只是載入規則有所不同。官方已棄用,但 laravel 有的組建包還是在用的,composer 仍然支援向下相容。"psr-0": {"Mockery": "library/"}
寫入 autoload_namespaces.php
。
/composer/autoload_namespaces.php >
return array(
...
'Mockery' => array($vendorDir . '/mockery/mockery/library'),
);
autoload_static.php
回顧 composer/ 目錄:
其中四個檔案中儲存了四種規範的對映,還有一個 autoload_static.php
檔案。它把這四個檔案中的所有對映集中在一起,通過 getInitializer()
方法注入到 ClassLoader
的屬性中( ClassLoader 是實現自動載入的類,稍後再提)
autoload_static.php >
<?php
namespace Composer\Autoload;
class ComposerStaticInitccb56ced66f82d50b9e1d3fd28a6ab26{
public static $files = array(..);
public static $classMap = array(..);
public static $prefixLengthsPsr4 = array(..);
public static $prefixDirsPsr4 = array(..);
public static $fallbackDirsPsr4 = array(..);
public static $prefixesPsr0 = array(..);
public static function getInitializer(ClassLoader $loader){..};
}
(二) 類檔案提取 驅動:/composer/autoload_real.php
從四個檔案中 或 autoload_static.php 中(因它包含了四個檔案的全部對映)將全部對映提取到實現自動載入的類(ClassLoader)中,由 ClassLoader 查詢類的對映,實現自動載入,
index.php > /vendor/autoload.php > /composer/autoload_real.php >
//重要概念:當前類的任務是把所有對映注入到ClassLoader中,由 ClassLoader 類實現自動載入。為了更好的說明,以下為簡化版程式碼。
class ComposerAutoloaderInitccb56ced66f82d50b9e1d3fd28a6ab26{
getLoader(){
----STEP1 例項化 ClassLoader-------
require __DIR__ . '/ClassLoader.php';
$loader = new ClassLoader();
----STEP2 提取對映並注入 ClassLoader-------
if ($useStaticLoader) {
//若符合執行環境,優先從 autoload_static.php 提取對映(見前面 autoload_static.php 的介紹)
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitccb56ced66f82d50b9e1d3fd28a6ab26::getInitializer($loader));
} else {
// 否則就從分別從三個檔案中提取出來並注入 ClassLoader
//psr-0
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
//psr-4
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
//classmap
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
----STEP3 STEP2完畢,啟動 ClassLoader 的自動載入方法-------
$loader->register(true); //spl_autoload_register
----STEP4 處理預載入檔案-------
//處理 autoload_files.php 中的預載入檔案,由於這些檔案需要立即載入,它和類的自動載入是不同的,只要載入後,檔案中的函式就能用。
//同理,能從 autoload_static.php 提取,優先從這個檔案提取。
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitccb56ced66f82d50b9e1d3fd28a6ab26::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
require $file;
}
//最後一句
return $loader;
};
}
(三) 實現自動載入 驅動:/composer/ClassLoader.php
ClassLoader.php >
class ClassLoader{
/**
* 將此例項註冊為自動載入程式
* -------------------------------------------
* new App/User();時將 'App/User'類命傳遞給 loadClass('App/User')方法
* 第二引數true debug
* $prepend為true,將 loadClass() 放在自動載入函式棧的第一個,即優先處理。
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* 載入給定的類或介面
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* 查詢定義類的檔案的路徑
* (二)類檔案提取 已將所有對映注入到此類中的陣列屬性中
* 從陣列中根據 Key 值取出
* 但並不是那麼簡單,本人沒有太過研究
*/
public function findFile($class){
//省略程式碼
return $file;
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結