TPriorityList::insertBefore()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

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

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
81
	{
82
		return true;
83
	}
84
85
	/**
86
	 * Returns the item at the index of a flattened priority list.
87
	 * {@see offsetGet} calls this method.
88
	 * @param int $index the index of the item to get
89
	 * @throws TInvalidDataValueException Issued when the index is invalid
90 90
	 * @return mixed the element at the offset
91
	 */
92 90
	public function itemAt($index)
93 90
	{
94 25
		if ($index >= 0 && $index < $this->getCount()) {
95
			$this->flattenPriorities();
96 90
			return $this->_fd[$index];
97 90
		} else {
98 90
			throw new TInvalidDataValueException('list_index_invalid', $index);
99 90
		}
100
	}
101
102
	/**
103
	 * Returns the item at an index within a priority
104
	 * @param int $index the index into the list of items at priority
105
	 * @param null|numeric $priority the priority which to index.  no parameter or null
106 3
	 *   will result in the default priority
107
	 * @throws TInvalidDataValueException if the index is out of the range at the
108 3
	 *   priority or no items at the priority.
109
	 * @return mixed the element at the offset, false if no element is found at the offset
110
	 */
111
	public function itemAtIndexInPriority($index, $priority = null)
112
	{
113
		$priority = $this->ensurePriority($priority);
114
		if (isset($this->_d[$priority]) && 0 <= $index && $index < count($this->_d[$priority])) {
115 133
			return $this->_d[$priority][$index];
116
		} else {
117 133
			throw new TInvalidDataValueException('prioritylist_index_invalid', $index, count($this->_d[$priority] ?? []), $priority);
118
		}
119
	}
120
121
	/**
122
	 * Appends an item into the list at the end of the specified priority.  The position of the added item may
123
	 * not be at the end of the list.
124
	 * @param mixed $item item to add into the list at priority
125 2
	 * @param null|numeric $priority priority blank or null for the default priority
126
	 * @throws TInvalidOperationException if the map is read-only
127 2
	 * @return int the index within the flattened array
128 2
	 */
129
	public function add($item, $priority = null)
130 2
	{
131
		if ($this->getReadOnly()) {
132 2
			throw new TInvalidOperationException('list_readonly', $this::class);
133
		}
134
135 2
		return $this->insertAtIndexInPriority($item, null, $priority, true);
136
	}
137
138
	/**
139
	 * Inserts an item at an index.  It reads the priority of the item at index within the flattened list
140
	 * and then inserts the item at that priority-index.
141 116
	 * @param int $index the specified position in the flattened list.
142
	 * @param mixed $item new item to add
143 116
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
144
	 * @throws TInvalidOperationException if the list is read-only
145
	 * @return ?float The priority of the inserted item.
146
	 */
147
	public function insertAt($index, $item)
148
	{
149
		if ($this->getReadOnly()) {
150 90
			throw new TInvalidOperationException('list_readonly', $this::class);
151
		}
152 90
153 90
		if (($priority = $this->priorityAt($index, true)) !== false) {
154
			$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
155
			return $priority[0];
156
		} else {
157
			throw new TInvalidDataValueException('list_index_invalid', $index);
158 1
		}
159
	}
160 1
161
	/**
162
	 * Inserts an item at the specified index within a priority.  Override and call this method to
163
	 * insert your own functionality.
164
	 * @param mixed $item item to add within the list.
165
	 * @param null|false|int $index index within the priority to add the item, defaults to null which appends the item at the priority
166
	 * @param null|numeric $priority priority of the item.  defaults to null, which sets it to the default priority
167 90
	 * @param bool $preserveCache preserveCache specifies if this is a special quick function or not. This defaults to false.
168
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
169 90
	 * @throws TInvalidOperationException if the list is read-only
170 90
	 * @todo PRADO version 4.3 remove $index: false => null conversion. false should be invalid input data.
171
	 */
