1

What happens with set_error_handler() on PHP7 now that all errors are exceptions? makes me think that recoverable errors can be caught but that does not appear to be the case, as demonstrated here:

<?php

$_SERVER['TEST'] = new stdClass;

try {
    phpinfo();
} catch (\Throwable $e) {}

If you run that code you'll get this as the output:

$_SERVER['argc'] => 1
$_SERVER['TEST'] =>
Recoverable fatal error: Object of class stdClass could not be converted to string in /Users/username/zzz.php on line 6

So clearly an error is being "issued" but one is not being thrown, unless I'm misunderstanding something?

yivi
  • 42,438
  • 18
  • 116
  • 138
neubert
  • 15,947
  • 24
  • 120
  • 212
  • This [Link](https://www.php.net/manual/en/language.errors.php7.php) can help you to resolve or understand the new exception handler in php 7+ – Dhaval Purohit Sep 04 '19 at 05:28
  • 1
    Exceptions thrown from `fatal` and `recoverable errors` do not extend `Exception` in php7 – Dhaval Purohit Sep 04 '19 at 05:31
  • Also please refer this [link](https://trowski.com/2015/06/24/throwable-exceptions-and-errors-in-php7/). – Dhaval Purohit Sep 04 '19 at 05:32
  • 2
    Are you expecting the posted code to throw an error? Why? – M. Eriksson Sep 04 '19 at 05:49
  • @DhavalPurohit - I'm not catching `Exception` - I'm catching `Throwable`. – neubert Sep 04 '19 at 12:56
  • @MagnusEriksson - because [the documentation](https://www.php.net/manual/en/language.errors.php7.php) says "_PHP 7 changes how most errors are reported by PHP. Instead of reporting errors through the traditional error reporting mechanism used by PHP 5, most errors are now reported by throwing Error exceptions_". So maybe you can perchance explain to me what exactly that means then? – neubert Sep 04 '19 at 12:58
  • The code you've posted is perfectly valid (I've tested it), so it doesn't make sense if you want to test catchable errors. The sentence means that before PHP 7, errors couldn't be caught since they weren't thrown as exceptions, so if you got an error, the script would halt. Now, in PHP 7, many errors (not all though) will be thrown as exceptions so you can catch them and decide for yourself if you want to halt the execution or if you can recover from it and continue. – M. Eriksson Sep 04 '19 at 13:09
  • I mean, it would seem that `phpinfo()` does _not_ throw anything but I shouldn't have to perform experiments or look at php-src to make that determination. And it all seems so inconsistent anyway. Why do some BUILT IN things throw stuff but some don't? – neubert Sep 04 '19 at 16:57

1 Answers1

3

Not all errors were converted to Throwable errors in PHP 7.

Actually, the docs say:

most errors are now reported by throwing Error exceptions.

(Emphasis mine). Most !== All.

Some errors are still not catchable.

Funnily enough, the error message used you get used to say "Catchable Fatal Error" instead of "Recoverable Fatal Error" before PHP 7.1 was released.

This was reported as a bug, but the solution implemented by the devs was change the error string from Catchable to Recoverable to dispel misconceptions.

In the specific case you are testing, it seems that phpinfo() raises a recoverable error instead of throwing an Error, so it makes sense you can't catch it this way.

Still, not all hope is lost.

What you could do is convert all errors to exceptions by implementing your own error handler. An example of this is described on the ErrorException documentation:

function exception_error_handler($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // This error code is not included in error_reporting
        return;
    }
    throw new ErrorException($message, 0, $severity, $file, $line);
}

set_error_handler("exception_error_handler");

The neat thing of this example is that it takes into account your error reporting settings, so only errors that would have been reported under your settings are actually thrown as exceptions. Otherwise, nothing happens.

Testing your code:

<?php
function exception_error_handler($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // This error code is not included in error_reporting
        return;
    }
    throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");

$_SERVER['TEST'] = new stdClass;

try {
    phpinfo(INFO_VARIABLES);
} catch (\Throwable $e) {echo 'CAUGHT!!!!!!';}

prints the following output:

$_SERVER['TERM_SESSION_ID'] => w0t1p0:xxx-cxx-xxxxxxx-xxxxx
$_SERVER['SSH_AUTH_SOCK'] => /private/tmp/com.apple.launchd.xxxxxxxx/Listeners
$_SERVER['LC_TERMINAL_VERSION'] => 3.3.2
....
$_SERVER['argc'] => 1
$_SERVER['TEST'] =>
CAUGHT!!!!!!%
yivi
  • 42,438
  • 18
  • 116
  • 138