Completed
Push — master ( 787657...876ca1 )
by Todd
10:41
created

LogFormatter::setShowDurations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
cc 1
eloc 3
nc 1
nop 1
crap 2
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 bool Whether or not to show durations for tasks.
41
     */
42
    protected $showDurations = true;
43
44
    /**
45
     * @var array An array of currently running tasks.
46
     */
47 9
    protected $taskStack = [];
48 9
49 9
    /**
50
     * LogFormatter constructor.
51
     */
52
    public function __construct() {
53
        $this->formatOutput = Cli::guessFormatOutput();
54
    }
55
56
    /**
57
     * Output an error message.
58
     *
59
     * When formatting is turned on, error messages are displayed in red. Error messages are always output, even if they
60 2
     * are past the maximum display level.
61 2
     *
62
     * @param string $str The message to output.
63
     * @return LogFormatter Returns `$this` for fluent calls.
64
     */
65
    public function error($str) {
66
        return $this->message($this->formatString($str, ["\033[1;31m", "\033[0m"]), true);
67
    }
68
69
    /**
70
     * Output a success message.
71
     *
72
     * When formatting is turned on, success messages are displayed in green.
73
     *
74
     * @param string $str The message to output.
75
     * @return LogFormatter Returns `$this` for fluent calls.
76
     */
77
    public function success($str) {
78
        return $this->message($this->formatString($str, ["\033[1;32m", "\033[0m"]));
79
    }
80
81 9
    /**
82 9
     * Get the current depth of tasks.
83
     *
84
     * @return int Returns the current level.
85
     */
86
    protected function currentLevel() {
87
        return count($this->taskStack) + 1;
88
    }
89
90
    /**
91 7
     * Output a message that designates the beginning of a task.
92 7
     *
93 7
     * @param string $str The message to output.
94
     * @return $this Returns `$this` for fluent calls.
95 7
     */
96 7
    public function begin($str) {
97 2
        $output = $this->currentLevel() <= $this->getMaxLevel();
98 2
        $task = [$str, microtime(true), $output];
99 2
100
        if ($output) {
101 7
            if (!$this->isNewline) {
102 7
                echo $this->getEol();
103 7
                $this->isNewline = true;
104
            }
105 7
106
            echo $this->messageStr($str, false);
107 7
            $this->isNewline = false;
108
        }
109
110
        array_push($this->taskStack, $task);
111
112
        return $this;
113
    }
114
115
    /**
116
     * Output a message that designates a task being completed.
117 7
     *
118
     * @param string $str The message to output.
119 7
     * @param bool $force Whether or not to always output the message even if the task is past the max depth.
120 7
     * @return $this Returns `$this` for fluent calls.
121 7
     */
122 7
    public function end($str = '', $force = false) {
123 7
        // Find the task we are finishing.
124
        $task = array_pop($this->taskStack);
125
        if ($task !== null) {
126
            list($taskStr, $taskTimestamp, $taskOutput) = $task;
127 7
            $timespan = microtime(true) - $taskTimestamp;
128 7
        } else {
129 3
            trigger_error('Called LogFormatter::end() without calling LogFormatter::begin()', E_USER_NOTICE);
130 1
        }
131
132 1
        $pastMaxLevel = $this->currentLevel() > $this->getMaxLevel();
133 1
        if ($pastMaxLevel) {
134 1
            if ($force && isset($taskStr) && isset($taskOutput)) {
135 1
                if (!$taskOutput) {
136 1
                    // Output the full task string if it hasn't already been output.
137 1
                    $str = trim($taskStr.' '.$str);
138 1
                }
139 2
                if (!$this->isNewline) {
140
                    echo $this->getEol();
141 1
                    $this->isNewline = true;
142
                }
143 7
            } else {
144
                return $this;
145 5
            }
146 5
        }
147
148 3
        if (!empty($timespan) && $this->getShowDurations()) {
149 3
            $str = trim($str.' '.$this->formatString($this->formatDuration($timespan), ["\033[1;34m", "\033[0m"]));
150
        }
151
152 7
        if ($this->isNewline) {
153
            // Output the end message as a normal message.
154
            $this->message($str, $force);
155
        } else {
156
            // Output the end message on the same line.
157
            echo ' '.$str.$this->getEol();
158
            $this->isNewline = true;
159
        }
160
161
        return $this;
162
    }
163
164
    /**
165
     * Output a message that represents a task being completed in success.
166
     *
167
     * When formatting is turned on, success messages are output in green.
168
     *
169
     * @param string $str The message to output.
170
     * @param bool $force Whether or not to force a message past the max level to be output.
171
     * @return LogFormatter Returns `$this` for fluent calls.
172
     */