172
	public function insertAtIndexInPriority($item, $index = null, $priority = null, $preserveCache = false)
173
	{
174
		$this->collapseReadOnly();
175
		if ($this->getReadOnly()) {
176
			throw new TInvalidOperationException('list_readonly', $this::class);
177 2
		}
178
		if ($index === false) {
179 2
			$index = null; // conversion, remove for PRADO 4.3
180
		}
181
		$itemPriority = null;
182
		if (($isPriorityItem = ($item instanceof IPriorityItem)) && ($priority === null || !is_numeric($priority))) {
183
			$itemPriority = $priority = $item->getPriority();
184
		}
185
		$priority = $this->ensurePriority($priority);
186 16
		if (($item instanceof IPriorityCapture) && (!$isPriorityItem || $itemPriority !== $priority)) {
187
			$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

187
			$item->setPriority(/** @scrutinizer ignore-type */ $priority);
Loading history...
188 16
		}
189 16
190
		if (isset($this->_d[$priority])) {
191
			if ($index === null) {
192
				$c = count($this->_d[$priority]);
193
				$this->_d[$priority][] = $item;
194
			} elseif (0 <= $index && $index <= count($this->_d[$priority])) {
195
				$c = $index;
196 121
				array_splice($this->_d[$priority], $index, 0, [$item]);
197
			} else {
198 121
				throw new TInvalidDataValueException('prioritylist_index_invalid', $index, count($this->_d[$priority] ?? []), $priority);
199 112
			}
200 112
		} elseif ($index === 0 || $index === null) {
201
			$c = 0;
202 121
			$this->_o = false;
203
			$this->_d[$priority] = [$item];
204
		} else {
205
			throw new TInvalidDataValueException('prioritylist_index_invalid', $index, 0, $priority);
206
		}
207
208 93
		if ($preserveCache) {
209
			if ($this->_fd !== null) {
210 93
				$this->sortPriorities();
211 68
				foreach ($this->_d as $prioritykey => $items) {
212
					if ($prioritykey >= $priority) {
213
						break;
214 59
					} else {
215 59
						$c += count($items);
216 59
					}
217 56
				}
218
				array_splice($this->_fd, $c, 0, [$item]);
219 59
			}
220
		} else {
221
			if ($this->_fd !== null && count($this->_d) == 1) {
222
				array_splice($this->_fd, $c, 0, [$item]);
223
			} else {
224
				$this->_fd = null;
225
				$c = null;
226
			}
227
		}
228
229
		$this->_c++;
230 8
231
		return $c;
232 8
	}
233 8
234 8
235
	/**
236 2
	 * Removes an item from the priority list.
237
	 * The list will search for the item.  The first matching item found will be removed from the list.
238
	 * @param mixed $item item the item to be removed.
239
	 * @param null|bool|float $priority priority of item to remove. without this parameter it defaults to false.
240
	 *   A value of false means any priority. null will be filled in with the default priority.
241
	 * @throws TInvalidDataValueException If the item does not exist
242
	 * @return int index within the flattened list at which the item is being removed
243
	 */
244
	public function remove($item, $priority = false)
245 16
	{
246
		if ($this->getReadOnly()) {
247 16
			throw new TInvalidOperationException('list_readonly', $this::class);
248 1
		}
249
250 16
		if ($priority !== false) {
251
			$this->sortPriorities();
252 16
253
			$priority = $this->ensurePriority($priority);
254
255
			$absindex = 0;
256
			foreach (array_keys($this->_d) as $p) {
257
				if ($p < $priority) {
258
					$absindex += count($this->_d[$p]);
259
					continue;
260
				} elseif ($p == $priority) {
261 1
					if (($index = array_search($item, $this->_d[$p], true)) !== false) {
262
						$absindex += $index;
263 1
						$this->removeAtIndexInPriority($index, $p);
0 ignored issues
show
Bug introduced by
It seems like $index can also be of type string; however, parameter $index of Prado\Collections\TPrior...moveAtIndexInPriority() 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

263
						$this->removeAtIndexInPriority(/** @scrutinizer ignore-type */ $index, $p);
Loading history...
264 1
						return $absindex;
265
					}
266 1
				}
267
				break;
268 1
			}
269 1
			throw new TInvalidDataValueException('list_item_inexistent');
270
		}
271
272
		if (($p = $this->priorityOf($item, true)) !== false) {
273
			$this->removeAtIndexInPriority($p[1], $p[0]);
274
			return $p[2];
275
		} else {
276
			throw new TInvalidDataValueException('list_item_inexistent');
277
		}
278
	}
