Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.98% |
97 / 98 |
|
97.50% |
39 / 40 |
CRAP | |
0.00% |
0 / 1 |
Nav | |
98.98% |
97 / 98 |
|
97.50% |
39 / 40 |
58 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
returnFalse | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setTree | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
addBranch | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
addLeaf | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
setConfig | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
4 | |||
setAcl | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setRole | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
addRole | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addRoles | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setAclStrict | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setIndent | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setBaseUrl | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setParentLevel | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
incrementParentLevel | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
decrementParentLevel | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setChildLevel | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
incrementChildLevel | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
decrementChildLevel | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isReturnFalse | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isAclStrict | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTree | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getConfig | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAcl | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasAcl | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasRoles | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasRole | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRoles | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRole | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getIndent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getBaseUrl | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getParentLevel | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getChildLevel | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getNav | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
nav | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
build | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
rebuild | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
render | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
traverseTree | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
8.05 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (https://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@noladev.com> |
7 | * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. |
8 | * @license https://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Nav; |
15 | |
16 | use Pop\Acl\Acl; |
17 | use Pop\Acl\AclRole; |
18 | use Pop\Dom\Child; |
19 | |
20 | /** |
21 | * Nav class |
22 | * |
23 | * @category Pop |
24 | * @package Pop\Nav |
25 | * @author Nick Sagona, III <dev@noladev.com> |
26 | * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. |
27 | * @license https://www.popphp.org/license New BSD License |
28 | * @version 4.1.4 |
29 | */ |
30 | class Nav |
31 | { |
32 | |
33 | /** |
34 | * Nav tree |
35 | * @var array |
36 | */ |
37 | protected array $tree = []; |
38 | |
39 | /** |
40 | * Nav config |
41 | * @var array |
42 | */ |
43 | protected array $config = []; |
44 | |
45 | /** |
46 | * Acl object |
47 | * @var ?Acl |
48 | */ |
49 | protected ?Acl $acl = null; |
50 | |
51 | /** |
52 | * AclRole role objects |
53 | * @var array |
54 | */ |
55 | protected array $roles = []; |
56 | |
57 | /** |
58 | * Acl strict flag |
59 | * @var bool |
60 | */ |
61 | protected bool $aclStrict = false; |
62 | |
63 | /** |
64 | * Indentation |
65 | * @var ?string |
66 | */ |
67 | protected ?string $indent = null; |
68 | |
69 | /** |
70 | * Base URL |
71 | * @var ?string |
72 | */ |
73 | protected ?string $baseUrl = null; |
74 | |
75 | /** |
76 | * Nav parent level |
77 | * @var int |
78 | */ |
79 | protected int $parentLevel = 1; |
80 | |
81 | /** |
82 | * Nav child level |
83 | * @var int |
84 | */ |
85 | protected int $childLevel = 1; |
86 | |
87 | /** |
88 | * Return false flag |
89 | * @var bool |
90 | */ |
91 | protected bool $returnFalse = false; |
92 | |
93 | /** |
94 | * Parent nav element |
95 | * @var ?Child |
96 | */ |
97 | protected ?Child $nav = null; |
98 | |
99 | /** |
100 | * Constructor |
101 | * |
102 | * Instantiate the nav object |
103 | * |
104 | * @param ?array $tree |
105 | * @param ?array $config |
106 | */ |
107 | public function __construct(?array $tree = null, ?array $config = null) |
108 | { |
109 | $this->setTree($tree); |
110 | $this->setConfig($config); |
111 | } |
112 | |
113 | /** |
114 | * Set the return false flag |
115 | * |
116 | * @param bool $return |
117 | * @return Nav |
118 | */ |
119 | public function returnFalse(bool $return): Nav |
120 | { |
121 | $this->returnFalse = $return; |
122 | return $this; |
123 | } |
124 | |
125 | /** |
126 | * Set the nav tree |
127 | * |
128 | * @param ?array $tree |
129 | * @return Nav |
130 | */ |
131 | public function setTree(?array $tree = null): Nav |
132 | { |
133 | $this->tree = ($tree !== null) ? $tree : []; |
134 | return $this; |
135 | } |
136 | |
137 | /** |
138 | * Add to a nav tree branch |
139 | * |
140 | * @param array $branch |
141 | * @param bool $prepend |
142 | * @return Nav |
143 | */ |
144 | public function addBranch(array $branch, bool $prepend = false): Nav |
145 | { |
146 | if (isset($branch['name'])) { |
147 | $branch = [$branch]; |
148 | } |
149 | $this->tree = ($prepend) ? array_merge($branch, $this->tree) : array_merge($this->tree, $branch); |
150 | return $this; |
151 | } |
152 | |
153 | /** |
154 | * Add to a leaf to nav tree branch |
155 | * |
156 | * @param string $branch |
157 | * @param array $leaf |
158 | * @param ?int $pos |
159 | * @param bool $prepend |
160 | * @return Nav |
161 | */ |
162 | public function addLeaf(string $branch, array $leaf, ?int $pos = null, bool $prepend = false): Nav |
163 | { |
164 | $this->tree = $this->traverseTree($this->tree, $branch, $leaf, $pos, $prepend); |
165 | $this->parentLevel = 1; |
166 | $this->childLevel = 1; |
167 | return $this; |
168 | } |
169 | |
170 | /** |
171 | * Set the nav tree |
172 | * |
173 | * @param ?array $config |
174 | * @return Nav |
175 | */ |
176 | public function setConfig(?array $config = null): Nav |
177 | { |
178 | if ($config === null) { |
179 | $this->config = [ |
180 | 'top' => [ |
181 | 'node' => 'nav' |
182 | ], |
183 | 'parent' => [ |
184 | 'node' => 'nav' |
185 | ], |
186 | 'child' => [ |
187 | 'node' => 'nav' |
188 | ] |
189 | ]; |
190 | } else { |
191 | $this->config = $config; |
192 | } |
193 | |
194 | if (isset($config['indent'])) { |
195 | $this->setIndent($config['indent']); |
196 | } |
197 | |
198 | if (isset($config['baseUrl'])) { |
199 | $this->setBaseUrl($config['baseUrl']); |
200 | } |
201 | |
202 | return $this; |
203 | } |
204 | |
205 | /** |
206 | * Set the Acl object |
207 | * |
208 | * @param ?Acl $acl |
209 | * @return Nav |
210 | */ |
211 | public function setAcl(?Acl $acl = null): Nav |
212 | { |
213 | $this->acl = $acl; |
214 | return $this; |
215 | } |
216 | |
217 | /** |
218 | * Set a AclRole object (alias method) |
219 | * |
220 | * @param ?AclRole $role |
221 | * @return Nav |
222 | */ |
223 | public function setRole(?AclRole $role = null): Nav |
224 | { |
225 | $this->roles[$role->getName()] = $role; |
226 | return $this; |
227 | } |
228 | |
229 | /** |
230 | * Add a AclRole object |
231 | * |
232 | * @param ?AclRole $role |
233 | * @return Nav |
234 | */ |
235 | public function addRole(?AclRole $role = null): Nav |
236 | { |
237 | return $this->setRole($role); |
238 | } |
239 | |
240 | /** |
241 | * Add AclRole objects |
242 | * |
243 | * @param array $roles |
244 | * @return Nav |
245 | */ |
246 | public function addRoles(array $roles): Nav |
247 | { |
248 | foreach ($roles as $role) { |
249 | $this->setRole($role); |
250 | } |
251 | |
252 | return $this; |
253 | } |
254 | |
255 | /** |
256 | * Set the Acl object as strict evaluation |
257 | * |
258 | * @param bool $strict |
259 | * @return Nav |
260 | */ |
261 | public function setAclStrict(bool $strict): Nav |
262 | { |
263 | $this->aclStrict = $strict; |
264 | return $this; |
265 | } |
266 | |
267 | /** |
268 | * Set the indent |
269 | * |
270 | * @param string $indent |
271 | * @return Nav |
272 | */ |
273 | public function setIndent(string $indent): Nav |
274 | { |
275 | $this->indent = $indent; |
276 | return $this; |
277 | } |
278 | |
279 | /** |
280 | * Set the base URL |
281 | * |
282 | * @param string $baseUrl |
283 | * @return Nav |
284 | */ |
285 | public function setBaseUrl(string $baseUrl): Nav |
286 | { |
287 | $this->baseUrl = $baseUrl; |
288 | return $this; |
289 | } |
290 | |
291 | /** |
292 | * Set parent level |
293 | * |
294 | * @param int $level |
295 | * @return Nav |
296 | */ |
297 | public function setParentLevel(int $level): Nav |
298 | { |
299 | $this->parentLevel = $level; |
300 | return $this; |
301 | } |
302 | |
303 | /** |
304 | * Increment parent level |
305 | * |
306 | * @return Nav |
307 | */ |
308 | public function incrementParentLevel(): Nav |
309 | { |
310 | $this->parentLevel++; |
311 | return $this; |
312 | } |
313 | |
314 | /** |
315 | * Decrement parent level |
316 | * |
317 | * @return Nav |
318 | */ |
319 | public function decrementParentLevel(): Nav |
320 | { |
321 | $this->parentLevel--; |
322 | return $this; |
323 | } |
324 | |
325 | /** |
326 | * Set child level |
327 | * |
328 | * @param int $level |
329 | * @return Nav |
330 | */ |
331 | public function setChildLevel(int $level): Nav |
332 | { |
333 | $this->childLevel = $level; |
334 | return $this; |
335 | } |
336 | |
337 | /** |
338 | * Increment child level |
339 | * |
340 | * @return Nav |
341 | */ |
342 | public function incrementChildLevel(): Nav |
343 | { |
344 | $this->childLevel++; |
345 | return $this; |
346 | } |
347 | |
348 | /** |
349 | * Decrement child level |
350 | * |
351 | * @return Nav |
352 | */ |
353 | public function decrementChildLevel(): Nav |
354 | { |
355 | $this->childLevel--; |
356 | return $this; |
357 | } |
358 | |
359 | /** |
360 | * Set the return false flag |
361 | * |
362 | * @return bool |
363 | */ |
364 | public function isReturnFalse(): bool |
365 | { |
366 | return $this->returnFalse; |
367 | } |
368 | |
369 | /** |
370 | * Determine if the Acl object is set as strict evaluation |
371 | * |
372 | * @return bool |
373 | */ |
374 | public function isAclStrict(): bool |
375 | { |
376 | return $this->aclStrict; |
377 | } |
378 | |
379 | /** |
380 | * Get the nav tree |
381 | * |
382 | * @return array |
383 | */ |
384 | public function getTree(): array |
385 | { |
386 | return $this->tree; |
387 | } |
388 | |
389 | /** |
390 | * Get the config |
391 | * |
392 | * @return array |
393 | */ |
394 | public function getConfig(): array |
395 | { |
396 | return $this->config; |
397 | } |
398 | |
399 | /** |
400 | * Get the Acl object |
401 | * |
402 | * @return Acl|null |
403 | */ |
404 | public function getAcl(): Acl|null |
405 | { |
406 | return $this->acl; |
407 | } |
408 | |
409 | /** |
410 | * Determine if there is an ACL object |
411 | * |
412 | * @return bool |
413 | */ |
414 | public function hasAcl(): bool |
415 | { |
416 | return ($this->acl !== null); |
417 | } |
418 | |
419 | /** |
420 | * Determine if there are roles |
421 | * |
422 | * @return bool |
423 | */ |
424 | public function hasRoles(): bool |
425 | { |
426 | return (count($this->roles) > 0); |
427 | } |
428 | |
429 | /** |
430 | * Determine if there is a certain role |
431 | * |
432 | * @param string $name |
433 | * @return bool |
434 | */ |
435 | public function hasRole(string $name): bool |
436 | { |
437 | return (isset($this->roles[$name])); |
438 | } |
439 | |
440 | /** |
441 | * Get the AclRole objects |
442 | * |
443 | * @return array |
444 | */ |
445 | public function getRoles(): array |
446 | { |
447 | return $this->roles; |
448 | } |
449 | |
450 | /** |
451 | * Get a AclRole object |
452 | * |
453 | * @param string $name |
454 | * @return AclRole|null |
455 | */ |
456 | public function getRole(string $name): AclRole|null |
457 | { |
458 | return $this->roles[$name] ?? null; |
459 | } |
460 | |
461 | /** |
462 | * Get the indent |
463 | * |
464 | * @return string|null |
465 | */ |
466 | public function getIndent(): string|null |
467 | { |
468 | return $this->indent; |
469 | } |
470 | |
471 | /** |
472 | * Get the base URL |
473 | * |
474 | * @return string|null |
475 | */ |
476 | public function getBaseUrl(): string|null |
477 | { |
478 | return $this->baseUrl; |
479 | } |
480 | |
481 | /** |
482 | * Get parent level |
483 | * |
484 | * @return int |
485 | */ |
486 | public function getParentLevel(): int |
487 | { |
488 | return $this->parentLevel; |
489 | } |
490 | |
491 | /** |
492 | * Get child level |
493 | * |
494 | * @return int |
495 | */ |
496 | public function getChildLevel(): int |
497 | { |
498 | return $this->childLevel; |
499 | } |
500 | |
501 | /** |
502 | * Get the nav object |
503 | * |
504 | * @return Child |
505 | */ |
506 | public function getNav(): Child |
507 | { |
508 | if ($this->nav === null) { |
509 | $this->nav = NavBuilder::build($this, $this->tree); |
510 | } |
511 | return $this->nav; |
512 | } |
513 | |
514 | /** |
515 | * Get the nav object (alias) |
516 | * |
517 | * @return Child |
518 | */ |
519 | public function nav(): Child |
520 | { |
521 | return $this->getNav(); |
522 | } |
523 | |
524 | /** |
525 | * Build the nav object |
526 | * |
527 | * @return Nav |
528 | */ |
529 | public function build(): Nav |
530 | { |
531 | if ($this->nav === null) { |
532 | $this->nav = NavBuilder::build($this, $this->tree); |
533 | } |
534 | return $this; |
535 | } |
536 | |
537 | /** |
538 | * Re-build the nav object |
539 | * |
540 | * @return Nav |
541 | */ |
542 | public function rebuild(): Nav |
543 | { |
544 | $this->parentLevel = 1; |
545 | $this->childLevel = 1; |
546 | $this->nav = NavBuilder::build($this, $this->tree); |
547 | return $this; |
548 | } |
549 | |
550 | /** |
551 | * Render the nav object |
552 | * |
553 | * @return string |
554 | */ |
555 | public function render(): string |
556 | { |
557 | if ($this->nav === null) { |
558 | $this->nav = NavBuilder::build($this, $this->tree); |
559 | } |
560 | |
561 | return ($this->nav->hasChildren()) ? $this->nav->render() : ''; |
562 | } |
563 | |
564 | /** |
565 | * Render Nav object to string |
566 | * |
567 | * @return string |
568 | */ |
569 | public function __toString() |
570 | { |
571 | return $this->render(); |
572 | } |
573 | |
574 | /** |
575 | * Traverse tree to insert new leaf |
576 | * |
577 | * @param array $tree |
578 | * @param string $branch |
579 | * @param array $newLeaf |
580 | * @param ?int $pos |
581 | * @param bool $prepend |
582 | * @param int $depth |
583 | * @return array |
584 | */ |
585 | protected function traverseTree( |
586 | array $tree, string $branch, array $newLeaf, ?int $pos = null, bool $prepend = false, int $depth = 0 |
587 | ): array |
588 | { |
589 | $t = []; |
590 | foreach ($tree as $leaf) { |
591 | if ((($pos === null) || ($pos == $depth)) && ($leaf['name'] == $branch)) { |
592 | if (isset($leaf['children'])) { |
593 | $leaf['children'] = ($prepend) ? |
594 | array_merge([$newLeaf], $leaf['children']) : array_merge($leaf['children'], [$newLeaf]); |
595 | } else { |
596 | $leaf['children'] = [$newLeaf]; |
597 | } |
598 | } |
599 | if (isset($leaf['children'])) { |
600 | $leaf['children'] = $this->traverseTree($leaf['children'], $branch, $newLeaf, $pos, $prepend, ($depth + 1)); |
601 | } |
602 | $t[] = $leaf; |
603 | } |
604 | |
605 | return $t; |
606 | } |
607 | |
608 | } |