Completed
Push — feature/logger ( 1c2c4d...c4d378 )
by Todd
03:49 queued 02:00
created

LogFormatter::end()   D

Complexity

Conditions 9
Paths 22

Size

Total Lines 37
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 37
ccs 17
cts 17
cp 1
rs 4.9091
cc 9
eloc 23
nc 22
nop 2
crap 9
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
        $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
    public function error($str) {
61
        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
    protected function currentLevel() {
82
        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 5
    public function begin($str) {
92
        $output = $this->currentLevel() <= $this->getMaxLevel();
93
        $task = [$str, microtime(true), $output];
94
95 2
        if ($output) {
96 5
            if (!$this->isNewline) {
97
                echo $this->getEol();
98 2
                $this->isNewline = true;
99
            }
100
101
            echo $this->messageStr($str, false);
102 5
            $this->isNewline = false;
103
        }
104
105
        array_push($this->taskStack, $task);
106
107 2
        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 4
    public function end($str, $force = false) {
118
        // Find the task we are finishing.
119
        $task = array_pop($this->taskStack);
120 2
        if ($task !== null) {
121 2
            list($taskStr, $taskTimestamp, $taskOutput) = $task;
122
            $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
        } else {
124
            trigger_error('Called LogFormatter::end() without calling LogFormatter::begin()', E_USER_NOTICE);
125 2
        }
126
127
        $pastMaxLevel = $this->currentLevel() > $this->getMaxLevel();
128 2
        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
                }
134 1
                if (!$this->isNewline) {
135
                    echo $this->getEol();
136 1
                    $this->isNewline = true;
137
                }
138
            } else {
139 2
                return $this;
140 1
            }
141
        }
142
143 4
        if ($this->isNewline) {
144
            // Output the end message as a normal message.
145
            $this->message($str, $force);
146
        } else {
147
            // Output the end message on the same line.
148
            echo ' '.$str.$this->getEol();
149 3
            $this->isNewline = true;
150 3
        }
151
152 4
        return $this;
153 3
    }
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
    public function endError($str) {
178
        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 7
    public function message($str, $force = false) {
214
        $pastMaxLevel = $this->currentLevel() > $this->getMaxLevel();
215
216 3
        if ($pastMaxLevel) {
217 2
            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 1
                    list($taskStr, $taskTimestamp, $taskOutput) = $this->taskStack[$indent];
221 1
                    if (!$taskOutput) {
222 1
                        if (!$this->isNewline) {
223 1
                            echo $this->eol;
224 1
                            $this->isNewline = true;
225
                        }
226
                        echo $this->fullMessageStr($taskTimestamp, $taskStr, $indent, true);
227 1
                        $this->taskStack[$indent][2] = true;
228
                    } else {
229 2
                        continue;
230 1
                    }
231
                }
232
            } else {
233 5
                return $this;
234 3
            }
235
        }
236
237 3
        if (!$this->isNewline) {
238 2
            echo $this->eol;
239 2
            $this->isNewline = true;
240
        }
241
        echo $this->messageStr($str, true);
242 3
        return $this;
243 7
    }
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 4
    protected function fullMessageStr($timestamp, $str, $indent = null, $eol = true) {
266 4
        if ($indent === null) {
267
            $indent = $this->currentLevel() - 1;
268
        }
269
270 4
        if ($indent <= 0) {
271 4
            $indentStr = '';
272 2
        } elseif ($indent === 1) {
273 3
            $indentStr = '- ';
274
        } else {
275
            $indentStr = str_repeat('  ', $indent - 1).'- ';
276 4
        }
277
278 4
        $result = $indentStr.$str;
279
280
        if ($this->getDateFormat()) {
281
            $result = strftime($this->getDateFormat(), $timestamp).' '.$result;
282
        }
283
284 4
        if ($eol) {
285 3
            $result .= $this->eol;
286
        }
287 4
        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
    protected function messageStr($str, $eol = true) {
298
        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 3
    }
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
    public function guessFormatOutput() {
325
        if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
326
            return false;
327
        } elseif (function_exists('posix_isatty')) {
328
            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 2
    public function getMaxLevel() {
340 2
        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 1
    public function getDateFormat() {
363 1
        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 8
    public function setDateFormat($dateFormat) {
374 8
        $this->dateFormat = $dateFormat;
375 8
        return $this;
376
    }
377
378
    /**
379
     * Get the end of line string to use.
380
     *
381
     * @return string Returns the eol string.
382
     */
383 4
    public function getEol() {
384 4
        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