Passed
Branch php-cs-fixer (b9836a)
by Fabio
15:02
created

TPriorityList::toPriorityArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * TPriorityList, TPriorityListIterator classes
4
 *
5
 * @author Brad Anderson <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @copyright Copyright &copy; 2005-2016 The PRADO Group
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 * @package Prado\Collections
10
 */
11
12
namespace Prado\Collections;
13
14
use Prado\Exceptions\TInvalidDataTypeException;
15
use Prado\Exceptions\TInvalidDataValueException;
16
use Prado\Exceptions\TInvalidOperationException;
17
use Prado\TPropertyValue;
18
19
/**
20
 * TPriorityList class
21
 *
22
 * TPriorityList implements a priority ordered list collection class.  It allows you to specify
23
 * any numeric for priorities down to a specific precision.  The lower the numeric, the high the priority of the item in the
24
 * list.  Thus -10 has a higher priority than -5, 0, 10 (the default), 18, 10005, etc.  Per {@link round}, precision may be negative and
25
 * thus rounding can go by 10, 100, 1000, etc, instead of just .1, .01, .001, etc. The default precision allows for 8 decimal
26
 * places. There is also a default priority of 10, if no different default priority is specified or no item specific priority is indicated.
27
 * If you replace TList with this class it will  work exactly the same with items inserted set to the default priority, until you start
28
 * using different priorities than the default priority.
29
 *
30
 * As you access the PHP array features of this class, it flattens and caches the results.  If at all possible, this
31
 * will keep the cache fresh even when manipulated.  If this is not possible the cache is cleared.
32
 * When an array of items are needed and the cache is outdated, the cache is recreated from the items and their priorities
33
 *
34
 * You can access, append, insert, remove an item by using
35
 * {@link itemAt}, {@link add}, {@link insertAt}, and {@link remove}.
36
 * To get the number of the items in the list, use {@link getCount}.
37
 * TPriorityList can also be used like a regular array as follows,
38
 * <code>
39
 * $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
40
 * $list[$index]=$item; // $index must be between 0 and $list->Count-1.  This sets the element regardless of priority.  Priority stays the same.
41
 * $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.
42
 * unset($list[$index]); // remove the item at $index
43
 * if(isset($list[$index])) // if the list has an item at $index
44
 * foreach($list as $index=>$item) // traverse each item in the list in proper priority order and add/insert order
45
 * $n=count($list); // returns the number of items in the list
46
 * </code>
47
 *
48
 * To extend TPriorityList for doing your own operations with each addition or removal,
49
 * override {@link insertAtIndexInPriority()} and {@link removeAtIndexInPriority()} and then call the parent.
50
 *
51
 * @author Brad Anderson <[email protected]>
52
 * @package Prado\Collections
53
 * @since 3.2a
54
 */
55
class TPriorityList extends TList
56
{
57
	/**
58
	 * @var array internal data storage
59
	 */
60
	private $_d = [];
61
	/**
62
	 * @var boolean indicates if the _d is currently ordered.
63
	 */
64
	private $_o = false;
65
	/**
66
	 * @var array cached flattened internal data storage
67
	 */
68
	private $_fd;
69
	/**
70
	 * @var integer number of items contain within the list
71
	 */
72
	private $_c = 0;
73
	/**
74
	 * @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...
75
	 */
76
	private $_dp = 10;
77
	/**
78
	 * @var integer the precision of the numeric priorities within this priority list.
79
	 */
80
	private $_p = 8;
81
82
	/**
83
	 * Constructor.
84
	 * Initializes the list with an array or an iterable object.
0 ignored issues
show
Bug introduced by
The type Prado\Collections\the 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...
85
	 * @param array|Iterator the intial data. Default is null, meaning no initial data.
86
	 * @param boolean whether the list is read-only
0 ignored issues
show
Bug introduced by
The type Prado\Collections\whether 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...
87
	 * @param numeric the default priority of items without specified priorities.
88
	 * @param integer the precision of the numeric priorities
89
	 * @throws TInvalidDataTypeException If data is not null and is neither an array nor an iterator.
90
	 */
91
	public function __construct($data = null, $readOnly = false, $defaultPriority = 10, $precision = 8)
92
	{
93
		parent::__construct();
94
		if($data !== null)
95
			$this->copyFrom($data);
96
		$this->setReadOnly($readOnly);
97
		$this->setPrecision($precision);
98
		$this->setDefaultPriority($defaultPriority);
99
	}
100
101
	/**
102
	 * Returns the number of items in the list.
103
	 * This method is required by \Countable interface.
104
	 * @return integer number of items in the list.
105
	 */
106
	public function count()
107
	{
108
		return $this->getCount();
109
	}
110
111
	/**
112
	 * Returns the total number of items in the list
113
	 * @return integer the number of items in the list
114
	 */
115
	public function getCount()
116
	{
117
		return $this->_c;
118
	}
119
120
	/**
121
	 * Gets the number of items at a priority within the list
122
	 * @param numeric optional priority at which to count items.  if no parameter, it will be set to the default {@link getDefaultPriority}
0 ignored issues
show
Bug introduced by
The type Prado\Collections\optional 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...
123
	 * @return integer the number of items in the list at the specified priority
124
	 */
125
	public function getPriorityCount($priority = null)
126
	{
127
		if($priority === null)
128
			$priority = $this->getDefaultPriority();
129
		$priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p);
130
131
		if(!isset($this->_d[$priority]) || !is_array($this->_d[$priority]))
132
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
133
		return count($this->_d[$priority]);
134
	}
