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のアプリ見たく、XMLやyaml を使った静的設定ファイルをパースするとか、利用者もフレームワーク製作者もパーサー製作者も、全員への時間の無駄でしか無いと思います。