Issues (43)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/MenuBuilder.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Nwidart\Menus;
4
5
use Countable;
6
use Illuminate\Contracts\Config\Repository;
7
use Illuminate\Support\Arr;
8
use Illuminate\View\Factory as ViewFactory;
9
10
class MenuBuilder implements Countable
11
{
12
    /**
13
     * Menu name.
14
     *
15
     * @var string
16
     */
17
    protected $menu;
18
19
    /**
20
     * Array menu items.
21
     *
22
     * @var array
23
     */
24
    protected $items = [];
25
26
    /**
27
     * Default presenter class.
28
     *
29
     * @var string
30
     */
31
    protected $presenter = Presenters\Bootstrap\NavbarPresenter::class;
32
33
    /**
34
     * Style name for each presenter.
35
     *
36
     * @var array
37
     */
38
    protected $styles = [];
39
40
    /**
41
     * Prefix URL.
42
     *
43
     * @var string|null
44
     */
45
    protected $prefixUrl;
46
47
    /**
48
     * The name of view presenter.
49
     *
50
     * @var string
51
     */
52
    protected $view;
53
54
    /**
55
     * The laravel view factory instance.
56
     *
57
     * @var \Illuminate\View\Factory
58
     */
59
    protected $views;
60
61
    /**
62
     * Determine whether the ordering feature is enabled or not.
63
     *
64
     * @var boolean
65
     */
66
    protected $ordering = false;
67
68
    /**
69
     * Resolved item binding map.
70
     *
71
     * @var array
72
     */
73
    protected $bindings = [];
74
    /**
75
     * @var Repository
76
     */
77
    private $config;
78
79
    /**
80
     * Constructor.
81
     *
82
     * @param string $menu
83
     * @param Repository $config
84
     */
85 10
    public function __construct($menu, Repository $config)
86
    {
87 10
        $this->menu = $menu;
88 10
        $this->config = $config;
89 10
    }
90
91
    /**
92
     * Get menu name.
93
     *
94
     * @return string
95
     */
96 1
    public function getName()
97
    {
98 1
        return $this->menu;
99
    }
100
101
    /**
102
     * Find menu item by given its title.
103
     *
104
     * @param  string        $title
105
     * @param  callable|null $callback
106
     * @return mixed
107
     */
108
    public function whereTitle($title, callable $callback = null)
109
    {
110
        $item = $this->findBy('title', $title);
111
112
        if (is_callable($callback)) {
113
            return call_user_func($callback, $item);
114
        }
115
116
        return $item;
117
    }
118
119
    /**
120
     * Find menu item by given key and value.
121
     *
122
     * @param  string $key
123
     * @param  string $value
124
     * @return \Nwidart\Menus\MenuItem
125
     */
126
    public function findBy($key, $value)
127
    {
128
        return collect($this->items)->filter(function ($item) use ($key, $value) {
129
            return $item->{$key} == $value;
130
        })->first();
131
    }
132
133
    /**
134
     * Set view factory instance.
135
     *
136
     * @param ViewFactory $views
137
     *
138
     * @return $this
139
     */
140 9
    public function setViewFactory(ViewFactory $views)
141
    {
142 9
        $this->views = $views;
143
144 9
        return $this;
145
    }
146
147
    /**
148
     * Set view.
149
     *
150
     * @param string $view
151
     *
152
     * @return $this
153
     */
154
    public function setView($view)
155
    {
156
        $this->view = $view;
157
158
        return $this;
159
    }
160
161
    /**
162
     * Set Prefix URL.
163
     *
164
     * @param string $prefixUrl
165
     *
166
     * @return $this
167
     */
168
    public function setPrefixUrl($prefixUrl)
169
    {
170
        $this->prefixUrl = $prefixUrl;
171
172
        return $this;
173
    }
174
175
    /**
176
     * Set styles.
177
     *
178
     * @param array $styles
179
     */
180
    public function setStyles(array $styles)
181
    {
182
        $this->styles = $styles;
183
    }
184
185
    /**
186
     * Set new presenter class.
187
     *
188
     * @param string $presenter
189
     */
190
    public function setPresenter($presenter)
191
    {
192
        $this->presenter = $presenter;
193
    }
194
195
    /**
196
     * Get presenter instance.
197
     *
198
     * @return \Nwidart\Menus\Presenters\PresenterInterface
199
     */
200 3
    public function getPresenter()
201
    {
202 3
        return new $this->presenter();
203
    }
204
205
    /**
206
     * Set new presenter class by given style name.
207
     *
208
     * @param string $name
209
     *
210
     * @return self
211
     */
212
    public function style($name)
213
    {
214
        if ($this->hasStyle($name)) {
215
            $this->setPresenter($this->getStyle($name));
216
        }
217
218
        return $this;
219
    }
220
221
    /**
222
     * Determine if the given name in the presenter style.
223
     *
224
     * @param $name
225
     *
226
     * @return bool
227
     */
228 3
    public function hasStyle($name)
229
    {
230 3
        return array_key_exists($name, $this->getStyles());
231
    }
232
233
    /**
234
     * Get style aliases.
235
     *
236
     * @return mixed
237
     */
238 3
    public function getStyles()
239
    {
240 3
        return $this->styles ?: $this->config->get('menus.styles');
241
    }
242
243
    /**
244
     * Get the presenter class name by given alias name.
245
     *
246
     * @param $name
247
     *
248
     * @return mixed
249
     */
250
    public function getStyle($name)
251
    {
252
        $style = $this->getStyles();
253
254
        return $style[$name];
255
    }
256
257
    /**
258
     * Set new presenter class from given alias name.
259
     *
260
     * @param $name
261
     */
262
    public function setPresenterFromStyle($name)
263
    {
264
        $this->setPresenter($this->getStyle($name));
265
    }
266
267
    /**
268
     * Set the resolved item bindings
269
     *
270
     * @param array $bindings
271
     * @return $this
272
     */
273 3
    public function setBindings(array $bindings)
274
    {
275 3
        $this->bindings = $bindings;
276
277 3
        return $this;
278
    }
279
280
    /**
281
     * Resolves a key from the bindings array.
282
     *
283
     * @param  string|array $key
284
     * @return mixed
285
     */
286
    public function resolve($key)
287
    {
288
        if (is_array($key)) {
289
            foreach ($key as $k => $v) {
290
                $key[$k] = $this->resolve($v);
291
            }
292
        } elseif (is_string($key)) {
293
            $matches = array();
294
            preg_match_all('/{[\s]*?([^\s]+)[\s]*?}/i', $key, $matches, PREG_SET_ORDER);
295
            foreach ($matches as $match) {
296
                if (array_key_exists($match[1], $this->bindings)) {
297
                    $key = preg_replace('/' . $match[0] . '/', $this->bindings[$match[1]], $key, 1);
298
                }
299
            }
300
        }
301
302
        return $key;
303
    }
304
305
    /**
306
     * Resolves an array of menu items properties.
307
     *
308
     * @param  array  &$items
309
     * @return void
310
     */
311 3
    protected function resolveItems(array &$items)
312
    {
313
        $resolver = function ($property) {
314
            return $this->resolve($property) ?: $property;
315 3
        };
316
317 3
        $totalItems = count($items);
318 3
        for ($i = 0; $i < $totalItems; $i++) {
319
            $items[$i]->fill(array_map($resolver, $items[$i]->getProperties()));
320
        }
321 3
    }
322
323
    /**
324
     * Add new child menu.
325
     *
326
     * @param array $attributes
327
     *
328
     * @return \Nwidart\Menus\MenuItem
329
     */
330
    public function add(array $attributes = array())
331
    {
332
        $item = MenuItem::make($attributes);
333
334
        $this->items[] = $item;
335
336
        return $item;
337
    }
338
339
    /**
340
     * Create new menu with dropdown.
341
     *
342
     * @param $title
343
     * @param callable $callback
344
     * @param array    $attributes
345
     *
346
     * @return $this
347
     */
348 View Code Duplication
    public function dropdown($title, \Closure $callback, $order = null, array $attributes = array())
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
349
    {
350
        $properties = compact('title', 'order', 'attributes');
351
352
        if (func_num_args() == 3) {
353
            $arguments = func_get_args();
354
355
            $title = Arr::get($arguments, 0);
356
            $attributes = Arr::get($arguments, 2);
357
358
            $properties = compact('title', 'attributes');
359
        }
360
361
        $item = MenuItem::make($properties);
362
363
        call_user_func($callback, $item);
364
365
        $this->items[] = $item;
366
367
        return $item;
368
    }
369
370
    /**
371
     * Register new menu item using registered route.
372
     *
373
     * @param $route
374
     * @param $title
375
     * @param array $parameters
376
     * @param array $attributes
377
     *
378
     * @return static
379
     */
380
    public function route($route, $title, $parameters = array(), $order = null, $attributes = array())
381
    {
382 View Code Duplication
        if (func_num_args() == 4) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
383
            $arguments = func_get_args();
384
385
            return $this->add([
386
                'route' => [Arr::get($arguments, 0), Arr::get($arguments, 2)],
387
                'title' => Arr::get($arguments, 1),
388
                'attributes' => Arr::get($arguments, 3),
389
            ]);
390
        }
391
392
        $route = array($route, $parameters);
393
394
        $item = MenuItem::make(
395
            compact('route', 'title', 'parameters', 'attributes', 'order')
396
        );
397
398
        $this->items[] = $item;
399
400
        return $item;
401
    }
