Passed
Push — php8 ( bd75dc...d8afb4 )
by Fabio
08:15
created

TWeakCallableCollection   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 69
dl 0
loc 303
rs 6.96
c 1
b 0
f 0
wmc 53

20 Methods

Rating   Name   Duplication   Size   Complexity  
B filterItemForOutput() 0 11 7
A flattenPriorities() 0 3 1
B filterItemForInput() 0 15 9
A priorityOf() 0 3 1
A getAutoGlobalListen() 0 3 1
A __construct() 0 6 2
A removeAtIndexInPriority() 0 3 1
B filterItemsForOutput() 0 19 9
A getWeakReferenceEnabled() 0 3 1
A indexOf() 0 6 2
A flattenPrioritiesWeak() 0 3 1
A _getZappableSleepProps() 0 6 1
A itemAt() 0 7 3
A toPriorityArray() 0 7 2
A toArrayBelowPriority() 0 3 1
B insertAtIndexInPriority() 0 11 7
A toArrayAbovePriority() 0 3 1
A itemsAtPriority() 0 3 1
A toPriorityArrayWeak() 0 3 1
A itemAtIndexInPriority() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like TWeakCallableCollection 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 TWeakCallableCollection, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * TWeakCallableCollection class
4
 *
5
 * @author Brad Anderson <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Collections;
11
12
use Prado\Exceptions\TInvalidDataValueException;
13
14
/**
15
 * TWeakCallableCollection class
16
 *
17
 * TWeakCallableCollection implements a priority ordered list collection of callables.  This extends
18
 * {@link TPriorityList}.  This holds the callables for object event handlers and global event handlers by
19
 * converting all callable objects into a WeakReference (for PHP 7.4+).  TWeakCallableCollection prevents circular
20
 * references in global events that would otherwise block object destruction, and thus removal of the callable
21
 * in __destruct. All data out has the callable objects converted back to the regular object reference in a callable.
22
 *
23
 * @author Brad Anderson <[email protected]>
24
 * @since 4.2.0
25
 */
