Passed
Push — master ( 4fc6c5...494471 )
by Fabio
07:54 queued 03:10
created

TPriorityCollectionTrait::setPrecision()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

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

137
		$this->setDefaultPriority(/** @scrutinizer ignore-type */ $this->_dp);
Loading history...
138
		$_d = [];
139
		foreach(array_keys($this->_d) as $priority) {
140
			$newPriority = $this->ensurePriority($priority);
141
			if (array_key_exists($newPriority, $_d)) {
142
				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

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