Http::getMiddlewares()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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