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

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.

Related

How can I find out all the errors that can possibly be thrown by a block of PHP code?

I want to know what errors can be thrown that I am not catching, for dynamic code - not static code. For example, my code may run without throwing any Exceptions for 10 years and then throw UncaughtException
I want to specifically (non-generically) catch every type of Exception that can be thrown by the methods I am using. How can I know what Exceptions MAY be thrown by these methods?
I want to non-generically catch every type of error that can possibly be thrown for a section of PHP code.
Examples of exceptions that may be thrown:
PDOException
ExpiredException
Now I have this around everything:
try{
...
}catch(Exception $e){
...
}
I'd like to replace it with something like this:
try{
...
}catch(PDOException $e){
...
}catch(ExpiredException $e){
...
}catch(Exception $e){
...
}
I'd like to be confident that I am catching all different kinds of Exception that can be thrown by the methods in the section of code
And if I catch all Exceptions individually, will it be safe to remove this part?:
catch(Exception $e){
...
}
Or are there methods which will simply throw Exception?
My solution preference list (1 is the most-preferred solution):
1: A flag I can turn on that will cause php.exe to warn me about each and every possible type of Exception that is not specifically being caught
2: A way to individually check each method and see what errors can be thrown. Is the documentation the only way to check? or is there some IDE or PHP block that will tell me which Exceptions may be thrown by individual methods?
You can set callback function by using register_shutdown_function() which will call on every end of your php code execution. In this callback function you can check whether any error occurs or not using error_get_last().
For Example:
// Register shutdown function
register_shutdown_function("shutdownTracker");
// Define all error types you want to catch and handle
define('E_FATAL', E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR);
function shutdownTracker() {
$error = error_get_last(); // This will return empty if no error occurs while executing php code.
if(!empty($error) && ($error['type'] & E_FATAL)) {
// Write your code here to handle you error
}
}
Note: You should include this code on top of your code.
Well if you are talking exceptions, you already have the answer
try{
...
}catch(PDOException $e){
...
}catch(ExpiredException $e){
...
}catch(Exception $e){
echo get_class($e); // get's the class of unrecorded exceptions.
//catches any exceptions missed by the above
}
Now if you are talking about "errors" you can do is use a custom error handler
if(!function_exists('myErrorHandler')){
function myErrorHandler($severity, $message, $file = 'UNKNOWN', $line = 'UNKNOWN')
{
throw new ErrorException(
$message,
1,
$severity,
$file,
$line
);
}
set_error_handler('myErrorHandler');
}
What it does is convert all PHP errors to exceptions or rather to ErrorExceptoins.
Now you can go a step further and use these other two functions.
register_shutdown_function
AND
set_exception_handler
I'm actually working on porting what I use to it's own stand alone github project. Right after I finish my eJinn project, which you may be interested in. eJinn is designed to build exception classes based off a config file, so you can have one error per exception file and unique error codes in a project.
You can catch all exceptions without even call try.
function hello($e)
{
if ($e instanceof PDOException){
echo "something".
}else echo $e->getMessage();
}
set_exception_handler('hello');
this should catch all exceptions.
UPDATE 2
I've edited the code so you know what exceptions will be thrown using get_class();
class mycustomException extends Exception{} // making new exception
function hello($e)
{
if ($e instanceof PDOException){ // exception already known
echo "something";
}else{
echo get_class($e); // get exception name.
}
}
set_exception_handler('hello');
throw new mycustomException(); // throw exception that we made.

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.

What are the best practices for catching and re-throwing exceptions?

