Completed
Push — master ( b6898b...f0c41e )
by Carlos
05:47 queued 02:03
created

Http   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 266
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 98.57%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 23
c 3
b 1
f 0
lcom 1
cbo 4
dl 0
loc 266
ccs 69
cts 70
cp 0.9857
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A setDefaultOptions() 0 4 1
A getDefaultOptions() 0 4 1
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 21 1
A fuckTheWeChatInvalidJSON() 0 4 1
A getHandler() 0 10 2
B parseJSON() 0 23 4
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
     * Guzzle client default settings.
51
     *
52
     * @var array
53
     */
54
    protected static $defaults = [];
55
56
    /**
57
     * Set guzzle default settings.
58
     *
59
     * @param array $defaults
60
     */
61 4
    public static function setDefaultOptions($defaults = [])
62
    {
63 4
        self::$defaults = $defaults;
64 4
    }
65
66
    /**
67
     * Return current guzzle default settings.
68
     *
69
     * @return array
70
     */
71 1
    public static function getDefaultOptions()
72
    {
73 1
        return self::$defaults;
74
    }
75
76
    /**
77
     * GET request.
78
     *
79
     * @param string $url
80
     * @param array  $options
81
     *
82
     * @return array|bool
83
     *
84
     * @throws HttpException
85
     */
86 1
    public function get($url, array $options = [])
87
    {
88 1
        return $this->request($url, 'GET', ['query' => $options]);
89
    }
90
91
    /**
92
     * POST request.
93
     *
94
     * @param string       $url
95
     * @param array|string $options
96
     *
97
     * @return array|bool
98
     *
99
     * @throws HttpException
100
     */
101 1
    public function post($url, $options = [])
102
    {
103 1
        $key = is_array($options) ? 'form_params' : 'body';
104
105 1
        return $this->request($url, 'POST', [$key => $options]);
106
    }
107
108
    /**
109
     * JSON request.
110
     *
111
     * @param string       $url
112
     * @param string|array $options
113
     * @param int          $encodeOption
114
     *
115
     * @return array|bool
116
     *
117
     * @throws HttpException
118
     */
119 1
    public function json($url, $options = [], $encodeOption = JSON_UNESCAPED_UNICODE)
120
    {
121 1
        is_array($options) && $options = json_encode($options, $encodeOption);
122
123 1
        return $this->request($url, 'POST', ['body' => $options, 'headers' => ['content-type' => 'application/json']]);
124
    }
125
126
    /**
127
     * Upload file.
128
     *
129
     * @param string $url
130
     * @param array  $files
131
     * @param array  $form
132
     *
133
     * @return array|bool
134
     *
135
     * @throws HttpException
136
     */
137 1
    public function upload($url, array $files = [], array $form = [], array $queries = [])
138
    {
139 1
        $multipart = [];
140
141 1
        foreach ($files as $name => $path) {
142 1
            $multipart[] = [
143 1
                'name' => $name,
144 1
                'contents' => fopen($path, 'r'),
145
            ];
146 1
        }
147
148 1
        foreach ($form as $name => $contents) {
149 1
            $multipart[] = compact('name', 'contents');
150 1
        }
151
152 1
        return $this->request($url, 'POST', ['query' => $queries, 'multipart' => $multipart]);
153
    }
154
155
    /**
156
     * Set GuzzleHttp\Client.
157
     *
158
     * @param \GuzzleHttp\Client $client
159
     *
160
     * @return Http
161
     */
162 6
    public function setClient(HttpClient $client)
163
    {
164 6
        $this->client = $client;
165
166 6
        return $this;
167
    }
168
169
    /**
170
     * Return GuzzleHttp\Client instance.
171
     *
172
     * @return \GuzzleHttp\Client.
173
     */
174 3
    public function getClient()
175
    {
176 3
        if (!($this->client instanceof HttpClient)) {
177 1
            $this->client = new HttpClient();
178 1
        }
179
180 3
        return $this->client;
181
    }
182
183
    /**
184
     * Add a middleware.
185
     *
186
     * @param callable $middleware
187
     *
188
     * @return $this
189
     */
190 7
    public function addMiddleware(callable $middleware)
191
    {
192 7
        array_push($this->middlewares, $middleware);
193
194 7
        return $this;
195
    }
196
197
    /**
198
     * Return all middlewares.
199
     *
200
     * @return array
201
     */
202 7
    public function getMiddlewares()
203
    {
204 7
        return $this->middlewares;
205
    }
206
207
    /**
208
     * Make a request.
209
     *
210
     * @param string $url
211
     * @param string $method
212
     * @param array  $options
213
     *
214
     * @return array|bool
215
     *
216
     * @throws HttpException
217
     */
218 2
    public function request($url, $method = 'GET', $options = [])
219
    {
220 2
        $method = strtoupper($method);
221
222 2
        $options = array_merge(self::$defaults, $options);
223
224 2
        Log::debug('Client Request:', compact('url', 'method', 'options'));
225
226 2
        $options['handler'] = $this->getHandler();
227
228 2
        $response = $this->getClient()->request($method, $url, $options);
229
230 2
        Log::debug('API response:', [
231 2
            'Status' => $response->getStatusCode(),
232 2
            'Reason' => $response->getReasonPhrase(),
233 2
            'Headers' => $response->getHeaders(),
234 2
            'Body' => strval($response->getBody()),
235 2
        ]);
236
237 2
        return $response;
238
    }
239
240
    /**
241
     * @param \Psr\Http\Message\ResponseInterface|string $body
242
     *
243
     * @return mixed
244
     *
245
     * @throws \EasyWeChat\Core\Exceptions\HttpException
246
     */
247 9
    public function parseJSON($body)
248
    {
249 9
        if ($body instanceof ResponseInterface) {
250 2
            $body = $body->getBody();
251 2
        }
252
253
        // XXX: json maybe contains special chars. So, let's FUCK the WeChat API developers ...
254 9
        $body = $this->fuckTheWeChatInvalidJSON($body);
255
256 9
        if (empty($body)) {
257 1
            return false;
258
        }
259
260 9
        $contents = json_decode($body, true);
261
262 9
        Log::debug('API response decoded:', compact('contents'));
263
264 9
        if (JSON_ERROR_NONE !== json_last_error()) {
265 1
            throw new HttpException('Failed to parse JSON: '.json_last_error_msg());
266
        }
267
268 9
        return $contents;
269
    }
270
271
    /**
272
     * Filter the invalid JSON string.
273
     *
274
     * @param \Psr\Http\Message\StreamInterface|string $invalidJSON
275
     *
276
     * @return string
277
     */
278 9
    protected function fuckTheWeChatInvalidJSON($invalidJSON)
279
    {
280 9
        return preg_replace("/\p{Cc}/u", '', trim($invalidJSON));
281
    }
282
283
    /**
284
     * Build a handler.
285
     *
286
     * @return HandlerStack
287
     */
288 2
    protected function getHandler()
289
    {
290 2
        $stack = HandlerStack::create();
291
292 2
        foreach ($this->middlewares as $middleware) {
293
            $stack->push($middleware);
294 2
        }
295
296 2
        return $stack;
297
    }
298
}
299