Tuesday, 28 July 2015

PHP 7: PHP's error "handling" lunacy is getting on my tits

G'day:
FFS. I'm part way through an article on something completely unrelated, and needed to knock up some code to demonstrate PHP's "magic" __toString() method. That code worked fine, but my sample code cacked-out because bloody PHP still can't deal with unexpected error conditions coherently.

So here's something about __toString(), and something about PHP being f***in' stupid.

PHP's __toString() method is something one can put into a class so what if an object of that class is ever used as a string, then __toString() will be called, and if it returns a string, that string will be used. Fairly obvious stuff really. I wish CFML had this (I think Lucee kinda might? But ColdFusion does not). Anyhow, here's an example:



<?php
// classWithToString.php

require __DIR__ . "/safeRun.php";

class Person{

    protected $name;

    function __construct($name){
        $this->name = $name;
    }

}

class StringablePerson extends Person {

    function __toString(){
        return $this->name;
    }
}

safeRun("Outputing a Person", function(){
    $son = new Person("Zachary");
    echo "The boy's name is $son<br>";
});

safeRun("Outputing a StringablePerson", function(){
    $son = new StringablePerson("Zachary");
    echo "The boy's name is $son<br>";
});

Here I've got a Person class which has no special treatment of string-conversion, and a subclass of that which provides a __toString() implementation.

Ignoring what safeRun() is supposed to do for a second, we can see what happens if we try to use a Person as a string:

Outputting a Person

Catchable fatal error: Object of class Person could not be converted to string in D:\src\php\php.local\www\experiment\7\types\classWithToString.php on line 25


Fair enough. Now... using a StringablePerson:

Outputing a StringablePerson
The boy's name is Zachary
Ran OK


So that demonstrates __toString() (superficially). All good.


Now back to what safeRun() is supposed to add to this mix. Let's look at it:

// safeRun.php

function safeRun($message="", $task){
    echo $message . "<br>";
    try {
        $task();
        echo "Ran OK<br>";
    }catch (Throwable $t){
        printf("Code: %s<br>", $t->getCode());
        printf("Message: %s<br>", $t->getMessage());
    }finally {
        echo "<hr>";
    }
}

Note how it's expecting error conditions to arise. It's catching all Throwables. So how - the f*** - come that first example still spews out:

Catchable fatal error: blah blah blah blah blah

FFS, PHP.
  1. A type-check failure is not a "fatal error" in any sense of what a fatal error might be. It's just a bloody type-check failure, which should be an Exception.
  2. Errors are sub-classes of Throwables, so I should still be able to catch it!
  3. Especially one that self-describes as being catchable. The hint is in the name.

I've been around the houses with this stupidity already ("PHP 7: PHP starts to play catch-up with error- / exception-handling", "PHP 7: alpha 2 breaks EngineExceptions (on purpose, as it turns out)"), and I'm getting sick of it. I can only presume there's yet a third type of error situation that needs special catching treatment: something that is not an Exception or Error (both Throwables), but is something else again. Get yer heads out of yer arses, PHP.

And now I won't have time to finish my other article this morning before I start work, as I've been pantsing around with this sh!t for the last half hour.

It's nice to start the day in a good mood. So I'm told. [wipes froth from mouth]

--
Adam