402
403
    /**
404
     * Format URL.
405
     *
406
     * @param string $url
407
     *
408
     * @return string
409
     */
410 2
    protected function formatUrl($url)
411
    {
412 2
        $uri = !is_null($this->prefixUrl) ? $this->prefixUrl . $url : $url;
413
414 2
        return $uri == '/' ? '/' : ltrim(rtrim($uri, '/'), '/');
415
    }
416
417
    /**
418
     * Register new menu item using url.
419
     *
420
     * @param $url
421
     * @param $title
422
     * @param array $attributes
423
     *
424
     * @return static
425
     */
426 2
    public function url($url, $title, $order = 0, $attributes = array())
427
    {
428 2 View Code Duplication
        if (func_num_args() == 3) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
429
            $arguments = func_get_args();
430
431
            return $this->add([
432
                'url' => $this->formatUrl(Arr::get($arguments, 0)),
433
                'title' => Arr::get($arguments, 1),
434
                'attributes' => Arr::get($arguments, 2),
435
            ]);
436
        }
437
438 2
        $url = $this->formatUrl($url);
439
440 2
        $item = MenuItem::make(compact('url', 'title', 'order', 'attributes'));
441
442 2
        $this->items[] = $item;
443
444 2
        return $item;
445
    }
446
447
    /**
448
     * Add new divider item.
449
     *
450
     * @param int $order
451
     * @return \Nwidart\Menus\MenuItem
452
     */
