This article is from 2004 and is kept here for historical reasons. Apparently (as of 2008) PHP will get native support for closures soon, making this ugly hack even less useful.
function rot13widget() {
$a = new TextField();
$b = new Button('Rotate!');
$b->setClickHandler(function() { // NOT VALID CODE!
$a->setText(str_rot13($a->getText()));
});
return Layout::horizontal($a, $b);
}
$panel->add(rot13widget());
... the point being that the function object passed to onClick contains the necessary pointers to $a.
function rot13widget() {
$a = new TextField();
$b = new Button('Rotate!');
$b->setClickHandler(eval(closure('
$a->setText(str_rot13($a->getText()));
')));
return Layout::horizontal($a, $b);
}
The button class would just store the function object, and call it like this:
class Button {
function setClickHandler($o) {
$this->clickHandler = $o;
}
function handleClick() {
if($this->clickHandler)
$this->clickHandler->call();
}
}
The hassle of maintaining the necessary pointers to the various widgets would be handled invisibly by the closure class.
new Closure(
'$a->setText(str_rot13($a->getText()));',
array('a' => &$a));
Here's the actual code:
Downlod this snippet as closures.txt
/** * Simple closure class; Erling Ellingsen, 2003. * * http://steike.com/PhpClosures */ class Closure { var $code; var $env; function Closure($code, $env) { $this->code = $code; $this->env = $env; } function call($__args = NULL) { // $this will probably be clobbered by the next step, so grab our // code and environment now $__code = $this->code; $__env =& $this->env; // set up the scope we need // extract() doesn't do references, so we can't use that foreach(array_keys($__env) as $__key) $$__key =& $__env[$__key]; return eval($__code); } function makeGrabber($s) { $zot = $seen = array(); // basically, grab everything that looks like a variable reference. // noone gets hurt if we happen to grab too much. if(preg_match_all('/\$(\w+)/', $s, $m)) foreach($m[1] as $var) if(!$seen[$var]++) $zot[] = "'$var' => &\$$var"; $grabber = join(', ', $zot); $escaped = preg_replace("/['\\\\]/", "\\$&", $s); $grabber = "return new Closure('$escaped', array($grabber));"; return $grabber; } } function closure($s) { return Closure::makeGrabber($s); } function _test_closure($n) { return eval(closure(' return $n++; ')); } $a =& _test_closure(5); $b =& _test_closure(10); assert($a->call() == 5); assert($b->call() == 10); assert($a->call() == 6);