Throwing an exception in destructor method - php

I cannot find a reliable source to ensure that I am able to throw an exception inside __destruct().
This is what php documentation says
Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error.
But when I test it
class A
{
public function __destruct()
{
throw new \Exception();
}
}
try {
$a = new A();
} catch(\Exception $x) {
var_dump($x);
}
it seems that this is not true. I throw and catch exceptions normally. So, why has the doc got this line?
edited after Mark Baker's answer:
it actually works, the exception can be thrown and caught from destructor. I am still uncertain why the documentation lies then.

The destructor isn't being called anywhere in your script, so your try/catch block won't catch anything. If you unset($x) inside the try/catch block, then an exception will be thrown, and duly caught.
class A {
public function __destruct() {
throw new \Exception();
}
}
try {
$a = new A();
unset($a);
} catch(\Exception $x) {
var_dump($x);
}
Otherwise, PHP throws an exception when the script terminates, but that is outside of your try/catch block
EDIT
The exception thrown on script termination if your object hasn't been manually destroyed can't be caught because you can't wrap try/catch around the PHP's internal termination handling that destroys the object; and this is what results in a fatal error
Fatal Error: Uncaught Exception
So this is what the documentation is warning you about

Related

Exceptions don't bubble through instances in PHP?

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.

Using an Exception to Exit PHP Application

My app has a registered shutdown function and it seems there's some issues with that and my method of using an exception with a try/catch to exit the application (instead of using the exit() method due to FastCGI not liking this).
My problem is that if another exception is thrown in the try/catch block that isn't the ExitApp exception, it causes some unexpected results and the end result is the ExitApp exception isn't caught.
I'm seeing this on PHP 5.3.6, going to test it on another version now, but I'm curious if anyone can immediately point out what's wrong here.
<?php
// Define dummy exception class
class ExitApp extends Exception {}
try {
// Define shutdown function
function shutdown() {
echo "Shutting down...";
throw new ExitApp;
}
register_shutdown_function("shutdown");
// Throw exception!
throw new Exception("EXCEPTION!");
} catch(ExitApp $e) {
echo "Catching the exit exception!";
}
/**
* Expected Result: Uncaught Exception Error and then "Catching the exit exception!" is printed.
* Actual Result: Uncaught Exception Error for "Exception" and then Uncaught Exception Error for "ExitApp" even though it's being caught.
*/
You have wrong expectations from your code. Firstly, if you throw exception in your shutdown function, you will always end up with uncaught exception - shutdown functions are called outside tr/catch block.
Secondly you have no attempt to intercept unknown exception - you are only catching ExitApp types. you may want to try something like this:
try {
//some stuff
} catch(ExitApp $ea) {
//normal exit, nothing to do here
} catch(Exception $e){
//something rather unexpected, log it
}
Your shutdown() function is not even in a try/catch block, so it will never jump down to the catch for this exception type. It is going to run on exit so you will not longer be in that try/catch block.
On a more spiritual, try/catch is not meant for flow control. I'm not quite sure why you're trying to throw this to cause script exit, rather than just calling your own shutdown() method.
Hope that helps.

Calling to static methods for unknown classes doesn't trigger an exception catch in __autoload

For some reason, exceptions thrown in an __autoload function aren't being caught when trying to call a static method on a class that doesn't exist yet (PHP 5.3.9).
See this sample code:
<?php
function __autoload($class)
{
throw new Exception('loaded ' . $class . "\n");
}
try {
echo "Object: ";
$test = new Log();
}
catch (Exception $e)
{
error_log($e->getMessage());
}
// Exception is caught with static var.
try {
echo "Static var: ";
Log::$blah;
}
catch (Exception $e)
{
error_log($e->getMessage());
}
// Fatal error occures with static method.
try {
echo "Static method: ";
Log::blah();
}
catch (Exception $e)
{
error_log($e->getMessage());
}
The exception is caught on the first 2 cases (new object and a static property of an unknown class), but is not caught on calling the static method.
PHP mixes errors and exceptions. In this case, the fatal error is "thrown" before the exception could be caught. It's about priorities. Calling an undefined function and giving the fatal error has a higher priority here than handling the exception, the code already stopped (Demo). Fatal errors itself can not be catched.
As you can not catch fatal errors with a try/catch block, your script stops at the fatal error.
Same would be for an undefined static property of a defined class. It would trigger a fatal error as well you won't be able to catch.
You can however turn errors into exceptions and catch those, however this does not work with fatal errors (Demo). I assume this is the root limitation you're running into: Fatal error brings script execution down, regardless if it appears inside a try/catch block or not. Exception handling is brought down as well.
Additionally, PHP 5.5.0-dev and PHP 5.4.0beta2-dev crashes with a simliar to yours code example. Which makes me believe that in the upcoming PHP version there will be a bug which could be considered a flaw. It might be worth to report this against PHP 5.4.
See also Exception slipping exception handler which illustrates your problem without the overhead of __autoload.
Yes, PHP have a problems with exceptions thrown in an __autoload.
But you can use one interesting trick:
function __autoload($class)
{
eval("class $class {
static function __callStatic(\$name,\$val){
throw new Exception('Loaded ' . $class . PHP_EOL);
}
}");
throw new Exception('Loaded ' . $class . PHP_EOL);
}
And don't forgot to implement work with namespaces in evaled expression.

