Laravel catch exception and add message to a messagebag - php

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

Related

Should PHP exception try/catch blocks be specific to the exception?

I am trying to determine what is best practice for PHP exception handling.
For example, a basic function which checks validation:
public function myValidationChecker(MyForm $form, $dateFrom, $dateTo) {
try {
$start = $dateFrom->format('Y-m-d'); // could cause exception
$finish = $dateTo->format('Y-m-d'); // could cause exception
// lots more logic here
$diff = $dateTo->diff($dateFrom); // could cause different exception
// ... more non-exception causing logic
} catch (\Exception $e) {
$this->log($e->getMessage());
// attach error to $form so that code stops executing
// & display generic error message to user
}
}
Should I potentially be catching a specific exception rather than just \Exception, e.g. UnexpectedValueException or InvalidArgumentException and have the catch surrounding just that specific, scary area of code?
Or equally, is it best to surround the entire function, in the case that there is an exception that I have not planned for? Either way, in this catch block, the code is prevented from being executed, as after this function is run, $form is checked for an error message.
You should only ever catch the exception you know how to handle. Don't just catch, because you want it, catch them only when you need to. Most of the time the exception should bubble up and be caught by the general logger, preferably the one built-in to PHP.
If you know what to do with the exception, then you know what you need to catch. Catch the exception you need to handle and let the others bubble up.

Would displaying PHP Exception Message be a security risk?

I want to set a custom message to be displayed to the user when I throw an error in Laravel 5.1. For example, in a controller I might have:
if(!has_access()){
abort('401', 'please contact support to gain access to this item.');
}
Then my custom error page I would display the error with:
$exception->getMessage();
However, what if there was a SQL error or other event? Wouldn't that also set the Exception Message which I would be unknowingly outputting on my error page?
The PHP docs for getMessage() don't go into much detail about this.
How can I set a specific exception message without introducing any security risk?
However, what if there was a SQL error or other event? Wouldn't that also set the Exception Message which I would be unknowingly outputting on my error page?
Potentially, yes. PHP makes no guarantees that the contents of exception messages will be "safe" to display to users, and it's quite likely that some classes will throw exceptions which include sensitive information in the message.
If you want to use exceptions to display errors to users, use a specific subclass of Exception for those exceptions, and only print the message if the exception was an instance of that subclass, e.g.
class UserVisibleException extends Exception {
// You don't need any code in here, but you could add a custom constructor
// if you wanted to.
}
// Then, in your abort() function...
throw new UserVisibleException($message);
// Then, in your exception handler...
if ($exc instanceof UserVisibleException) {
print $exc->getMessage();
} else {
print "An internal error occurred.";
}
If you access your app.php file:
'debug' => env('APP_DEBUG', false),
In your production env, set this to false. This would make sure that no debug errors would be displayed in the production environment.
Once this is set, you can respond to normal exceptions through your controller. Anything else, laravel wouldn't display the error page.
Yes,
$e->getMessage() can potentially reveal more information about your code IF you use it in a similar way:
try {
$executeSomethingHereForWhichYouExpectAnException();
// Basic \Exception that reports everything
} catch (\Exception $e) {
$error = $e->getMessage();
}
even with 'debug' => false in app.php. For example if you have an error with your code $error would display it - basically ANY type of error (PHP,MYSQL,ETC);
However, there is a fix - to catch your CustomException messages and prevent typical error displaying if you use it in like so:
try {
$executeSomethingHereForWhichYouExpectAnException();
// Our custom exception that throws only the messages we want
} catch (\CustomException $e) {
// Would contain only 'my_custom_message_here'
$error = $e->getMessage();
}
What is the difference you may ask - the difference is that instead of \Exception which is the basic error reporting, we use \CustomException class, which you throw from $executeSomethingHereForWhichYouExpectAnException() function:
executeSomethingHereForWhichYouExpectAnException(){
if (something) {
throw new CustomException("my_custom_message_here", 1);
}
}
If you have more exceptions you can include them like so (as of PHP7.1):
try {
something();
} catch(\CustomException | \SecondCustomException $e) {
// custom exceptions
} catch(\Exception $e) {
// basic exception containing everything
}

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.

Detect if an exception has been thrown manually without using a custom exception class

I got a try-catch block in my php application like this:
try {
if ($userForgotToEnterField) {
throw new Exception('You need to fill in your name!');
}
...
doDifferentThingsThatCanThrowExceptions();
...
} catch (ExpectedException $e) {
$template->setError('A database error occured.');
} catch (Exception $e) {
$template->setError($e->getMessage());
}
I would like to only output $e->getMessage() for the exceptions I have manually thrown with a custom error text and not the ones that have been thrown by the other code as these might contain sensitive information or very technical info that the user should not see.
Is it possible to differentiate from a manually thrown exception and a random exception thrown by some method without using a custom exception class?
I agree that it might be best to just write your own exceptions. If for whatever reason you don't want to, you could set a custom error message and a custom error code (the second parameter for the Exception constructor.) Check each thrown Exception if the error code is yours, and display only those:
public Exception::__construct() ([ string $message = "" [,int $code = 0[, Exception $previous = NULL ]]] )
and then use getCode
I've thought about this a bit and I'd say that what you are doing DOES call for a custom exception class. If you want to get around it (which in the end is going to be more confusing), you would basically create a global (or same-scope) variable that all exceptions can modify, and in your throw block flag it.
$threwCustomException = false;
try {
if ($userForgotToEnterField) {
throw new Exception('You need to fill in your name!');
$threwCustomException = true;
}
...
doDifferentThingsThatCanThrowExceptions();
...
} catch (ExpectedException $e) {
$template->setError('A database error occured.');
} catch (Exception $e) {
if($threwCustomException){
//Whatever custom exception handling you wanted here....
}
$template->setError($e->getMessage());
}
That's the best I can think of. However, this is a bad idea, and it's the whole reason you are allowed to create your own exception classes. I know you're not looking for this answer, but since you look like you're trying not to create a TON of extra code, I would just extend Exception to "CustomException" or some other name specific to your project, and throw that for all cases, and handle it that way.
Hope that helps.

Exception handling in Symfony2

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.

Categories