Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Fix stringable implementation #37603

Closed

Conversation

ibrasho
Copy link
Contributor

@ibrasho ibrasho commented Jun 4, 2021

The current implementation compiles echo statements with ternary operators (e.g. {{ $test ? "1" : "2" }} to the following code which throws a FatalError on PHP 8:

<?php echo e(is_object($test? "1" : "2") && isset(app('blade.compiler')->echoHandlers[get_class($test? "1" : "2")]) ? call_user_func_array(app('blade.compiler')->echoHandlers[get_class($test? "1" : "2")], [$test? "1" : "2"]) : $test? "1" : "2"); ?>

This PR tries to fix that issue by generating a compiled view without an included ternary operator.

@ibrasho ibrasho force-pushed the update-stringable-implementation branch from 7ad70f8 to d0c0ad7 Compare June 4, 2021 21:52
@taylorotwell
Copy link
Member

@lukeraymonddowning looks like string handling is broken if the person is already using ternarys.

@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 4, 2021

This caused an issue since I'm using Livewire and @entangle blade directive is already using a ternary operator.

@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 4, 2021

@lukeraymonddowning Any reason we are outputting ternaries in the complied view? I switched to just calling a method in the Blade compiler (since we are already resolving the compiler) instead.

@ibrasho ibrasho force-pushed the update-stringable-implementation branch from d0c0ad7 to b2940e2 Compare June 4, 2021 22:03
@taylorotwell
Copy link
Member

@ibrasho why make the call to the echo handler method in every compiled echo instead of only when there are actually echo handlers that are registered?

@lukeraymonddowning
Copy link
Contributor

@ibrasho ternary isn't necessary, although I think you've removed the ability for it to skip over if no handlers have been defined, which causes lots of other tests to fail

@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 4, 2021

@ibrasho why make the call to the echo handler method in every compiled echo instead of only when there are actually echo handlers that are registered?

The main reason I did that was that having different compiled outputs for echo tags was a bit confusing and made debugging hard for me. Having more consistent compiled output (regardless if the user has defined stringable handlers or not) seemed like a better idea.

WDYT @taylorotwell @lukeraymonddowning? I can either fix the tests, or restore the empty handler check?

@ibrasho ibrasho force-pushed the update-stringable-implementation branch from b2940e2 to 8584050 Compare June 4, 2021 22:30
@lukeraymonddowning
Copy link
Contributor

@ibrasho I would say 2 reasons for not having logic if no handlers have been defined:

  1. Loads of test cases need updating if you don't (that's why the test suite is currently failing).
  2. For those who don't need the functionality, they'll get a minor increase in performance. Nothing too noticeable, but worth factoring in.

@ibrasho ibrasho force-pushed the update-stringable-implementation branch from 8584050 to 2b9301d Compare June 4, 2021 22:34
@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 4, 2021

I'd personally prioritize maintainability over an unnoticeable performance improvement. But I'll leave that decision to @taylorotwell .

I updated the commit to add the empty handlers check back.

@lukeraymonddowning
Copy link
Contributor

lukeraymonddowning commented Jun 4, 2021

Thanks for the fix @ibrasho 👍 out of curiosity, how often do you find yourself debugging and reading through the compiled PHP?

@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 4, 2021

Thanks for the fix @ibrasho 👍 out of curiosity, how often do you find yourself debugging and reading through the compiled PHP?

Not frequently enough to make it matter honestly. But I'd rather I don't waste any time doing that in the first place. 😁

@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 5, 2021