135
136
	/**
137
	 * @return numeric gets the default priority of inserted items without a specified priority
138
	 */
139
	public function getDefaultPriority()
140
	{
141
		return $this->_dp;
142
	}
143
144
	/**
145
	 * This must be called internally or when instantiated.
146
	 * @param numeric sets the default priority of inserted items without a specified priority
0 ignored issues
show
Bug introduced by
The type Prado\Collections\sets 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...
147
	 */
148
	protected function setDefaultPriority($value)
149
	{
150
		$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...
151
	}
152
153
	/**
154
	 * @return integer The precision of numeric priorities, defaults to 8
155
	 */
156
	public function getPrecision()
157
	{
158
		return $this->_p;
159
	}
160
161
	/**
162
	 * This must be called internally or when instantiated.
163
	 * @param integer The precision of numeric priorities.
0 ignored issues
show
Bug introduced by
The type Prado\Collections\The 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...
164
	 */
165
	protected function setPrecision($value)
166
	{
167
		$this->_p = TPropertyValue::ensureInteger($value);
168
	}
169
170
	/**
171
	 * Returns an iterator for traversing the items in the list.
172
	 * This method is required by the interface \IteratorAggregate.
173
	 * @return Iterator an iterator for traversing the items in the list.
0 ignored issues
show
Bug introduced by
The type Prado\Collections\Iterator was not found. Did you mean Iterator? If so, make sure to prefix the type with \.
Loading history...
174
	 */
175
	public function getIterator()
176
	{
177
		return new \ArrayIterator($this->flattenPriorities());
0 ignored issues
show
Bug Best Practice introduced by
The expression return new ArrayIterator...s->flattenPriorities()) returns the type ArrayIterator which is incompatible with the documented return type Prado\Collections\Iterator.
Loading history...
178
	}
179
180
	/**
181
	 * This returns a list of the priorities within this list, ordered lowest to highest.
182
	 * @return array the array of priority numerics in decreasing priority order
183
	 */
184
	public function getPriorities()
185
	{
186
		$this->sortPriorities();
187
		return array_keys($this->_d);
188
	}
189
190
191
	/**
192
	 * This orders the priority list internally.
193
	 */
194
	protected function sortPriorities() {
195
		if(!$this->_o) {
196
			ksort($this->_d, SORT_NUMERIC);
197
			$this->_o = true;
198
		}
199
	}
200
201
	/**
202
	 * This flattens the priority list into a flat array [0,...,n-1]
203
	 * @return array array of items in the list in priority and index order
204
	 */
205
	protected function flattenPriorities() {
206
		if(is_array($this->_fd))
0 ignored issues
show
introduced by
The condition is_array($this->_fd) can never be false.
Loading history...
207
			return $this->_fd;
208
209
		$this->sortPriorities();
210
		$this->_fd = [];
211
		foreach($this->_d as $priority => $itemsatpriority)
212
			$this->_fd = array_merge($this->_fd, $itemsatpriority);
213
		return $this->_fd;
214
	}
215
216
217
	/**
218
	 * Returns the item at the index of a flattened priority list.
219
	 * {@link offsetGet} calls this method.
220
	 * @param integer the index of the item to get
221
	 * @return mixed the element at the offset
222
	 * @throws TInvalidDataValueException Issued when the index is invalid
223
	 */
224
	public function itemAt($index)
225
	{
226
		if($index >= 0 && $index < $this->getCount()) {
227
			$arr = $this->flattenPriorities();
228
			return $arr[$index];
229
		} else
230
			throw new TInvalidDataValueException('list_index_invalid', $index);
231
	}
232
233
	/**
234
	 * Gets all the items at a specific priority.
0 ignored issues
show
Bug introduced by
The type Prado\Collections\priority 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...
235
	 * @param numeric priority of the items to get.  Defaults to null, filled in with the default priority, if left blank.
236
	 * @return array all items at priority in index order, null if there are no items at that priority
237
	 */
238
	public function itemsAtPriority($priority = null)
239
	{
240
		if($priority === null)
241
			$priority = $this->getDefaultPriority();
242
		$priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p);
243
244
		return isset($this->_d[$priority])?$this->_d[$priority]:null;
245
	}
246
247
	/**
248
	 * Returns the item at an index within a priority
249
	 * @param integer the index into the list of items at priority
250
	 * @param numeric the priority which to index.  no parameter or null will result in the default priority
251
	 * @return mixed the element at the offset, false if no element is found at the offset
252
	 */
253
	public function itemAtIndexInPriority($index, $priority = null)
254
	{
255
		if($priority === null)
256
			$priority = $this->getDefaultPriority();
257
		$priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p);
