Closures, Anonymous Classes and an alternative approach to Test Mocking (Part 1)

Since their first introduction with PHP 5.3, Closures have proven an incredibly useful feature for writing simple callback code, making it cleaner and more intuitive. Anonymous Functions can be used inline for many of the array functions


$price = 100.00;
$filteredArrayData = array_filter(
    $arrayData,
    function($value) use ($price) { return $value->price >= $price; }
);

or assigned to a variable as a Lambda that can be referenced many times in different places in your code.


$sortByField = function($valueA, $valueB) use (&$field) {
    return $valueA->$field <=> $valueB->$field;
};

$field = 'title';
usort($arrayData, $sortByField);

But this isn’t an article about the differences between Anonymous and Lambda Functions and Closures, or about whether Closures really are Closures compared to their equivalent in other languages, or about the difference between the function arguments and “use” variables, or even the significance of “pass by reference” in this example; there are other blogs that cover such topics. Instead, I want to take a look at binding Closures to objects as a first step to demonstrating an alternative approach to test mocking.

PHPUnit provides its own methods for creating mocks, and there are other libraries like Mockery or Prophecy that can provide easier creation of mocks; but sometimes (for testing very simple methods) the number of lines of code to create mocks can far exceed the lines of code to execute the tests (or the size of the method that we’re testing). That feels excessive!

Yet by combining Closures with Anonymous Classes, it’s easy for us to create mocks and stubs that are precisely customised for our individual tests with minimal lines of code, and often more readable within our tests as well.


So lets take a look at the first tool in our armoury: Anonymous Functions, or Closures.

Internally, PHP implements any Anonymous Function as an object of type Closure. We can see this just by doing a var_dump() of the function that we created above:


class Closure#9 (2) {
    public $static =>
        array(1) {
            'field' => string(5) "title"
        }
    public $parameter =>
        array(2) {
            '$valueA' => string(10) ""
            '$valueB' => string(10) ""
        }
}

We can even see the properties and values that we used when defining the Closure – the $parameter values are the parameters to be passed as run-time arguments when we call the closure, the $static values are the “use” variables set when we create the Closure – although we can’t access these properties as we would normally be able to access public properties of an object.

And the Closure class gives us access to a number of methods: perhaps the most powerful are those methods which allow us to bind the Closure to a class or object, because they allow us to set the scope of the closure. That gives us a mechanism to add new methods to a class (or an object instance): we can use $this or self within the Closure to reference the object that it is bound to. As an example, we’ll define a simple class, with a few levels of inheritance and instantiate it.


namespace Foo {

    class Generation_1 {
        private $secret = 1.23;
    }

    class Generation_2 extends Generation_1 {
        private $secret = 2.34;
    }

    class Generation_3 extends Generation_2 {
        private $secret = 3.45;
    }

    class Generation_4 extends Generation_3 {
        private $secret = 4.56;
    }

    $latestGeneration = new Generation_4();
}

As you can see, each class definition within that hierarchy has its own secret, defined as private, and with no getter or setter methods allowing us to access the values.

So now we create a Closure that will attempt to access and reveal those hidden secrets:


namespace Bar {

    $snooper = function() {
        echo sprintf("I am %s, and my secret is %03.2f", self::class, $this->secret), PHP_EOL;
    };
}

We’re referencing self and $this within the function for Closure, but with nothing to provide any useful scope that identifies what self and $this refer to (by default, a Closure is bound to the scope of the object in which it is created). So now we have to bind our snooper Closure to the $latestGeneration instance to give it a scope before we call it:


$latestGenerationSnooper = $snooper->bindTo($latestGeneration, get_class($latestGeneration));
$latestGenerationSnooper();

Note that a call to bindTo() (or the static equivalent bind()) actually returns a clone of the original Closure, but with the new binding and scope, rather than binding the existing Closure to the scope of the original. And if we did a var_dump() of this Closure now, we would also see the instance that it was bound to.

Now bound to the $latestGeneration instance and within the scope of the Generation_4 class within that inheritance hierarchy; when we call the Closure we see the output


I am Foo\Generation_4, and my secret is 4.56

If we don’t provide the second scope argument, we can only see the public properties of the class, and self::class will return a value of Closure; but providing that scope allows us direct access to those private and protected properties (and methods) within the instance.

Should we need to, we can also bind just to the scope of the class, rather than an instance and class, if we need access purely to static methods and properties without an object having been intantiated.

Nor are we limited to the child scope of the instance: if we know the class inheritance hierarchy, we can bind to another level, easily accessing properties that are otherwise only accessible in the scope of the parent or grandparent:


$Generation2Snooper = $snooper->bindTo($latestGeneration, 'Foo\Generation_2');
$Generation2Snooper();

and the result of executing the Closure is now:


I am Foo\Generation_2, and my secret is 2.34

Using this principle, we can even write a snooper that will traverse each level of the inheritance tree in turn retrieving the value of the $private property at that level (or more generally, we could write a Closure to view all of the private properties at each level).


class ClosureFactory {
    public static function getSnooper($object, $parent) {
        $closure = function() {
            echo sprintf(
                "I am %s, and my secret is %03.2f" . PHP_EOL,
                self::class, $this->secret
            );
            if ($parent = get_parent_class(self::class)) {
                $snooper = ClosureFactory::getSnooper($this, $parent); $snooper();
            }
        };
        return $closure->bindTo($object, $parent);
    }
}

$snooper = ClosureFactory::getSnooper($latestGeneration, get_class($latestGeneration));
$snooper();

When we run this snooper, it gives us the following output:


I am Foo\Generation_4, and my secret is 4.56
I am Foo\Generation_3, and my secret is 3.45
I am Foo\Generation_2, and my secret is 2.34
I am Foo\Generation_1, and my secret is 1.23

All we’re really doing here is recursively creating a new snooper at each level, and binding it to the next level in the hierarchy, then calling it to display its secrets before stepping down to the next level of the tree. I’ve used a factory, because otherwise accessing the “snooper” Closure object from within itself can be awkward, given that the binding of the Closure to a new scope creates a clone.


These simple examples of Closures just read the values of properties that we wouldn’t otherwise be able to access; but we could easily write and bind Closures that would allow us to set values for private or protected properties, or to call methods from within the scope of a class. Hopefully it provides you with some insight into the power that Closures can give us when bound to objects and/or Classes, and how they might provide valuable functionality when writing tests.

In the next article in this series, I’ll look at how to use Anonymous Classes to create stubs and mocks; followed by a couple of further articles showing how to combine Closures to decorate those stub and mock Anonymous Classes to enhance their functionality.

This entry was posted in PHP and tagged , , , , . Bookmark the permalink.

7 Responses to Closures, Anonymous Classes and an alternative approach to Test Mocking (Part 1)

  1. Pingback: Closure Binding for Unit testing of Private and Protected Methods | Mark Baker's Blog

  2. Pingback: Closures, Anonymous Classes and an alternative approach to Test Mocking (Part 3) | Mark Baker's Blog

  3. Pingback: Closures, Anonymous Classes and an alternative approach to Test Mocking (Part 2) | Mark Baker's Blog

  4. petermcfarlane says:

    I didn’t know now you could bind closures like this, now I’m inspired to try. Just last week I was debating an issue with a colleague: we had an object which had getter methods to return private properties, but these were only used by the tests to check the state of the object and were never called, or intended to be called, by the client. It felt a bit dirty to just introduce getters on private properties that could unintentionally be used by other developers in the future. But by using a closure and binding we could “snoop” on these properties very easily!

    Like

Leave a comment