Completed
Push — response_middleware ( 918c24 )
by Yuichi
01:48
created

FinishMiddleware::onRejected()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 8
nc 1
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)
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 false;
49
        };
50
    }
51
52
    /**
53
     * @param RequestInterface $request
54
     * @return \Closure
55
     * @throws RequestException
56
     */
57
    private function onRejected(RequestInterface $request)
58
    {
59
        return function ($reason) use ($request) {
60
            if ($reason instanceof RequestException) {
61
                $response = $reason->getResponse();
62
                if ($response !== null && $response->getStatusCode() >= 300) {
63
                    if (self::isJsonResponse($response)) {
64
                        self::jsonError($request, $response);
65
                    }
66
                    self::domError($request, $response);
67
                }
68
            }
69
        };
70
    }
71
72
    /**
73
     * @param ResponseInterface $response
74
     * @return bool
75
     */
76
    private static function isJsonResponse(ResponseInterface $response)
77
    {
78
        $contentType = $response->getHeader('Content-Type');
79
        $contentType = is_array($contentType) && isset($contentType[0]) ? $contentType[0] : $contentType;
80
81
        return is_string($contentType) && $contentType === 'application/json';
82
    }
83
84
85
    /**
86
     * @param RequestInterface $request
87
     * @param ResponseInterface $response
88
     * @throws RequestException
89
     */
90
    private static function domError(RequestInterface $request, ResponseInterface $response)
91
    {
92
        $body = (string)$response->getBody();
93
        $dom = new \DOMDocument('1.0', 'UTF-8');
94
        $dom->preserveWhiteSpace = false;
95
        $dom->formatOutput = true;
96
        $dom->loadHTML($body);
97
        if ($dom->loadHTML($body)) {
98
            $title = $dom->getElementsByTagName('title');
99
            if (is_object($title)) {
100
                $title = $title->item(0)->nodeValue;
101
            }
102
            if ($title === 'Error') {
103
                $message = $dom->getElementsByTagName('h3')->item(0)->nodeValue;
104
                throw self::createException($message, $request, $response);
105
            }
106
            if ($title === 'Unauthorized') {
107
                $message = $dom->getElementsByTagName('h2')->item(0)->nodeValue;
108
                throw self::createException($message, $request, $response);
109
            }
110
111
            throw self::createException('Invalid auth.', $request, $response);
112
        }
113
    }
114
115
    /**
116
     * @param RequestInterface $request
117
     * @param ResponseInterface $response
118
     * @throws RequestException
119
     */
120
    private static function jsonError(RequestInterface $request, ResponseInterface $response)
121
    {
122
        try {
123
            $body = (string)$response->getBody();
124
            $json = \GuzzleHttp\json_decode($body, true);
125
            if (json_last_error() !== JSON_ERROR_NONE) {
126
                throw new \RuntimeException(
127
                    'Error trying to decode response: ' .
128
                    json_last_error_msg()
129
                );
130
            }
131
        } catch (\RuntimeException $e) {
132
            return;
133
        }
134
135
        $message = $json['message'];
136
        if (isset($json['errors']) && is_array($json['errors'])) {
137
            $message .= self::addErrorMessages($json['errors']);
138
        }
139
140
        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
    private static function createException($message, RequestInterface $request, ResponseInterface $response)
172
    {
173
        $level = (int) floor($response->getStatusCode() / 100);
174
        $className = RequestException::class;
175
176
        if ($level === 4) {
177
            $className = ClientException::class;
178
        } elseif ($level === 5) {
179
            $className = ServerException::class;
180
        }
181
182
        return new $className($message, $request, $response);
183
    }
184
}