laravel configを読み込む仕組み

laravel 7.5.2でconfigが読み込まれる仕組みを追う。

index.phpでkernelのhendle関数が実行され、内部でbootstrap処理が実行されます。
bootstrap処理では、`Illuminate\Foundation\Http\Kernel.php`のbootstrappersプロパティで指定しているクラスを順次実行し、Applicationの起動処理を行います。
ここの `LoadConfiguration::class`でconfigに関連する処理を行ってそうです。

```lang:Illuminate\Foundation\Http\Kernel.php
    protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];
```

# LoadConfiguration.php
bootstrap処理では、bootstrapとして指定されたクラスのbootstrap関数が実行されます。
LoadConfiguration.phpのbootstrap関数はこちらです。

```lang:LoadConfiguration.php
    public function bootstrap(Application $app)
    {
        $items = [];

        // First we will see if we have a cache configuration file. If we do, we'll load
        // the configuration items from that file so that it is very quick. Otherwise
        // we will need to spin through every configuration file and load them all.
        if (file_exists($cached = $app->getCachedConfigPath())) {
            $items = require $cached;

            $loadedFromCache = true;
        }

        // Next we will spin through all of the configuration files in the configuration
        // directory and load each one into the repository. This will make all of the
        // options available to the developer for use in various parts of this app.
        $app->instance('config', $config = new Repository($items));

        if (! isset($loadedFromCache)) {
            $this->loadConfigurationFiles($app, $config);
        }

        // Finally, we will set the application's environment based on the configuration
        // values that were loaded. We will pass a callback which will be used to get
        // the environment in a web context where an "--env" switch is not present.
        $app->detectEnvironment(function () use ($config) {
            return $config->get('app.env', 'production');
        });

        date_default_timezone_set($config->get('app.timezone', 'UTC'));

        mb_internal_encoding('UTF-8');
    }
```

まず、configのキャッシュファイルパスがあるかを調べます。ある場合はそれを使います。

```lang:LoadConfiguration.php
        if (file_exists($cached = $app->getCachedConfigPath())) {
            $items = require $cached;

            $loadedFromCache = true;
        }
```


続いて、サービスコンテナにconfigを登録します。instanceはRepository.phpになります。

```lang:LoadConfiguration.php
$app->instance('config', $config = new Repository($items));
```

もしキャッシュからの読み込みでなかったらconfig情報を読み込みに行きます。

```
        if (! isset($loadedFromCache)) {
            $this->loadConfigurationFiles($app, $config);
        }
```


loadConfigurationFiles関数を見てみましょう。
内部ではgetConfigurationFiles関数でfile連想配列を取得し、$repositoryにsetしています。
getConfigurationFiles関数では、configPathに存在する全phpファイルパスを加工して取得し、
key = `ディレクトリを.で繋いだファイルパス(config/hoge/fuga.phpならhoge.fuga.piyo)`
value = `configファイルの絶対パス`
にしたfiles連想配列を返しています。
そして`$repository->set($key, require $path);`で
key = `ディレクトリを.で繋いだファイルパス`
value = `config読み込んだもの`
で値をセットしています。
これでbootstrap処理は終わりです。

```
    protected function loadConfigurationFiles(Application $app, RepositoryContract $repository)
    {
        $files = $this->getConfigurationFiles($app);

        if (! isset($files['app'])) {
            throw new Exception('Unable to load the "app" configuration file.');
        }

        foreach ($files as $key => $path) {
            $repository->set($key, require $path);
        }
    }
```

```
    protected function getConfigurationFiles(Application $app)
    {
        $files = [];

        $configPath = realpath($app->configPath());

        foreach (Finder::create()->files()->name('*.php')->in($configPath) as $file) {
            $directory = $this->getNestedDirectory($file, $configPath);

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

        ksort($files, SORT_NATURAL);

        return $files;
    }
```

```
    protected function getNestedDirectory(SplFileInfo $file, $configPath)
    {
        $directory = $file->getPath();

        if ($nested = trim(str_replace($configPath, '', $directory), DIRECTORY_SEPARATOR)) {
            $nested = str_replace(DIRECTORY_SEPARATOR, '.', $nested).'.';
        }

        return $nested;
    }
```


# configヘルパー関数
次はconfigヘルパー関数の動きを見ていきましょう。
configヘルパー関数は `Illuminate/Foundation/helpers.php` で定義されています。
helpersはautoloadによって読み込まれます(たぶん？)

configヘルパー関数では単純にサービスコンテナを使ってconfigインスタンス = Repositoryインスタンスを取得し、get関数を実行しています。
(keyがarrayだとsetになるの怖すぎ...)

```lang:helpers.php
if (! function_exists('config')) {
    /**
     * Get / set the specified configuration value.
     *
     * If an array is passed as the key, we will assume you want to set an array of values.
     *
     * @param  array|string|null  $key
     * @param  mixed  $default
     * @return mixed|\Illuminate\Config\Repository
     */
    function config($key = null, $default = null)
    {
        if (is_null($key)) {
            return app('config');
        }

        if (is_array($key)) {
            return app('config')->set($key);
        }

        return app('config')->get($key, $default);
    }
}
```

ではRepositoryのget関数を見てみましょう。先ほどsetしたitemsに指定したkeyにマッチするにvalueを返しています。
なるほど。

```Repository.php
    public function get($key, $default = null)
    {
        if (is_array($key)) {
            return $this->getMany($key);
        }

        return Arr::get($this->items, $key, $default);
    }

```