* @link http://www.pradosoft.com/ * @copyright Copyright © 2008-2014 Pradosoft * @license http://www.pradosoft.com/license/ */ /** * TCallChain is a recursive event calling mechanism. This class implements * the {@link IDynamicMethods} class so that any 'dy' event calls can be caught * and patched through to the intended recipient * @author Brad Anderson * @package System.Util * @since 3.2.3 */ class TCallChain extends TList implements IDynamicMethods { /** * @var {@link TListIterator} for moving through the chained method calls */ private $_iterator=null; /** * @var string the method name of the call chain */ private $_method=null; /** * This initializes the list and the name of the method to be called * @param string the name of the function call */ public function __construct($method) { $this->_method=$method; parent::__construct(); } /** * This initializes the list and the name of the method to be called * @param string|array this is a callable function as a string or array with * the object and method name as string * @param array The array of arguments to the function call chain */ public function addCall($method,$args) { $this->add(array($method,$args)); } /** * This method calls the next Callable in the list. All of the method arguments * coming into this method are substituted into the original method argument of * call in the chain. * * If the original method call has these parameters * * $originalobject->dyExampleMethod('param1', 'param2', 'param3') * * * $callchain->dyExampleMethod('alt1', 'alt2') * * then the next call in the call chain will recieve the parameters as if this were called * * $behavior->dyExampleMethod('alt1', 'alt2', 'param3', $callchainobject) * * * When dealing with {@link IClassBehaviors}, the first parameter of the stored argument * list in 'dy' event calls is always the object containing the behavior. This modifies * the parameter replacement mechanism slightly to leave the object containing the behavior * alone and only replacing the other parameters in the argument list. As per {@link __call}, * any calls to a 'dy' event do not need the object containing the behavior as the addition of * the object to the argument list as the first element is automatic for IClassBehaviors. * * The last parameter of the method parameter list for any callable in the call chain * will be the TCallChain object itself. This is so that any behavior implementing * these calls will have access to the call chain. Each callable should either call * the TCallChain call method internally for direct chaining or call the method being * chained (in which case the dynamic handler will pass through to this call method). * * If the dynamic intra object/behavior event is not called in the behavior implemented * dynamic method, it will return to this method and call the following behavior * implementation so as no behavior with an implementation of the dynamic event is left * uncalled. This does break the call chain though and will not act as a "parameter filter". * * When there are no handlers or no handlers left, it returns the first parameter of the * argument list. * */ public function call() { $args=func_get_args(); if($this->getCount()===0) return isset($args[0])?$args[0]:null; if(!$this->_iterator) { $chain_array=array_reverse($this->toArray()); $this->_iterator=new TListIterator($chain_array); } if($this->_iterator->valid()) do { $handler=$this->_iterator->current(); $this->_iterator->next(); if(is_array($handler[0])&&$handler[0][0] instanceof IClassBehavior) array_splice($handler[1],1,count($args),$args); else array_splice($handler[1],0,count($args),$args); $handler[1][]=$this; $result=call_user_func_array($handler[0],$handler[1]); } while($this->_iterator->valid()); else $result = $args[0]; return $result; } /** * This catches all the unpatched dynamic events. When the method call matches the * call chain method, it passes the arguments to the original __call (of the dynamic * event being unspecified in TCallChain) and funnels into the method {@link call}, * so the next dynamic event handler can be called. * If the original method call has these parameters * * $originalobject->dyExampleMethod('param1', 'param2', 'param3') * * and within the chained dynamic events, this can be called * * class DyBehavior extends TBehavior { * public function dyExampleMethod($param1, $param2, $param3, $callchain) * $callchain->dyExampleMethod($param1, $param2, $param3) * } * { * * to call the next event in the chain. * @param string method name of the unspecified object method * @param array arguments to the unspecified object method */ public function __dycall($method,$args) { if($this->_method==$method) return call_user_func_array(array($this,'call'),$args); return null; } }