skip to Main Content

I am looking for a reason why in blade files (Laravel) code with mixed @php() and @php ... @endphp fails, and works fine with consistent usage of those codes across the file. I’ve just learned that the hard way but can’t find the reason.

Fails:

@php($foo = 'foo')
<h1>{{ $foo }}</h1>
@php
    $bar = $foo . 'bar';
@endphp
@for($i = 1; $i<10; $i++)
    <h2><b>{{ $i }}<b/>{{ $bar }}</h2>
@endfor

Works fine:

@php($foo = 'foo')
<h1>{{ $foo }}</h1>
@php($bar = $foo . 'bar');
@for($i = 1; $i<10; $i++)
    <h2><b>{{ $i }}<b/>{{ $bar }}</h2>
@endfor

Also works fine:

@php
    $foo = 'foo';
@endphp
<h1>{{ $foo }}</h1>
@php
    $bar = $foo . 'bar';
@endphp
@for($i = 1; $i<10; $i++)
    <h2><b>{{ $i }}<b/>{{ $bar }}</h2>
@endfor

An error

Example

2

Answers


  1. It seems to be a scoping issue (or whatever you want to call it) as I mentioned in the comments.

    I did some testing in my own project.
    Adding below code snippet in a view;

    @php($foo = 'foo')
    <p>{{ $foo }}</p>
    @php
        $bar = $foo . 'bar';
    @endphp
    @for($i = 1; $i<10; $i++)
        <h2><b>{{ $i }}<b/>{{ $foo }}</h2>
    @endfor
    

    Results in the below, raw PHP output:

    <?php($foo = 'foo')
    <p>{{ $foo }}</p>
    @php
        $bar = $foo . 'bar';
    ?>
    <?php for($i = 1; $i<10; $i++): ?>
        <h2><b><?php echo e($i); ?><b/><?php echo e($bar); ?></h2>
    <?php endfor; ?>
    

    You can see why it fails, the @php($foo = 'foo') directive is seen as a starting PHP tag which does not end until the @endphp directive after the $bar variable initialization. That means that all the code between the @php($foo = 'foo') and @endphp directives is not seen as PHP code.

    Laravel also does not recommend the usage of the inline PHP directive anymore, as can be seen in this (quite recent) reply to someone with the same issue.

    So, I recommend (as do they), to just use a regular PHP block as you did in your last example:

    @php
        $foo = 'foo';
    @endphp
    <h1>{{ $foo }}</h1>
    @php
        $bar = $foo . 'bar';
    @endphp
    @for($i = 1; $i<10; $i++)
        <h2><b>{{ $i }}<b/>{{ $bar }}</h2>
    @endfor
    

    Update

    As @apokryfos pointed out, the link referenced in the answer is actually not correct. It references another issue not related to the @php() directive. However, it does reference the Laravel core team member recommending not using anything other than the @php and @endphp directives, therefore I’m leaving it in.

    Login or Signup to reply.
  2. The issue in my view is the order of operations when processing @php blocks.

    The relevant source code will first store uncompiled blocks before it actually compiles anything. Uncompiled blocks seem to be @php ... @endphp and @verbatim ... @endverbatim blocks. The code for @php is:

       protected function storePhpBlocks($value)
        {
            return preg_replace_callback('/(?<!@)@php(.*?)@endphp/s', function ($matches) {
                return $this->storeRawBlock("<?php{$matches[1]}?>");
            }, $value);
        }
    

    So in your code block, the process of trying to store code between a @php ... @endphp everything between @php($foo = 'foo') and @endphp will be matched (which includes the 2nd @php tag) which will result in broken PHP code being stored.

    Reading the docs I can see that while the syntax @php ... @endphp is documented, the alternative syntax @php(...) is not which makes me think that while the blade compiler implementation supports this syntax, it has not been documented (or not documented anymore) because it is problematic.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search