279
280
	/**
281 116
	 * Removes an item at the specified index in the flattened list.
282
	 * @param int $index index of the item to be removed.
283 116
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
284 1
	 * @throws TInvalidOperationException if the list is read-only
285
	 * @return mixed the removed item.
286
	 */
287 116
	public function removeAt($index)
288
	{
289
		if ($this->getReadOnly()) {
290
			throw new TInvalidOperationException('list_readonly', $this::class);
291
		}
292
293
		if (($priority = $this->priorityAt($index, true)) !== false) {
294
			return $this->removeAtIndexInPriority($priority[1], $priority[0]);
295
		}
296
		throw new TInvalidDataValueException('list_index_invalid', $index);
297
	}
298 3
299
	/**
300 3
	 * Removes the item at a specific index within a priority.  Override
301 1
	 * and call this method to insert your own functionality.
302
	 * @param int $index index of item to remove within the priority.
303
	 * @param null|numeric $priority priority of the item to remove, defaults to null, or left blank, it is then set to the default priority
304 2
	 * @throws TInvalidDataValueException If the item does not exist
305 2
	 * @return mixed the removed item.
306
	 */
307
	public function removeAtIndexInPriority($index, $priority = null)
308
	{
309 2
		if ($this->getReadOnly()) {
310
			throw new TInvalidOperationException('list_readonly', $this::class);
311
		}
312
313
		$priority = $this->ensurePriority($priority);
314
		if (!isset($this->_d[$priority]) || $index < 0 || $index >= ($c = count($this->_d[$priority]))) {
315
			throw new TInvalidDataValueException('list_item_inexistent');
316
		}
317
318
		if ($index === $c - 1) {
319
			$value = array_pop($this->_d[$priority]);
320
		} else {
321 116
			$value = array_splice($this->_d[$priority], $index, 1);
322
			$value = $value[0];
323 116
		}
324 1
		if (!count($this->_d[$priority])) {
325
			unset($this->_d[$priority]);
326
		}
327 116
328 116
		$this->_c--;
329
		$this->_fd = null;
330 116
		return $value;
331
	}
332 116
333 116
	/**
334 116
	 * Removes all items in the priority list by calling removeAtIndexInPriority from the last item to the first.
335 116
	 */
336 89
	public function clear(): void
337 89
	{
338
		if ($this->getReadOnly()) {
339 65
			throw new TInvalidOperationException('list_readonly', $this::class);
340
		}
341
342
		foreach (array_keys($this->_d) as $priority) {
343 116
			for ($index = count($this->_d[$priority]) - 1; $index >= 0; $index--) {
344 89
				$this->removeAtIndexInPriority($index, $priority);
345 89
			}
346 89
		}
347 106
	}
348 1
349 1
	/**
350
	 * @param mixed $item item
351 106
	 * @param mixed $priority
352 106
	 * @return int the index of the item in the flattened list (0 based), -1 if not found.
353 106
	 */
354
	public function indexOf($item, $priority = false)
