5

In my homemade PHP MVC framework, I've written a little error handler that wraps PHP errors in an exception, then throws it.

class ErrorController extends ControllerAbstract {

    ...

    public static function createErrorException($number, $message = NULL, $file = NULL, $line = NULL, array $context = array()) {
        throw new ErrorException($message, $number, 0, $file, $line);
    }
}

Which is then registered using set_error_handler(). This works fine, with the exception (no pun intended) of fatal errors. My custom error handler is still called, but I can't catch the ErrorException that is thrown.

An example of such an error would be trying to include a file that doesn't exist:

    try {
        require 'Controller/NonExistentController.php';
    } catch (ErrorException $exc) {
        echo $exc->getTraceAsString(); // code never reaches this block
    }

My custom error handler is called and the exception is thrown, but the code never reaches the "catch" block. Instead, PHP generates HTML (bad!):


Warning: Uncaught exception 'ErrorException' with message 'require(Controller/NonExistentController.php): failed to open stream: ...

Followed by:

Fatal error: Program::main(): Failed opening required 'Controller/NonExistentController.php' (include_path='.:') in ...

I do not want to attempt recovering from a fatal error, but I do want my code to exit gracefully. In this instance, that means sending back an XML or JSON response indicating an internal error, because this is a REST application and that's what my clients expect. HTML responses would most likely break the client applications as well.

Should I go about this differently?

6
  • 1
    Haha, PHP error handling, always makes me giggle, "@, the warning about the non-existent file won’t be printed. But it will be printed if scream.enabled is set in php.ini. Or if scream.enabled is set manually with ini_set. But not if the right error_reporting level isn’t set. If it is printed, exactly where it goes depends on display_errors, again in php.ini. Or ini_set." yes....
    – Alec Teal
    Commented Nov 16, 2013 at 14:29
  • 1
    Oh and my fav "Catchable fatal error"
    – Alec Teal
    Commented Nov 16, 2013 at 14:30
  • I admit I'm spoiled by how easy exception handling is in C#, but surely it can't be that difficult in PHP? Commented Nov 16, 2013 at 14:31
  • There's A LOT wrong with PHP, the == operator is wrong, strpos returning 0 which can be false... have a read of me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design I honestly don't think this can be solved, A for effort, but .... is it worth trying?
    – Alec Teal
    Commented Nov 16, 2013 at 14:33
  • I'm just not happy with PHP generating HTML output whenever it pleases. I haven't looked at how to configure php.ini files though. I thought that the world had moved on from ini files. Commented Nov 16, 2013 at 14:35

2 Answers 2

6

Look at documentation about require on php.net:

require is identical to include except upon failure it will also produce a fatal E_COMPILE_ERROR level error. In other words, it will halt the script whereas include only emits a warning (E_WARNING) which allows the script to continue.

In your case you can handle fatal errors with help of register_shutdown_function, which requires PHP 5.2+:

function customFatalHandler() {
  $error = error_get_last();

  if( $error === NULL) {
      return;
  }

  $type   = $error["type"];
  $file = $error["file"];
  $line = $error["line"];
  $message  = $error["message"];

  echo "Error `$type` in file `$file` on line $line with message `$message`"; 
}

register_shutdown_function("customFatalHandler");

Also this can be helpfull for you

  1. error_get_last() and custom error handler
  2. Why doesn't PHP catch a "Class not found" error?
5
  • I didn't even understand the difference between require and include until it slapped me in the face. Thanks for clearing that up! Commented Nov 16, 2013 at 14:54
  • I managed to get your solution to work, but it doesn't suppress the HTML warnings. What's up with that? Commented Nov 16, 2013 at 16:15
  • The answer is stackoverflow.com/questions/13952328/… Commented Nov 16, 2013 at 16:21
  • You're right. Prefixing the require statement with the @ operator also worked. I'm still wondering how I'm supposed to know all this. Commented Nov 16, 2013 at 16:31
  • Argh! This is seriously tripping me up. It seems that 1 line of code can generate multiple errors (for example 1x E_WARNING and 1x E_ERROR) depending on the error reporting setting. Does that sound right to you? Commented Nov 16, 2013 at 18:41
0

I'm sorry to have such a negative answer, but you (VERY PROBABLY) can't.

PHP is designed to chug on for as long as it can for some weird reason, you have a typo? silently initialise that variable to null, you put [] after something, evaluate as null if operator makes no sense!

This is extra messy with error handling, there are like 4 systems at play that do not work together. PHP does some error handling by "monkey patching", this causes a problem if you try to handle something in an error handler by throwing, it isn't "unpatched" until the handler exits.

With the shutdown handler, throwing from there can cause huge problems! It bit me on the ass before, I don't want to talk about it :P

It's also implementation defined as to whether files "resources" in PHP speak) are closed when shutting down, it was created to help plugins tidy up on web-servers. (hence you can have a list of them)

6
  • Just when I thought I was starting to get the hang of it. Very discouraging. :( Commented Nov 16, 2013 at 14:48
  • @StevenLiekens I HATE Php, I hate how "arrays" are neither lists or associative containers, I hate error handling, I hate the bolted on features that don't really work in the bigger picture. PHP is an exceptional language because it never comes naturally, you will always need to search, is it str2upper or str_to_upper no! It's strtoupper wait str_replace wtf? If you can use Python, USE PYTHON. It is a language that makes sense, it is consistent and well thought out, and far easier than PHP.
    – Alec Teal
    Commented Nov 16, 2013 at 14:50
  • Well, I'm cross-compiling, so I'd probably spend far more time getting Python to work than working around the PHP stupidities. But I'll consider it. Commented Nov 16, 2013 at 15:53
  • Cross compiling....php? Regardless Python is a lot easier to get work, a lot easier to extend .... it's really worth looking at.
    – Alec Teal
    Commented Nov 16, 2013 at 16:16
  • I mean the actual PHP interpreter, not my scripts. I'm compiling for a different CPU architecture (MIPS to be exact), where no precompiled binaries exist. I got PHP to work, but Python might be a different story altogether. Commented Nov 16, 2013 at 16:19

Not the answer you're looking for? Browse other questions tagged or ask your own question.