173
    public function endSuccess($str, $force = false) {
174
        return $this->end($this->formatString($str, ["\033[1;32m", "\033[0m"]), $force);
175
    }
176
177 1
    /**
178 1
     * Output a message that represents a task being completed in an error.
179
     *
180
     * When formatting is turned on, error messages are output in red. Error messages are always output even if they are
181
     * past the maximum depth.
182
     *
183
     * @param string $str The message to output.
184
     * @return LogFormatter Returns `$this` for fluent calls.
185
     */
186
    public function endError($str) {
187
        return $this->end($this->formatString($str, ["\033[1;31m", "\033[0m"]), true);
188
    }
189
190
    /**
191
     * Output a message that ends a task with an HTTP status code.
192
     *
193
     * 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
194
     * response code to this message.
195
     *
196
     * @param int $httpStatus The HTTP status code that represents the completion of a task.
197
     * @param bool $force Whether or not to force message output.
198
     * @return $this Returns `$this` for fluent calls.
199
     * @see LogFormatter::endSuccess(), LogFormatter::endError().
200
     */
201
    public function endHttpStatus($httpStatus, $force = false) {
202
        $statusStr = sprintf('%03d', $httpStatus);
203
204
        if ($httpStatus == 0 || $httpStatus >= 400) {
205
            $this->endError($statusStr);
206
        } elseif ($httpStatus >= 200 && $httpStatus < 300) {
207
            $this->endSuccess($statusStr, $force);
208
        } else {
209
            $this->end($statusStr, $force);
210
        }
211
212
        return $this;
213 8
    }
214 8
215
    /**
216 8
     * Format a time duration.
217 5
     *
218
     * @param float $duration The duration in seconds and fractions of a second.
219 3
     * @return string Returns the duration formatted for humans.
220 3
     * @see microtime()
221 3
     */
222 1
    public function formatDuration($duration) {
223 1
        $suffixes = [0.000001 => 'μs', 1 => 'ms', 60 => 's', 3600 => 'm'];
0 ignored issues
show
Unused Code introduced by
$suffixes 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...
224 1
225 1
        if ($duration < 1.0e-3) {
226 1
            $n = number_format($duration * 1.0e6, 0);
227 1
            $sx = 'μs';
228 1
        } elseif ($duration < 1) {
229 3
            $n = number_format($duration * 1000, 0);
230
            $sx = 'ms';
231 3
        } elseif ($duration < 60) {
232 3
            $n = number_format($duration, 2);
233 5
            $sx = 's';
234
        } elseif ($duration < 3600) {
235 3
            $n = number_format($duration / 60, 1);
236
            $sx = 'm';
237 7
        } elseif ($duration < 86400) {
238 2
            $n = number_format($duration / 3600, 1);
239 2
            $sx = 'h';
240 2
        } else {
241 7
            $n = number_format($duration / 86400, 1);
242 7
            $sx = 'd';
243
        }
244
245
        $result = rtrim($n, '0.').$sx;
246
        return $result;
247
    }
248
249
    /**
250
     * Output a message.
251
     *
252
     * @param string $str The message to output.
253
     * @param bool $force Whether or not to force output of the message even if it's past the max depth.
254
     * @return $this Returns `$this` for fluent calls.
255
     */
256
    public function message($str, $force = false) {
257
        $pastMaxLevel = $this->currentLevel() > $this->getMaxLevel();
258
259
        if ($pastMaxLevel) {
260 9
            if ($force) {
261 9
                // Trace down the task list and output everything that hasn't already been output.
262 9
                foreach ($this->taskStack as $indent => $task) {
263
                    list($taskStr, $taskTimestamp, $taskOutput) = $this->taskStack[$indent];
264
                    if (!$taskOutput) {
265 9
                        if (!$this->isNewline) {
266 9
                            echo $this->eol;
267 9
                            $this->isNewline = true;
268 9
                        }
269
                        echo $this->fullMessageStr($taskTimestamp, $taskStr, $indent, true);
270 9
                        $this->taskStack[$indent][2] = true;
271 9
                    } else {
272 9
                        continue;
273 5
                    }
274 5
                }
275 2
            } else {
276
                return $this;
277
            }
278 9
        }
279
280 9
        if (!$this->isNewline) {
281 8
            echo $this->eol;
282 8
            $this->isNewline = true;
283
        }
284 9
        echo $this->messageStr($str, true);
285 7
        return $this;
286 7
    }
