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\Writer; |
9
|
|
|
|
10
|
|
|
use Garden\Cli\Logger\Formatter\ColorizerFormatter; |
11
|
|
|
use Garden\Cli\Logger\Formatter\FormatterInterface; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Log writer to output to a stream. |
15
|
|
|
*/ |
16
|
|
|
class IoStreamWriter implements WriterInterface { |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var resource The stream resource. |
20
|
|
|
*/ |
21
|
|
|
protected $stream; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var FormatterInterface[] An array of formatters. |
25
|
|
|
*/ |
26
|
|
|
protected $formatters = []; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var bool Whether or not to show durations for tasks. |
30
|
|
|
*/ |
31
|
|
|
protected $showDurations = true; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* IoStreamWriter constructor. |
35
|
|
|
* |
36
|
|
|
* @param string $stream The stream to write to. |
37
|
|
|
* |
38
|
|
|
* @throws \Exception |
39
|
|
|
* |
40
|
|
|
* @see http://php.net/manual/en/wrappers.php.php |
41
|
|
|
*/ |
42
|
|
|
public function __construct(string $stream) { |
43
|
|
|
// this writer is only for php streams |
44
|
|
|
if (substr($stream, 0, 6) !== 'php://') { |
45
|
|
|
throw new \Exception('Stream must start with "php://"'); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
$this->stream = fopen($stream, 'w'); |
|
|
|
|
49
|
|
|
|
50
|
|
|
// ensure the stream opened |
51
|
|
|
if ($this->stream === false) { |
52
|
|
|
throw new \Exception('Stream "'.$stream.'" could not be opened'); |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Set the showDurations. |
58
|
|
|
* |
59
|
|
|
* @param boolean $showDurations Flag to indicate if durations should be displayed. |
60
|
|
|
* |
61
|
|
|
* @return $this |
62
|
|
|
*/ |
63
|
|
|
public function setShowDurations(bool $showDurations) { |
64
|
|
|
$this->showDurations = $showDurations; |
65
|
|
|
|
66
|
|
|
return $this; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Get the showDurations. |
71
|
|
|
* |
72
|
|
|
* @return boolean Returns the showDurations. |
73
|
|
|
*/ |
74
|
|
|
public function getShowDurations() { |
75
|
|
|
return $this->showDurations; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Add a formatter to be used by the writer. |
80
|
|
|
* |
81
|
|
|
* @param FormatterInterface $formatter A formatter to use for the writer. |
82
|
|
|
* |
83
|
|
|
* @return $this |
84
|
|
|
*/ |
85
|
|
|
public function addFormatter(FormatterInterface $formatter) { |
86
|
|
|
// edge case: the ColorizerFormatter cannot be used if the output terminal doesn't support it |
87
|
|
|
if ($formatter instanceof ColorizerFormatter && !$this->doesStreamSupportColors($this->stream)) { |
88
|
|
|
return $this; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
array_push($this->formatters, $formatter); |
92
|
|
|
|
93
|
|
|
return $this; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Write the stream. |
98
|
|
|
* |
99
|
|
|
* @param int $timestamp The unix timestamp of the log. |
100
|
|
|
* @param string $logLevel The level of the message (e.g. INFO, SUCCESS, WARNING, ERROR). |
101
|
|
|
* @param string $message The message. |
102
|
|
|
* @param int $indentLevel The nesting level of the message. |
103
|
|
|
* @param float|null $duration The duration to add to the message. |
104
|
|
|
*/ |
105
|
|
|
public function write(int $timestamp, string $logLevel, string $message, int $indentLevel = 0, $duration = null) { |
106
|
|
|
// ensure our stream hasn't died |
107
|
|
|
if (feof($this->stream)) { |
108
|
|
|
// @codeCoverageIgnoreStart |
109
|
|
|
trigger_error('Called '.__CLASS__.'::write() but file handle was closed.', E_USER_WARNING); |
110
|
|
|
return; |
111
|
|
|
// @codeCoverageIgnoreEnd |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
// apply each of the formatters we've attached to this writer |
115
|
|
|
foreach($this->formatters as $formatter) { |
116
|
|
|
list($timestamp, $logLevel, $message, $indentLevel, $duration) = $formatter->format($timestamp, $logLevel, $indentLevel, $message, $duration); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
// convert the indent levels to a visual representation |
120
|
|
|
$indentation = $indentLevel ? str_repeat(' ', $indentLevel).'- ' : ''; |
121
|
|
|
|
122
|
|
|
// build the log entry we'll write to the file |
123
|
|
|
$logEntry = $timestamp . ' ' . strtoupper($logLevel) . ' ' . $indentation . ' ' .$message; |
124
|
|
|
if ($this->showDurations) { |
125
|
|
|
$logEntry .= ' '.$duration; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
// write the message |
129
|
|
|
fwrite($this->stream, trim($logEntry).PHP_EOL); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Guess whether or not to format the output with colors for a provided stream. |
134
|
|
|
* |
135
|
|
|
* Windows machines and some terminals do not support colors, so perform a best-guess effort to determine if it |
136
|
|
|
* the current environment does. |
137
|
|
|
* |
138
|
|
|
* @param Resource $stream An open php://**** stream |
139
|
|
|
* |
140
|
|
|
* @return bool Returns **true** if the output can be formatter or **false** otherwise. |
141
|
|
|
* |
142
|
|
|
* @see Garden\Cli::guessFormatOutput |
143
|
|
|
*/ |
144
|
|
|
private function doesStreamSupportColors($stream) { |
145
|
|
|
if (defined('PHP_WINDOWS_VERSION_MAJOR')) { |
146
|
|
|
return false; |
147
|
|
|
} elseif (function_exists('posix_isatty')) { |
148
|
|
|
return posix_isatty($stream); |
149
|
|
|
} else { |
150
|
|
|
return true; |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.