Completed
Pull Request — master (#104)
by
unknown
01:47
created

MenuStyle::calculateContentWidth()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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 $coloursResetCode = "\033[0m";
90
91
    /**
92
     * Default Values
93
     *
94
     * @var array
95
     */
96
    private static $defaultStyleValues = [
97
        'fg' => 'white',
98
        'bg' => 'blue',
99
        'width' => 100,
100
        'padding' => 2,
101
        'margin' => 2,
102
        'selectedMarker' => '●',
103
        'unselectedMarker' => '○',
104
        'itemExtra' => '✔',
105
        'displaysExtra' => false,
106
        'titleSeparator' => '=',
107
    ];
108
109
    public static function getDefaultStyleValues() : array
110
    {
111
        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...
112
    }
113
114
    /**
115
     * @var array
116
     */
117
    private static $availableForegroundColors = array(
118
        'black'   => 30,
119
        'red'     => 31,
120
        'green'   => 32,
121
        'yellow'  => 33,
122
        'blue'    => 34,
123
        'magenta' => 35,
124
        'cyan'    => 36,
125
        'white'   => 37,
126
        'default' => 39,
127
    );
128
129
    /**
130
     * @var array
131
     */
132
    private static $availableBackgroundColors = array(
133
        'black'   => 40,
134
        'red'     => 41,
135
        'green'   => 42,
136
        'yellow'  => 43,
137
        'blue'    => 44,
138
        'magenta' => 45,
139
        'cyan'    => 46,
140
        'white'   => 47,
141
        'default' => 49,
142
    );
143
144
    /**
145
     * @var array
146
     */
147
    private static $availableOptions = array(
148
        'bold'       => array('set' => 1, 'unset' => 22),
149
        'dim'        => array('set' => 2, 'unset' => 22),
150
        'underscore' => array('set' => 4, 'unset' => 24),
151
        'blink'      => array('set' => 5, 'unset' => 25),
152
        'reverse'    => array('set' => 7, 'unset' => 27),
153
        'conceal'    => array('set' => 8, 'unset' => 28)
154
    );
155
156
    /**
157
     * Initialise style
158
     */
159
    public function __construct(Terminal $terminal = null)
160
    {
161
        $this->terminal = $terminal ?: TerminalFactory::fromSystem();
162
163
        $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...
164
        $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...
165
        $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...
166
        $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...
167
        $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...
168
        $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...
169
        $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...
170
        $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...
171
        $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...
172
        $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...
173
    }
174
175
    public static function getAvailableColours() : array
176
    {
177
        return array_keys(self::$availableBackgroundColors);
178
    }
179
180
    public function getDisabledItemText(string $text) : string
181
    {
182
        return sprintf(
183
            "\033[%sm%s\033[%sm",
184
            self::$availableOptions['dim']['set'],
185
            $text,
186
            self::$availableOptions['dim']['unset']
187
        );
188
    }
189
190
    /**
191
     * Generates the ansi escape sequence to set the colours
192
     */
193
    private function generateColoursSetCode() : void
194
    {
195
        if (is_string($this->fg)) {
196
            $fgCode = self::$availableForegroundColors[$this->fg];
197
        } else {
198
            $fgCode = sprintf("38;5;%s", $this->fg);
199
        }
200
201
        if (is_string($this->bg)) {
202
            $bgCode = self::$availableBackgroundColors[$this->bg];
203
        } else {
204
            $bgCode = sprintf("48;5;%s", $this->bg);
205
        }
206
207
        $this->coloursSetCode = sprintf("\033[%s;%sm", $fgCode, $bgCode);
208
    }
209
210
    /**
211
     * Get the colour code for Bg and Fg
212
     */
213
    public function getColoursSetCode() : string
214
    {
215
        return $this->coloursSetCode;
216
    }
217
218
    /**
219
     * Get the inverted escape sequence (used for selected elements)
220
     */
221
    public function getInvertedColoursSetCode() : string
222
    {
223
        return $this->invertedColoursSetCode;
224
    }
225
226
    /**
227
     * Get the escape sequence used to reset colours to default
228
     */
229
    public function getColoursResetCode() : string
230
    {
231
        return $this->coloursResetCode;
232
    }
233
234
    /**
235
     * Calculate the contents width
236
     */
237
    protected function calculateContentWidth() : void
238
    {
239
        $this->contentWidth = $this->width - ($this->padding*2) - ($this->margin*2);
240
    }
241
242
    public function getFg() : string
243
    {
244
        return $this->fg;
245
    }
246
247 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...
248
    {
249
        if (is_int($fg)) {
250
            if ($this->terminal->getColourSupport() < 256) {
251
                // Need to map to 8 colors
252
                return $this;
253
            } elseif ($fg < 0 || $fg > 255) {
254
                throw new Exception("Invalid colour code");
255
            }
256
        }
257
258
        $this->fg = $fg;
259
        $this->generateColoursSetCode();
260
261
        return $this;
262
    }
263
264
    public function getBg() : string
265
    {
266
        return $this->bg;
267
    }
268
269 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...
270
    {
271
        if (is_int($bg)) {
272
            if ($this->terminal->getColourSupport() < 256) {
273
                // Need to map to 8 colors
274
                return $this;
275
            }
276
            if ($bg < 0 || $bg > 255) {
277
                throw new Exception("Invalid colour code");
278
            }
279
        }
280
        $this->bg = $bg;
281
        $this->generateColoursSetCode();
282
283
        return $this;
284
    }
285
286
    public function getWidth() : int
287
    {
288
        return $this->width;
289
    }
290
291
    public function setWidth(int $width) : self
292
    {
293
        $availableWidth = $this->terminal->getWidth() - ($this->margin * 2) - ($this->padding * 2);
294
295
        if ($width >= $availableWidth) {
296
            $width = $availableWidth;
297
        }
298
299
        $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...
300
        $this->calculateContentWidth();
301
302
        return $this;
303
    }
304
305
    public function getPadding() : int
306
    {
307
        return $this->padding;
308
    }
309
310
    public function setPadding(int $padding) : self
311
    {
312
        $this->padding = $padding;
313
314
        $this->calculateContentWidth();
315
316
        return $this;
317
    }
318
319
    public function getMargin() : int
320
    {
321
        return $this->margin;
322
    }
323
324
    public function setMargin(int $margin) : self
325
    {
326
        $this->margin = $margin;
327
328
        $this->calculateContentWidth();
329
330
        return $this;
331
    }
332
333
    public function getContentWidth() : int
334
    {
335
        return $this->contentWidth;
336
    }
337
338
    /**
339
     * Get padding for right had side of content
340
     */
341
    public function getRightHandPadding(int $contentLength) : int
342
    {
343
        return $this->getContentWidth() - $contentLength + $this->getPadding();
344
    }
345
346
    public function getSelectedMarker() : string
347
    {
348
        return $this->selectedMarker;
349
    }
350
351
    public function setSelectedMarker(string $marker) : self
352
    {
353
        $this->selectedMarker = mb_substr($marker, 0, 1);
354
355
        return $this;
356
    }
357
358
    public function getUnselectedMarker() : string
359
    {
360
        return $this->unselectedMarker;
361
    }
362
363
    public function setUnselectedMarker(string $marker) : self
364
    {
365
        $this->unselectedMarker = mb_substr($marker, 0, 1);
366
367
        return $this;
368
    }
369
370
    /**
371
     * Get the correct marker for the item
372
     */
373
    public function getMarker(bool $selected) : string
374
    {
375
        return $selected ? $this->selectedMarker : $this->unselectedMarker;
376
    }
377
378
    public function setItemExtra(string $itemExtra) : self
379
    {
380
        $this->itemExtra = $itemExtra;
381
382
        return $this;
383
    }
384
385
    public function getItemExtra() : string
386
    {
387
        return $this->itemExtra;
388
    }
389
390
    public function getDisplaysExtra() : bool
391
    {
392
        return $this->displaysExtra;
393
    }
394
395
    public function setDisplaysExtra(bool $displaysExtra) : self
396
    {
397
        $this->displaysExtra = $displaysExtra;
398
399
        return $this;
400
    }
401
402
    public function getTitleSeparator() : string
403
    {
404
        return $this->titleSeparator;
405
    }
406
407
    public function setTitleSeparator(string $actionSeparator) : self
408
    {
409
        $this->titleSeparator = $actionSeparator;
410
411
        return $this;
412
    }
413
}
414