7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

LaravelAdvent Calendar 2022

Day 7

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

Last updated at Posted at 2022-12-06

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

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

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

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

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

Bladeテンプレートの中では、下記のような表記方法でテンプレートを記述すると思います、

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

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

この時、経験の差でこのような表記になるんだ。と考える方もいると思いますし、

<?php echo $product->id ?>

下記のようになるのだと考える方もいると思います。

<?php echo htmlspecialchars($product->id, ENT_QUOTES) ?>

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

つまり、下記のような表記は htmlspecialchars を直接記載必要なく適切だと安心して使用できます。

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

ここまでくると気になるのが 変数に html タグを入れて そのまま表示したくないときです。
滅多にありませんが、たまにあります。たぶん。

    <div>{!! $name !!}</div

このような表記方法をすればよいと laravel の blade のドキュメントにもあります。
そう、この表記をすれば エスケープ処理されず正しくでるのです。

どんな使い道があるか?ぱっと思いつくのは、decorationなどのヘルパ関数があり、
その中で適当に html 修飾されて出てくる等です。

    <div>{!! decoration($name) !!}</div

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

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

app/Http/Controllers/ProductController.php
    public function index(Request $request){
        $products = Product::paginate(15);//※1
        return view('product.index',[
            'products' => $products,
        ]);
    }
resources/views/product/index.blade.php
@foreach( $products as $product )
   <div>{{$product->id}}</div>
   <div>{{$product->name}}</div>
@endforeach
{{ $products->links() }}   {{-- //※2 --}}

はい ※1 のようにページネーションして結果を収集し、※2 のようにすると、なんとそこに

<a href="">prev</a>
<a href="">[1]</a>
<a href="">next</a>

のような表記が展開されるのです。

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

{{ }} の正体

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

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

つまり、 {{ $name }} は e($name)で囲まれて出力されるのです。

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 でしょ!?
とおもったら、さぁ…

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脆弱性判断のために {!! を全体的に走査していたので、 {!! で囲むものには特殊な命名規則でつけたものを渡そう。
そういう独自ルールで対処した時期があったのですが、こちら使えばいいですね。

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

詳細のソースはこちら

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?