Exception handling in Symfony2 - php

How to handle exceptions manually in symfony2 in case like this:
class Foo {
// ..
public function __toString()
{
try {
$this->render();
}
catch (\Exception $e)
{
// log $e
// handle $e - display 500 error page in prod mode
}
}
}
Redirect? But how.
EDIT
My solution so far is to dispatch exception event, and it works.
$dispatcher->dispatch(KernelEvents::EXCEPTION, $event)
But i requires creating an event. Is there some better solution?

You can also define your own exception controller and perform needed behavior.
Check How to customize Error Pages and Configuration: exception_controller
UPD
Creating event listener is a good solution to catch exceptions.

We can also customize specific TWIG error templates according to the HTTP status ( returned )code. For example, create a app/Resources/TwigBundle/views/Exception/error404.html.twig template to display a special page for 404 (page not found) errors.
OR, we can also customize exceptionController.

Related

Laravel - One view for all possible errors

So I want to cover all posible and unexpected errors (like 401, 404, 500) with just one view. I want the same view to show up on all possible errors. I came up with a solution - to copy/paste the same code and just name the views with different error codes. But that seems stiff and wrong. Is there a better way of achieving this?
In the file app/Exceptions/Handler.php you can change what happens when an exception is thrown. In particular there's a render method in there that you can use to catch all the exceptions in an application.
public function render($request, Exception $e)
{
// Handle your error here. Perhaps you can show a view
// when a condition is met. Anything that isn't caught
// here will be handled by Laravel with the
// parent::render call below
return parent::render($request, $e);
}
The parent::render($request, $e) is where Laravel would normally show it's exception/oops page. So by overriding this method you can catch all application errors, including 404, 401 etc.
A cleaner way to achieve this effect is by modifying Laravel's exception handler.
Modify App\Exceptions\Handler to catch every error and return your shared custom error page.
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $e
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
if ($e instanceof NotFoundHttpException) {
return response()->view('errors.custom', [], 404);
}
return parent::render($request, $e);
}
Some customization may be required to fully meet exactly what & how you want data passed to your shared custom view.
Pass the error code to your view on the handler, and display the code on your page, use a switch to handle all the messages depending on the error code.
You can create one unique view (default to 404 error), use try catch in your code to capture other errors and call to this view with parameters so you can change the 404 default error to other error.

Laravel. Catch Exception thrown on App::make('foo')->bar()

I have a PriceController that updates the prices of my items. In my app, a Price is a set or PriceSegments (or Rules).
So my PriceController#update does:
foreach (Input::get('price_segment_id') as $price_segment_id)
{
try {
\App::make('BF\Controllers\PriceSegmentController')->update($price_segment_id);
} catch(BF\Exceptions\ValidationException $e) {
$errors[] = $e->get_errors();
}
}
And my PriceSegmentController#update does the update of each segment as follow:
$priceSegment = $this->repository->find($id);
if($priceSegment) {
// UPDATE $priceSegment with Input, etc.
$this->validator->validate($priceSegment->toArray());
$priceSegment->save();
}
If I do the try-catch in PriceSegmentController it works as expected, but I would like to do the try-catch un PriceController in order to collect all the error and being able to inform them all at once.
The problem is the Exception is not catched and I do not know why. I guess is something related with the behaviour of App::make('foo') but I did not figure it out.
Some help please?
Thanks
EDIT: The structure of my code is based on this example: http://www.sitepoint.com/data-validation-laravel-right-way/ and I am using my own Exceptions extending the Exception PHP class
I think the problem occurs because you are using the default validation mechanism in laravel as described here.
What you can do instead is to catch exceptions in PriceSegmentController and:
either wrap them with a custom exception type
or create a custom exception using exceptions you catch in that controller
and throw your custom exception further up in the food chain.
I hope it helps.

Laravel catch exception and add message to a messagebag