26
class TWeakCallableCollection extends TPriorityList
27
{
28
	/**
29
	 * @var bool wether or not WeakReference is available
30
	 */
31
	private static $_weak;
32
33
34
	/**
35
	 * Constructor.
36
	 * Initializes the list with an array or an iterable object. Discovers the availability of the
37
	 * {@link WeakReference} object in PHP 7.4.0+.
38
	 * @param null|array|\Iterator $data the initial data. Default is null, meaning no initial data.
39
	 * @param bool $readOnly whether the list is read-only
40
	 * @param numeric $defaultPriority the default priority of items without specified priorities.
0 ignored issues
show
Bug introduced by
The type Prado\Collections\numeric was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
41
	 * @param int $precision the precision of the numeric priorities
42
	 * @throws \Prado\Exceptions\TInvalidDataTypeException If data is not null and is neither an array nor an iterator.
43
	 */
44
	public function __construct($data = null, $readOnly = false, $defaultPriority = 10, $precision = 8)
45
	{
46
		if (self::$_weak === null) {
0 ignored issues
show
introduced by
The condition self::_weak === null is always false.
Loading history...
47
			self::$_weak = class_exists('\WeakReference', false);
48
		}
49
		parent::__construct($data, $readOnly, $defaultPriority, $precision);
0 ignored issues
show
Bug introduced by
It seems like $defaultPriority can also be of type integer; however, parameter $defaultPriority of Prado\Collections\TPriorityList::__construct() does only seem to accept Prado\Collections\numeric, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

49
		parent::__construct($data, $readOnly, /** @scrutinizer ignore-type */ $defaultPriority, $precision);
Loading history...
50
	}
51
52
53
	/**
54
	 * TWeakCallableCollection cannot auto listen to global events or there will be a loop.
55
	 *
56
	 * @return bool returns false
57
	 */
58
	public function getAutoGlobalListen()
59
	{
60
		return false;
61
	}
62
63
	/**
64
	 * returns whether or not WeakReference is enabled, thus the PHP version is over 7.4.0
65
	 * @return bool is WeakReference available
66
	 */
67
	public static function getWeakReferenceEnabled()
68
	{
69
		return self::$_weak;
70
	}
71
72
73
	/**
74
	 * If WeakReference is available, converts the $items array of callable that
75
	 * has WeakReferences rather than the actual object back into a regular callable.
76
	 * @param array $items an array of callable where objects are WeakReference
77
	 * @return array array of callable where WeakReference are converted back to the object
78
	 */
79
	protected function filterItemsForOutput($items)
80
	{
81
		if (!self::$_weak) {
82
			return $items;
83
		}
84
		if (!is_array($items)) {
0 ignored issues
show
introduced by
The condition is_array($items) is always true.
Loading history...
85
			return $items;
86
		}
87
		$result = [];
88
		foreach ($items as $i => $handler) {
89
			if (is_array($handler) && is_object($handler[0]) && is_a($handler[0], '\WeakReference')) {
90
				$result[] = [$handler[0]->get(), $handler[1]];
91
			} elseif (is_object($handler) && is_a($handler, '\WeakReference')) {
92
				$result[] = $handler->get();
93
			} else {
94
				$result[] = $handler;
95
			}
96
		}
97
		return $result;
98
	}
99
100
101
	/**
102
	 * Converts the $handler callable that has WeakReferences rather than the actual object
103
	 * back into a regular callable.
104
	 * @param callable $handler but the $handler or $handler[0] is a WeakReference
105
	 * @return mixed callable by removing the WeakReferences
106
	 */
107
	protected function filterItemForOutput($handler)
108
	{
109
		if (!self::$_weak) {
110
			return $handler;
111
		}
112
		if (is_array($handler) && is_object($handler[0]) && is_a($handler[0], '\WeakReference')) {
113
			return [$handler[0]->get(), $handler[1]];
114
		} elseif (is_object($handler) && is_a($handler, '\WeakReference')) {
115
			return $handler->get();
116
		}
117
		return $handler;
118
	}
119
120
121
	/**
122
	 * Converts the $handler callable into a WeakReference version for storage
123
	 * @param callable $handler callable to convert into a WeakReference version
124
	 * @param bool $validate whether or not to validate the input as a callable
125
	 * @return mixed callable but with the objects as WeakReferences
126
	 */
127
	protected function filterItemForInput($handler, $validate = false)
128
	{
129
		if ($validate && !is_callable($handler, false)) {
130
			throw new TInvalidDataValueException('weakcallablecollection_callable_required');
131
		}
132
		if (!self::$_weak) {
133
			return $handler;
134
		}
135
		if (is_array($handler) && is_object($handler[0]) && !is_a($handler[0], '\WeakReference')) {
136
			return [\WeakReference::create($handler[0]), $handler[1]];
137
		} elseif (is_object($handler) && !is_a($handler, '\WeakReference')) {
138
			return \WeakReference::create($handler);
139
		}
140
141
		return $handler;
142
	}
143
144
	/**
145
	 * This flattens the priority list into a flat array [0,...,n-1]. This is needed to filter the output.
146
	 * @return array array of items in the list in priority and index order
147
	 */
148
	protected function flattenPriorities(): array
149
	{
150
		return $this->filterItemsForOutput(parent::flattenPriorities());
151
	}
152
153
	/**
154
	 * This flattens the priority list into a flat array [0,...,n-1], but
155
	 * leaves the objects in the array as WeakReference rather than standard
156
	 * callable.
157
	 * @return array array of items in the list in priority and index order,
158
	 *    where objects are WeakReference
159
	 */
160
	protected function flattenPrioritiesWeak(): array
161
	{
162
		return parent::flattenPriorities();
163
	}
164
165
166
	/**
167
	 * Returns the item at the index of a flattened priority list. This is needed to filter the output.
168
	 * {@link offsetGet} calls this method.
169
	 * @param int $index the index of the item to get
170
	 * @throws TInvalidDataValueException Issued when the index is invalid
171
	 * @return mixed the element at the offset
172
	 */
173
	public function itemAt($index)
174
	{
175
		if ($index >= 0 && $index < $this->getCount()) {
176
			$arr = $this->flattenPrioritiesWeak();
177
			return $this->filterItemForOutput($arr[$index]);
178
		} else {
179
			throw new TInvalidDataValueException('list_index_invalid', $index);
180
		}
181
	}
182
183
	/**
184
	 * Gets all the items at a specific priority. This is needed to filter the output.
185
	 * @param null|numeric $priority priority of the items to get.  Defaults to null, filled
186
	 * in with the default priority, if left blank.
187
	 * @return ?array all items at priority in index order, null if there are no items at that priority
188
	 */
189
	public function itemsAtPriority($priority = null): ?array
190
	{
191
		return $this->filterItemsForOutput(parent::itemsAtPriority($priority));
192
	}
193
194
	/**
195
	 * Returns the item at an index within a priority. This is needed to filter the output.
196
	 * @param int $index the index into the list of items at priority
197
	 * @param numeric $priority the priority which to index.  no parameter or null will result in the default priority
198
	 * @return mixed the element at the offset, false if no element is found at the offset
199
	 */
200
	public function itemAtIndexInPriority($index, $priority = null)
201
	{
202
		return $this->filterItemForOutput(parent::itemAtIndexInPriority($index, $priority));
0 ignored issues
show
Bug introduced by
It seems like parent::itemAtIndexInPriority($index, $priority) can also be of type false; however, parameter $handler of Prado\Collections\TWeakC...::filterItemForOutput() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

202
		return $this->filterItemForOutput(/** @scrutinizer ignore-type */ parent::itemAtIndexInPriority($index, $priority));
Loading history...
203
	}
204
205
	/**
206
	 * Inserts an item at the specified index within a priority.  This is needed to filter the input.
207
	 * @param mixed $item item to add within the list.
208
	 * @param false|int $index index within the priority to add the item, defaults to false which appends the item at the priority
209
	 * @param null|numeric $priority priority of the item.  defaults to null, which sets it to the default priority
210
	 * @param bool $preserveCache preserveCache specifies if this is a special quick function or not. This defaults to false.
211
	 * @throws \Prado\Exceptions\TInvalidDataValueException If the index specified exceeds the bound
212
	 * @throws \Prado\Exceptions\TInvalidOperationException if the list is read-only
213
	 */
214
	public function insertAtIndexInPriority($item, $index = false, $priority = null, $preserveCache = false)
215
	{
216
		$itemPriority = null;
217
		if (($isPriorityItem = ($item instanceof IPriorityItem)) && ($priority === null || !is_numeric($priority))) {
218
			$itemPriority = $priority = $item->getPriority();
219
		}
220
		$priority = $this->ensurePriority($priority);
221
		if (($item instanceof IPriorityCapture) && (!$isPriorityItem || $itemPriority !== $priority)) {
222
			$item->setPriority($priority);
0 ignored issues
show
Bug introduced by
$priority of type string is incompatible with the type Prado\Collections\numeric expected by parameter $value of Prado\Collections\IPriorityCapture::setPriority(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

222
			$item->setPriority(/** @scrutinizer ignore-type */ $priority);
Loading history...
223
		}
224
		return parent::insertAtIndexInPriority($this->filterItemForInput($item, true), $index, $priority, $preserveCache);
0 ignored issues
show
Bug introduced by
It seems like $item can also be of type Prado\Collections\IPriorityCapture and Prado\Collections\IPriorityItem and Prado\Collections\IPrior...ctions\IPriorityCapture; however, parameter $handler of Prado\Collections\TWeakC...n::filterItemForInput() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

224
		return parent::insertAtIndexInPriority($this->filterItemForInput(/** @scrutinizer ignore-type */ $item, true), $index, $priority, $preserveCache);
Loading history...
Bug introduced by
$priority of type string is incompatible with the type Prado\Collections\numeric|null expected by parameter $priority of Prado\Collections\TPrior...sertAtIndexInPriority(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

224
		return parent::insertAtIndexInPriority($this->filterItemForInput($item, true), $index, /** @scrutinizer ignore-type */ $priority, $preserveCache);
Loading history...
225
	}
226
227
	/**
228
	 * Removes the item at a specific index within a priority.  This is needed to filter the output.
229
	 * @param int $index index of item to remove within the priority.
230
	 * @param null|numeric $priority priority of the item to remove, defaults to null, or left blank, it is then set to the default priority
231
	 * @throws TInvalidDataValueException If the item does not exist
232
	 * @return mixed the removed item.
233
	 */
234
	public function removeAtIndexInPriority($index, $priority = null)
235
	{
236
		return $this->filterItemForOutput(parent::removeAtIndexInPriority($index, $priority));
237
	}
238
239
	/**
240
	 * This is needed to filter the input.
241
	 * @param mixed $item item being indexed.
242
	 * @return int the index of the item in the flattened list (0 based), -1 if not found.
243
	 */
244
	public function indexOf($item)
245
	{
246
		if (($index = array_search($this->filterItemForInput($item), $this->flattenPrioritiesWeak(), true)) === false) {
247
			return -1;
248
		} else {
249
			return $index;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $index also could return the type string which is incompatible with the documented return type integer.
Loading history...
250
		}
251
	}
252
253
	/**
254
	 * Returns the priority of a particular item.  This is needed to filter the input.
255
	 * @param mixed $item the item to look for within the list.
256
	 * @param bool $withindex this specifies if the full positional data of the item within the list is returned.
257
	 * 		This defaults to false, if no parameter is provided, so only provides the priority number of the item by default.
258
	 * @return array|numeric the priority of the item in the list, false if not found.
259
	 *   if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex,
260
	 * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex]
261
	 */
262
	public function priorityOf($item, $withindex = false)
263
	{
264
		return parent::priorityOf($this->filterItemForInput($item), $withindex);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::priorityO...put($item), $withindex) could also return false which is incompatible with the documented return type Prado\Collections\numeric|array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
265
	}
266
267
	/**
268
	 *  This is needed to filter the output.
269
	 * @return array the array of priorities keys with values of arrays of callables.
270
	 * The priorities are sorted so important priorities, lower numerics, are first.
271
	 */
272
	public function toPriorityArray(): array
273
	{
274
		$result = [];
275
		foreach (parent::toPriorityArray() as $i => $v) {
276
			$result[$i] = $this->filterItemsForOutput($v);
277
		}
278
		return $result;
279
	}
280
281
	/**
282
	 * @return array the array of priorities keys with values of arrays of callables with
283
	 * WeakReference rather than objects.  The priorities are sorted so important priorities,
284
	 * lower numerics, are first.
285
	 */
286
	public function toPriorityArrayWeak()
287
	{
288
		return parent::toPriorityArray();
289
	}
290
291
	/**
292
	 * Combines the map elements which have a priority below the parameter value.  This is needed to filter the output.
293
	 * @param numeric $priority the cut-off priority.  All items of priority less than this are returned.
294
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: false, not inclusive.
295
	 * @return array the array of priorities keys with values of arrays of items that are below a specified priority.
296
	 *  The priorities are sorted so important priorities, lower numerics, are first.
297
	 */
298
	public function toArrayBelowPriority($priority, $inclusive = false): array
299
	{
300
		return $this->filterItemsForOutput(parent::toArrayBelowPriority($priority, $inclusive));
301
	}
302
303
	/**
304
	 * Combines the map elements which have a priority above the parameter value. This is needed to filter the output.
305
	 * @param numeric $priority the cut-off priority.  All items of priority greater than this are returned.
306
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: true, inclusive.
307
	 * @return array the array of priorities keys with values of arrays of items that are above a specified priority.
308
	 *  The priorities are sorted so important priorities, lower numerics, are first.
309
	 */
310
	public function toArrayAbovePriority($priority, $inclusive = true): array
311
	{
312
		return $this->filterItemsForOutput(parent::toArrayAbovePriority($priority, $inclusive));
313
	}
314
315
	/**
316
	 * Returns an array with the names of all variables of this object that should NOT be serialized
317
	 * because their value is the default one or useless to be cached for the next page loads.
318
	 * Reimplement in derived classes to add new variables, but remember to  also to call the parent
319
	 * implementation first.
320
	 * @param array $exprops by reference
321
	 * @since 4.2.3
322
	 */
323
	protected function _getZappableSleepProps(&$exprops)
324
	{
325
		$c = $this->_c;
326
		$this->_c = 0;
327
		parent::_getZappableSleepProps($exprops);
328
		$this->_c = $c;
329
	}
330
}
331