自學TP5源碼(二)註冊自動加載機制

主要細看一下 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);
    }

複製代碼
1. 註冊系統自動加載

主要是用了函數 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;
    }
複製代碼
2. Composer 自動加載支持
// 若是根目錄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

3. 註冊命名空間定義
.
.
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

4. 加載類庫映射文件
// 若是 存在文件 /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 類型隊列

5. 加載composer autofile 文件
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個文件引入了。
複製代碼

在後面的章節筆記中,後面就一一看這四個文件。

相關文章
相關標籤/搜索