I basically want to take the errors raised by PHP, turn them into an equivalent exception, and have it passed to my normal exception handler. However, I am getting some behavior that I don't understand.
I'm running Apache/2.2.3 (Linux/SUSE), with PHP Version 5.2.0.
Here is the code I am running (note, there are a few extraneous things here that aren't specifically related to the test):
<?php
class WE_Exception extends Exception
{
protected $fatal;
public function __construct($fatal, $message) { parent::__construct($message); $this->fatal = ($fatal) ? TRUE : FALSE; }
public function handle() { print "\ninto WE_Exception->handle() method."; }
}
class WE_PHPError extends WE_Exception
{
public function __construct($errno, $errstr, $errfile, $errline)
{
parent::__construct(TRUE, "PHP Error [".$errno."]: ".$errstr);
$this->file = $errfile;
$this->line = $errline;
}
}
function WE_exceptionHandler($exception) { print "\ninto WE_exceptionHandler"; $exception->handle(); }
function WE_errorHandler($errno, $errstr, $errfile, $errline)
{ print "\ninto WE_errorHandler"; throw new WE_PHPError($errno, $errstr, $errfile, $errline); return; }
set_error_handler("WE_errorHandler");
set_exception_handler("WE_exceptionHandler");
print "<pre>\n";
include("/non/existent/file");
print "\nin between include() and require() calls";
require("/non/existent/file");
print "\n</pre>";
?>
The script outputs:
into WE_errorHandler
into WE_exceptionHandler
into WE_Exception->handle() method.
I expected this to continue on and process the require() call (from PHP site: "Also note that it is your responsibility to die() if necessary. If the error-handler function returns, script execution will continue with the next statement after the one that caused an error.").
Finally, if the include() call is commented out, the output is:
in between include() and require() calls
into WE_errorHandler
Fatal error: main(): Failed opening required '/non/existent/file' (include_path='.:/usr/share/php5:/usr/share/php5/PEAR') in /srv/www/htdocs/website/test.php on line 37
It appears that the exception handler is never being called. I checked, and the WE_Exception constructor is getting called, but for some reason, the exception handler is not being called after it is constructed.
I would appreciate someone's insight into this as I have been stepping through my code and the related documentation and (obviously) am confused about some aspect of PHP's error/exception handling mechanisms.<!-- m --><a class="postlink" href="http://us.php.net/manual/en/function.set-error-handler.php">http://us.php.net/manual/en/function.se ... andler.php</a><!-- m -->
Using set_error_handler causes your error_reporting settings to be ignored. So even warnings (such as not being able to find an include file) will cause WE_errorHandler to be called. Need to add code to ignore warnings (by checking $errno) if you don't want an exception to be thrown.This is the desired behavior. I am basically converting any PHP error to an exception so that I can use the same mechanisms as I do for my own exceptions. Since I want to do the same things (logging, displaying, redirecting, etc) I want to have a common code base.You can't catch fatals, in any way, even with a custom error handler.
require() gives a fatal if it fails, therefore you can't catch a require failure.
Likewise, calling a nonexistent function is a fatal, which you can't catch.
I don't know why you can't - I'd be very keen to be able to handle fatals, but you can't.
MarkI expected this to continue on and process the require() call (from PHP site: "Also note that it is your responsibility to die() if necessary. If the error-handler function returns, script execution will continue with the next statement after the one that caused an error.").
The error handler is not returning because you throw an exception. The only way to keep things going is to put a try/catch block around the include statement.
And as MarkR said, there is nothing you can do about catching require failures.I should have read the set_exception_handler page better before I posted. My fault (RTFM, I know). I made the assumption that set_exception_handler behaved like set_error_handler in that it would continue executing (not so).
So, if I want to continue executing, I have to wrap all my potential exception spots in a try/catch block. Generally, I have only one or two places in a method where an exception could be generated and my methods are typically pretty short. So, the try/catch mechanism seems to be more of a hassle than it's worth.
Here's an example of what I'm talking about:
// first way of doing it
if (!is_writable($filepath)) { MyException::throw("File cannot be written to"); }
// php way of doing it
try
{
if (!is_writable($filepath)) { throw new MyException("File cannot be written to"); }
}
catch (MyException $e) { $e->log(); print($e); }
What benefits does using the try/catch architecture with PHP's exceptions provide over simply writing a static method (or function) that can accomplish the same thing? Furthermore, isn't it more logical to separate your error handling code from the code that generates it? In the try/catch architecture, your handling of the error is closely tied to the generation of the exception. In the static method, you can change the way exceptions are handled entirely if you want to.I don't know why you can't - I'd be very keen to be able to handle fatals, but you can't.PHP6 will be introducing a "Recoverable Fatal" error once it's established just what fatal errors can be recovered from. I think failed requires() (though include() would be a better choice if you want to continue in the event of failure) and missing functions will be included in this category.
Furthermore, isn't it more logical to separate your error handling code from the code that generates it?That's what try/catch does
try{
// code that might directly *or indirectly* throw an exception at any point
}
catch
{
// Code that handles the exception
}
In the try/catch architecture, your handling of the error is closely tied to the generation of the exception.
You don't have to immediately catch every exception that a try block may throw (and in many cases it would be silly do do so: a file-writing method shouldn't need to know what the application as a whole is meant to do in the event that a file cannot be written to - all it knows is that it can't write to it). If the method that causes the exception doesn't catch it, the exception bubbles up the call stack until it encounters a method that does catch it. Hence the *or indirectly* in the comment above: there could be a dozen or more method calls lying between the point where the exception is thrown and the point where it is caught.
One does not usually see throw statements inside try blocks (unless the type of exception being thrown is different from those cited in the corresponding catch). Straightforward use of constructs like if() can achieve the same effect without the (large) expense of exception handling; if the method knows enough to throw an exception AND catch it as well, then it knows enough to handle it normally. try blocks are to enclose code the failure modes of which are outside the method's control, and if the method isn't intending to catch any exceptions that get thrown up, there's no need for it to try.I had actually been waiting on a reply from you Weedpacket. No offense intended to anyone else; he tends to explain things in a way that is clear and that I understand.
So, I'm going to have to do some messing around and some refactoring of my ideas, but I think I "get it".
Thanks again.
I'm running Apache/2.2.3 (Linux/SUSE), with PHP Version 5.2.0.
Here is the code I am running (note, there are a few extraneous things here that aren't specifically related to the test):
<?php
class WE_Exception extends Exception
{
protected $fatal;
public function __construct($fatal, $message) { parent::__construct($message); $this->fatal = ($fatal) ? TRUE : FALSE; }
public function handle() { print "\ninto WE_Exception->handle() method."; }
}
class WE_PHPError extends WE_Exception
{
public function __construct($errno, $errstr, $errfile, $errline)
{
parent::__construct(TRUE, "PHP Error [".$errno."]: ".$errstr);
$this->file = $errfile;
$this->line = $errline;
}
}
function WE_exceptionHandler($exception) { print "\ninto WE_exceptionHandler"; $exception->handle(); }
function WE_errorHandler($errno, $errstr, $errfile, $errline)
{ print "\ninto WE_errorHandler"; throw new WE_PHPError($errno, $errstr, $errfile, $errline); return; }
set_error_handler("WE_errorHandler");
set_exception_handler("WE_exceptionHandler");
print "<pre>\n";
include("/non/existent/file");
print "\nin between include() and require() calls";
require("/non/existent/file");
print "\n</pre>";
?>
The script outputs:
into WE_errorHandler
into WE_exceptionHandler
into WE_Exception->handle() method.
I expected this to continue on and process the require() call (from PHP site: "Also note that it is your responsibility to die() if necessary. If the error-handler function returns, script execution will continue with the next statement after the one that caused an error.").
Finally, if the include() call is commented out, the output is:
in between include() and require() calls
into WE_errorHandler
Fatal error: main(): Failed opening required '/non/existent/file' (include_path='.:/usr/share/php5:/usr/share/php5/PEAR') in /srv/www/htdocs/website/test.php on line 37
It appears that the exception handler is never being called. I checked, and the WE_Exception constructor is getting called, but for some reason, the exception handler is not being called after it is constructed.
I would appreciate someone's insight into this as I have been stepping through my code and the related documentation and (obviously) am confused about some aspect of PHP's error/exception handling mechanisms.<!-- m --><a class="postlink" href="http://us.php.net/manual/en/function.set-error-handler.php">http://us.php.net/manual/en/function.se ... andler.php</a><!-- m -->
Using set_error_handler causes your error_reporting settings to be ignored. So even warnings (such as not being able to find an include file) will cause WE_errorHandler to be called. Need to add code to ignore warnings (by checking $errno) if you don't want an exception to be thrown.This is the desired behavior. I am basically converting any PHP error to an exception so that I can use the same mechanisms as I do for my own exceptions. Since I want to do the same things (logging, displaying, redirecting, etc) I want to have a common code base.You can't catch fatals, in any way, even with a custom error handler.
require() gives a fatal if it fails, therefore you can't catch a require failure.
Likewise, calling a nonexistent function is a fatal, which you can't catch.
I don't know why you can't - I'd be very keen to be able to handle fatals, but you can't.
MarkI expected this to continue on and process the require() call (from PHP site: "Also note that it is your responsibility to die() if necessary. If the error-handler function returns, script execution will continue with the next statement after the one that caused an error.").
The error handler is not returning because you throw an exception. The only way to keep things going is to put a try/catch block around the include statement.
And as MarkR said, there is nothing you can do about catching require failures.I should have read the set_exception_handler page better before I posted. My fault (RTFM, I know). I made the assumption that set_exception_handler behaved like set_error_handler in that it would continue executing (not so).
So, if I want to continue executing, I have to wrap all my potential exception spots in a try/catch block. Generally, I have only one or two places in a method where an exception could be generated and my methods are typically pretty short. So, the try/catch mechanism seems to be more of a hassle than it's worth.
Here's an example of what I'm talking about:
// first way of doing it
if (!is_writable($filepath)) { MyException::throw("File cannot be written to"); }
// php way of doing it
try
{
if (!is_writable($filepath)) { throw new MyException("File cannot be written to"); }
}
catch (MyException $e) { $e->log(); print($e); }
What benefits does using the try/catch architecture with PHP's exceptions provide over simply writing a static method (or function) that can accomplish the same thing? Furthermore, isn't it more logical to separate your error handling code from the code that generates it? In the try/catch architecture, your handling of the error is closely tied to the generation of the exception. In the static method, you can change the way exceptions are handled entirely if you want to.I don't know why you can't - I'd be very keen to be able to handle fatals, but you can't.PHP6 will be introducing a "Recoverable Fatal" error once it's established just what fatal errors can be recovered from. I think failed requires() (though include() would be a better choice if you want to continue in the event of failure) and missing functions will be included in this category.
Furthermore, isn't it more logical to separate your error handling code from the code that generates it?That's what try/catch does
try{
// code that might directly *or indirectly* throw an exception at any point
}
catch
{
// Code that handles the exception
}
In the try/catch architecture, your handling of the error is closely tied to the generation of the exception.
You don't have to immediately catch every exception that a try block may throw (and in many cases it would be silly do do so: a file-writing method shouldn't need to know what the application as a whole is meant to do in the event that a file cannot be written to - all it knows is that it can't write to it). If the method that causes the exception doesn't catch it, the exception bubbles up the call stack until it encounters a method that does catch it. Hence the *or indirectly* in the comment above: there could be a dozen or more method calls lying between the point where the exception is thrown and the point where it is caught.
One does not usually see throw statements inside try blocks (unless the type of exception being thrown is different from those cited in the corresponding catch). Straightforward use of constructs like if() can achieve the same effect without the (large) expense of exception handling; if the method knows enough to throw an exception AND catch it as well, then it knows enough to handle it normally. try blocks are to enclose code the failure modes of which are outside the method's control, and if the method isn't intending to catch any exceptions that get thrown up, there's no need for it to try.I had actually been waiting on a reply from you Weedpacket. No offense intended to anyone else; he tends to explain things in a way that is clear and that I understand.
So, I'm going to have to do some messing around and some refactoring of my ideas, but I think I "get it".
Thanks again.