Completed
Push — master ( c7d4dc...13d232 )
by Carlos
32:44 queued 24:34
created

Http   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 92.19%

Importance

Changes 9
Bugs 2 Features 0
Metric Value
wmc 21
c 9
b 2
f 0
lcom 1
cbo 4
dl 0
loc 237
ccs 59
cts 64
cp 0.9219
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A get() 0 4 1
A post() 0 6 2
A json() 0 6 2
A upload() 0 17 3
A setClient() 0 6 1
A getClient() 0 8 2
A addMiddleware() 0 6 1
A getMiddlewares() 0 4 1
A request() 0 19 1
B parseJSON() 0 23 4
A fuckTheWeChatInvalidJSON() 0 4 1
A getHandler() 0 10 2
1
<?php
2
3
/*
4
 * This file is part of the overtrue/wechat.
5
 *
6
 * (c) overtrue <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
/**
13
 * Http.php.
14
 *
15
 * This file is part of the wechat-components.
16
 *
17
 * (c) overtrue <[email protected]>
18
 *
19
 * This source file is subject to the MIT license that is bundled
20
 * with this source code in the file LICENSE.
21
 */
22
namespace EasyWeChat\Core;
23
24
use EasyWeChat\Core\Exceptions\HttpException;
25
use EasyWeChat\Support\Log;
26
use GuzzleHttp\Client as HttpClient;
27
use GuzzleHttp\HandlerStack;
28
use Psr\Http\Message\ResponseInterface;
29
30
/**
31
 * Class Http.
32
 */
