主要細看一下 register 這個函數,在上一章節的結尾頭有\think\Loader::register();php
/** * 註冊自動加載機制 * @access public * @param callable $autoload 自動加載處理方法 * @return void */
public static function register($autoload = null) {
// 註冊系統自動加載
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
// Composer 自動加載支持
if (is_dir(VENDOR_PATH . 'composer')) {
if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
$declaredClass = get_declared_classes();
$composerClass = array_pop($declaredClass);
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
if (property_exists($composerClass, $attr)) {
self::${$attr} = $composerClass::${$attr};
}
}
} else {
self::registerComposerLoader();
}
}
// 註冊命名空間定義
self::addNamespace([
'think' => LIB_PATH . 'think' . DS,
'behavior' => LIB_PATH . 'behavior' . DS,
'traits' => LIB_PATH . 'traits' . DS,
]);
// 加載類庫映射文件
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
}
self::loadComposerAutoloadFiles();
// 自動加載 extend 目錄
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
}
複製代碼
主要是用了函數 spl_autoload_register
函數裏寫到的是thinkphp
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
複製代碼
說明 若是參數裏傳了要註冊加載的函數(裏面寫了須要引入require的類),那麼就直接調用,不然,就是將Loader
類裏的 autoload
方法傳進去,而後第二個參數爲true
,意思是沒法成功註冊類的時候,拋出異常,第三個參數爲 true
的意思是將其要註冊的類排在隊列之首。數組
下面看看autuload
方法。bash
/** * 自動加載 * @access public * @param string $class 類名 * @return bool */
public static function autoload($class) {
// 首先檢測了是否存在命名空間別名數組是否有值,
if (!empty(self::$namespaceAlias)) { // 若是有
$namespace = dirname($class); // 獲取該類的目錄結構
if (isset(self::$namespaceAlias[$namespace])) { // 查看數組裏是否有存
$original = self::$namespaceAlias[$namespace] . '\\' . basename($class); // 將類完整的命名空間拼接起來
if (class_exists($original)) { // 若是存在該類
return class_alias($original, $class, false); // 將其命類別名
}
}
}
if ($file = self::findFile($class)) { // 查找文件 (後面再詳看)
// 非 Win 環境不嚴格區分大小寫
if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) {
__include_file($file); // 引入include該文件
return true;
}
}
return false;
}
複製代碼
// 若是根目錄vendor下面有composer目錄
if (is_dir(VENDOR_PATH . 'composer')) {
// 若是php版本大於5.6, 而且composer目錄下有 autoload_static.php 文件
if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
// 將該php文件引入進來。
require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
// 獲取引入過的全部的類名
$declaredClass = get_declared_classes();
// 彈出剛纔引入的那個
$composerClass = array_pop($declaredClass);
// 循環這些字符串命名
foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
// 若是有該屬性,就在loader類裏也存該屬性
if (property_exists($composerClass, $attr)) {
self::${$attr} = $composerClass::${$attr};
}
}
} else {
// 不然 註冊 composer 自動加載
self::registerComposerLoader();
}
}
複製代碼
這裏TP裏 是有 autoload_static.php
這個文件 ,因此走上面那個 if
裏的程序。 繼續往下走。composer
.
.
self::addNamespace([
'think' => LIB_PATH . 'think' . DS,
'behavior' => LIB_PATH . 'behavior' . DS,
'traits' => LIB_PATH . 'traits' . DS,
]);
.
.
複製代碼
這裏的關鍵就是Loader
類裏的 addNamespace
函數了。函數
/** * 註冊命名空間 * @access public * @param string|array $namespace 命名空間 * @param string $path 路徑 * @return void */
public static function addNamespace($namespace, $path = '') {
// 若是是數組 剛纔傳的是一個數組。
if (is_array($namespace)) {
foreach ($namespace as $prefix => $paths) { // 進行遍歷
self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
}
} else {
self::addPsr4($namespace . '\\', rtrim($path, DS), true);
}
}
複製代碼
拿遍歷的第一次進行分析,看是怎樣經過 addPsr4
方法添加。ui
/** * 添加 PSR-4 空間 * @access private * @param array|string $prefix 空間前綴 * @param string $paths 路徑 * @param bool $prepend 預先設置的優先級更高 * @return void */
private static function addPsr4($prefix, $paths, $prepend = false) {
// $prefix: "think\"
// $paths: "C:\laragon\www\mini-project\thinkphp\library\think"
// $prepend: true
// 是否傳了prefix
if (!$prefix) {
// Register directories for the root namespace.
// 沒有就直接放到 $fallbackDirsPsr4 數組屬性裏
// $prepend 決定是否放在數組最前面。
self::$fallbackDirsPsr4 = $prepend ?
array_merge((array) $paths, self::$fallbackDirsPsr4) :
array_merge(self::$fallbackDirsPsr4, (array) $paths);
// 若是 $prefixDirsPsr4 數組裏不存在 think\ 這個鍵名
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix); // 計算鍵名的長度
if ('\\' !== $prefix[$length - 1]) { // 看鍵名末尾是否有 "\"
throw new \InvalidArgumentException(
"A non-empty PSR-4 prefix must end with a namespace separator."
);
}
// 最後按照 autoload_static.php 那種格式存起來。
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
self::$prefixDirsPsr4[$prefix] = (array) $paths;
// 若是有$prefixDirsPsr4對應的鍵名
} else {
// 存起來
self::$prefixDirsPsr4[$prefix] = $prepend ?
// Prepend directories for an already registered namespace.
array_merge((array) $paths, self::$prefixDirsPsr4[$prefix]) :
// Append directories for an already registered namespace.
array_merge(self::$prefixDirsPsr4[$prefix], (array) $paths);
}
}
// 假設之前沒有這個 `think/` 這個鍵名,最後$prefixDirsPsr4數組就應該是
/** $prefixLengthsPsr4 = [ ... 't' => [ ... 'think\\' => 6 ], ]; $prefixDirsPsr4 = [ ... 'think\\' => ['C:\laragon\www\mini-project\thinkphp\library\think'] ]; 對比composer目錄下的 autoload_static.php文件類裏的屬性 **/
複製代碼
這樣就按psr4
的規則將這個類存到了數組 $prefixDirsPsr4
和數組 $prefixLengthsPsr4
等着註冊就好了。spa
// 若是 存在文件 /runtime/classmap.php
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
// 就添加映射
self::addClassMap(_include_file(RUNTIME_PATH . 'classmap' . EXT));
}
複製代碼
addClassMap方法code
/** * 註冊 classmap * @access public * @param string|array $class 類名 * @param string $map 映射 * @return void */
public static function addClassMap($class, $map = '') {
// 通常映射文件裏都是寫成數組格式,能夠參考composer裏的文件。
if (is_array($class)) {
self::$classMap = array_merge(self::$classMap, $class);
} else {
self::$classMap[$class] = $map;
}
}
複製代碼
通常狀況下是沒有的,接着看關鍵的自動加載composer的文件 file 類型隊列
self::loadComposerAutoloadFiles();
複製代碼
且細看 這個方法
public static function loadComposerAutoloadFiles() {
// 開始將靜態變量 $files 裏的文件進行遍歷 require加載 了
foreach (self::$files as $fileIdentifier => $file) {
// 查看是否有全局變量 __composer_autoload_files 若是是空的 就進行記錄。
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
// require進來
__require_file($file);
// 進行記錄
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
}
而在開始的時候就已經將 autoload_static.php 裏的文件就加載到 files裏了。
self::$files = [
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
'ddc3cd2a04224f9638c5d0de6a69c7e3' => __DIR__ . '/..' . '/topthink/think-migration/src/config.php',
'cc56288302d9df745d97c934d6a6e5f0' => __DIR__ . '/..' . '/topthink/think-queue/src/common.php',
];
這樣對應着看就很清晰了,就把這4個文件引入了。
複製代碼
在後面的章節筆記中,後面就一一看這四個文件。