Completed
Pull Request — master (#32)
by Yuichi
08:17 queued 06:31
created

FinishMiddleware::isJsonResponse()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 4
nc 8
nop 1
crap 4
1
<?php
2
3
namespace CybozuHttp\Middleware;
4
5
use GuzzleHttp\Exception\ClientException;
6
use GuzzleHttp\Exception\RequestException;
7
use GuzzleHttp\Exception\ServerException;
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
11
/**
12
 * @author ochi51 <[email protected]>
13
 */
14
class FinishMiddleware
15
{
16
17
    /**
18
     * Called when the middleware is handled by the client.
19
     *
20
     * @param callable $handler
21
     *
22
     * @return \Closure
23
     * @throws RequestException
24
     * @throws \InvalidArgumentException
25
     */
26 58
    public function __invoke(callable $handler)
27
    {
28
        return function ($request, array $options) use ($handler) {
29
30 58
            return $handler($request, $options)->then(
31 58
                $this->onFulfilled($request),
32 58
                $this->onRejected($request)
33 58
            );
34 3
        };
35
    }
36
37
    /**
38
     * @param RequestInterface $request
39
     * @return \Closure
40
     * @throws \InvalidArgumentException
41
     */
42 58
    private function onFulfilled(RequestInterface $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
43
    {
44
        return function (ResponseInterface $response) {
45 58
            if (self::isJsonResponse($response)) {
46 55
                return $response->withBody(new JsonStream($response->getBody()));
47
            }
48 14
            return $response;
49 58
        };
50
    }
51
52
    /**
53
     * @param RequestInterface $request
54
     * @return \Closure
55
     * @throws RequestException
56
     */
57
    private function onRejected(RequestInterface $request)
58
    {
59 58
        return function ($reason) use ($request) {
60 5
            if (!($reason instanceof RequestException)) {
61
                return $reason;
62
            }
63 5
            $response = $reason->getResponse();
64 5
            if ($response === null || $response->getStatusCode() < 300) {
65
                return $reason;
66
            }
67 5
            if (self::isJsonResponse($response)) {
68 4
                self::jsonError($request, $response);
69
            }
70 1
            self::domError($request, $response);
71
72
            return $reason;
73 58
        };
74
    }
75
76
    /**
77
     * @param ResponseInterface $response
78
     * @return bool
79
     */
80 58
    private static function isJsonResponse(ResponseInterface $response)
81
    {
82 58
        $contentType = $response->getHeader('Content-Type');
83 58
        $contentType = is_array($contentType) && isset($contentType[0]) ? $contentType[0] : $contentType;
84
85 58
        return is_string($contentType) && strpos($contentType, 'application/json') === 0;
86
    }
87
88
89
    /**
90
     * @param RequestInterface $request
91
     * @param ResponseInterface $response
92
     * @throws RequestException
93
     */
94 1
    private static function domError(RequestInterface $request, ResponseInterface $response)
95
    {
96 1
        $body = (string)$response->getBody();
97 1
        $dom = new \DOMDocument('1.0', 'UTF-8');
98 1
        $dom->preserveWhiteSpace = false;
99 1
        $dom->formatOutput = true;
100 1
        $dom->loadHTML($body);
101 1
        if ($dom->loadHTML($body)) {
102 1
            $title = $dom->getElementsByTagName('title');
103 1
            if (is_object($title)) {
104 1
                $title = $title->item(0)->nodeValue;
105 1
            }
106 1
            if ($title === 'Error') {
107 1
                $message = $dom->getElementsByTagName('h3')->item(0)->nodeValue;
108 1
                throw self::createException($message, $request, $response);
109
            }
110 1
            if ($title === 'Unauthorized') {
111 1
                $message = $dom->getElementsByTagName('h2')->item(0)->nodeValue;
112 1
                throw self::createException($message, $request, $response);
113
            }
114
115
            throw self::createException('Invalid auth.', $request, $response);
116
        }
117
    }
118
119
    /**
120
     * @param RequestInterface $request
121
     * @param ResponseInterface $response
122
     * @throws RequestException
123
     */
124 4
    private static function jsonError(RequestInterface $request, ResponseInterface $response)
125
    {
126
        try {
127 4
            $body = (string)$response->getBody();
128 4
            $json = \GuzzleHttp\json_decode($body, true);
129 4
        } catch (\InvalidArgumentException $e) {
130
            return;
131
        } catch (\RuntimeException $e) {
132
            return;
133
        }
134
135 4
        $message = $json['message'];
136 4
        if (isset($json['errors']) && is_array($json['errors'])) {
137
            $message .= self::addErrorMessages($json['errors']);
138
        }
139
140 4
        throw self::createException($message, $request, $response);
141
    }
142
143
    /**
144
     * @param array $errors
145
     * @return string
146
     */
147
    private static function addErrorMessages(array $errors)
148
    {
149
        $message = ' (';
150
        foreach ($errors as $k => $err) {
151
            $message .= $k . ' : ';
152
            if (is_array($err['messages'])) {
153
                foreach ($err['messages'] as $m) {
154
                    $message .= $m . ' ';
155
                }
156
            } else {
157
                $message .= $err['messages'];
158
            }
159
        }
160
        $message .= ' )';
161
162
        return $message;
163
    }
164
165
    /**
166
     * @param string $message
167
     * @param RequestInterface $request
168
     * @param ResponseInterface|null $response
169
     * @return RequestException
170
     */
171 5
    private static function createException($message, RequestInterface $request, ResponseInterface $response)
172
    {
173 5
        $level = (int) floor($response->getStatusCode() / 100);
174 5
        $className = RequestException::class;
175
176 5
        if ($level === 4) {
177 1
            $className = ClientException::class;
178 5
        } elseif ($level === 5) {
179 5
            $className = ServerException::class;
180 5
        }
181
182 5
        return new $className($message, $request, $response);
183
    }
184
}