Should caught exceptions be re-thrown directly, or should they be wrapped around a new exception?
That is, should I do this:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
} catch (Exception $e) {
throw $e;
}
or this:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
} catch (Exception $e) {
throw new Exception("Exception Message", 1, $e);
}
If your answer is to throw directly please suggest the use of exception chaining, I am not able to understand a real world scenario where we use exception chaining.
You should not be catching the exception unless you intend to do something meaningful.
"Something meaningful" might be one of these:
Handling the exception
The most obvious meaningful action is to handle the exception, e.g. by displaying an error message and aborting the operation:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}
Logging or partial cleanup
Sometimes you do not know how to properly handle an exception inside a specific context; perhaps you lack information about the "big picture", but you do want to log the failure as close to the point where it happened as possible. In this case, you may want to catch, log, and re-throw:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}
A related scenario is where you are in the right place to perform some cleanup for the failed operation, but not to decide how the failure should be handled at the top level. In earlier PHP versions this would be implemented as
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
catch (Exception $e) {
$connect->disconnect(); // we don't want to keep the connection open anymore
throw $e; // but we also don't know how to respond to the failure
}
PHP 5.5 has introduced the finally keyword, so for cleanup scenarios there is now another way to approach this. If the cleanup code needs to run no matter what happened (i.e. both on error and on success) it's now possible to do this while transparently allowing any thrown exceptions to propagate:
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}
Error abstraction (with exception chaining)
A third case is where you want to logically group many possible failures under a bigger umbrella. An example for logical grouping:
class ComponentInitException extends Exception {
// public constructors etc as in Exception
}
class Component {
public function __construct() {
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
throw new ComponentInitException($e->getMessage(), $e->getCode(), $e);
}
}
}
In this case, you do not want the users of Component to know that it is implemented using a database connection (maybe you want to keep your options open and use file-based storage in the future). So your specification for Component would say that "in the case of an initialization failure, ComponentInitException will be thrown". This allows consumers of Component to catch exceptions of the expected type while also allowing debugging code to access all the (implementation-dependent) details.
Providing richer context (with exception chaining)
Finally, there are cases where you may want to provide more context for the exception. In this case it makes sense to wrap the exception in another one which holds more information about what you were trying to do when the error occurred. For example:
class FileOperation {
public static function copyFiles() {
try {
$copier = new FileCopier(); // the constructor may throw
// this may throw if the files do no not exist
$copier->ensureSourceFilesExist();
// this may throw if the directory cannot be created
$copier->createTargetDirectory();
// this may throw if copying a file fails
$copier->performCopy();
}
catch (Exception $e) {
throw new Exception("Could not perform copy operation.", 0, $e);
}
}
}
This case is similar to the above (and the example probably not the best one could come up with), but it illustrates the point of providing more context: if an exception is thrown, it tells us that the file copy failed. But why did it fail? This information is provided in the wrapped exceptions (of which there could be more than one level if the example were much more complicated).
The value of doing this is illustrated if you think about a scenario where e.g. creating a UserProfile object causes files to be copied because the user profile is stored in files and it supports transaction semantics: you can "undo" changes because they are only performed on a copy of the profile until you commit.
In this case, if you did
try {
$profile = UserProfile::getInstance();
}
and as a result caught a "Target directory could not be created" exception error, you would have a right to be confused. Wrapping this "core" exception in layers of other exceptions that provide context will make the error much easier to deal with ("Creating profile copy failed" -> "File copy operation failed" -> "Target directory could not be created").
Well, it's all about maintaining the abstraction. So I'd suggest using exception chaining to throw directly. As far as why, let me explain the concept of leaky abstractions
Let's say you're building a model. The model is supposed to abstract away all of the data persistence and validation from the rest of the application. So now what happens when you get a database error? If you rethrow the DatabaseQueryException, you're leaking the abstraction. To understand why, think about the abstraction for a second. You don't care how the model stores the data, just that it does. Likewise you don't care exactly what went wrong in the underlying systems of the model, just that you know that something went wrong, and approximately what went wrong.
So by rethrowing the DatabaseQueryException, you're leaking the abstraction and requiring the calling code to understand the semantics of what's going on under the model. Instead, create a generic ModelStorageException, and wrap the caught DatabaseQueryException inside of that. That way, your calling code can still try to deal with the error semantically, but it doesn't matter the underlying technology of the Model since you're only exposing errors from that abstraction layer. Even better, since you wrapped the exception, if it bubbles all the way up and needs to be logged, you can trace to the root exception thrown (walk the chain) so you still have all the debugging information that you need!
Don't simply catch and rethrow the same exception unless you need to do some post-processing. But a block like } catch (Exception $e) { throw $e; } is pointless. But you can re-wrap the exceptions for some significant abstraction gain.
IMHO, catching an Exception to just rethrow it is useless. In this case, just don't catch it, and let earlier called methods handle it (aka methods that are 'upper' in the call stack).
If you rethrow it, chaining the caught exception into the new one you'll throw is definitely a good practise, as it will keep the informations that the caught exception contains. However, rethrowing it is only usefull if you add some information or handle something to the caught exception, may it be some context, values, logging, freeing resources, whatever.
A way to add some information is to extend the Exception class, to have exceptions like NullParameterException, DatabaseException, etc. More over, this allow the developper to only catch some exceptions that he can handle. For example, one can catch only DatabaseException and try to solve what caused the Exception, like reconnecting to the databse.
You usually think of it this way.
A class might throw many types of exceptions that will not match. So you create an exception class for that class or type of class and throw that.
So the code that uses the class only has to catch one type of exception.
We have try/catch which could produce exceptions - because
make logical decisions in your app - you might want to change the Type of the exception if you make logical decisions based on the exception type. But the exception type thrown are most of the time specific enough. So this is more a reason not to do anything to the exception just re-throw it.
show different things to users - in this case just re-throw as it is and decide what to do at the highest level - in the Controller. You need to change the message - to log the real technical message and show to user a friendly one.
DB transactions - it can fall in any of the 2 types above - if you make some logical decisions or you just need to tell something to the user.
So, unless you got very good reasons, handling exceptions should be made in only one place (otherwise it becomes confusing) - and that place must be the very top - in the controller.
All the other places should be treated as intermediary and you should just bubble the exception - unless you have a very good reason not to.

Exceptions in PHP. How to use them? How to extend 'em?

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.

Why and how would you use Exceptions in this sample PHP code?

