Thinkphp5類加載機制

版權聲明:本文爲予懷的原創文章,未經博主允許不得轉載。PS:就算要轉,也要註明轉載地址 :) https://blog.csdn.net/qq_26683009/article/details/60467948

Thinkphp5類加載機制


PS:本文適用於熟悉Thinkphp框架或其他MVC框架,對於命名空間及自動加載類,有一定理解的程序工作者觀看(其實是寫給自己看的>_<—予懷之言)

我一直對於thinkphp5的加載運行的時候做了什麼,他是怎麼自動加載類的,是和composer一樣的嗎—能否實現運行時再加載類,於是做了一下測試,追蹤了整個加載流程。

 

以文件爲單位進行講解:

入口文件index.php

<?php
// 定義應用目錄
define('APP_PATH', __DIR__ . '/../application/');
// 加載框架引導文件
require __DIR__ . '/../thinkphp/start.php';

 

1. 定義了APP_PATH常量

2. 加載start.php文件

 

start.php文件

<?php
namespace think;

// ThinkPHP 引導文件
// 加載基礎文件
require __DIR__ . '/base.php';
// 執行應用
App::run()->send();

 

1. 加載base.php

2. 執行App類的靜態方法run(),再執行返回值的send()方法

來到這裏,出現兩個問題,App類。。。是從哪裏加載進來的呢?看看base.php

 

base.php文件

<?php
define('THINK_VERSION', '5.0.3');
define('THINK_START_TIME', microtime(true));
define('THINK_START_MEM', memory_get_usage());
define('EXT', '.php');
define('DS', DIRECTORY_SEPARATOR);
defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS);
define('LIB_PATH', THINK_PATH . 'library' . DS);
define('CORE_PATH', LIB_PATH . 'think' . DS);
define('TRAIT_PATH', LIB_PATH . 'traits' . DS);
defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . DS);
defined('ROOT_PATH') or define('ROOT_PATH', dirname(realpath(APP_PATH)) . DS);
defined('EXTEND_PATH') or define('EXTEND_PATH', ROOT_PATH . 'extend' . DS);
defined('VENDOR_PATH') or define('VENDOR_PATH', ROOT_PATH . 'vendor' . DS);
defined('RUNTIME_PATH') or define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS);
defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS);
defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH . 'cache' . DS);
defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS);
defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目錄
defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件後綴
defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 環境變量的配置前綴

// 環境常量
define('IS_CLI', PHP_SAPI == 'cli' ? true : false);
define('IS_WIN', strpos(PHP_OS, 'WIN') !== false);

// 載入Loaderrequire CORE_PATH . 'Loader.php';

// 加載環境變量配置文件
if (is_file(ROOT_PATH . '.env')) {
    $env = parse_ini_file(ROOT_PATH . '.env', true);
    foreach ($env as $key => $val) {
        $name = ENV_PREFIX . strtoupper($key);
        if (is_array($val)) {
            foreach ($val as $k => $v) {
                $item = $name . '_' . strtoupper($k);
                putenv("$item=$v");
            }
        } else {
            putenv("$name=$val");
        }
    }
}

// 註冊自動加載
\think\Loader::register();

// 註冊錯誤和異常處理機制
\think\Error::register();

// 加載慣例配置文件
\think\Config::set(include THINK_PATH . 'convention' . EXT);

 

1. 定義了許多常量

2. 載入Loader類

3. 查看有無“環境變量配置文件”,有的話就加載

4. 運行Loader類的register()方法

5. 運行Error類的register()方法

 

從上下文以及註釋可以知道,應該是Loader類的靜態方法register()方法,實現了類的自動加載。重點就是它了。

 

Loader類的靜態方法register()方法

// 註冊自動加載機制
public static function register($autoload = '')
{
    // 註冊系統自動加載
    spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
    // 註冊命名空間定義
    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));
    }

    // Composer自動加載支持
    if (is_dir(VENDOR_PATH . 'composer')) {
        self::registerComposerLoader();
    }

    // 自動加載extend目錄
    self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
}

 

1. 默認$autoload爲空,使用PHP內置函數spl_autoload_register註冊Loader類的靜態方法autoload()方法爲自動加載機制。

其意思即是,當使用類的時候,找不到類或者還沒有包含,會將類名傳給autoload()方法,讓它處理

2. 註冊命名空間定義,是將命名空間與真實的文件路徑對應一一對應,並且存儲在一個類靜態變量數組裏面。

其作用是,在autoload()方法接收到類的名字的時候,通過類所屬的命名空間,找到類文件的真實路徑,進而將類文件包含include

3. 以下的基本是一樣的加載,只不過不是加載框架自身的類文件,而是加載composer庫或者自己寫的類

 

autoload()方法

// 自動加載
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)) {
            return false;
        }

        __include_file($file);
        return true;
    }
}

 

如你所見,autoload方法就是接收類名,搜索類文件,包含類文件。

 

恩,至此Thinkphp5運行時加載類的流程已經明瞭。

總結一下,index.php加載start.php,在start.php裏面加載base.php(類自動加載機制就在這裏出現了),然後下面調用App的靜態方法run方法執行“模塊/控制器/操作”,返回Respose類的實例執行send方法,將響應數據發送給客戶端,這樣,一個完整的請求就完成了。 


附錄

各個文件的路徑:

index.php

\think5\public

 

start.php

\think5\thinkphp

 

base.php

\think5\thinkphp

 

Loader.php

\think5\thinkphp\library\think

 

對於spl_autoload_register與自動加載有疑惑或不懂的可以看看我的另外一篇博客

__autoload,spl_autoload_register與自動加載

 

 

 

 

 




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章