The Real Error

22 07 2015

flickr: by Poulepondeuse

Sometimes a PHP script may produce an inconsistent type of error, i.e. in one version of PHP a less serious parse error displays while in another version a fatal error message appears. Why does PHP do such things? A question arising from a recent discussion on the Internals List concerns whether this sort of error reporting is erroneous itself.  The conversation focused on a class with a static method that when called with a single colon instead of the paamayim nekudotaim (double colon) different error messages display depending on the PHP version.

The snippet below caused the discussion to ensue:

class Foo {
 public static function bar() {
   echo 'bar';
 }
}
Foo:bar();

Since PHP5.3, the result is a fatal error while previous versions show either a parse or syntax error indicative of different perceptions over the years about the source for the error.   According to a discussion at StackOverflow, parse errors occur when the user makes a syntax error, so it’s apparently been a matter of deciding where to assign culpability.

The error message that occurs in PHP5.3 and successive versions reads as follows:

Fatal error: Call to undefined function bar()

The message may leave one feeling a bit perplexed.  In fact, one user thought that the error itself was a mistake and should properly be a compile time error instead.  With PHP, however, often  appearances may conceal a much more complicated picture and, indeed such is the case here.

The fatal error naturally and correctly results as a consequence of PHP having acquired starting with PHP5.3 a feature considered controversial at the time, namely the goto statement.  With its addition, labels too became a part of PHP.  So, starting with  PHP5.3 the parser understands the statement “Foo:bar()” as a label with an undefined function bar(), hence the resulting fatal error, which begs the question how may one avoid making such an error other than exercising visual vigilance.

Or, one may accept that errors will happen — that’s life.  A more helpful approach involves figuring out how to quickly detect and fix any errors.  Prior to PHP7, the onus was on Userland to seek a solution for this particular error. The following represents one way to inform the user when only half of the double-colon symbol manages to enter the code:

function Whoops($f) {
    return "Missing colon caused $f() to execute.";
}
function bar(){
 echo Whoops(__FUNCTION__);
}
class Foo {
 public static function bar() {
   echo 'bar';
 }
}
Foo:bar(); // Missing colon caused bar() to execute.

See live example.

PHP7 inspires hope of a more sophisticated alternative with the possibility of recoverable fatal errors that a developer can transform into Zend engine exceptions; see Nikita Popov’s RFC: “Exceptions in the engine (for PHP 7)”.  Let’s find out how well its implementation handles the following snippet, including the fatal error it should produce:

class Foo {
    public static function bar() {
        echo 'bar';
    }
}
try {
Foo:bar();
} catch (EngineException $e) {
    echo "Exception: {$e->getMessage()}\n"; 
}

See live code

PHP 7.0.0alpha1 deftly handles the fatal error, transforming it into a Zend engine exception as the output indicates:

Exception: Call to undefined function bar()

Alas, something changed in a subsequent update since neither PHP7.0.0alpha2 nor PHP7.0.0beta2 treats the error as a catchable fatal error, displaying instead the mystifying message:

Fatal error: Uncaught Error: Call to undefined function bar()

A stack trace accompanies this feedback, showing that an error was thrown instead of the expected exception. Is this kind of error actually recoverable, i.e. catchable? The RFC states the following:

Some errors are easy to change to exceptions, others are more complicated. Some are impossible …

Perhaps, down the road, this fatal error will merit being worthy of being caught.  Until then, if ever, owing to the addition of goto, PHP users need to practice defensive coding when employing static methods or else devise some kind of strategy for handling the mistake of using one colon where the presence of two makes all the difference between code that runs and that which fails.

And, sometimes the impossible becomes possible in PHPland.  A striking change occurred with respect to errors and exceptions between the two alpha versions of PHP7, involving the elimination of the ZendEngine Exception (see  explanation by Aaron Piotrowski, RFC author of Throwable Interface).  In this light, the failure of the previous example to catch the error becomes comprehensible. Referring to a non-existent Zend Engine Exception would naturally cause an error message to appear and for the code to cease executing.

One may correct the last example by taking advantage of the new throwable interface which permits PHP to throw an exception or an error object.  The following code revises the last example so that it may successfully catch the fatal error; it works if you use PHP7.0.0alpha2 and higher:

class Foo {
 public static function bar() {
 echo 'bar';
 }
}

try {
Foo:bar();
} catch (Error $e) {
 echo "Just now caught error: {$e->getMessage()}\n";
}

See live code.

Predictably this code errors out for PHP7.0.0alpha1, but in the later versions of PHP7 the result displays:

Just now caught error: Call to undefined function bar()

With the new throwable error object, a mere typo involving one colon instead of two need hardly result in failure.  Instead, employing that object along with a try-catch structure, the error may be caught, while the code executes uninterrupted.

This work is licensed under a Creative Commons License

Advertisements

Actions

Information

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: