Completed
Pull Request — master (#104)
by
unknown
05:54 queued 29s
created

MenuStyle   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 413
Duplicated Lines 7.75 %

Coupling/Cohesion

Components 3
Dependencies 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 46
c 1
b 0
f 0
lcom 3
cbo 1
dl 32
loc 413
rs 8.3999

33 Methods

Rating   Name   Duplication   Size   Complexity  
A getAvailableColours() 0 4 1
A getWidth() 0 4 1
A setWidth() 0 13 2
A getPadding() 0 4 1
A setPadding() 0 8 1
A getMargin() 0 4 1
A setMargin() 0 8 1
A getContentWidth() 0 4 1
A getRightHandPadding() 0 4 1
A getSelectedMarker() 0 4 1
A setSelectedMarker() 0 6 1
A getUnselectedMarker() 0 4 1
A setUnselectedMarker() 0 6 1
A getMarker() 0 4 2
A setItemExtra() 0 6 1
A getItemExtra() 0 4 1
A getDisplaysExtra() 0 4 1
A setDisplaysExtra() 0 6 1
A getTitleSeparator() 0 4 1
A setTitleSeparator() 0 6 1
A getDefaultStyleValues() 0 4 1
A __construct() 0 15 2
A getDisabledItemText() 0 9 1
A generateColoursSetCode() 0 16 3
A getColoursSetCode() 0 4 1
A getInvertedColoursSetCode() 0 4 1
A getInvertedColoursUnsetCode() 0 4 1
A getColoursResetCode() 0 4 1
A calculateContentWidth() 0 4 1
A getFg() 0 4 1
B setFg() 16 16 5
A getBg() 0 4 1
B setBg() 16 16 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MenuStyle often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MenuStyle, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpSchool\CliMenu;
4
5
use PhpSchool\CliMenu\Exception\InvalidInstantiationException;
6
use PhpSchool\CliMenu\Terminal\TerminalFactory;
7
use PhpSchool\Terminal\Terminal;
8
9
//TODO: B/W fallback
10
11
/**
12
 * @author Michael Woodward <[email protected]>
13
 */
14
class MenuStyle
15
{
16
    /**
17
     * @var Terminal
18
     */
19
    protected $terminal;
20
21
    /**
22
     * @var string
23
     */
24
    protected $fg;
25
26
    /**
27
     * @var string
28
     */
29
    protected $bg;
30
31
    /**
32
     * @var int
33
     */
34
    protected $width;
35
36
    /**
37
     * @var int
38
     */
39
    protected $padding;
40
41
    /**
42
     * @var int
43
     */
44
    protected $margin;
45
46
    /**
47
     * @var int
48
     */
49
    protected $contentWidth;
50
51
    /**
52
     * @var string
53
     */
54
    private $selectedMarker;
55
56
    /**
57
     * @var string
58
     */
59
    private $unselectedMarker;
60
61
    /**
62
     * @var string
63
     */
64
    private $itemExtra;
65
66
    /**
67
     * @var bool
68
     */
69
    private $displaysExtra;
70
71
    /**
72
     * @var string
73
     */
74
    private $titleSeparator;
75
76
    /**
77
     * @var string
78
     */
79
    private $coloursSetCode;
80
81
    /**
82
     * @var string
83
     */
84
    private $invertedColoursSetCode = "\033[7m";
85
86
    /**
87
     * @var string
88
     */
89
    private $invertedColoursUnsetCode = "\033[27m";
90
91
    /**
92
     * @var string
93
     */
94
    private $coloursResetCode = "\033[0m";
95
96
    /**
97
     * Default Values
98
     *
99
     * @var array
100
     */
101
    private static $defaultStyleValues = [
102
        'fg' => 'white',
103
        'bg' => 'blue',
104
        'width' => 100,
105
        'padding' => 2,
106
        'margin' => 2,
107
        'selectedMarker' => '●',
108
        'unselectedMarker' => '○',
109
        'itemExtra' => '✔',
110
        'displaysExtra' => false,
111
        'titleSeparator' => '=',
112
    ];
113
114
    public static function getDefaultStyleValues() : array
115
    {
116
        return static::$defaultStyleValues;
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
117
    }
118
119
    /**
120
     * @var array
121
     */
122
    private static $availableForegroundColors = array(
123
        'black'   => 30,
124
        'red'     => 31,
125
        'green'   => 32,
126
        'yellow'  => 33,
127
        'blue'    => 34,
128
        'magenta' => 35,
129
        'cyan'    => 36,
130
        'white'   => 37,
131
        'default' => 39,
132
    );
133
134
    /**
135
     * @var array
136
     */
137
    private static $availableBackgroundColors = array(
138
        'black'   => 40,
139
        'red'     => 41,
140
        'green'   => 42,
141
        'yellow'  => 43,
142
        'blue'    => 44,
143
        'magenta' => 45,
144
        'cyan'    => 46,
145
        'white'   => 47,
146
        'default' => 49,
147
    );
148
149
    /**
150
     * @var array
151
     */
152
    private static $availableOptions = array(
153
        'bold'       => array('set' => 1, 'unset' => 22),
154
        'dim'        => array('set' => 2, 'unset' => 22),
155
        'underscore' => array('set' => 4, 'unset' => 24),
156
        'blink'      => array('set' => 5, 'unset' => 25),
157
        'reverse'    => array('set' => 7, 'unset' => 27),
158
        'conceal'    => array('set' => 8, 'unset' => 28)
159
    );
160
161
    /**
162
     * Initialise style
163
     */
164
    public function __construct(Terminal $terminal = null)
165
    {
166
        $this->terminal = $terminal ?: TerminalFactory::fromSystem();
167
168
        $this->setFg(static::$defaultStyleValues['fg']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
169
        $this->setBg(static::$defaultStyleValues['bg']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
170
        $this->setWidth(static::$defaultStyleValues['width']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
171
        $this->setPadding(static::$defaultStyleValues['padding']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
172
        $this->setMargin(static::$defaultStyleValues['margin']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
173
        $this->setSelectedMarker(static::$defaultStyleValues['selectedMarker']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
174
        $this->setUnselectedMarker(static::$defaultStyleValues['unselectedMarker']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
175
        $this->setItemExtra(static::$defaultStyleValues['itemExtra']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
176
        $this->setDisplaysExtra(static::$defaultStyleValues['displaysExtra']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
177
        $this->setTitleSeparator(static::$defaultStyleValues['titleSeparator']);
0 ignored issues
show
Bug introduced by
Since $defaultStyleValues is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $defaultStyleValues to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
178
    }
179
180
    public static function getAvailableColours() : array
181
    {
182
        return array_keys(self::$availableBackgroundColors);
183
    }
184
185
    public function getDisabledItemText(string $text) : string
186
    {
187
        return sprintf(
188
            "\033[%sm%s\033[%sm",
189
            self::$availableOptions['dim']['set'],
190
            $text,
191
            self::$availableOptions['dim']['unset']
192
        );
193
    }
194
195
    /**
196
     * Generates the ansi escape sequence to set the colours
197
     */
198
    private function generateColoursSetCode() : void
199
    {
200
        if (is_string($this->fg)) {
201
            $fgCode = self::$availableForegroundColors[$this->fg];
202
        } else {
203
            $fgCode = sprintf("38;5;%s", $this->fg);
204
        }
205
206
        if (is_string($this->bg)) {
207
            $bgCode = self::$availableBackgroundColors[$this->bg];
208
        } else {
209
            $bgCode = sprintf("48;5;%s", $this->bg);
210
        }
211
212
        $this->coloursSetCode = sprintf("\033[%s;%sm", $fgCode, $bgCode);
213
    }
214
215
    /**
216
     * Get the colour code for Bg and Fg
217
     */
218
    public function getColoursSetCode() : string
219
    {
220
        return $this->coloursSetCode;
221
    }
222
223
    /**
224
     * Get the inverted escape sequence (used for selected elements)
225
     */
226
    public function getInvertedColoursSetCode() : string
227
    {
228
        return $this->invertedColoursSetCode;
229
    }
230
231
    /**
232
     * Get the inverted escape sequence (used for selected elements)
233
     */
234
    public function getInvertedColoursUnsetCode() : string
235
    {
236
        return $this->invertedColoursUnsetCode;
237
    }
238
239
    /**
240
     * Get the escape sequence used to reset colours to default
241
     */
242
    public function getColoursResetCode() : string
243
    {
244
        return $this->coloursResetCode;
245
    }
246
247
    /**
248
     * Calculate the contents width
249
     */
250
    protected function calculateContentWidth() : void
251
    {
252
        $this->contentWidth = $this->width - ($this->padding*2) - ($this->margin*2);
253
    }
254
255
    public function getFg() : string
256
    {
257
        return $this->fg;
258
    }
259
260 View Code Duplication
    public function setFg($fg) : self
0 ignored issues
show
Duplication introduced by
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...
261
    {
262
        if (is_int($fg)) {
263
            if ($this->terminal->getColourSupport() < 256) {
264
                // Need to map to 8 colors
265
                return $this;
266
            } elseif ($fg < 0 || $fg > 255) {
267
                throw new Exception("Invalid colour code");
268
            }
269
        }
270
271
        $this->fg = $fg;
272
        $this->generateColoursSetCode();
273
274
        return $this;
275
    }
276
277
    public function getBg() : string
278
    {
279
        return $this->bg;
280
    }
281
282 View Code Duplication
    public function setBg($bg) : self
0 ignored issues
show
Duplication introduced by
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...
283
    {
284
        if (is_int($bg)) {
285
            if ($this->terminal->getColourSupport() < 256) {
286
                // Need to map to 8 colors
287
                return $this;
288
            }
289
            if ($bg < 0 || $bg > 255) {
290
                throw new Exception("Invalid colour code");
291
            }
292
        }
293
        $this->bg = $bg;
294
        $this->generateColoursSetCode();
295
296
        return $this;
297
    }
298
299
    public function getWidth() : int
300
    {
301
        return $this->width;
302
    }
303
304
    public function setWidth(int $width) : self
305
    {
306
        $availableWidth = $this->terminal->getWidth() - ($this->margin * 2) - ($this->padding * 2);
307
308
        if ($width >= $availableWidth) {
309
            $width = $availableWidth;
310
        }
311
312
        $this->width = $width;
0 ignored issues
show
Documentation Bug introduced by
It seems like $width can also be of type double. However, the property $width 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...
313
        $this->calculateContentWidth();
314
315
        return $this;
316
    }
317
318
    public function getPadding() : int
319
    {
320
        return $this->padding;
321
    }
322
323
    public function setPadding(int $padding) : self
324
    {
325
        $this->padding = $padding;
326
327
        $this->calculateContentWidth();
328
329
        return $this;
330
    }
331
332
    public function getMargin() : int
333
    {
334
        return $this->margin;
335
    }
336
337
    public function setMargin(int $margin) : self
338
    {
339
        $this->margin = $margin;
340
341
        $this->calculateContentWidth();
342
343
        return $this;
344
    }
345
346
    public function getContentWidth() : int
347
    {
348
        return $this->contentWidth;
349
    }
350
351
    /**
352
     * Get padding for right had side of content
353
     */
354
    public function getRightHandPadding(int $contentLength) : int
355
    {
356
        return $this->getContentWidth() - $contentLength + $this->getPadding();
357
    }
358
359
    public function getSelectedMarker() : string
360
    {
361
        return $this->selectedMarker;
362
    }
363
364
    public function setSelectedMarker(string $marker) : self
365
    {
366
        $this->selectedMarker = mb_substr($marker, 0, 1);
367
368
        return $this;
369
    }
370
371
    public function getUnselectedMarker() : string
372
    {
373
        return $this->unselectedMarker;
374
    }
375
376
    public function setUnselectedMarker(string $marker) : self
377
    {
378
        $this->unselectedMarker = mb_substr($marker, 0, 1);
379
380
        return $this;
381
    }
382
383
    /**
384
     * Get the correct marker for the item
385
     */
386
    public function getMarker(bool $selected) : string
387
    {
388
        return $selected ? $this->selectedMarker : $this->unselectedMarker;
389
    }
390
391
    public function setItemExtra(string $itemExtra) : self
392
    {
393
        $this->itemExtra = $itemExtra;
394
395
        return $this;
396
    }
397
398
    public function getItemExtra() : string
399
    {
400
        return $this->itemExtra;
401
    }
402
403
    public function getDisplaysExtra() : bool
404
    {
405
        return $this->displaysExtra;
406
    }
407
408
    public function setDisplaysExtra(bool $displaysExtra) : self
409
    {
410
        $this->displaysExtra = $displaysExtra;
411
412
        return $this;
413
    }
414
415
    public function getTitleSeparator() : string
416
    {
417
        return $this->titleSeparator;
418
    }
419
420
    public function setTitleSeparator(string $actionSeparator) : self
421
    {
422
        $this->titleSeparator = $actionSeparator;
423
424
        return $this;
425
    }
426
}
427