I have some IF/THEN logic that is getting difficult to read, and am thinking of using using exceptions instead.
The code will test the user input, and throw exceptions as appropriate. My catch statement will deal with the anticipated exceptions, but if the exception is not anticipated (like I messed up a PDO statement), I wish to throw the exception and let PHP's error system deal with it. All of the anticipated exceptions are pretty much dealt with the same way, and I don't wish to use multiple try/catches around each test.
Within the catch statement, how can I perform different actions based on the exception?
try {
$user_input=$_POST['user_input'];
// rest of code here...
if (test1($user_input)) {
throw new Exception("Anticipated exception 1.");
}
// rest of code here...
//Some PDO which might generate a non-anticipated exception
if (test2($user_input)) {
throw new Exception("Anticipated exception 2.");
}
// rest of code here...
}
catch (Exception $e) {
if(anticipatedException($e)) {
//Deal with it
}
else {
throw $e;
}
}
By using different exceptions. i.e.
class ExceptionOne extends Exception {}
class ExceptionTwo extends Exception {}
try {
$user_input=$_POST['user_input'];
// rest of code here...
if (test1($user_input)) {
throw new ExceptionOne("Anticipated exception 1.");
}
// rest of code here...
//Some PDO which might generate a non-anticipated exception
if (test2($user_input)) {
throw new ExceptionTwo("Anticipated exception 2.");
}
// rest of code here...
}
catch (ExceptionOne $e) {
/*...*/
}
catch (ExceptionTwo $e) {
/*...*/
}
Also check Predefined exceptions which might help to group yours: http://php.net/manual/en/spl.exceptions.php
Related
I perfectly understand the nuances of the (try/throw/catch) block.
What I don't understand is:
If we gonna use an IF (or any control structure) inside our try block anyway in order to test if a condition is met, only then, 'throw' an exception if the results of that test is false, then... in my opinion: throw/generate an exception is useles; because if a condition is not met, we can simply print an error message, call a function, instantiate a class, redirect to another location, etc.
Another story would be, if for instance, a variable was not initialized, we enclose that variable inside a try{} block, echo the variable, and from that point onward, everything will be handle by the catch() block because the try block raises an error; and since the try/catch blocks talk each other, the catch block will catch every error that was originated from his corresponding try block. However, you can set a custom error message inside yout try block (optional).
What I've read so far:
every results from searching: if vs. try
I do see the difference.
But I can not understand why some people choose try/throw/catch over if...else...switch...while... etc.
As far I can see, try/throw/catch can be used for debugging, though.
One benefit of exceptions over if/then is that you can wrap try/catch around a large block of code. It will be triggered if an error happens anywhere in the block.
try {
$db = db_open();
$statement = $db->prepare($sql);
$result = $statement->execute($params);
} catch (Exception $e) {
die($e->getMessage());
}
With if/then, you would have to perform a test at each step.
$db = db_open();
if (!$db) {
die(db_connect_error());
}
$statement = $db->prepare($sql);
if (!$statement) {
die(db_error($db));
}
$result = $statement->execute($params);
if (!$result) {
die(db_error($db));
}
As you said, it's a lot of overhead to throw an exception inside a try/catch block and catch it immediately.
try {
if (...) {
// good, do no throw
} else {
throw new Exception();
}
} catch ($e) {
// handle exception
}
This should be replaced by:
if (...) {
// good
} else {
// handle error, no exception
}
Exceptions are useful because they bubble up. So imagine if you have this code instead:
function bla() {
try {
tryToDoSomething();
} catch ($e) {
// handle error
}
}
function tryToDoSomething() {
if ($somethingNotAvailable) {
throw new Exception();
}
doSomething();
}
In this case, the function that defines the try/catch is NOT the one throwing the exception. tryToDoSomething() does not know how to handle the errors so it will let parent methods to take care of it. The exception can bubble up the call stack until someone catches it and handles the error. That's how exceptions can actually be useful :)
I have a problem where I want to catch all exception except descendants of my custom exception.
Maybe bad design, but here it is (Simplified and names changed, but the code is quite accurate):
function doStuff()
{
try {
// code
if (something) {
// manually throw an exception
throw StuffError("Something is bad.");
}
// a third-party code, can throw exceptions
LibraryClass::arcaneMagic();
} catch (Exception $e) {
throw new StuffError("Error occured while doing stuff: "
. $e->getMessage());
}
}
/** My custom exception */
class StuffError extends Exception
{
function __construct($msg) {
parent::__construct('StuffError: ' . $msg);
}
}
However, the issue here is that I don't want the try-catch to intercept the manually throws StuffError. Or, seamlessly rethrow it or something.
As it is now, I'd get:
StuffError: Error occured while doing stuff: StuffError: Something is bad.
I want just:
StuffError: Something is bad.
How would I do it?
You can have multiple catch clauses, and the first one that matches will be the one that runs. So you could have something like this:
try {
do_some_stuff();
}
catch (StuffError $e) {
throw $e;
}
catch (Exception $e) {
throw new StuffError(Error occurred while doing stuff: " . $e->getMessage());
}
But you might want to rethink wrapping stuff like this. It obscures the real cause of the error. For one thing, you lose the stack trace. But it also complicates error handling, since now someone can't differentiate exception types the way you're trying to do, short of trying to parse the exception message (which is rather an anti-pattern in itself).
I might be misinterpreting you, but I think this is what you're looking for:
...
} catch (Exception $e) {
if (get_class($e) == 'StuffError' || is_subclass_of($e, 'StuffError')) {
throw $e;
} else {
throw new StuffError("Error occured while doing stuff: "
. $e->getMessage());
}
}
...
Replace your catch statement with the code above. It checks to see if the exception is a StuffError or a child class of StuffError. I'm still very confused at why you would need to throw a StuffError exception after you catch, but maybe that's just some weirdness coming from translating/cleaning your code.
I have a situation where I would like to re-throw an exception inside a catch block and have it caught by a more generic catch.
Example:
try {
somethingThatMightThrowExceptions();
}
catch (ClientErrorResponseException $e) {
if ($e->getResponse()->getStatusCode() == 404) {
return $this->notFound();
}
throw $e; // even tried throw new \Exception();
}
catch (\Exception $e) {
return $this->serverError('Server error');
}
So in my first catch block I check for a specific condition, and if that fails I would like to re-throw the exception and have it caught by the generic catch (\Exception) block. But in this case it just bubbles back up to the caller.
The problem is that there are actually a couple more lines of code in the final catch, which I don't want to repeat. I could of course extract them to a method, but that feels like overkill.
So basically I want to do it all in-line without having to add extra layers. Any suggestions?
It's due to catch only working once as you are throwing an exception not withing a try block.
If you are determined, that you want to do it this way, then you need to nest try..catch statements as #deceze suggested in comments..
You should describe what you are trying to accomplish instead, as there might be a better way to do it.
You have two options:
a) Extract common logic to a method. (This can be overkill sometimes as you've mentioned)
b) Catch the most generic exception and check for its type. ie:
try{
somethingThatMightThrowExceptions();
}
catch (\Exception $e) { // The most generic exception
if($e instanceof ClientErrorResponseException) {
// Some actions specific to ClientErrorResponseException
}
// Shared actions
}
You have two options:
a) Catch the most generic exception and check for its type, as #deceze suggested
try{
somethingThatMightThrowExceptions();
}
catch (\Exception $e) { // The most generic exception
if($e instanceof ClientErrorResponseException) {
// Some actions specific to ClientErrorResponseException
}
// Shared actions
}
b) Put anoter try/catch block at upper level
try {
try {
somethingThatMightThrowExceptions();
}
catch (ClientErrorResponseException $e) {
// Some actions specific to ClientErrorResponseException
throw $e ;
}
}
catch (\Exception $e) { // The most generic exception
// Shared actions
}
This question is about the best way to execute code outside of try block only if no exception is thrown.
try {
//experiment
//can't put code after experiment because I don't want a possible exception from this code to be caught by the following catch. It needs to bubble.
} catch(Exception $explosion) {
//contain the blast
} finally {
//cleanup
//this is not the answer since it executes even if an exception occured
//finally will be available in php 5.5
} else {
//code to be executed only if no exception was thrown
//but no try ... else block exists in php
}
This is method suggested by #webbiedave in response to the question php try .. else. I find it unsatisfactory because of the use of the extra $caught variable.
$caught = false;
try {
// something
} catch (Exception $e) {
$caught = true;
}
if (!$caught) {
}
So what is a better (or the best) way to accomplish this without the need for an extra variable?
One possibility is to put the try block in a method, and return false if an exception is cought.
function myFunction() {
try {
// Code that throws an exception
} catch(Exception $e) {
return false;
}
return true;
}
Have your catch block exit the function or (re)throw/throw an exception. You can filter your exceptions as well. So if your other code also throws an exception you can catch that and (re)throw it. Remember that:
Execution continues if no exception is caught.
If an exception happens and is caught and not (re)throw or a new one throw.
You don't exit your function from the catch block.
It's always a good idea to (re)throw any exception that you don't handle.
We should always be explicit in our exception handling. Meaning if you catch exceptions check the error that we can handle anything else should be (re)throw(n)
The way I would handle your situation would be to (re)throw the exception from the second statement.
try {
$this->throwExceptionA();
$this->throwExceptionB();
} catch (Exception $e) {
if($e->getMessage() == "ExceptionA Message") {
//Do handle code
} elseif($e->getMessage() == "ExceptionB Message") {
//Do other clean-up
throw $e;
} else {
//We should always do this or we will create bugs that elude us because
//we are catching exception that we are not handling
throw $e;
}
}
In the controllers class files, most of the method functions include try/catch block something like this:
try
{
$stmt = $this->prepare($sql);
$stmt->execute($params);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
//foreach() or so on...
}
catch (Exception $e)
{
//bunch of code...
//save error into database, etc.
//error into json and pass to view file
}
There are a lot of code in the catch block, is there a way to reduce it. Is possible to add "throw exception" in the catch block?
Yes, it is. Try it by yourself. You can always throw a new Exception in a catch block or rethrow the same exception.
try
{
// ...
}
catch (Exception $e)
{
// do whatever you want
throw new Your_Exception($e->getMessage());
// or
throw $e;
}
I don't know what "bunch of code" is. I'm not sure I believe you. If you have that much going on in a catch block you're doing something wrong.
I'd put this kind of code into an aspect if you have AOP available to you.
"Error into database" might throw its own exception. What happens to that?
The only step that I see here that's necessary is routing to the error view.
What does rethrowing the exception do? It's just passing the buck somewhere else. If all these steps don't need to be done, and all you're doing to rethrowing, then don't catch it at all. Let the exception bubble up to where it's truly handled.
You shouldn't be catching Exception. That's much too general. Catch each specific type of Exception with multiple catch statements on your try block:
try {
} catch(PDOException $err) {
} catch(DomainException $err) {
}