$v) { $Child->{$k} = &$this->{$k}; } if(!isset($this->datahash)) { $Child->base = &$this; $Child->datahash = spl_object_hash($this); } } public function AddDecorator($func, $args = array()) { if(is_callable($func)) { array_push($this->Decorators, array($func, $args)); } else { throw new InvalidArgumentException(sprintf('%s::AddDecorator(callable $func, array $args): Invalid function ( %s ).', get_class($this), var_export($func, true))); } } public function RemoveDecorator($func, $args = array()) { foreach($this->Decorators as & $decorator) { if($decorator === array($func, $args)) { unset($decorator); return true; } } return false; } public function Decorate() { foreach($this->Decorators as $call) { call_user_func_array($call[0], $call[1]); } } public function __call($Key, $Params) { $prev = debug_backtrace(); $Key = (string)$Key; $base = isset($this->base) ? $this->base : $this ; if(!isset($this->{$Key}) && !isset($base->{$Key})) { throw new BadMethodCallException(sprintf('Call to undefined method `%s::%s()`', get_class($this), $Key)); return; } if(is_callable($this->{$Key})) { return call_user_func_array($this->{$Key}, $Params); } elseif(is_callable($base->{$Key})) { return call_user_func_array($base->{$Key}, $Params); } else { throw new BadMethodCallException(sprintf('Call to undefined method `%s::{%s}()`.', get_class($this), var_export($this->{$Key}, true))); return; } } private function getHash() { return isset($this->datahash) ? $this->datahash : $this->datahash = spl_object_hash($this) ; } public function offsetSet($offset, $value) { static $count = 0; $hash = $this->getHash(); if (is_null($offset)) { $this->{$hash.$count++} = $value; $this->SharedProps++; } else { if(!isset($this[$offset])) $this->SharedProps++; $this->{$hash.$offset} = $value; } } public function offsetExists($offset) { return isset($this->{$this->getHash().$offset}); } public function offsetUnset($offset) { $hash = $this->getHash(); if(isset($this->{$hash.$offset})) $this->SharedProps--; unset($this->{$hash.$offset}); } public function &offsetGet($offset) { return $this->{$this->getHash().$offset}; } public function count() { return count($this->SharedProps); } protected function nop(){;} public function SetFunction($name, $func = 'nop') { $base = isset($this->base) ? $this->base : $this ; $old = isset($base->{$name}) ? $base->{$name} : NULL ; if(!is_callable(array($this, $func))) { var_dump($this, $func); throw new InvalidArgumentException(sprintf('%s::SetFunction(): Undefined method `%s`', get_class($this), var_export($func, true))); } $this->{$name} = $base->{$name} = array($this, $func); return $old; } // Note Uses shared version of water and can cause notices function PourWater() { printf('Adding Water: %d%s', $this['Water'], "\n"); } } abstract class Decorator extends Component { public function __construct(Component $Parent) { $this->parent = &$Parent; $Parent->GetProperties($this); } public function __call($Key, $Params) { $prev = debug_backtrace(); $prev = $prev[0]['class']; if($prev == __CLASS__) { if(!is_callable(array($this->parent, $Key))) { throw new BadMethodCallException(sprintf('Call to undefined method `%s::%s()`', get_class($this), $Key)); return; } return call_user_func_array(array($this->parent, $Key), $Params); } parent::__call($Key, $Params); } } class SimpleCofee extends Component { protected $Name = 'Cofee'; protected $Cup = array(); public function __construct($Parent = NULL) { $this->Water = 150; $this->Cup['cofeee'] = 25; if($Parent instanceof Component) { $Parent->GetProperties($this); } else { $this['Name'] = $this->Name; $this['Water'] = $this->Water; $this['Cup'] = $this->Cup; } $this->SetFunction('Produce', 'doProduce'); $this->SetFunction('SuperChain', 'doSuperChain'); } public function doProduce() { printf("Making %s....\n", $this->Name); $this->Decorate(); //..... $this->PourWater(); //...... printf("Making %s: %s\n", $this['Name'], var_export($this['Cup'], true)); } public function doSuperChain() { print __CLASS__.'::SuperChain('.var_export(func_get_args(), true).")\n"; } } class SimpleTea extends SimpleCofee { protected $Name = 'Tea'; public function __construct($Parent = NULL) { $this->Water = 150; $this->Cup['tea'] = 25; if($Parent instanceof Component) { $Parent->GetProperties($this); } else { $this['Name'] = $this->Name; $this['Water'] = $this->Water; $this['Cup'] = $this->Cup; } $this->SetFunction('Produce', 'doProduce'); $this->SetFunction('SuperChain', 'doSuperChain'); } public function doSuperChain() { print __CLASS__.'::SuperChain('.var_export(func_get_args(), true).")\n"; parent::doSuperChain(__CLASS__); } } class SugarCube extends Decorator { public function __construct(Component $Parent) { parent::__construct($Parent); $Parent->AddDecorator(array($this, 'AddSugar')); $this->SetFunction('SuperChain', 'doSuperChain'); } public function AddSugar() { $this['Cup']['Spoon'] = 1; $this['Ammount'] = @$this['Ammount'] + 1; $this['Water'] -= 10; printf('Adding sugar: %d%s', 1, "\n"); } public function doSuperChain() { print __CLASS__.'::SuperChain('.var_export(func_get_args(), true).")\n"; parent::doSuperChain(__CLASS__); } } class DoubleSugarCube extends SugarCube { protected $old; public function __construct(Component $Component) { parent::__construct($Component); $this->old = $this->SetFunction('Produce', 'doProduce'); $this->SetFunction('SuperChain', 'doSuperChain'); } public function AddSugar() { // Call parent class then parent object $this['Cup']['Spoon'] = 1; $this['Ammount'] = @$this['ammount'] + 2; $this['Water'] -= 20; printf('Adding sugar: %d%s', 2, "\n"); } public function doProduce() { print "\nI have take over Produce!\n"; print "But I'm a nice guy so I'll call my parent\n\n"; // Call parent objects function $this->parent->doProduce(); } public function doSuperChain() { static $count = 0; print __CLASS__.'::SuperChain('.var_export(func_get_args(), true).")\n"; parent::doSuperChain(__CLASS__); } } $Sugar = 1; $DoubleSugar = 1; $Cofee = new SimpleCofee(); $Tea = new SimpleTea(); $Cofee->Produce(); $Tea->Produce(); print "\n============\n\n"; if($Sugar) { new SugarCube($Cofee); $Cofee->Produce(); new SugarCube($Cofee); $Cofee->Produce(); } if($DoubleSugar) { new DoubleSugarCube($Tea); $Tea->Produce(); new DoubleSugarCube($Tea); $Tea->Produce(); } print "\n============\n\n"; $Tea->SuperChain('test');