Completed
Pull Request — master (#1)
by Harry
02:24
created

BufferedConsoleOutput::setVerbosity()   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 1
1
<?php
2
3
namespace Graze\BufferedConsole;
4
5
use Graze\BufferedConsole\Diff\ConsoleDiff;
6
use Graze\BufferedConsole\Terminal\Terminal;
7
use Graze\BufferedConsole\Terminal\TerminalInterface;
8
use Graze\BufferedConsole\Wrap\Wrapper;
9
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
10
use Symfony\Component\Console\Output\ConsoleOutputInterface;
11
use Symfony\Component\Console\Output\OutputInterface;
12
13
/**
14
 * This takes an array of lines to write to the console, does a different and only over-writes what has changed to the
15
 * console
16
 */
17
class BufferedConsoleOutput implements ConsoleOutputInterface
18
{
19
    /** @var string[] */
20
    private $buffer = [];
21
    /** @var ConsoleDiff */
22
    private $diff;
23
    /** @var TerminalInterface */
24
    private $terminal;
25
    /** @var ConsoleOutputInterface */
26
    private $output;
27
    /** @var Wrapper */
28
    private $wrapper;
29
    /** @var bool */
30
    private $trim = false;
31
32
    /**
33
     * Constructor.
34
     *
35
     * @param ConsoleOutputInterface $output
36
     * @param TerminalInterface      $terminal
37
     * @param Wrapper                $wrapper
38
     */
39
    public function __construct(
40
        ConsoleOutputInterface $output,
41
        TerminalInterface $terminal = null,
42
        Wrapper $wrapper = null
43
    ) {
44
        $this->output = $output;
45
        $this->diff = new ConsoleDiff();
46
        $this->terminal = $terminal ?: new Terminal();
47
        $this->wrapper = $wrapper ?: new Wrapper($this->terminal->getWidth());
48
    }
49
50
    /**
51
     * Sets information about the terminal
52
     *
53
     * @param TerminalInterface $terminal
54
     */
55
    public function setTerminal(TerminalInterface $terminal)
56
    {
57
        $this->terminal = $terminal;
58
        $this->wrapper->setWidth($terminal->getWidth());
59
    }
60
61
    /**
62
     * @return TerminalInterface
63
     */
64
    public function getTerminal()
65
    {
66
        return $this->terminal;
67
    }
68
69
    /**
70
     * @param string|array $messages The message as an array of lines or a single string
71
     * @param bool         $newline  Whether to add a newline
72
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
73
     *                               the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
74
     */
75
    public function write($messages, $newline = false, $options = 0)
76
    {
77
        $this->buffer = [];
78
        $this->output->write($messages, $newline, $options);
79
    }
80
81
    /**
82
     * @param string|array $messages The message as an array of lines or a single string
83
     * @param bool         $newline  Whether to add a newline
84
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
85
     *                               the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
86
     */
87
    public function reWrite($messages, $newline = false, $options = 0)
88
    {
89
        $messages = ($this->trim) ? $this->wrapper->trim($messages) : $this->wrapper->wrap($messages);
0 ignored issues
show
Bug introduced by
It seems like $messages can also be of type array; however, Graze\BufferedConsole\Wrap\Wrapper::trim() does only seem to accept string|array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $messages can also be of type array; however, Graze\BufferedConsole\Wrap\Wrapper::wrap() does only seem to accept string|array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
90
91
        if (count($this->buffer) === 0) {
92
            $this->buffer = $messages;
93
            $this->output->write($messages, $newline, $options);
94
            return;
95
        }
96
97
        $sizeDiff = ($newline ? 1 : 0);
98
        if (count($messages) + $sizeDiff > $this->terminal->getHeight()) {
99
            $messages = array_slice($messages, count($messages) + $sizeDiff - $this->terminal->getHeight());
100
        }
101
102
        $diff = $this->diff->lines($this->buffer, $messages);
103
104
        $output = $this->buildOutput($diff, $newline);
105
        $this->buffer = $messages;
106
107
        $this->output->write($output, $newline, $options);
108
    }
109
110
    /**
111
     * @param array $diff
112
     * @param bool  $newline
113
     *
114
     * @return string
115
     */
116
    private function buildOutput(array $diff, $newline = false)
117
    {
118
        $buffer = '';
119
120
        // reset cursor position
121
        $count = count($this->buffer);
122
        $mod = ($newline ? 1 : 0);
123
        $up = ($count > 0 ? $count - 1 + $mod : $mod);
124
        if ($up > 0) {
125
            $buffer .= $this->terminal->moveUp($up);
126
        }
127
        $buffer .= "\r";
128
129
        for ($i = 0; $i < count($diff); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
130
            $d = $diff[$i];
131
            if ($i !== 0) {
132
                $buffer .= PHP_EOL;
133
            }
134
            if (!is_null($d)) {
135
                if ($d['col'] > 0) {
136
                    $buffer .= $this->terminal->moveRight($d['col']);
137
                }
138
                $buffer .= $this->terminal->eraseToEnd() . $d['str'];
139
            }
140
        }
141
142
        return $buffer;
143
    }
144
145
    /**
146
     * Gets the OutputInterface for errors.
147
     *
148
     * @return OutputInterface
149
     */
150
    public function getErrorOutput()
151
    {
152
        return $this->output->getErrorOutput();
153
    }
154
155
    /**
156
     * Sets the OutputInterface used for errors.
157
     *
158
     * @param OutputInterface $error
159
     */
160
    public function setErrorOutput(OutputInterface $error)
161
    {
162
        $this->output->setErrorOutput($error);
163
    }
164
165
    /**
166
     * Writes a message to the output and adds a newline at the end.
167
     *
168
     * @param string|array $messages The message as an array of lines of a single string
169
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
170
     *                               the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
171
     */
172
    public function writeln($messages, $options = 0)
173
    {
174
        $this->write($messages, true, $options);
175
    }
176
177
    /**
178
     * Sets the verbosity of the output.
179
     *
180
     * @param int $level The level of verbosity (one of the VERBOSITY constants)
181
     */
182
    public function setVerbosity($level)
183
    {
184
        $this->output->setVerbosity($level);
185
    }
186
187
    /**
188
     * Gets the current verbosity of the output.
189
     *
190
     * @return int The current level of verbosity (one of the VERBOSITY constants)
191
     */
192
    public function getVerbosity()
193
    {
194
        return $this->output->getVerbosity();
195
    }
196
197
    /**
198
     * Returns whether verbosity is quiet (-q).
199
     *
200
     * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise
201
     */
202
    public function isQuiet()
203
    {
204
        return $this->output->isQuiet();
205
    }
206
207
    /**
208
     * Returns whether verbosity is verbose (-v).
209
     *
210
     * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise
211
     */
212
    public function isVerbose()
213
    {
214
        return $this->output->isVerbose();
215
    }
216
217
    /**
218
     * Returns whether verbosity is very verbose (-vv).
219
     *
220
     * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise
221
     */
222
    public function isVeryVerbose()
223
    {
224
        return $this->output->isVeryVerbose();
225
    }
226
227
    /**
228
     * Returns whether verbosity is debug (-vvv).
229
     *
230
     * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise
231
     */
232
    public function isDebug()
233
    {
234
        return $this->output->isDebug();
235
    }
236
237
    /**
238
     * Sets the decorated flag.
239
     *
240
     * @param bool $decorated Whether to decorate the messages
241
     */
242
    public function setDecorated($decorated)
243
    {
244
        $this->output->setDecorated($decorated);
245
    }
246
247
    /**
248
     * Gets the decorated flag.
249
     *
250
     * @return bool true if the output will decorate messages, false otherwise
251
     */
252
    public function isDecorated()
253
    {
254
        return $this->output->isDecorated();
255
    }
256
257
    /**
258
     * Sets output formatter.
259
     *
260
     * @param OutputFormatterInterface $formatter
261
     */
262
    public function setFormatter(OutputFormatterInterface $formatter)
263
    {
264
        $this->output->setFormatter($formatter);
265
    }
266
267
    /**
268
     * Returns current output formatter instance.
269
     *
270
     * @return OutputFormatterInterface
271
     */
272
    public function getFormatter()
273
    {
274
        return $this->output->getFormatter();
275
    }
276
277
    /**
278
     * @return bool
279
     */
280
    public function isTrim()
281
    {
282
        return $this->trim;
283
    }
284
285
    /**
286
     * Should we wrap the input or not, if this is set to false, it will trim each line
287
     *
288
     * @param bool $trim
289
     *
290
     * @return $this
291
     */
292
    public function setTrim($trim)
293
    {
294
        $this->trim = $trim;
295
        return $this;
296
    }
297
}
298