Completed
Pull Request — master (#24)
by
unknown
06:24 queued 03:07
created

IoStreamWriter::write()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 25
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 9
nop 5
dl 0
loc 25
rs 8.439
c 0
b 0
f 0
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');
0 ignored issues
show
Documentation Bug introduced by
It seems like fopen($stream, 'w') can also be of type false. However, the property $stream is declared as type resource. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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