« Final Fantasy XI tip: get out of being "stuck" targeting someone Married »

*Before* I begin, let me state that I agree entirely with others who have said that lambda functions and closures aren’t meant to allow you to dynamically extend classes at runtime, what I disagree with is the assertion that there are other ways of doing this. Currently there is no other way of doing this, and while I don’t think I’d use this method in production, it is still an extremely fun hack to use!
You can do things like this inside Lisp-like languages such as Python and Ruby, but they’re naturally parts of the language itself, rather than exploiting the inner working of magic methods with the lambda language feature as I do here.

Returning to the to using some of PHP5.3’s more magic features, I realised that I was being a little short-sighted with my example. In fact you can expand it to include not only methods, but static methods, and not only modifying single objects at runtime but you can emulate modifying the actual class definition itself so that any instantiated objects will have the new methods.

This is made possible by using the new late static binding and __callStatic magic method features. By using late static binding we can call a static method in the base ancestor class which assigns the lamba function passed in to a static hash of methods, indexed by the descendant class you called it via, using the new get_calling_class() function to get it’s class name and use that as the index.
We can use a separate hash of static methods to add static methods (which are just lambdas again) to the class by defining an extra __callStatic() magic method, that works just like the __call() method, but obviously for static calls.

<?php
require ‘OneVersion/DynamicClass.php’;
class MyClass extends OneVersion\DynamicClass {
public $name = ‘Dis is my class’;

public function __construct() {
echo ‘Instantiating “’ . CLASS . ‘”’ . PHP_EOL;
}
}

class MyOtherClass extends OneVersion\DynamicClass {
public function __construct() {
echo ‘Instantiating “’ . CLASS . ‘”’ . PHP_EOL;
}
}

MyClass::addMethod(
‘test’,
function($this, $text=null) {
if ($text === null) {
echo ‘default test’ . PHP_EOL;
} else {
echo $text . ‘ test’ . PHP_EOL;
}
}
);

MyOtherClass::addMethod(
‘test’,
function($this, $text=null) {
if ($text === null) {
echo ‘default test2’ . PHP_EOL;
} else {
echo $text . ‘ test2’ . PHP_EOL;
}
}
);

$object = new MyClass();
// Should output:
// Instantiating “MyClass”

echo ’1: ‘;
$object->test();
// Should output:
// default test

echo ’2: ‘;
$object->test(‘monkeys’);
// Should output: monkeys test

echo ’3: ‘;
$object->test(‘chimps’);
// Should output: chimps test

echo ’4: ‘;
$object2 = new MyOtherClass();
// Should output: Instantiating “MyOtherClass”

echo ’5: ‘;
$object2->test();
// Should output: default test2

echo ’6: ‘;
$object2->test(‘chimps’);
// Should output: chimps test2

echo ’7: ‘;
$object2->test(‘conan’);
// Should output: conan test2

echo ’8: ‘;
$object->test(‘conan’);
// Should output: conan test
?>

I’ve released "OneVersion\DyanmicClass":http://1stvamp.org/code/OneVersion_DynamicClass.tar.gz 1.0 out into the wild under the LGPL v2.1, so download and play at your own peril. ;-)


blog comments powered by Disqus