287 9
288
    /**
289
     * Get whether or not output should be formatted.
290
     *
291
     * @return boolean Returns **true** if output should be formatted or **false** otherwise.
292
     */
293
    public function getFormatOutput() {
294
        return $this->formatOutput;
295
    }
296
297 9
    /**
298 9
     * Set whether or not output should be formatted.
299
     *
300
     * @param boolean $formatOutput Whether or not to format output.
301
     * @return LogFormatter Returns `$this` for fluent calls.
302
     */
303
    public function setFormatOutput($formatOutput) {
304
        $this->formatOutput = $formatOutput;
305
        return $this;
306
    }
307
308 3
    protected function fullMessageStr($timestamp, $str, $indent = null, $eol = true) {
309 3
        if ($indent === null) {
310
            $indent = $this->currentLevel() - 1;
311
        }
312 3
313
        if ($indent <= 0) {
314
            $indentStr = '';
315
        } elseif ($indent === 1) {
316
            $indentStr = '- ';
317
        } else {
318
            $indentStr = str_repeat('  ', $indent - 1).'- ';
319
        }
320
321
        $result = $indentStr.$str;
322
323
        if ($this->getDateFormat()) {
324 9
            $result = strftime($this->getDateFormat(), $timestamp).' '.$result;
325 9
        }
326
327 9
        if ($eol) {
328 9
            $result .= $this->eol;
329
        }
330
        return $result;
331
    }
332
333
    /**
334
     * Create a message string.
335
     *
336
     * @param string $str The message to output.
337
     * @param bool $eol Whether or not to add an EOL.
338
     * @return string Returns the message.
339 9
     */
340 9
    protected function messageStr($str, $eol = true) {
341
        return $this->fullMessageStr(time(), $str, null, $eol);
342
    }
343
344
    /**
345
     * Format some text for the console.
346
     *
347 9
     * @param string $text The text to format.
348 9
     * @param string[] $wrap The format to wrap in the form ['before', 'after'].
349
     * @return string Returns the string formatted according to {@link Cli::$format}.
350
     */
351
    protected function formatString($text, array $wrap) {
352 9
        if ($this->formatOutput) {
353 9
            return "{$wrap[0]}$text{$wrap[1]}";
354
        } else {
355
            return $text;
356
        }
357
    }
358
359
    /**
360
     * Get the maxLevel.
361
     *
362 9
     * @return int Returns the maxLevel.
363 9
     */
364
    public function getMaxLevel() {
365
        return $this->maxLevel;
366
    }
367
368
    /**
369
     * @param int $maxLevel
370
     * @return LogFormatter
371
     */
372
    public function setMaxLevel($maxLevel) {
373 9
        if ($maxLevel < 0) {
374 9
            throw new \InvalidArgumentException("The max level must be greater than zero.", 416);
375 9
        }
376
377
        $this->maxLevel = $maxLevel;
378
        return $this;
379
    }
380
381
    /**
382
     * Get the date format as passed to {@link strftime()}.
383 5
     *
384 5
     * @return string Returns the date format.
385
     * @see strftime()
386
     */
387
    public function getDateFormat() {
388
        return $this->dateFormat;
389
    }
390
391
    /**
392
     * Set the date format as passed to {@link strftime()}.
393 9
     *
394 9
     * @param string $dateFormat
395 9
     * @return LogFormatter Returns `$this` for fluent calls.
396
     * @see strftime()
397
     */
398
    public function setDateFormat($dateFormat) {
399
        $this->dateFormat = $dateFormat;
400
        return $this;
401
    }
402
403
    /**
404
     * Get the end of line string to use.
405
     *
406
     * @return string Returns the eol string.
407
     */
408
    public function getEol() {
409
        return $this->eol;
410
    }
411
412
    /**
413
     * Set the end of line string.
414
     *
415
     * @param string $eol The end of line string to use.
416
     * @return LogFormatter Returns `$this` for fluent calls.
417
     */
418
    public function setEol($eol) {
419
        $this->eol = $eol;
420
        return $this;
421
    }
422
423
    /**
424
     * Get the showDurations.
425
     *
426
     * @return boolean Returns the showDurations.
427
     */
428
    public function getShowDurations() {
429
        return $this->showDurations;
430
    }
431
432
    /**
433
     * Set the showDurations.
434
     *
435
     * @param boolean $showDurations
436
     * @return LogFormatter Returns `$this` for fluent calls.
437
     */
438
    public function setShowDurations($showDurations) {
439
        $this->showDurations = $showDurations;
440
        return $this;
441
    }
442
}
443