ブレードで変数の値を表示するのによく使う括弧。{{ }} と {!! !!}2種類あります。これらに関しての話です。

{{ }} と {!! !!}の違い

{{ }} と {!! !!}の違いは、いたって簡単です。前者はhtmlをエスケープして、後者はエスケープしません。と言っても??なので、具体的に以下のようなブレードを作成して、違いを見てみましょう。

{{ $x }}
{!! $x !!}

tinkerでこの$xに値を入れてブレードをビューすると、以下のようなHTML文が出力されます。

>>> view('test')->with(['x' => '<h1>hello</h1>'])->render();
=> """
   &lt;h1&gt;hello&lt;/h1&gt;\n
   <h1>hello</h1>\n
   """

前者の表示は、HTMLのタグが見事にエスケープされていますが、後者はHTMLのタグがそのまま残っています。ブラウザでこれらを閲覧すると、

と表示の違いがはっきりします。

PHPにコンパイルされたブレード

先の例で使用したtest.blade.phpは、サーバーから表示される前にphpにコンパイルされてstorage/framework/viewsにキャッシュされます。コンパイルされたブレードの中身を見ると、

<?php echo e($x); ?>

<?php echo $x; ?>

<?php /**PATH /var/www/repos/l8x/resources/views/test.blade.php ENDPATH**/ ?>

お馴染みのphpファイルです(ブレードはphpファイル拡張子ですがphpファイルではありません)。なるほど単に標準のPHP関数のecho()を使用しています。しかし、前者において、e()という関数は標準のPHP関数ではありません。調べてみると、これはLaravelのヘルパーです。以下にその定義を掲載します。

...
   /**
     * Encode HTML special characters in a string.
     *
     * @param  \Illuminate\Contracts\Support\DeferringDisplayableValue|\Illuminate\Contracts\Support\Htmlable|string|null  $value
     * @param  bool  $doubleEncode
     * @return string
     */
    function e($value, $doubleEncode = true)
    {
        if ($value instanceof DeferringDisplayableValue) {
            $value = $value->resolveDisplayableValue();
        }

        if ($value instanceof Htmlable) {
            return $value->toHtml();
        }

        return htmlspecialchars($value ?? '', ENT_QUOTES, 'UTF-8', $doubleEncode);
    }
...

標準関数のhtmlspecialchars()が最終的にはHTMLタグをエスケープしていることわかります。

HtmlString

さて、ブレードで使用されている括弧の意味が解ったところで、先のtest.blade.phpを以下のように編集します。csrf_field()は、LaravelのCSRFトークンのためのヘルパー関数です。

{{ csrf_field() }}

これをtinkerで先と同様にレンダリングします。今回は変数の渡しなしです。

>>> view('test')->render()
=> "<input type="hidden" name="_token" value="">\n"

おかしいですね。二重波括弧なのに、HTMLタグがエスケープされていません。どうしてでしょう?

csrf_field()の定義を見てみましょう。

...
    /**
     * Generate a CSRF token form field.
     *
     * @return \Illuminate\Support\HtmlString
     */
    function csrf_field()
    {
        return new HtmlString('<input type="hidden" name="_token" value="'.csrf_token().'">');
    }
...

この関数が返しているのは、文字列ではなくHtmlStringのオブジェクトです。このオブジェクトがブレードでの括弧の変数の値とすると、エスケープ処理がされないのです。tinkerで試してみましょう。

>>> use Illuminate\Support\HtmlString;
>>> $x = new HtmlString('<h1>hello</h1>');
=> Illuminate\Support\HtmlString {#3517
     html: "<h1>hello</h1>",
   }
>>> e($x)
=> "<h1>hello</h1>"

HtmlStringを使う意味

表示される値にHTMLタグが含まれるかどうか前もってわかっていれば、{{ }} と {!! !!}の使いわけできますが、たいていはブレードを作成するのは開発者ではなくデザイナーさんです。HTML文を含むときに文字列ではなく、HtmlStringのオブジェクトとすれば、迷わずにいつも{{ }}の使用となります。

By khino