453
    public function addDivider($order = null)
454
    {
455
        $this->items[] = new MenuItem(array('name' => 'divider', 'order' => $order));
456
457
        return $this;
458
    }
459
460
    /**
461
     * Add new header item.
462
     *
463
     * @return \Nwidart\Menus\MenuItem
464
     */
465
    public function addHeader($title, $order = null)
466
    {
467
        $this->items[] = new MenuItem(array(
468
            'name' => 'header',
469
            'title' => $title,
470
            'order' => $order,
471
        ));
472
473
        return $this;
474
    }
475
476
    /**
477
     * Alias for "addHeader" method.
478
     *
479
     * @param string $title
480
     *
481
     * @return $this
482
     */
483
    public function header($title)
484
    {
485
        return $this->addHeader($title);
486
    }
487
488
    /**
489
     * Alias for "addDivider" method.
490
     *
491
     * @return $this
492
     */
493
    public function divider()
494
    {
495
        return $this->addDivider();
496
    }
497
498
    /**
499
     * Get items count.
500
     *
501
     * @return int
502
     */
503 1
    public function count()
504
    {
505 1
        return count($this->items);
506
    }
507
508
    /**
509
     * Empty the current menu items.
510
     */
511
    public function destroy()
512
    {
513
        $this->items = array();
514
515
        return $this;
516
    }
