Completed
Pull Request — master (#617)
by mingyoung
05:10
created

Http::upload()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

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