<?php
abstract class Component extends stdCLass implements ArrayAccess, Countable
{
protected $Decorators = array();
protected $Water = 0;
protected $SharedProps = 0;
public function GetProperties(Component $Child)
{
foreach($this as $k => $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');