Passed
Push — master ( 5de87e...779290 )
by Dev
42:42 queued 27:09
created

Request::getRequestInfos()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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