🐘

Laravelを使ったことがある人はdddを試してみてほしい(小ネタ)

2023/06/12に公開
2

はじめに

ddd を"ドメイン駆動開発(Domain Driven Development)=DDD"のタイポだと信じ「実はちゃんとドメイン駆動開発のことを説明するんでしょ?」と期待してこの記事を開かれた方、誠に申し訳ありません。

ここでバックしていただいて結構です。これ以上読んでもご期待に添えることはできません。平に謝罪致します。

一方で ddd がタイポではなく、何のことか察しておられる方はこのまま答え合わせしていってください。どうやら幅広く Laravel の知識をお持ちとお見受けします。

また、何のことか察してはいないものの「じゃあなんなんだよ?」と気になる方もこのまま最後までお付き合いください。

ところで dd は知ってますよね?

Laravel でデバッグするときに使うヘルパー関数です。

ただ、この言い回しには若干の語弊を含む可能性があります。

よく dd を紹介する記事で Laravel のヘルパー関数という表現をしている記事がありますが、それは間違いで dd 自体は Laravel の土台になっている Symfony のヘルパー関数です。

なので語弊なく表現するのであればLaravel の機能として提供されている Symfony のヘルパー関数というところでしょうか。

vendor/symfony/var-dumper/Resources/functions/dump.php
if (!function_exists('dd')) {
    /**
     * @return never
     */
    function dd(...$vars): void
    {
        if (!in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !headers_sent()) {
            header('HTTP/1.1 500 Internal Server Error');
        }

        foreach ($vars as $v) {
            VarDumper::dump($v);
        }

        exit(1);
    }
}

今回は VarDumper::dump($v)が何してるかまでは解説しません。(めんどくさいので)

気になる人は vendor/symfony/var-dumper/VarDumper.php の中で dump を検索してみてください。

そこから処理を追っていくと$handler という変数に「どういう形式で出力するか?」という情報をセットして、あとはネストされた変数の値を頑張って出力しようとしてる感じなのが分かります。

ちなみに、dd を使うとこんな感じでデバッグ(インスペクション)ができます。

使い方は簡単で dd という関数の引数に中に何が入ってるのか確認したい変数を渡すだけです。可変長引数になっているのでこんな感じで複数渡しても良いです。

dd($request, $hoge);

なお、dd 関数本体を見ると分かるように exit(1);となっているのでこいつを呼んだ時点で処理が終了します。

では、ddd とは?

きっかけはタイポでした。

ddd($request);

このまま気づかず実行していたところ、なぜかいつもとは違う画面が表示されました。

めちゃくちゃリッチなエラーページが出ました。

どうもいろいろとできそうです。

エディターの設定

エディターを設定し、以下のリンクを押下するとエディターを開こうとしてくれます。

スタックトレースと停止箇所の表示

リクエストヘッダ、ボディの表示

今回は単純に GET のためボディはなし。

どのルートを通り、どのミドルウェアが適用されたか

ログインユーザーと実行環境

dd と同じく変数のインスペクション

内部で実行されたクエリ

ddd の実体

タイポには気付いたのですが、エラーになっていないということは ddd という関数自体は存在しているわけで…

とくれば定義元に飛んでいくと…ありました。

vendor/spatie/laravel-ignition/src/helpers.php
<?php

use Spatie\LaravelIgnition\Renderers\ErrorPageRenderer;

if (! function_exists('ddd')) {
    function ddd()
    {
        $args = func_get_args();

        if (count($args) === 0) {
            throw new Exception('You should pass at least 1 argument to `ddd`');
        }

        call_user_func_array('dump', $args);

        $renderer = app()->make(ErrorPageRenderer::class);

        $exception = new Exception('Dump, Die, Debug');

        $renderer->render($exception);

        die();
    }
}

どうやらこの ddd という関数は、spatie というライブラリの一部のようです。

調べてみると、主に Laravel 便利機能を提供してるライブラリみたいです。

https://github.com/spatie

