Laravel6 改行をビューで表示する

# 目的

- PHPのエスケープシーケンスを用いた改行をビューで表示する方法をまとめる

# 実施環境

- ハードウェア環境

| 項目 | 情報 |
| --- | --- |
| OS | macOS Catalina(10.15.5) |
| ハードウェア | MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) |
| プロセッサ | 2 GHz クアッドコアIntel Core i5 |
| メモリ | 32 GB 3733 MHz LPDDR4 |
| グラフィックス | Intel Iris Plus Graphics 1536 MB |

- ソフトウェア環境

| 項目 | 情報 | 備考 |
| --- | --- | --- |
| PHP バージョン | 7.4.8 | Homebrewを用いてこちらの方法で導入→[Mac HomebrewでPHPをインストールする](https://qiita.com/miriwo/items/cd54077aad7e139cf518) |
| Laravel バージョン | 6.X | commposerを用いてこちらの方法で導入→[Mac Laravelの環境構築を行う](https://qiita.com/miriwo/items/adaaaeecae69d6392e08) |
| MySQLバージョン | 8.0.19 for osx10.13 on x86_64 | Homwbrewを用いてこちらの方法で導入→[Mac HomebrewでMySQLをインストールする](https://qiita.com/miriwo/items/ef6a064bc545fbea29d9) |

# 前提条件

- 実施環境に近い開発環境が用意できていること。
- ローカルサーバを起動する事ができるLaravel6のアプリケーションが存在していること。

# 前提情報

- PHPのエスケープシーケンスを用いて改行を含んだ文字列をビューで改行して表示したい。
- ブラウザでは下記のように表示してほしい。

    ```html
    A
    <br>
    B
    <br>
    C
    ```

# 概要

1. ルーティング情報の記載
1. コントローラファイルの作成と記載
1. ビューファイルの作成と記載
1. 確認

# 詳細
1. ルーティング情報の記載
    1. Laravelアプリ名ディレクトリで下記コマンドを実行してルーティングファイルを開く。

        ```terminal
        $ vi routes/web.php
        ```
    1. 下記のルーティング情報を追記する。

        ```Laravelアプリ名ディレクトリ/routes/web.php
        Route::get('/index', 'TestController@index')->name('index');
        ```
1. コントローラファイルの作成と記載
    1. Laravelアプリ名ディレクトリで下記コマンドを実行してコントローラファイルを作成する。
    
        ```terminal
        $ php artisan make:controller TestController
        ```
    1. Laravelアプリ名ディレクトリで下記コマンドを実行してコントローラファイルを開く。
    
        ```terminal
        $ vi app/Http/Controllers/TestController.php
        ```
    1. 下記のアクションをコントローラファイルに記載する。
    
        ```Laravelアプリ名ディレクトリ/app/Http/Controllers/TestController.php
        public function index()
        {
            /* シングルクオートで文字列を囲むとエスケープシーケンスが文字列扱いされるので注意 */
            $str = "A\nB\nC";
            return view('tests.index', ['str' => $str]);
        }
        ```

    1. 記載後のコントローラファイルの全体の内容を下記に記載する。
    
        ```Laravelアプリ名ディレクトリ/app/Http/Controllers/TestController.php
        <?php
        
        namespace App\Http\Controllers;
        
        use Illuminate\Http\Request;
        
        class TestController extends Controller
        {
            // 下記を追記する
            public function index()
            {
                /* シングルクオートで文字列を囲むとエスケープシーケンスが文字列扱いされるので注意 */
                $str = "A\nB\nC";
                return view('tests.index', ['str' => $str]);
            }
            // 上記までを追記する
        }
        ```

1. ビューファイルの作成と記載
    1. Laravelアプリ名ディレクトリで下記コマンドを実行してビューファイルを格納するディレクトリを作成する。
    
        ```terminal
        $ mkdir -p resources/views/tests
        ```
    1. Laravelアプリ名ディレクトリで下記コマンドを実行してビューファイルを作成して開く。
    
        ```terminal
        $ vi resources/views/tests/index.blade.php
        ```
    1. 下記の内容を記載する。
    
        ```Laravelアプリ名ディレクトリ/resources/views/tests/index.blade.php
        <p>{!! nl2br(e($str)) !!}</p>
        ```
1. 確認
    1. Laravelアプリ名ディレクトリで下記コマンドを実行してローカルサーバを起動する。
    
        ```terminal
        $ php artisan serve
        ```
    1. 下記にアクセスする。
        - [http://127.0.0.1:8000/index](http://127.0.0.1:8000/index)
    1. 下記のようにABCがそれぞれ改行されて表示されることを確認する。

        ![127_0_0_1_8000_index.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/492db39d-81cc-58bc-f796-c8cefb7f0c97.png)

# 検証

1. 変数に入った文字列をビューファイルで表示する際、本来は`{{ }}`でくくって表示するが今回は``{!! !!}``でくくっている。試しにビューファイルの記載を`{{ }}`にして$strだけを表示してみる。
    1. 下記のようにビューファイルのソースを修正した。

        ```Laravelアプリ名ディレクトリ/resources/views/tests/index.blade.php
        <p>{{$str}}</p>
        ```
    1. 下記のように改行がなされずに表示されてしまう。

        ![127_0_0_1_8000_index.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/01079100-e22b-ebfd-f032-2789dfcec34e.png)

# {!! nl2br(e($str)) !!}の簡単な解説

1. `e($str)`の説明
    - 変数$strに格納された文字列の中の「& (アンパサンド)」「" (ダブルクォート)」「' (シングルクォート)」「< (小なり)」「> (だいなり)」を別の文字に変換している。
        - [https://readouble.com/laravel/6.x/ja/helpers.html#method-e](https://readouble.com/laravel/6.x/ja/helpers.html#method-e)
    - もし仮に$strにXSS攻撃を意図としたリンクなどが含まれていたときに悪意のあるリンクタグを表示されないようにエスケープする。
1. `nl2br()`の説明
    - PHPのエスケープシーケンス（`\n`など）からhtmlのタグ（`<br>`など）を作成する関数である。
        - [https://www.php.net/manual/ja/function.nl2br.php](https://www.php.net/manual/ja/function.nl2br.php)
    - これは変数$strに格納されている`\n`をhtmlの`<br>`に置き換えてくれている。（正確には`\n`の直前に`<br>`を追記してくれている。）
1. `{!! !!}`の説明
    - 通常の`{{ }}`はXSS攻撃を予防するために特殊文字を勝手にエスケープしてしまう。
        - [https://readouble.com/laravel/6.x/ja/blade.html#displaying-data](https://readouble.com/laravel/6.x/ja/blade.html#displaying-data)「Tip!! Bladeの{{ }}記法はXSS攻撃を防ぐため、自動的にPHPのhtmlspecialchars関数を通されます。」
    - これは関数`nl2br()`て作成した`<br>`タグの例外ではなくエスケープされてしまう。
        - [https://readouble.com/laravel/6.x/ja/blade.html#displaying-data](https://readouble.com/laravel/6.x/ja/blade.html#displaying-data)「エスケープしないデータの表示 デフォルトでブレードの{{ }}文はXSS攻撃を防ぐために、PHPのhtmlspecialchars関数を自動的に通されます。しかしデータをエスケープしたくない場合は、以下の構文を使ってください。」
1. まとめると「`{!! !!}`を使用しないと`nl2br()`で作成した`<br>`がエスケープされてしまう。しかし`{!! !!}`はXSS攻撃される可能性がある。それを防ぐために`nl2br()`にわたす変数$strには特殊文字が入っていたら困る。`nl2br()`にわたす前に`e()`でXSS攻撃と思しき特殊文字はエスケープして渡す」という感じ。

# おまけ

1. PHPのhtmlspecialchars関数について
    1. `{{ }}`を用いたビューファイルの表示はPHPのhtmlspecialchars関数を通したものであるらしい。
        - [https://readouble.com/laravel/6.x/ja/blade.html](https://readouble.com/laravel/6.x/ja/blade.html)「Tip!! Bladeの{{ }}記法はXSS攻撃を防ぐため、自動的にPHPのhtmlspecialchars関数を通されます。」
    1. PHPのhtmlspecialchars関数が何をしているのかを気になったので調べてみる。下記にPHPドキュメントのリンクを記載する。
        - [https://www.php.net/manual/ja/function.htmlspecialchars.php](https://www.php.net/manual/ja/function.htmlspecialchars.php)
    1. 当該関数を通すと下記の特殊文字列が変換される。

        ![PHP__htmlspecialchars_-_Manual.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/4415c421-0e1a-7807-6e48-875195a66d81.png)

    1. なので`{{ }}`を通して表示する文字列は上記の変換が行われた上で表示が行われる。
    1. htmlspecialchars関数を迂回して表示したいときは`{!! !!}`を使用する。
    1. Laravelで意図的にhtmlspecialchars関数で特殊文字列を変換したいときはヘルパ関数`e()`を使用する。
    1. 脆弱性の観点からみて今回のような特殊ケースを覗いて`{!! !!}`は多用しないほうが良いと思う。
    1. 特殊ケースで`{!! !!}`を使用するとしてもLaravelならヘルパ関数`e()`を併用するなど攻撃に対する対策が必要であると考える。