Passed
Push — master ( 1e2338...e1e446 )
by Fabio
05:44
created

TPriorityCollectionTrait::toArrayBelowPriority()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 12
nc 9
nop 2
dl 0
loc 17
rs 8.8333
c 1
b 0
f 0
1
<?php
2
/**
3
 * TPriorityCollectionTrait 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
 * TPriorityCollectionTrait class
19
 *
20
 * This trait implements the common properties and methods of Priority Collection
21
 * classes.
22
 *
23
 * The trait adds a boolean for whether or not _d is ordered, a cached flattened
24
 * array _fd, a default priority (by default, 10) _dp, and precision of priorities (by
25
 * default, 8 [decimal places]) _p.
26
 *
27
 * The trait adds methods:
28
 *	- {@link getDefaultPriority} returns the default priority of items without priority.
29
 *	- {@link setDefaultPriority} sets the default priority. (protected)
30
 *	- {@link getPrecision} returns the precision of priorities.
31
 *	- {@link setPrecision} sets the precision of priorities. (protected)
32
 *	- {@link ensurePriority} standardize and round priorities. (protected)
33
 *	- {@link sortPriorities} sorts _d and flags as sorted. (protected)
34
 *	- {@link flattenPriorities} flattens the priority items, in order into cache.. (protected)
35
 *	- {@link getPriorities} gets the priorities of the collection.
36
 *	- {@link getPriorityCount} gets the number of items at a priority.
37
 *	- {@link itemsAtPriority} gets the items at a given priority.
38
 *	- {@link getIterator} overrides subclasses for an iterator of the flattened array.
39
 *	- {@link toArray} the flattened collection in order.
40
 *	- {@link toPriorityArray} the array of priorities (keys) and array of items (value).
41
 *	- {@link toArrayBelowPriority} the items below a Priority, default is not inclusive
42
 *	- {@link toArrayAbovePriority} the items above a priority, default is inclusive.
43
 *	- {@link _priorityZappableSleepProps} to add the excluded trait properties on sleep.
44
 *
45
 * The priorities are implemented as numeric strings.
46
 *
47
 * Any class using this trait must implement a getPriorityCombineStyle method to
48
 * determine if arrays are merged or replaced to combine together.
49
 *
50
 * For example, something like this is required in your class:
51
 * <code>
52
 *		private function getPriorityCombineStyle(): bool
53
 *		{
54
 *			return true; // for merge (list style), and false for replace (map style)
55
 *		}
56
 * </code>
57
 *
58
 * @author Brad Anderson <[email protected]>
59
 * @since 4.2.3
60
 */
