Completed
Push — master ( aa878f...b0d00a )
by Todd
02:31
created

LogFormatter::message()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 31
ccs 25
cts 25
cp 1
rs 6.7273
cc 7
eloc 21
nc 5
nop 2
crap 7
1
<?php
2
/**
3
 * @author Todd Burry <[email protected]>
4
 * @copyright 2009-2015 Vanilla Forums Inc.
5
 * @license MIT
6
 */
7
8
namespace Garden\Cli;
9
10
/**
11
 * A helper class to format CLI output in a log-style format.
12
 */
13
class LogFormatter {
14
    /**
15
     * @var string The date format as passed to {@link strftime()}.
16
     */
17
    protected $dateFormat = '[%F %T]';
18
19
    /**
20
     * @var string The end of line string to use.
21
     */
22
    protected $eol = PHP_EOL;
23
24
    /**
25
     * @var bool Whether or not to format output.
26
     */
27
    protected $formatOutput;
28
29
    /**
30
     * @var bool Whether or not the console is on a new line.
31
     */
32
    protected $isNewline = true;
33
34
    /**
35
     * @var int The maximum level deep to output.
36
     */
37
    protected $maxLevel = 2;
38
39
    /**
40
     * @var array An array of currently running tasks.
41
     */
42
    protected $taskStack = [];
43
44
    /**
45
     * LogFormatter constructor.
46
     */
47 9
    public function __construct() {
48 9
        $this->formatOutput = $this->guessFormatOutput();
49 9
    }
50
51
    /**
52
     * Output an error message.
53
     *
54
     * When formatting is turned on, error messages are displayed in red. Error messages are always output, even if they
55
     * are past the maximum display level.
56
     *
57
     * @param string $str The message to output.
58
     * @return LogFormatter Returns `$this` for fluent calls.
59
     */
60 2
    public function error($str) {
61 2
        return $this->message($this->formatString($str, ["\033[1;31m", "\033[0m"]), true);
62
    }
63
64
    /**
65
     * Output a success message.
66
     *
67
     * When formatting is turned on, success messages are displayed in green.
68
     *
69
     * @param string $str The message to output.
70
     * @return LogFormatter Returns `$this` for fluent calls.
71
     */
72
    public function success($str) {
73
        return $this->message($this->formatString($str, ["\033[1;32m", "\033[0m"]));
74
    }
75
76
    /**
77
     * Get the current depth of tasks.
78
     *
79
     * @return int Returns the current level.
80
     */
81 9
    protected function currentLevel() {
82 9
        return count($this->taskStack) + 1;
83
    }
84
85
    /**
86
     * Output a message that designates the beginning of a task.
87
     *
88
     * @param string $str The message to output.
89
     * @return $this Returns `$this` for fluent calls.
90
     */
91 7
    public function begin($str) {
92 7
        $output = $this->currentLevel() <= $this->getMaxLevel();
93 7
        $task = [$str, microtime(true), $output];
94
95 7
        if ($output) {
96 7
            if (!$this->isNewline) {
97 2
                echo $this->getEol();
98 2
                $this->isNewline = true;
99 2
            }
100
101 7
            echo $this->messageStr($str, false);
102 7
            $this->isNewline = false;
103 7
        }
104
105 7
        array_push($this->taskStack, $task);
106
107 7
        return $this;
108
    }
109
110
    /**
111
     * Output a message that designates a task being completed.
112
     *
113
     * @param string $str The message to output.
114
     * @param bool $force Whether or not to always output the message even if the task is past the max depth.
115
     * @return $this Returns `$this` for fluent calls.
116
     */
117 7
    public function end($str, $force = false) {
118
        // Find the task we are finishing.
119 7
        $task = array_pop($this->taskStack);
120 7
        if ($task !== null) {
121 7
            list($taskStr, $taskTimestamp, $taskOutput) = $task;
122 7
            $timespan = microtime(true) - $taskTimestamp;
0 ignored issues
show
Unused Code introduced by
$timespan is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
123 7
        } else {
124
            trigger_error('Called LogFormatter::end() without calling LogFormatter::begin()', E_USER_NOTICE);
125
        }
126
127 7
        $pastMaxLevel = $this->currentLevel() > $this->getMaxLevel();
128 7
        if ($pastMaxLevel) {
129 3
            if ($force && isset($taskStr) && isset($taskOutput)) {
130 1
                if (!$taskOutput) {
131
                    // Output the full task string if it hasn't already been output.
132 1
                    $str = $taskStr.' '.$str;
133 1
                }
134 1
                if (!$this->isNewline) {
135 1
                    echo $this->getEol();
136 1
                    $this->isNewline = true;
137 1
                }
138 1
            } else {
139 2
                return $this;
140
            }
141 1
        }
142
143 7
        if ($this->isNewline) {
144
            // Output the end message as a normal message.
145 5
            $this->message($str, $force);
146 5
        } else {
147
            // Output the end message on the same line.
148 3
            echo ' '.$str.$this->getEol();
149 3
            $this->isNewline = true;
150
        }
151
152 7
        return $this;
153
    }
154
155
    /**
156
     * Output a message that represents a task being completed in success.
157
     *
158
     * When formatting is turned on, success messages are output in green.
159
     *
160
     * @param string $str The message to output.
161
     * @param bool $force Whether or not to force a message past the max level to be output.
162
     * @return LogFormatter Returns `$this` for fluent calls.
163
     */
164
    public function endSuccess($str, $force = false) {
165
        return $this->end($this->formatString($str, ["\033[1;32m", "\033[0m"]), $force);
166
    }
167
168
    /**
169
     * Output a message that represents a task being completed in an error.
170
     *
171
     * When formatting is turned on, error messages are output in red. Error messages are always output even if they are
172
     * past the maximum depth.
173
     *
174
     * @param string $str The message to output.
175
     * @return LogFormatter Returns `$this` for fluent calls.
176
     */
177 1
    public function endError($str) {
178 1
        return $this->end($this->formatString($str, ["\033[1;31m", "\033[0m"]), true);
179
    }
180
181
    /**
182
     * Output a message that ends a task with an HTTP status code.
183
     *
184
     * This method is useful if you are making a call to an external API as a task. You can end the task by passing the
185
     * response code to this message.
186
     *
187
     * @param int $httpStatus The HTTP status code that represents the completion of a task.
188
     * @param bool $force Whether or not to force message output.
189
     * @return $this Returns `$this` for fluent calls.
190
     * @see LogFormatter::endSuccess(), LogFormatter::endError().
191
     */
192
    public function endHttpStatus($httpStatus, $force = false) {
193
        $statusStr = sprintf('%03d', $httpStatus);
194
195
        if ($httpStatus == 0 || $httpStatus >= 400) {
196
            $this->endError($statusStr);
197
        } elseif ($httpStatus >= 200 && $httpStatus < 300) {
198
            $this->endSuccess($statusStr, $force);
199
        } else {
200
            $this->end($statusStr, $force);
201
        }
202
203
        return $this;
204
    }
205
206
    /**
207
     * Output a message.
208
     *
209
     * @param string $str The message to output.
210
     * @param bool $force Whether or not to force output of the message even if it's past the max depth.
211
     * @return $this Returns `$this` for fluent calls.
212
     */
213 8
    public function message($str, $force = false) {
214 8
        $pastMaxLevel = $this->currentLevel() > $this->getMaxLevel();
215
216 8
        if ($pastMaxLevel) {
217 5
            if ($force) {
218
                // Trace down the task list and output everything that hasn't already been output.
219 3
                foreach ($this->taskStack as $indent => $task) {
220 3
                    list($taskStr, $taskTimestamp, $taskOutput) = $this->taskStack[$indent];
221 3
                    if (!$taskOutput) {
222 1
                        if (!$this->isNewline) {
223 1
                            echo $this->eol;
224 1
                            $this->isNewline = true;
225 1
                        }
226 1
                        echo $this->fullMessageStr($taskTimestamp, $taskStr, $indent, true);
227 1
                        $this->taskStack[$indent][2] = true;
228 1
                    } else {
229 3
                        continue;
230
                    }
231 3
                }
232 3
            } else {
233 5
                return $this;
234
            }
235 3
        }
236
237 7
        if (!$this->isNewline) {
238 2
            echo $this->eol;
239 2
            $this->isNewline = true;
240 2
        }
241 7
        echo $this->messageStr($str, true);
242 7
        return $this;
243
    }
244
245
    /**
246
     * Get the format.
247
     *
248
     * @return boolean Returns the format.
249
     */
250
    public function getFormatOutput() {
251
        return $this->formatOutput;
252
    }
253
254
    /**
255
     * Set the format.
256
     *
257
     * @param boolean $formatOutput
258
     * @return LogFormatter Returns `$this` for fluent calls.
259
     */
260 9
    public function setFormatOutput($formatOutput) {
261 9
        $this->formatOutput = $formatOutput;
262 9
        return $this;
263
    }
264
265 9
    protected function fullMessageStr($timestamp, $str, $indent = null, $eol = true) {
266 9
        if ($indent === null) {
267 9
            $indent = $this->currentLevel() - 1;
268 9
        }
269
270 9
        if ($indent <= 0) {
271 9
            $indentStr = '';
272 9
        } elseif ($indent === 1) {
273 5
            $indentStr = '- ';
274 5
        } else {
275 2
            $indentStr = str_repeat('  ', $indent - 1).'- ';
276
        }
277
278 9
        $result = $indentStr.$str;
279
280 9
        if ($this->getDateFormat()) {
281 8
            $result = strftime($this->getDateFormat(), $timestamp).' '.$result;
282 8
        }
283
284 9
        if ($eol) {
285 7
            $result .= $this->eol;
286 7
        }
287 9
        return $result;
288
    }
289
290
    /**
291
     * Create a message string.
292
     *
293
     * @param string $str The message to output.
294
     * @param bool $eol Whether or not to add an EOL.
295
     * @return string Returns the message.
296
     */
297 9
    protected function messageStr($str, $eol = true) {
298 9
        return $this->fullMessageStr(time(), $str, null, $eol);
299
    }
300
301
    /**
302
     * Format some text for the console.
303
     *
304
     * @param string $text The text to format.
305
     * @param array $wrap The format to wrap in the form ['before', 'after'].
306
     * @return string Returns the string formatted according to {@link Cli::$format}.
307
     */
308 3
    protected function formatString($text, array $wrap) {
309 3
        if ($this->formatOutput) {
310
            return "{$wrap[0]}$text{$wrap[1]}";
311
        } else {
312 3
            return $text;
313
        }
314
    }
315
316
    /**
317
     * Guess whether or not to format the output with colors.
318
     *
319
     * If the current environment is being redirected to a file then output should not be formatted. Also, Windows
320
     * machines do not support terminal colors so formatting should be suppressed on them too.
321
     *
322
     * @return bool Returns **true** if the output can be formatter or **false** otherwise.
323
     */
324 9
    public function guessFormatOutput() {
325 9
        if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
326
            return false;
327 9
        } elseif (function_exists('posix_isatty')) {
328 9
            return posix_isatty(STDOUT);
329
        } else {
330
            return true;
331
        }
332
    }
