解讀 thinkphp5 源碼(一):自動加載

據說 TP5 已經 RC4 了,曾經在 RC3 的時候用它寫過一個小東西。官方說從 RC4 之後改動不是太大。索性讀一下它的源碼。而後順便記錄一下,若有錯漏,請路過大神多多指正!php

入口

做爲單入口框架,就從入口文件看起,按照tp5文檔所示的規範,入口文件應該是放在public/ 下。html

那麼爲何大多數要把入口放到子文件夾下呢?這是一個小技巧。java

第一爲了動靜分離,由於如今的php框架通常都是單入口,既然是單入口,那麼必然要作rewrite,若是把靜態文件和程序文件放到一塊兒。框架路由勢必要對每個請求進行篩選。因此這些框架不約而同的把資源文件和程序文件區分開來,放在了不一樣的文件夾下。從總體來看,也就是爲何入口會在子目錄下了。node

第二是爲了安全,linux下的權限劃分很是嚴格,分別分爲讀、寫、執行,在這個基礎上又分爲文件全部組、所在組、其餘組。這樣劃分能夠更好的對文件權限進行梳理,避免上傳漏洞(用戶上傳php文件被執行)等等。linux

打開public/index.phpgit

define('APP_PATH', __DIR__ . '/../application/');
// 加載框架引導文件
require __DIR__ . '/../thinkphp/start.php';

只有兩行代碼,定義 APP_PATH,加載 '/../thinkphp/start.php'。APP_PATH 能夠本身修改。thinkphp

而後打開 /../thinkphp/start.php數據庫

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

也只有三行代碼,定義命名空間,加載基礎文件,啓動應用。這裏注意一下命名空間,全部thinkphp類都在think及其子命名空間下。程序中用到框架類的時候要先use 該類的命名空間;npm

環境配置

而後咱們打開base.php
12-31行定義了一坨常量。注意裏面 defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS); 這種定義方式,先判斷時候存在,若是不存在則定義。也就是說咱們能夠在這行代碼以前(通常在index.php中)執行定義這個常量,而不會被覆蓋。php框架

36-37行

// 載入Loader類
require CORE_PATH . 'Loader.php';

載入Loader類,這個類比較重要,實現了自動加載。

39-51行

// 加載環境變量配置文件
if (is_file(ROOT_PATH . 'env' . EXT)) {
    $env = include ROOT_PATH . 'env' . EXT;
    foreach ($env as $key => $val) {
        $name = ENV_PREFIX . strtoupper($key);
        if (is_bool($val)) {
            $val = $val ? 1 : 0;
        } elseif (!is_scalar($val)) {
            continue;
        }
        putenv("$name=$val");
    }
}

加載環境變量配置文件,可能不少同窗不理解是幹什麼用的。

咱們假設一個場景,你在公司和家裏開發程序,在內網服務器上進行測試,在外網服務器上部署,全部的配置不能可能所有相同(好比數據庫賬號密碼、文件路徑等等)。總不能每次都改配置吧?若是作負載、有幾十個服務器怎麼部署?總不能都用ftp上傳,而後改配置吧?

因此如今主流的作法就是區分環境(開發環境、測試環境、生產環境),而後程序自動加載不一樣的配置。可是經過什麼區分呢?方法有不少,可是大多數都是選擇經過環境變量來區分,而後加載對應的配置文件。而後使用 git 作版本控制,而後在服務器部署同步腳本,經過 git push鉤子進行代碼同步,以達到自動化部署的模式。固然也還有其餘方式,可是大多都相似。

自動加載

爲何要使用自動加載呢?由於像java、C等編譯型語言在編譯過程當中會把程序中引用的庫、包等等自動引入進來。可是php是腳本行語言啊,沒有編譯過程,怎麼辦呢?最先期的程序都是手動引入,好比早期的xxshop、xxcms,都是寫一坨require、include。又搓又不方便,對於世界上最好的語言來講這樣多丟面啊,因此咱們須要用自動加載讓咱們最好的語言看起來更有B格(至於某些性能論的同窗會說自動加載影響性能啊之類的,請用匯編!)。

咱們繼續看base.php 的 54行\think\Loader::register();
註冊自動加載,從這一行以後就可使用符合自動加載規範的任何類了。

好比56-60行,雖然沒有加載對應的文件,可是經過自動加載就能夠直接使用。

// 註冊錯誤和異常處理機制
\think\Error::register();
// 加載慣例配置文件
\think\Config::set(include THINK_PATH . 'convention' . EXT);

接下來咱們看一下自動加載的實現方法。打開Loader.php,按照上面的執行順序,先看Loader類的register方法

核心是

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));
}

spl_autoload_register方法可能不少同窗都有了解,在咱們實例化一個當前已加載文件中不存在的類後(好比在a.php中new一個類,會先在a.php和已加載的文件中找),會執行此方法指定的函數,並把類名傳遞進去。在這個函數中若是能正確加載到該文件,那麼也能夠實例化成功,並不會報錯。因此藉助此函數能夠達到自動加載。

首先咱們知道當 new 一個不存在的類時,若是使用spl_autoload_register定義了一個處理函數,那麼這個函數能夠得到一個參數,參數名是new 的類名。好比從前面base.php中咱們看到 \think\Error::register();使用think命名空間下的Error類的register靜態方法,可是咱們並無引入這個文件。因而咱們能夠在spl_autoload_register註冊的函數中獲得一個參數thinkError,若是咱們的命名空間按照文件夾格式的方法命名(這也是推薦的、經常使用的命名方式),那麼就能夠經過該參數來加載對應的文件,可是若是特殊狀況下沒有按照文件夾的格式來進行命名空間的命名,那麼就須要手動指定映射關係。self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT)); 加載了映射文件。而後咱們看spl_autoload_register中指定的函數:autoload。
這個不用詳細解釋了,先處理由 addNamespace 設定的命名空間別名,而後經過 findFile 來處理映射關係,獲得真實的路徑,並加載文件。

__autoload()函數具備相似的功能。可是爲何用的不多呢?由於 __autoload()只能指定一個函數,而spl_autoload_register能夠註冊多個函數來處理這個邏輯。一旦業務複雜 __autoload()就徹底不能勝任。好比咱們繼續看Loader類的register方法下面的內容

// Composer自動加載支持
        if (is_dir(VENDOR_PATH . 'composer')) {
            self::registerComposerLoader();
        }
        // 自動加載extend目錄
        self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);

一個框架是否好用,很大程度取決於它的擴展能力。因此自動加載除了要處理自身類庫的加載、還要處理擴展類庫的自動加載。tp5支持使用兩種方式來擴展類庫一種是Composer,一種是手動放入 extend 目錄。
Composer不用多說,就像npm之於nodejs、yum之於Centos、apt-get之於、Ubuntu。一個php的包管理工具。Composer有一套本身的自動加載機制,tp5這裏只不過是調用了Composer本身的註冊自動加載函數的方法。有興趣的同窗能夠看一下registerComposerLoader方法,以及vendor/composer下幾個autoload開頭的文件。原理基本上和上面的一致。

Loader.php中核心方法已經介紹完畢,其餘的幾個方法有處理規範的方法(PSR-0,PSR-2,PSR-4,有興趣的同窗能夠谷歌、百度瞭解一下)、和根據類名處理模型和控制器的方法,這點須要看完路由才能詳解。

博客連接:解讀 thinkphp5 源碼(一):自動加載

相關文章
相關標籤/搜索