Laravelのdumpは手軽に使える値の確認方法です。今何が起きているかを前準備なくちゃちゃっと確認できる点、読みやすい点が優れています。dumpを使うと次のように色付きで構造を読み取りやすい形式でdumpに渡した内容が表示されます。
dumpを使いたい時には様々な時があります。例えば問題が起こった時にそこに至るまでの値はどうなっていたかを後から知りたい時です。こういった時に入力や状態が再現できるならばデバッガーを使うのもいいですが、そうは行かない場合もあります。またログ的なdump結果を読むだけで済むならば、そちらの方が手早く済みます。この記事ではそういった時に使えるdumpの遅延表示方法を紹介します。
実際のコードと使用例が次です。このコードはLaravel10で試しました。
<?php
namespace App\Library\Test;
use Illuminate\Foundation\Console\CliDumper;
use Illuminate\Foundation\Providers\FoundationServiceProvider;
use Laravel\Prompts\Output\BufferedConsoleOutput;
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\VarDumper;
/**
* 後でまとめてdump内容をコンソールに表示するためのクラス。
* ファイルをまたいでいたるところで使う想定のため静的プロパティ、静的メソッドを使っている。
*/
class DeferredDumper
{
/** @var BufferedConsoleOutput 出力内容を表示せずメモリ上に保持するOutPutクラス */
private static BufferedConsoleOutput $output;
/** dumpの遅延機能をONにする */
public static function activate(): void
{
// outputの初期化。このインスタンスの中にdumpの内容がたまっていく
self::$output = new BufferedConsoleOutput();
// Laravelのdumpの挙動を上書き。self::$outputにdumpの内容が貯まるようにする
VarDumper::setHandler(function ($var) {
// self::$output以外はLaravelが行っている設定を踏襲していつものdumpと同じ内容が得られるようにする
$dumper = new CliDumper(self::$output, base_path(), storage_path('framework/views'));
$cloner = tap(new VarCloner())->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO);
$dumper->dumpWithSource($cloner->cloneVar($var));
});
}
/** 元のdumpの挙動に戻す */
public static function resetToDefaultDumper(): void
{
// Laravelのdumpの挙動を決定する処理を呼び出すことで元のdumpの挙動に戻す
(new FoundationServiceProvider(app()))->registerDumper();
}
/** まとめて出力 */
public static function flush(): void
{
if (!isset(self::$output)) {
return;
}
// self::$outputに貯まった内容を出力する
// fetchはそれまで貯めたdumpの内容を空にして返してくるメソッド
echo self::$output->fetch();
}
}
/*******************
* 使用例
*******************/
DeferredDumper::activate(); // dumpの挙動を変更
// いつも通り dumpする
dump('Hello World');
dump([
'name' => '浜松 太郎',
'detail' => (object)[
'age' => 20,
'address' => '静岡県浜松市',
],
]);
// この時点ではまだ画面に出力されていない
DeferredDumper::flush(); // flushすることでdumpの内容をまとめて出力
DeferredDumper::resetToDefaultDumper(); // dumpの挙動を元に戻す
dump機能はグローバルな領域で設定されており、そこを上書きすることでdumpの挙動を変えられます。上記のコードではdumpする際に使われているOutputクラスをBufferedConsoleOutputクラスに変えることによってdumpされた内容を貯め込み、後で出力できるようにしています。これを使うことで次のようなこともできます。
class BaseTestCase extends TestCase
{
// PHPUnitのテスト開始時に遅延dumpモードにする
protected function setUp(): void
{
parent::setUp();
DeferredDumper::activate();
}
protected function tearDown(): void
{
if ($this->status()->isFailure()) {
// テストが失敗した場合にのみdumpの内容を表示する
DeferredDumper::flush();
}
// 元のdumpの挙動に戻す
DeferredDumper::resetToDefaultDumper();
parent::tearDown();
}
}
class PiyoTest extends BaseTestCase
{
public function test使用例()
{
$input = [];
$result = func($input);
// テストに失敗した時だけ引数と返り値の内容が出力される
dump($input, $result);
$this->assertEquals('hoge', $result['fuga']);
}
}
こうするとテストを一斉に走らせた時、失敗したテストについてのみ詳細な情報が表示されます。
この記事ではdumpの内容を後から表示するようにコードを書きましたが、同様の方法でdumpの挙動を好きなように変えられます。ログに書き込んだり、外部に送ったりすることもできます。
2024-04-09追記:
Laravel9以前でこれを行う場合はLaravel\Prompts\Output\BufferedConsoleOutputの代わりに次のようなプロパティにdump内容を貯めるOutputクラスを用意して使う必要があります。
<?php
namespace App\Library\Dumper;
use Symfony\Component\Console\Output\ConsoleOutput as SymfonyConsoleOutput;
class DeferredOutput extends SymfonyConsoleOutput
{
/**
* The output buffer.
*/
protected string $buffer = '';
/**
* Empties the buffer and returns its content.
*/
public function fetch(): string
{
$content = $this->buffer;
$this->buffer = '';
return $content;
}
/**
* Return the content of the buffer.
*/
public function content(): string
{
return $this->buffer;
}
/**
* Write to the output buffer.
*/
protected function doWrite(string $message, bool $newline): void
{
$this->buffer .= $message;
if ($newline) {
$this->buffer .= \PHP_EOL;
}
}
}
