Handling a non-fatal exception in Laravel - php

In my laravel app, say I have a bit of code as follows, as an example
function convert_amount($amount, $currency, $date)
{
if (strlen($currency) <> 3)
{
// Exception thrown
} else {
// convert $amount from $currency on $date
}
return $amount;
}
Here, I am simply converting a number from a currency to base. I perform a simple check to see if the currency string passed is 3 character to ensure it's a ISO currency code (EUR, GBP, USD etc). If not I want to throw an exception but not result in the app falling to an error page like is often the case with Laravel's error handler.
Instead, I'd like to continue processing the page but log the exception and perhaps display an error in a flash message.
Is there a listener I can define for Laravel that achieves this? Do I need to define a new exception type NonFatelException perhaps that does the logic.
Edit
Essentially, I guess I could register a new exception handler like so:
class NonFatalException extends Exception {}
App::error(function(NonFatalException $e)
{
// Log the exception
Log::error($e);
// Push it into a debug warning in the session that can be displayed in the view
Session::push('debug_warnings', $e->getMessage());
});
Then somewhere in my app:
throw new NonFatalException('Currency is the wrong format. The amount was not converted');
The trouble with this is that the default exception handlers will then be called resulting in an error page rather than the page that was going to be reached.
I could return a value in my handler to avoid the default but I believe that would result in that return value alone being shown and the rest of my scripts would not be run.

You are on a right path. Why not use try...catch tho?
Your helper method will be:
function convert_amount($amount, $currency, $date)
{
if (strlen($currency) <> 3)
{
throw new NonFatalException('Currency is the wrong format. The amount was not converted');
} else {
// convert $amount from $currency on $date
}
return $amount;
}
And whenever you'll use it, put it in a try...catch:
try {
convert_amount($amount, $currency, $date);
} catch (NonFatalException $e) {
// Log the exception
Log::error($e);
// Push it into a debug warning in the session that can be displayed in the view
Session::push('debug_warnings', $e->getMessage());
}
This way, your app will never stopp and you have the error message in Session.

Related

(Cake)PHP Error Thrown in Nested Function Doesn't Get Back to Initial Function

This isn't necessarily a CakePHP problem but I'm using CakePHP 2.8 and PHP 5.6.
I have a function named save_order which calls another function named changePathItemOrder using a try/catch. That function calls another function named _reorderItemsOnPath, which in turn calls another function named _moveItemsForwards. It's a few levels deep, so here's a little graphic to keep us all on track:
Cascade of Functions
The try/catch in sort_order is:
$data['status'] = 'success';
try {
$this->PathRepository->deletePathItemFromPath($milestoneId, $pathId, $accountId);
} catch(Exception $e) {
debug('Caught error');
$data['status'] = 'error';
$data['message'] = $e->getMessage();
}
$this->set(compact('data'));
$this->render('/Elements/json');
If an error occurs in _moveItemsForwards, I throw an error like:
throw new InternalErrorException('Invalid path item ID: ' . $pathItemId);
The problem is that the try/catch in sort_order doesn't catch the error thrown by _moveItemsForwards. The catch doesn't even execute because the debug doesn't show up in the resulting error message. I just get the following Error :-
500 Error! Something broke!
Return to the homepage
Invalid path item ID: 76
What's the best way to handle this error and get the error message back to the save_order function?

Laravel email notification upon all exceptions