61
trait TPriorityCollectionTrait
62
{
63
	/**
64
	 * @var bool indicates if the _d is currently ordered.
65
	 */
66
	protected bool $_o = false;
67
68
	/**
69
	 * @var null|array cached flattened internal data storage
70
	 */
71
	protected ?array $_fd = null;
72
73
	/**
74
	 * @var string the default priority of items without specified priorities
75
	 */
76
	private string $_dp = '10';
77
78
	/**
79
	 * @var int the precision of the numeric priorities within this priority list.
80
	 */
81
	private int $_p = 8;
82
83
	/**
84
	 * @return numeric gets the default priority of inserted items without a specified priority
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...
85
	 */
86
	public function getDefaultPriority()
87
	{
88
		return $this->_dp;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_dp returns the type string which is incompatible with the documented return type Prado\Collections\numeric.
Loading history...
89
	}
90
91
	/**
92
	 * This must be called internally or when instantiated.
93
	 * @param numeric $value sets the default priority of inserted items without a specified priority
94
	 */
95
	protected function setDefaultPriority($value)
96
	{
97
		$this->_dp = (string) round(TPropertyValue::ensureFloat($value), $this->_p);
98
	}
99
100
	/**
101
	 * @return int The precision of numeric priorities, defaults to 8
102
	 */
103
	public function getPrecision(): int
104
	{
105
		return $this->_p;
106
	}
107
108
	/**
109
	 * This must be called internally or when instantiated.
110
	 * This resets the array priorities to the new precision and adjusts
111
	 * the DefaultPriority to the new precision as well.
112
	 * @param int $value The precision of numeric priorities.
113
	 */
114
	protected function setPrecision($value): void
115
	{
116
		$this->_p = TPropertyValue::ensureInteger($value);
117
		$this->setDefaultPriority($this->_dp);
0 ignored issues
show
Bug introduced by
$this->_dp of type string is incompatible with the type Prado\Collections\numeric expected by parameter $value of Prado\Collections\TPrior...t::setDefaultPriority(). ( Ignorable by Annotation )

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

117
		$this->setDefaultPriority(/** @scrutinizer ignore-type */ $this->_dp);
Loading history...
118
		$_d = [];
119
		foreach(array_keys($this->_d) as $priority) {
120
			$newPriority = $this->ensurePriority($priority);
121
			if (array_key_exists($newPriority, $_d)) {
122
				if ($this->getPriorityCombineStyle()) {
0 ignored issues
show
Bug introduced by
The method getPriorityCombineStyle() does not exist on Prado\Collections\TPriorityCollectionTrait. Did you maybe mean getPriorityCount()? ( Ignorable by Annotation )

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

122
				if ($this->/** @scrutinizer ignore-call */ getPriorityCombineStyle()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
123
					$_d[$newPriority] = array_merge($_d[$newPriority], $this->_d[$priority]);
124
				} else {
125
					$_d[$newPriority] = array_replace($_d[$newPriority], $this->_d[$priority]);
126
				}
127
			} else {
128
				$_d[$newPriority] = $this->_d[$priority];
129
			}
130
		}
131
		$this->_d = $_d;
0 ignored issues
show
Bug Best Practice introduced by
The property _d does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
132
		$this->_fd = null;
133
	}
134
135
	/**
136
	 * Taken an input Priority and ensures its value.
137
	 * Sets the default $priority when none is set,
138
	 * then rounds to the proper precision and makes
139
	 * into a string.
140
	 * @param mixed $priority
141
	 * @return string the priority in string format
142
	 */
143
	protected function ensurePriority($priority): string
144
	{
145
		if ($priority === null || !is_numeric($priority)) {
146
			$priority = $this->getDefaultPriority();
147
		}
148
		return (string) round((float) $priority, $this->_p);
149
	}
150
151
152
	/**
153
	 * This orders the priority list internally.
154
	 */
155
	protected function sortPriorities(): void
156
	{
157
		if (!$this->_o) {
158
			ksort($this->_d, SORT_NUMERIC);
159
			$this->_o = true;
160
		}
161
	}
162
163
	/**
164
	 * This flattens the priority list into a flat array [0,...,n-1] (with array_merge)
165
	 * and a priority map into a single flat map of keys and values (with array_replace).
166
	 */
167
	protected function flattenPriorities(): void
168
	{
169
		if (is_array($this->_fd)) {
170
			return;
171
		}
172
		if (empty($this->_d)) {
173
			$this->_fd = [];
174
			return;
175
		}
176
		$this->sortPriorities();
177
		if ($this->getPriorityCombineStyle()) {
178
			$this->_fd = array_merge(...$this->_d);
179
		} else {
180
			$this->_fd = array_replace(...$this->_d);
181
		}
182
	}
183
184
	/**
185
	 * This returns a list of the priorities within this list, ordered lowest to highest.
186
	 * @return array the array of priority numerics in decreasing priority order
187
	 */
188
	public function getPriorities(): array
189
	{
190
		$this->sortPriorities();
191
		return array_keys($this->_d);
192
	}
193
194
	/**
195
	 * Gets the number of items at a priority within the list
196
	 * @param null|numeric $priority optional priority at which to count items.  if no parameter, it will be set to the default {@link getDefaultPriority}
197
	 * @return int the number of items in the list at the specified priority
198
	 */
199
	public function getPriorityCount($priority = null)
200
	{
201
		$priority = $this->ensurePriority($priority);
202
		if (empty($this->_d[$priority])) {
203
			return 0;
204
		}
205
		return count($this->_d[$priority]);
206
	}
207
208
	/**
209
	 * Returns an iterator for traversing the items in the list.
210
	 * This method is required by the interface \IteratorAggregate.
211
	 * @return \Iterator an iterator for traversing the items in the list.
212
	 */
213
	public function getIterator(): \Iterator
214
	{
215
		$this->flattenPriorities();
216
		return new \ArrayIterator($this->_fd);
217
	}
218
219
	/**
220
	 * Gets all the items at a specific priority.
221
	 * @param null|numeric $priority priority of the items to get.  Defaults to null, filled in with the default priority, if left blank.
222
	 * @return ?array all items at priority in index order, null if there are no items at that priority
223
	 */
224
	public function itemsAtPriority($priority = null): ?array
225
	{
226
		$priority = $this->ensurePriority($priority);
227
		return $this->_d[$priority] ?? null;
228
	}
229
230
	/**
231
	 * @return array the priority list of items in array
232
	 */
233
	public function toArray(): array
234
	{
235
		$this->flattenPriorities();
236
		return $this->_fd;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_fd could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
237
	}
238
239
	/**
240
	 * @return array the array of priorities keys with values of arrays of items.  The priorities are sorted so important priorities, lower numerics, are first.
241
	 */
242
	public function toPriorityArray(): array
243
	{
244
		$this->sortPriorities();
245
		return $this->_d;
246
	}
247
248
	/**
249
	 * Combines the map elements which have a priority below the parameter value
250
	 * @param numeric $priority the cut-off priority.  All items of priority less than this are returned.
251
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: false, not inclusive.
252
	 * @return array the array of priorities keys with values of arrays of items that are below a specified priority.
253
	 *  The priorities are sorted so important priorities, lower numerics, are first.
254
	 */
255
	public function toArrayBelowPriority($priority, bool $inclusive = false): array
256
	{
257
		$this->sortPriorities();
258
		$items = [];
259
		foreach ($this->_d as $itemspriority => $itemsatpriority) {
260
			if ((!$inclusive && $itemspriority >= $priority) || $itemspriority > $priority) {
261
				break;
262
			}
263
			$items[] = $itemsatpriority;
264
		}
265
		if(empty($items)) {
266
			return [];
267
		}
268
		if ($this->getPriorityCombineStyle()) {
269
			return array_merge(...$items);
270
		} else {
271
			return array_replace(...$items);
272
		}
273
	}
274
275
	/**
276
	 * Combines the map elements which have a priority above the parameter value
277
	 * @param numeric $priority the cut-off priority.  All items of priority greater than this are returned.
278
	 * @param bool $inclusive whether or not the input cut-off priority is inclusive.  Default: true, inclusive.
279
	 * @return array the array of priorities keys with values of arrays of items that are above a specified priority.
280
	 *  The priorities are sorted so important priorities, lower numerics, are first.
281
	 */
282
	public function toArrayAbovePriority($priority, bool $inclusive = true): array
283
	{
284
		$this->sortPriorities();
285
		$items = [];
286
		foreach ($this->_d as $itemspriority => $itemsatpriority) {
287
			if ((!$inclusive && $itemspriority <= $priority) || $itemspriority < $priority) {
288
				continue;
289
			}
290
			$items[] = $itemsatpriority;
291
		}
292
		if(empty($items)) {
293
			return [];
294
		}
295
		if ($this->getPriorityCombineStyle()) {
296
			return array_merge(...$items);
297
		} else {
298
			return array_replace(...$items);
299
		}
300
	}
301
302
	/**
303
	 * Returns an array with the names of all variables of this object that should NOT be serialized
304
	 * because their value is the default one or useless to be cached for the next page loads.
305
	 * Reimplement in derived classes to add new variables, but remember to  also to call the parent
306
	 * implementation first.
307
	 * @param array $exprops by reference
308
	 */
309
	protected function _priorityZappableSleepProps(&$exprops)
310
	{
311
		if ($this->_o === false) {
312
			$exprops[] = "\0*\0_o";
313
		}
314
		$exprops[] = "\0*\0_fd";
315
		if ($this->_dp == 10) {
316
			$exprops[] = "\0" . __CLASS__ . "\0_dp";
317
		}
318
		if ($this->_p === 8) {
319
			$exprops[] = "\0" . __CLASS__ . "\0_p";
320
		}
321
	}
322
}
323