summaryrefslogtreecommitdiff
path: root/framework/Util/TCallChain.php
diff options
context:
space:
mode:
Diffstat (limited to 'framework/Util/TCallChain.php')
-rw-r--r--framework/Util/TCallChain.php147
1 files changed, 147 insertions, 0 deletions
diff --git a/framework/Util/TCallChain.php b/framework/Util/TCallChain.php
new file mode 100644
index 00000000..9d6144b3
--- /dev/null
+++ b/framework/Util/TCallChain.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * TCallChain class file.
+ *
+ * @author Brad Anderson <javalizard@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2008-2013 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 <javalizard@gmail.com>
+ * @version $Id: TCallChain.php 564 2009-01-21 22:07:10Z javalizard $
+ * @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
+ * <code>
+ * $originalobject->dyExampleMethod('param1', 'param2', 'param3')
+ * </code>
+ * <code>
+ * $callchain->dyExampleMethod('alt1', 'alt2')
+ * </code>
+ * then the next call in the call chain will recieve the parameters as if this were called
+ * <code>
+ * $behavior->dyExampleMethod('alt1', 'alt2', 'param3', $callchainobject)
+ * </code>
+ *
+ * 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
+ * <code>
+ * $originalobject->dyExampleMethod('param1', 'param2', 'param3')
+ * </code>
+ * and within the chained dynamic events, this can be called
+ * <code>
+ * class DyBehavior extends TBehavior {
+ * public function dyExampleMethod($param1, $param2, $param3, $callchain)
+ * $callchain->dyExampleMethod($param1, $param2, $param3)
+ * }
+ * {
+ * </code>
+ * 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;
+ }
+} \ No newline at end of file