33
class Http
34
{
35
    /**
36
     * Http client.
37
     *
38
     * @var HttpClient
39
     */
40
    protected $client;
41
42
    /**
43
     * The middlewares.
44
     *
45
     * @var array
46
     */
47
    protected $middlewares = [];
48
49
    /**
50
     * GET request.
51
     *
52
     * @param string $url
53
     * @param array  $options
54
     *
55
     * @return array|bool
56
     *
57
     * @throws HttpException
58
     */
59 1
    public function get($url, array $options = [])
60
    {
61 1
        return $this->request($url, 'GET', ['query' => $options]);
62
    }
63
64
    /**
65
     * POST request.
66
     *
67
     * @param string       $url
68
     * @param array|string $options
69
     *
70
     * @return array|bool
71
     *
72
     * @throws HttpException
73
     */
74 1
    public function post($url, $options = [])
75
    {
76 1
        $key = is_array($options) ? 'form_params' : 'body';
77
78 1
        return $this->request($url, 'POST', [$key => $options]);
79
    }
80
81
    /**
82
     * JSON request.
83
     *
84
     * @param string       $url
85
     * @param string|array $options
86
     * @param int          $encodeOption
87
     *
88
     * @return array|bool
89
     *
90
     * @throws HttpException
91
     */
92 1
    public function json($url, $options = [], $encodeOption = JSON_UNESCAPED_UNICODE)
93
    {
94 1
        is_array($options) && $options = json_encode($options, $encodeOption);
95
96 1
        return $this->request($url, 'POST', ['body' => $options, 'headers' => ['content-type' => 'application/json']]);
97
    }
98
99
    /**
100
     * Upload file.
101
     *
102
     * @param string $url
103
     * @param array  $files
104
     * @param array  $form
105
     *
106
     * @return array|bool
107
     *
108
     * @throws HttpException
109
     */
110 1
    public function upload($url, array $files = [], array $form = [], array $queries = [])
111
    {
112 1
        $multipart = [];
113
114 1
        foreach ($files as $name => $path) {
115 1
            $multipart[] = [
116 1
                'name' => $name,
117 1
                'contents' => fopen($path, 'r'),
118
            ];
119 1
        }
120
121 1
        foreach ($form as $name => $contents) {
122 1
            $multipart[] = compact('name', 'contents');
123 1
        }
124
125 1
        return $this->request($url, 'POST', ['query' => $queries, 'multipart' => $multipart]);
126
    }
127
128
    /**
129
     * Set GuzzleHttp\Client.
130
     *
131
     * @param \GuzzleHttp\Client $client
132
     *
133
     * @return Http
134
     */
135 6
    public function setClient(HttpClient $client)
136
    {
137 6
        $this->client = $client;
138
139 6
        return $this;
140
    }
141
142
    /**
143
     * Return GuzzleHttp\Client instance.
144
     *
145
     * @return \GuzzleHttp\Client.
146
     */
147 2
    public function getClient()
148
    {
149 2
        if (!($this->client instanceof HttpClient)) {
150
            $this->client = new HttpClient();
151
        }
152
153 2
        return $this->client;
154
    }
155
156
    /**
157
     * Add a middleware.
158
     *
159
     * @param callable $middleware
160
     *
161
     * @return $this
162
     */
163 79
    public function addMiddleware(callable $middleware)
164
    {
165 79
        array_push($this->middlewares, $middleware);
166
167 79
        return $this;
168
    }
169
170
    /**
171
     * Return all middlewares.
172
     *
173
     * @return array
174
     */
175
    public function getMiddlewares()
176
    {
177
        return $this->middlewares;
178
    }
179
180
    /**
181
     * Make a request.
182
     *
183
     * @param string $url
184
     * @param string $method
185
     * @param array  $options
186
     *
187
     * @return array|bool
188
     *
189
     * @throws HttpException
190
     */
191 2
    public function request($url, $method = 'GET', $options = [])
192
    {
193 2
        $method = strtoupper($method);
194
195 2
        Log::debug('Client Request:', compact('url', 'method', 'options'));
196
197 2
        $options['handler'] = $this->getHandler();
198
199 2
        $response = $this->getClient()->request($method, $url, $options);
200
201 2
        Log::debug('API response:', [
202 2
            'Status' => $response->getStatusCode(),
203 2
            'Reason' => $response->getReasonPhrase(),
204 2
            'Headers' => $response->getHeaders(),
205 2
            'Body' => strval($response->getBody()),
206 2
        ]);
207
208 2
        return $response;
209
    }
210
211
    /**
212
     * @param \Psr\Http\Message\ResponseInterface|string $body
213
     *
214
     * @return mixed
215
     *
216
     * @throws \EasyWeChat\Core\Exceptions\HttpException
217
     */
218 9
    public function parseJSON($body)
219
    {
220 9
        if ($body instanceof ResponseInterface) {
221 2
            $body = $body->getBody();
222 2
        }
223
224
        // XXX: json maybe contains special chars. So, let's FUCK the WeChat API developers ...
225 9
        $body = $this->fuckTheWeChatInvalidJSON($body);
226
227 9
        if (empty($body)) {
228 1
            return false;
229
        }
230
231 9
        $contents = json_decode($body, true);
232
233 9
        Log::debug('API response decoded:', compact('contents'));
234
235 9
        if (JSON_ERROR_NONE !== json_last_error()) {
236 1
            throw new HttpException('Failed to parse JSON: '.json_last_error_msg());
237
        }
238
239 9
        return $contents;
240
    }
241
242
    /**
243
     * Filter the invalid JSON string.
244
     *
245
     * @param \Psr\Http\Message\StreamInterface|string $invalidJSON
246
     *
247
     * @return string
248
     */
249 9
    protected function fuckTheWeChatInvalidJSON($invalidJSON)
250
    {
251 9
        return preg_replace("/\p{Cc}/u", '', trim($invalidJSON));
252
    }
253
254
    /**
255
     * Build a handler.
256
     *
257
     * @return HandlerStack
258
     */
259 2
    protected function getHandler()
260
    {
261 2
        $stack = HandlerStack::create();
262
263 2
        foreach ($this->middlewares as $middleware) {
264
            $stack->push($middleware);
265 2
        }
266
267 2
        return $stack;
268
    }
269
}
270