Throwing exception within exception handler

I have a script with an exception handler. This exception handler cleans up a couple connections, prior to the script exiting after an exception.
I would like to re-throw the exception from this exception handler so that it is handled by PHP's own last-resort exception handler, where the error is written to PHP's error log, or whatever the default is, as configured in PHP.ini.
Unfortunately, this doesn't seem like a possibility, as outlined here:
http://www.php.net/manual/en/function.set-exception-handler.php#68712
Will cause a Fatal error: Exception thrown without a stack frame
Is there another way to bubble the error up the stack so that PHP handles it after my exception handler is done cleaning up?
You can not re-throw from the exception handler, however, there are other places you can. For example you can de-couple the re-throw from the handler by encapsulating things into a class of it's own and then use the __destruct() function (PHP 5.3, Demo):
<?php
class ExceptionHandler
{
private $rethrow;
public function __construct()
{
set_exception_handler(array($this, 'handler'));
}
public function handler($exception)
{
echo "cleaning up.\n";
$this->rethrow = $exception;
}
public function __destruct()
{
if ($this->rethrow) throw $this->rethrow;
}
}
$handler = new ExceptionHandler;
throw new Exception();
Put this into my error log:
[29-Oct-2011 xx:32:25] PHP Fatal error: Uncaught exception 'Exception' in /.../test-exception.php:23
Stack trace:
#0 {main}
thrown in /.../test-exception.php on line 23
Just catch the exception and log the message yourself, then rethrow.
try {
$foo->doSomethingToCauseException();
} catch (Exception $e) {
error_log($e->getMessage());
throw $e;
}
If you bubble up to the top and PHP is unable to handle, it will result in uncaught exception.
Will cause a Fatal error: Exception thrown without a stack frame
This error means that your exception is thrown from a code that is not part of the script (as far as PHP knows). Examples of such code include custom exception handler set with set_exception_handler() and any class destructor method. There's no choice but to NOT throw an exception from such a code.
If you want PHP native error handling, I'd suggest you to call trigger_error() instead. It should log the error if you don't have custom error handler and you use suitable error type. For example, E_USER_ERROR should be fine.
Just rethrow the exception as a RunTimeException and it will keep the stacktrace :)
try {
// bad exception throwing code
} catch (Exception $e) {
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
}
From http://www.php.net/manual/en/function.set-exception-handler.php#88082
i read:
Another solution is to restore the error handler at the beginning of the exception handler.
Have you tried it?

Is it possible to know if you're already in an exception in PHP?

I have some code in a __destruct() method that sometimes throws an exception. The __destruct() method is being called during another exception and I'm seeing a vague error:
PHP Fatal error: Ignoring exception from exampleClass::__destruct() while an exception is already active
which is hiding the actual exception that's being called. I'd like to do something like:
public function __destruct()
{
try
{
// do work here
}
catch(Exception $e)
{
// check if we're already in an exception and log it
if(already_in_exception())
{
error_log($e->getMessage());
}
// normal destruct, re-throw
else
{
throw $e;
}
}
}
Bonus points if it's PHP 5.1.6 compatible!
Thank you in advanced!
Your problem isn't because you're throwing an exception from within another, it's because you're throwing an exception from a destructor.
From php.net: http://php.net/manual/en/language.oop5.decon.php I quote:
"NOTE: Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error."
Rethink your logic abit and do this prior to deconstruction.

Categories