I've been wondering why would I use Exceptions in my PHP. Let's take a look at a simple example:
class Worker
{
public function goToWork()
{
return $isInThatMood ?
// Okay, I'll do it.
true :
// In your dreams...
false;
}
}
$worker = new Worker;
if (!$worker->goToWork())
{
if (date('l',time()) == 'Sunday')
echo "Fine, you don't have to work on Sundays...";
else
echo "Get your a** back to work!";
}
else
echo "Good.";
Is there a reason for me to use Exceptions for that kind of code? Why? How would the code be built?
And what about code that may produce errors:
class FileOutputter
{
public function outputFile($file)
{
if (!file_exists($file))
return false;
return file_get_contents($file);
}
}
Why would I use Exceptions in the above case? I have a feeling that Exceptions help you to recognize the type of the problem, which occured, true?
So, am I using Exceptions appropriately in this code:
class FileOutputter
{
public function outputFile($file)
{
if (!file_exists($file))
return throw new Exception("File not found.",123);
try
{
$contents = file_get_contents($file);
}
catch (Exception $e)
{
return $e;
}
return $contents;
}
}
Or is that poor? Now the underlying code can do this:
$fo = new FileOutputter;
try
{
$fo->outputFile("File.extension");
}
catch (Exception $e)
{
// Something happened, we could either display the error/problem directly
echo $e->getMessage();
// Or use the info to make alternative execution flows
if ($e->getCode() == 123) // The one we specified earlier
// Do something else now, create "an exception"
}
Or am I completely lost here?
When should I use an exception?
You use an exception to indicate an exceptional condition; that is, something which prevents a method from fulfilling its contract, and which shouldn't have occurred at that level.
For example, you might have a method, Record::save(), which saves changes to a record into a database. If, for some reason, this can't be done (e.g. a database error occurs, or a data constraint is broken), then you could throw an exception to indicate failure.
How do I create custom exceptions?
Exceptions are usually named such that they indicate the nature of the error, for example, DatabaseException. You can subclass Exception to create custom-named exceptions in this manner, e.g.
class DatabaseException extends Exception {}
(Of course, you could take advantage of inheritance to give this exception some additional diagnostic information, such as connection details or a database error code, for example.)
When shouldn't I use an exception?
Consider a further example; a method which checks for file existence. This should probably not throw an exception if the file doesn't exist, since the purpose of the method was to perform said check. However, a method which opens a file and performs some processing could throw an exception, since the file was expected to exist, etc.
Initially, it's not always clear when something is and isn't exceptional. Like most things, experience will teach you, over time, when you should and shouldn't throw an exception.
Why use exceptions instead of returning special error codes, etc?
The useful thing about exceptions is that they immediately leap out of the current method and head up the call stack until they're caught and handled, which means you can move error-handling logic higher up, although ideally, not too high.
By using a clear mechanism to deal with failure cases, you automatically kick off the error handling code when something bad happens, which means you can avoid dealing with all sorts of magic sentinel values which have to be checked, or worse, a global error flag to distinguish between a bunch of different possibilities.
I'm not a PHP programmer, but this looks similar to C#.
Typically you'd want to throw errors if it is a point of no return. Then you would be able to log something to show that the impossible happened.
If you can tell that the file doesn't exist then you could just say that. No real need in my mind to also throw an exception.
Now if the file was found and you are processing it, and say only half the file was uploaded and you had no way of telling that without an exception, then it'd be nice to have around.
I would say it's a good design practice to avoid the catch all exceptions "catch (Exception $e)" and design by contract instead, just like you seem to be doing in the prior example. I would at first be more specific with the type of exception being thrown and then work from there if needed. Otherwise, stay away from the try->catch and exceptions.
In no way can you assume that a file exists just because you called file_exists()! The function file_exists doesn't open the file, so the file can potentially be deleted or renamed or moved at any time!
class FileOutputter
{
public function outputFile($file)
{
if (!file_exists($file))
return false;
///<--- file may be deleted right here without you knowing it
return file_get_contents($file);//<-- then this will throw an error
//if you don't catch it, you're program may halt.
}
}
I believe this it better:
class FileOutputter
{
public function outputFile($file)
{
try{
if (!file_exists($file))
return false;
return file_get_contents($file);
}catch(Exception e){
check_what_went_wrong_and_go_to_plan_B();
}
}
}
(Edit: And it's probably even better to actually try to open the file from the beginning. If you succeed then you have a 'lock' on the file and it won't just disappear. If not, then catch the exception and see what went wrong.
Then again, you might feel that this level of reduncancy is just silly. In that case I don't think you need to worry about try/catch at all :)
Just a note really, your code for file outputter is invalid, since file_get_contents($file)
does not throw an exception. It will however raise a warning if the file doesn't exist, or can't be accessed for some reason. Additionally, you are returning an exception from outputFile, when you should probably be just letting the error propagate up the call stack.
However, you can register the error handler in php to throw exceptions when an error occurs:
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");
That way, any standard function calls that cause an error will throw an exception.
So, I would change FileOutputter to this (with that snippet above added):
class FileOutputter
{
public function outputFile($file)
{
if (!file_exists($file))
throw new Exception("File not found.",123);
return file_get_contents($file);
}
}
The calling code is then basically the same.
Basically rule is not to return an Exception when an error occurs - you can catch it if you want, and throw a custom exception, but let the exception go up the call stack until you want to use it.

Categories