Passed
Push — master ( bb2c5a...c1a196 )
by Fabio
06:48
created

TWeakCallableCollection::itemAtIndexInPriority()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
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
 * @package Prado\Collections
9
 */
10
11
namespace Prado\Collections;
12
13
use Prado\Exceptions\TInvalidDataValueException;
14
use WeakReference;
15
16
/**
17
 * TWeakCallableCollection class
18
 *
19
 * TWeakCallableCollection implements a priority ordered list collection of callables.  This extends
20
 * {@link TPriorityList}.  This holds the callables for object event handlers and global event handlers by
21
 * converting all callable objects into a WeakReference (for PHP 7.4+).  TWeakCallableCollection prevents circular
22
 * references in global events that would otherwise block object destruction, and thus removal of the callable
23
 * in __destruct. All data out has the callable objects converted back to the regular object reference in a callable.
24
 *
25
 * @author Brad Anderson <[email protected]>
26
 * @package Prado\Collections
27
 * @since 4.2.0
28
 */
29
class TWeakCallableCollection extends TPriorityList
30
{
31
	/**
32
	 * @var bool wether or not WeakReference is available
33
	 */
34
	private static $_weak = null;
35
	
36
37
	/**
38
	 * Constructor.
39
	 * Initializes the list with an array or an iterable object. Discovers the availability of the
40
	 * {@link WeakReference} object in PHP 7.4.0+.
41
	 * @param null|array|\Iterator $data the initial data. Default is null, meaning no initial data.
42
	 * @param bool $readOnly whether the list is read-only
43
	 * @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...
44
	 * @param int $precision the precision of the numeric priorities
45
	 * @throws TInvalidDataTypeException If data is not null and is neither an array nor an iterator.
46
	 */
47
	public function __construct($data = null, $readOnly = false, $defaultPriority = 10, $precision = 8)
48
	{
49
		if (self::$_weak === null) {
0 ignored issues
show
introduced by
The condition self::_weak === null is always false.
Loading history...
50
			self::$_weak = class_exists('\WeakReference');
51
		}
52
		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

52
		parent::__construct($data, $readOnly, /** @scrutinizer ignore-type */ $defaultPriority, $precision);
Loading history...
53
	}
54
	
55
56
	/**
57
	 * TWeakCallableCollection cannot auto listen to global events or there will be a loop.
58
	 *
59
	 * @return bool returns false
60
	 */
61
	public function getAutoGlobalListen()
62
	{
63
		return false;
64
	}
65
	
66
	/**
67
	 * returns whether or not WeakReference is enabled, thus the PHP version is over 7.4.0
68
	 * @return bool is WeakReference available
69
	 */
70
	public static function getWeakReferenceEnabled()
71
	{
72
		return self::$_weak;
73
	}
74
	
75
	
76
	/**
77
	 * If WeakReference is available, converts the $items array of callable that
78
	 * has WeakReferences rather than the actual object back into a regular callable.
79
	 * @param array $items an array of callable where objects are WeakReference
80
	 * @return array array of callable where WeakReference are converted back to the object
81
	 */
82
	protected function filterItemsForOutput($items)
83
	{
84
		if (!self::$_weak) {
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
	/**
146
	 * This flattens the priority list into a flat array [0,...,n-1]. This is needed to filter the output.
147
	 * @return array array of items in the list in priority and index order
148
	 */
149
	protected function flattenPriorities()
150
	{
151
		return $this->filterItemsForOutput(parent::flattenPriorities());
152
	}
153
154
155
	/**
156
	 * This flattens the priority list into a flat array [0,...,n-1], but
157
	 * leaves the objects in the array as WeakReference rather than standard
158
	 * callable.
159
	 * @return array array of items in the list in priority and index order,
160
	 *    where objects are WeakReference
161
	 */
162
	protected function flattenPrioritiesWeak()
163
	{
164
		return parent::flattenPriorities();
165
	}
166
167
168
	/**
169
	 * Returns the item at the index of a flattened priority list. This is needed to filter the output.
170
	 * {@link offsetGet} calls this method.
171
	 * @param int $index the index of the item to get
172
	 * @throws TInvalidDataValueException Issued when the index is invalid
173
	 * @return mixed the element at the offset
174
	 */
175
	public function itemAt($index)
176
	{
177
		if ($index >= 0 && $index < $this->getCount()) {
178
			$arr = $this->flattenPrioritiesWeak();
179
			return $this->filterItemForOutput($arr[$index]);
180
		} else {
181
			throw new TInvalidDataValueException('list_index_invalid', $index);
182
		}
183
	}
184
185
	/**
186
	 * Gets all the items at a specific priority. This is needed to filter the output.
187
	 * @param null|numeric $priority priority of the items to get.  Defaults to null, filled
188
	 * in with the default priority, if left blank.
189
	 * @return array all items at priority in index order, null if there are no items at that priority
190
	 */
191
	public function itemsAtPriority($priority = null)
192
	{
193
		return $this->filterItemsForOutput(parent::itemsAtPriority($priority));
194
	}
195
196
	/**
197
	 * Returns the item at an index within a priority. This is needed to filter the output.
198
	 * @param int $index the index into the list of items at priority
199
	 * @param numeric $priority the priority which to index.  no parameter or null will result in the default priority
200
	 * @return mixed the element at the offset, false if no element is found at the offset
201
	 */
202
	public function itemAtIndexInPriority($index, $priority = null)
203
	{
204
		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

204
		return $this->filterItemForOutput(/** @scrutinizer ignore-type */ parent::itemAtIndexInPriority($index, $priority));
Loading history...
205
	}
206
207
	/**
208
	 * Inserts an item at the specified index within a priority.  This is needed to filter the input.
209
	 * @param mixed $item item to add within the list.
210
	 * @param int $index index within the priority to add the item, defaults to false which appends the item at the priority
211
	 * @param null|numeric $priority priority of the item.  defaults to null, which sets it to the default priority
212
	 * @param bool $preserveCache preserveCache specifies if this is a special quick function or not. This defaults to false.
213
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
214
	 * @throws TInvalidOperationException if the list is read-only
215
	 */
216
	public function insertAtIndexInPriority($item, $index = false, $priority = null, $preserveCache = false)
217
	{
218
		return parent::insertAtIndexInPriority($this->filterItemForInput($item, true), $index, $priority, $preserveCache);
219
	}
220
221
	/**
222
	 * Removes the item at a specific index within a priority.  This is needed to filter the output.
223
	 * @param int $index index of item to remove within the priority.
224
	 * @param null|numeric $priority priority of the item to remove, defaults to null, or left blank, it is then set to the default priority
225
	 * @throws TInvalidDataValueException If the item does not exist
226
	 * @return mixed the removed item.
227
	 */
228
	public function removeAtIndexInPriority($index, $priority = null)
229
	{
230
		return $this->filterItemForOutput(parent::removeAtIndexInPriority($index, $priority));
231
	}
232
233
	/**
234
	 * This is needed to filter the input.
235
	 * @param mixed $item item being indexed.
236
	 * @return int the index of the item in the flattened list (0 based), -1 if not found.
237
	 */
238
	public function indexOf($item)
239
	{
240
		if (($index = array_search($this->filterItemForInput($item), $this->flattenPrioritiesWeak(), true)) === false) {
241
			return -1;
242
		} else {
243
			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...
244
		}
245
	}
246
247
	/**
248
	 * Returns the priority of a particular item.  This is needed to filter the input.
249
	 * @param mixed $item the item to look for within the list.
250
	 * @param bool $withindex this specifies if the full positional data of the item within the list is returned.
251
	 * 		This defaults to false, if no parameter is provided, so only provides the priority number of the item by default.
252
	 * @return array|numeric the priority of the item in the list, false if not found.
253
	 *   if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex,
254
	 * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex]
255
	 */
256
	public function priorityOf($item, $withindex = false)
257
	{
258
		return parent::priorityOf($this->filterItemForInput($item), $withindex);
259
	}
260
261
262
	/**
263
	 *  This is needed to filter the output.
264
	 * @return array the array of priorities keys with values of arrays of callables.
265
	 * The priorities are sorted so important priorities, lower numerics, are first.
266
	 */
267
	public function toPriorityArray()
268
	{
269
		$result = [];
270
		foreach (parent::toPriorityArray() as $i => $v) {
271
			$result[$i] = $this->filterItemsForOutput($v);
272
		}
273
		return $result;
274
	}
275
276
277
	/**
278
	 * @return array the array of priorities keys with values of arrays of callables with
279
	 * WeakReference rather than objects.  The priorities are sorted so important priorities,
280
	 * lower numerics, are first.
281
	 */
282
	public function toPriorityArrayWeak()
283
	{
284
		return parent::toPriorityArray();
285
	}
286
287
	/**
288
	 * Combines the map elements which have a priority below the parameter value.  This is needed to filter the output.
289
	 * @param numeric $priority the cut-off priority.  All items of priority less than this are returned.
290
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: false, not inclusive.
291
	 * @return array the array of priorities keys with values of arrays of items that are below a specified priority.
292
	 *  The priorities are sorted so important priorities, lower numerics, are first.
293
	 */
294
	public function toArrayBelowPriority($priority, $inclusive = false)
295
	{
296
		return $this->filterItemsForOutput(parent::toArrayBelowPriority($priority, $inclusive));
297
	}
298
299
	/**
300
	 * Combines the map elements which have a priority above the parameter value. This is needed to filter the output.
301
	 * @param numeric $priority the cut-off priority.  All items of priority greater than this are returned.
302
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: true, inclusive.
303
	 * @return array the array of priorities keys with values of arrays of items that are above a specified priority.
304
	 *  The priorities are sorted so important priorities, lower numerics, are first.
305
	 */
306
	public function toArrayAbovePriority($priority, $inclusive = true)
307
	{
308
		return $this->filterItemsForOutput(parent::toArrayAbovePriority($priority, $inclusive));
309
	}
310
}
311