iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 12
2
自我挑戰組

花式PHP系列 第 13

Laravel:深入config:cache

這篇文章的性質偏向個人在研究 config:cache 指令的實作,
其中仍有部份自己不能理解、理解錯誤的部份,如果有前輩發現,還請不吝指教!

PHP ARTISAN CONFIG:CACHE

與這指令搭配的還有 php artisan config:clear
兩者搭配用來對 Laravel 專案的設定檔產生快取,增進效能。

下指令時發生了什麼魔法?

下指令時會執行到 \Illuminate\Foundation\Console\ConfigCacheCommand 這個檔案。
那這隻檔案的執行點是 fire() 這個函式,裡面依序做了幾個動作:

  1. 清除舊的快取
  2. 讀取設定檔、並產生新的快取
  3. 寫入新產生的快取檔案
    1. 把讀取出的所有設定透過 var_export() 匯出成一段 PHP 語法
    2. 透過 $this->files->put 寫入到指定路徑
  4. 回傳「Configuration cached successfully!」訊息

快取檔案產生好之後,具體要研究它如何被 Laravel 載入的話,
需要找到 \Illuminate\Foundation\Bootstrap\LoadConfiguration 這個檔案

載入設定檔快取

\Illuminate\Foundation\Bootstrap\LoadConfiguration 檔案中,
定義了一個叫做 bootstrappublic 函式,其中便包含我想知道的東西。

底下是該函式的程式碼:

<?php

class LoadConfiguration
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        // 擺放設定的變數
        $items = [];

        // 首先檢查有沒有快取檔案的存在,有則載入;
        // 沒有的話我們就只能一個一個檔案挨著載入了。
        if (file_exists($cached = $app->getCachedConfigPath())) {
            $items = require $cached;

            $loadedFromCache = true;
        }

        // 在 Laravel 核心容器建立一個 "config" 的實例,
        // 它的 class 是一個 \Illuminate\Config\Repository;
        $app->instance('config', $config = new Repository($items));

        // 接著我們需要把所有設定檔案(/config/ 資料夾下的所有檔案) 載入進 Repository,
        // 這樣開發者才能在 Laravel 應用程式中任意調用 config
        if (! isset($loadedFromCache)) {
            $this->loadConfigurationFiles($app, $config);
        }

        // 透過 \Illuminate\Foundation\Application::detectEnvironment
        // 調用 \Illuminate\Foundation\EnvironmentDetector::detect
        // 以決定目前的執行環境為何(預設是 production)
        $app->detectEnvironment(function () use ($config) {
            return $config->get('app.env', 'production');
        });

        // 根據設定檔設定 php 的時區
        date_default_timezone_set($config['app.timezone']);

        // 設定 php multi-byte string 函式的內部編碼
        mb_internal_encoding('UTF-8');
    }
}

設定檔快取的載入流程差不多就是這樣了,
更細部的步驟需要繼續深入 loadConfigurationFiles()

細部步驟

loadConfigurationFiles

<?php

// 透過 foreach 迭代每一筆從 getConfigurationFiles 得到的設定檔
foreach ($this->getConfigurationFiles($app) as $key => $path) {

    // 一個一個 require 出來並放進(set) Repository
    $repository->set($key, require $path);
}

getConfigurationFiles

<?php

$files = [];

// 透過 realpath 取得設定檔資料夾(/config)在檔案系統中的絕對路徑
$configPath = realpath($app->configPath());

// 透過 foreach 迭代每一筆(用 Finder)從設定檔資料夾底下找到的所有 .php 檔案
foreach (Finder::create()->files()->name('*.php')->in($configPath) as $file) {

    $nesting = $this->getConfigurationNesting($file, $configPath);

    $files[$nesting.basename($file->getRealPath(), '.php')] = $file->getRealPath();
}

return $files;

getConfigurationNesting

<?php

// 取得路徑(不含檔名副檔名)
$directory = dirname($file->getRealPath());

// 先用 str_replace 把路徑中,從根目錄直到 /config/ 的路徑全部拔掉
// 再用 trim($path, '/') 移除路徑頭尾的「/」並得到 $tree
if ($tree = trim(str_replace($configPath, '', $directory), DIRECTORY_SEPARATOR)) {

    // 再把檔案名稱中所有的「/」換成「.」
    $tree = str_replace(DIRECTORY_SEPARATOR, '.', $tree).'.';
}

return $tree;

嘗試解釋自己看不懂的部份

其實我還是不太懂 getConfigurationNesting() 的用意為何,
而且取出 $tree 之後回頭在 getConfigurationFiles() 之中,
把它與路徑的 basename 串在一起當成 key 的意義在哪?

不過我在猜,
tree 就是我們在使用 config('app.env') 時的「app」(指向 config/app.php)吧?

murmur

呀,居然寫到Laravel的章節了,
可是已經有點懶的寫了阿~

/images/emoticon/emoticon13.gif

參考


上一篇
Object:Pass By Reference By Default
下一篇
Laravel:深入Query/Builder
系列文
花式PHP31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言