Passed
Push — master ( d4f0dd...ebfa4f )
by Robin
05:37
created

Response::existenceCheck()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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