Completed
Pull Request — master (#127)
by Aydin
01:52
created

SplitItem::addMenuItems()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
3
namespace PhpSchool\CliMenu\MenuItem;
4
5
use Assert\Assertion;
6
use PhpSchool\CliMenu\CliMenu;
7
use PhpSchool\CliMenu\CliMenuBuilder;
8
use PhpSchool\CliMenu\MenuStyle;
9
use PhpSchool\CliMenu\Util\StringUtil;
10
11
/**
12
 * @author Michael Woodward <[email protected]>
13
 */
14
class SplitItem implements MenuItemInterface
15
{
16
    /**
17
     * @var array
18
     */
19
    private $items;
20
21
    /**
22
     * @var int|null
23
     */
24
    private $selectedItemIndex;
25
26
    /**
27
     * @var bool
28
     */
29
    private $canBeSelected = true;
30
31
    /**
32
     * @var int
33
     */
34
    private $margin = 2;
35
36
    /**
37
     * @var array
38
     */
39
    private static $blacklistedItems = [
40
        \PhpSchool\CliMenu\MenuItem\AsciiArtItem::class,
41
        \PhpSchool\CliMenu\MenuItem\LineBreakItem::class,
42
        \PhpSchool\CliMenu\MenuItem\SplitItem::class,
43
    ];
44
45
    public function __construct(array $items = [])
46
    {
47
        $this->items = $items;
48
49
        $this->setDefaultSelectedItem();
50
    }
51
52
    public function addMenuItem(MenuItemInterface $item) : self
53
    {
54
        foreach (self::$blacklistedItems as $bl) {
55
            if ($item instanceof $bl) {
56
                throw new \InvalidArgumentException("Cannot add a $bl to a SplitItem");
57
            }
58
        }
59
        $this->items[] = $item;
60
        $this->setDefaultSelectedItem();
61
        return $this;
62
    }
63
64
    public function addMenuItems(array $items) : self
65
    {
66
        foreach ($items as $item) {
67
            $this->addMenuItem($item);
68
        }
69
        return $this;
70
    }
71
    
72
    public function setItems(array $items) : self
73
    {
74
        $this->items = [];
75
        $this->addMenuItems($items);
76
        return $this;
77
    }
78
79
    /**
80
     * Select default item
81
     */
82
    private function setDefaultSelectedItem() : void
83
    {
84
        foreach ($this->items as $index => $item) {
85
            if ($item->canSelect()) {
86
                $this->canBeSelected = true;
87
                $this->selectedItemIndex = $index;
0 ignored issues
show
Documentation Bug introduced by
It seems like $index can also be of type string. However, the property $selectedItemIndex is declared as type integer|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
88
                return;
89
            }
90
        }
91
92
        $this->canBeSelected = false;
93
        $this->selectedItemIndex = null;
94
    }
95
96
    /**
97
     * The output text for the item
98
     */
99
    public function getRows(MenuStyle $style, bool $selected = false) : array
100
    {
101
        $numberOfItems = count($this->items);
102
103
        if (!$selected) {
104
            $this->setDefaultSelectedItem();
105
        }
106
107
        $length = $style->getDisplaysExtra()
108
            ? floor(($style->getContentWidth() - mb_strlen($style->getItemExtra()) + 2) / $numberOfItems)
109
            : floor($style->getContentWidth() / $numberOfItems);
110
            
111
        $length -= $this->margin;
112
        
113
        $missingLength = $style->getContentWidth() % $numberOfItems;
114
115
        $cells = array_map(function ($index, $item) use ($selected, $length, $style) {
116
            $isSelected = $selected && $index === $this->selectedItemIndex;
117
            $marker = $item->canSelect()
118
                ? sprintf("%s ", $style->getMarker($isSelected))
119
                : sprintf("%s ", str_repeat(' ', mb_strlen($style->getMarker(false))));
120
            $content = StringUtil::wordwrap(
121
                sprintf('%s%s', $marker, $item->getText()),
122
                $length
123
            );
124
            return array_map(function ($row) use ($length, $style, $isSelected) {
125
                $invertedColoursSetCode = $isSelected
126
                    ? $style->getInvertedColoursSetCode()
127
                    : '';
128
                $invertedColoursUnsetCode = $isSelected
129
                    ? $style->getInvertedColoursUnsetCode()
130
                    : '';
131
132
                return sprintf(
133
                    "%s%s%s%s%s",
134
                    $invertedColoursSetCode,
135
                    $row,
136
                    str_repeat(' ', $length - mb_strlen($row)),
137
                    $invertedColoursUnsetCode,
138
                    str_repeat(' ', $this->margin)
139
                );
140
            }, explode("\n", $content));
141
        }, array_keys($this->items), $this->items);
142
143
        $lines = max(array_map('count', $cells));
144
145
        $rows = [];
146
        for ($i = 0; $i < $lines; $i++) {
147
            $row = "";
148
            if ($i > 0) {
149
                $row .= str_repeat(' ', 2);
150
            }
151
            foreach ($cells as $cell) {
152
                if (isset($cell[$i])) {
153
                    $row .= $cell[$i];
154
                } else {
155
                    $row .= str_repeat(' ', $length);
156
                }
157
            }
158
            if ($missingLength) {
159
                $row .= str_repeat(' ', $missingLength);
160
            }
161
            $rows[] = $row;
162
        }
163
164
        return $rows;
165
    }
166
167
    public function setSelectedItemIndex(int $index) : void
168
    {
169
        $this->selectedItemIndex = $index;
170
    }
171
172
    public function getSelectedItemIndex() : ?int
173
    {
174
        return $this->selectedItemIndex;
175
    }
176
177
    public function getSelectedItem() : MenuItemInterface
178
    {
179
        return $this->selectedItemIndex !== null
180
            ? $this->items[$this->selectedItemIndex]
181
            : $this;
182
    }
183
184
    public function getItems() : array
185
    {
186
        return $this->items;
187
    }
188
189
    /**
190
     * Can the item be selected
191
     * In this case, it indicates if at least 1 item inside the SplitItem can be selected
192
     */
193
    public function canSelect() : bool
194
    {
195
        return $this->canBeSelected;
196
    }
197
198
    /**
199
     * Execute the items callable if required
200
     */
201
    public function getSelectAction() : ?callable
202
    {
203
        return null;
204
    }
205
206
    /**
207
     * Whether or not the menu item is showing the menustyle extra value
208
     */
209
    public function showsItemExtra() : bool
210
    {
211
        return false;
212
    }
213
214
    /**
215
     * Enable showing item extra
216
     */
217
    public function showItemExtra() : void
218
    {
219
        //noop
220
    }
221
222
    /**
223
     * Disable showing item extra
224
     */
225
    public function hideItemExtra() : void
226
    {
227
        //noop
228
    }
229
230
    /**
231
     * Nothing to return with SplitItem
232
     */
233
    public function getText() : string
234
    {
235
        throw new \BadMethodCallException(sprintf('Not supported on: %s', SplitItem::class));
236
    }
237
}
238