I\'m curious if there is a \"better\" design for the following behavior:
I found a better general way avoiding closures and other ugly tricks.
class A {
/**************************************************************/
// Chain caller helpers, defined in base class only
// (single point of maintenance)
protected $_chain_params;
final public function chain_call($method_name, $params){
$class = get_class($this); // get last child classname
$chain = array($class);
while ($class !== 'A'){ // get all parents classname
$class = get_parent_class($class);
$chain[] = $class;
}
// Call reversed chain
$this->_chain_params = $params;
for ($k = count($chain) - 1; $k >= 0; $k--){
$class = $chain[$k];
$refl = new \ReflectionMethod($class, $method_name);
if ($refl->class === $class)
$ret = call_user_func_array(array($this,
$class.'::'.$method_name),
$this->_chain_params);
}
return $ret;
}
final protected function chain_modify_params($params){
$this->_chain_params = $params;
}
/*************************************************************/
// Methods overrided by child classes:
public function foo($a, $b){
echo "A foo fired with params a=$a b=$b
";
}
protected function bar($a, &$b){
echo "A bar fired with params a=$a b=$b
";
return 1000;
}
}
// Child classes extending base class. NOTE: no need to smell the code!
class B extends A {
public function foo($a, $b){
echo "B foo fired with params a=$a b=$b
";
}
protected function bar($a, &$b){
echo "B bar fired with params a=$a b=$b
";
return 2000;
}
}
class C extends B {
public function foo($a, $b){
echo "C foo fired with params a=$a b=$b
";
}
protected function bar($a, &$b){
echo "C bar fired with params a=$a b=$b
";
$a++; // override param value
$b++; // override referenced param value
echo " - C modify => a=$a b=$b
";
// reflect changed parameters to the next child class in chain ;)
$this->chain_modify_params(array($a, &$b));
return 3000;
}
}
class D extends C {
public function foo($a, $b){
echo "D foo fired with params a=$a b=$b
";
}
protected function bar($a, &$b){
echo "D bar fired with params a=$a b=$b
";
return 4000;
}
}
$d = new D();
echo 'Call "foo" directly...
';
$d->foo(10, 20);
echo '
Call "foo" in chain mode...
';
$d->chain_call('foo', array(10, 20));
echo '
More complex example: call "bar" in chain mode,'.
'passing $k by reference, '.
'and getting last method result...
';
$k = 40;
$ret = $d->chain_call('bar', array(30, &$k));
echo "
D->bar() return: " . $ret;
echo "
k = $k";
Result:
Call "foo" directly...
D foo fired with params a=10 b=20
Call "foo" in chain mode...
A foo fired with params a=10 b=20
B foo fired with params a=10 b=20
C foo fired with params a=10 b=20
D foo fired with params a=10 b=20
More complex example: call "bar" in chain mode,
passing $k by reference, and getting last method result...
A bar fired with params a=30 b=40
B bar fired with params a=30 b=40
C bar fired with params a=30 b=40
- C modify => a=31 b=41
D bar fired with params a=31 b=41
D->bar() return: 4000
k = 41