While the title of this blog entry might sound rather scary to straight OO evangelists it might attract other developers - e.g. those that played around with
runkit or had a look into the Ruby world where the language supports adding new methods to a class or just to an instance of a class at runtime (see
singleton methods, and
this blog article; for those not familiar with Ruby:
Fixnum is a class already defined by Ruby core.)
With the advent of PHP 5.3 adding new methods to an instance of a class at runtime becomes possible with PHP as well, using anonymous functions and a little bit of __call() magic. First, let's define a new class (boring old foo, bar, baz example for lack of fantasy):
class Foo
{
public function bar()
{
echo "This is Foo::bar()\n";
}
}
As you surely already know you can add public properties to an instance at any time:
$foo = new Foo();
$foo->baz = 'Hello World.'`;
We can use this to store an anonymous function:
$foo = new Foo();
$foo->baz = function () { echo "This is Foo::\$baz()\n"; };
Unfortunately, we are not able to call this as a method:
$foo->baz();
This will blow up with a fatal error saying "Call to undefined method Foo::baz()". Of course we could do
$func = $foo->baz;
$func();
but that is not what we wanted to achieve. Let's add some __call() magic to our Foo class:
class Foo
{
// method bar() omitted
public function __call($method, $args)
{
if (isset($this->$method) === true) {
$func = $this->$method;
$func();
}
}
}
Now we can safely call $foo->baz() with the desired result. However, compared to Ruby you can not redefine existing methods. Therefore,
$foo->bar = function () { echo "This is Foo::\$bar()\n"; };
$foo->bar();
will still call the method Foo::bar() defined earlier and ignore the redefinition due to the nature of how __call() works.
What can you do with it? If you use
duck typing this might be useful as it reduces the amount of code required for the
Adapter design pattern as you just extend the instance you want to use instead of creating a separate adapter class. The result is the same, both a full fledged adapter class and the closure can only access the public properties and methods of the instance to adapt. Problem is, the class to adapt most likely does not have the required __call() implementation.
Another use case could be a simplified version of the
extension methods mechanism where you want to add a method locally without the need to have it available globally in the application.
If you have another good idea of what this can be used for please feel free to comment - just wanted to write my thoughts down.