Test Failed
Push — master ( 202f47...6a15b7 )
by Rudi
03:20
created

Logger   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 96%

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 4
dl 0
loc 255
ccs 72
cts 75
cp 0.96
rs 10
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A getDefaultFormatter() 0 4 1
A setRequestLoggingEnabled() 0 4 1
A setLogger() 0 10 3
A setFormatter() 0 10 3
A setLogLevel() 0 4 1
A log() 0 24 4
A getLogMessage() 0 15 2
A getLogLevel() 0 12 3
A getDefaultLogLevel() 0 7 3
A onSuccess() 0 7 1
A onFailure() 0 12 2
A __invoke() 0 15 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 11
        } else {
86 1
            throw new InvalidArgumentException(
87
                "Logger has to be a Psr\Log\LoggerInterface or callable"
88 1
            );
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 11
        } else {
105 1
            throw new InvalidArgumentException(
106
                "Formatter has to be a \GuzzleHttp\MessageFormatter or callable"
107 1
            );
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 10
    protected function log(
131
        RequestInterface $request,
132
        ResponseInterface $response = null,
133
        $reason = null
134
    ) {
135 10
        if ($reason instanceof RequestException) {
136 1
            $response = $reason->getResponse();
137 1
        }
138
139 10
        $level   = $this->getLogLevel($response);
140 10
        $message = $this->getLogMessage($request, $response, $reason);
141 10
        $context = compact('request', 'response', 'reason');
142
143
        // Make sure that the content of the body is available again.
144 10
        if ($response) {
145 8
            $response->getBody()->seek(0);;
146
        }
147
148 3
        if (is_callable($this->logger)) {
149
            return call_user_func($this->logger, $level, $message, $context);
150
        }
151
152 3
        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 10
    protected function getLogMessage(
165
        RequestInterface $request,
166
        ResponseInterface $response = null,
167
        $reason = null
168
    ) {
169 10
        if ($this->formatter instanceof MessageFormatter) {
170 9
            return $this->formatter->format(
171 9
                $request,
172 9
                $response,
173
                $reason
174 9
            );
175
        }
176
177 1
        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 10
    protected function getLogLevel(ResponseInterface $response = null)
188
    {
189 10
        if ( ! $this->logLevel) {
190 8
            return $this->getDefaultLogLevel($response);
191
        }
192
193 2
        if (is_callable($this->logLevel)) {
194 1
            return call_user_func($this->logLevel, $response);
195
        }
196
197 1
        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 8
    protected function getDefaultLogLevel(ResponseInterface $response = null) {
208 8
        if ($response && $response->getStatusCode() >= 300) {
209 1
            return LogLevel::NOTICE;
210
        }
211
212 7
        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 10
    protected function onSuccess(RequestInterface $request)
223
    {
224
        return function ($response) use ($request) {
225 8
            $this->log($request, $response);
226
            return $response;
227 10
        };
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 10
    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 2
            if ( ! $this->logRequests) {
243 1
                $this->log($request, null, $reason);
244 1
            }
245
246 2
            return Promise\rejection_for($reason);
247 10
        };
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 10
        return function ($request, array $options) use ($handler) {
260
261
            // Only log requests if explicitly set to do so
262 10
            if ($this->logRequests) {
263 2
                $this->log($request);
264 2
            }
265
266 10
            return $handler($request, $options)->then(
267 10
                $this->onSuccess($request),
268 10
                $this->onFailure($request)
269 10
            );
270 10
        };
271
    }
272
}
273