で、さっき紹介した画面は vendor/spatie/laravel-ignition/src/Renderers/ErrorPageRenderer.php の render というメソッドで作ってるようです。

vendor/spatie/laravel-ignition/src/Renderers/ErrorPageRenderer.php
<?php

namespace Spatie\LaravelIgnition\Renderers;

use Spatie\FlareClient\Flare;
use Spatie\Ignition\Config\IgnitionConfig;
use Spatie\Ignition\Contracts\SolutionProviderRepository;
use Spatie\Ignition\Ignition;
use Spatie\LaravelIgnition\ContextProviders\LaravelContextProviderDetector;
use Spatie\LaravelIgnition\Solutions\SolutionTransformers\LaravelSolutionTransformer;
use Spatie\LaravelIgnition\Support\LaravelDocumentationLinkFinder;
use Throwable;

class ErrorPageRenderer
{
    public function render(Throwable $throwable): void
    {
        $viteJsAutoRefresh = '';

        if (class_exists('Illuminate\Foundation\Vite')) {
            $vite = app(\Illuminate\Foundation\Vite::class);

            if (is_file($vite->hotFile())) {
                $viteJsAutoRefresh = $vite->__invoke([]);
            }
        }

        app(Ignition::class)
            ->resolveDocumentationLink(
                fn (Throwable $throwable) => (new LaravelDocumentationLinkFinder())->findLinkForThrowable($throwable)
            )
            ->setFlare(app(Flare::class))
            ->setConfig(app(IgnitionConfig::class))
            ->setSolutionProviderRepository(app(SolutionProviderRepository::class))
            ->setContextProviderDetector(new LaravelContextProviderDetector())
            ->setSolutionTransformerClass(LaravelSolutionTransformer::class)
            ->applicationPath(base_path())
            ->addCustomHtmlToHead($viteJsAutoRefresh)
            ->renderException($throwable);
    }
}

具体的に何をやっているかは、メソッドチェーンの中をみていけば分かるんじゃないかと。
(まあさっき紹介した画面の情報を頑張って作ってるだけなんですが)

ちなみに ddd も最後には die()しているので、こいつを呼んだ時点で処理が終わります。

おわりに

というわけで小ネタ・ddd ヘルパー関数の紹介でした。

ちなみに私自身は dd も ddd もあまり関心がないです。

だって VSCode のデバッガの方がブレークポイント貼りながらインスペクションもできるので明らかに便利じゃん…

ただ、今回発見した ddd 関数はエラーの原因箇所が特定できているシチュエーションにおいては便利かなと思っています。

その時点でのプログラム内部のいろんな情報を総合的に出力してくれているので、分析に使えそうです。

メンバー募集中!

サーバーサイド Kotlin コミュニティを作りました!

Kotlin ユーザーはぜひご参加ください!!

https://serverside-kt.connpass.com/

また関西在住のソフトウェア開発者を中心に、関西エンジニアコミュニティを一緒に盛り上げてくださる方を募集しています。

よろしければ Conpass からメンバー登録よろしくお願いいたします。

https://blessingsoftware.connpass.com/

Discussion

PCS開発チームPCS開発チーム

元々はdump()はSymfonyのヘルパー、dd()はLaravelのヘルパーだった。
途中でSymfonyにdd()が追加されたのでLaravelからは削除された。
https://github.com/laravel/framework/pull/25087
ddd()はいつものエラー画面を強制的に呼び出してるだけ。
普通に開発していればこのエラー画面が必要なら勝手に出てくるので強制的に呼び出さなくていい。
dd()を使うのは変数の中身をさっと見たい時なので詳細なエラー画面は不要。
ddd()を使うことはない。

Laravelのドキュメントに書かれてないのでddd()はLaravelのヘルパー扱いしない。
あくまでもサードパーティパッケージであるspatie/laravel-ignition内のヘルパー。
エラー画面のパッケージは何度か変わってるのでignitionから変わったらddd()は使えなくなる。