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

TPriorityList::removeAtIndexInPriority()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 15
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 25
rs 8.8333
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
	public function getDefaultPriority()
91
	{
92
		return $this->_dp;
93
	}
94
95
	/**
96
	 * This must be called internally or when instantiated.
97
	 * @param numeric $value sets the default priority of inserted items without a specified priority
98
	 */
99
	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
	 */
107
	public function getPrecision(): int
108
	{
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
	 */
116
	protected function setPrecision($value): void
117
	{
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
	 * into a string.
126
	 * @param mixed $priority
127
	 * @return string the priority in string format
128
	 */
129
	protected function ensurePriority($priority): string
130
	{
131
		if ($priority === null || !is_numeric($priority)) {
132
			$priority = $this->getDefaultPriority();
133
		}
134
		return (string) round((float) $priority, $this->_p);
135
	}
136
137
138
	/**
139
	 * This orders the priority list internally.
140
	 */
141
	protected function sortPriorities(): void
142
	{
143
		if (!$this->_o) {
144
			ksort($this->_d, SORT_NUMERIC);
145
			$this->_o = true;
146
		}
147
	}
148
149
	/**
150
	 * 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
	 */
153
	protected function flattenPriorities(): array
154
	{
155
		if (is_array($this->_fd)) {
156
			return $this->_fd;
157
		}
158
159
		$this->sortPriorities();
160
		$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
	/**
168
	 * This returns a list of the priorities within this list, ordered lowest to highest.
169
	 * @return array the array of priority numerics in decreasing priority order
170
	 */
171
	public function getPriorities(): array
172
	{
173
		$this->sortPriorities();
174
		return array_keys($this->_d);
175
	}
176
177
	/**
178
	 * Gets the number of items at a priority within the list
179
	 * @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
			return false;
187
		}
188
		return count($this->_d[$priority]);
189
	}
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
	public function getIterator(): \Iterator
197
	{
198
		return new \ArrayIterator($this->flattenPriorities());
199
	}
200
201
202
	/**
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
	 */
209
	public function itemAt($index)
210
	{
211
		if ($index >= 0 && $index < $this->getCount()) {
212
			$arr = $this->flattenPriorities();
213
			return $arr[$index];
214
		} else {
215
			throw new TInvalidDataValueException('list_index_invalid', $index);
216
		}
217
	}
218
219
	/**
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
	/**
231
	 * Returns the item at an index within a priority
232
	 * @param int $index the index into the list of items at priority
233
	 * @param numeric $priority the priority which to index.  no parameter or null will result in the default priority
234
	 * @return mixed the element at the offset, false if no element is found at the offset
235
	 */
236
	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
	 * 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
	 * @param mixed $item item to add into the list at priority
248
	 * @param null|numeric $priority priority blank or null for the default priority
249
	 * @throws TInvalidOperationException if the map is read-only
250
	 * @return int the index within the flattened array
251
	 */
252
	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
	/**
262
	 * Inserts an item at an index.  It reads the priority of the item at index within the flattened list
263
	 * and then inserts the item at that priority-index.
264
	 * @param int $index the specified position in the flattened list.
265
	 * @param mixed $item new item to add
266
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
267
	 * @throws TInvalidOperationException if the list is read-only
268
	 */
269
	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
282
	/**
283
	 * Inserts an item at the specified index within a priority.  Override and call this method to
284
	 * 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
	 * @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
		$itemPriority = null;
299
		if (($isPriorityItem = ($item instanceof IPriorityItem)) && ($priority === null || !is_numeric($priority))) {
300
			$itemPriority = $priority = $item->getPriority();
301
		}
302
		$priority = $this->ensurePriority($priority);
303
		if (($item instanceof IPriorityCapture) && (!$isPriorityItem || $itemPriority !== $priority)) {
304
			$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
		}
306
307
		if ($index === false && isset($this->_d[$priority])) {
308
			$c = count($this->_d[$priority]);
309
			$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
				$this->sortPriorities();
322
				foreach ($this->_d as $prioritykey => $items) {
323
					if ($prioritykey >= $priority) {
324
						break;
325
					} else {
326
						$c += count($items);
327
					}
328
				}
329
				array_splice($this->_fd, $c, 0, [$item]);
330
			}
331
		} elseif (!$preserveCache) {
0 ignored issues
show
introduced by
The condition $preserveCache is always false.
Loading history...
332
			if ($this->_fd !== null && count($this->_d) == 1) {
333
				array_splice($this->_fd, $c, 0, [$item]);
334
			} else {
335
				$this->_fd = null;
336
				$c = null;
337
			}
338
		}
339
340
		$this->_c++;
341
342
		return $c;
343
	}
344
345
346
	/**
347
	 * Removes an item from the priority list.
348
	 * The list will search for the item.  The first matching item found will be removed from the list.
349
	 * @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
	 * A value of false means any priority. null will be filled in with the default priority.
352
	 * @throws TInvalidDataValueException If the item does not exist
353
	 * @return int index within the flattened list at which the item is being removed
354
	 */
355
	public function remove($item, $priority = false)
356
	{
357
		if ($this->getReadOnly()) {
358
			throw new TInvalidOperationException('list_readonly', get_class($this));
359
		}
360
361
		if (($p = $this->priorityOf($item, true)) !== false) {
362
			if ($priority !== false) {
363
				$priority = $this->ensurePriority($priority);
364
				if ($p[0] != $priority) {
365
					throw new TInvalidDataValueException('list_item_inexistent');
366
				}
367
			}
368
			$this->removeAtIndexInPriority($p[1], $p[0]);
369
			return $p[2];
370
		} else {
371
			throw new TInvalidDataValueException('list_item_inexistent');
372
		}
373
	}
374
375
	/**
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
	 * @throws TInvalidOperationException if the list is read-only
380
	 * @return mixed the removed item.
381
	 */
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
	/**
395
	 * Removes the item at a specific index within a priority.  Override
396
	 * and call this method to insert your own functionality.
397
	 * @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
	 * @return mixed the removed item.
401
	 */
402
	public function removeAtIndexInPriority($index, $priority = null)
403
	{
404
		if ($this->getReadOnly()) {
405
			throw new TInvalidOperationException('list_readonly', get_class($this));
406
		}
407
408
		$priority = $this->ensurePriority($priority);
409
		if (!isset($this->_d[$priority]) || $index < 0 || $index >= ($c = count($this->_d[$priority]))) {
410
			throw new TInvalidDataValueException('list_item_inexistent');
411
		}
412
413
		// $value is an array of elements removed, only one
414
		if ($index === $c - 1) {
415
			$value = array_pop($this->_d[$priority]);
416
		} else {
417
			$value = array_splice($this->_d[$priority], $index, 1);
418
			$value = $value[0];
419
		}
420
		if (!count($this->_d[$priority])) {
421
			unset($this->_d[$priority]);
422
		}
423
424
		$this->_c--;
425
		$this->_fd = null;
426
		return $value;
427
	}
428
429
	/**
430
	 * Removes all items in the priority list by calling removeAtIndexInPriority from the last item to the first.
431
	 */
432
	public function clear(): void
433
	{
434
		if ($this->getReadOnly()) {
435
			throw new TInvalidOperationException('list_readonly', get_class($this));
436
		}
437
438
		foreach ($this->_d as $priority => $items) {
439
			for ($index = count($items) - 1; $index >= 0; $index--) {
440
				$this->removeAtIndexInPriority($index, $priority);
441
			}
442
		}
443
	}
444
445
	/**
446
	 * @param mixed $item item
447
	 * @return int the index of the item in the flattened list (0 based), -1 if not found.
448
	 */
449
	public function indexOf($item)
450
	{
451
		if (($index = array_search($item, $this->flattenPriorities(), true)) === false) {
452
			return -1;
453
		} else {
454
			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...
455
		}
456
	}
457
458
	/**
459
	 * Returns the priority of a particular item
460
	 * @param mixed $item the item to look for within the list
461
	 * @param bool $withindex this specifies if the full positional data of the item within the list is returned.
462
	 * 		This defaults to false, if no parameter is provided, so only provides the priority number of the item by default.
463
	 * @return array|false|numeric the priority of the item in the list, false if not found.
464
	 *   if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex,
465
	 * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex]
466
	 */
467
	public function priorityOf($item, $withindex = false)
468
	{
469
		$this->sortPriorities();
470
471
		$absindex = 0;
472
		foreach ($this->_d as $priority => $items) {
473
			if (($index = array_search($item, $items, true)) !== false) {
474
				$absindex += $index;
475
				return $withindex ? [$priority, $index, $absindex,
476
						'priority' => $priority, 'index' => $index, 'absindex' => $absindex, ] : $priority;
477
			} else {
478
				$absindex += count($items);
479
			}
480
		}
481
482
		return false;
483
	}
484
485
	/**
486
	 * Retutrns the priority of an item at a particular flattened index.
487
	 * @param int $index index of the item within the list
488
	 * @param bool $withindex this specifies if the full positional data of the item within the list is returned.
489
	 * 		This defaults to false, if no parameter is provided, so only provides the priority number of the item by default.
490
	 * @return array|false|numeric the priority of the item in the list, false if not found.
491
	 *   if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex,
492
	 * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex]
493
	 */
494
	public function priorityAt($index, $withindex = false)
495
	{
496
		if ($index < 0 || $index >= $this->getCount()) {
497
			throw new TInvalidDataValueException('list_index_invalid', $index);
498
		}
499
500
		$absindex = $index;
501
		$this->sortPriorities();
502
		foreach ($this->_d as $priority => $items) {
503
			if ($index >= ($c = count($items))) {
504
				$index -= $c;
505
			} else {
506
				return $withindex ? [$priority, $index, $absindex,
507
						'priority' => $priority, 'index' => $index, 'absindex' => $absindex, ] : $priority;
508
			}
509
		}
510
		return false;
511
	}
512
513
	/**
514
	 * This inserts an item before another item within the list.  It uses the same priority as the
515
	 * found index item and places the new item before it.
516
	 * @param mixed $indexitem the item to index
517
	 * @param mixed $item the item to add before indexitem
518
	 * @throws TInvalidDataValueException If the item does not exist
519
	 * @return int where the item has been inserted in the flattened list
520
	 */
521
	public function insertBefore($indexitem, $item)
522
	{
523
		if ($this->getReadOnly()) {
524
			throw new TInvalidOperationException('list_readonly', get_class($this));
525
		}
526
527
		if (($priority = $this->priorityOf($indexitem, true)) === false) {
528
			throw new TInvalidDataValueException('list_item_inexistent');
529
		}
530
531
		$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
532
533
		return $priority[2];
534
	}
535
536
	/**
537
	 * This inserts an item after another item within the list.  It uses the same priority as the
538
	 * found index item and places the new item after it.
539
	 * @param mixed $indexitem the item to index
540
	 * @param mixed $item the item to add after indexitem
541
	 * @throws TInvalidDataValueException If the item does not exist
542
	 * @return int where the item has been inserted in the flattened list
543
	 */
544
	public function insertAfter($indexitem, $item)
545
	{
546
		if ($this->getReadOnly()) {
547
			throw new TInvalidOperationException('list_readonly', get_class($this));
548
		}
549
550
		if (($priority = $this->priorityOf($indexitem, true)) === false) {
551
			throw new TInvalidDataValueException('list_item_inexistent');
552
		}
553
554
		$this->insertAtIndexInPriority($item, $priority[1] + 1, $priority[0]);
555
556
		return $priority[2] + 1;
557
	}
558
559
	/**
560
	 * @return array the priority list of items in array
561
	 */
562
	public function toArray(): array
563
	{
564
		return $this->flattenPriorities();
565
	}
566
567
	/**
568
	 * @return array the array of priorities keys with values of arrays of items.  The priorities are sorted so important priorities, lower numerics, are first.
569
	 */
570
	public function toPriorityArray(): array
571
	{
572
		$this->sortPriorities();
573
		return $this->_d;
574
	}
575
576
	/**
577
	 * Combines the map elements which have a priority below the parameter value
578
	 * @param numeric $priority the cut-off priority.  All items of priority less than this are returned.
579
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: false, not inclusive.
580
	 * @return array the array of priorities keys with values of arrays of items that are below a specified priority.
581
	 *  The priorities are sorted so important priorities, lower numerics, are first.
582
	 */
583
	public function toArrayBelowPriority($priority, $inclusive = false): array
584
	{
585
		$this->sortPriorities();
586
		$items = [];
587
		foreach ($this->_d as $itemspriority => $itemsatpriority) {
588
			if ((!$inclusive && $itemspriority >= $priority) || $itemspriority > $priority) {
589
				break;
590
			}
591
			$items = array_merge($items, $itemsatpriority);
592
		}
593
		return $items;
594
	}
595
596
	/**
597
	 * Combines the map elements which have a priority above the parameter value
598
	 * @param numeric $priority the cut-off priority.  All items of priority greater than this are returned.
599
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: true, inclusive.
600
	 * @return array the array of priorities keys with values of arrays of items that are above a specified priority.
601
	 *  The priorities are sorted so important priorities, lower numerics, are first.
602
	 */
603
	public function toArrayAbovePriority($priority, $inclusive = true): array
604
	{
605
		$this->sortPriorities();
606
		$items = [];
607
		foreach ($this->_d as $itemspriority => $itemsatpriority) {
608
			if ((!$inclusive && $itemspriority <= $priority) || $itemspriority < $priority) {
609
				continue;
610
			}
611
			$items = array_merge($items, $itemsatpriority);
612
		}
613
		return $items;
614
	}
615
616
617
	/**
618
	 * Copies iterable data into the priority list.
619
	 * Note, existing data in the map will be cleared first.
620
	 * @param mixed $data the data to be copied from, must be an array or object implementing Traversable
621
	 * @throws TInvalidDataTypeException If data is neither an array nor an iterator.
622
	 */
623
	public function copyFrom($data): void
624
	{
625
		if ($data instanceof TPriorityList) {
626
			if ($this->getCount() > 0) {
627
				$this->clear();
628
			}
629
			foreach ($data->getPriorities() as $priority) {
630
				foreach ($data->itemsAtPriority($priority) as $index => $item) {
631
					$this->insertAtIndexInPriority($item, $index, $priority);
632
				}
633
			}
634
		} elseif (is_array($data) || $data instanceof \Traversable) {
635
			if ($this->getCount() > 0) {
636
				$this->clear();
637
			}
638
			foreach ($data as $key => $item) {
639
				$this->add($item);
640
			}
641
		} elseif ($data !== null) {
642
			throw new TInvalidDataTypeException('map_data_not_iterable');
643
		}
644
	}
645
646
	/**
647
	 * Merges iterable data into the priority list.
648
	 * New data will be appended to the end of the existing data.  If another TPriorityList is merged,
649
	 * the incoming parameter items will be appended at the priorities they are present.  These items will be added
650
	 * to the end of the existing items with equal priorities, if there are any.
651
	 * @param mixed $data the data to be merged with, must be an array or object implementing Traversable
652
	 * @throws TInvalidDataTypeException If data is neither an array nor an iterator.
653
	 */
654
	public function mergeWith($data): void
655
	{
656
		if ($data instanceof TPriorityList) {
657
			foreach ($data->getPriorities() as $priority) {
658
				foreach ($data->itemsAtPriority($priority) as $index => $item) {
659
					$this->insertAtIndexInPriority($item, false, $priority);
660
				}
661
			}
662
		} elseif (is_array($data) || $data instanceof \Traversable) {
663
			foreach ($data as $priority => $item) {
664
				$this->add($item);
665
			}
666
		} elseif ($data !== null) {
667
			throw new TInvalidDataTypeException('map_data_not_iterable');
668
		}
669
	}
670
671
	/**
672
	 * Returns whether there is an element at the specified offset.
673
	 * This method is required by the interface \ArrayAccess.
674
	 * @param mixed $offset the offset to check on
675
	 * @return bool
676
	 */
677
	public function offsetExists($offset): bool
678
	{
679
		return ($offset >= 0 && $offset < $this->getCount());
680
	}
681
682
	/**
683
	 * Sets the element at the specified offset. This method is required by the interface \ArrayAccess.
684
	 * Setting elements in a priority list is not straight forword when appending and setting at the
685
	 * end boundary.  When appending without an offset (a null offset), the item will be added at
686
	 * the default priority.  The item may not be the last item in the list.  When appending with an
687
	 * offset equal to the count of the list, the item will get be appended with the last items priority.
688
	 *
689
	 * All together, when setting the location of an item, the item stays in that location, but appending
690
	 * an item into a priority list doesn't mean the item is at the end of the list.
691
	 * @param int $offset the offset to set element
692
	 * @param mixed $item the element value
693
	 */
694
	public function offsetSet($offset, $item): void
695
	{
696
		if ($offset === null) {
0 ignored issues
show
introduced by
The condition $offset === null is always false.
Loading history...
697
			$this->add($item);
698
			return;
699
		}
700
		if ($offset === $this->getCount()) {
701
			$priority = $this->priorityAt($offset - 1, true);
702
			$priority[1]++;
703
		} else {
704
			$priority = $this->priorityAt($offset, true);
705
			$this->removeAtIndexInPriority($priority[1], $priority[0]);
706
		}
707
		$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
708
	}
709
710
	/**
711
	 * Unsets the element at the specified offset.
712
	 * This method is required by the interface \ArrayAccess.
713
	 * @param mixed $offset the offset to unset element
714
	 */
715
	public function offsetUnset($offset): void
716
	{
717
		$this->removeAt($offset);
718
	}
719
720
	/**
721
	 * Returns an array with the names of all variables of this object that should NOT be serialized
722
	 * because their value is the default one or useless to be cached for the next page loads.
723
	 * Reimplement in derived classes to add new variables, but remember to  also to call the parent
724
	 * implementation first.
725
	 * @param array $exprops by reference
726
	 * @since 4.2.3
727
	 */
728
	protected function _getZappableSleepProps(&$exprops)
729
	{
730
		parent::_getZappableSleepProps($exprops);
731
		if ($this->_o === false) {
732
			$exprops[] = "\0*\0_o";
733
		}
734
		$exprops[] = "\0*\0_fd";
735
		if ($this->_dp == 10) {
0 ignored issues
show
introduced by
The condition $this->_dp == 10 is always false.
Loading history...
736
			$exprops[] = "\0" . __CLASS__ . "\0_dp";
737
		}
738
		if ($this->_p === 8) {
739
			$exprops[] = "\0" . __CLASS__ . "\0_p";
740
		}
741
	}
742
}
743