错误处理 函数
在线手册:中文 英文
PHP手册

set_exception_handler

(PHP 5)

set_exception_handler Sets a user-defined exception handler function

说明

callable set_exception_handler ( callable $exception_handler )

Sets the default exception handler if an exception is not caught within a try/catch block. Execution will stop after the exception_handler is called.

参数

exception_handler

Name of the function to be called when an uncaught exception occurs. This function must be defined before calling set_exception_handler(). This handler function needs to accept one parameter, which will be the exception object that was thrown.

Note:

NULL may be passed instead, to reset this handler to its default state.

返回值

Returns the name of the previously defined exception handler, or NULL on error. If no previous handler was defined, NULL is also returned. If NULL is passed, resetting the handler to its default state, TRUE is returned.

范例

Example #1 set_exception_handler() example

<?php
function exception_handler($exception) {
  echo 
"Uncaught exception: " $exception->getMessage(), "\n";
}

set_exception_handler('exception_handler');

throw new 
Exception('Uncaught Exception');
echo 
"Not Executed\n";
?>

参见


错误处理 函数
在线手册:中文 英文
PHP手册
PHP手册 - N: Sets a user-defined exception handler function

用户评论:

irijain at hotmail dot com (21-Dec-2010 01:44)

In my case solution was simple for "exception thrown without a stack frame in Unknown on line 0" - just added "(string)" before simpleXML value:

<?php
$url
= "http://www.example.com/";
$data = file_get_contents($url);
$xml = new SimpleXMLElement($data);
$myValue = (string)$xml->myValue;
?>

pinkgothic at gmail dot com (01-Jun-2010 10:30)

If you're handling sensitive data and you don't want exceptions logging details such as variable contents when you throw them, you may find yourself frustratedly looking for the bits and pieces that make up a normal stack trace output, so you can retain its legibility but just alter a few things. In that case, this may help you:

<?php

function exceptionHandler($exception) {

   
// these are our templates
   
$traceline = "#%s %s(%s): %s(%s)";
   
$msg = "PHP Fatal error:  Uncaught exception '%s' with message '%s' in %s:%s\nStack trace:\n%s\n  thrown in %s on line %s";

   
// alter your trace as you please, here
   
$trace = $exception->getTrace();
    foreach (
$trace as $key => $stackPoint) {
       
// I'm converting arguments to their type
        // (prevents passwords from ever getting logged as anything other than 'string')
       
$trace[$key]['args'] = array_map('gettype', $trace[$key]['args']);
    }

   
// build your tracelines
   
$result = array();
    foreach (
$trace as $key => $stackPoint) {
       
$result[] = sprintf(
           
$traceline,
           
$key,
           
$stackPoint['file'],
           
$stackPoint['line'],
           
$stackPoint['function'],
           
implode(', ', $stackPoint['args'])
        );
    }
   
// trace always ends with {main}
   
$result[] = '#' . ++$key . ' {main}';

   
// write tracelines into main template
   
$msg = sprintf(
       
$msg,
       
get_class($exception),
       
$exception->getMessage(),
       
$exception->getFile(),
       
$exception->getLine(),
       
implode("\n", $result),
       
$exception->getFile(),
       
$exception->getLine()
    );

   
// log or echo as you please
   
error_log($msg);
}

?>

If you're not a fan of sprintf() or the duplicate $exception->getFile() and $exception->getLine() calls you can of course replace that as you like - consider this a mere compilation of the parts.

Anonymous (16-Dec-2009 02:12)

Things you should be aware of:

An exception handler handles exceptions that were not caught before. It is the nature of an exception that it discontinues execution of your program - since it declares an exceptional situation in which the program cannot continue (except you catch it).

Since it has not been catched your code signals it is not being aware of the situation and cant go on.

This implies: returning to the script is simply impossible when the exception handler has already been called, since an uncaught exception is not a notice. use your own debug- or notice-log-system for things like that.

Furthermore: While is is still possible to call functions from your script, since the exception handler has already been called exceptions bubbling from that piece of code won't trigger the exception handler again. php will die without leaving any information apart form "uncaught exception with unknown stack frame". So if you call functions from your script, make sure that you catch any exceptions that possibly occur via try..catch inside the exception handler.

For those of you who misinterpreted the essential meaning of the exception handler: it's only use is to handle the abortion of your script gracefully, e.g. in a project like facebook or wikipedia: render a nice error page, eventually hiding information which shall not leak into the public (instead you may want to write to your log or mail the sys-admin or stuff like that).

In other words: Redirecting all php-errors form an error-handler using exceptions - including notices - is a very dumb idea, if you do not intend having your script aborted everytime you didn't set a variable (for example).

my 2 cents.

Anonymous (11-Dec-2009 02:09)

When an uncaught exception is handled, execution does NOT return to the script, but rather (unexpectedly, on my end anyway) terminates.

I am using set_error_handler() and set_exception_handler() in conjunction for a system I am currently developing (on v5.3.0 with Xampp). Lets say two E_USER_NOTICES are triggered, the script will die after the first one is processed.

<?php
    set_exception_handler