355
	{
356 116
		if ($priority !== false) {
357 116
			$this->sortPriorities();
358
359
			$priority = $this->ensurePriority($priority);
360 20
361 20
			$absindex = 0;
362 2
			foreach (array_keys($this->_d) as $p) {
363 2
				if ($p < $priority) {
364 20
					$absindex += count($this->_d[$p]);
365 18
					continue;
366 18
				} elseif ($p == $priority) {
367
					if (($index = array_search($item, $this->_d[$p], true)) !== false) {
368 17
						$absindex += $index;
369 17
						return $absindex;
370 17
					}
371
				}
372 20
				break;
373 1
			}
374
			return -1;
375 20
		}
376
		$this->flattenPriorities();
377
		if (($index = array_search($item, $this->_fd, true)) === false) {
0 ignored issues
show
Bug introduced by
It seems like $this->_fd can also be of type null; however, parameter $haystack of array_search() does only seem to accept array, 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

377
		if (($index = array_search($item, /** @scrutinizer ignore-type */ $this->_fd, true)) === false) {
Loading history...
378
			return -1;
379 116
		} else {
380
			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...
381 116
		}
382
	}
383
384
	/**
385
	 * Returns the priority of a particular item
386
	 * @param mixed $item the item to look for within the list
387
	 * @param bool $withindex this specifies if the full positional data of the item within the list is returned.
388
	 * 		This defaults to false, if no parameter is provided, so only provides the priority number of the item by default.
389
	 * @return array|false|numeric the priority of the item in the list, false if not found.
390
	 *   if $withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex,
391
	 * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex]
392
	 */
393
	public function priorityOf($item, $withindex = false)
394 41
	{
395
		$this->sortPriorities();
396 41
397 1
		$absindex = 0;
398
		foreach (array_keys($this->_d) as $priority) {
399
			if (($index = array_search($item, $this->_d[$priority], true)) !== false) {
400 40
				$absindex += $index;
401 40
				return $withindex ? [$priority, $index, $absindex,
402 1
						'priority' => $priority, 'index' => $index, 'absindex' => $absindex, ] : $priority;
403 1
			} else {
404
				$absindex += count($this->_d[$priority]);
405 1
			}
406
		}
407 1
408 1
		return false;
409
	}
410
411 40
	/**
412 40
	 * Returns the priority of an item at a particular flattened index.  The index after
413
	 * the last item does not exist but receives a priority from the last item so that
414 2
	 * priority information about any new items being appended is available.
415
	 * @param int $index index of the item within the list
416
	 * @param bool $withindex this specifies if the full positional data of the item within the list is returned.
417
	 * 		This defaults to false, if no parameter is provided, so only provides the priority number of the item by default.
418
	 * @return array|false|numeric the priority of the item in the list, false if not found.
419
	 *   if $withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex,
420
	 * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex]
421
	 */
422
	public function priorityAt($index, $withindex = false)
423
	{
424
		if (0 <= $index && $index <= $this->_c) {
425 5
			$c = $absindex = $index;
426
			$priority = null;
427 5
			$this->sortPriorities();
428 1
			foreach (array_keys($this->_d) as $priority) {
429
				if ($index >= ($c = count($this->_d[$priority]))) {
430
					$index -= $c;
431 4
				} else {
432 4
					return $withindex ? [$priority, $index, $absindex,
0 ignored issues
show
Bug Best Practice introduced by
The expression return $withindex ? arra... $absindex) : $priority also could return the type array<integer|string,integer|mixed> which is incompatible with the return type mandated by Prado\Collections\IPrior...ollection::priorityAt() of Prado\Collections\numeric|false.
Loading history...
433
							'priority' => $priority, 'index' => $index, 'absindex' => $absindex, ] : $priority;
434
				}
435
			}
436
			return $withindex ? [$priority, $c, $absindex,
0 ignored issues
show
Bug Best Practice introduced by
The expression return $withindex ? arra... $absindex) : $priority also could return the type array<integer|string,integer|mixed> which is incompatible with the return type mandated by Prado\Collections\IPrior...ollection::priorityAt() of Prado\Collections\numeric|false.
Loading history...
437
					'priority' => $priority, 'index' => $c, 'absindex' => $absindex, ] : $priority;
438
		}
