Quickshiftin - Clever Crazy Code

Passing ‘pointers’ to private methods in PHP

September 28th, 2015

PHP has come a long way

One thing that’s remarkable about PHP is how it got out there, annexed a large segment of the Web with limited features compared to contemporaries and filled in the robust features over time. To a degree this causes headaches; as things change code must be refactored to leverage new features, but that’s just life really. What inspired this article is, I was about to do this the same way I have been for years, when I discovered a new, better way! While working on the feature it dawned on me… Wow – PHP actually has a cool way to ‘do this right’ now. So here it is, the how to, along with the back story.

Boilerplate

The Worker class

Before we look at different client classes, let’s define a Worker class and a base Client class that will let the forthcoming concrete Client classes be more succinct. Here then is the Worker class. For me this is doing some sort of heavy Imagick or Magento API calls. You can see the Worker class never makes a call to echo or anything of the sort. It merely calls _notifyClient and passes a string which the client object can do anything it wants with.

class Worker
{
    private $_callback;

    public function setNotifier(callable $callback)
    {
        $this->_callback = $callback;
    }

    public function run()
    {
        $this->_notifyClient("Doing stuff specific to Worker...\n");

        // Do stuff

        $this->_notifyClient("Doing other stuff that takes time...\n");

        // Do more stuff
    }

    private function _notifyClient($sMsg)
    {
        call_user_func($this->_callback, $sMsg);
    }
}

The base Client class

And here is a base client class which for me would be a controller echoing back to the web or command line. The Client class passes a reference to itself to the Worker. The Worker can then invoke the callback method with a string to get a message to the Client while it’s doing its longwinded task.

abstract class Client
{
    private $_worker;

    abstract protected function _setNotifier(Worker $worker);

    public function __construct()
    {
        $this->_worker = new Worker();
        $this->_setNotifier($this->_worker);
    }

    public function run()
    {
        echo get_called_class() . " running...\n";
        $this->_worker->run();
    }
}

Concreate Client classes

Psuedo-type callback requires public methods

Way back in the day of PHP4 before the advent of the   __call magic method, your only recourse was to define a public method on the subject class. While this works, you end up cluttering up the public interface of the concrete Client class for something that only ever needs to be used by the Worker class.

class ClientA extends Client
{
    protected function _setNotifier(Worker $worker)
    {
        $worker->setNotifier([$this, '_callback']);
    }

    // @note This method is not for general use...
    //       It's only made public so the Worker object can call it...
    public function _callback($sMsg)
    {
        echo $sMsg;
    }
}

__call magic method is a little better

When PHP5 came out, along with it came various magic methods, one of them being __call. This paved the way to hide the callback but now required you to go through the __call method in doing so. Not only does this potentially add unwanted logic to a given __call implementation, it’s quite slow as it adds an extra function call to invoke the callback.

class ClientB extends Client
{
    protected function _setNotifier(Worker $worker)
    {
        $worker->setNotifier([$this, '_callback']);
    }

    public function __call($name, array $args)
    {
        $this->$name($args[0]);
    }

    // @note This is private, but invoked via public __call method
    private function _callback($sMsg)
    {
        echo $sMsg;
    }
}

Closures allow references to private methods

Finally, when PHP5.3 debuted, so did closures, and that was a big day for PHP. Closures were a feature folks had been clamoring about for some time. Not only did it usher in an era of functions as first-class-citizens it obliterated the need for the old create_function function, which frankly is hideous. The use of ReflectionMethod may be a bit roundabout still, and there may be a cleaner syntax to achieve what I’ve done here, but fundamentally, you see what is going on. We’re passing a more-or-less direct ‘pointer’ to the private _callback method!

class ClientC extends Client
{
    protected function _setNotifier(Worker $worker)
    {
        $rm = new ReflectionMethod($this, '_callback');
        $worker->setNotifier($rm->getClosure($this));
    }

    // @note This is 'truly' private, yet invokable by the Worker object!
    private function _callback($sMsg)
    {
        echo $sMsg;
    }
}

Comparison to other languages

The three implementations I showed you evolved over years of PHP implementations and years of my programming career. I’d known about friend functions in C++ that let you specify one class may access another’s private members. PHP never really had any such thing and it hadn’t dawned on me that closures could be used to facilitate a similar mechanism until this past weekend!

Next I’ll take a look at another way to achieve similar results and some nice syntactic sugar to make this mechanism even easier to use.