What does $previous in the Exception constructor argument denote? How could I use it?
class MyException extends \Exception {
private $message;
private $code;
public function __construct($message,$code,\Exception $previous=null){
$this->message = $message;
$this->code = isset($code) ? $code : 0;
parent::__construct($message,$code,$previous);
}
}
I didn't find anything in the API Doc
If you throw an exception because you caught an exception, you can add the original exception as $previous. That means you can actually "nest" exceptions:
try {
throw new FooException('Foo exception');
} catch (FooException $e) {
$code = 1;
throw new BarException('Bar exception', $code, $e);
}
You can then loop over the exception "stack" instead of just the exception caught, providing you with more context.
while($e instanceof \Exception) {
echo $e->getMessage();
$e = $e->getPrevious();
}
Now, you would probably use this if you're implementing a library that can throw an exception, but you'd like to wrap that exception in your own. That way, your clients' code only needs to know of your exceptions, and not that of the exceptions that the dependencies of your code has, while not losing any kind of information.
the reason is that PHP 5.3 introduces nested exceptions as a default part of the PHP base Exception class. While the above code will work, if you are utilizing PHP 5.3, you can pass any previous exception as a third argument , and use the Exception::getPrevious() method to get a previously raised exception.
argument of a previous exception, allowing you to nest the exceptions. When preparing to log your exception, you can opt to iterate through any possible previously thrown and nested exceptions, and log any of the data you need.
What is nesting? Nesting is the ability to catch a particular exception, create a new exception object to be thrown with a reference to the original exception. This then allows the caller access to both the exception thrown from within the consumed library of the more well known type, but also access to the exception that originated this exceptional behavior as well.
Why is this useful? Typically, this is most useful in code that consumes other code that throws exceptions of its own type. This might be code that utilizes the
for more
http://www.brandonsavage.net/exceptional-php-nesting-exceptions-in-php/
Related
I have a custom exception (which is further extended in may other custom exceptions). My project requires log all the customExceptions (and all of its decendents) that occurs. I have a logger that can log customException (and anything else). One of the way of doing so is explicitly log the exception whenever it is being handled as follows.
try{
//some exception occur
}
catch(customeException $e)
{
$log->logException($e);
$e->showMessage(); // or do anything that we have to do with the error.
}
Since we are logging all the customExceptions, the other way I can think of, updating the customException constructor and log the exception right there inside the constructor. In that way, it ensure that all the customException is logged. However, if we go down to this path, my questions is:
How to inject the logger to the customException?
Will it be against the SRP principle?
Will it be consider bad practice in OOP sense or what is the best practice on this?
I think that injecting logger into the CustomException is not right, cause (as you pointed) it breaks SRP and increase the complexity of your exceptions classes.
I'll suggest you to separate Exception from ExceptionHandler. Exception class should only contain information about "what (and where) went wrong". ExceptionHandler is responsible for logging exception (and doing some other work with exception if needed).
So you can setup one global ExceptionHandler (using set_exception_handler and set_error_handler or some framework-based exception handling mechanism like symfony's ExceptionListener), that will catch all unhandled exceptions.
<?php
class ExceptionHandler {
/**
* #var Logger
*/
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function handle(Throwable $e)
{
$this->logger->logException($e);
}
}
In application code you can still throw and catch exceptions. I think there are 4 common situations.
Fully recoverable exceptions
This is general way for handling recoverable exceptions – such situations when you don't want to fail at all, but you need to do something when such exception occurs.
<?php
try {
$methodThatThrowsException();
}
catch (DoesNotMatterException $e) {
// do some stuff and continue the execution
// note, that this exception won't be logged
}
Recoverable logged exception
The same as previous, but you want to log this exception.
<?php
try {
$methodThatThrowsException();
}
catch (NonCriticalExceptionThatShouldBeLogged $e) {
$this->exceptionHandler->handle($e); // log exception
// do some stuff and continue the execution
}
Non-recoverable exceptions with "finalizer"
You want to execute some specific business logic and then fail. You can catch the exception, process it and then throw it again. Global exception handler will handle this exception and log it.
<?php
try {
$methodThatThrowsException();
}
catch (CriticalException $e) {
// do some stuff like cleanup/transaction rollback
throw $e;
}
Non-recoverable exceptions
If you want to just log the exception and fail, you can just throw this exception and global exception handler will catch and log it.
<?php
$methodThatThrowsException();
// ExceptionHandler::handle will be executed
Services can be injected into other services as long as they are relevant and maintain SRP or do something general systemy like error logging. However, an exception isn't really a service, and it should only be concerned with managing the exception data based on being throwable.
I think logging in an exception class is also a problem due to:
No control over which log type to use, warning, error, critical, etc. Unless you make it convoluted/complex
No control over whether to log or not. There are scenarios where an exception can happen but it's a known scenario that needs
no error handling etc
You'd have to pass various other logging data in to the exception, which is possibly a bit of a smell depending on the data you want to log
At the point of catching an exception, log it, then throw a new one so the caller can also catch, log, throw. Repeat this throughout your app so every thinga method calls that needs to catch a potential exception, logs and throws it's own exception named relevant to that class.
When you get to the top, e.g. a controller, don't throw but render the view with a nice message.
This is more just for documentation, since I've already solved the issue, but it was subtle and difficult enough to debug that I thought it would be useful in the public sphere.
The issue was that I had a try/catch block in an object method that just wasn't working. The reduced example is in two files, which look like this:
TestClass.php:
<?php
//TestClass.php
namespace MyTest;
class TestClass {
public function __construct() {
\e("Initializing object");
try {
\e("Trying object exception");
\throwTestException("Failing gracefully in object");
\e("After exception");
} catch (Exception $e) {
\e($e->getMessage());
}
\e("After object init exception");
}
}
?>
Main.php:
<?php
//Main.php
function e($str) { echo "\n$str"; }
function throwTestException($msg) {
throw new RuntimeException($msg);
}
require "TestClass.php";
e("Beginning");
try {
e("First try");
throwTestException("Failing gracefully first");
e("After exception");
} catch (Exception $e) {
e($e->getMessage());
}
e("Ending");
e('');
e('Beginning object test');
new \MyTest\TestClass();
e('Ending object test');
?>
The expected result on loading Main.php was this:
Beginning
First try
Failing gracefully first
Ending
Beginning object test
Initializing object
Trying object exception
Failing gracefully in object
After object init exception
Ending object test
What I actually got was something like this:
Beginning
First try
Failing gracefully first
Ending
Beginning object test
Initializing object
Trying object exception
Fatal Error: Uncaught Exception: Failing gracefully in object......
As you can see, the exception was not being caught. I tried all sorts of things and just couldn't figure out why it wasn't being caught. And then.... (See answer below)
I realized it was a namespace issue. Because I had declared TestClass within the namespace MyTest, and throwTestException in the global namespace, my reference to Exception within the class method was tacitly resolving to \MyTest\Exception and thus NOT matching the actual exception being thrown, \RuntimeException. And since I wasn't actually trying to instantiate the exception from within the namespace, no "Unknown Class" errors emerged to reveal what was happening.
The solution, then, was simply to properly resolve the exception class I was trying to catch:
catch(\Exception $e) { .... }
To be fair, this became obvious as I built my highly reduced example. It wasn't obvious initially because the exception I was expecting to catch was being generated by the class's superclass (which was the SQLite3 class). Thus, I didn't have to worry about the namespace when generating the exception, and all I was thinking about when catching it was to use the most general form of exception, Exception. And again, since I wasn't instantiating that exception -- only matching against it in a catch block --, I didn't get any notices that it was unresolved.
I was getting an error about an exception thrown in a __toString() method in a class of mine, even though there was a try...catch() in there. I did some tracking down and it turns out an object within my instance was throwing an exception in its __toString(), which meant that it wasn't being caught by the containing class' catch!
I wrote some test code as a demonstration:
class test {
public function __toString() {
try {
$b = new b();
echo (string)$b;
} catch (exception $e) {
return (string)$e;
}
}
}
class b {
public function __toString() {
throw new Exception ("test");
}
}
$test = new test();
echo $test;
I had thought that exceptions always "bubbled up" through the code until they were caught or made it all the way out.
Is there any workaround for this? The instance within my class is from a library; I don't know if I can maintainably modify it to have a catch in its own __toString().
Per PHP's __toString() section in the magic methods docs:
Warning
You cannot throw an exception from within a __toString() method. Doing
so will result in a fatal error.
When executing the code snippet you gave you get a message that says the same thing:
PHP Fatal error: Method b::__toString() must not throw an exception
There's a feature request here to support this, which states that in current state of things it would be hard to implement.
Having said all that, the proper way to fix this is to submit a patch to your upstream library to not throw exceptions from that method.
If that's not possible, the workaround for you is instead of:
echo (string)$b;
write
echo $b->__toString();
In which case you can catch the exception as you expect.
I'm using the Yii 2 framework and it uses a number of extended exceptions and I'm having an issue with them where I threw a UserException but it ended up being caught by the base Exception but I'm not sure why!?
The code:
try {
//........
if ($reader->count() > 0) {
if (!$already_active) {
//.....
} else {
throw new UserException('You have already activated your account; you may continue to login.');
}
}
} catch (\Exception $e) {
// User exception above is caught in here?
} catch (UserException $e) {
// Rethrow the exception
throw $e;
}
Shouldn't the User Exception be passed onto and caught by the second catch?
From http://php.net/manual/en/language.exceptions.php
When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matching catch block.
The catch block for Exception will be executed since Exception is a parent of UserException and therefore any object of type UserException is also of type Exception.
Therefore you should refactor your code to have the catch block for child classes first. In your case UserException should be first.
If you take a look at UserException class you can see:
class UserException extends Exception
Therefore, Exception has the higher priority.
However you can do just:
//if something's wrong
throw new \yii\base\UserException('You have already activated your account; you may continue to login.');
I want to catch an exception that is thrown by the Google API PHP library, but for some reason it generates a 'Fatal error: uncaught exception' before reaching my catch block.
In my app I have something like this:
try {
$google_client->authenticate($auth_code);
} catch (Exception $e) {
// do something
}
This is Google_Client's authenticate():
public function authenticate($code)
{
$this->authenticated = true;
return $this->getAuth()->authenticate($code);
}
The authenticate($code) above is Google_Auth_OAuth2::authenticate(), which at some point throws the exception:
throw new Google_Auth_Exception(
sprintf(
"Error fetching OAuth2 access token, message: '%s'",
$decodedResponse
),
$response->getResponseHttpCode()
);
If I put a try/catch block in the Google_Client's authenticate, it catches the exception, but without it the program just dies instead of reaching the main try/catch block from my app.
As far as I know this shouldn't be happening. Any ideas?
The problem was that the try/catch block was in a namespaced file and PHP requires you to use "\Exception". More info: PHP 5.3 namespace/exception gotcha
Example (taken from the link above):
<?php
namespace test;
class Foo {
public function test() {
try {
something_that_might_break();
} catch (\Exception $e) { // <<<<<<<<<<< You must use the backslash
// something
}
}
}
?>
I'm not sure how the structure of Google's API is, and I'm not a real fluent PHP programmer, but you're catching a specific exception type of Exception, with which Google's Google_Auth_Exception may not inherit from.
Therefore, since your try-catch block is looking for an exception that is a member of Exception and the Google_Auth_Exception is perhaps not a member of Exception, then your try catch block will miss it.
Try catching the specific exception. This has happened to me before in many different languages.
Edit
The link you posted inherits its exception from: Google/Auth/Exception
Google/Auth/Exception inherits its exception from: Google/Exception
Google/Exception extends Exception, which may, in this context be the Exception that your class is referring to.
It seems my justification for your try-catch block not catching an exception is completely wrong, but the wisdom could still be true. Try catching the specific exception, then use instanceof to see if PHP recognizes Google_Auth_Exception as a member of Exception.