浜松のWEBシステム開発・スマートフォンアプリ開発・RTK-GNSS関連の開発はお任せください
株式会社シーポイントラボ
TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:静岡県浜松市中区富塚町1933-1 佐鳴湖パークタウンサウス2F

【Laravel】Blade で”@if($hoge)checked@endif”が動作しない原因と対策

 Laravel は PHP のフレームワークであり Web サイトの構築によく使われます。HTML と PHP をいい感じにつなぐテンプレートエンジンも備えており、Laravel の使うテンプレートエンジンは Blade を使っています。
Bladeテンプレート 8.x Laravel
 Blade では次の様に @ を使って PHP 的な様々な制御を行い、よしなに PHP に変換、エスケープすることで開発者のコーディングを楽にしてくれます。

{{-- Blade なら --}}
@if( true === false )
if が true なら表示される文字列
@endif

// 素のPHPでは
<?php if( true === false): ?>
if が true なら表示される文字列
<?php endif; ?>

 この Blade を使っているときに文法エラーとなるパターンに立ち会うことがしばしばあります。一見 Blade 的に正しくも、文法エラーが発生するパターンが次です。

<input
    type="checkbox"
    name="checkbox-example"
    {{-- 渡された checkbox-example が truthy ならばチェックボックスをチェック済みにする --}}
    @if( $data['checkbox-example'] )checked@endif
>

 これを動かすと次のエラーが出ます。

syntax error, unexpected end of file, expecting elseif (T_ELSEIF) or else (T_ELSE) or endif (T_ENDIF) 

 if 文が閉じないままファイルが終わってしまったというエラーです。@endif が働いていないということです。実際にコンパイル後の PHP ファイルを見ると次の様になっており、@endif が期待したように変換されないことでエラーが起きる状態になっています。

<input
    type="checkbox"
    name="checkbox-example"
    
    <?php if( $data['checkbox-example'] ): ?>checked@endif
>

 これは次で解決します。

<input
    type="checkbox"
    name="checkbox-example"
    {{-- 渡された checkbox-example が truthy ならばチェックボックスをチェック済みにする --}}
    @if( $data['checkbox-example'] )checked @endif{{-- @の前に半角スペースを入れる --}}
>

この様にすると期待どおりに @endif が次の様にコンパイルされます。

<input
    type="checkbox"
    name="checkbox-example"
    
    <?php if( $data['checkbox-example'] ): ?>checked <?php endif; ?>
>

とりあえずディレクティブと何かの間には隙間を作っておくのが無難です。何かの事態で色を付けられないエディタ(あるいはつけ方がわからない不慣れなエディタ)で作業する羽目になっても視覚上わかりやすくなります。

 半角スペースを入れることで対処できる理由はドキュメントにないコンパイラの都合にソースコードを合わせたためです。原因となっているのは Laravel 8.37.0 においては \Illuminate\View\Compilers\BladeCompiler::compileStatements の次のコード中の正規表現です。

    /**
     * Compile Blade statements that start with "@".
     *
     * @param  string  $value
     * @return string
     */
    protected function compileStatements($value)
    {
        return preg_replace_callback(
            '/\B@(@?\w+(?:::\w+)?)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', function ($match) {
                return $this->compileStatement($match);
            }, $value
        );
    }


 これは正規表現に合致する制御文字列らをコンパイラの次の段階に送り込む処理です。グループ化部分は複雑ですが、今回重要なのは頭の\Bです。これは正規表現の非単語境界を示すエスケープシーケンスです。
PHP: エスケープシーケンス – Manual
 非単語境界はざっくばらんに言えば単語境界でない文字の境界です。単語境界は単語構成文字(\wでヒットする文字)と非単語構成文字(\Wでヒットする文字)か行頭か行末の境界です。例えば”public function”とあったら単語境界は”行頭”と”p”、”c”と”半角スペース”、”半角スペース”と”f”、”n”と”行末”のそれぞれの間となります。 非単語境界は逆に”p”と”u”、”u”と”b”、”b”と”l”、……それぞれの間です。
 このため、エラーとなった場合の次の文字列は

@if( $data['checkbox-example'] )checked@endif

単語構成文字である”checked”中の”d”と非単語構成文字である”@endif”中の”@”が続くことで”@”の左側の境界が単語境界となり、”\B”を満たせず、次のコンパイラの処理に @endif が渡らず、@endif がコンパイルされないまま残り、文法エラーとなりました。
 期待通りの結果のなった次の文字列は

@if( $data['checkbox-example'] )checked @endif

非単語構成文字である”@endif”中の”@”の直前に、非単語構成文字である” “が置かれたことで、”@”の左側の境界が非単語境界となり、”\B”を満たせ、無事コンパイラに @endif が渡され、コンパイルされ、期待通りの出力を行うことになりました。

  • この記事いいね! (1)