-
Notifications
You must be signed in to change notification settings - Fork 11.4k
View exception handling fix (discussion) #7965
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
Conversation
Can you describe the issue? |
Also it seems sort of weird we would return exception results back out as the view result? Wouldn't taht potentially show an exception message to users of the application? |
If I do an include in a view:
And that partial generates an exception - you get a message something like this:
And it doesn't give any real feedback information. The code in the PR is just to demonstrate and get the discussion going - am happy to implement/change whatever you think would suit it best. I also don't like how it is written currently - it's ugly. But we need to do something :) |
{ | ||
return $this->render(); | ||
} | ||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please indent with tabs.
Yeah, the solution presented doesn't seem acceptable as it just returns an error message as if that is what the view rendered, which would be confusing. I wish PHP would let you throw exceptions from __toString but it doesn't. How did you cause this error. Are you echoing a view without calling render? |
@taylorotwell na as I mentioned before, just doing an @include of a view from within a blade template that throws an exception. |
@taylorotwell perhaps the language-friendly approach would not to use __toString for the render. Perhaps @include could instead call another method outputRender (or something) that immediately echoes the output from the method (which is effectively what __toString is doing. This way we can still have our nice exception handling? |
Well @include does call ->render(), it doesn't use __toString. Was there On Wed, Mar 11, 2015 at 9:03 PM, Kirk Bushell notifications@github.com
|
@taylorotwell hmmm, it could have been. It's a nested menu system, so it could have been several levels down. They all use @include, though. It does this: @include('partials.menu') Then inside that, somewhere: @include('partials.menu') or @include('partials/menuitem') So it could be 3-4 levels deep. |
If you look in BladeCompiler you can see that compileInclude calls On Wed, Mar 11, 2015 at 9:11 PM, Kirk Bushell notifications@github.com
|
Alright, this is the full menu.blade.php and item.blade.php: menu.blade.php
item.blade.php
The issue that was causing it previously was the $item->icon (I got it wrong, debugging it was a pain). This threw an exception (no property found iirc) which then resulted in the weird "__toString cannot throw exception" error. I had to impelment that debugging code in the PR to figure out what the hell was going on. |
I came across the same problem here, the only solution was to debug the __toString method. Yesterday, when not aware of this discussion, I even created a PR with the solution I found.
|
Yeah we definitely need @taylorotwell's input regarding how he wants to handle this. I've encountered it more than a handful of times and each time is a real pain to debug. |
I think the first step is figuring out where in your application __toString is being called from, perhaps with a debug_backtrace. |
@taylorotwell will that help, though? I think this is an unseen issue due to perhaps the way we use blade templates and includes (not sure). I wonder if resolving how __toString is handled on the view will at least alleviate that problem not just now, but in the future. |
Once we know where it's called, we might be able to come up with a better solution where calling For a readable stack, use:
|
Yeah my point is same as @JoostK, the framework itself should never really be calling __toString so we need to figure out where that is happening so we can make it call ->render() instead. PHP (for now) is never going to let us give a clean exception out of __toString so best to just avoid it altogether. |
Okay good point - will investigate further. @JoostK thanks for the sample code, will use that to debug. |
Right, here's the full stack:
|
What is in that compiled view (6faf30a2d9347875c5edecdce8fceeb6) on that line 29? |
Ahhh, okay now we're getting closer. Line #29, is this:
Which is this macro: HTML::macro('menu', function ($menu) {
// If a menu instance was not passed, let's instead try and fetch the menu by name
if (!($menu instanceof \Tectonic\Shift\Library\Menu\Menu)) {
$menu = Menufy::menu($menu)->first();
}
// Render the menu
return view('partials.menu.menu', compact('menu'));
}); I think now I realise the issue - view should probably return render? |
Yep. return view()->render(); |
Okay cool, soooo.... This is a slightly different behaviour from what is recommended when working with controllers (obviously). Is there anything you'd like to change or for me to work on @taylorotwell to try and avoid this in future? |
I wonder if __toString is necessary in this case, as it seems to create problems if exceptions are thrown in views? |
Aka, should __toString be removed? |
What's interesting is that apparently we can use trigger_error from within the __toString method to perhaps raise an error message. |
Perhaps HTML macros should automatically call |
I'm curious if we can catch the exception in __toString and do this...
|
That's not a bad idea either (as another solution) - but html/form is being managed by laravel-collective now and they've made it clear to me that no improvements/changes/extra features will be forthcoming. @taylorotwell will test now :) |
@taylorotwell unfortunately it results in the same error: FatalErrorException in 6faf30a2d9347875c5edecdce8fceeb6 line 0: This is when triggering an error manually if an exception has been caught: public function __toString()
{
try {
return $this->render();
}
catch (\Exception $e) {
trigger_error((string) $e, E_USER_ERROR);
}
} |
it seems to me that the only way this is happening is if an echo is done directly on the view in these helpers. @taylorotwell what's the need for __toString if it's never been called by Laravel itself? |
The need is because some people still call it (as evidence by this PR) :) haha... but yeah ideally it would just be removed entirely |
@taylorotwell yup, true - but it results in an issue that cannot be easily resolved. It seems to me using __toString on a rather lengthy/complex set of logic which can result in exceptions being thrown, simply means annoyed developers as it's rather a pain to debug. It appears to be an anti-pattern within PHP. i double-checked by fixing up all my macros/helpers and then throw an exception in __toString - it never gets called by the framework itself. This would be a breaking change - but I think a minor one. |
We're only about 8 weeks away from 5.1. We can probably remove it there. |
I may have another solution for now :) |
See: #7980 |
Another hack I just thought of is assigning the exception to a static property and at the end of the request throwing that exception if it's been set. This may however give an incorrect stacktrace, I'm not sure how generating an exception and throwing it somewhere different affects the trace. |
Yeah that would be very hacky - the trace would start from where it's thrown. |
Actually that's not what happens, it works pretty nicely. There is no sign of it being thrown somewhere completely different. |
Hmmm, I've seen quite the opposite when dealing with caught exceptions that are re-thrown in Laravel. Perhaps something else was at play... |
If an exception is caught and then a new exception was created, then yes. Quoting from a comment in the PHP documentation:
|
yeah that's super cool - it must have been something else I was fighting against when I saw another case. |
I've started this PR as it needs to be fixed, but unsure as to how you would like it handled @taylorotwell . If you can provide some direction I'll resolve and then update this PR.