( 'exc_handler' );
    function
exc_handler($exception) {
        echo
"Uncaught exception: " , $exception->getMessage(), "\n";
    }
    function
errorone() {
        throw new
Exception("Test 1");
    }
    function
errortwo() {
        throw new
Exception("Test 2");
    }
    function
test() {
       
errorone();
       
errortwo();
    }
   
test();
   
test();
?>

Instead of printing (as I'd expect) "Uncaught exception: Text 1\nUncaught exception: Text 2\nUncaught exception: Text 1\nUncaught exception: Text 2\n"

It is only printed ONCE. I've tried a number of different things, but I can't figure out how to return execution to the script after the EXCEPTION HANDLER has run.

If anyone has a solution on how to return execution (hence, allow the script to log uncaught exceptions but continue processing) then PLEASE email me! Thanks!

joshua dot boyle-petrie at its dot monash dot edu (09-Jan-2009 07:44)

Thanks to mastabog we know that throwing an exception within the exception handler will trigger a fatal error and a debugging nightmare.  To avoid throwing an exception within there should be easy.

However, if you use a custom error handler to convert errors to ErrorExceptions suddenly there are a multitude of new ways to accidentally throw exceptions within the exception handler.

<?php
function error_handler($code, $message, $file, $line)
{
    if (
0 == error_reporting())
    {
        return;
    }
    throw new
ErrorException($message, 0, $code, $file, $line);
}
function
exception_handler($e)
{
   
// ... normal exception stuff goes here
   
print $undefined; // This is the underlying problem
}
set_error_handler("error_handler");
set_exception_handler("exception_handler");
throw new
Exception("Just invoking the exception handler");
?>
Output: Fatal error: Exception thrown without a stack frame in Unknown on line 0

The best way I have found to avoid this is to wrap up everything in the exception handler in a try/catch block.
<?php
function exception_handler($e)
{
    try
    {
       
// ... normal exception stuff goes here
       
print $undefined; // This is the underlying problem
   
}
    catch (
Exception $e)
    {
        print
get_class($e)." thrown within the exception handler. Message: ".$e->getMessage()." on line ".$e->getLine();
    }
}
?>
Output: ErrorException thrown within the exception handler. Message: Undefined variable: undefined on line 14

This speeds up debugging and offers some scalability to any other exceptions accidentally thrown within the exception handler.

Another solution is to restore the error handler at the beginning of the exception handler.  While this is a silver bullet in terms of avoiding the ErrorExceptions, debugging messages then rely on the error_reporting() level and the display_errors directive.  Why mention this?  It might be preferable for production code since we care more about hiding errors from users than convenient debugging messages.

marques at displague dot com (14-Apr-2008 03:19)

frank,

Your exception handler is configured to be the handler for all exceptions, yet if a basic 'Exception' is thrown, your static method will error because 'Exception's do not have 'getException'.  Because of this I don't see a real purpose to making the uncaught handler a class that extends Exception. 

I do like the idea of using static methods of a general Exception handling class. 

<?php
class ExceptionHandler {  
    public static function
printException(Exception $e)
    {
        print
'Uncaught '.get_class($e).', code: ' . $e->getCode() . "<br />Message: " . htmlentities($e->getMessage())."\n";
    }
  
    public static function
handleException(Exception $e)
    {
        
self::printException($e);
    }
}

set_exception_handler(array("ExceptionHandler", "handleException"));

class
NewException extends Exception {}
try {
  throw new
NewException("Catch me once", 1);
} catch (
Exception $e) {
 
ExceptionHandler::handleException($e);
}

throw new
Exception("Catch me twice", 2);
?>

Gives:
Uncaught NewException, code: 1<br />Message: Catch me once
Uncaught Exception, code: 2<br />Message: Catch me twice

There are much more interesting things that can be done like reformating and optionally displaying or emailing them.  But this class acts a nice container for those functions.

frank at netventures dot com dot au (06-Nov-2007 06:10)

Hey all, i've just started to use the exception suite instead of the normal PHP error suite. For those of you looking for an object orientated way to do this without looking down at Glen and Sean's examples (Lesson 1: ALWAYS read the logs!), here you go:

<?php

class NewException extends Exception
{
    public function
__construct($message, $code=NULL)
    {
       
parent::__construct($message, $code);
    }
   
    public function
__toString()
    {
        return
"Code: " . $this->getCode() . "<br />Message: " . htmlentities($this->getMessage());
    }
   
    public function
getException()
    {
        print
$this; // This will print the return from the above method __toString()
   
}
   
    public static function
getStaticException($exception)
    {
        
$exception->getException(); // $exception is an instance of this class
   
}
}

set_exception_handler(array("NewException", "getStaticException"));
throw new
NewException("Catch me!!!", 69);

?>

Let me know if i'm missing something obvious as I left my glasses at home and I just came back from the Melbourne cup (If I won then I wouldn't be at work still!).

bhyoram at zahav dot net dot il (11-Jul-2007 05:33)

