I everyone, I have this simple try catch snippet, written in PHP :
try {
...
// some code
...
} catch (Throwable $e) {
$response = ['message' => $e->getMessage(), 'trace' => $e->getTraceAsString()];
}
return $response;
This code works good, but PHPSTAN does not think it is well-done :
caught "Throwable" must be rethrown. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception.
I understand the message, but I wonder if it is anyway possible to catch an exception whitout throwing it, and still validate phpstan checks ?
Thank you very much for reading and if you have a clue, I take it!
This is coming from thecodingmachine/phpstan-strict-rules which you must have installed. If you're not interested in this rule, you can uninstall the package.
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.
I am having an issue where an exception is being thrown but not caught in Module.php. The code that throws the exception is wrapped in a try/catch. There's nothing really fancy going on so I am assuming ZF2 has a quirk and/or reason for not allowing exceptions to be caught here.
The code is as follows (simplified as necessary):
class Module
{
public function onBootstrap(MvcEvent $e)
{
// Get service manager, etc.
// Set up some environment stuff
try {
// exception is thrown
}
catch (\Exception $e) {
echo $e->get_message();
exit;
}
}
}
Why is the exception not being caught?
In an ideal world there would be a way to catch this exception here. But if that is not possible, or is too convoluted to be worth the effort, an alternative process to fetching this data (regardless of source) as early in the page loading process as possible would be appreciated.
meta
I know the code in Module.php is supposed to be lightweight. But we have to fetch some data immediately before any action is performed as it will contain data vital to those actions to be performed. This data is cached after the first visit so every other page load will avoid this overhead.
I also have Googled this but apparently no one else has either experienced this, asked about it, or documented it.
This code works just fine for me in a module class:
public function onBootstrap(MvcEvent $e)
{
try {
// exception is thrown
throw new \Exception('My exception here');
}
catch (\Exception $e) {
echo $e->getMessage();
exit;
}
}
It displays the exception message and exits.
One way to investigate what is happening is to use xdebug for step by step debugging.
Just add a breakpoint in your Module and see what ZF is doing.
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.
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.