In C#, doing the following would destroy the stack trace of an exception:
try{
throw new RuntimeException();
}
catch(Exception e){
//Log error
//Re-throw
throw e;
}
Because of this, using throw rather than throw e is preferred. This will let the same exception propagate upwards, instead of wrapping it in a new one.
However, using throw; without specifying the exception object is invalid syntax in PHP. Does this problem simply not exist in PHP? Will using throw $e as follows not destroy the stack trace?
<?php
try{
throw new RuntimeException();
}
catch(Exception $e){
//Log error
//Re-throw
throw $e;
}
When you throw $e in PHP like you did you rethrow the exisiting exception object without changing anything of its contents and send all given information including the stacktrace of the catched exception - so your second example is the correct way to rethrow an exception in PHP.
If (for whatever reason) you want to throw the new position with the last message, you have to rethrow a newly created exception object:
throw new RuntimeException( $e->getMessage() );
Note that this will not only lose the stack trace, but also all other information which may be contained in the exception object except for the message (e.g. Code, File and Line for RuntimeException). So this is generally not recommended!
Re-throwing the same exception will not destroy the stack trace.
But depending on what you need, you might want to just throw the same exception or build an Exception Chaining ( see PHP Documentation > Exception::__construct )
A very good explanation of when and why one would choose one approach over another is given in this answer
Yes.This is a best way to catch the exception and re-throw the same exception object which carries the stack-trace data. Once you reaches the point of method that handles requests, just catch it there and send response back to user accordingly.
Its a bad idea to throw a new exception object which looses the stack-trace and create an additional object causing memory load.
Hope this helps.
Related
Can you please explain what (PDOException $e) and PDOException($e->getMessage(), (int)$e->getCode()) does and mean here?
try
{
$pdo = new PDO($attr, $user, $pass, $opts);
}
catch (PDOException $e)
{
throw new PDOException($e->getMessage(), (int)$e->getCode());
}
The best place to start is the PHP manual page on Exceptions.
Exceptions are a mechanism of mandatory error handling - rather than setting a flag that subsequent code must actively check, throwing an exception aborts all running code until it reaches a matching catch block. If no catch block is found, the entire script exits.
catch blocks match using the same logic as the instanceof operator - you name a class or interface, and the exception must be the same class, a sub-class, or implement that interface. You then give a variable name - in your example $e, but it can be any name you like - and PHP assigns the exception to that variable and runs the content of the catch block.
Creating an exception is the same as creating any other object - you use the new operator. Once created, they can have properties and methods, including a default set which includes getMessage() and getCode(), which return values which were passed when originally constructed. Their special behaviour happens when you use the throw keyword.
So, the code you've shown:
Runs some code that might throw an exception (new PDO(...))
Catches any exception matching instanceof PDOException
Looks up the "message" and "code" on that exception object
Creates a new PDOException object
Throws that new exception
The result on its own is pretty much useless - in particular, steps 4 and 5 can be replaced by throw $e, which essentially means "I've looked at this exception, but decided it's still a problem; carry on looking for another matching catch block". That's occasionally useful if you want to do something between steps 3 and 4, such as logging or cleanup.
As pointed out in another answer it does remove the backtrace, which might otherwise expose credentials. That would be better handled by setting zend.exception_ignore_args=true in your PHP configuration. PHP 8.2 will also have a new facility that automatically hides certain parameters in backtraces.
Let's compare the output of this code with and without try catch
$pdo = new PDO('foo', 'username', 'password');
will give us
PHP Fatal error: Uncaught PDOException: PDO::__construct():
Argument #1 ($dsn) must be a valid data source name in pdo.php:6
Stack trace:
#0 C:\SERVER\www\htdocs\pdo.php(6): PDO->__construct('foo', 'username', 'password')
#1 {main} thrown in pdo.php on line 6
notice the username and password can be seen in the error message
Now with try catch and re-throw
try
{
$pdo = new PDO('foo', 'username', 'password');
}
catch (PDOException $e)
{
throw new PDOException($e->getMessage(), (int)$e->getCode());
}
the output will be
PHP Fatal error: Uncaught PDOException: PDO::__construct():
Argument #1 ($dsn) must be a valid data source name in pdo.php:12
Stack trace:
#0 {main}
thrown in pdo.php on line 12
as you can see, there is no username or password.
This is the only reason such a clumsy code is employed
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 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.
I have following piece of code:
function doSomething()
{
try {
doSomeNastyStuff() // throws Exception
} catch(\Exception $e) {
if ($this->errorHandler) {
call_user_func($e);
} else {
throw($e);
}
}
}
However, the catch block does not work. The stack trace shows me the error happened at the line doSomeNastyStuff(). Where is the problem?
The problem is, you are rethrowing your Exception. The stack trace is part of the Exception instance and is recorded at the moment, exception is created. You can get the stack trace by
$e->getTrace(); // Exception $e
When you rethrow exception in your code, it still has the old stack trace recorded and this tricks your framework to show you, the exception actually happened at the line doSomeNastyStuff() and it seems like the catch does not work.
Therefore, it is better idea to rethrow exceptions the following way:
/** instead of throw($e) do */
throw new \Exception("Unhandled exception", 1, $e);
Beginining with php5.3, Exception constructor has optional third parameter $previous exactly for this purpose. You can then get the previous Exception using $e->getPrevious();
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.