Completed
Push — feature/logger ( 65a307 )
by
unknown
01:30
created

Logger::end()   B

Complexity

Conditions 8
Paths 4

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 7.1428
c 0
b 0
f 0
cc 8
eloc 12
nc 4
nop 3
1
<?php
2
/**
3
 * @author Chris dePage <[email protected]>
4
 * @copyright 2009-2018 Vanilla Forums Inc.
5
 * @license MIT
6
 */
7
8
namespace Garden\Cli\Logger;
9
10
use Garden\Cli\Logger\Writer\WriterInterface;
11
12
/**
13
 * Garden logger that accepts multiple writers.
14
 */
15
class Logger implements LoggerInterface {
16
17
    /**
18
     * @var LoggerInterface[] An array of writers.
19
     */
20
    protected $writers = [];
21
22
    /**
23
     * @var int The max indent level to send to the writers.
24
     */
25
    protected $maxLevel = 2;
26
27
    /**
28
     * @var array An array of currently running tasks.
29
     */
30
    protected $taskStack = [];
31
32
    /**
33
     * Add a writer.
34
     *
35
     * @param WriterInterface $writer A writer to use.
36
     *
37
     * @return $this
38
     */
39
    public function addWriter(WriterInterface $writer) {
40
        array_push($this->writers, $writer);
41
42
        return $this;
43
    }
44
45
    /**
46
     * Set the maximum indent level to send to the writers.
47
     *
48
     * @param int $maxLevel The maximum indent level
49
     *
50
     * @return $this
51
     */
52
    public function setMaxLevel(int $maxLevel) {
53
        if ($maxLevel < 0) {
54
            throw new \InvalidArgumentException("The max level must be greater than zero.", 416);
55
        }
56
57
        $this->maxLevel = $maxLevel;
58
59
        return $this;
60
    }
61
62
    /**
63
     * Get the max indent level to send to the writers.
64
     *
65
     * @return int Returns the maxLevel.
66
     */
67
    public function getMaxLevel() {
68
        return $this->maxLevel;
69
    }
70
71
    /**
72
     * Log a message that designates the beginning of a task.
73
     *
74
     * @param string $str The message to output.
75
     * @return $this
76
     */
77
    public function begin(string $str) {
78
        $indentLevel = count($this->taskStack) + 1;
79
        $task = [$str, microtime(true), $indentLevel];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
80
81
        array_push($this->taskStack, $task);
82
83
        // if the current indent level is less than the max, we trigger the logging behaviour
84
        if ($indentLevel <= $this->getMaxLevel()) {
85
            $this->write(time(), LogLevels::INFO, $str, $indentLevel);
86
        }
87
88
        return $this;
89
    }
90
91
    /**
92
     * Log an error message.
93
     *
94
     * @param string $str The message to output.
95
     *
96
     * @return $this
97
     */
98
    public function error(string $str) {
99
        $indentLevel = count($this->taskStack);
100
        $this->write(time(), LogLevels::ERROR, $str, $indentLevel);
101
102
        return $this;
103
    }
104
105
    /**
106
     * Log a success message.
107
     *
108
     * @param string $str The message to output.
109
     *
110
     * @return $this
111
     */
112
    public function success(string $str) {
113
        $indentLevel = count($this->taskStack);
114
        $this->write(time(), LogLevels::SUCCESS, $str, $indentLevel);
115
116
        return $this;
117
    }
118
119
    /**
120
     * Log a warning message.
121
     *
122
     * @param string $str The message to output.
123
     *
124
     * @return $this
125
     */
126
    public function warn(string $str) {
127
        $indentLevel = count($this->taskStack);
128
        $this->write(time(), LogLevels::WARNING, $str, $indentLevel);
129
130
        return $this;
131
    }
132
133
    /**
134
     * Log an info message.
135
     *
136
     * @param string $str The message to output.
137
     * @param bool $force Whether or not to force output of the message even if it's past the max depth.
138
     *
139
     * @return $this
140
     */
141
    public function message(string $str, bool $force = false) {
142
        $indentLevel = count($this->taskStack);
143
144
        if ($indentLevel > $this->getMaxLevel()) {
145
146
            // if not forced we drop the message
147
            if (!$force) {
148
                return $this;
149
            }
150
151
            // output everything that hasn't been output so far
152
            foreach ($this->taskStack as $task) {
153
                list($taskStr, $taskTimestamp, $taskIndentLevel) = $task;
154
                if ($taskIndentLevel > $this->getMaxLevel()) {
155
                  $this->write($taskTimestamp, LogLevels::INFO, $taskStr, $taskIndentLevel);
156
                }
157
            }
158
        }
159
160
        $this->write(time(), LogLevels::INFO, $str, $indentLevel);
161
162
        return $this;
163
    }
164
165
    /**
166
     * Log a message that represents a task being completed in success.
167
     *
168
     * @param string $str The message to output.
169
     * @param bool $force Whether or not to force a message past the max level to be output.
170
     *
171
     * @return $this
172
     */
173
    public function endSuccess(string $str, bool $force = false) {
174
        return $this->end($str, $force, LogLevels::SUCCESS);
175
    }
176
177
    /**
178
     * Log 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
     *
185
     * @return $this
186
     */
187
    public function endError(string $str) {
188
        return $this->end($str, true, LogLevels::ERROR);
189
    }
190
191
    /**
192
     * Log a message that designates a task being completed.
193
     *
194
     * @param string $str The message to output.
195
     * @param bool $force Whether or not to always output the message even if the task is past the max depth.
196
     * @param string $logLevel The level/type of log to write.
197
     *
198
     * @return $this
199
     */
200
    public function end(string $str = '', bool $force =  false, $logLevel = LogLevels::INFO) {
201
        // get the task we are finishing (there has to be one)
202
        $task = array_pop($this->taskStack);
203
        if (is_null($task)) {
204
            trigger_error('Called Logger::end() without calling Logger::begin()', E_USER_NOTICE);
205
        } else {
206
            list($taskStr, $taskTimestamp, $indentLevel) = $task;
207
            $duration = microtime(true) - $taskTimestamp;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 36 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
208
209
            if (count($this->taskStack) >= $this->getMaxLevel()) {
210
                if (!$force || !isset($taskStr) || $indentLevel >= $this->getMaxLevel()) {
211
                    return $this;
212
                }
213
            }
214
215
            // update the $str so we prepend the original task $str to it
216
            $str = trim($taskStr.' '.$str);
217
        }
218
219
        return $this->write(time(), $logLevel, $str, $indentLevel || 0, $duration || null);
0 ignored issues
show
Bug introduced by
The variable $indentLevel does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $duration does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Documentation introduced by
$indentLevel || 0 is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$duration || null is of type boolean, but the function expects a double|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
220
    }
221
222
    /**
223
     * Log a message that ends a task with an HTTP status code.
224
     *
225
     * @param int $httpStatus The HTTP status code that represents the completion of a task.
226
     * @param bool $force Whether or not to force message output.
227
     *
228
     * @return $this
229
     *
230
     * @see LogFormatter::endSuccess(), LogFormatter::endError().
231
     */
232
    public function endHttpStatus(int $httpStatus, bool $force = false) {
233
        $statusStr = sprintf('%03d', $httpStatus);
234
235
        if ($httpStatus == 0 || $httpStatus >= 400) {
236
            $this->endError($statusStr);
237
        } elseif ($httpStatus >= 200 && $httpStatus < 300) {
238
            $this->endSuccess($statusStr, $force);
239
        } else {
240
            $this->end($statusStr, $force);
241
        }
242
243
        return $this;
244
    }
245
246
    /**
247
     * Write the stream.
248
     *
249
     * @param int $timestamp The unix timestamp of the log.
250
     * @param string $logLevel The level of the message (e.g. SUCCESS, WARNING, ERROR).
251
     * @param int $indentLevel The nesting level of the message.
252
     * @param string $message The message.
253
     * @param float|null $duration The duration to add to the message.
254
     *
255
     * @return $this
256
     */
257
    public function write(int $timestamp, string $logLevel, string $message, $indentLevel = 0, $duration = null) {
258
        foreach($this->writers as $writer) {
259
            $writer->write($timestamp, $logLevel, $message, $indentLevel, $duration);
260
        }
261
262
        return $this;
263
    }
264
}
265