Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.98% covered (success)
98.98%
97 / 98
97.50% covered (success)
97.50%
39 / 40
CRAP
0.00% covered (danger)
0.00%
0 / 1
Nav
98.98% covered (success)
98.98%
97 / 98
97.50% covered (success)
97.50%
39 / 40
58
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 returnFalse
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setTree
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 addBranch
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 addLeaf
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 setConfig
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
4
 setAcl
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setRole
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addRole
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addRoles
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setAclStrict
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setIndent
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setBaseUrl
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setParentLevel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 incrementParentLevel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 decrementParentLevel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setChildLevel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 incrementChildLevel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 decrementChildLevel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isReturnFalse
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isAclStrict
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTree
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAcl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasAcl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasRoles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasRole
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRoles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRole
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIndent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBaseUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getParentLevel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getChildLevel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNav
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 nav
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 build
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 rebuild
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 traverseTree
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
8.05
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Nav;
15
16use Pop\Acl\Acl;
17use Pop\Acl\AclRole;
18use Pop\Dom\Child;
19
20/**
21 * Nav class
22 *
23 * @category   Pop
24 * @package    Pop\Nav
25 * @author     Nick Sagona, III <dev@nolainteractive.com>
26 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
27 * @license    http://www.popphp.org/license     New BSD License
28 * @version    3.3.0
29 */
30class 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}