Passed
Pull Request — master (#890)
by
unknown
04:44 queued 25s
created

TPriorityList::_getZappableSleepProps()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 8
nop 1
dl 0
loc 12
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 4
1
<?php
2
/**
3
 * TPriorityList 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\TInvalidDataTypeException;
13
use Prado\Exceptions\TInvalidDataValueException;
14
use Prado\Exceptions\TInvalidOperationException;
15
use Prado\TPropertyValue;
16
17
/**
18
 * TPriorityList class
19
 *
20
 * TPriorityList implements a priority ordered list collection class.  It allows you to specify
21
 * any numeric for priorities down to a specific precision.  The lower the numeric, the high the priority of the item in the
22
 * list.  Thus -10 has a higher priority than -5, 0, 10 (the default), 18, 10005, etc.  Per {@link round}, precision may be negative and
23
 * thus rounding can go by 10, 100, 1000, etc, instead of just .1, .01, .001, etc. The default precision allows for 8 decimal
24
 * places. There is also a default priority of 10, if no different default priority is specified or no item specific priority is indicated.
25
 * If you replace TList with this class it will  work exactly the same with items inserted set to the default priority, until you start
26
 * using different priorities than the default priority.
27
 *
28
 * As you access the PHP array features of this class, it flattens and caches the results.  If at all possible, this
29
 * will keep the cache fresh even when manipulated.  If this is not possible the cache is cleared.
30
 * When an array of items are needed and the cache is outdated, the cache is recreated from the items and their priorities
31
 *
32
 * You can access, append, insert, remove an item by using
33
 * {@link itemAt}, {@link add}, {@link insertAt}, and {@link remove}.
34
 * To get the number of the items in the list, use {@link getCount}.
35
 * TPriorityList can also be used like a regular array as follows,
36
 * <code>
37
 * $list[]=$item;  // append with the default priority.  It may not be the last item if other items in the list are prioritized after the default priority
38
 * $list[$index]=$item; // $index must be between 0 and $list->Count-1.  This sets the element regardless of priority.  Priority stays the same.
39
 * $list[$index]=$item; // $index is $list->Count.  This appends the item to the end of the list with the same priority as the last item in the list.
40
 * unset($list[$index]); // remove the item at $index
41
 * if(isset($list[$index])) // if the list has an item at $index
42
 * foreach($list as $index=>$item) // traverse each item in the list in proper priority order and add/insert order
43
 * $n=count($list); // returns the number of items in the list
44
 * </code>
45
 *
46
 * To extend TPriorityList for doing your own operations with each addition or removal,
47
 * override {@link insertAtIndexInPriority()} and {@link removeAtIndexInPriority()} and then call the parent.
48
 *
49
 * @author Brad Anderson <[email protected]>
50
 * @since 3.2a
51
 */
52
class TPriorityList extends TList
53
{
54
	/**
55
	 * @var bool indicates if the _d is currently ordered.
56
	 */
57
	protected bool $_o = false;
58
	/**
59
	 * @var null|array cached flattened internal data storage
60
	 */
61
	protected ?array $_fd = null;
62
	/**
63
	 * @var numeric 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...
64
	 */
65
	private $_dp = 10;
66
	/**
67
	 * @var int the precision of the numeric priorities within this priority list.
68
	 */
69
	private int $_p = 8;
70
71
	/**
72
	 * Constructor.
73
	 * Initializes the list with an array or an iterable object.
74
	 * @param null|array|\Iterator $data the initial data. Default is null, meaning no initial data.
75
	 * @param bool $readOnly whether the list is read-only
76
	 * @param numeric $defaultPriority the default priority of items without specified priorities.
77
	 * @param int $precision the precision of the numeric priorities
78
	 * @throws TInvalidDataTypeException If data is not null and is neither an array nor an iterator.
79
	 */
80
	public function __construct($data = null, $readOnly = false, $defaultPriority = 10, $precision = 8)
81
	{
82
		$this->setPrecision($precision);
83
		$this->setDefaultPriority($defaultPriority);
0 ignored issues
show
Bug introduced by
It seems like $defaultPriority can also be of type integer; however, parameter $value of Prado\Collections\TPrior...t::setDefaultPriority() 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

83
		$this->setDefaultPriority(/** @scrutinizer ignore-type */ $defaultPriority);
Loading history...
84
		parent::__construct($data, $readOnly);
85
	}
86
87
	/**
88
	 * @return numeric gets the default priority of inserted items without a specified priority
89
	 */
90 90
	public function getDefaultPriority()
91
	{
92 90
		return $this->_dp;
93 90
	}
94 25
95
	/**
96 90
	 * This must be called internally or when instantiated.
97 90
	 * @param numeric $value sets the default priority of inserted items without a specified priority
98 90
	 */
99 90
	protected function setDefaultPriority($value)
100
	{
101
		$this->_dp = (string) round(TPropertyValue::ensureFloat($value), $this->_p);
0 ignored issues
show
Documentation Bug introduced by
It seems like (string)round(Prado\TPro...oat($value), $this->_p) of type string is incompatible with the declared type Prado\Collections\numeric of property $_dp.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
102
	}
103
104
	/**
105
	 * @return int The precision of numeric priorities, defaults to 8
106 3
	 */
107
	public function getPrecision(): int
108 3
	{
109
		return $this->_p;
110
	}
111
112
	/**
113
	 * This must be called internally or when instantiated.
114
	 * @param int $value The precision of numeric priorities.
115 133
	 */
116
	protected function setPrecision($value): void
117 133
	{
118
		$this->_p = TPropertyValue::ensureInteger($value);
119
	}
120
121
	/**
122
	 * Taken an input Priority and ensures its value.
123
	 * Sets the default $priority when none is set,
124
	 * then rounds to the proper precision and makes
125 2
	 * into a string.
126
	 * @param mixed $priority
127 2
	 * @return string the priority in string format
128 2
	 */
129
	protected function ensurePriority($priority): string
130 2
	{
131
		if ($priority === null || !is_numeric($priority)) {
132 2
			$priority = $this->getDefaultPriority();
133
		}
134
		return (string) round((float) $priority, $this->_p);
135 2
	}
136
137
138
	/**
139
	 * This orders the priority list internally.
140
	 */
141 116
	protected function sortPriorities(): void
142
	{
143 116
		if (!$this->_o) {
144
			ksort($this->_d, SORT_NUMERIC);
145
			$this->_o = true;
146
		}
147
	}
148
149
	/**
150 90
	 * This flattens the priority list into a flat array [0,...,n-1]
151
	 * @return array array of items in the list in priority and index order
152 90
	 */
153 90
	protected function flattenPriorities(): array
154
	{
155
		if (is_array($this->_fd)) {
156
			return $this->_fd;
157
		}
158 1
159
		$this->sortPriorities();
160 1
		$this->_fd = [];
161
		foreach ($this->_d as $priority => $itemsatpriority) {
162
			$this->_fd = array_merge($this->_fd, $itemsatpriority);
0 ignored issues
show
Bug introduced by
$this->_fd of type null is incompatible with the type array expected by parameter $arrays of array_merge(). ( Ignorable by Annotation )

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

162
			$this->_fd = array_merge(/** @scrutinizer ignore-type */ $this->_fd, $itemsatpriority);
Loading history...
163
		}
164
		return $this->_fd;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_fd returns the type null which is incompatible with the type-hinted return array.
Loading history...
165
	}
166
167 90
	/**
168
	 * This returns a list of the priorities within this list, ordered lowest to highest.
169 90
	 * @return array the array of priority numerics in decreasing priority order
170 90
	 */
171
	public function getPriorities(): array
172
	{
173
		$this->sortPriorities();
174
		return array_keys($this->_d);
175
	}
176
177 2
	/**
178
	 * Gets the number of items at a priority within the list
179 2
	 * @param null|numeric $priority optional priority at which to count items.  if no parameter, it will be set to the default {@link getDefaultPriority}
180
	 * @return false|int the number of items in the list at the specified priority
181
	 */
182
	public function getPriorityCount($priority = null)
183
	{
184
		$priority = $this->ensurePriority($priority);
185
		if (!isset($this->_d[$priority]) || !is_array($this->_d[$priority])) {
186 16
			return false;
187
		}
188 16
		return count($this->_d[$priority]);
189 16
	}
190
191
	/**
192
	 * Returns an iterator for traversing the items in the list.
193
	 * This method is required by the interface \IteratorAggregate.
194
	 * @return \Iterator an iterator for traversing the items in the list.
195
	 */
196 121
	public function getIterator(): \Iterator
197
	{
198 121
		return new \ArrayIterator($this->flattenPriorities());
199 112
	}
200 112
201
202 121
	/**
203
	 * Returns the item at the index of a flattened priority list.
204
	 * {@link offsetGet} calls this method.
205
	 * @param int $index the index of the item to get
206
	 * @throws TInvalidDataValueException Issued when the index is invalid
207
	 * @return mixed the element at the offset
208 93
	 */
209
	public function itemAt($index)
210 93
	{
211 68
		if ($index >= 0 && $index < $this->getCount()) {
212
			$arr = $this->flattenPriorities();
213
			return $arr[$index];
214 59
		} else {
215 59
			throw new TInvalidDataValueException('list_index_invalid', $index);
216 59
		}
217 56
	}
218
219 59
	/**
220
	 * Gets all the items at a specific priority.
221
	 * @param null|numeric $priority priority of the items to get.  Defaults to null, filled in with the default priority, if left blank.
222
	 * @return ?array all items at priority in index order, null if there are no items at that priority
223
	 */
224
	public function itemsAtPriority($priority = null): ?array
225
	{
226
		$priority = $this->ensurePriority($priority);
227
		return $this->_d[$priority] ?? null;
228
	}
229
230 8
	/**
231
	 * Returns the item at an index within a priority
232 8
	 * @param int $index the index into the list of items at priority
233 8
	 * @param numeric $priority the priority which to index.  no parameter or null will result in the default priority
234 8
	 * @return mixed the element at the offset, false if no element is found at the offset
235
	 */
236 2
	public function itemAtIndexInPriority($index, $priority = null)
237
	{
238
		$priority = $this->ensurePriority($priority);
239
		return !isset($this->_d[$priority]) ? false : (
240
			$this->_d[$priority][$index] ?? false
241
		);
242
	}
243
244
	/**
245 16
	 * Appends an item into the list at the end of the specified priority.  The position of the added item may
246
	 * not be at the end of the list.
247 16
	 * @param mixed $item item to add into the list at priority
248 1
	 * @param null|numeric $priority priority blank or null for the default priority
249
	 * @throws TInvalidOperationException if the map is read-only
250 16
	 * @return int the index within the flattened array
251
	 */
252 16
	public function add($item, $priority = null)
253
	{
254
		if ($this->getReadOnly()) {
255
			throw new TInvalidOperationException('list_readonly', get_class($this));
256
		}
257
258
		return $this->insertAtIndexInPriority($item, false, $priority, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->insertAtIn...false, $priority, true) could also return false which is incompatible with the documented return type integer. 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...
259
	}
260
261 1
	/**
262
	 * Inserts an item at an index.  It reads the priority of the item at index within the flattened list
263 1
	 * and then inserts the item at that priority-index.
264 1
	 * @param int $index the specified position in the flattened list.
265
	 * @param mixed $item new item to add
266 1
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
267
	 * @throws TInvalidOperationException if the list is read-only
268 1
	 */
269 1
	public function insertAt($index, $item)
270
	{
271
		if ($this->getReadOnly()) {
272
			throw new TInvalidOperationException('list_readonly', get_class($this));
273
		}
274
275
		if (($priority = $this->priorityAt($index, true)) !== false) {
276
			$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
277
		} else {
278
			throw new TInvalidDataValueException('list_index_invalid', $index);
279
		}
280
	}
281 116
282
	/**
283 116
	 * Inserts an item at the specified index within a priority.  Override and call this method to
284 1
	 * insert your own functionality.
285
	 * @param mixed $item item to add within the list.
286
	 * @param false|int $index index within the priority to add the item, defaults to false which appends the item at the priority
287 116
	 * @param null|numeric $priority priority of the item.  defaults to null, which sets it to the default priority
288
	 * @param bool $preserveCache preserveCache specifies if this is a special quick function or not. This defaults to false.
289
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
290
	 * @throws TInvalidOperationException if the list is read-only
291
	 */
292
	public function insertAtIndexInPriority($item, $index = false, $priority = null, $preserveCache = false)
293
	{
294
		if ($this->getReadOnly()) {
295
			throw new TInvalidOperationException('list_readonly', get_class($this));
296
		}
297
298 3
		$itemPriority = null;
299
		if (($isPriorityItem = ($item instanceof IPriorityItem)) && ($priority === null || !is_numeric($priority))) {
300 3
			$itemPriority = $priority = $item->getPriority();
301 1
		}
302
		$priority = $this->ensurePriority($priority);
303
		if (($item instanceof IPriorityCapture) && (!$isPriorityItem || $itemPriority !== $priority)) {
304 2
			$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

304
			$item->setPriority(/** @scrutinizer ignore-type */ $priority);
Loading history...
305 2
		}
306
307
		if ($index === false && isset($this->_d[$priority])) {
308
			$c = count($this->_d[$priority]);
309 2
			$this->_d[$priority][] = $item;
310
		} elseif (isset($this->_d[$priority])) {
311
			$c = $index;
312
			array_splice($this->_d[$priority], $index, 0, [$item]);
0 ignored issues
show
Bug introduced by
It seems like $index can also be of type false; however, parameter $offset of array_splice() does only seem to accept integer, 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

312
			array_splice($this->_d[$priority], /** @scrutinizer ignore-type */ $index, 0, [$item]);
Loading history...
313
		} else {
314
			$c = 0;
315
			$this->_o = false;
316
			$this->_d[$priority] = [$item];
317
		}
318
319
		if ($preserveCache) {
320
			if ($this->_fd !== null) {
321 116
				$this->sortPriorities();
322
				foreach ($this->_d as $prioritykey => $items) {
323 116
					if ($prioritykey >= $priority) {
324 1
						break;
325
					} else {
326
						$c += count($items);
327 116
					}
328 116
				}
329
				array_splice($this->_fd, $c, 0, [$item]);
330 116
			}
331
		} elseif (!$preserveCache) {
0 ignored issues
show
introduced by
The condition $preserveCache is always false.
Loading history...
332 116
			if ($this->_fd !== null && count($this->_d) == 1) {
333 116
				array_splice($this->_fd, $c, 0, [$item]);
334 116
			} else {
335 116
				$this->_fd = null;
336 89
				$c = null;
337 89
			}
338
		}
339 65
340
		$this->_c++;
341
342
		return $c;
343 116
	}
344 89
345 89
346 89
	/**
347 106
	 * Removes an item from the priority list.
348 1
	 * The list will search for the item.  The first matching item found will be removed from the list.
349 1
	 * @param mixed $item item the item to be removed.
350
	 * @param null|bool|float $priority priority of item to remove. without this parameter it defaults to false.
351 106
	 * A value of false means any priority. null will be filled in with the default priority.
352 106
	 * @throws TInvalidDataValueException If the item does not exist
353 106
	 * @return int index within the flattened list at which the item is being removed
354
	 */
355
	public function remove($item, $priority = false)
356 116
	{
357 116
		if ($this->getReadOnly()) {
358
			throw new TInvalidOperationException('list_readonly', get_class($this));
359
		}
360 20
361 20
		if (($p = $this->priorityOf($item, true)) !== false) {
362 2
			if ($priority !== false) {
363 2
				$priority = $this->ensurePriority($priority);
364 20
				if ($p[0] != $priority) {
365 18
					throw new TInvalidDataValueException('list_item_inexistent');
366 18
				}
367
			}
368 17
			$this->removeAtIndexInPriority($p[1], $p[0]);
369 17
			return $p[2];
370 17
		} else {
371
			throw new TInvalidDataValueException('list_item_inexistent');
372 20
		}
373 1
	}
374
375 20
	/**
376
	 * Removes an item at the specified index in the flattened list.
377
	 * @param int $index index of the item to be removed.
378
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
379 116
	 * @throws TInvalidOperationException if the list is read-only
380
	 * @return mixed the removed item.
381 116
	 */
382
	public function removeAt($index)
383
	{
384
		if ($this->getReadOnly()) {
385
			throw new TInvalidOperationException('list_readonly', get_class($this));
386
		}
387
388
		if (($priority = $this->priorityAt($index, true)) !== false) {
389
			return $this->removeAtIndexInPriority($priority[1], $priority[0]);
390
		}
391
		throw new TInvalidDataValueException('list_index_invalid', $index);
392
	}
393
394 41
	/**
395
	 * Removes the item at a specific index within a priority.  Override
396 41
	 * and call this method to insert your own functionality.
397 1
	 * @param int $index index of item to remove within the priority.
398
	 * @param null|numeric $priority priority of the item to remove, defaults to null, or left blank, it is then set to the default priority
399
	 * @throws TInvalidDataValueException If the item does not exist
400 40
	 * @return mixed the removed item.
401 40
	 */
402 1
	public function removeAtIndexInPriority($index, $priority = null)
403 1
	{
404
		if ($this->getReadOnly()) {
405 1
			throw new TInvalidOperationException('list_readonly', get_class($this));
406
		}
407 1
408 1
		$priority = $this->ensurePriority($priority);
409
		if (!isset($this->_d[$priority]) || $index < 0 || $index >= count($this->_d[$priority])) {
410
			throw new TInvalidDataValueException('list_item_inexistent');
411 40
		}
412 40
413
		// $value is an array of elements removed, only one
414 2
		$value = array_splice($this->_d[$priority], $index, 1);
415
		$value = $value[0];
416
417
		if (!count($this->_d[$priority])) {
418
			unset($this->_d[$priority]);
419
		}
420
421
		$this->_c--;
422
		$this->_fd = null;
423
		return $value;
424
	}
425 5
426
	/**
427 5
	 * Removes all items in the priority list by calling removeAtIndexInPriority from the last item to the first.
428 1
	 */
429
	public function clear(): void
430
	{
431 4
		if ($this->getReadOnly()) {
432 4
			throw new TInvalidOperationException('list_readonly', get_class($this));
433
		}
434
435
		foreach ($this->_d as $priority => $items) {
436
			for ($index = count($items) - 1; $index >= 0; $index--) {
437
				$this->removeAtIndexInPriority($index, $priority);
438
			}
439
		}
440
	}
441
442
	/**
443
	 * @param mixed $item item
444
	 * @return int the index of the item in the flattened list (0 based), -1 if not found.
445 51
	 */
446
	public function indexOf($item)
447 51
	{
448 1
		if (($index = array_search($item, $this->flattenPriorities(), true)) === false) {
449
			return -1;
450
		} else {
451 50
			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...
452
		}
453
	}
454 50
455
	/**
456 50
	 * Returns the priority of a particular item
457 1
	 * @param mixed $item the item to look for within the list
458
	 * @param bool $withindex this specifies if the full positional data of the item within the list is returned.
459
	 * 		This defaults to false, if no parameter is provided, so only provides the priority number of the item by default.
460
	 * @return array|false|numeric the priority of the item in the list, false if not found.
461 49
	 *   if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex,
462 49
	 * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex]
463
	 */
464 49
	public function priorityOf($item, $withindex = false)
465 30
	{
466
		$this->sortPriorities();
467
468 49
		$absindex = 0;
469 49
		foreach ($this->_d as $priority => $items) {
470 49
			if (($index = array_search($item, $items, true)) !== false) {
471
				$absindex += $index;
472
				return $withindex ? [$priority, $index, $absindex,
473
						'priority' => $priority, 'index' => $index, 'absindex' => $absindex, ] : $priority;
474
			} else {
475
				$absindex += count($items);
476 7
			}
477
		}
478 7
479 1
		return false;
480
	}
481
482 6
	/**
483 6
	 * Retutrns the priority of an item at a particular flattened index.
484 6
	 * @param int $index index of the item within the list
485
	 * @param bool $withindex this specifies if the full positional data of the item within the list is returned.
486 6
	 * 		This defaults to false, if no parameter is provided, so only provides the priority number of the item by default.
487
	 * @return array|false|numeric the priority of the item in the list, false if not found.
488 6
	 *   if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex,
489
	 * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex]
490
	 */
491
	public function priorityAt($index, $withindex = false)
492
	{
493
		if ($index < 0 || $index >= $this->getCount()) {
494 2
			throw new TInvalidDataValueException('list_index_invalid', $index);
495
		}
496 2
497
		$absindex = $index;
498
		$this->sortPriorities();
499
		foreach ($this->_d as $priority => $items) {
500
			if ($index >= ($c = count($items))) {
501
				$index -= $c;
502
			} else {
503 13
				return $withindex ? [$priority, $index, $absindex,
504
						'priority' => $priority, 'index' => $index, 'absindex' => $absindex, ] : $priority;
505 13
			}
506 11
		}
507
		return false;
508 11
	}
509
510
	/**
511
	 * This inserts an item before another item within the list.  It uses the same priority as the
512
	 * found index item and places the new item before it.
513
	 * @param mixed $indexitem the item to index
514
	 * @param mixed $item the item to add before indexitem
515
	 * @throws TInvalidDataValueException If the item does not exist
516
	 * @return int where the item has been inserted in the flattened list
517
	 */
518
	public function insertBefore($indexitem, $item)
519
	{
520
		if ($this->getReadOnly()) {
521 48
			throw new TInvalidOperationException('list_readonly', get_class($this));
522
		}
523 48
524
		if (($priority = $this->priorityOf($indexitem, true)) === false) {
525 48
			throw new TInvalidDataValueException('list_item_inexistent');
526 48
		}
527 48
528 46
		$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
529 46
530 46
		return $priority[2];
531
	}
532 11
533
	/**
534
	 * This inserts an item after another item within the list.  It uses the same priority as the
535
	 * found index item and places the new item after it.
536 4
	 * @param mixed $indexitem the item to index
537
	 * @param mixed $item the item to add after indexitem
538
	 * @throws TInvalidDataValueException If the item does not exist
539
	 * @return int where the item has been inserted in the flattened list
540
	 */
541
	public function insertAfter($indexitem, $item)
542
	{
543
		if ($this->getReadOnly()) {
544
			throw new TInvalidOperationException('list_readonly', get_class($this));
545
		}
546
547
		if (($priority = $this->priorityOf($indexitem, true)) === false) {
548 12
			throw new TInvalidDataValueException('list_item_inexistent');
549
		}
550 12
551 4
		$this->insertAtIndexInPriority($item, $priority[1] + 1, $priority[0]);
552
553
		return $priority[2] + 1;
554 12
	}
555 12
556 12
	/**
557 12
	 * @return array the priority list of items in array
558 7
	 */
559
	public function toArray(): array
560 12
	{
561 12
		return $this->flattenPriorities();
562
	}
563
564
	/**
565
	 * @return array the array of priorities keys with values of arrays of items.  The priorities are sorted so important priorities, lower numerics, are first.
566
	 */
567
	public function toPriorityArray(): array
568
	{
569
		$this->sortPriorities();
570
		return $this->_d;
571
	}
572
573
	/**
574
	 * Combines the map elements which have a priority below the parameter value
575 3
	 * @param numeric $priority the cut-off priority.  All items of priority less than this are returned.
576
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: false, not inclusive.
577 3
	 * @return array the array of priorities keys with values of arrays of items that are below a specified priority.
578 1
	 *  The priorities are sorted so important priorities, lower numerics, are first.
579
	 */
580
	public function toArrayBelowPriority($priority, $inclusive = false): array
581 2
	{
582 1
		$this->sortPriorities();
583
		$items = [];
584
		foreach ($this->_d as $itemspriority => $itemsatpriority) {
585 1
			if ((!$inclusive && $itemspriority >= $priority) || $itemspriority > $priority) {
586
				break;
587 1
			}
588
			$items = array_merge($items, $itemsatpriority);
589
		}
590
		return $items;
591
	}
592
593
	/**
594
	 * Combines the map elements which have a priority above the parameter value
595
	 * @param numeric $priority the cut-off priority.  All items of priority greater than this are returned.
596
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: true, inclusive.
597
	 * @return array the array of priorities keys with values of arrays of items that are above a specified priority.
598 3
	 *  The priorities are sorted so important priorities, lower numerics, are first.
599
	 */
600 3
	public function toArrayAbovePriority($priority, $inclusive = true): array
601 1
	{
602
		$this->sortPriorities();
603
		$items = [];
604 2
		foreach ($this->_d as $itemspriority => $itemsatpriority) {
605 1
			if ((!$inclusive && $itemspriority <= $priority) || $itemspriority < $priority) {
606
				continue;
607
			}
608 1
			$items = array_merge($items, $itemsatpriority);
609
		}
610 1
		return $items;
611
	}
612
613
614
	/**
615
	 * Copies iterable data into the priority list.
616 70
	 * Note, existing data in the map will be cleared first.
617
	 * @param mixed $data the data to be copied from, must be an array or object implementing Traversable
618 70
	 * @throws TInvalidDataTypeException If data is neither an array nor an iterator.
619
	 */
620
	public function copyFrom($data): void
621
	{
622
		if ($data instanceof TPriorityList) {
623
			if ($this->getCount() > 0) {
624 1
				$this->clear();
625
			}
626 1
			foreach ($data->getPriorities() as $priority) {
627 1
				foreach ($data->itemsAtPriority($priority) as $index => $item) {
628
					$this->insertAtIndexInPriority($item, $index, $priority);
629
				}
630
			}
631
		} elseif (is_array($data) || $data instanceof \Traversable) {
632
			if ($this->getCount() > 0) {
633
				$this->clear();
634
			}
635
			foreach ($data as $key => $item) {
636
				$this->add($item);
637 2
			}
638
		} elseif ($data !== null) {
639 2
			throw new TInvalidDataTypeException('map_data_not_iterable');
640 2
		}
641 2
	}
642 2
643 2
	/**
644
	 * Merges iterable data into the priority list.
645 2
	 * New data will be appended to the end of the existing data.  If another TPriorityList is merged,
646
	 * the incoming parameter items will be appended at the priorities they are present.  These items will be added
647 2
	 * to the end of the existing items with equal priorities, if there are any.
648
	 * @param mixed $data the data to be merged with, must be an array or object implementing Traversable
649
	 * @throws TInvalidDataTypeException If data is neither an array nor an iterator.
650
	 */
651
	public function mergeWith($data): void
652
	{
653
		if ($data instanceof TPriorityList) {
654
			foreach ($data->getPriorities() as $priority) {
655
				foreach ($data->itemsAtPriority($priority) as $index => $item) {
656
					$this->insertAtIndexInPriority($item, false, $priority);
657 2
				}
658
			}
659 2
		} elseif (is_array($data) || $data instanceof \Traversable) {
660 2
			foreach ($data as $priority => $item) {
661 2
				$this->add($item);
662 2
			}
663 2
		} elseif ($data !== null) {
664
			throw new TInvalidDataTypeException('map_data_not_iterable');
665 2
		}
666
	}
667 2
668
	/**
669
	 * Returns whether there is an element at the specified offset.
670
	 * This method is required by the interface \ArrayAccess.
671
	 * @param mixed $offset the offset to check on
672
	 * @return bool
673
	 */
674
	public function offsetExists($offset): bool
675
	{
676
		return ($offset >= 0 && $offset < $this->getCount());
677 27
	}
678
679 27
	/**
680 14
	 * Sets the element at the specified offset. This method is required by the interface \ArrayAccess.
681
	 * Setting elements in a priority list is not straight forword when appending and setting at the
682
	 * end boundary.  When appending without an offset (a null offset), the item will be added at
683 14
	 * the default priority.  The item may not be the last item in the list.  When appending with an
684 14
	 * offset equal to the count of the list, the item will get be appended with the last items priority.
685 14
	 *
686
	 * All together, when setting the location of an item, the item stays in that location, but appending
687
	 * an item into a priority list doesn't mean the item is at the end of the list.
688 15
	 * @param int $offset the offset to set element
689 15
	 * @param mixed $item the element value
690 1
	 */
691
	public function offsetSet($offset, $item): void
692 15
	{
693 15
		if ($offset === null) {
0 ignored issues
show
introduced by
The condition $offset === null is always false.
Loading history...
694
			$this->add($item);
695 1
			return;
696 1
		}
697
		if ($offset === $this->getCount()) {
698 27
			$priority = $this->priorityAt($offset - 1, true);
699
			$priority[1]++;
700
		} else {
701
			$priority = $this->priorityAt($offset, true);
702
			$this->removeAtIndexInPriority($priority[1], $priority[0]);
703
		}
704
		$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
705
	}
706
707
	/**
708 2
	 * Unsets the element at the specified offset.
709
	 * This method is required by the interface \ArrayAccess.
710 2
	 * @param mixed $offset the offset to unset element
711 1
	 */
712 1
	public function offsetUnset($offset): void
713 1
	{
714
		$this->removeAt($offset);
715
	}
716 1
717 1
	/**
718 1
	 * Returns an array with the names of all variables of this object that should NOT be serialized
719
	 * because their value is the default one or useless to be cached for the next page loads.
720 1
	 * Reimplement in derived classes to add new variables, but remember to  also to call the parent
721 1
	 * implementation first.
722
	 * @param array $exprops by reference
723 2
	 * @since 4.2.3
724
	 */
725
	protected function _getZappableSleepProps(&$exprops)
726
	{
727
		parent::_getZappableSleepProps($exprops);
728
		if ($this->_o === false) {
729
			$exprops[] = "\0*\0_o";
730
		}
731 2
		$exprops[] = "\0*\0_fd";
732
		if ($this->_dp == 10) {
0 ignored issues
show
introduced by
The condition $this->_dp == 10 is always false.
Loading history...
733 2
			$exprops[] = "\0" . __CLASS__ . "\0_dp";
734
		}
735
		if ($this->_p === 8) {
736
			$exprops[] = "\0" . __CLASS__ . "\0_p";
737
		}
738
	}
739
}
740