I am developing a project and i would like to have a notification system especially for the "first days of live" - to receive an email everytime an exception is thrown .
I've read that article and implemented what it says. My problem is that the function report() in App\Exception\Handler.php doesn't trigger if an exception is thrown in one of my Controllers.
My problem :
I have an AdminController and a function which inserts some data in Database. The queries are inside a try/catch :
try {
// commands
} catch (\Exception $e){
Log::channel('admin_submissions')->info("Could not save submission. " . $e->getMessage());
return Redirect::back()->withErrors(['Could not save submission. Try again!']);
}
For testing purposes i've inserted inside the try{} an error to be thrown like :
try {
$error = \Illuminate\Validation\ValidationException::withMessages([
'field_name_1' => ['Validation Message #1'],
'field_name_2' => ['Validation Message #2'],
]);
throw $error;
// commands
But the dd("trigger") function (look below) is not triggered.
How could i make it so on every exception (everywhere) an email will be sent?
App\Exceptions\Handler.php
I have modified the report function just to check if the exception actually goes through that function :
public function report(Exception $exception)
{
dd("trigger");
if ($this->shouldReport($exception)) {
app('sneaker')->captureException($exception);
}
parent::report($exception);
}
You must firstly know which kind of exception your application thrown, may be the Exception that is thrown are in the $dontReport if so, you must firstly remove all exception from that table on you will get report for that Exception

Using additional data in php exceptions

I have php code that execute python cgi and I want to pass python trace (returned from cgi) as extra data to php exception how can I do this and how can I get that value from catch(Exception e) { (It should check if that extra value exesit or not).
I have code like this:
$response = json_decode(curl_exec($ch));
if (isset($response->error)) {
// how to send $response->trace with exception.
throw new Exception($response->error);
}
return $response->result;
and I use json-rpc library that should return that data to the user:
} catch (Exception $e) {
//catch all exeption from user code
$msg = $e->getMessage();
echo response(null, $id, array("code"=>200, "message"=>$msg));
}
Do I need to write new type of exception or can I do this with normal Exception? I would like to send everything that was thrown in "data" =>
You need to extend Exception class:
<?php
class ResponseException extends Exception
{
private $_data = '';
public function __construct($message, $data)
{
$this->_data = $data;
parent::__construct($message);
}
public function getData()
{
return $this->_data;
}
}
When throwing:
<?php
...
throw new ResponseException($response->error, $someData);
...
And when catching:
catch(ResponseException $e) {
...
$data = $e->getData();
...
}
Dynamic Property (not recommended)
Please note that this will cause deprecation error in PHP 8.2 and will stop working in PHP 9 according to one of the PHP RFC https://wiki.php.net/rfc/deprecate_dynamic_properties
As the OP asking about doing this task without extending Exception class, you can totally skip ResponseException class declaration. I really not recommend do it this way, unless you've got really strong reason (see this topic for more details: https://softwareengineering.stackexchange.com/questions/186439/is-declaring-fields-on-classes-actually-harmful-in-php)
In throwing section:
...
$e = new Exception('Exception message');
$e->data = $customData; // we're creating object property on the fly
throw $e;
...
and when catching:
catch(Exception $e) {
$data = $e->data; // Access data property
}
September 2018 edit:
As some of readers found this answer useful, I have added a link to another Stack Overflow question which explains the downsides of using dynamically declared properties.
Currently, your code converts the response text directly into an object without any intermediate step. Instead, you could always just keep the serialized (via JSON) text it and append it to the end of the Exception message.
$responseText = curl_exec($ch);
$response = json_decode($responseText);
if (isset($response->error)) {
throw new Exception('Error when fetching resource. Response:'.$responseText);
}
return $response->result;
Then you could just recover everything after "Response:" in your error log and optionally de-serialize it or just read it.
As an aside, I would also not count on the server sending JSON, you should verify that the response text was actually parseable as JSON and return a separate error for that if it isn't.

User friendly php try catch

Since a short period of time I'm working with Try Catch in PHP. Now, every time a new error is thrown you get a fatal error on the screen, this isn't really user friendly so I was wondering if there's a way to give the user a nice message like an echo instead of a fatal error.
This is the code I have now:
public static function forceNumber($int){
if(is_numeric($int)){
return $int;
} else {
throw new TypeEnforcerException($int.' must be a number');
}
}
public function setStatus($status) {
try {
$this->status = TypeEnforcer::forceInt($status);
} catch (TypeEnforcerException $e) {
throw new Exception($e->getMessage());
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
This is best solved with a frontend controller that is able to catch all uncatched exceptions:
<?php
require('bootstrap.php');
try {
$controllerService->execute($request);
} catch (Exception $e) {
$controllerService->handleControllerException($e);
}
You can then write code to return the internal server error because an exception signals an exceptional case so it normally is an 500 internal server error. The user must not be interested what went wrong other than it just didn't work out and your program crashed.
If you throw exceptions to give validation notices you need to catch those in a different layer (and you're probably doing it wrong if you use exceptions for that).
Edit: For low-level functions, because PHP is loosely typed, if a function expects and int, cast to intDocs:
public static function forceNumber($int){
$int = (int) $int;
return $int;
}
this will actually force the integer. In case the cast is not possible to do (e.g. $int it totally incompatible) PHP will throw the exception for you.
The example is a bit akward because by the method's name you use it to validate some number and provide an error if not (here wrongly with an exception). Instead you should do some validation. If you expect wrong input, it's not an exceptional case when wrong input is provided, so I would not use exceptions for that.

Handling exceptions for multiple calls to functions of a class

I can't get my head around WHEN to throw and catch exceptions for when I call functions from classes.
Please imagine that my QuizMaker class looks like this:
// Define exceptions for use in class
private class CreateQuizException extends Exception {}
private class InsertQuizException extends Exception {}
class QuizMaker()
{
// Define the items in my quiz object
$quiz_id = null;
$quiz_name = null;
// Function to create a quiz record in the database
function CreateQuizInDatabase()
{
try
{
$create_quiz = // Code to insert quiz
if (!$create_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting record");
}
else
{
// Return true, the quiz was created successfully
return true;
}
}
catch (CreateQuizException $create_quiz_exception)
{
// There was an error creating the quiz in the database
return false;
}
}
function InsertQuestions()
{
try
{
$insert_quiz = // Code to insert quiz
if (!$insert_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting quiz in to database");
}
else
{
// Success inserting quiz, return true
return true;
}
}
catch (InsertQuizException $insert_exception)
{
// Error inserting question
return false;
}
}
}
... and using this code, I use the class to create a new quiz in the database
class QuizMakerException extends Exception {}
try
{
// Create a blank new quiz maker object
$quiz_object = new QuizMaker();
// Set the quiz non-question variables
$quiz_object->quiz_name = $_POST['quiz_name'];
$quiz_object->quiz_intro = $_POST['quiz_intro'];
//... and so on ...
// Create the quiz record in the database if it is not already set
$quiz_object->CreateQuizRecord();
// Insert the quiz in to the database
$quiz_object->InsertQuestions();
}
catch (QuizMakerException $quiz_maker_error)
{
// I want to handle any errors from these functions here
}
For this piece of code, I want to call a QuizMakerException if any of the functions don't perform what I want them to (at the moment they return TRUE or FALSE).
What is the correct way to go about catching when any of the functions in this code does not perform what I want them to? At the moment they simply return TRUE or FALSE.
Do I really have to put lots of if/else statements between calling each function, I thought that was the whole point in exceptions, they simply halt the execution of further statements within the try/catch?
Do I throw a QuizMakerException from within the catch of my functions?
What is the right thing to do?
Help!
Well typically in the function which throws the exception, say your InsertQuestions method, you don't want to catch any exceptions, you want to throw them or let ones occurring to "bubble up". Then your "controller" code can make the determination of how to handle the exception.
If your goal here is to halt if CreateQuizRecord fails I would wrap CreateQuizRecord and InsertQuestions each in their own try block.
One advantage of exceptions is they can tell you more than a simple bool pass/fail. Either extending your base exception into things like "Invalid_Parameter" and testing for specific exceptions or -less ideally- inferring from properties of the exception. You can nest your catch blocks to handle exceptions individually.
Do I throw a QuizMakerException from within the catch of my functions?
Yes. Typically your code under // Code to insert quiz would itself return an exception. Say if the Model failed to insert it might be raising a database exception. In which case you can let that database exception bubble up, or do what you sort of doing now and catch it simply to in turn throw another exception (kinda dumbs down your exceptions in a way, doing that though).
Do I really have to put lots of if/else statements between calling each function, I thought that was the whole point in exceptions, they simply halt the execution of further statements within the try/catch?
I look at it like this, each call which throws an exception and is followed by a subsequent call which depends on this one not throwing any exceptions, should be wrapped in a try block. Assuming you want to handle it gracefully, if you simply want it to error out and halt just don't handle the exception. You'll get an error and stack trace. Sometimes that is desirable.
You might want to change the structure a bit. Your class QuizMaker can become:
<?php
// Define exceptions for use in class
public class CreateQuizException extends Exception {}
public class InsertQuizException extends Exception {}
class QuizMaker()
{
// Define the items in my quiz object
$quiz_id = null;
$quiz_name = null;
// Function to create a quiz record in the database
function CreateQuizInDatabase()
{
try
{
$create_quiz = // Code to insert quiz
if (!$create_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting record");
}
else
{
// Return true, the quiz was created successfully
return true;
}
}
catch (Exception $create_quiz_exception)
{
// There was an error creating the quiz in the database
throw new CreateQuizException(
"Failed inserting record " .
$create_quiz_exception->getMessage()
);
}
}
function InsertQuestions()
{
try
{
$insert_quiz = // Code to insert quiz
if (!$insert_quiz)
{
// There was an error creating record in the database
throw new InsertQuizException("Failed inserting quiz in to database");
}
else
{
// Success inserting quiz, return true
return true;
}
}
catch (Exception $insert_exception)
{
// Error inserting question
throw new InsertQuizException(
"Failed inserting quiz in to database " .
$create_quiz_exception->getMessage()
);
}
}
}
Effectively, if you cannot insert the record correctly, you throw an Insert exception. If anything goes wrong with that block of code, you catch it and throw again an Insert exception. Same goes with the Create function (or any other ones you might have).
In the main block of code you can have:
try
{
// Create a blank new quiz maker object
$quiz_object = new QuizMaker();
// Set the quiz non-question variables
$quiz_object->quiz_name = $_POST['quiz_name'];
$quiz_object->quiz_intro = $_POST['quiz_intro'];
//... and so on ...
// Create the quiz record in the database if it is not already set
$quiz_object->CreateQuizRecord();
// Insert the quiz in to the database
$quiz_object->InsertQuestions();
}
catch (InsertQuizException $insert_exception)
{
// Insert error
}
catch (CreateQuizException $create_quiz_exception)
{
// Create error
}
catch (Exception $quiz_maker_error)
{
// Any other error
}
If you don't want to have the multiple catch block there, just keep the catch(Exception) one and then check in it the type of each exception thrown to specify the actions taken from there.
HTH
The best thing to do is to not have to thhrow exceptions in the first place.
Exceptions are thrown when the program crashes and they are not made to handle wrong output.
If your function returns anything (even its the wrong thing) it doesn't need an exception.
To answer your question, if it's necessaryto use a lot of ifs/elses then you must use them.

Categories