Completed
Pull Request — master (#125)
by
unknown
04:54
created

SplitItem::getRows()   D

Complexity

Conditions 13
Paths 260

Size

Total Lines 67
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 67
rs 4.3252
c 0
b 0
f 0
cc 13
eloc 49
nc 260
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 CliMenuBuilder
23
     */
24
    private $parentBuilder;
25
26
    /**
27
     * @var int
28
     */
29
    private $selectedItemIndex;
30
31
    /**
32
     * @var bool
33
     */
34
    private $canBeSelected = true;
35
36
    /**
37
     * @var int
38
     */
39
    private $margin = 2;
40
41
    /**
42
     * @var array
43
     */
44
    private $blacklistedItems = [
45
        "AsciiArtItem",
46
        "LineBreakItem",
47
        "SplitItem",
48
    ];
49
50
51
    public function __construct(CliMenuBuilder $builder, array $items = [])
52
    {
53
        $this->parentBuilder = $builder;
54
        $this->items         = $items;
55
56
        $this->setDefaultSelectedItem();
57
    }
58
59
    /**
60
     * Select default item
61
     */
62
    private function setDefaultSelectedItem()
63
    {
64
        foreach ($this->items as $index => $item) {
65
            if ($item->canSelect()) {
66
                $this->canBeSelected = true;
67
                $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. 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...
68
                return;
69
            }
70
        }
71
72
        $this->canBeSelected = false;
73
        $this->selectedItemIndex = null;
74
    }
75
76
    public function addMenuItem(MenuItemInterface $item) : self
77
    {
78
        foreach ($this->blacklistedItems as $bl) {
79
            if ($item instanceof $bl) {
80
                throw new \InvalidArgumentException("Cannot add a $bl to a SplitItem");
81
            }
82
        }
83
84
        $this->items[] = $item;
85
86
        return $this;
87
    }
88
89
    public function addItem(
90
        string $text,
91
        callable $itemCallable,
92
        bool $showItemExtra = false,
93
        bool $disabled = false
94
    ) : self {
95
        $this->items[] = new SelectableItem($text, $itemCallable, $showItemExtra, $disabled);
96
97
        return $this;
98
    }
99
100
    public function addStaticItem(string $text) : self
101
    {
102
        $this->items[] = new StaticItem($text);
103
104
        return $this;
105
    }
106
107
    public function addSubMenu(string $id, CliMenuBuilder $subMenuBuilder = null) : CliMenuBuilder
108
    {
109
        if (null === $subMenuBuilder) {
110
            $subMenuBuilder = new CliMenuBuilder($this->parentBuilder, $this);
111
        }
112
113
        $this->items[] = $id;
114
        $this->subMenuBuilders[$id] = $subMenuBuilder;
0 ignored issues
show
Bug introduced by
The property subMenuBuilders does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
115
116
        return $subMenuBuilder;
117
    }
118
119
    public function end() : CliMenuBuilder
120
    {
121
        $this->items = array_map(function ($item) {
122
            if (!is_string($item) || empty($this->subMenuBuilders[$item])) {
123
                return $item;
124
            }
125
126
            $subMenuBuilder = $this->subMenuBuilders[$item];
127
            $subMenu = $subMenuBuilder->build();
128
            $this->parentBuilder->injectSubMenu($item, $subMenu);
129
130
            return new MenuMenuItem($item, $subMenu, $subMenuBuilder->isMenuDisabled());
131
        }, $this->items);
132
133
        $this->setDefaultSelectedItem();
134
135
        return $this->parentBuilder;
136
    }
137
138
    /**
139
     * The output text for the item
140
     */
141
    public function getRows(MenuStyle $style, bool $selected = false) : array
142
    {
143
        $numberOfItems = count($this->items);
144
145
        if (!$selected) {
146
            $this->setDefaultSelectedItem();
147
        }
148
149
        $length = $style->getDisplaysExtra()
150
            ? floor(($style->getContentWidth() - mb_strlen($style->getItemExtra()) + 2) / $numberOfItems) - $this->margin
151
            : floor($style->getContentWidth() / $numberOfItems) - $this->margin;
152
        $missingLength = $style->getContentWidth() % $numberOfItems;
153
154
        $lines = 0;
155
        $cells = [];
156
        foreach ($this->items as $index => $item) {
157
            $isSelected = $selected && $index === $this->selectedItemIndex;
158
            $marker = sprintf("%s ", $style->getMarker($isSelected));
159
            $content = StringUtil::wordwrap(
160
                sprintf('%s%s', $marker, $item->getText()),
161
                $length
162
            );
163
            $cell = array_map(function ($row) use ($index, $length, $style, $isSelected) {
164
                $invertedColoursSetCode = $isSelected
165
                    ? $style->getInvertedColoursSetCode()
166
                    : '';
167
                $invertedColoursUnsetCode = $isSelected
168
                    ? $style->getInvertedColoursUnsetCode()
169
                    : '';
170
171
                return sprintf(
172
                    "%s%s%s%s%s",
173
                    $invertedColoursSetCode,
174
                    $row,
175
                    str_repeat(' ', $length - mb_strlen($row)),
176
                    $invertedColoursUnsetCode,
177
                    str_repeat(' ', $this->margin)
178
                );
179
            }, explode("\n", $content));
180
            $lineCount = count($cell);
181
            if ($lineCount > $lines) {
182
                $lines = $lineCount;
183
            }
184
            $cells[] = $cell;
185
        }
186
187
        $rows = [];
188
        for ($i = 0; $i < $lines; $i++) {
189
            $row = "";
190
            if ($i > 0) {
191
                $row .= str_repeat(' ', 2);
192
            }
193
            foreach ($cells as $cell) {
194
                if (isset($cell[$i])) {
195
                    $row .= $cell[$i];
196
                } else {
197
                    $row .= str_repeat(' ', $length);
198
                }
199
            }
200
            if ($missingLength) {
201
                $row .= str_repeat(' ', $missingLength);
202
            }
203
            $rows[] = $row;
204
        }
205
206
        return $rows;
207
    }
208
209
    public function setSelectedItemIndex(int $index) : void
210
    {
211
        $this->selectedItemIndex = $index;
212
    }
213
214
    public function getSelectedItemIndex() : int
215
    {
216
        if ($this->selectedItemIndex === null) {
217
            return 0;
218
        }
219
        return $this->selectedItemIndex;
220
    }
221
222
    public function getSelectedItem() : MenuItem
223
    {
224
        return $this->items[$this->selectedItemIndex];
225
    }
226
227
    public function getItems() : array
228
    {
229
        return $this->items;
230
    }
231
232
    /**
233
     * Can the item be selected
234
     * Not really in this case but that's the trick
235
     */
236
    public function canSelect() : bool
237
    {
238
        return $this->canBeSelected;
239
    }
240
241
    /**
242
     * Execute the items callable if required
243
     */
244
    public function getSelectAction() : ?callable
245
    {
246
        return null;
247
    }
248
249
    /**
250
     * Whether or not the menu item is showing the menustyle extra value
251
     */
252
    public function showsItemExtra() : bool
253
    {
254
        return false;
255
    }
256
257
    /**
258
     * Enable showing item extra
259
     */
260
    public function showItemExtra() : void
261
    {
262
        //noop
263
    }
264
265
    /**
266
     * Disable showing item extra
267
     */
268
    public function hideItemExtra() : void
269
    {
270
        //noop
271
    }
272
273
    /**
274
     * Return the raw string of text
275
     */
276
    public function getText() : string
277
    {
278
        $text = [];
279
        foreach ($this->items as $item) {
280
            $text[] = $item->getText();
281
        }
282
        return explode(' - ', $text);
283
    }
284
}
285