1. 程式人生 > >TP5 自動載入機制詳解

TP5 自動載入機制詳解

http://www.php.cn/php-weizijiaocheng-383032.html

TP作為國內主流框架,目前已經發布了5.*版本,相對於3.*版本是進行了重構。今天我們就從原始碼來研究下TP5自動載入的實現。

 

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

那麼為什麼大多數要把入口放到子資料夾下呢?這是一個小技巧。

第一為了動靜分離,因為現在的php框架一般都是單入口,既然是單入口,那麼必然要做rewrite,如果把靜態檔案和程式檔案放到一起。框架路由勢必要對每一個請求進行篩選。所以這些框架不約而同的把資原始檔和程式檔案區分開來,放在了不同的資料夾下。從整體來看,也就是為什麼入口會在子目錄下了。

第二是為了安全,linux下的許可權劃分非常嚴格,分別分為讀、寫、執行,在這個基礎上又分為檔案所有組、所在組、其他組。這樣劃分可以更好的對檔案許可權進行梳理,避免上傳漏洞(使用者上傳php檔案被執行)等等。

開啟public/index.php

1

2

3

define('APP_PATH', __DIR__ . '/../application/');

// 載入框架引導檔案

require __DIR__ . '/../thinkphp/start.php';

只有兩行程式碼,定義 APP_PATH,載入 '/../thinkphp/start.php'。APP_PATH 可以自己修改。

然後開啟 /../thinkphp/start.php

1

2

3

4

5

6

namespace think;

// ThinkPHP 引導檔案

// 載入基礎檔案

require __DIR__ . '/base.php';

// 執行應用

App::run()->send();

也只有三行程式碼,定義名稱空間,載入基礎檔案,啟動應用。這裡注意一下名稱空間,所有thinkphp類都在think及其子名稱空間下。程式中用到框架類的時候要先use 該類的名稱空間;

環境配置

然後我們開啟base.php

12-31行定義了一坨常量。注意裡面 defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS); 這種定義方式,先判斷是否存在,如果不存在則定義。也就是說我們可以在這行程式碼之前(一般在index.php中)執行定義這個常量,而不會被覆蓋。

36-37行

1

2

// 載入Loader類

require CORE_PATH . 'Loader.php';

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

39-51行

1

2

3

4

5

6

7

8

9

10

11

12

13

// 載入環境變數配置檔案

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行,雖然沒有載入對應的檔案,但是通過自動載入就可以直接使用。

1

2

3

4

// 註冊錯誤和異常處理機制

\think\Error::register();

// 載入慣例配置檔案

\think\Config::set(include THINK_PATH . 'convention' . EXT);

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

核心是

1

2

3

4

5

6

7

8

9

10

11

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方法下面的內容

1

2

3

4

5

6

// 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,有興趣的同學可以谷歌、百度瞭解一下)、和根據類名處理模型和控制器的方法,這點需要看完路由才能詳解。

以上就是TP5 自動載入機制詳解的詳細內容,更多請關注php中文網其它相關文章!