Completed
Push — 0.1 ( 1296ef...144915 )
by Yuichi
19:51 queued 09:46
created

FinishMiddleware::isJsonResponse()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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