Error Handling in Laravel, how can specify on Routes? - php

The basic of this is, I want to handle errors from my API and make sure all errors thrown via the API give json responses but for the website I want HTML responses to be sent.
If there a better way then purely applying the logic from http://laravel.com/docs/requests#request-information and using something like:
if (Request::is('admin/*'))
{
//
}
This works but seems a tiny bit clunky doing it for each exception type that might be thrown? The documentation doesn't seem to have anything but I was wandering if there's anything that might work so can apply errors for set route wildcards? Something like:
App::error('admin/*', function(Exception $exception)
{
Log::error($exception);
});

You can add an App::error filter and in that filter do the Route check, and then send a response in json. A simple example like this:
App::error(function(Exception $exception) {
if (Request::is('api/*')) {
$message = get_class($exception) . ":: message: " . $exception->getMessage();
return Response::json(["success"=>false,"message"=>$message]);
}
});

You could use a Route Group http://laravel.com/docs/routing#route-groups and apply a filter on the group, or prehaps you could use the App::error method if you declared it inside a prefixed route group http://four.laravel.com/docs/routing#route-prefixing . I'm not 100% whether either method would work, but if you have no luck the Laravel IRC is very active see http://laravel.io/irc for a quick easy web client.

Related

Handling PHP exceptions the correct way

I'm developing my own PHP framework and my code althought is working like it should, it's getting bigger and bigger; that of course leads to multiple ways for my framework to break, so I have decided it is time to implement Exception handling like any other PHP framework does, that 'nice' error view that tells you what might went wrong.
I have done my research and kind of understand how the Extension PHP default class works, I know that I'm able to extend this class and customize the error messages.
I also know that to trigger an Exception you gotta throw it and catch it with a "try/catch" statement, somethin like this...
class MyCustomException extends \Exception()
{
// My stuff
}
public function dontBeZero($number)
{
if ($number == 0) {
throw new MyCustomException('You gave me zero!!');
}
}
try {
dontBeZero(0);
} catch (MyCustomException $e) {
echo '<pre>';
$e->getMessage();
echo '</pre>';
}
I understand that, but my real question is: How does this popular frameworks such as Laravel, Symfony, etc manage to throw you a pretty message showing you what the error was, where do they keep all the logic that verifies whether it should or should not throw an exception, and most importantly where did they catch them?.
Most frameworks show these errors via a custom error handler. A popular one used by laravel is whoops.
You just need to register it as a custom handler, and you'll see the pretty error pages:
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
Keep in mind, you should disable these on production (so that your stack traces/code isn't exposed).
See the two functions set_error_handler and set_exception_handler. These functions allow you to register a callback function which is called when an error or exception occurs.
These callback functions are called by the Php runtime and provided with error details as arguments. The error details include error line number, stack trace, file name and more. The callback function can then format and display this information

Change "Whoops, looks like something went wrong." message

I'm sure I'm missing something silly here. I'm trying to replace the non-debug error screen that Laravel throws when there's an exception. It seems to be ignoring the code below (placed in start/global.php):
App::error(function(Exception $exception, $code)
{
Log::error($exception);
if(!Config::get('app.debug')) {
return Response::view('errors.exception', ['message' => $exception->getMessage()], 500);
}
});
Why would it ignore that? And was I supposed to do something elsewhere as well?
Bit of clarity:
I'm testing this with a QueryException (HY000). But surely this should not make a difference?
Using Laravel 4.2
It'd be hard to say without seeing your system, but my first guess would be there's another call to App:error made after yours that overrides what's you're trying to do in app/global.php.
I just wrote about how Laravel sets up it's error handling recently. After reading that article (or maybe skipping it and diving in), the way I'd debug this would be to hop into
vendor/laravel/framework/src/Illuminate/Exception/Handler.php
and look at the definition of callCustomHandlers. This is the method that calls any handlers setup via App:error
protected function callCustomHandlers($exception, $fromConsole = false)
{
foreach ($this->handlers as $handler)
{
//...
}
}
Your handler will be in the $this->handlers array. I'd add some temporary debugging code to this class (the class may be in Laravel's single combined optimized file) to determine
If your handler fails the handlesException test
If there's another handler added to the queue after your that "wins" and sends a response.
It also never hurts to start with a
App::error(function()
{
exit(__FILE__);
});
and then build out your error handler until it stops being called. That way you know what part of it Laravel's having a problem with.
In app/global.php there is a default error handler setup. You will want to remove that to use yours, or just modify that one.
Check this out for more information... http://laravel.com/docs/errors#handling-errors