333
334
    /**
335
     * Get the maxLevel.
336
     *
337
     * @return int Returns the maxLevel.
338
     */
339 9
    public function getMaxLevel() {
340 9
        return $this->maxLevel;
341
    }
342
343
    /**
344
     * @param int $maxLevel
345
     * @return LogFormatter
346
     */
347 9
    public function setMaxLevel($maxLevel) {
348 9
        if ($maxLevel < 0) {
349
            throw new \InvalidArgumentException("The max level must be greater than zero.", 416);
350
        }
351
352 9
        $this->maxLevel = $maxLevel;
353 9
        return $this;
354
    }
355
356
    /**
357
     * Get the date format as passed to {@link strftime()}.
358
     *
359
     * @return string Returns the date format.
360
     * @see strftime()
361
     */
362 9
    public function getDateFormat() {
363 9
        return $this->dateFormat;
364
    }
365
366
    /**
367
     * Set the date format as passed to {@link strftime()}.
368
     *
369
     * @param string $dateFormat
370
     * @return LogFormatter Returns `$this` for fluent calls.
371
     * @see strftime()
372
     */
373 9
    public function setDateFormat($dateFormat) {
374 9
        $this->dateFormat = $dateFormat;
375 9
        return $this;
376
    }
377
378
    /**
379
     * Get the end of line string to use.
380
     *
381
     * @return string Returns the eol string.
382
     */
383 5
    public function getEol() {
384 5
        return $this->eol;
385
    }
386
387
    /**
388
     * Set the end of line string.
389
     *
390
     * @param string $eol The end of line string to use.
391
     * @return LogFormatter Returns `$this` for fluent calls.
392
     */
393 9
    public function setEol($eol) {
394 9
        $this->eol = $eol;
395 9
        return $this;
396
    }
397
}
398