Completed
Push — master ( a826f8...312899 )
by Yuichi
25s
created

FinishMiddleware::domError()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5.0342

Importance

Changes 0
Metric Value
dl 0
loc 24
ccs 16
cts 18
cp 0.8889
rs 9.2248
c 0
b 0
f 0
cc 5
nc 7
nop 2
crap 5.0342
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
            );
34 3
        };
35
    }
36
37
    /**
38
     * @param RequestInterface $request
39
     * @return \Closure
40
     * @throws \InvalidArgumentException
41
     */
42 58
    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 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 58
    private function onRejected(RequestInterface $request): callable
58
    {
59
        return function ($reason) use ($request) {
60 3
            if (!($reason instanceof RequestException)) {
61
                return $reason;
62
            }
63 3
            $response = $reason->getResponse();
64 3
            if ($response === null || $response->getStatusCode() < 300) {
65
                return $reason;
66
            }
67 3
            if (self::isJsonResponse($response)) {
68 2
                self::jsonError($request, $response);
69
            } else {
70 1
                self::domError($request, $response);
71
            }
72
73
            return $reason;
74 58
        };
75
    }
76
77
    /**
78
     * @param ResponseInterface $response
79
     * @return bool
80
     */
81 58
    private static function isJsonResponse(ResponseInterface $response): bool
82
    {
83 58
        $contentType = $response->getHeader('Content-Type');
84 58
        $contentType = is_array($contentType) && isset($contentType[0]) ? $contentType[0] : $contentType;
85
86 58
        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 1
    private static function domError(RequestInterface $request, ResponseInterface $response): void
96
    {
97 1
        $body = (string)$response->getBody()->getContents();
98 1
        $dom = new \DOMDocument('1.0', 'UTF-8');
99 1
        $dom->preserveWhiteSpace = false;
100 1
        $dom->formatOutput = true;
101 1
        $dom->loadHTML($body);
102 1
        if ($dom->loadHTML($body)) {
103 1
            $title = $dom->getElementsByTagName('title');
104 1
            if (is_object($title)) {
105 1
                $title = $title->item(0)->nodeValue;
106
            }
107 1
            if ($title === 'Error') {
108 1
                $message = $dom->getElementsByTagName('h3')->item(0)->nodeValue;
109 1
                throw self::createException($message, $request, $response);
110
            }
111 1
            if ($title === 'Unauthorized') {
112 1
                $message = $dom->getElementsByTagName('h2')->item(0)->nodeValue;
113 1
                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 2
    private static function jsonError(RequestInterface $request, ResponseInterface $response): void
126
    {
127
        try {
128 2
            $body = (string)$response->getBody();
129 2
            $json = \GuzzleHttp\json_decode($body, true);
130
        } catch (\InvalidArgumentException $e) {
131
            return;
132
        } catch (\RuntimeException $e) {
133
            return;
134
        }
135
136 2
        $message = $json['message'];
137 2
        if (isset($json['errors']) && is_array($json['errors'])) {
138
            $message .= self::addErrorMessages($json['errors']);
139
        }
140
141 2
        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 3
    private static function createException($message, RequestInterface $request, ResponseInterface $response): RequestException
173
    {
174 3
        $level = (int) floor($response->getStatusCode() / 100);
175 3
        $className = RequestException::class;
176
177 3
        if ($level === 4) {
178 1
            $className = ClientException::class;
179 3
        } elseif ($level === 5) {
180 3
            $className = ServerException::class;
181
        }
182
183 3
        return new $className($message, $request, $response);
184
    }
185
}
186