Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
53 / 53 |
|
100.00% |
17 / 17 |
CRAP | |
100.00% |
1 / 1 |
PriorityQueue | |
100.00% |
53 / 53 |
|
100.00% |
17 / 17 |
32 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
insert | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
2 | |||
getInsertPosition | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
6 | |||
getInsertFIFO | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getInsertLIFO | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getInsertHighest | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getInsertLowest | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
increaseAll | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
pop | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
delete | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
setPriority | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
get | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAll | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
count | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
serialize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
unserialize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Stdlib\Queue |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Stdlib\Queue; |
16 | |
17 | use phpOMS\Contract\SerializableInterface; |
18 | use phpOMS\Stdlib\Base\Exception\InvalidEnumValue; |
19 | |
20 | /** |
21 | * Priority queue class. |
22 | * |
23 | * @package phpOMS\Stdlib\Queue |
24 | * @license OMS License 2.0 |
25 | * @link https://jingga.app |
26 | * @since 1.0.0 |
27 | */ |
28 | class PriorityQueue implements \Countable, SerializableInterface |
29 | { |
30 | /** |
31 | * Queue type. |
32 | * |
33 | * @var int |
34 | * @since 1.0.0 |
35 | */ |
36 | private int $type = PriorityMode::FIFO; |
37 | |
38 | /** |
39 | * Queue. |
40 | * |
41 | * @var array<int, array{data:mixed, priority:float}> |
42 | * @since 1.0.0 |
43 | */ |
44 | private array $queue = []; |
45 | |
46 | /** |
47 | * Constructor. |
48 | * |
49 | * @throws InvalidEnumValue This exception is thrown if the priority mode is invalid |
50 | * |
51 | * @since 1.0.0 |
52 | */ |
53 | public function __construct(int $type = PriorityMode::FIFO) |
54 | { |
55 | if (!PriorityMode::isValidValue($type)) { |
56 | throw new InvalidEnumValue($type); |
57 | } |
58 | |
59 | $this->type = $type; |
60 | } |
61 | |
62 | /** |
63 | * Get priority queue type |
64 | * |
65 | * @return int |
66 | * |
67 | * @since 1.0.0 |
68 | */ |
69 | public function getType() : int |
70 | { |
71 | return $this->type; |
72 | } |
73 | |
74 | /** |
75 | * Insert element into queue. |
76 | * |
77 | * @param mixed $data Queue element |
78 | * @param float $priority Priority of this element |
79 | * |
80 | * @return int |
81 | * |
82 | * @since 1.0.0 |
83 | */ |
84 | public function insert(mixed $data, float $priority = 1.0) : int |
85 | { |
86 | do { |
87 | $key = \mt_rand(); |
88 | } while (isset($this->queue[$key])); |
89 | |
90 | if (empty($this->queue)) { |
91 | $this->queue[$key] = ['data' => $data, 'priority' => $priority]; |
92 | } else { |
93 | $pos = $this->getInsertPosition($priority); |
94 | |
95 | $this->queue = \array_slice($this->queue, 0, $pos, true) |
96 | + [$key => ['data' => $data, 'priority' => $priority]] |
97 | + \array_slice($this->queue, $pos, null, true); |
98 | } |
99 | |
100 | return $key; |
101 | } |
102 | |
103 | /** |
104 | * Get insert position |
105 | * |
106 | * @param float $priority Priority of new element |
107 | * |
108 | * @return int |
109 | * |
110 | * @throws InvalidEnumValue |
111 | * |
112 | * @since 1.0.0 |
113 | */ |
114 | private function getInsertPosition(float $priority) : int |
115 | { |
116 | switch($this->type) { |
117 | case PriorityMode::FIFO: |
118 | return $this->getInsertFIFO(); |
119 | case PriorityMode::LIFO: |
120 | return $this->getInsertLIFO(); |
121 | case PriorityMode::HIGHEST: |
122 | return $this->getInsertHighest($priority); |
123 | case PriorityMode::LOWEST: |
124 | return $this->getInsertLowest($priority); |
125 | default: |
126 | throw new InvalidEnumValue($this->type); // @codeCoverageIgnore |
127 | } |
128 | } |
129 | |
130 | /** |
131 | * Get insert position |
132 | * |
133 | * @return int |
134 | * |
135 | * @since 1.0.0 |
136 | */ |
137 | private function getInsertFIFO() : int |
138 | { |
139 | return 0; |
140 | } |
141 | |
142 | /** |
143 | * Get insert position |
144 | * |
145 | * @return int |
146 | * |
147 | * @since 1.0.0 |
148 | */ |
149 | private function getInsertLIFO() : int |
150 | { |
151 | return \count($this->queue); |
152 | } |
153 | |
154 | /** |
155 | * Get insert position |
156 | * |
157 | * @param float $priority Priority of new element |
158 | * |
159 | * @return int |
160 | * |
161 | * @since 1.0.0 |
162 | */ |
163 | private function getInsertHighest(float $priority) : int |
164 | { |
165 | $pos = 0; |
166 | foreach ($this->queue as $ele) { |
167 | if ($ele['priority'] > $priority) { |
168 | break; |
169 | } |
170 | |
171 | ++$pos; |
172 | } |
173 | |
174 | return $pos; |
175 | } |
176 | |
177 | /** |
178 | * Get insert position |
179 | * |
180 | * @param float $priority Priority of new element |
181 | * |
182 | * @return int |
183 | * |
184 | * @since 1.0.0 |
185 | */ |
186 | private function getInsertLowest(float $priority) : int |
187 | { |
188 | $pos = 0; |
189 | foreach ($this->queue as $ele) { |
190 | if ($ele['priority'] < $priority) { |
191 | break; |
192 | } |
193 | |
194 | ++$pos; |
195 | } |
196 | |
197 | return $pos; |
198 | } |
199 | |
200 | /** |
201 | * Increase all queue priorities. |
202 | * |
203 | * @param float $increase Value to increase every element |
204 | * |
205 | * @return void |
206 | * |
207 | * @since 1.0.0 |
208 | */ |
209 | public function increaseAll(float $increase = 1.0) : void |
210 | { |
211 | foreach ($this->queue as $key => &$ele) { |
212 | $ele['priority'] += $increase; |
213 | } |
214 | } |
215 | |
216 | /** |
217 | * Pop element. |
218 | * |
219 | * @return array |
220 | * |
221 | * @since 1.0.0 |
222 | */ |
223 | public function pop() : array |
224 | { |
225 | return \array_pop($this->queue) ?? []; |
226 | } |
227 | |
228 | /** |
229 | * Delete element. |
230 | * |
231 | * @param int $id Element to delete |
232 | * |
233 | * @return bool |
234 | * |
235 | * @since 1.0.0 |
236 | */ |
237 | public function delete(int $id) : bool |
238 | { |
239 | if (isset($this->queue[$id])) { |
240 | unset($this->queue[$id]); |
241 | |
242 | return true; |
243 | } |
244 | |
245 | return false; |
246 | } |
247 | |
248 | /** |
249 | * Set element priority. |
250 | * |
251 | * @param int $id Element ID |
252 | * @param float $priority Element priority |
253 | * |
254 | * @return void |
255 | * |
256 | * @since 1.0.0 |
257 | */ |
258 | public function setPriority(int $id, float $priority) : void |
259 | { |
260 | if ($this->type === PriorityMode::FIFO || $this->type === PriorityMode::LIFO) { |
261 | $this->queue[$id]['priority'] = $priority; |
262 | |
263 | return; |
264 | } |
265 | |
266 | $temp = $this->queue[$id]; |
267 | $this->delete($id); |
268 | |
269 | $pos = $this->getInsertPosition($priority); |
270 | |
271 | $this->queue = \array_slice($this->queue, 0, $pos, true) |
272 | + [$id => ['data' => $temp['data'], 'priority' => $priority]] |
273 | + \array_slice($this->queue, $pos, null, true); |
274 | } |
275 | |
276 | /** |
277 | * Get element |
278 | * |
279 | * @param int $id Element ID |
280 | * |
281 | * @return array |
282 | * |
283 | * @since 1.0.0 |
284 | */ |
285 | public function get(int $id) : array |
286 | { |
287 | return $this->queue[$id] ?? []; |
288 | } |
289 | |
290 | /** |
291 | * Get element |
292 | * |
293 | * @return array<int, array{data:mixed, priority:float}> |
294 | * |
295 | * @since 1.0.0 |
296 | */ |
297 | public function getAll() : array |
298 | { |
299 | return $this->queue; |
300 | } |
301 | |
302 | /** |
303 | * {@inheritdoc} |
304 | */ |
305 | public function count() : int |
306 | { |
307 | return \count($this->queue); |
308 | } |
309 | |
310 | /** |
311 | * {@inheritdoc} |
312 | */ |
313 | public function serialize() : string |
314 | { |
315 | return (string) \json_encode($this->queue); |
316 | } |
317 | |
318 | /** |
319 | * Unserialize queue. |
320 | * |
321 | * @param string $data Data to unserialze |
322 | * |
323 | * @return void |
324 | * |
325 | * @since 1.0.0 |
326 | */ |
327 | public function unserialize(mixed $data) : void |
328 | { |
329 | $this->queue = \json_decode($data, true); /* @phpstan-ignore-line */ |
330 | } |
331 | } |