Test Failed
Push — master ( 772b7d...4063e2 )
by Rudi
02:12 queued 11s
created

Logger::onSuccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Concat\Http\Middleware;
4
5
use GuzzleHttp\Exception\RequestException;
6
use GuzzleHttp\MessageFormatter;
7
use GuzzleHttp\Promise;
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Log\LogLevel;
11
use Psr\Log\LoggerInterface;
12
13
use InvalidArgumentException;
14
15
/**
16
 * Guzzle middleware which logs a request and its response.
17
 */
18
class Logger
19
{
20
    /**
21
     * @var \Psr\Log\LoggerInterface|callable
22
     */
23
    protected $logger;
24
25
    /**
26
     * @var \GuzzleHttp\MessageFormatter|callable
27
     */
28
    protected $formatter;
29
30
    /**
31
     * @var string|callable Constant or callable that accepts a Response.
32
     */
33
    protected $logLevel;
34
35
    /**
36
     * @var boolean Whether or not to log requests as they are made.
37
     */
38
    protected $logRequests;
39
40
    /**
41
     * Creates a callable middleware for logging requests and responses.
42
     *
43
     * @param LoggerInterface|callable $logger
44
     * @param string|callable Constant or callable that accepts a Response.
45
     */
46 12
    public function __construct($logger, $formatter = null)
47
    {
48
        // Use the setters to take care of type validation
49 12
        $this->setLogger($logger);
50 11
        $this->setFormatter($formatter ?: $this->getDefaultFormatter());
51 11
    }
52
53
    /**
54
     * Returns the default formatter;
55
     *
56
     * @return MessageFormatter
57
     */
58 10
    protected function getDefaultFormatter()
59
    {
60 10
        return new MessageFormatter();
61
    }
62
63
    /**
64
     * Sets whether requests should be logged before the response is received.
65
     *
66
     * @param boolean $logRequests
67
     */
68 2
    public function setRequestLoggingEnabled($logRequests = true)
69
    {
70 2
        $this->logRequests = (bool) $logRequests;
71 2
    }
72
73
    /**
74
     * Sets the logger, which can be a PSR-3 logger or a callable that accepts
75
     * a log level, message, and array context.
76
     *
77
     * @param LoggerInterface|callable $logger
78
     *
79
     * @throws InvalidArgumentException
80
     */
81 12
    public function setLogger($logger)
82
    {
83 12
        if ($logger instanceof LoggerInterface || is_callable($logger)) {
84 11
            $this->logger = $logger;
0 ignored issues
show
Documentation Bug introduced by
It seems like $logger can also be of type object<Psr\Log\LoggerInterface>. However, the property $logger is declared as type callable. 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...
85
        } else {
86 1
            throw new InvalidArgumentException(
87 1
                "Logger has to be a Psr\Log\LoggerInterface or callable"
88
            );
89
        }
90 11
    }
91
92
    /**
93
     * Sets the formatter, which can be a MessageFormatter or callable that
94
     * accepts a request, response, and a reason if an error has occurred.
95
     *
96
     * @param MessageFormatter|callable $formatter
97
     *
98
     * @throws InvalidArgumentException
99
     */
100 11
    public function setFormatter($formatter)
101
    {
102 11
        if ($formatter instanceof MessageFormatter || is_callable($formatter)) {
103 11
            $this->formatter = $formatter;
104
        } else {
105 1
            throw new InvalidArgumentException(
106 1
                "Formatter has to be a \GuzzleHttp\MessageFormatter or callable"
107
            );
108
        }
109 11
    }
110
111
   /**
112
     * Sets the log level to use, which can be either a string or a callable
113
     * that accepts a response (which could be null). A log level could also
114
     * be null, which indicates that the default log level should be used.
115
     *
116
     * @param string|callable|null
117
     */
118 2
    public function setLogLevel($logLevel)
119
    {
120 2
        $this->logLevel = $logLevel;
121 2
    }
122
123
    /**
124
     * Logs a request and/or a response.
125
     *
126
     * @param RequestInterface $request
127
     * @param ResponseInterface|null $response
128
     * @param mixed $reason
129
     */
130
    protected function log(
131
        RequestInterface $request,
132
        ResponseInterface $response = null,
133
        $reason = null
134
    ) {
135
        if ($reason instanceof RequestException) {
136
            $response = $reason->getResponse();
137
        }
138
139
        $level   = $this->getLogLevel($response);
140
        $message = $this->getLogMessage($request, $response, $reason);
141
        $context = compact('request', 'response', 'reason');
142
143
        // Make sure that the content of the body is available again.
144
        if ($response) {
145
            $response->getBody()->seek(0);;
146
        }
147
148
        if (is_callable($this->logger)) {
149
            return call_user_func($this->logger, $level, $message, $context);
150
        }
151
152
        return $this->logger->log($level, $message, $context);
153
    }
154
155
    /**
156
     * Formats a request and response as a log message.
157
     *
158
     * @param RequestInterface $request
159
     * @param ResponseInterface|null $response
160
     * @param mixed $reason
161
     *
162
     * @return string The formatted message.
163
     */
164
    protected function getLogMessage(
165
        RequestInterface $request,
166
        ResponseInterface $response = null,
167
        $reason = null
168
    ) {
169
        if ($this->formatter instanceof MessageFormatter) {
170
            return $this->formatter->format(
171
                $request,
172
                $response,
173
                $reason
174
            );
175
        }
176
177
        return call_user_func($this->formatter, $request, $response, $reason);
178
    }
179
180
    /**
181
     * Returns a log level for a given response.
182
     *
183
     * @param ResponseInterface $response The response being logged.
184
     *
185
     * @return string LogLevel
186
     */
187
    protected function getLogLevel(ResponseInterface $response = null)
188
    {
189
        if ( ! $this->logLevel) {
190
            return $this->getDefaultLogLevel($response);
191
        }
192
193
        if (is_callable($this->logLevel)) {
194
            return call_user_func($this->logLevel, $response);
195
        }
196
197
        return (string) $this->logLevel;
198
    }
199
200
    /**
201
     * Returns the default log level for a response.
202
     *
203
     * @param ResponseInterface $response
204
     *
205
     * @return string LogLevel
206
     */
207
    protected function getDefaultLogLevel(ResponseInterface $response = null) {
208
        if ($response && $response->getStatusCode() >= 300) {
209
            return LogLevel::NOTICE;
210
        }
211
212
        return LogLevel::INFO;
213
    }
214
215
    /**
216
     * Returns a function which is handled when a request was successful.
217
     *
218
     * @param RequestInterface $request
219
     *
220
     * @return Closure
221
     */
222
    protected function onSuccess(RequestInterface $request)
223
    {
224
        return function ($response) use ($request) {
225
            $this->log($request, $response);
226
            return $response;
227
        };
228
    }
229
230
    /**
231
     * Returns a function which is handled when a request was rejected.
232
     *
233
     * @param RequestInterface $request
234
     *
235
     * @return Closure
236
     */
237
    protected function onFailure(RequestInterface $request)
238
    {
239
        return function ($reason) use ($request) {
240
241
            // Only log a rejected request if it hasn't already been logged.
242
            if ( ! $this->logRequests) {
243
                $this->log($request, null, $reason);
244
            }
245
246
            return Promise\rejection_for($reason);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Promise\rejection_for() has been deprecated with message: rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
247
        };
248
    }
249
250
    /**
251
     * Called when the middleware is handled by the client.
252
     *
253
     * @param callable $handler
254
     *
255
     * @return Closure
256
     */
257
    public function __invoke(callable $handler)
258
    {
259
        return function ($request, array $options) use ($handler) {
260
261
            // Only log requests if explicitly set to do so
262
            if ($this->logRequests) {
263
                $this->log($request);
264
            }
265
266
            return $handler($request, $options)->then(
267
                $this->onSuccess($request),
268
                $this->onFailure($request)
269
            );
270
        };
271
    }
272
}
273