258
259
		return !isset($this->_d[$priority])?false:(
260
				isset($this->_d[$priority][$index])?$this->_d[$priority][$index]:false
261
			);
262
	}
263
264
	/**
265
	 * Appends an item into the list at the end of the specified priority.  The position of the added item may
0 ignored issues
show
Bug introduced by
The type Prado\Collections\item 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...
266
	 * not be at the end of the list.
267
	 * @param mixed item to add into the list at priority
268
	 * @param numeric priority blank or null for the default priority
269
	 * @return int the index within the flattened array
270
	 * @throws TInvalidOperationException if the map is read-only
271
	 */
272
	public function add($item, $priority = null)
273
	{
274
		if($this->getReadOnly())
275
			throw new TInvalidOperationException('list_readonly', get_class($this));
276
277
		return $this->insertAtIndexInPriority($item, false, $priority, true);
278
	}
279
280
	/**
281
	 * Inserts an item at an index.  It reads the priority of the item at index within the flattened list
282
	 * and then inserts the item at that priority-index.
283
	 * @param integer the specified position in the flattened list.
284
	 * @param mixed new item to add
0 ignored issues
show
Bug introduced by
The type Prado\Collections\new 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...
285
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
286
	 * @throws TInvalidOperationException if the list is read-only
287
	 */
288
	public function insertAt($index, $item)
289
	{
290
		if($this->getReadOnly())
291
			throw new TInvalidOperationException('list_readonly', get_class($this));
292
293
		if(($priority = $this->priorityAt($index, true)) !== false)
0 ignored issues
show
introduced by
The condition $priority = $this->prior...$index, true) !== false can never be false.
Loading history...
294
			$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
295
		else
296
			throw new TInvalidDataValueException('list_index_invalid', $index);
297
	}
298
299
	/**
300
	 * Inserts an item at the specified index within a priority.  Override and call this method to
0 ignored issues
show
Bug introduced by
The type Prado\Collections\index 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...
301
	 * insert your own functionality.
302
	 * @param mixed item to add within the list.
303
	 * @param integer index within the priority to add the item, defaults to false which appends the item at the priority
304
	 * @param numeric priority priority of the item.  defaults to null, which sets it to the default priority
305
	 * @param boolean preserveCache specifies if this is a special quick function or not. This defaults to false.
0 ignored issues
show
Bug introduced by
The type Prado\Collections\preserveCache 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...
306
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
307
	 * @throws TInvalidOperationException if the list is read-only
308
	 */
309
	public function insertAtIndexInPriority($item, $index = false, $priority = null, $preserveCache = false)
310
	{
311
		if($this->getReadOnly())
312
			throw new TInvalidOperationException('list_readonly', get_class($this));
313
314
		if($priority === null)
315
			$priority = $this->getDefaultPriority();
316
		$priority = (string)round(TPropertyValue::ensureFloat($priority), $this->_p);
317
318
		if($preserveCache) {
319
			$this->sortPriorities();
320
			$cc = 0;
321
			foreach($this->_d as $prioritykey => $items)
322
				if($prioritykey >= $priority)
323
					break;
324
				else
325
					$cc += count($items);
326
327
			if($index === false && isset($this->_d[$priority])) {
328
				$c = count($this->_d[$priority]);
329
				$c += $cc;
330
				$this->_d[$priority][] = $item;
331
			} elseif(isset($this->_d[$priority])) {
332
				$c = $index + $cc;
333
				array_splice($this->_d[$priority], $index, 0, [$item]);
334
			} else {
335
				$c = $cc;
336
				$this->_o = false;
337
				$this->_d[$priority] = [$item];
338
			}
339
340
			if($this->_fd && is_array($this->_fd)) // if there is a flattened array cache
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_fd of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
341
				array_splice($this->_fd, $c, 0, [$item]);
342
		} else {
343
			$c = null;
344
			if($index === false && isset($this->_d[$priority])) {
345
				$cc = count($this->_d[$priority]);
346
				$this->_d[$priority][] = $item;
347
			} elseif(isset($this->_d[$priority])) {
348
				$cc = $index;
349
				array_splice($this->_d[$priority], $index, 0, [$item]);
350
			} else {
351
				$cc = 0;
352
				$this->_o = false;
353
				$this->_d[$priority] = [$item];
354
			}
355
			if($this->_fd && is_array($this->_fd) && count($this->_d) == 1)
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_fd of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

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

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
728
		if($offset === $this->getCount()) {
729
			$priority = $this->priorityAt($offset - 1, true);
730
			$priority[1]++;
731
		} else {
732
			$priority = $this->priorityAt($offset, true);
733
			$this->removeAtIndexInPriority($priority[1], $priority[0]);
734
		}
735
		$this->insertAtIndexInPriority($item, $priority[1], $priority[0]);
736
	}
737
738
	/**
739
	 * Unsets the element at the specified offset.
740
	 * This method is required by the interface \ArrayAccess.
741
	 * @param mixed the offset to unset element
742
	 */
743
	public function offsetUnset($offset)
744
	{
745
		$this->removeAt($offset);
746
	}
747
}
748