517
518
    /**
519
     * Render the menu to HTML tag.
520
     *
521
     * @param string $presenter
522
     *
523
     * @return string
524
     */
525 3
    public function render($presenter = null)
526
    {
527 3
        $this->resolveItems($this->items);
528
529 3
        if (!is_null($this->view)) {
530
            return $this->renderView($presenter);
531
        }
532
533 3
        if ($this->hasStyle($presenter)) {
534
            $this->setPresenterFromStyle($presenter);
535
        }
536
537 3
        if (!is_null($presenter) && !$this->hasStyle($presenter)) {
538
            $this->setPresenter($presenter);
539
        }
540
541 3
        return $this->renderMenu();
542
    }
543
544
    /**
545
     * Render menu via view presenter.
546
     *
547
     * @return \Illuminate\View\View
548
     */
549
    public function renderView($presenter = null)
550
    {
551
        return $this->views->make($presenter ?: $this->view, [
552
            'items' => $this->getOrderedItems(),
553
        ]);
554
    }
555
556
    /**
557
     * Get original items.
558
     *
559
     * @return array
560
     */
561
    public function getItems()
562
    {
563
        return $this->items;
564
    }
565
566
    /**
567
     * Get menu items as laravel collection instance.
568
     *
569
     * @return \Illuminate\Support\Collection
570
     */
571
    public function toCollection()
572
    {
573
        return collect($this->items);
574
    }
575
576
    /**
577
     * Get menu items as array.
578
     *
579
     * @return array
580
     */
581
    public function toArray()
582
    {
583
        return $this->toCollection()->toArray();
584
    }
585
586
    /**
587
     * Enable menu ordering.
588
     *
589
     * @return self
590
     */
591
    public function enableOrdering()
592
    {
593
        $this->ordering = true;
594
595
        return $this;
596
    }
597
598
    /**
599
     * Disable menu ordering.
600
     *
601
     * @return self
602
     */
603
    public function disableOrdering()
604
    {
605
        $this->ordering = false;
606
607
        return $this;
608
    }
609
610
    /**
611
     * Get menu items and order it by 'order' key.
612
     *
613
     * @return array
614
     */
615 3
    public function getOrderedItems()
616
    {
617 3
        if (config('menus.ordering') || $this->ordering) {
618
            return $this->toCollection()->sortBy(function ($item) {
619
                return $item->order;
620
            })->all();
621
        }
622
623 3
        return $this->items;
624
    }
625
626
    /**
627
     * Render the menu.
628
     *
629
     * @return string
630
     */
631 3
    protected function renderMenu()
632
    {
633 3
        $presenter = $this->getPresenter();
634 3
        $menu = $presenter->getOpenTagWrapper();
635
636 3
        foreach ($this->getOrderedItems() as $item) {
637
            if ($item->hidden()) {
638
                continue;
639
            }
640
641
            if ($item->hasSubMenu()) {
642
                $menu .= $presenter->getMenuWithDropDownWrapper($item);
643
            } elseif ($item->isHeader()) {
644
                $menu .= $presenter->getHeaderWrapper($item);
645
            } elseif ($item->isDivider()) {
646
                $menu .= $presenter->getDividerWrapper();
647
            } else {
648
                $menu .= $presenter->getMenuWithoutDropdownWrapper($item);
649
            }
650
        }
651
652 3
        $menu .= $presenter->getCloseTagWrapper();
653
654 3
        return $menu;
655
    }
656
}
657