@taylorotwell it looks like that change also breaks semicolons in echo statements. Is that even a supported feature that we should bring back? (Check livewire/livewire#2935)

@lukeraymonddowning
Copy link
Contributor

Could we strip out the last semicolon in the value? Can you see an issue with that?

@lukeraymonddowning
Copy link
Contributor

lukeraymonddowning commented Jun 5, 2021

Just done a test and it does work. Here's a test:

public function testHandlerLogicWorksCorrectlyWithSemicolon()
    {
        $this->expectExceptionMessage('The fluent object has been successfully handled!');

        $this->compiler->stringable(Fluent::class, function ($object) {
            throw new Exception('The fluent object has been successfully handled!');
        });

        app()->singleton('blade.compiler', function () {
            return $this->compiler;
        });

        $exampleObject = new Fluent();

        eval(
        Str::of($this->compiler->compileString('{{$exampleObject;}}'))
            ->after('<?php')
            ->beforeLast('?>')
        );
    }

And here's the updated method logic:

protected function wrapInEchoHandler($value)
    {
        return empty($this->echoHandlers) 
            ? $value 
            : 'app(\'blade.compiler\')->applyEchoHandler(' . Str::beforeLast($value, ';') . ')';
    }

Might be a good case for a test dataprovider, and throw it a bunch of common use cases

@GrahamCampbell GrahamCampbell changed the title Fix stringable implementation [8.x] Fix stringable implementation Jun 5, 2021
@taylorotwell
Copy link
Member

I'm not sure I totally follow the semicolon issue. What is the problem?

@lukeraymonddowning
Copy link
Contributor

@taylorotwell basically if the value inside the {{ }} ends in a semicolon, it throws an error because it gets wrapped in a method call, and you can't have a semicolon in a method parameter.

@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 5, 2021

For an example, this:

{{ $a; }}

now compiles into:

<?php echo e($a;); ?>

or if a stringable is defined:

<?php echo e(is_object($a;) && isset(app('blade.compiler')->echoHandlers[get_class($a;)]) ? call_user_func_array(app('blade.compiler')->echoHandlers[get_class($a;)], [$a;]) : $a;); ?>

Both cause a ParseError (notice the misplaced semicolons).

@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 5, 2021

Could we strip out the last semicolon in the value? Can you see an issue with that?

I don't see any issues with doing that, but I want to make sure @taylorotwell agrees this is the way forward.

@taylorotwell
Copy link
Member

taylorotwell commented Jun 6, 2021

Personally this whole feature is starting to give me a headache 😬

Is there any faster way to do this than calling into the container to resolve something on every single echo in your application's templates? That could be thousands of times per template when showing a lot of data. Calling into the container has to check for aliases, raise a before resolving event, etc.

If we can't think of a clean, fast way to solve this I may just remove the feature.

@lukeraymonddowning
Copy link
Contributor

lukeraymonddowning commented Jun 6, 2021

Let me take another look this evening. Might be able to resolve it once at the top of the template.

I think the original issue is solved to be honest. I'm struggling to see how the semicolon issue wasn't a problem previously, since it's also broke on echos it doesn't touch.

@lukeraymonddowning
Copy link
Contributor

@taylorotwell couldn't add to this PR so opened a new one: #37613

@ibrasho ibrasho force-pushed the update-stringable-implementation branch from 2b9301d to 65fe8fb Compare June 6, 2021 21:21
@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 6, 2021

@taylorotwell I'd suggest adding a new class and injecting it into the Illuminate\View\Factory like $__env and $app ? That way we don't need to resolve in every echo statement.

I just updated the Illuminate\View\Factory with the ability to echo objects since it's already injected in views (and honestly this functionality is not limited to the Blade compiler).

@ibrasho ibrasho force-pushed the update-stringable-implementation branch 3 times, most recently from 5cdbb41 to dd909db Compare June 6, 2021 21:58
@lukeraymonddowning
Copy link
Contributor

@ibrasho doesn't this change how you'd declare a handler seen as the compiler no longer has the stringable method?

@ibrasho ibrasho force-pushed the update-stringable-implementation branch from dd909db to 2527bed Compare June 6, 2021 22:29
@ibrasho
Copy link
Contributor Author

ibrasho commented Jun 6, 2021

I made a simple change to add that method back to BladeCompiler.

@ibrasho ibrasho force-pushed the update-stringable-implementation branch from 2527bed to 52c393f Compare June 6, 2021 22:37
@taylorotwell
Copy link
Member

Fixed. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants