laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.69k stars 11.05k forks source link

Unsupported use of @php? #22621

Closed collegeman closed 6 years ago

collegeman commented 6 years ago

In my exploration of the Sage WordPress Starter Theme—a WordPress project that borrows much from Laravel, including introducing Blade as a dependency—I have found an interesting problem. I'm reporting it as a bug instead of a pull request only because I can't think of an easy way to solve the problem, and I'm hoping the community might.

The problem arises from the following (potentially unsupported use case) of the @php directive. Consider the following script:

<html @php(language_attributes())>
<head>
  <title>
    @php
       $someArbitraryThing = 'whatever';
       echo $someArbitraryThing;
    @endphp
  </title>
</head>
<body></body>
</html>

When compiling the above, the Blade engine produces something like the following:

<html <?php(language_attributes())>
<head>
  <title>
    @php
       $someArbitraryThing = 'whatever';
       echo $someArbitraryThing;
    @endphp
  </title>
</head>
<body></body>
</html>

Instead of what was expected, e.g.,

<html <?php (language_attributes()); ?>>
<head>
  <title>
    <?php $someArbitraryThing = 'whatever';
       echo $someArbitraryThing;
     ?>
  </title>
</head>
<body></body>
</html>

I have discovered that this is caused by the regular expression in BladeCompiler::storePhpBlocks, which is used to collect all of the template content inside each section of @php to @endphp, including sections that span multiple lines of the template.

So the conflict seems to be that if one uses the single-tag form of the @php directive anywhere in a template, one cannot also use the open/close form of the @php directive anywhere thereafter in the same template.

It's entirely possible that @php shouldn't be used in a single-tag form—the Laravel documentation does not say it can be used this way, though it does work, so long as no @endphp appears in the same template. Maybe instead of this single-tag form, folks should simply use something like {!! functionCall() !!} instead?

devcircus commented 6 years ago

According to the 5.5 upgrade guide, the inline syntax is no longer supported. See below:

@php Blade Directive The @php blade directive no longer accepts inline tags. Instead, use the full form of the directive:

@php
$teamMember = true;
@endphp
mul14 commented 6 years ago

I believe they change @php Blade directive behaviour because of this https://github.com/laravel/framework/pull/20065

collegeman commented 6 years ago

@devcircus, @mul14, and @GrahamCampbell: thank you all for the time you spent on this. It was exactly the confirmation I needed.

ameenross commented 6 years ago

I found the mention of "inline tags" in the upgrade guide vague, so I googled it and landed here. So I tested this snippet, but it seems that the behavior changed again?

>>> Blade::compileString('@php(1+2+3) {{$bar}} @php(4+5)')
=> "<?php (1+2+3); ?> <?php echo e($bar); ?> <?php (4+5); ?>"
ameenross commented 6 years ago

Also interesting:

>>> Blade::compileString('@php(echo"foo") @php(print"bar")')
=> "<?php (echo"foo"); ?> <?php (print"bar"); ?>"

Obviously the echo won't work, because it's a (PHP) language construct. However, print will work.

ameenross commented 6 years ago

Ok, hold on. It seems the behavior wasn't changed, it's just unpredictable:

>>> Blade::compileString('@php(echo"foo") @php print"bar"; @endphp')
=> "<?php(echo"foo") @php print"bar"; ?>"

Apparently, when you have an @endphp in your code, no ?> will be added after the closing parentheses. So combining @php() with @php $expr + $ession; @endphp is problematic

mirafzal commented 2 years ago

regex replace: @php((.*)) @php $1 @endphp