to do it in command-line, use this :
echo '<?php function ff ($exception) { printf ("I am a handler, got %s\n",$exception->getMessage ()) ;} set_exception_handler ("ff") ; throw new Exception ("blabla") ; echo "This is bad\n" ;' | php

parazuce at gmail dot com (09-Jul-2007 11:41)

I've been messing around with this function, and have noticed you can pass an anonymous function (created with create_function()) through as the default handler, for example:

set_exception_handler(create_function('$e', 'exit("An unknown error occurred");'));

That snippet of code can be used if you simply want to suppress all exceptions that are not handled.  This can be a great thing, because secure data could possibly be leaked otherwise (for example, the default exception handler could output a snippet of your SQL code that was involved with the exception being thrown).

You will want to use this wisely, however (if at all).

reg dot php dot manual at entropy dot ch (09-May-2007 02:59)

In my experience, the static keyword is crucial for error handlers which are methods of a class instead of free-standing functions.

    static function exceptionHandler($exception)

works but

    function exceptionHandler($exception)

doesn't and results in a "Fatal error: Exception thrown without a stack frame in Unknown on line 0" message.

"public" is optional as it is the default anyway (but it is probably clearer to write it explicitly).

Glen (03-Mar-2007 08:34)

If you want a class instance to handle the exception, this is how you do it :

<?php
class example {
   public function
__construct() {
       @
set_exception_handler(array($this, 'exception_handler'));
       throw new
Exception('DOH!!');
   }

   public function
exception_handler($exception) {
       print
"Exception Caught: ". $exception->getMessage() ."\n";
   }
}

$example = new example;

?>

See the first post (Sean's) for a static example.  As Sean points out, the exception_handler function must be declared public.

mastabog at hotmail dot com (08-Aug-2006 03:44)

A behaviour not documented or discussed enough, yet pretty common is that is that if an exception is thrown from the global exception handler then a fatal error occurs (Exception thrown without a stack frame). That is, if you define your own global exception handler by calling set_exception_handler() and you throw an exception from inside it then this fatal error occurs. It is only natural though, as the callback defined by set_exception_handler() is only called on uncaught (unhandled) exceptions so if you throw one from there then you get this fatal error as there is no exception handler left (you override the php internal one by calling set_exception_handler()), hence no stack frame for it.

Example:

<?php

function myExceptionHandler (Exception $ex)
{
    throw
$ex;
}

set_exception_handler("myExceptionHandler");

throw new
Exception("This should cause a fatal error and this message will be lost");

?>

Will cause a Fatal error: Exception thrown without a stack frame

If you skip/comment the set_exception_handler("...") line then the internal PHP global handler will catch the exception and output the exception message and trace (as string) to the browser, allowing you to at least see the exception message.

While it is a very good idea to always define your own global exception handler by using the set_exception_handler() function, you should pay attention and never throw an exception from it (or if you do then catch it).

Finally, every serious coder should use an IDE with debugging capabilities. Tracking down an error like this becomes a trivial matter by using simple debugging "Step into" commands (I for one recommend Zend IDE v5.2 at the moment of this writing). I have seen numerous messages on the internet with people wondering why this message pops up.

Cheers

p.s. Other causes for this error which are somehow unrelated to this is when you throw an exception from a destructor (the reasons behind that are similar though, the global handler might no longer exist due to the php engine shutting the page down).

mc-php-doco at oak dot homeunix dot org (07-Apr-2006 05:41)

This seems not to work when calling the PHP binary with the '-r' flag.

For example, if I run it like this:

    php -r '
      function exception_handler($exception) {
        echo "Uncaught exception: " , $exception->getMessage(), "\n";
      }
 
      set_exception_handler("exception_handler");
 
      throw new Exception("Uncaught Exception");
      echo "Not Executed\n";
    '

Or if I place it in a file and run it like this:

    php -r 'include "./tmp.php";'

I get a stack trace instead of having the function 'exception_handler' called.  If run it like this:

    php tmp.php

It works fine. 

(Why run code from '-r'?  Sometimes it's useful to add stuff around the include like calls to microtime for benchmarks, or to include a library and then call a few functions from the library, all in an ad-hoc way without having to create new files.)

PHP versions 5.1.2 and 5.0.4.

ch at westend dot com (21-Feb-2006 03:06)

It seems that although the Exception contains a backtrace itself, the stack for the debug_backtrace() function is empty when entering an exception handler. This is very inconvinient. See bug #36477.

sean at seanodonnell dot com (24-Oct-2005 12:48)

Using the 'set_exception_handler' function within a class, the defined 'exception_handler' method must be declared as 'public' (preferrable 'public static' if you use the "array('example', 'exception_handler')" syntax).

<?php
class example {
    public function
__construct() {
        @
set_exception_handler(array('example', 'exception_handler'));
        throw new
Exception('DOH!!');
    }

    public static function
exception_handler($exception) {
        print
"Exception Caught: ". $exception->getMessage() ."\n";
    }
}

$example = new example;

echo
"Not Executed\n";
?>

Declaring the 'exception_handler' function as 'private' causes a FATAL ERROR.

[derick: red. updated statement about static a bit]