Passed
Pull Request — master (#4)
by
unknown
07:09
created

Spinner::setMessage()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 7
c 2
b 0
f 1
dl 0
loc 14
ccs 0
cts 0
cp 0
rs 10
cc 3
nc 4
nop 1
crap 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AlecRabbit\Snake;
6
7
use AlecRabbit\Snake\Contracts\Color;
8
use AlecRabbit\Snake\Contracts\SpinnerInterface;
9
use AlecRabbit\Snake\Core\Driver;
10
11
class Spinner implements SpinnerInterface
12
{
13
    private const CHARS = ['⠏', '⠛', '⠹', '⢸', '⣰', '⣤', '⣆', '⡇'];
14
    private const COLORS = [
15
        196,
16
        196,
17
        202,
18
        202,
19
        208,
20
        208,
21
        214,
22
        214,
23
        220,
24
        220,
25
        226,
26
        226,
27
        190,
28
        190,
29
        154,
30
        154,
31
        118,
32
        118,
33
        82,
34
        82,
35
        46,
36
        46,
37
        47,
38
        47,
39
        48,
40
        48,
41
        49,
42
        49,
43
        50,
44
        50,
45
        51,
46
        51,
47
        45,
48
        45,
49
        39,
50
        39,
51
        33,
52
        33,
53
        27,
54
        27,
55
        56,
56
        56,
57
        57,
58
        57,
59
        93,
60
        93,
61
        129,
62
        129,
63
        165,
64
        165,
65
        201,
66
        201,
67
        200,
68
        200,
69
        199,
70
        199,
71
        198,
72
        198,
73
        197,
74
        197,
75
    ];
76
77
    /** @var Driver */
78
    private $driver;
79
    /** @var int */
80
    private $currentCharIdx = 0;
81
    /** @var int */
82
    private $currentColorIdx = 0;
83
    /** @var int */
84
    private $framesCount;
85
    /** @var int */
86
    private $colorCount;
87
    /** @var string|null */
88 3
    protected $message;
89
    /** @var int */
90 3
    protected $terminalCols;
91 1
    /** @var float */
92 1
    protected $lastFrameTimestamp = 0;
93 1
94
    public function __construct(int $colorLevel = Color::COLOR_256)
95 1
    {
96
        $this->driver = new Driver($colorLevel);
97 1
        $this->framesCount = count(self::CHARS);
98 1
        $this->colorCount = count(self::COLORS);
99 1
        $this->terminalCols = (int) (exec('tput cols') ?? 80);
100 1
    }
101 1
102
    public function spin(): void
103 1
    {
104
        $message = is_string($this->message) && !empty($this->message)
105 1
            ? ' '.$this->message
0 ignored issues
show
Coding Style introduced by
Expected at least 1 space before "."; 0 found
Loading history...
Coding Style introduced by
Expected at least 1 space after "."; 0 found
Loading history...
106 1
            : '';
107
108 1
        $output =
109
            $this->driver->eraseSequence()
110 1
            . $this->driver->frameSequence(
111 1
                self::COLORS[$this->currentColorIdx],
112
                self::CHARS[$this->currentCharIdx]
113 1
            )
114
            . $message;
115
116 1
        $spaces = $this->terminalCols - mb_strlen($output);
117
        $output = $output . str_repeat(' ', max(0, $spaces));
118
119
        $this->driver->write(
120
            $output,
121
            "\r"
122 1
        );
123
        $this->update();
124 1
    }
125
126
    private function update(): void
127 1
    {
128
        $now = microtime(true);
129 1
        if ($now >= $this->lastFrameTimestamp + $this->interval()) {
130 1
            $this->lastFrameTimestamp = $now;
0 ignored issues
show
Documentation Bug introduced by
It seems like $now can also be of type string. However, the property $lastFrameTimestamp is declared as type double. 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...
131
132 1
            if (++$this->currentCharIdx === $this->framesCount) {
133
                $this->currentCharIdx = 0;
134 1
            }
135 1
            if (++$this->currentColorIdx === $this->colorCount) {
136 1
                $this->currentColorIdx = 0;
137
            }
138 1
        }
139
    }
140 1
141 1
    /** @inheritDoc */
142
    public function interval(): float
143 1
    {
144
        return 0.1;
145
    }
146
147
    public function begin(): void
148
    {
149
        $this->driver->hideCursor();
150
    }
151
152
    public function end(): void
153
    {
154
        $this->erase();
155
        $this->driver->showCursor();
156
    }
157
158
    public function erase(): void
159
    {
160
        $this->driver->write(
161
            $this->driver->eraseSequence()
162
        );
163
    }
164
165
    public function useStdOut(): void
166
    {
167
        $this->driver->useStdOut();
168
    }
169
170
    /** @inheritDoc */
171
    public function setMessage(?string $message): void
172
    {
173
        if ($message === null) {
174
            $this->message = null;
175
        }
176
177
        $message = mb_substr($message, 0, $this->terminalCols-10);
0 ignored issues
show
Bug introduced by
It seems like $message can also be of type null; however, parameter $string of mb_substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

177
        $message = mb_substr(/** @scrutinizer ignore-type */ $message, 0, $this->terminalCols-10);
Loading history...
Coding Style introduced by
Expected at least 1 space before "-"; 0 found
Loading history...
Coding Style introduced by
Expected at least 1 space after "-"; 0 found
Loading history...
178
        $breakPosition = mb_strpos($message, "\n");
179
180
        if ($breakPosition !== false) {
181
            $message = mb_substr($message, 0, $breakPosition);
182
        }
183
184
        $this->message = trim($message);
185
    }
186
}
187