439
		return false;
440
	}
441
442
	/**
443
	 * This inserts an item before another item within the list.  It uses the same priority as the
444
	 * found index item and places the new item before it.
445 51
	 * @param mixed $indexitem the item to index
446
	 * @param mixed $item the item to add before indexitem
447 51
	 * @throws TInvalidDataValueException If the item does not exist
448 1
	 * @return int where the item has been inserted in the flattened list
449
	 */
450
	public function insertBefore($indexitem, $item)
451 50
	{
452
		if ($this->getReadOnly()) {
453
			throw new TInvalidOperationException('list_readonly', $this::class);
454 50
		}
455
456 50
		if (($priority = $this->priorityOf($indexitem, true)) === false) {
457 1
			throw new TInvalidDataValueException('list_item_inexistent');
458
		}
459
460
		$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
461 49
462 49
		return $priority[2];
463
	}
464 49
465 30
	/**
466
	 * This inserts an item after another item within the list.  It uses the same priority as the
467
	 * found index item and places the new item after it.
468 49
	 * @param mixed $indexitem the item to index
469 49
	 * @param mixed $item the item to add after indexitem
470 49
	 * @throws TInvalidDataValueException If the item does not exist
471
	 * @return int where the item has been inserted in the flattened list
472
	 */
473
	public function insertAfter($indexitem, $item)
474
	{
475
		if ($this->getReadOnly()) {
476 7
			throw new TInvalidOperationException('list_readonly', $this::class);
477
		}
478 7
479 1
		if (($priority = $this->priorityOf($indexitem, true)) === false) {
480
			throw new TInvalidDataValueException('list_item_inexistent');
481
		}
482 6
483 6
		$this->insertAtIndexInPriority($item, $priority[1] + 1, $priority[0]);
484 6
485
		return $priority[2] + 1;
486 6
	}
487
488 6
	/**
489
	 * Copies iterable data into the priority list.
490
	 * Note, existing data in the map will be cleared first.
491
	 * @param mixed $data the data to be copied from, must be an array or object implementing Traversable
492
	 * @throws TInvalidDataTypeException If data is neither an array nor an iterator.
493
	 */
494 2
	public function copyFrom($data): void
495
	{
496 2
		if ($data instanceof TPriorityList) {
497
			if ($this->getCount() > 0) {
498
				$this->clear();
499
			}
500
			$array = $data->toPriorityArray();
501
			foreach (array_keys($array) as $priority) {
502
				for ($i = 0, $c = count($array[$priority]); $i < $c; $i++) {
503 13
					$this->insertAtIndexInPriority($array[$priority][$i], null, $priority);
504
				}
505 13
			}
506 11
		} elseif ($data instanceof TPriorityMap) {
507
			if ($this->getCount() > 0) {
508 11
				$this->clear();
509
			}
510
			$array = $data->toPriorityArray();
511
			foreach (array_keys($array) as $priority) {
512
				foreach ($array[$priority] as $item) {
513
					$this->insertAtIndexInPriority($item, null, $priority);
514
				}
515
			}
516
		} elseif (is_array($data) || $data instanceof \Traversable) {
517
			if ($this->getCount() > 0) {
518
				$this->clear();
519
			}
520
			foreach ($data as $item) {
521 48
				$this->insertAtIndexInPriority($item);
522
			}
523 48
		} elseif ($data !== null) {
524
			throw new TInvalidDataTypeException('map_data_not_iterable');
525 48
		}
526 48
	}
527 48
528 46
	/**
529 46
	 * Merges iterable data into the priority list.
530 46
	 * New data will be appended to the end of the existing data.  If another TPriorityList is merged,
531
	 * the incoming parameter items will be appended at the priorities they are present.  These items will be added
532 11
	 * to the end of the existing items with equal priorities, if there are any.
533
	 * @param mixed $data the data to be merged with, must be an array or object implementing Traversable
534
	 * @throws TInvalidDataTypeException If data is neither an array nor an iterator.
535
	 */
