Laravel 源碼解析之 composer 自動(dòng)加載

在研究 Laravel 源碼的過(guò)程中,我遇到的第一個(gè)問(wèn)題就是:Laravel 的中使用到的那些類是如何被加載進(jìn)來(lái)的?

在 Laravel 的入口文件的第二行代碼中,引入了 bootstrap 文件夾下的 app.php 文件。而在這個(gè)文件的開(kāi)頭,一個(gè)名為 Application 的類就被初始化,這個(gè)文件在此之前并未引入,程序順利執(zhí)行。

很顯然,這個(gè)自動(dòng)加載的問(wèn)題是在入口文件的第一行被解決的。在第一行引入的文件中,引入了 vendor 文件夾的 autoload.php 文件。這個(gè)文件與 composer 有關(guān),Laravel 的自動(dòng)加載是借助了 composer。

composer 自動(dòng)加載

那么 composer 是如何做到自動(dòng)加載的呢?

打開(kāi) autoload.php 文件,這里面只有兩行代碼。第一行引入了一個(gè)文件,第二行調(diào)用了該文件中的類的 getLoader 方法。ComposerAutoloaderInit5ba1fc8bf6c79636a218962c1a6da048 這個(gè)類名之所以看起來(lái)奇怪,是因?yàn)橐乐诡惷麤_突而使用了 hash。

<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInit5ba1fc8bf6c79636a218962c1a6da048::getLoader();

在 getLoader 方法中,\Composer\Autoload\ClassLoader 被初始化為 $loader,然后 $loader 這個(gè)加載器的四個(gè)屬性 $prefixLengthsPsr4、$prefixDirsPsr4、$prefixesPsr0、$classMap 被依次填充,前兩個(gè)對(duì)應(yīng)著 psr-4 規(guī)范,后面兩個(gè)分別對(duì)應(yīng) psr-0 和 classMap。

    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        spl_autoload_register(array('ComposerAutoloaderInit5ba1fc8bf6c79636a218962c1a6da048', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInit5ba1fc8bf6c79636a218962c1a6da048', 'loadClassLoader'));

        $includePaths = require __DIR__ . '/include_paths.php';
        array_push($includePaths, get_include_path());
        set_include_path(join(PATH_SEPARATOR, $includePaths));

        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        if ($useStaticLoader) {
            require_once __DIR__ . '/autoload_static.php';

            call_user_func(\Composer\Autoload\ComposerStaticInit5ba1fc8bf6c79636a218962c1a6da048::getInitializer($loader));
        } else {
            $map = require __DIR__ . '/autoload_namespaces.php';
            foreach ($map as $namespace => $path) {
                $loader->set($namespace, $path);
            }

            $map = require __DIR__ . '/autoload_psr4.php';
            foreach ($map as $namespace => $path) {
                $loader->setPsr4($namespace, $path);
            }

            $classMap = require __DIR__ . '/autoload_classmap.php';
            if ($classMap) {
                $loader->addClassMap($classMap);
            }
        }

        $loader->register(true);

        if ($useStaticLoader) {
            $includeFiles = Composer\Autoload\ComposerStaticInit5ba1fc8bf6c79636a218962c1a6da048::$files;
        } else {
            $includeFiles = require __DIR__ . '/autoload_files.php';
        }
        foreach ($includeFiles as $fileIdentifier => $file) {
            composerRequire5ba1fc8bf6c79636a218962c1a6da048($fileIdentifier, $file);
        }

        return $loader;
    }

填充的內(nèi)容來(lái)自于 vendor/composer 文件夾下的這三個(gè)文件:autoload_classmap、autoload_namespaces、autoload_psr4。打開(kāi)這些文件,每一個(gè)文件都返回了一個(gè)包含了很多文件路徑的數(shù)組。這些則來(lái)自于 composer.json。

    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/TestCase.php"
        ]
    },

不只是 Laravel 的根目錄存在 composer.json,vendor 文件夾下每一個(gè)由 composer 安裝的依賴庫(kù),都存在著一個(gè)這樣的文件。

$loader 加載器被填充后,調(diào)用了它的 register 方法,在這個(gè)方法中,使用了 spl_autoload_register 注冊(cè)該類的 loadClass 方法作為 __autoload 的實(shí)現(xiàn)。需要用到一個(gè)類的時(shí)候,就會(huì)調(diào)用 loadClass 方法。

    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
    }

緊接著,引入了 autoload_files,與其他三個(gè)不同,它返回的數(shù)組,數(shù)組中定義的多個(gè)文件會(huì)被提前加載,而不是用到時(shí)才加載。如其中的 '/laravel/framework/src/Illuminate/Foundation/helpers.php'、'/laravel/framework/src/Illuminate/Support/helpers.php'。正是由于這兩個(gè)文件被提前加載,其中的定義的框架方法才可以被在項(xiàng)目中任意使用。

    // vendor/laravel/framework/composer.json
    "autoload": {
        "files": [
            "src/Illuminate/Foundation/helpers.php",
            "src/Illuminate/Support/helpers.php"
        ],
        "psr-4": {
            "Illuminate\\": "src/Illuminate/"
        }
    },

最后,$loader 被返回。接下來(lái)就可以在項(xiàng)目中沒(méi)有顧忌的使用各種類和方法了,而不需要把 include 寫滿每一個(gè)角落。當(dāng)然,前提是需要符合 psr-4 的規(guī)范。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,996評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,316評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,406評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,128評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,524評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,759評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,310評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,065評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,249評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,479評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,909評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,140評(píng)論 1 290
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,984評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,228評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容