Extension methods for PHP
Edit on GitHubThe 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.
This is an imported post. It was imported from my old blog using an automated tool and may contain formatting errors and/or broken images.
14 responses