Passed
Pull Request — master (#203)
by
unknown
01:56
created

SplitItem::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
1
<?php
2
3
namespace PhpSchool\CliMenu\MenuItem;
4
5
use Assert\Assertion;
6
use PhpSchool\CliMenu\MenuStyle;
7
use PhpSchool\CliMenu\Style\SplitStyle;
8
use PhpSchool\CliMenu\Util\StringUtil;
9
10
/**
11
 * @author Michael Woodward <[email protected]>
12
 */
13
class SplitItem implements MenuItemInterface
14
{
15
    /**
16
     * @var array
17
     */
18
    private $items = [];
19
20
    /**
21
     * @var int|null
22
     */
23
    private $selectedItemIndex;
24
25
    /**
26
     * @var bool
27
     */
28
    private $canBeSelected = true;
29
30
    /**
31
     * @var int
32
     */
33
    private $gutter = 2;
34
35
    /**
36
     * @var SplitStyle
37
     */
38
    private $style;
39
40
    /**
41
     * @var \Callable
0 ignored issues
show
Bug introduced by
The type Callable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
42
     */
43
    private $styleCallback = null;
44
45
    /**
46
     * @var array
47
     */
48
    private static $blacklistedItems = [
49
        \PhpSchool\CliMenu\MenuItem\AsciiArtItem::class,
50
        \PhpSchool\CliMenu\MenuItem\LineBreakItem::class,
51
        \PhpSchool\CliMenu\MenuItem\SplitItem::class,
52
    ];
53
54
    public function __construct(array $items = [])
55
    {
56
        $this->addItems($items);
57
        $this->setDefaultSelectedItem();
58
59
        $this->style = new SplitStyle();
60
    }
61
62
    public function setStyleCallback(Callable $callable)
63
    {
64
        $this->styleCallback = $callable;
0 ignored issues
show
Documentation Bug introduced by
It seems like $callable of type callable is incompatible with the declared type Callable of property $styleCallback.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
65
    }
66
67
    public function setGutter(int $gutter) : void
68
    {
69
        Assertion::greaterOrEqualThan($gutter, 0);
70
        $this->gutter = $gutter;
71
    }
72
73
    public function getGutter() : int
74
    {
75
        return $this->gutter;
76
    }
77
78
    public function addItem(MenuItemInterface $item) : self
79
    {
80
        foreach (self::$blacklistedItems as $bl) {
81
            if ($item instanceof $bl) {
82
                throw new \InvalidArgumentException("Cannot add a $bl to a SplitItem");
83
            }
84
        }
85
        $this->items[] = $item;
86
        $this->setDefaultSelectedItem();
87
        return $this;
88
    }
89
90
    public function addItems(array $items) : self
91
    {
92
        foreach ($items as $item) {
93
            $this->addItem($item);
94
        }
95
            
96
        return $this;
97
    }
98
99
    public function setItems(array $items) : self
100
    {
101
        $this->items = [];
102
        $this->addItems($items);
103
        return $this;
104
    }
105
106
    /**
107
     * Select default item
108
     */
109
    private function setDefaultSelectedItem() : void
110
    {
111
        foreach ($this->items as $index => $item) {
112
            if ($item->canSelect()) {
113
                $this->canBeSelected = true;
114
                $this->selectedItemIndex = $index;
115
                return;
116
            }
117
        }
118
119
        $this->canBeSelected = false;
120
        $this->selectedItemIndex = null;
121
    }
122
123
    /**
124
     * The output text for the item
125
     */
126
    public function getRows(MenuStyle $style, bool $selected = false) : array
127
    {
128
        // Set local style with parent style
129
        if ($this->styleCallback) {
130
            ($this->styleCallback)($this->style);
131
132
            $this->styleCallback = null;
133
        }
134
135
        $numberOfItems = count($this->items);
136
137
        if ($numberOfItems === 0) {
138
            throw new \RuntimeException(sprintf('There should be at least one item added to: %s', __CLASS__));
139
        }
140
        
141
        if (!$selected) {
142
            $this->setDefaultSelectedItem();
143
        }
144
145
        $length = $this->style->getDisplaysExtra()
146
            ? floor($style->getContentWidth() / $numberOfItems) - (mb_strlen($this->style->getItemExtra()) + 2)
147
            : floor($style->getContentWidth() / $numberOfItems);
148
        
149
        $length -= $this->gutter;
150
        $length = (int) $length;
151
        
152
        $missingLength = $style->getContentWidth() % $numberOfItems;
153
        
154
        return $this->buildRows(
155
            array_map(function ($index, $item) use ($selected, $length, $style) {
156
                /** @var ItemStyleInterface|MenuItemInterface $item */
157
                $isSelected = $selected && $index === $this->selectedItemIndex;
158
159
                $itemStyle = $item->getStyle();
0 ignored issues
show
Bug introduced by
The method getStyle() does not exist on PhpSchool\CliMenu\MenuItem\MenuItemInterface. It seems like you code against a sub-type of said class. However, the method does not exist in PhpSchool\CliMenu\MenuItem\AsciiArtItem or PhpSchool\CliMenu\MenuItem\LineBreakItem or PhpSchool\CliMenu\MenuItem\SplitItem. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

159
                /** @scrutinizer ignore-call */ 
160
                $itemStyle = $item->getStyle();
Loading history...
160
161
                $getMarkerType = $item instanceof ToggableItemInterface
162
                    ? $item->getChecked()
163
                    : $isSelected;
164
165
                $marker = $item->canSelect()
0 ignored issues
show
Bug introduced by
The method canSelect() does not exist on PhpSchool\CliMenu\MenuItem\ItemStyleInterface. It seems like you code against a sub-type of said class. However, the method does not exist in PhpSchool\CliMenu\MenuItem\CheckableInterface or PhpSchool\CliMenu\MenuItem\SelectableInterface or PhpSchool\CliMenu\MenuItem\RadioInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

165
                $marker = $item->/** @scrutinizer ignore-call */ canSelect()
Loading history...
166
                    ? sprintf('%s', $itemStyle->getMarker($getMarkerType))
167
                    : '';
168
169
                $itemExtra = '';
170
                if ($itemStyle->getDisplaysExtra()) {
171
                    $itemExtraString = $itemStyle->getItemExtra();
172
                    $itemExtra = $item->showsItemExtra()
0 ignored issues
show
Bug introduced by
The method showsItemExtra() does not exist on PhpSchool\CliMenu\MenuItem\ItemStyleInterface. It seems like you code against a sub-type of said class. However, the method does not exist in PhpSchool\CliMenu\MenuItem\CheckableInterface or PhpSchool\CliMenu\MenuItem\SelectableInterface or PhpSchool\CliMenu\MenuItem\RadioInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

172
                    $itemExtra = $item->/** @scrutinizer ignore-call */ showsItemExtra()
Loading history...
173
                        ? sprintf('  %s', $itemExtraString)
174
                        : sprintf('  %s', str_repeat(' ', mb_strlen($itemExtraString)));
175
                }
176
177
                return $this->buildCell(
178
                    explode(
179
                        "\n",
180
                        StringUtil::wordwrap(
181
                            sprintf('%s%s', $marker, $item->getText()),
0 ignored issues
show
Bug introduced by
The method getText() does not exist on PhpSchool\CliMenu\MenuItem\ItemStyleInterface. It seems like you code against a sub-type of said class. However, the method does not exist in PhpSchool\CliMenu\MenuItem\CheckableInterface or PhpSchool\CliMenu\MenuItem\SelectableInterface or PhpSchool\CliMenu\MenuItem\RadioInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

181
                            sprintf('%s%s', $marker, $item->/** @scrutinizer ignore-call */ getText()),
Loading history...
182
                            $length,
183
                            sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
184
                        )
185
                    ),
186
                    $length,
187
                    $style,
188
                    $isSelected,
189
                    $itemExtra
190
                );
191
            }, array_keys($this->items), $this->items),
192
            $style,
193
            $missingLength,
194
            $length
195
        );
196
    }
197
198
    private function buildRows(array $cells, MenuStyle $style, int $missingLength, int $length) : array
0 ignored issues
show
Unused Code introduced by
The parameter $style is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

198
    private function buildRows(array $cells, /** @scrutinizer ignore-unused */ MenuStyle $style, int $missingLength, int $length) : array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
199
    {
200
        $extraPadLength = $this->style->getDisplaysExtra() ? 2 + mb_strlen($this->style->getItemExtra()) : 0;
201
        
202
        return array_map(
203
            function ($i) use ($cells, $length, $missingLength, $extraPadLength) {
204
                return $this->buildRow($cells, $i, $length, $missingLength, $extraPadLength);
205
            },
206
            range(0, max(array_map('count', $cells)) - 1)
207
        );
208
    }
209
210
    private function buildRow(array $cells, int $index, int $length, int $missingLength, int $extraPadLength) : string
211
    {
212
        return sprintf(
213
            '%s%s',
214
            implode(
215
                '',
216
                array_map(
217
                    function ($cell) use ($index, $length, $extraPadLength) {
218
                        return $cell[$index] ?? str_repeat(' ', $length + $this->gutter + $extraPadLength);
219
                    },
220
                    $cells
221
                )
222
            ),
223
            str_repeat(' ', $missingLength)
224
        );
225
    }
226
227
    private function buildCell(
228
        array $content,
229
        int $length,
230
        MenuStyle $style,
231
        bool $isSelected,
232
        string $itemExtra
233
    ) : array {
234
        return array_map(function ($row, $index) use ($length, $style, $isSelected, $itemExtra) {
235
            $invertedColoursSetCode = $isSelected
236
                ? $style->getInvertedColoursSetCode()
237
                : '';
238
            $invertedColoursUnsetCode = $isSelected
239
                ? $style->getInvertedColoursUnsetCode()
240
                : '';
241
242
            return sprintf(
243
                '%s%s%s%s%s%s',
244
                $invertedColoursSetCode,
245
                $row,
246
                str_repeat(' ', $length - mb_strlen($row)),
247
                $index === 0 ? $itemExtra : str_repeat(' ', mb_strlen($itemExtra)),
248
                $invertedColoursUnsetCode,
249
                str_repeat(' ', $this->gutter)
250
            );
251
        }, $content, array_keys($content));
252
    }
253
254
    /**
255
     * Is there an item with this index and can it be
256
     * selected?
257
     */
258
    public function canSelectIndex(int $index) : bool
259
    {
260
        return isset($this->items[$index]) && $this->items[$index]->canSelect();
261
    }
262
263
    /**
264
     * Set the item index which should be selected. If the item does
265
     * not exist then throw an exception.
266
     */
267
    public function setSelectedItemIndex(int $index) : void
268
    {
269
        if (!isset($this->items[$index])) {
270
            throw new \InvalidArgumentException(sprintf('Index: "%s" does not exist', $index));
271
        }
272
        
273
        $this->selectedItemIndex = $index;
274
    }
275
276
    /**
277
     * Get the currently select item index.
278
     * May be null in case of no selectable item.
279
     */
280
    public function getSelectedItemIndex() : ?int
281
    {
282
        return $this->selectedItemIndex;
283
    }
284
285
    /**
286
     * Get the currently selected item - if no items are selectable
287
     * then throw an exception.
288
     */
289
    public function getSelectedItem() : MenuItemInterface
290
    {
291
        if (null === $this->selectedItemIndex) {
292
            throw new \RuntimeException('No item is selected');
293
        }
294
        
295
        return $this->items[$this->selectedItemIndex];
296
    }
297
298
    public function getItems() : array
299
    {
300
        return $this->items;
301
    }
302
303
    /**
304
     * Can the item be selected
305
     * In this case, it indicates if at least 1 item inside the SplitItem can be selected
306
     */
307
    public function canSelect() : bool
308
    {
309
        return $this->canBeSelected;
310
    }
311
312
    /**
313
     * Execute the items callable if required
314
     */
315
    public function getSelectAction() : ?callable
316
    {
317
        return null;
318
    }
319
320
    /**
321
     * Whether or not the menu item is showing the menustyle extra value
322
     */
323
    public function showsItemExtra() : bool
324
    {
325
        return false;
326
    }
327
328
    /**
329
     * Enable showing item extra
330
     */
331
    public function showItemExtra() : void
332
    {
333
        //noop
334
    }
335
336
    /**
337
     * Disable showing item extra
338
     */
339
    public function hideItemExtra() : void
340
    {
341
        //noop
342
    }
343
344
    /**
345
     * Nothing to return with SplitItem
346
     */
347
    public function getText() : string
348
    {
349
        throw new \BadMethodCallException(sprintf('Not supported on: %s', __CLASS__));
350
    }
351
}
352