Total Complexity | 77 |
Total Lines | 308 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like TEventHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use TEventHandler, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
94 | class TEventHandler implements IPriorityProperty, IWeakRetainable, \ArrayAccess, \Countable |
||
95 | { |
||
96 | use TPriorityPropertyTrait; |
||
97 | |||
98 | /** @var mixed The callable event handler being managed. */ |
||
99 | private mixed $_handler; |
||
100 | |||
101 | /** @var mixed The data to feed the handler when invoked. */ |
||
102 | private mixed $_data; |
||
103 | |||
104 | /** @var bool Is the callable object converted into WeakReference? */ |
||
105 | private bool $_weakObject = false; |
||
106 | |||
107 | /** |
||
108 | * Constructs a new TEventHandler and initializes the Event Handler and Data. |
||
109 | * @param mixed $handler The event handler being managed. |
||
110 | * @param mixed $data The data to feed the event handler. |
||
111 | */ |
||
112 | public function __construct(mixed $handler, mixed $data = null) |
||
113 | { |
||
114 | if (!is_callable($handler)) { |
||
115 | throw new TInvalidDataTypeException('eventhandler_not_callable'); |
||
116 | } |
||
117 | |||
118 | if (is_array($handler) && is_object($handler[0]) && !($handler[0] instanceof IWeakRetainable)) { |
||
119 | $handler[0] = WeakReference::create($handler[0]); |
||
120 | $this->_weakObject = true; |
||
121 | } elseif (is_object($handler) && !($handler instanceof Closure) && !($handler instanceof IWeakRetainable)) { |
||
122 | $handler = WeakReference::create($handler); |
||
123 | $this->_weakObject = true; |
||
124 | } |
||
125 | |||
126 | $this->_handler = $handler; |
||
127 | $this->_data = $data; |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * This calls the handler with the specified data. If no $data is specified then |
||
132 | * TEventHandler injects its own data for the event handler. When $data is an array |
||
133 | * and the TEventHandler data is an array, the data is combined by `array_replace`, |
||
134 | * with the function input array data taking precedence over TEventHandler data. |
||
135 | * This allows TEventHandlers to be nested, with children taking precedence. |
||
136 | * @param mixed $sender The sender that raised the event. |
||
137 | * @param mixed $param The parameter that goes with the raised event. |
||
138 | * @param null|mixed $data The data for the managed event handler. default null |
||
139 | * for data from the TEventHandler. |
||
140 | * @param array $argv Any additional function arguments. |
||
141 | * @throws TApplicationException |
||
142 | * @return mixed The result of the event handler. |
||
143 | */ |
||
144 | public function __invoke(mixed $sender = null, mixed $param = null, mixed $data = null, ...$argv): mixed |
||
145 | { |
||
146 | $handler = $this->getHandler(); |
||
147 | if (!$handler) { |
||
148 | throw new TApplicationException('eventhandler_lost_weak_ref'); |
||
149 | } |
||
150 | if (is_array($data) && is_array($this->_data)) { |
||
151 | $data = array_replace($this->_data, $data); |
||
152 | } elseif ($data === null) { |
||
153 | $data = $this->_data; |
||
154 | } |
||
155 | return $handler($sender, $param, $data, ...$argv); |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * Gets the handler being managed. The WeakReference version of the handler can |
||
160 | * be retrieved by passing true to the $weak parameter. When there are nested |
||
161 | * TEventHandlers, this will return the next TEventHandler as an invokable and will |
||
162 | * not link to the last and actual callable handler. |
||
163 | * @param bool $weak Return the handler with WeakReference instead of objects. Default false. |
||
164 | * @return null|array|object|string The callable event handler. |
||
165 | */ |
||
166 | public function getHandler(bool $weak = false): null|string|object|array |
||
167 | { |
||
168 | $handler = $this->_handler; |
||
169 | if (!$weak && $this->_weakObject) { |
||
170 | if (is_array($handler) && is_object($handler[0]) && ($handler[0] instanceof WeakReference)) { |
||
171 | if ($obj = $handler[0]->get()) { |
||
172 | $handler[0] = $obj; |
||
173 | } else { |
||
174 | $handler = null; |
||
175 | } |
||
176 | } elseif (is_object($handler) && ($handler instanceof WeakReference)) { |
||
177 | $handler = $handler->get(); |
||
178 | } |
||
179 | } |
||
180 | return $handler; |
||
181 | } |
||
182 | |||
183 | /** |
||
184 | * This methods checks if the item is the same as the handler. When TEventHandler |
||
185 | * are nested, we check the nested TEventHandler for isSameHandler. |
||
186 | * @param mixed $item |
||
187 | * @param bool $weak |
||
188 | * @return bool Is the $item the same as the managed callable. |
||
189 | */ |
||
190 | public function isSameHandler(mixed $item, bool $weak = false): bool |
||
191 | { |
||
192 | $handler = $this->getHandler($weak); |
||
193 | if ($item === $handler) { |
||
194 | return true; |
||
195 | } |
||
196 | if ($handler instanceof TEventHandler) { |
||
197 | return $handler->isSameHandler($item, $weak); |
||
198 | } |
||
199 | return false; |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * If there is an object referenced in the callable, this method returns the object. |
||
204 | * By default, the WeakReference is re-referenced. The WeakReference can be returned |
||
205 | * by passing true for $weak. This will return Closure and IWeakRetainable objects |
||
206 | * without any WeakReference because they are exempt from conversion into WeakReference. |
||
207 | * When TEventHandlers are nested, this returns the last and actual callable object. |
||
208 | * @param bool $weak Return the callable with WeakReference instead of objects. |
||
209 | * @return ?object The object of the event handler if there is one. |
||
210 | */ |
||
211 | public function getHandlerObject(bool $weak = false): ?object |
||
212 | { |
||
213 | $handler = null; |
||
214 | if (is_array($this->_handler) && is_object($this->_handler[0])) { |
||
215 | $handler = $this->_handler[0]; |
||
216 | } elseif ($this->_handler instanceof TEventHandler) { |
||
217 | return $this->_handler->getHandlerObject($weak); |
||
218 | } elseif (is_object($this->_handler)) { |
||
219 | $handler = $this->_handler; |
||
220 | |||
221 | } |
||
222 | if (!$weak && $this->_weakObject) { |
||
223 | $handler = $handler->get(); |
||
224 | } |
||
225 | return $handler; |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * This checks if the managed handler is still valid. When TEventHandler are nested, |
||
230 | * this returns if the last and actual handler is still valid (from WeakReference). |
||
231 | * @return bool Does the managed event Handler exist and is callable. |
||
232 | */ |
||
233 | public function hasHandler(): bool |
||
234 | { |
||
235 | if ($this->_handler instanceof TEventHandler) { |
||
236 | return $this->_handler->hasHandler(); |
||
237 | } |
||
238 | return $this->getHandler() !== null; |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Returns the data associated with the managed event handler. By passing true to |
||
243 | * $withHandlerData, this will combine the data from nested TEventHandlers when the |
||
244 | * data is in array format. The children data take precedence in the combining |
||
245 | * of data. |
||
246 | * @param bool $withHandlerData |
||
247 | * @return mixed The data associated with the event handler. |
||
248 | */ |
||
249 | public function getData(bool $withHandlerData = false): mixed |
||
250 | { |
||
251 | if ($withHandlerData && ($this->_handler instanceof TEventHandler) && is_array($this->_data) && is_array($data = $this->_handler->getData(true))) { |
||
252 | return array_replace($data, $this->_data); |
||
253 | } |
||
254 | return $this->_data; |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * @param mixed $data The data associated with the event handler. |
||
259 | */ |
||
260 | public function setData(mixed $data): void |
||
261 | { |
||
262 | $this->_data = $data; |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * Does the object contain any WeakReference objects? When there are nested TEventHandler |
||
267 | * this will return the last and actual data from the actual callable. |
||
268 | * @return bool If there are objects as WeakReferences. |
||
269 | */ |
||
270 | public function hasWeakObject(): bool |
||
271 | { |
||
272 | if ($this->_handler instanceof TEventHandler) { |
||
273 | return $this->_handler->hasWeakObject(); |
||
274 | } |
||
275 | return $this->_weakObject; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Returns the number of items in the managed event handler (with data). |
||
280 | * This method is required by \Countable interface. |
||
281 | * @return int The number of items in the managed event handler. |
||
282 | */ |
||
283 | public function count(): int |
||
284 | { |
||
285 | return $this->getCount(); |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * There are 3 items when the handler is an array, and 2 items when the handler |
||
290 | * is an invokable object or string. |
||
291 | * @return int The number of items in the managed event handler (with data). |
||
292 | */ |
||
293 | public function getCount(): int |
||
294 | { |
||
295 | return is_array($this->_handler) ? 3 : 2; |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * These values exist: null (the handler), 0, 2, and conditionally 1 on the handler |
||
300 | * being an array. |
||
301 | * @param mixed $offset The offset to check existence. |
||
302 | * @return bool Does the property exist for the managed event handler (with data). |
||
303 | */ |
||
304 | public function offsetExists(mixed $offset): bool |
||
305 | { |
||
306 | return $offset === null || $offset === 0 || $offset === 2 || (is_array($this->_handler) ? $offset === 1 : 0); |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * This is a convenience method for getting the data of the TEventHandler. |
||
311 | * - Index null will return the {@see getHandler Handler}, |
||
312 | * - Index '0' will return the handler if its a string or object, or the first element |
||
313 | * of the callable array; |
||
314 | * - Index '1' will return the second element of the callable array or null if not |
||
315 | * an array; and |
||
316 | * - Index '2' will return the data associated with the managed event handler. |
||
317 | * If the WeakReference object of the managed event handler is invalid, this will return null |
||
318 | * for all handler offsets (null, 0 and 1). Data will still return properly even |
||
319 | * when the handler is invalid. |
||
320 | * @param mixed $offset Which property of the managed event handler to retrieve. |
||
321 | * @throws TInvalidDataValueException When $offset is not a property of the managed event handler. |
||
322 | * @return mixed The value of the handler, handler elements, or data. |
||
323 | */ |
||
324 | public function offsetGet(mixed $offset): mixed |
||
325 | { |
||
326 | if ($offset === null) { |
||
327 | return $this->getHandler(); |
||
328 | } |
||
329 | if (is_numeric($offset)) { |
||
330 | $offset = (int) $offset; |
||
331 | if ($offset === 2) { |
||
332 | return $this->_data; |
||
333 | } elseif (is_array($this->_handler)) { |
||
334 | if ($offset === 0) { |
||
335 | if ($this->_weakObject) { |
||
336 | return $this->_handler[$offset]->get(); |
||
337 | } |
||
338 | return $this->_handler[$offset]; |
||
339 | } elseif ($offset === 1) { |
||
340 | if ($this->_weakObject && !$this->_handler[0]->get()) { |
||
341 | return null; |
||
342 | } |
||
343 | return $this->_handler[$offset]; |
||
344 | } |
||
345 | } elseif ($offset === 0) { |
||
346 | if ($this->_weakObject) { |
||
347 | return $this->_handler->get(); |
||
348 | } |
||
349 | return $this->_handler; |
||
350 | } elseif ($offset === 1) { |
||
351 | return null; |
||
352 | } |
||
353 | } |
||
354 | throw new TInvalidDataValueException('eventhandler_bad_offset', $offset); |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * This is a convenience method for setting the data of the managed event handler. |
||
359 | * This cannot set the event handler but can set the data. |
||
360 | * - Index '2' will set the data associated with the managed event handler. |
||
361 | * @param mixed $offset Only accepts '2' to set the data associated with the event handler. |
||
362 | * @param mixed $value The data being set. |
||
363 | * @throws TInvalidOperationException When trying to set the handler elements. |
||
364 | * @throws TInvalidDataValueException When $offset is not a property of the managed event handler. |
||
365 | */ |
||
366 | public function offsetSet(mixed $offset, mixed $value): void |
||
367 | { |
||
368 | if (is_numeric($offset)) { |
||
369 | $offset = (int) $offset; |
||
370 | if ($offset === 2) { |
||
371 | $this->setData($value); |
||
372 | return; |
||
373 | } |
||
374 | } |
||
375 | if ($offset === null || is_numeric($offset) && ($offset == 0 || $offset == 1)) { |
||
376 | throw new TInvalidOperationException('eventhandler_no_set_handler', $offset); |
||
377 | } |
||
378 | throw new TInvalidDataValueException('eventhandler_bad_offset', $offset); |
||
379 | } |
||
380 | |||
381 | /** |
||
382 | * This is a convenience method for resetting the data to null. The Handler cannot |
||
383 | * be unset. However, the data can be unset. The only valid index for unsetting |
||
384 | * the handler data is '2'. |
||
385 | * @param mixed $offset Only accepts '2' for the data element. |
||
386 | * @throws TInvalidOperationException When trying to set the handler elements. |
||
387 | * @throws TInvalidDataValueException When $offset is not a property of the managed event handler. |
||
388 | */ |
||
389 | public function offsetUnset(mixed $offset): void |
||
402 | } |
||
403 | } |
||
404 |