phpreturnlanguage-featurestry-finally

PHP working after Fatal Error in the include file. Error disappear in the try-finally-return


index.php

<?php

function bar()
{
    try {
        $res = 'loading foo:';
        include 'foo.php';
    } finally {
        return $res . 'finally.';
    }
}
$res = bar() . " Why it's working???";

# php 7.1.33 is work
# php 7.4.21 is work
# php 8.0.8 is work

echo $res; // "loading foo:foo is load.finally. Why it's working???"

foo.php

<?php

$res .= 'foo is load.';
$smth = '';
$smth->getBar();

If in the foo.php will be syntax error this sample will be work without fatal error... but why?

It's not safe use this feature, what you know about it?

I want to use it in my project as "easy catch" for control require module files:

function bar()
{
    $path = 'module.php';
    $res = '';
    ob_start();
    try {
        include $path;
        $res = ob_get_clean();
    } catch (Exception $e) {
        $res = 'Module has error (' . $path . '): file:' . $e->getFile()
            . ', line:' . $e->getLine()
            . ', mess:' . $e->getMessage();
    } finally {
        ob_clean();
        return $res ?: 'Module has Fatal Error (' . $path . ')';
    }
}

echo bar();

it's work for Fatal Errors, but without backtrace :(

I try to find more information or samples for this feature


Solution

  • PHP changed most errors into exceptions. This is documented in Errors in PHP 7:

    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.

    This includes parser errors in include files, since include() is called at runtime. It doesn't include parser errors in the main script, since those are detected before any code runs, and it can't run the code if there are any parse errors.

    And for the same reason, if there's a syntax error anywhere in the include file, none of the code there will be executed, because it has to parse the entire file before executing any of it. So if there's a syntax error, $res .= 'foo is load.' will never be executed and you won't get that message when you return $res.

    As also mentioned on the above page, Error is not a subclass of Exception (they didn't want all these new errors to be caught by existing Exception handlers). So you have to use

    catch (Error $e)`
    

    to catch it.

    As for why you can return in the finally block, that's explained in the documentation of exceptions:

    Additionally, if the finally block also contains a return statement, the value from the finally block is returned.