Maarten Balliauw {blog}

ASP.NET MVC, Microsoft Azure, PHP, web development ...

NAVIGATION - SEARCH

Extension methods for PHP

PHP Extension Methods The concept of “extension” methods will be nothing new to this blog’s .NET-related audience. For the PHP-related audience, this is probably something new. Let’s start with the official definition for extension methods: Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type.

Let’s visualize this. Imagine having the following class:

[code:c#]

class HelloWorld
{
    public function sayHello($to = 'World', $from = null)
    {
        $helloString = "Hello, " . $to . "!";
        if (!is_null($from)) {
            $helloString .= " From " . $from;
        }

        echo $helloString . "\r\n";
    }
}

[/code]

Not too difficult. There’s a sayHello() method that allows you to optionally pass who should be greeted and who the greeting is from. Imagine you would like to have a sayHelloTo() method as well, a shortcut to sayHello($to). And some other helper methods that use sayHello() under the hood. Wouldn’t it be nice to have the HelloWorld class concentrating on the real logic and defining the helper/utility functions somewhere else? How about a class named HelloExtensions:

[code:c#]

class HelloWorldExtensions
{
    public static function sayHelloTo(HelloWorld $subject, $name = '')
    {
        $subject->sayHello($name);
    }

    public static function sayHelloFrom(HelloWorld $subject, $name = '')
    {
        $subject->sayHello('World', $name);
    }
}

[/code]

Ok, nice. But this means I should call HelloWorldExtensions::sayHelloTo(‘Maarten’); to use the utility functions, right? Well: no! Not if we implement the concept of extension methods. Let’s see what we can do to HelloWorld in order to make our life easier.

[code:c#]

class HelloWorld
{
    public function sayHello($to = 'World', $from = null)
    {
        $helloString = "Hello, " . $to . "!";
        if (!is_null($from)) {
            $helloString .= " From " . $from;
        }

        echo $helloString . "\r\n";
    }

    public function __call($functionName, $arguments = array())
    {
        // Add an extra parameter
        array_unshift($arguments, $this);

        // Current reflected class
        $reflectedClass = new reflectionObject($this);

        // Find suitable class and function
        $availableClasses = get_declared_classes();
        foreach ($availableClasses as $class) {
            $classDefinition = new ReflectionClass($class);
            $availableMethods = $classDefinition->getMethods();
            foreach ($availableMethods as $method) {
                if ($method->isStatic() && $method->getName() == $functionName) {
                    $availableParameters = $method->getParameters();
                    if ($availableParameters[0]->getClass()->getName() == $reflectedClass->getName()) {
                        $method->invokeArgs(null, $arguments);
                    }
                }
            }
        }
    }
}

[/code]

The magic method __call() is used to look for a class that defines a static method with the first parameter of our type, HelloWorld. This now allows us to write very clean calls to code, as well as simplify our HelloWorld class:

[code:c#]

$helloWorld = new HelloWorld();
$helloWorld->sayHello();
$helloWorld->sayHelloTo('Maarten');
$helloWorld->sayHelloFrom('Maarten');

[/code]

Sweet, no? Can someone get this into the PHP core? I would greatly appreciate it: my classes can focus on functionality and all utility functions that just pass around variables can be defined in a separate class.

blog comments powered by Disqus