536 4
	public function mergeWith($data): void
537
	{
538
		if ($data instanceof TPriorityList) {
539
			$array = $data->toPriorityArray();
540
			foreach (array_keys($array) as $priority) {
541
				for ($i = 0, $c = count($array[$priority]); $i < $c; $i++) {
542
					$this->insertAtIndexInPriority($array[$priority][$i], null, $priority);
543
				}
544
			}
545
		} elseif ($data instanceof TPriorityMap) {
546
			$array = $data->toPriorityArray();
547
			foreach (array_keys($array) as $priority) {
548 12
				foreach ($array[$priority] as $item) {
549
					$this->insertAtIndexInPriority($item, null, $priority);
550 12
				}
551 4
			}
552
		} elseif (is_array($data) || $data instanceof \Traversable) {
553
			foreach ($data as $item) {
554 12
				$this->insertAtIndexInPriority($item);
555 12
			}
556 12
		} elseif ($data !== null) {
557 12
			throw new TInvalidDataTypeException('map_data_not_iterable');
558 7
		}
559
	}
560 12
561 12
	/**
562
	 * Returns whether there is an element at the specified offset.
563
	 * This method is required by the interface \ArrayAccess.
564
	 * @param mixed $offset the offset to check on
565
	 * @return bool
566
	 */
567
	public function offsetExists($offset): bool
568
	{
569
		return ($offset >= 0 && $offset < $this->getCount());
570
	}
571
572
	/**
573
	 * Sets the element at the specified offset. This method is required by the interface \ArrayAccess.
574
	 * Setting elements in a priority list is not straight forword when appending and setting at the
575 3
	 * end boundary.  When appending without an offset (a null offset), the item will be added at
576
	 * the default priority.  The item may not be the last item in the list.  When appending with an
577 3
	 * offset equal to the count of the list, the item will get be appended with the last items priority.
578 1
	 *
579
	 * All together, when setting the location of an item, the item stays in that location, but appending
580
	 * an item into a priority list doesn't mean the item is at the end of the list.
581 2
	 * @param int $offset the offset to set element
582 1
	 * @param mixed $item the element value
583
	 */
584
	public function offsetSet($offset, $item): void
585 1
	{
586
		if ($offset === null) {
0 ignored issues
show
introduced by
The condition $offset === null is always false.
Loading history...
587 1
			$this->add($item);
588
			return;
589
		}
590
		if (0 <= $offset && $offset <= ($count = $this->getCount())) {
591
			$priority = $this->priorityAt($offset, true);
592
			if ($offset !== $count) {
593
				$this->removeAtIndexInPriority($priority[1], $priority[0]);
594
			}
595
		} else {
596
			throw new TInvalidDataValueException('list_index_invalid', $offset);
597
		}
598 3
		$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
599
	}
600 3
601 1
	/**
602
	 * Unsets the element at the specified offset.
603
	 * This method is required by the interface \ArrayAccess.
604 2
	 * @param mixed $offset the offset to unset element
605 1
	 */
606
	public function offsetUnset($offset): void
607
	{
608 1
		$this->removeAt($offset);
609
	}
610 1
611
	/**
612
	 * Returns an array with the names of all variables of this object that should NOT be serialized
613
	 * because their value is the default one or useless to be cached for the next page loads.
614
	 * Reimplement in derived classes to add new variables, but remember to  also to call the parent
615
	 * implementation first.
616 70
	 * @param array $exprops by reference
617
	 * @since 4.3.0
618 70
	 */
619
	protected function _getZappableSleepProps(&$exprops)
620
	{
621
		parent::_getZappableSleepProps($exprops);
622
		$this->_priorityZappableSleepProps($exprops);
623
	}
624
}
625