Tuesday 19 May 2015

Random PHP bits: __debugInfo()

This is not a very insightful article, but I just happened to stumble across this functionality, and found it moderately interesting. This is another one of these "PHP n00b" articles, so if you're a seasoned PHP person, you'll not be reading anything here you don't already know.


Yay PHP and the undescores! Oh well. So this one comes under the banner of the (irksomely-named) "magic methods" (Is it Programming for Harry Potter? No. Well f*** off with your "magic" then).

OK, so __debugInfo(). PHP has some debugging-assistance functions along the lines of writeDump() in CFML (except not as good), which dump out stuff about a variable, eg:

$numbers = ['tahi', 'rua', 'toru', 'wha'];


This yields:

array(4) {
  string(4) "tahi"
  string(3) "rua"
  string(4) "toru"
  string(3) "wha"

I think CFML wins here:

Anyway, you get the idea. When it comes to var_dumping()-ing objects though, I think things go slightly off the rails:

// Person.php
namespace me\adamcameron\misc\model;

class Person{

    private $firstName;
    private $lastName;
    public $fullName;

    function __construct($firstName, $lastName){
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->fullName = "$firstName $lastName";

$person = new Person('Simone', 'de Beauvoir');


object(me\adamcameron\misc\model\Person)#94 (3) {
  string(6) "Simone"
  string(11) "de Beauvoir"
  string(18) "Simone de Beauvoir"

What's the dump doing outputting private properties? They're private. And that aside, because they're private they cannot interact in any way with the code which is doing the dump, so they're just irrelevant clutter, in an already-cluttered output.

Contrast with CFML:

This just shows the stuff that's relevant to the code requesting it: public properties and public methods.

But anyway, this is just framing the situation, not the actual situation itself. Where does __debugInfo() come into it?

Well, if one doesn't like the behaviour of the default debug-output handling for a class, then one can override it with the __debuginfo() method:

// PrivatePerson.php
namespace me\adamcameron\misc\model;

class PrivatePerson extends Person {

    function __debugInfo(){
        return call_user_func('get_object_vars', $this);

Here the class extends the afore-used Person.php, additionally implementing a __debuginfo() method. Now if we call our test rig:

$person = new Person('Iris', 'Murdoch');

$privatePerson = new PrivatePerson('Mary', 'Wollstonecraft');

We see the difference:
object(me\adamcameron\misc\model\Person)#110 (3) {
  string(4) "Iris"
  string(7) "Murdoch"
  string(12) "Iris Murdoch"

object(me\adamcameron\misc\model\PrivatePerson)#110 (1) {
  string(19) "Mary Wollstonecraft"

Now the debug for the PrivatePerson class uses the code from its __debugInfo() method instead. Excellent. This also works if I substitute print_r() for var_dump():

me\adamcameron\misc\model\Person Object
    [firstName:me\adamcameron\misc\model\Person:private] => Iris
    [lastName:me\adamcameron\misc\model\Person:private] => Murdoch
    [fullName] => Iris Murdoch

me\adamcameron\misc\model\PrivatePerson Object
    [fullName] => Mary Wollstonecraft

So what's that __debugInfo() method doing? I have to admit I did not come up with the inner workings of it myself, I found it on StackOverflow, I think:

function __debugInfo(){
    return call_user_func('get_object_vars', $this);

call_user_func() simply calls a callback, passing it some arguments. PHP's a bit odd in that one can supply two different variations for a callback: either an inline function, or a string representing the name of a function. What I didn't realise until now is that string value can represent a built-in function too, which is what we're doing here. get_object_vars() simply returns the public properties of the object passed to it. Which is what we want debug output to return.

So why don't I just call get_object_vars() directly? Well it turns out "no reason", as far as I can tell. This is the problem copying someone else's code and assuming they got it right. As far as I can tell, that __debugInfo() function could just as easily have been this:

function __debugInfo(){
    return get_object_vars($this);

The output is the same.

Anway, the object of the exercise is that this __debugInfo() method strikes me as being a good way to cut down on the amount inappropriate of cruft PHP outputs by default.

I wonder if there's a way to override it globally? I mean... it's "never" appropriate to expose the internal workings of an object, so it'd be good to suppress that across the board; perhaps instead when one actually decides one needs to see private property values then override the default behaviour to do so. But if you need to know that stuff, yer probably doing something wrong, I reckon. Anyhow... I'll look into that at some stage. Or if you know how to do it: lemme know!

I was gonna talk about output buffering in this article too - as I was using it as part of this exercise as well - but there's enough here for one article. I'll look at output buffering next.