Request::getHandle()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PiedWeb\Curl;
4
5
use CurlHandle;
6
7
class Request
8
{
9
    use StaticWrapperTrait;
10
    use UserAgentTrait;
11
12
    public const RETURN_HEADER_ONLY = 2;
13
14
    public const RETURN_HEADER = 1;
15
16
    /**
17
     * @var resource // When dropping 7.4 set CurlHandle
18
     */
19
    private $handle;
20
21
    /** @var string contains targeted URL */
22
    private string $url;
23
24
    /** @var string contains current UA */
25
    private string $userAgent;
26
27
    private int $returnHeaders = 0;
28
29
    /**
30
     * @var callable
31
     */
32
    private $filter;
33
34
    private bool $optChangeDuringRequest = false;
35
36
    public function __construct(?string $url = null)
37
    {
38
        $this->handle = \Safe\curl_init();
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_init() of type CurlHandle is incompatible with the declared type resource of property $handle.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
39 36
        $this->setOpt(\CURLOPT_RETURNTRANSFER, 1);
40
41 36
        if (null !== $url) {
42 36
            $this->setUrl($url);
43
        }
44 36
    }
45 36
46
    /**
47 36
     * @return resource // When dropping 7.4 set CurlHandle
48
     */
49 36
    public function getHandle()
50
    {
51 36
        return $this->handle;
52
    }
53
54 6
    public function getUrl(): string
55
    {
56 6
        return $this->url;
57
    }
58
59
    /**
60
     * Change the URL to cURL.
61
     *
62
     * @param string $url to request
63
     */
64
    public function setUrl(string $url): self
65
    {
66 36
        $this->url = $url;
67
        $this->setOpt(\CURLOPT_URL, $url);
68 36
69 36
        return $this;
70
    }
71 36
72
    /**
73
     * Add a cURL's option.
74
     *
75
     * @param int   $option cURL Predefined Constant
76
     * @param mixed $value
77
     * @psalm-suppress InvalidArgument (for $handle)
78
     */
79
    public function setOpt(int $option, $value): self
80
    {
81
        curl_setopt($this->getHandle(), $option, $value);
82 36
83
        return $this;
84 36
    }
85
86 36
    /**
87
     * A short way to set some classic options to cURL a web page.
88
     */
89
    public function setDefaultGetOptions(
90
        int $connectTimeOut = 5,
91
        int $timeOut = 10,
92
        int $dnsCacheTimeOut = 600,
93
        bool $followLocation = true,
94 27
        int $maxRedirs = 5,
95
        bool $autoReferer = true
96
    ): self {
97
        $this
98
            ->setOpt(\CURLOPT_AUTOREFERER, $autoReferer)
99
            ->setOpt(\CURLOPT_FOLLOWLOCATION, $followLocation)
100
            ->setOpt(\CURLOPT_MAXREDIRS, $maxRedirs)
101
            ->setOpt(\CURLOPT_CONNECTTIMEOUT, $connectTimeOut)
102 27
            ->setOpt(\CURLOPT_DNS_CACHE_TIMEOUT, $dnsCacheTimeOut)
103 27
            ->setOpt(\CURLOPT_TIMEOUT, $timeOut)
104 27
        ;
105 27
106 27
        return $this;
107 27
    }
108
109
    /**
110
     * A short way to set some classic options to cURL a web page quickly
111 27
     * (but loosing some data like header, cookie...).
112
     */
113
    public function setDefaultSpeedOptions(): self
114
    {
115
        $this->setOpt(\CURLOPT_SSL_VERIFYHOST, 0);
116
        $this->setOpt(\CURLOPT_SSL_VERIFYPEER, 0);
117
118
        if (! $this->returnHeaders) {
119
            $this->setOpt(\CURLOPT_HEADER, 0);
120 9
        }
121
122 9
        $this->setDefaultGetOptions(5, 10, 600, true, 1);
123 9
        $this->setEncodingGzip();
124
125 9
        return $this;
126 9
    }
127
128
    /**
129 9
     * A short way to not follow redirection.
130 9
     */
131
    public function setNoFollowRedirection(): self
132 9
    {
133
        return $this
134
            ->setOpt(\CURLOPT_FOLLOWLOCATION, false)
135
            ->setOpt(\CURLOPT_MAXREDIRS, 0)
136
        ;
137
    }
138
139
    /**
140 6
     * Call it if you want header informations.
141
     * After self::exec(), you would have this informations with getHeader();.
142
     */
143 6
    public function setReturnHeader(bool $only = false): self
144 6
    {
145
        $this->setOpt(\CURLOPT_HEADER, 1);
146
        $this->returnHeaders = $only ? self::RETURN_HEADER_ONLY : self::RETURN_HEADER;
147
148
        if ($only) {
149
            $this->setOpt(\CURLOPT_RETURNTRANSFER, 0);
150
            $this->setOpt(\CURLOPT_NOBODY, 1);
151
        }
152
153
        return $this;
154 24
    }
155
156 24
    public function mustReturnHeaders(): int
157 24
    {
158
        return $this->returnHeaders;
159 24
    }
160 3
161 3
    /**
162
     * An self::setOpt()'s alias to add a cookie to your request.
163
     */
164 24
    public function setCookie(string $cookie): self
165
    {
166
        $this->setOpt(\CURLOPT_COOKIE, $cookie);
167 27
168
        return $this;
169 27
    }
170
171
    /**
172
     * An self::setOpt()'s alias to add a referer to your request.
173
     */
174
    public function setReferer(string $referer): self
175
    {
176
        $this->setOpt(\CURLOPT_REFERER, $referer);
177
178
        return $this;
179 3
    }
180
181 3
    /**
182
     * An self::setOpt()'s alias to add an user-agent to your request.
183 3
     */
184
    public function setUserAgent(string $ua): self
185
    {
186
        $this->userAgent = $ua;
187
188
        $this->setOpt(\CURLOPT_USERAGENT, $ua);
189
190
        return $this;
191
    }
192
193 27
    public function getUserAgent(): string
194
    {
195 27
        return $this->userAgent;
196
    }
197 27
198
    /**
199
     * A short way to set post's options to cURL a web page.
200
     *
201
     * @param mixed $post if it's an array, will be converted via http build query
202
     *
203
     * @return self
204
     */
205
    public function setPost($post)
206
    {
207 27
        $this->setOpt(\CURLOPT_CUSTOMREQUEST, 'POST');
208
        $this->setOpt(\CURLOPT_POST, 1);
209 27
        $this->setOpt(\CURLOPT_POSTFIELDS, \is_array($post) ? http_build_query($post) : $post);
210
211 27
        return $this;
212
    }
213 27
214
    /**
215
     * If you want to request the URL and hope get the result gzipped.
216 3
     * The output will be automatically uncompress with exec();.
217
     */
218 3
    public function setEncodingGzip(): self
219
    {
220
        $this->setOpt(\CURLOPT_ENCODING, 'gzip, deflate');
221
222
        return $this;
223
    }
224
225
    /**
226
     * If you want to request the URL with a (http|socks...) proxy (public or private).
227
     *
228 3
     * @param string $proxy [scheme]IP:PORT[:LOGIN:PASSWORD]
229
     *                      Eg. : socks5://98.023.023.02:1098:cUrlRequestProxId:SecretPassword
230 3
     */
231 3
    public function setProxy(string $proxy): self
232 3
    {
233
        $scheme = Helper::getSchemeFrom($proxy);
234 3
        $proxy = explode(':', $proxy);
235
        $this->setOpt(\CURLOPT_HTTPPROXYTUNNEL, 1);
236
        $this->setOpt(\CURLOPT_PROXY, $scheme.$proxy[0].':'.$proxy[1]);
237
        if (isset($proxy[2])) {
238
            $this->setOpt(\CURLOPT_PROXYUSERPWD, $proxy[2].':'.$proxy[3]);
239
        }
240
241
        return $this;
242
    }
243 24
244
    /**
245 24
     * @param callable $func function wich must return boolean
246
     */
247 24
    public function setDownloadOnlyIf(callable $func): self
248
    {
249
        $this->setReturnHeader();
250
251
        $this->filter = $func;
252
        $this->setOpt(\CURLOPT_HEADERFUNCTION, [$this, 'checkHeader']);
253
        $this->setOpt(\CURLOPT_NOBODY, 1);
254
255
        return $this;
256
    }
257
258 3
    /**
259
     * @param int $tooBig Default 2000000 = 2000 Kbytes = 2 Mo
260 3
     * @psalm-suppress UnusedClosureParam
261 3
     */
262 3
    public function setAbortIfTooBig(int $tooBig = 2000000): self
263 3
    {
264 3
        //$this->setOpt(CURLOPT_BUFFERSIZE, 128); // more progress info
265
        $this->setOpt(\CURLOPT_NOPROGRESS, false);
266
        $this->setOpt(\CURLOPT_PROGRESSFUNCTION, function ($ch, $totalBytes, $receivedBytes) use ($tooBig) {
267
            if ($receivedBytes > $tooBig) {
268 3
                return 1;
269
            }
270
        });
271
272
        return $this;
273
    }
274
275
    public function setDownloadOnly(string $range = '0-500'): self
276 18
    {
277
        $this->setOpt(\CURLOPT_RANGE, $range);
278 18
279
        return $this;
280 18
    }
281 18
282 18
    public function checkHeader(CurlHandle $handle, string $line): int
283
    {
284 18
        if (\call_user_func($this->filter, $line)) {
285
            $this->optChangeDuringRequest = true;
286
            $this->setOpt(\CURLOPT_NOBODY, 0);
287
        }
288
289
        return \strlen($line);
290
    }
291
292 3
    /**
293
     * Execute the request.
294
     *
295 3
     * @return Response|int corresponding to the curl error
296 1
     */
297 3
    public function exec(bool $optChange = false)
298 3
    {
299
        $return = Response::get($this);
300 3
301
        // Permits to transform HEAD request in GET request
302 3
        if ($this->optChangeDuringRequest && false === $optChange) {
303
            $this->optChangeDuringRequest = true;
304
305 3
            return $this->exec(true);
306
        }
307 3
308
        if ($return instanceof Response && ($effectiveUrl = $return->getEffectiveUrl()) !== null) {
309 3
            $this->setReferer($effectiveUrl);
310
        }
311
312 18
        return $return;
313
    }
314 18
315 18
    public function getResponse(): ?Response
316 12
    {
317 12
        $response = $this->exec();
318
319
        if (\is_int($response)) {
320
            return null;
321 18
        }
322
323
        return $response;
324
    }
325
326
    /**
327
     * Return the last error number (curl_errno).
328
     *
329 36
     * @return int the error number or 0 (zero) if no error occurred
330
     * @psalm-suppress InvalidArgument (for $handle)
331 36
     */
332
    public function hasError(): int
333
    {
334 36
        return curl_errno($this->getHandle());
335 12
    }
336
337 12
    /**
338
     * Return a string containing the last error for the current session (curl_error).
339
     *
340 36
     * @return string the error message or '' (the empty string) if no error occurred
341 27
     * @psalm-suppress InvalidArgument (for $handle)
342
     */
343
    public function getError(): string
344 36
    {
345
        return curl_error($this->getHandle());
346
    }
347
348
    /**
349
     * Get information regarding the request.
350
     *
351
     * @param int $opt This may be one of the following constants:
352 3
     *                 http://php.net/manual/en/function.curl-getinfo.php
353
     *
354 3
     * @return string|array<string, string> If opt is given, returns its value as a string. Otherwise, returns an associative array with the following elements (which correspond to opt): "url" "content_type" "http_code" "header_size" "request_size" "filetime" "ssl_verify_result" "redirect_count" "total_time" "namelookup_time" "connect_time" "pretransfer_time" "size_upload" "size_download" "speed_download" "speed_upload" "download_content_length" "upload_content_length" "starttransfer_time" "redirect_time"
355
     * @psalm-suppress InvalidArgument (for $handle)
356
     */
357
    public function getInfo(?int $opt = null)
358
    {
359
        return curl_getinfo($this->handle, $opt); // @phpstan-ignore-line
360
    }
361
362 3
    /**
363
     * @return string|int
364 3
     * @psalm-suppress InvalidArgument (for $handle)
365
     */
366
    public function getRequestInfo(int $opt)
367
    {
368
        return curl_getinfo($this->handle, $opt); // @phpstan-ignore-line
369
    }
370
371
    /**
372
     * @return string[]
373
     * @psalm-suppress InvalidArgument (for $handle)
374
     */
375 3
    public function getRequestInfos(): array
376
    {
377 3
        return curl_getinfo($this->handle);  // @phpstan-ignore-line
378
    }
379
380
    /**
381
     * Close the connexion
382
     * Call curl_reset function.
383
     *
384 3
     * @psalm-suppress InvalidArgument (for $handle)
385
     */
386 3
    public function close(): void
387 3
    {
388
        curl_reset($this->getHandle());
389
    }
390
}
391