When running unit tests with laravel, how do you test your App::error() implementations?

I'm currently working on an open source personal project that provides a nice backend api for game developers. I'm in the early stages of development, but I plan to write tests as I go along, which is where I've hit a snag.
Through out the system when an error occurs such as incorrect api credentials or missing credentials, I throw a custom exception which stores a bit of extra data so that I can catch it and give a JSON encoded response.
The tests work fine for those thrown in my BaseController, but I also capture a few Laravel Exceptions so I can respond with my own, or at least, output JSON like below:
app/start/global.php
App::error(function(Exception $exception, $code) {
Log::error($exception);
});
App::missing(function(Exception $exception) {
return BaseController::error(
Config::get('response.method.code'),
Config::get('response.method.http'),
'Method not found'
);
});
App::error(function(Viper\Exception $exception) {
return BaseController::error(
$exception->getCode(),
$exception->getStatusCode(),
$exception->getMessage()
);
});
I'm using the try { } catch() { } approach as I need to check an extra value that isn't in the normal Exceptions.
public function testNoMethodGET() {
$config = Config::get('response.method');
try {
$this->call('GET', '/');
} catch(\Viper\Exception $e) {
$this->assertEquals($e->getCode(), $config['code']);
$this->assertEquals($e->getStatusCode(), $config['http']);
}
$this->fail('Exception not thrown');
}
This is all good and well, but I want to check a few things on the actual response, like for example, whether or not the json is valid, whether or not the response structure matches and whether or not the response values are correct.
If I set the return value of $this->call() to a variable, I'd be unable to access that variable within the catch block, so the question is this, how can I test the return value of $this->call() once the Exception has been caught?
According to Taylor Otwell:
"this can be solved by de-coupling your
test. You really want to test the handler and that the exception is
thrown totally separately anyways [sic] to isolate your tests. For
instance:
App::error(function(ErrorType $e)
{
App::make('ErrorTypeHandler')->handle($e);
});
Now you can write test cases for ErrorTypeHandler class separately
from the rest of your application. Then check that proper exceptions
are thrown by your app with #expectedException."
see How do you test your App::error implementations?
In your case, you already have isolated your error handler in BaseController::error(), so you can test the responses directly in separate unit tests, without the use of $this->call(). Instead, just call $response = BaseController::error() with the desired parameters and then inspect the response and apply relevant assertions.

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

PHP - Are translations in controllers always bad?

I am using Zend Framework 2, but this question is not really ZF2 specific.
I have a situation where it would be convenient for me to translate a string in my controller rather than in my view script. Generally, I translate in the view scripts, but in a few scenarios, it is not so convenient. I feel as if it is definitely not the controller's job to do this, but I would like to hear opinions about this as well as recommendations.
To show what I mean, here is a scenario where it would be convenient:
public function registerAction() {
try {
$service_layer->registerUser(...);
}
catch (\My\Specific\Exception $e) {
$error = $this->translate('Something specific went wrong');
}
catch (\Exception $e) {
$error = $this->translate('Unfortunately, an error occurred!');
}
return array('error' => $error); // Pass error to view
}
I know how I can translate in the controller, so that is not a worry. Here I make use of a service layer, which may potentially throw exceptions. I would like to catch these so I can present the user with a helpful error message. Because several different errors can occur, I cannot simply send a boolean value to the view indicating if an error occurred, because then I cannot distinguish between which error happened.
So, I could translate the strings in the controller and print out the variables in my view script. This would make things easy when I use poedit to find translation strings. Alternatively, I could just return the non-translated strings and do $this->translate($error); in the view script. With this approach, I have a problem with detecting translation strings in my controllers. Using PHP's gettext() method just for this purpose seems like a waste of resources just so I can find the strings automatically.
I would like to hear what you guys think I should do in this situation. Thanks!
I'm pretty sure that the correct approach is to have the exception throwing an error code that should be translated within the view context.
I agree with you that this would be a wate of ressource but at the other hand it would make the application more reliable and modular which is the sole propose of using this approach, isn't it?
Always throw the error message. If someone really needs error messages translated, then they can do so on their own. The primary language for Exceptions though should always be english.
And then if someone needs to translate the Exception they can do so from the view-scripts:
echo $this->translate($e->getMessage());

Categories