I have a repository that throws an exception if it can't find a record in the database. Rather than redirect to another page I just want to display a warning alert as the record is not critical to the page but is an "exceptional event".
It's probably best to demonstrate with code:
// FxRateRepositoy
public function getRate(/** args **/)
{
$rate = FxRate::where(.... //query to get the rate
if (!rate)
throw new NonExistentCurrencyException(//message);
return $rate;
}
In my start/global.php I have a handler:
App::error(function(NonExistentCurrencyException $e)
{
Session::flash('alert', $e->getMessage());
return \\ ??
});
What to return? I must return a response or the exception continue uncaught. I want to continue to the intended page but with the alert flashed in the session. Is this possible without having to use try catch blocks in every place this method is called?
Ass an additional question, assuming this exception may be thrown multiple times in one request, what's the best way to accumulate alert messages and display them? I'm thinking something akin to the validation messageBag. Can I just use the global $errors variable or should I create a new, specific messagebag for this purpose?
The problem is that if you return nothing from App::error Laravel will display it's default error page. On the other side you can't return a response because you don't know what response it should be in the error handler.
I suggest you handle it in the controller itself.
You can catch the exception there and flash the message or don't throw an exception at all:
$rate = FxRate::where(.... //query to get the rate
if (!rate){
Session::flash('alert', 'Whoops');
}
Also the findOrFail() and firstOrFail methods might be of use. They throw an ModelNotFoundException if the query yields no results:
try {
$rate = FxRate::where(....)->firstOrFail()
// and so on
} catch (Illuminate\Database\Eloquent\ModelNotFoundException $e){
Session::flash('alert', 'Whoops');
}
As for a messages system, take a look at the laracasts/flash package

custom error page - can't get into handler

I'm trying to add my custom pages. I'm using Kohana 3.3. The official documentation states that I should ovveride the method hander of native Kohana_Exception class. This is pretty easy to do so I've done that. Now I'm expecting that Kohana would call that method every time an exception or an error occurs. But this is not the case. I've found 2 catch blocks where an exception is caught inside execute_request method of Kohana_Request_Client_Internal class.
First catch
catch (HTTP_Exception $e)
{
// Get the response via the Exception
$response = $e->get_response();
}
Second catch
catch (Exception $e)
{
// Generate an appropriate Response object
$response = Kohana_Exception::_handler($e);
}
As you can see, none of the catch blocks calls handler method overriden by me.
Setting your own exception handler set_exception_handler has no effect since it is applied only to uncaught exceptions and the exceptions like 404 are thrown and caught.
No problems with run-time errors though. This block catches them and explicitly calls overriden handler method.
if (Kohana::$errors AND $error = error_get_last() AND in_array($error['type'],
Kohana::$shutdown_errors))
{
// Clean the output buffer
ob_get_level() AND ob_clean();
// Fake an exception for nice debugging
Kohana_Exception::handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
// Shutdown now to avoid a "death loop"
exit(1);
}
So my question how do I set up everything to have custom error page for Exception and HTTP_Exception?
PS. I can ovveride HTTP_Exception_404 and HTTP_Exception_500 to have my custom error page displayed, but I don't think that's the best option since it could work for these two, but overriding all possible HTTP_Exceptions is not a good way to go.
PS2. Or I can set my custom view in bootstrap.php:
Kohana_Exception::$error_view = 'custom_error.tpl';
Also don't like that solution.
All links to Kohana's documentation in this post are for version 3.3
You won't get what you want by overwriting just one method. Below I explain the methods that you can overwrite in order to achieve what you want. Just make just sure you put the right code in the right method.
Don't try to do everything in one place. While it will be all in one place is will most likely become a mess.
Kohana_Exception::handler()
Kohana_Exception::handler() is for when exceptions reach the exception handler or like you showed, in the shutdown handler. The last chance you have to display a nice error page in production environments. It outputs the result of Kohana_exception::_handler(), which is a Response object, and is therefor not suited to be called inside Request_Client_Internal::execute_response().
For production: Log the original exception. Since this scenario matches the description of HTTP Status Code 500 (Internal Server Error) Kohana_Exception::handler() should display a 500 error page.
During development you probably want to call parent::handler().
Kohana_Exception::_handler()
Kohana_Exception::_handler() return a Response object, so it is suited to be called in Request_Client_External::execute_response(), Kohana_Exception::handler() and View::__toString().
HTTP_Exception::get_response()
HTTP_Exception::get_response() will get called for exceptions extending HTTP_Exception, with the exception of exceptions extending HTTP_Exception_Expected. A few examples of exceptions extending HTTP_Expected_Exception are the 3xx and 401 HTTP_Exceptions.
By default it returns Kohana_Exception::response().
Overwrite it in specific exceptions to return specific responses for said exception.
Overwrite it in HTTP_Exception when you want the replace the default response.
Kohana_Exception::response()
Kohana_Exception::response() is responsible for collecting the data needed to render the Kohana_Exception::$error_view template. An example of output can be seen on the kohana/errors page of the userguide.
Change Kohana_Exception::$error_view when you want a different layout for the same data.
Overwrite Kohana_Exception::response() to replace the entire thing.
PS. Kevin pointed you to the 3.2 documentation. How to do this is very different for 3.2 and 3.3.

Capture exceptions with a different handler for applications REST

The problem
I'm building a small application with Silex. It's divided between a REST application and a website. (two controllers, same app).
The website has installed its own custom error handler, which returns a user friendly html page. The problem is that in the part dedicated REST application, I should somehow handle exceptions to return type [json] and content different from the error handler's custom website.
With Symfony2
This argument can also be applied to Symfony2, I would like also possible solution for it!
A first solution for Silex
Wrap the methods in try-catch block in order to rethrowing the exception to handler.
$app->get('/api/show-list', function() use($app){
try {
$show = // db query, etc.
return $app->json(array('show' => $show), 200);
} catch (Exception $e) {
throw new MyException;
}
});
$app->error(function (MyException $e, $code) {
// error api
});
The issue is that if an exception is thrown out of my controllor the default error handler will be used. Some tips?
And with Symfony?
I have been using the following in my Silex RESTful app to return errors in json format:
$app->error(function (\Exception $e, $code) use($app) {
return $app->json(array("error" => $e->getMessage()),$code);
});
Not sure if this is the right way, but it works for me.
This is documented on the Silex site:
http://silex.sensiolabs.org/doc/usage.html#error-handlers
On Symfony2 you can use the ExceptionHandler. On the Exception you have the stack trace, so you can identify where it was thrown.
Also, in Symfony2 you can customize depending on the expected format. It's well explained in it's documentation.
For instance, if you replace the ExceptionController with one of yours, the third parameter shows the expected format:
Reference on where to change the ExceptionController
ExceptionController API

Categories