Passed
Push — master ( 246025...18c029 )
by Fabio
05:31
created

TPriorityList::remove()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 33
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 22
nc 8
nop 2
dl 0
loc 33
rs 8.4444
c 0
b 0
f 0
ccs 11
cts 11
cp 1
crap 8
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 implements IPriorityCollection
53
{
54
	use TPriorityCollectionTrait;
55
56
	/**
57
	 * Constructor.
58
	 * Initializes the list with an array or an iterable object.
59
	 * @param null|array|\Iterator $data the initial data. Default is null, meaning no initial data.
60
	 * @param ?bool $readOnly whether the list is read-only
61
	 * @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...
62
	 *   priorities. Default null for 10.
63
	 * @param ?int $precision the precision of the numeric priorities.  Default null for 8.
64
	 * @throws TInvalidDataTypeException If data is not null and is neither an array nor an iterator.
65
	 */
66
	public function __construct($data = null, $readOnly = null, $defaultPriority = null, $precision = null)
67
	{
68
		$this->setPrecision($precision);
69
		$this->setDefaultPriority($defaultPriority);
70
		parent::__construct($data, $readOnly);
71
	}
72
73
	/**
74
	 * This is required for TPriorityCollectionTrait to determine the style of combining
75
	 * arrays.
76
	 * @return bool This returns true for array_merge (list style).  false would be
77
	 *   array_replace (map style).
78
	 */
79
	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...
80
	{
81
		return true;
82
	}
83
84
	/**
85
	 * Returns the item at the index of a flattened priority list.
86
	 * {@link offsetGet} calls this method.
87
	 * @param int $index the index of the item to get
88
	 * @throws TInvalidDataValueException Issued when the index is invalid
89
	 * @return mixed the element at the offset
90 90
	 */
91
	public function itemAt($index)
92 90
	{
93 90
		if ($index >= 0 && $index < $this->getCount()) {
94 25
			$this->flattenPriorities();
95
			return $this->_fd[$index];
96 90
		} else {
97 90
			throw new TInvalidDataValueException('list_index_invalid', $index);
98 90
		}
99 90
	}
100
101
	/**
102
	 * Returns the item at an index within a priority
103
	 * @param int $index the index into the list of items at priority
104
	 * @param null|numeric $priority the priority which to index.  no parameter or null
105
	 *   will result in the default priority
106 3
	 * @throws TInvalidDataValueException if the index is out of the range at the
107
	 *   priority or no items at the priority.
108 3
	 * @return mixed the element at the offset, false if no element is found at the offset
109
	 */
110
	public function itemAtIndexInPriority($index, $priority = null)
111
	{
112
		$priority = $this->ensurePriority($priority);
113
		if (isset($this->_d[$priority]) && 0 <= $index && $index < count($this->_d[$priority])) {
114
			return $this->_d[$priority][$index];
115 133
		} else {
116
			throw new TInvalidDataValueException('prioritylist_index_invalid', $index, count($this->_d[$priority] ?? []), $priority);
117 133
		}
118
	}
119
120
	/**
121
	 * Appends an item into the list at the end of the specified priority.  The position of the added item may
122
	 * not be at the end of the list.
123
	 * @param mixed $item item to add into the list at priority
124
	 * @param null|numeric $priority priority blank or null for the default priority
125 2
	 * @throws TInvalidOperationException if the map is read-only
126
	 * @return int the index within the flattened array
127 2
	 */
128 2
	public function add($item, $priority = null)
129
	{
130 2
		if ($this->getReadOnly()) {
131
			throw new TInvalidOperationException('list_readonly', $this::class);
132 2
		}
133
134
		return $this->insertAtIndexInPriority($item, null, $priority, true);
135 2
	}
136
137
	/**
138
	 * Inserts an item at an index.  It reads the priority of the item at index within the flattened list
139
	 * and then inserts the item at that priority-index.
140
	 * @param int $index the specified position in the flattened list.
141 116
	 * @param mixed $item new item to add
142
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
143 116
	 * @throws TInvalidOperationException if the list is read-only
144
	 * @return ?float The priority of the inserted item.
145
	 */
146
	public function insertAt($index, $item)
147
	{
148
		if ($this->getReadOnly()) {
149
			throw new TInvalidOperationException('list_readonly', $this::class);
150 90
		}
151
152 90
		if (($priority = $this->priorityAt($index, true)) !== false) {
153 90
			$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
154
			return $priority[0];
155
		} else {
156
			throw new TInvalidDataValueException('list_index_invalid', $index);
157
		}
158 1
	}
159
160 1
	/**
161
	 * Inserts an item at the specified index within a priority.  Override and call this method to
162
	 * insert your own functionality.
163
	 * @param mixed $item item to add within the list.
164
	 * @param null|false|int $index index within the priority to add the item, defaults to null which appends the item at the priority
165
	 * @param null|numeric $priority priority of the item.  defaults to null, which sets it to the default priority
166
	 * @param bool $preserveCache preserveCache specifies if this is a special quick function or not. This defaults to false.
167 90
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
168
	 * @throws TInvalidOperationException if the list is read-only
169 90
	 * @todo PRADO version 4.3 remove $index: false => null conversion. false should be invalid input data.
170 90
	 */
171
	public function insertAtIndexInPriority($item, $index = null, $priority = null, $preserveCache = false)
172
	{
173
		$this->collapseReadOnly();
174
		if ($this->getReadOnly()) {
175
			throw new TInvalidOperationException('list_readonly', $this::class);
176
		}
177 2
		if ($index === false) {
178
			$index = null; // conversion, remove for PRADO 4.3
179 2
		}
180
		$itemPriority = null;
181
		if (($isPriorityItem = ($item instanceof IPriorityItem)) && ($priority === null || !is_numeric($priority))) {
182
			$itemPriority = $priority = $item->getPriority();
183
		}
184
		$priority = $this->ensurePriority($priority);
185
		if (($item instanceof IPriorityCapture) && (!$isPriorityItem || $itemPriority !== $priority)) {
186 16
			$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

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

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

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