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.
Related
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
In a Laravel 5.2 app, I'm trying to figure out the most elegant way to handle exceptions when those exceptions are thrown by API calls of external services. These shouldn't stop the program from continuing, because there are other parts of the app that still can be run afterwards that can be done even without the problematic API call.
E.g. currently I have
try {
$statistics->results = $api->call($parameter);
$statistics->status = Statistic::SUCCESS;
} catch (ExternalApiCallException $e) {
$statistics->results = null;
$statistics->status = Statistic::API_CALL_ERROR;
}
I was thinking of using Laravel's app/Exceptions/Handler.php and using
if ($e instanceof ExternalApiCallException $e) {
Log::warning("API Call didn't work");
}
but then I wouldn't be able to set the status of the statistics there, because Handler.php wouldn't have access to it. Is there a better way or are try-catch-blocks the way to go here?
try-catch-finally is definitely a good approach here. In your case API exceptions are localized and should not appear globally (I presume), so I would opt for keeping logic in one place and not putting exception handling as a condition in Handler.php file. In my opinion Handler.php should be used as a last resort option, to style and report otherwise uncaught and unexpected exceptions. For all other cases where exceptions are not critical or even expected, local try {} blocks are easier to maintain, as important parts of the code are not hidden from developer in another file.
Do not forget that you can also use finally {} block, which will be executed after both try{} and catch{} blocks, no matter if the exception was triggered or not.
try
{
// run this first
}
catch (ExternalApiCallException $e)
{
// in case something went wrong
}
finally
{
// this runs after everything else
}
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
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.
Few days ago I deal with errors like this...
exit( 'Error!' );
or exit( 'Error!' );
Doh, right? =] Now I'm trying to learn Exceptions. This is how far I got...
http://pastie.org/1555970
Is that correct use of them? It would be cool that I can have more info about 'em. Like file where exception is thrown and line of that file. I know that there are build-in methods (in Exception class), but I want to somehow extend it so I don't need to write...
throw new My_Exception( '...' );
catch( My_Exception $e ) {
echo $e->my_method();
}
...but use old syntax.
throw new Exception( '...' );
catch( Exception $e ) {
echo $e->getMessage();
}
...or maybe you have any greater thought of Exceptions? How to deal with them? Help me! =]
In general you only need to echo/log exceptions, that cannot be otherwise handled. This pretty much means, that if you put your entire application within try block, there's only one place where you need to put echoing/logging logic (i.e. the catch block associated with the outermost try block).
If on the other hand the exception can be handled without stopping the application (in your example this could be providing a default numeric value, instead of incorrect value), then there's usually no need to echo/log it.
If you do want to log such exceptions (for debugging for example), then your application should contain a logging framework, so that it's enough to do in your catch blocks something like
} catch (Exception $e) {
ExceptionLogger::log($e); //static method == ugly, but it's for simplicity in this example
// do whatever needs to be done
}
log() method above would check if the logging is enabled, and if it is savenecessary data to a file.
Exceptions should be typed based upon the error that you find. The Spl Exceptions are a good start, but you really should be creating your own exceptions as well. Some common ones that I use:
FileNotFoundException extends RuntimeException <- self explanatory
HTTPException extends RuntimeException <- Used for http classes when a non-200 result is encountered
DatabaseQueryException extends LogicException <- Used for database query errors
Now, by typing them specifically, it lets you handle the errors in your code. So let's say that you want to fetch a HTTP resource. If that fails with anything but a 404, you want to try a backup URL. You could do that with:
try {
return getHttp($url1):
} catch (HttpException $e) {
if ($e->getCode() != 404) {
try {
return getHttp($url2);
} catch (HttpException $e2) {
//It's ok to ignore this, since why know it's an HTTP error and not something worse
return false;
}
} else {
return false;
}
}
As far as your example code that you posted, I would change a few things:
Change the thrown exception to InvalidArgumentException since it has more semantic meaning (I almost never throw a raw exception).
You should try to avoid catch(Exception $e) at all costs. You have no idea what exception was thrown, so how can you possibly handle it?
Only catch exceptions that you are reasonably sure you know how to handle (and outputting an error/logging is not handling, it's removing the usefulness of the exception). You should never see something like catch($e) { logerror($e); } or catch($e) { print $e->getMessage(); } since netiher is actually handling the exception.
If you don't fix or workaround the cause of the exception in your catch block, you should re-throw it. Let the code above you in the stack try to handle it. This is especially true with libraries and classes that are reused all over the place.
Now, with user interfaces, it may be acceptable to catch the exception and show the user an error message. So your example where you print the exception's message might be ok, but you'd really need to think about the use-cases of it. Are you calling it from a model or controller? If so, it may be ok display an error message. Are you calling it from a library? If so, it's probably better to let the exception bubble up.
Also, don't use a global try{} catch() {} block. Instead, install an Exception Handler to handle it for you. It's cleaner, and more semantically correct (since any try{}catch{} implies that you know how to handle the exception that you caught, whereas the exception handler is precisely meant for exceptions that weren't handled because you didn't know how to handle them.
Exceptions are for exceptional circumstances. Do not use them for all error conditions. If a user submits a password that's too short, don't throw an exception, handle that in validation. But if your hash function is expecting sha256 to be available and it isn't, that's a time for an exception. Exceptions are useful for program errors (when a condition that is unexpected happens, such as invalid input to a function), state errors (when the application enters a state that is unknown or unstable, such as if the requested view does not exist) and runtime errors (when the application encounters an error that can only be detected at runtime, such as a file-not-found error).
There is an entire page of the PHP manual devoted to extending exceptions and that page also gives you a lot of information on the methods to identify file/line number, backtrace etc. where the exception was thrown. This is the type of information that is extremely useful for debugging.