Test Failed
Push — master ( 6b4bb5...ecc1a0 )
by Robin
10:25
created

Response::formatErrors()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
ccs 10
cts 10
cp 1
rs 6.9811
cc 7
eloc 11
nc 9
nop 1
crap 7
1
<?php
2
3
namespace Klever\JustGivingApiSdk\Support;
4
5
use Klever\JustGivingApiSdk\Exceptions\UnexpectedStatusException;
6
use Psr\Http\Message\ResponseInterface;
7
use Psr\Http\Message\StreamInterface;
8
9
/**
10
 * @property mixed body
11
 * @property array errors
12
 */
13
class Response implements ResponseInterface
14
{
15
    /**
16
     * The original response object.
17
     *
18
     * @var ResponseInterface
19
     */
20
    protected $response;
21
22
    /**
23
     * The attributes returned from the request.
24
     *
25
     * @var array
26
     */
27
    protected $attributes = [];
28
29
    /**
30
     * Store the response object.
31
     *
32
     * @param ResponseInterface $response
33
     */
34 76
    public function __construct(ResponseInterface $response)
35
    {
36 76
        $this->response = $response;
37
38 76
        $this->updateAttributesArray();
39 76
    }
40
41
    /**
42
     * Allow attributes to be retrieved as if properties on the class.
43
     *
44
     * @param string $name
45
     * @return mixed
46
     */
47 70
    public function __get($name)
48
    {
49
        switch ($name) {
50 70
            case 'body':
51 64
                return $this->getBodyAsObject();
52 14
            case 'errors':
53 5
                return $this->formatErrors($this->body);
54
        }
55
56 9
        return $this->getAttribute($name);
57
    }
58
59
    /**
60
     * Format errors in a unified object format, regardless of whether the supplied errors are a list of objects or a
61
     * single error message string.
62
     *
63
     * @param object $errorBody
64
     * @return array
65
     */
66 5
    protected function formatErrors($errorBody)
67
    {
68 5
        if ($this->wasSuccessful()) {
69 3
            return [];
70
        }
71
72 3
        $errors['ReasonPhrase'] = $this->getReasonPhrase();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$errors was never initialized. Although not strictly required by PHP, it is generally a good practice to add $errors = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
73
74 3
        if (isset($errorBody->errorMessage)) {
75 1
            $errors += ['General' => $errorBody->errorMessage];
76
        }
77
78 2
        if (isset($errorBody->error)) {
79 2
            $errors += $this->errorsToArray($errorBody->error);
80 2
        }
81
82
        if (is_array($errorBody) && isset($errorBody[0]->id) && isset($errorBody[0]->desc)) {
83
            $errors += $this->errorsToArray($errorBody);
84 2
        }
85
86
        return $errors;
87
    }
88
89
    /**
90
     * Take in an array of errors with separate ID and description, and format them as an associative array of [$id =>
91
     * $description].
92 1
     *
93
     * @param array $inputErrors
94 1
     * @return array
95
     */
96
    protected function errorsToArray($inputErrors)
97
    {
98
        $errors = [];
99
        foreach ($inputErrors as $error) {
100
            $errors[$error->id ?? null] = $error->desc ?? null;
101
        }
102
103 10
        return $errors;
104
    }
105 10
106
    /**
107
     * Check if the response contains any error messages.
108
     *
109
     * @return bool
110
     */
111
    public function hasErrorMessages()
112
    {
113 3
        return ! empty((array) $this->errors);
114
    }
115 3
116
    /**
117
     * Get the specified attribute.
118
     *
119
     * @param string $name
120
     * @return mixed
121
     */
122
    public function getAttribute($name)
123 78
    {
124
        return $this->attributes[$name];
125 78
    }
126
127
    /**
128
     * Get all attributes.
129
     *
130
     * @return array
131
     */
132
    public function getAttributes()
133
    {
134 31
        return $this->attributes;
135
    }
136 31
137 31
    /**
138
     * Decode the JSON body.
139
     *
140
     * @return mixed
141
     */
142
    public function getBodyAsObject()
143
    {
144
        return json_decode($this->response->getBody()->__toString());
145
    }
146 6
147
    /**
148 6
     * If there a 'success' variable contained in the response body, return that. If not, return true if the response
149 1
     * has a 2xx status code.
150
     *
151
     * @return bool
152 5
     */
153
    public function wasSuccessful()
154
    {
155
        return $this->body->success
156
            ?? $this->getStatusCode() >= 200 && $this->getStatusCode() < 300;
157
    }
158 76
159
    /**
160 76
     * Check if the requested resource exists. Throw an exception if the status is not 200 or 404.
161 60
     *
162 22
     * @return bool
163 76
     * @throws UnexpectedStatusException
164
     */
165
    public function existenceCheck()
166
    {
167
        if ( ! in_array($this->getStatusCode(), [200, 404])) {
168
            throw new UnexpectedStatusException($this);
169
        }
170
171
        return $this->getStatusCode() == 200;
172
    }
173
174
    /**
175
     * Update the attributes array from the decoded JSON response.
176
     */
177
    protected function updateAttributesArray()
178
    {
179
        $this->attributes = is_object($this->getBodyAsObject())
180
            ? get_object_vars($this->getBodyAsObject())
181
            : $this->getBodyAsObject();
182
    }
183
184
    // Parent Response class should have its own tests for the remaining methods
185
    // @codeCoverageIgnoreStart
186
187
    /**
188
     * Defer all unknown methods to main response class.
189
     *
190
     * @param $method
191
     * @param $args
192
     * @return mixed
193
     */
194
    public function __call($method, $args)
195
    {
196
        return $this->response->$method(...$args);
197
    }
198
199
    /**
200
     * Retrieves the HTTP protocol version as a string.
201
     *
202
     * @return string HTTP protocol version.
203
     */
204
    public function getProtocolVersion()
205
    {
206
        return $this->response->getProtocolVersion();
207
    }
208
209
    /**
210
     * Return an instance with the specified HTTP protocol version.
211
     *
212
     * @param string $version HTTP protocol version
213
     * @return ResponseInterface
214
     */
215
    public function withProtocolVersion($version)
216
    {
217
        return $this->response->withProtocolVersion($version);
218
    }
219
220
    /**
221
     * Retrieves all message header values.
222
     *
223
     * @return string[][] Returns an associative array of the message's headers. Each
224
     *     key MUST be a header name, and each value MUST be an array of strings
225
     *     for that header.
226
     */
227
    public function getHeaders()
228
    {
229
        return $this->response->getHeaders();
230
    }
231
232
    /**
233
     * Checks if a header exists by the given case-insensitive name.
234
     *
235
     * @param string $name Case-insensitive header field name.
236
     * @return bool Returns true if any header names match the given header
237
     *                     name using a case-insensitive string comparison. Returns false if
238
     *                     no matching header name is found in the message.
239
     */
240
    public function hasHeader($name)
241
    {
242
        return $this->response->hasHeader($name);
243
    }
244
245
    /**
246
     * Retrieves a message header value by the given case-insensitive name.
247
     *
248
     * @param string $name Case-insensitive header field name.
249
     * @return string[] An array of string values as provided for the given
250
     *                     header. If the header does not appear in the message, this method MUST
251
     *                     return an empty array.
252
     */
253
    public function getHeader($name)
254
    {
255
        return $this->response->getHeader($name);
256
    }
257
258
    /**
259
     * Retrieves a comma-separated string of the values for a single header.
260
     *
261
     * @param string $name Case-insensitive header field name.
262
     * @return string A string of values as provided for the given header
263
     *                     concatenated together using a comma. If the header does not appear in
264
     *                     the message, this method MUST return an empty string.
265
     */
266
    public function getHeaderLine($name)
267
    {
268
        return $this->response->getHeaderLine($name);
269
    }
270
271
    /**
272
     * Return an instance with the provided value replacing the specified header.
273
     *
274
     * @param string          $name  Case-insensitive header field name.
275
     * @param string|string[] $value Header value(s).
276
     * @return ResponseInterface
277
     * @throws \InvalidArgumentException for invalid header names or values.
278
     */
279
    public function withHeader($name, $value)
280
    {
281
        return $this->response->withHeader($name, $value);
282
    }
283
284
    /**
285
     * Return an instance with the specified header appended with the given value.
286
     *
287
     * @param string          $name  Case-insensitive header field name to add.
288
     * @param string|string[] $value Header value(s).
289
     * @return ResponseInterface
290
     * @throws \InvalidArgumentException for invalid header names or values.
291
     */
292
    public function withAddedHeader($name, $value)
293
    {
294
        return $this->response->withAddedHeader($name, $value);
295
    }
296
297
    /**
298
     * Return an instance without the specified header.
299
     *
300
     * @param string $name Case-insensitive header field name to remove.
301
     * @return ResponseInterface
302
     */
303
    public function withoutHeader($name)
304
    {
305
        return $this->response->withoutHeader($name);
306
    }
307
308
    /**
309
     * Gets the body of the message.
310
     *
311
     * @return StreamInterface Returns the body as a stream.
312
     */
313
    public function getBody()
314
    {
315
        return $this->response->getBody();
316
    }
317
318
    /**
319
     * Return an instance with the specified message body.
320
     *
321
     * @param StreamInterface $body Body.
322
     * @return ResponseInterface
323
     * @throws \InvalidArgumentException When the body is not valid.
324
     */
325
    public function withBody(StreamInterface $body)
326
    {
327
        return $this->response->withBody($body);
328
    }
329
330
    /**
331
     * Gets the response status code.
332
     *
333
     * @return int Status code.
334
     */
335
    public function getStatusCode()
336
    {
337
        return $this->response->getStatusCode();
338
    }
339
340
    /**
341
     * Return an instance with the specified status code and, optionally, reason phrase.
342
     *
343
     * @param int    $code         The 3-digit integer result code to set.
344
     * @param string $reasonPhrase The reason phrase to use with the
345
     *                             provided status code; if none is provided, implementations MAY
346
     *                             use the defaults as suggested in the HTTP specification.
347
     * @return ResponseInterface * @throws \InvalidArgumentException For invalid status code arguments.
348
     */
349
    public function withStatus($code, $reasonPhrase = '')
350
    {
351
        return $this->response->withStatus($code, $reasonPhrase);
352
    }
353
354
    /**
355
     * Gets the response reason phrase associated with the status code.
356
     *
357
     * @return string Reason phrase; must return an empty string if none present.
358
     */
359
    public function getReasonPhrase()
360
    {
361
        return $this->response->getReasonPhrase();
362
    }
363
364
    // @codeCoverageIgnoreEnd
365
}
366