Issues (1474)

framework/Util/TCallChain.php (1 issue)

Severity
1
<?php
2
3
/**
4
 * TCallChain class file.
5
 *
6
 * @author Brad Anderson <[email protected]>
7
 * @link https://github.com/pradosoft/prado
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 */
10
11
namespace Prado\Util;
12
13
use Prado\Exceptions\TApplicationException;
14
use Prado\Collections\TList;
15
16
/**
17
 * TCallChain is a recursive event calling mechanism.  This class implements
18
 * the {@see \Prado\Util\IDynamicMethods} class so that any 'dy' event calls can be caught
19
 * and patched through to the intended recipient
20
 * @author Brad Anderson <[email protected]>
21
 * @since 3.2.3
22
 */
23
class TCallChain extends TList implements IDynamicMethods
24
{
25
	/**
26
	 *	@var \ArrayIterator for moving through the chained method calls
27
	 */
28
	private $_iterator;
29
30
	/**
31
	 *	@var string the method name of the call chain
32
	 */
33
	private $_method;
34
35
	/**
36
	 * This initializes the list and the name of the method to be called
37
	 *	@param string $method the name of the function call
38 27
	 */
39
	public function __construct($method)
40 27
	{
41 27
		$this->_method = $method;
42 27
		parent::__construct();
43
	}
44
45
46
	/**
47
	 * This initializes the list and the name of the method to be called
48
	 * @param array|string $method this is a callable function as a string or array with
49
	 *	the object and method name as string
50
	 * @param array $args The array of arguments to the function call chain
51 3
	 */
52
	public function addCall($method, $args)
53 3
	{
54 3
		$this->add([$method, $args]);
55
	}
56
57
	/**
58
	 * This method calls the next Callable in the list.  All of the method arguments
59
	 * coming into this method are substituted into the original method argument of
60
	 * call in the chain.
61
	 *
62
	 * If the original method call has these parameters
63
	 * ```php
64
	 * $originalobject->dyExampleMethod('param1', 'param2', 'param3')
65
	 * ```
66
	 * ```php
67
	 * $callchain->dyExampleMethod('alt1', 'alt2')
68
	 * ```
69
	 * then the next call in the call chain will recieve the parameters as if this were called
70
	 * ```php
71
	 * $behavior->dyExampleMethod('alt1', 'alt2', 'param3', $callchainobject)
72
	 * ```
73
	 *
74
	 * When dealing with {@see IClassBehaviors}, the first parameter of the stored argument
75
	 * list in 'dy' event calls is always the object containing the behavior.  This modifies
76
	 * the parameter replacement mechanism slightly to leave the object containing the behavior
77
	 * alone and only replacing the other parameters in the argument list.  As per {@see __call},
78
	 * any calls to a 'dy' event do not need the object containing the behavior as the addition of
79
	 * the object to the argument list as the first element is automatic for IClassBehaviors.
80
	 *
81
	 * The last parameter of the method parameter list for any callable in the call chain
82
	 * will be the TCallChain object itself.  This is so that any behavior implementing
83
	 * these calls will have access to the call chain.  Each callable should either call
84
	 * the TCallChain call method internally for direct chaining or call the method being
85
	 * chained (in which case the dynamic handler will pass through to this call method).
86
	 *
87
	 * If the dynamic intra object/behavior event is not called in the behavior implemented
88
	 * dynamic method, it will return to this method and call the following behavior
89
	 * implementation so as no behavior with an implementation of the dynamic event is left
90
	 * uncalled.  This does break the call chain though and will not act as a "parameter filter".
91
	 *
92
	 * When there are no handlers or no handlers left, it returns the first parameter of the
93
	 * argument list.
94
	 *
95 3
	 * @param array $args The parameters to send the function chain.
96
	 */
97 3
	public function call(...$args)
98 3
	{
99
		if ($this->getCount() === 0) {
100
			return $args[0] ?? null;
101
		}
102 3
103 3
		if (!$this->_iterator) {
104 3
			$this->_iterator = new \ArrayIterator($this->toArray());
105
		}
106 3
		if ($this->_iterator->valid()) {
107
			do {
108 3
				$handler = $this->_iterator->current();
109 3
				$this->_iterator->next();
110 3
				if (is_array($handler[0]) && $handler[0][0] instanceof IClassBehavior) {
111 2
					array_splice($handler[1], 1, count($args), $args);
112
				} else {
113 3
					array_splice($handler[1], 0, count($args), $args);
114
				}
115 3
				$handler[1][] = $this;
116 3
				$result = call_user_func_array($handler[0], $handler[1]);
117 3
			} while ($this->_iterator->valid());
118
		} else {
119 3
			$result = $args[0] ?? null;
120
		}
121 3
		return $result;
122
	}
123
124
125
	/**
126
	 * This catches all the unpatched dynamic events.  When the method call matches the
127
	 * call chain method, it passes the arguments to the original __call (of the dynamic
128
	 * event being unspecified in TCallChain) and funnels into the method {@see call},
129
	 * so the next dynamic event handler can be called.
130
	 * If the original method call has these parameters
131
	 * ```php
132
	 * $originalobject->dyExampleMethod('param1', 'param2', 'param3')
133
	 * ```
134
	 * and within the chained dynamic events, this can be called
135
	 * ```php
136
	 * class DyBehavior extends TBehavior {
137
	 * public function dyExampleMethod($param1, $param2, $param3, $callchain)
138
	 * $callchain->dyExampleMethod($param1, $param2, $param3)
139
	 * }
140
	 * }
141
	 * ```
142
	 * to call the next event in the chain.
143
	 * @param string $method method name of the unspecified object method
144
	 * @param array $args arguments to the unspecified object method
145
	 */
146 3
	public function __dycall($method, $args)
147
	{
148 3
		if ($this->_method == $method) {
149 3
			return call_user_func_array([$this, 'call'], $args);
150
		} else {
151
			throw new TApplicationException('callchain_bad_dynamic_event', $method, $this->_method);
152
		}
153
		return null;
0 ignored issues
show
return null is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
154
	}
155
}
156