Completed
Push — master ( e09c01...ac9146 )
by De Cramer
02:18 queued 02:12
created

ParentItem::addChild()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.2
c 0
b 0
f 0
cc 3
eloc 15
nc 4
nop 5
1
<?php
2
3
namespace eXpansion\Bundle\Menu\Model\Menu;
4
use eXpansion\Bundle\Menu\Services\ItemBuilder;
5
use eXpansion\Framework\AdminGroups\Helpers\AdminGroups;
6
use eXpansion\Framework\Core\Model\Gui\ManialinkInterface;
7
use eXpansion\Framework\Core\Plugins\Gui\ManialinkFactory;
8
use FML\Controls\Quad;
9
10
/**
11
 * Class ParentItem
12
 *
13
 * @author    de Cramer Oliver<[email protected]>
14
 * @copyright 2017 Smile
15
 * @package eXpansion\Bundle\Menu\Model\Menu
16
 */
17
class ParentItem extends AbstractItem
18
{
19
    /** @var ItemBuilder */
20
    protected $itemBuilder;
21
22
    /** @var ItemInterface[] */
23
    private $childItems = [];
24
25
    /**
26
     * ParentItem constructor.
27
     *
28
     * @param ItemBuilder $itemBuilder
29
     * @param string      $id
30
     * @param string      $path
31
     * @param string      $labelId
32
     * @param AdminGroups $adminGroups
33
     * @param null        $permission
34
     */
35
    public function __construct(
36
        ItemBuilder $itemBuilder,
37
        $id,
38
        $path,
39
        $labelId,
40
        AdminGroups $adminGroups,
41
        $permission = null
42
    ) {
43
        parent::__construct($id, $path, $labelId, $adminGroups, $permission);
44
        $this->itemBuilder = $itemBuilder;
45
    }
46
47
48
    /**
49
     * @inheritdoc
50
     */
51
    public function execute(
52
        ManialinkFactory $manialinkFactory,
53
        ManialinkInterface $manialink,
54
        $login,
55
        $answerValues,
56
        $args
57
    ) {
58
        $manialink->setData('current_path', $this->getPath());
59
        $manialinkFactory->update($manialink->getUserGroup());
60
    }
61
62
    /**
63
     * Add a new child element to the parent node.
64
     *
65
     * @param string $class Class of the item.
66
     * @param string $id Id of the item
67
     * @param string $label
68
     * @param string $permission
69
     * @param array $options
70
     *
71
     * @return ItemInterface
72
     */
73
    public function addChild($class, $id, $label, $permission, $options =[])
74
    {
75
        if (is_string($id)) {
76
            $id = explode('/', $id);
77
        }
78
79
        if (count($id) == 1) {
80
            $item = $this->itemBuilder->create(
81
                $class,
82
                $id[0],
83
                $this->getPath() . '/' . $id[0],
84
                $label,
85
                $permission,
86
                $options
87
            );
88
            $this->childItems[$id[0]] = $item;
89
            return $item;
90
        }
91
92
        $parent = array_splice($id, 0, count($id) - 1);
93
        return $this->getChild($parent)->addChild($class, $id, $label, $permission, $options);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface eXpansion\Bundle\Menu\Model\Menu\ItemInterface as the method addChild() does only exist in the following implementations of said interface: eXpansion\Bundle\Menu\Model\Menu\ParentItem.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
94
    }
95
96
    /**
97
     * Get child element from path py searching recursiveley.
98
     *
99
     * @param $path
100
     *
101
     * @return ItemInterface|null
102
     */
103
    public function getChild($path)
104
    {
105
        if (is_string($path)) {
106
            $path = explode('/', $path);
107
        }
108
        $remaining = array_splice($path, 1, count($path) - 1);
109
110
        foreach ($this->childItems as $childItem) {
111
            if ($childItem->getId() == $path[0]) {
112
                if (empty($remaining)) {
113
                    return $childItem;
114
                } elseif ($childItem instanceof ParentItem) {
115
                    return $childItem->getChild($remaining);
116
                } else {
117
                    return null;
118
                }
119
            }
120
        }
121
122
        return null;
123
    }
124
125
    /**
126
     * Get childrens of this parent.
127
     *
128
     * @return ItemInterface[]
129
     */
130
    public function getChilds()
131
    {
132
        return $this->childItems;
133
    }
134
135
    /**
136
     * Check if menu is visible and at least one children is visible.
137
     *
138
     * @param string $login
139
     *
140
     * @return bool|mixed
141
     */
142
    public function isVisibleFor($login)
143
    {
144
        $personalVisibility = parent::isVisibleFor($login);
145
        if (!$personalVisibility) {
146
            return $personalVisibility;
147
        }
148
149
150
        foreach ($this->getChilds() as $childItem) {
151
            if ($childItem->isVisibleFor($login)) {
152
                return true;
153
            }
154
        }
155
156
        return false;
157
    }
158
}
159