Laravel Blade の {{ }} 記法について

# Laravel Blade の {{ }} 記法について

Laravel Blade テンプレートをお使いのみなさん、いかがお過ごしでしょうか。

blade テンプレートを用いて文字列を展開するときに {{ }} を用いていると思います。

例えばコントローラーの中で下記のように書くと

```php:app/Http/Controllers/ProductController.php
    public function index(Request $request){
        $products = Product::all();
        return view('product.index',[
            'products' => $products,
        ]);
    }
```

Bladeテンプレートの中では、下記のような表記方法でテンプレートを記述すると思います、
```html:resources/views/product/index.blade.php
@foreach( $products as $product )
   <div>{{$product->id}}</div>
   <div>{{$product->name}}</div>
@endforeach
```

{{ }} で囲んだ値が html の中に展開されます。

この時、経験の差でこのような表記になるんだ。と考える方もいると思いますし、
```php
<?php echo $product->id ?>
```
下記のようになるのだと考える方もいると思います。
```php
<?php echo htmlspecialchars($product->id, ENT_QUOTES) ?>
```

もちろん {{ }} で囲まれた場合は、下記の htmlspecialchars で処理されます。
この htmlspecialchars は XSS 対策でも重要です。 展開文字列に & < > 
等の文字が含まれた場合 &amp; などに変換されます。

つまり、下記のような表記は htmlspecialchars を直接記載必要なく適切だと安心して使用できます。
```html:resources/views/product/index.blade.php
@foreach( $products as $product )
   <div>{{$product->id}}</div>
   <div>{{$product->name}}</div>
@endforeach
```

ここまでくると気になるのが 変数に html タグを入れて そのまま表示したくないときです。
滅多にありませんが、たまにあります。たぶん。
```php
    <div>{!! $name !!}</div
```
このような表記方法をすればよいと laravel の blade のドキュメントにもあります。
そう、この表記をすれば エスケープ処理されず正しくでるのです。


どんな使い道があるか？ぱっと思いつくのは、decorationなどのヘルパ関数があり、
その中で適当に html 修飾されて出てくる等です。
```php
    <div>{!! decoration($name) !!}</div
```

そう、私もそう思っていました。
しかし、とあるときに不思議な気持ちにおいやられました。

ページネーションするときです。

```php:app/Http/Controllers/ProductController.php
    public function index(Request $request){
        $products = Product::paginate(15);//※1
        return view('product.index',[
            'products' => $products,
        ]);
    }
```

```html:resources/views/product/index.blade.php
@foreach( $products as $product )
   <div>{{$product->id}}</div>
   <div>{{$product->name}}</div>
@endforeach
{{ $products->links() }}   {{-- //※2 --}}
```

はい ※1 のようにページネーションして結果を収集し、※2 のようにすると、なんとそこに
```html
<a href="">prev</a>
<a href="">[1]</a>
<a href="">next</a>
```
のような表記が展開されるのです。


勘の言い方！「ちょったまったー」といいたくなると思います。
{{ }} で囲まれているのだから htmlspecialchars でエスケープ処理されて htmlタグは表示されないだろ！と


## {{ }} の正体

じつは {{ }} は htmlspecialchars で囲んで表示するではなかったのです。

実装をみてみたいとおもいます。
{{ }} は 下記のメソッドの部分で展開の処理をしています。 
※3 の部分は、echoFormat は e() で囲んで表示するのです。

つまり、 `{{ $name }}` は e($name)で囲まれて出力されるのです。
```php:vendor/laravel/framework/src/Illuminate/View/Compilers/Concerns/CompilesEchos.php
   protected function compileRegularEchos($value)
    {
        $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]);

        $callback = function ($matches) {
            $whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3];

            $wrapped = sprintf($this->echoFormat, $this->wrapInEchoHandler($matches[2]));//※3

            return $matches[1] ? substr($matches[0], 1) : "<?php echo {$wrapped}; ?>{$whitespace}";
        };

        return preg_replace_callback($pattern, $callback, $value);
    }
```

はい、やっぱり e() で htmlspecialchars でしょ！？
とおもったら、さぁ…

```php:vendor/laravel/framework/src/Illuminate/Support/helpers.php
    function e($value, $doubleEncode = true)
    {
        if ($value instanceof DeferringDisplayableValue) {
            $value = $value->resolveDisplayableValue();
        }

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

        if ($value instanceof BackedEnum) {
            $value = $value->value;
        }

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

e() は、確かに htmlspecialcharsをしていましたが、実は違ったのです。
そう、Htmlableの場合、`return $value->toHtml();` つまり `htmlspecialchars` せずに返していたのです。
{{ $products->links() }}が正しくhtmlを表示していたのはここだったのです。


## htmlを出力したい場合 Htamlable なオブジェクトを {{}} に渡そう

もし、プロジェクトで {!! !!} を使わざるおえなく、困っていたらこの仕掛けを導入するのもありかもしれません。

いや、昔、 html をコントローラーから渡すことがあり、それを表示するために {!! !!} を使っていました。
XSS脆弱性判断のために {!! を全体的に走査していたので、 {!! で囲むものには特殊な命名規則でつけたものを渡そう。
そういう独自ルールで対処した時期があったのですが、こちら使えばいいですね。

{!! !!} を世界から追い出しましょう←暴論です。

## 詳細のソースはこちら

https://github.com/laravel/framework/blob/b9203fca96960ef9cd8860cb4ec99d1279353a8d/src/Illuminate/View/Compilers/Concerns/CompilesEchos.php#L89

https://github.com/laravel/framework/blob/37bbb8c299b51195fc38f845dae847726d982522/src/Illuminate/Support/helpers.php#L109