Passed
Pull Request — master (#123)
by Arman
06:22 queued 02:05
created

HttpClient::getRequestHeaders()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 13
rs 10
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.0
13
 */
14
15
namespace Quantum\Libraries\Curl;
16
17
use Quantum\Exceptions\HttpException;
18
use Quantum\Exceptions\LangException;
19
use Quantum\Exceptions\AppException;
20
use Curl\CaseInsensitiveArray;
21
use ErrorException;
22
use Curl\MultiCurl;
23
use Curl\Curl;
24
25
/**
26
 * Curl Class
27
 * @package Quantum\Libraries\Curl
28
 * @uses php-curl-class/php-curl-class
29
 */
30
class HttpClient
31
{
32
33
    /**
34
     * Available methods
35
     */
36
    const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
37
38
    /**
39
     * Response headers section
40
     */
41
    const RESPONSE_HEADERS = 'headers';
42
43
    /**
44
     * Response cookies section
45
     */
46
    const RESPONSE_COOKIES = 'cookies';
47
48
    /**
49
     * Response body section
50
     */
51
    const RESPONSE_BODY = 'body';
52
53
    /**
54
     * @var MultiCurl|Curl
55
     */
56
    private $client = null;
57
58
    /**
59
     * @var string
60
     */
61
    private $method = 'GET';
62
63
    /**
64
     * @var mixed|null
65
     */
66
    private $data = null;
67
68
    /**
69
     * @var array
70
     */
71
    private $response = [];
72
73
    /**
74
     * @var array
75
     */
76
    private $errors = [];
77
78
    /**
79
     * Creates request
80
     * @param string $url
81
     * @return HttpClient
82
     */
83
    public function createRequest(string $url): HttpClient
84
    {
85
        $this->client = new Curl();
86
        $this->client->setUrl($url);
87
        return $this;
88
    }
89
90
    /**
91
     * Creates multi request
92
     * @return HttpClient
93
     */
94
    public function createMultiRequest(): HttpClient
95
    {
96
        $this->client = new MultiCurl();
97
98
        $this->client->complete(function (Curl $instance) {
99
            $this->handleResponse($instance);
100
        });
101
102
        return $this;
103
    }
104
105
    /**
106
     * Creates async multi
107
     * @param callable $success
108
     * @param callable $error
109
     * @return HttpClient
110
     */
111
    public function createAsyncMultiRequest(callable $success, callable $error): HttpClient
112
    {
113
        $this->client = new MultiCurl();
114
115
        $this->client->success($success);
116
        $this->client->error($error);
117
118
        return $this;
119
    }
120
121
    /**
122
     * Sets http method
123
     * @param string $method
124
     * @return HttpClient
125
     * @throws HttpException
126
     * @throws LangException
127
     */
128
    public function setMethod(string $method): HttpClient
129
    {
130
        if (!in_array($method, self::METHODS)) {
131
            throw HttpException::methodNotAvailable($method);
132
        }
133
134
        $this->method = $method;
135
        return $this;
136
    }
137
138
    /**
139
     * Gets the current http method
140
     * @return string
141
     */
142
    public function getMethod(): string
143
    {
144
        return $this->method;
145
    }
146
147
    /**
148
     * Sets data
149
     * @param mixed $data
150
     * @return HttpClient
151
     */
152
    public function setData($data): HttpClient
153
    {
154
        $this->data = $data;
155
        return $this;
156
    }
157
158
    /**
159
     * Gets the post
160
     * @return mixed|null
161
     */
162
    public function getData()
163
    {
164
        return $this->data;
165
    }
166
167
    /**
168
     * Checks if the request is multi cURL
169
     * @return bool
170
     */
171
    public function isMultiRequest(): bool
172
    {
173
        return $this->client instanceof MultiCurl;
174
    }
175
176
    /**
177
     * Starts the request
178
     * @throws ErrorException
179
     * @throws HttpException
180
     * @throws LangException
181
     */
182
    public function start()
183
    {
184
        if (!$this->client) {
185
            throw HttpException::requestNotCreated();
186
        }
187
188
        if ($this->isMultiRequest()) {
189
            $this->client->start();
190
            return;
191
        }
192
193
        $this->client->setOpt(CURLOPT_CUSTOMREQUEST, $this->method);
194
195
        if ($this->data) {
196
            $this->client->setOpt(CURLOPT_POSTFIELDS, $this->client->buildPostData($this->data));
197
        }
198
199
        $this->client->exec();
200
201
        $this->handleResponse($this->client);
202
203
    }
204
205
    /**
206
     * Gets single or all request headers
207
     * @param string|null $header
208
     * @return mixed|null
209
     * @throws AppException
210
     */
211
    public function getRequestHeaders(string $header = null)
212
    {
213
        if ($this->isMultiRequest()) {
214
            throw AppException::methodNotSupported(__METHOD__, MultiCurl::class);
215
        }
216
217
        $requestHeaders = $this->handleHeaders($this->client->getRequestHeaders());
218
219
        if ($header) {
220
            return $requestHeaders[$header] ?? null;
221
        }
222
223
        return $requestHeaders;
224
    }
225
226
    /**
227
     * Gets the response headers
228
     * @param string|null $header
229
     * @return mixed|null
230
     * @throws AppException
231
     */
232
    public function getResponseHeaders(string $header = null)
233
    {
234
        if ($this->isMultiRequest()) {
235
            throw AppException::methodNotSupported(__METHOD__, MultiCurl::class);
236
        }
237
238
        $responseHeaders = $this->getResponse()[self::RESPONSE_HEADERS];
239
240
        if ($header) {
241
            return $responseHeaders[$header] ?? null;
242
        }
243
244
        return $responseHeaders;
245
    }
246
247
    /**
248
     * Gets the response cookies
249
     * @param string|null $cookie
250
     * @return mixed|null
251
     * @throws AppException
252
     */
253
    public function getResponseCookies(string $cookie = null)
254
    {
255
        if ($this->isMultiRequest()) {
256
            throw AppException::methodNotSupported(__METHOD__, MultiCurl::class);
257
        }
258
259
        $responseCookies = $this->getResponse()[self::RESPONSE_COOKIES];
260
261
        if ($cookie) {
262
            return $responseCookies[$cookie] ?? null;
263
        }
264
265
        return $responseCookies;
266
    }
267
268
    /**
269
     * Gets the response body
270
     * @return mixed|null
271
     * @throws AppException
272
     */
273
    public function getResponseBody()
274
    {
275
        if ($this->isMultiRequest()) {
276
            throw AppException::methodNotSupported(__METHOD__, MultiCurl::class);
277
        }
278
279
        return $this->response[$this->client->getId()][self::RESPONSE_BODY] ?? null;
280
    }
281
282
    /**
283
     * Gets the entire response
284
     * @return array
285
     */
286
    public function getResponse(): array
287
    {
288
        if (!$this->isMultiRequest()) {
289
            return $this->response[$this->client->getId()];
290
        }
291
292
        return $this->response;
293
    }
294
295
    /**
296
     * Returns the errors
297
     * @return array
298
     */
299
    public function getErrors(): array
300
    {
301
        if (!$this->isMultiRequest()) {
302
            return $this->errors[$this->client->getId()];
303
        }
304
305
        return $this->errors;
306
    }
307
308
    /**
309
     * Gets the curl info
310
     * @param int|null $option
311
     * @return mixed
312
     * @throws AppException
313
     */
314
    public function info(int $option = null)
315
    {
316
        if ($this->isMultiRequest()) {
317
            throw AppException::methodNotSupported(__METHOD__, MultiCurl::class);
318
        }
319
320
        if ($option) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $option of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
321
            return $this->client->getInfo($option);
322
        }
323
324
        return $this->client->getInfo();
325
    }
326
327
    /**
328
     * @param string $function
329
     * @param array $arguments
330
     * @return HttpClient
331
     * @throws HttpException
332
     * @throws LangException
333
     */
334
    public function __call(string $function, array $arguments): HttpClient
335
    {
336
        if (is_null($this->client)) {
337
            throw HttpException::requestNotCreated();
338
        }
339
340
        $this->client->{$function}(...$arguments);
341
342
        return $this;
343
    }
344
345
    /**
346
     * Handles the response
347
     * @param Curl $instance
348
     */
349
    private function handleResponse(Curl $instance)
350
    {
351
        if ($instance->isError()) {
352
            $this->errors[$instance->getId()] = [
353
                'code' => $instance->getErrorCode(),
354
                'message' => $instance->getErrorMessage()
355
            ];
356
357
            return;
358
        }
359
360
        $this->response[$instance->getId()] = [
361
            self::RESPONSE_HEADERS => $this->handleHeaders($instance->getResponseHeaders()),
362
            self::RESPONSE_COOKIES => $instance->getResponseCookies(),
363
            self::RESPONSE_BODY => $instance->getResponse()
364
        ];
365
    }
366
367
    /**
368
     * @param CaseInsensitiveArray $httpHeaders
369
     * @return array
370
     */
371
    private function handleHeaders(CaseInsensitiveArray $httpHeaders): array
372
    {
373
        $headers = [];
374
375
        while ($httpHeaders->valid()) {
376
            $headers[strtolower((string)$httpHeaders->key())] = $httpHeaders->current();
377
            $httpHeaders->next();
378
        }
379
380
        $httpHeaders->rewind();
381
382
        return $headers;
383
    }
384
385
}
386