それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

laravelの設定(config) のキャッシュと実行環境(テスト環境testing/local ) の関係

laravel の env / config の関係について。

env と configキャッシュで testing のデータベースが使われずにパニクった。

TL;DR

.env.testing を使ってる場合、テスト実行する前には、cache:clearする。

laravel の実行環境と設定について

実行環境と設定は、.env ファイルと、config によって変わる。

実行環境

実行環境は、 envで決まる。 env は .env ファイルで決める。

.env ファイル

環境変数(OS/シェル)の実行環境の変数 Environment (通称ENV)の値を上書きする。自分で作る。

.env.testing ファイル

テスト実行時に使うファイル。自分で作る。

設定ファイル。

設定が書き込まれたファイル。

config/xxxx.php ファイル。

laravel 内で使うのは主にこれ。xxxx の部分が名前空間になっている。

config/ と ファイル名(ex:config/database.php)の関係

ファイル名が名前空間プレフィックス・カテゴリ)になるように調整される。

たとえば、database.php の場合は、次のようになる

ファイル名 名前空間
config/database.php database
config/queue.php queue

設定は名前空間を使ってアクセスする

config/queue.php を取得

config('queue.myname');

config と env の関係

殆どの場合、config はロード時に .env を参照するように書かれている。

config/session.php

<?php 
return [
 'lifetime' => env('SESSION_LIFETIME', 120),
];

config() を呼び出し、env() は ENV(+.env)を呼び出すのがわかっっている。

config 設定ファイルの中で env() が多用される。

config と env の呼び出し順(キャッシュなし)

config() が実行されるときに、env (名前, デフォルト値)への呼び出しのショートカットになっている。

呼び出しの順序が次のようになっている。

config() 
    -> env() 
        -> ENV(OS/シェル) 
        ->  デフォルト値

config は初期ロード前に変数にまとめられる。

configは一旦変数にキャッシュされる。というか config/ に散らばるArrayをまとめて一つにしている。var_exportしてますね。

config キャッシュ

config は、毎回ロードを避けるためにキャッシュすることが可能。

config のキャッシュ保存。

キャッシュ保存先は次のとおりになる。

bootstrap/cache/config.php

bootstrapを見ればわかるが、env() の結果で置き換わっているのがわかるでしょう。var_exportしてますね。

.env と .env.testing とキャッシュの関係

config が env() から取ってきて、env() が .env から取ってくるのはわかった。 だったら、.env.testing はいつ使うんだよ。.env.testingはどうなるの。

今使ってるのはどっちなんだよ問題。

キャッシュがあればキャッシュを使います。

env()の結果をconfigとしてキャッシュしてるんですから。testing/dev環境ではキャッシュを使ってはいけません。その理由をコードを動かして見ていきます。

キャッシュがあるときの優先度

先程の優先度の関係は、キャッシュが優先です。

config
   ->キャッシュある
   -> キャッシュ無い
    -> config() 
            -> env() 
                -> ENV(OS/シェル) 
                    ->  デフォルト値

であったとわかりますよね。

実際に試してみるとよくわかりますよね

testing 環境とキャッシュを試してみる、

config をダンプするコマンドを作って試してみます。

ファイルを作ります。

php artisan make:command MySample

コードを書きます。

namespace App\Console\Commands;
use Illuminate\Console\Command;

class MySample extends Command {
  
  protected $signature = 'my:sample';
  
  public function handle () {
    dd(config('my.name'));
  }
}

設定を作成します。

config/my.php

<?php

return ['name' => env("MY_NAME"),];

.envを2つ書きます。

開発用と、テスト用の2つのenv ファイルを用意します。

.env

MY_NAME=takuya-prod

.env.testing ファイルに書きます。

.env.testing

MY_NAME=takuya-testing

キャッシュをクリアして動作を確認します。

確認のために、まずキャッシュをクリアします。

$ php8.0 artisan config:clear
Configuration cache cleared!

通常のconfig 確認

通常環境でコマンドを実行してconfigを見ます。

$ php8.0 artisan my:sample
"takuya-prod"

テスト環境でconfig 確認

testing 環境でコマンドを実行してconfigを見ます。

$ APP_ENV=testing php8.0 artisan my:sample
"takuya-testing"

キャッシュ有りで テスト環境のconfig

通常環境でキャッシュを作成してconfigを見ます。

んーキャッシュ優先ですね。

config を確認してみる。

$ php8.0 artisan my:sample
"takuya-prod"
$ APP_ENV=testing php8.0 artisan my:sample
"takuya-prod"
$ APP_ENV=testing php8.0 artisan my:sample
"takuya-prod"

テスティング環境でキャッシュを作ってconfigを見ます。

testingでもキャッシュ優先ですね。

$ APP_ENV=testing php8.0 artisan config:cache
Configuration cache cleared!
Configuration cached successfully!
$ php8.0 artisan my:sample
"takuya-testing"
$ APP_ENV=testing php8.0 artisan my:sample
"takuya-testing"

ということで、artisan config:cacheは .env.testingと併せて使うと、落とし穴にハマりますよ。ってことで。

まとめ

.env.testing を使ってる場合、テスト実行する前には、cache:clear しましょう。

今回実験に使用したバージョン

$ cat composer.json | grep laravel/framework
    "laravel/framework": "^8.54",
$ php8.0  -v
PHP 8.0.9 (cli) (built: Jul 30 2021 13:09:07) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.9, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.9, Copyright (c), by Zend Technologies

phpでは、config に ini/xml/jsonファイルを使わないの?

設定といえば、json や ini ファイルを想像するかもしれませんが。

parse_ini_file という便利な関数があるのに config を php の配列ので書くのは、config 内で、ソースコードを実行したいからですね。設定生成用にコンソールを作るのがめんどくさいからですね。ini ファイルで設定書くとかしません。ましてやJavaのアプリ見たく、XMLyaml を使った静的設定ファイルをパースするとか、利用者もフレームワーク製作者もパーサー製作者も、全員への時間の無駄でしか無いと思います。