Passed
Push — master ( cc4828...00e84f )
by Igor
10:05
created

src/Transport/CurlerRequest.php (1 issue)

1
<?php
2
3
namespace ClickHouseDB\Transport;
4
5
use const CURLOPT_HTTPGET;
6
use const CURLOPT_POST;
7
8
class CurlerRequest
9
{
10
    /**
11
     * @var array
12
     */
13
    public $extendinfo = array();
14
15
    /**
16
     * @var string|array
17
     */
18
    private $parameters = '';
19
20
    /**
21
     * @var array
22
     */
23
    private $options;
24
25
    /**
26
     * @var array
27
     */
28
    private $headers; // Parsed reponse header object.
29
30
    /**
31
     * @var string
32
     */
33
    private $url;
34
35
    /**
36
     * @var string
37
     */
38
    private $method;
39
40
    /**
41
     * @var bool
42
     */
43
    private $id;
44
45
    /**
46
     * @var resource|null
47
     */
48
    private $handle;
49
50
    /** @var CurlerResponse */
51
    private $response;
52
53
    /** @var bool */
54
    private $_persistent = false;
55
56
    /**
57
     * @var bool
58
     */
59
    private $_attachFiles = false;
60
61
    /**
62
     * @var string
63
     */
64
    private $callback_class = '';
65
66
    /**
67
     * @var string
68
     */
69
    private $callback_functionName = '';
70
71
    /**
72
     * @var bool
73
     */
74
    private $_httpCompression = false;
75
76
    /**
77
     * @var callable
78
     */
79
    private $callback_function = null;
80
81
    /**
82
     * @var bool|resource
83
     */
84
    private $infile_handle = false;
85
86
    /**
87
     * @var int
88
     */
89
    private $_dns_cache = 120;
90
91
    /**
92
     * @var resource
93
     */
94
    private $resultFileHandle = null;
95
96
    /**
97
     * @var string
98
     */
99
    private $sslCa = null;
100
101
    /**
102
     * @param bool $id
103
     */
104 52
    public function __construct($id = false)
105
    {
106 52
        $this->id = $id;
107
108 52
        $this->header('Cache-Control', 'no-cache, no-store, must-revalidate');
109 52
        $this->header('Expires', '0');
110 52
        $this->header('Pragma', 'no-cache');
111
112 52
        $this->options = array(
113 52
            CURLOPT_SSL_VERIFYHOST => 0,
114 52
            CURLOPT_SSL_VERIFYPEER => false,
115 52
            CURLOPT_TIMEOUT => 10,
116 52
            CURLOPT_CONNECTTIMEOUT => 5, // Количество секунд ожидания при попытке соединения
117 52
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
118 52
            CURLOPT_MAXREDIRS => 10,
119 52
            CURLOPT_HEADER => TRUE,
120 52
            CURLOPT_FOLLOWLOCATION => TRUE,
121 52
            CURLOPT_AUTOREFERER => 1, // при редиректе подставлять в «Referer:» значение из «Location:»
122 52
            CURLOPT_BINARYTRANSFER => 1, // передавать в binary-safe
123 52
            CURLOPT_RETURNTRANSFER => TRUE,
124 52
            CURLOPT_USERAGENT => 'smi2/PHPClickHouse/client',
125
        );
126 52
    }
127
128
    /**
129
     *
130
     */
131 47
    public function __destruct()
0 ignored issues
show
Method \ClickHouseDB\Transport\CurlerRequest::__destruct() does not need documentation comment.
Loading history...
132
    {
133 47
        $this->close();
134 47
    }
135
136
137 47
    public function close()
138
    {
139 47
        if ($this->handle) {
140 47
            curl_close($this->handle);
141
        }
142 47
        $this->handle = null;
143 47
    }
144
145
    /**
146
     * @param array $attachFiles
147
     */
148 1
    public function attachFiles($attachFiles)
149
    {
150 1
        $this->header("Content-Type", "multipart/form-data");
151
152 1
        $out = [];
153 1
        foreach ($attachFiles as $post_name => $file_path) {
154 1
            $out[$post_name] = new \CURLFile($file_path);
155
        }
156
157 1
        $this->_attachFiles = true;
158 1
        $this->parameters($out);
159 1
    }
160
161
162
    /**
163
     * @param bool $set
164
     * @return $this
165
     */
166
    public function id($set = false)
167
    {
168
        if ($set) {
169
            $this->id = $set;
170
        }
171
172
        return $this;
173
    }
174
175
    /**
176
     * @param array $params
177
     * @return $this
178
     */
179 43
    public function setRequestExtendedInfo($params)
180
    {
181 43
        $this->extendinfo = $params;
182 43
        return $this;
183
    }
184
185
    /**
186
     * @param string|integer|null $key
187
     * @return mixed
188
     */
189 43
    public function getRequestExtendedInfo($key = null)
190
    {
191 43
        if ($key) {
192 43
            return isset($this->extendinfo[$key]) ? $this->extendinfo[$key] : false;
193
        }
194
195
        return $this->extendinfo;
196
    }
197
198
    /**
199
     * @return bool|resource
200
     */
201 8
    public function getInfileHandle()
202
    {
203 8
        return $this->infile_handle;
204
    }
205
206
    /**
207
     * @param string $file_name
208
     * @return bool|resource
209
     */
210 8
    public function setInfile($file_name)
211
    {
212 8
        $this->header('Expect', '');
213 8
        $this->infile_handle = fopen($file_name, 'r');
214 8
        if (is_resource($this->infile_handle)) {
215
216 8
            if ($this->_httpCompression) {
217 8
                $this->header('Content-Encoding', 'gzip');
218 8
                $this->header('Content-Type', 'application/x-www-form-urlencoded');
219
220 8
                stream_filter_append($this->infile_handle, 'zlib.deflate', STREAM_FILTER_READ, ["window" => 30]);
221
222 8
                $this->options[CURLOPT_SAFE_UPLOAD] = 1;
223
            } else {
224
                $this->options[CURLOPT_INFILESIZE] = filesize($file_name);
225
            }
226
227 8
            $this->options[CURLOPT_INFILE] = $this->infile_handle;
228
        }
229
230 8
        return $this->infile_handle;
231
    }
232
233
    /**
234
     * @param callable $callback
235
     */
236 8
    public function setCallbackFunction($callback)
237
    {
238 8
        $this->callback_function = $callback;
239 8
    }
240
241
    /**
242
     * @param callable $callback
243
     */
244 1
    public function setWriteFunction($callback)
245
    {
246 1
        $this->options[CURLOPT_WRITEFUNCTION] = $callback;
247 1
    }
248
249
    /**
250
     * @param callable $callback
251
     */
252 3
    public function setReadFunction($callback)
253
    {
254 3
        $this->options[CURLOPT_READFUNCTION] = $callback;
255 3
    }
256
257
    public function setHeaderFunction($callback)
258
    {
259
        $this->options[CURLOPT_HEADERFUNCTION] = $callback;
260
    }
261
262
    /**
263
     * @param string $classCallBack
264
     * @param string $functionName
265
     */
266
    public function setCallback($classCallBack, $functionName)
267
    {
268
        $this->callback_class = $classCallBack;
269
        $this->callback_functionName = $functionName;
270
    }
271
272
    /**
273
     *
274
     */
275 10
    public function onCallback()
276
    {
277 10
        if ($this->callback_function) {
278 8
            $x = $this->callback_function;
279 8
            $x($this);
280
        }
281
282 10
        if ($this->callback_class && $this->callback_functionName) {
283
            $c = $this->callback_functionName;
284
            $this->callback_class->$c($this);
285
        }
286 10
    }
287
288
    public function getDetails(): array
289
    {
290
        return [
291
            'url'        => $this->url,
292
            'method'     => $this->method,
293
            'parameters' => $this->parameters,
294
            'headers'    => $this->headers,
295
        ];
296
    }
297
298
    /**
299
     * @param bool $result
300
     * @return string
301
     */
302
    public function dump($result = false)
303
    {
304
        $message = "\n------------  Request ------------\n";
305
        $message .= 'URL:' . $this->url . "\n\n";
306
        $message .= 'METHOD:' . $this->method . "\n\n";
307
        $message .= 'PARAMS:' . print_r($this->parameters, true) . "\n";
308
        $message .= 'PARAMS:' . print_r($this->headers, true) . "\n";
309
        $message .= "-----------------------------------\n";
310
311
        if ($result) {
312
            return $message;
313
        }
314
315
        echo $message;
316
        return '';
317
    }
318
319
    /**
320
     * @return bool
321
     */
322 15
    public function getId()
323
    {
324 15
        return $this->id;
325
    }
326
327
    /**
328
     * @param integer $key
329
     * @param mixed $value
330
     * @return $this
331
     */
332
    private function option($key, $value)
333
    {
334
        $this->options[$key] = $value;
335
        return $this;
336
    }
337
338
    /**
339
     * @return $this
340
     */
341 1
    public function persistent()
342
    {
343 1
        $this->_persistent = true;
344 1
        return $this;
345
    }
346
347
    /**
348
     * @return bool
349
     */
350 10
    public function isPersistent()
351
    {
352 10
        return $this->_persistent;
353
    }
354
355
    /**
356
     * @param int $sec
357
     * @return $this
358
     */
359
    public function keepAlive($sec = 60)
360
    {
361
        $this->options[CURLOPT_FORBID_REUSE] = TRUE;
362
        $this->headers['Connection'] = 'Keep-Alive';
363
        $this->headers['Keep-Alive'] = $sec;
364
365
        return $this;
366
    }
367
368
    /**
369
     * @param bool $flag
370
     * @return $this
371
     */
372 52
    public function verbose($flag = true)
373
    {
374 52
        $this->options[CURLOPT_VERBOSE] = $flag;
375 52
        return $this;
376
    }
377
378
    /**
379
     * @param string $key
380
     * @param string $value
381
     * @return $this
382
     */
383 52
    public function header($key, $value)
384
    {
385 52
        $this->headers[$key] = $value;
386 52
        return $this;
387
    }
388
389
    /**
390
     * @return array
391
     */
392
    public function getHeaders()
393
    {
394
        $head = [];
395
        foreach ($this->headers as $key => $value) {
396
            $head[] = sprintf("%s: %s", $key, $value);
397
        }
398
        return $head;
399
    }
400
401
    /**
402
     * @param string $url
403
     * @return $this
404
     */
405 52
    public function url($url)
406
    {
407 52
        $this->url = $url;
408 52
        return $this;
409
    }
410
411
    /**
412
     * @return mixed
413
     */
414
    public function getUrl()
415
    {
416
        return $this->url;
417
    }
418
419
420
    /**
421
     * @param string $id
422
     * @return string
423
     */
424 15
    public function getUniqHash($id)
425
    {
426 15
        return $id . '.' . microtime() . mt_rand(0, 1000000);
427
    }
428
429
    /**
430
     * @param bool $flag
431
     */
432 43
    public function httpCompression($flag)
433
    {
434 43
        if ($flag) {
435 43
            $this->_httpCompression = $flag;
436 43
            $this->options[CURLOPT_ENCODING] = 'gzip';
437
        } else {
438
            $this->_httpCompression = false;
439
            unset($this->options[CURLOPT_ENCODING]);
440
        }
441 43
    }
442
443
    /**
444
     * @param string $username
445
     * @param string $password
446
     * @return $this
447
     */
448
    public function authByBasicAuth($username, $password)
449
    {
450
        $this->options[CURLOPT_USERPWD] = sprintf("%s:%s", $username, $password);
451
        return $this;
452
    }
453
454 43
    public function authByHeaders($username, $password)
455
    {
456 43
        $this->headers['X-ClickHouse-User'] = $username;
457 43
        $this->headers['X-ClickHouse-Key'] = $password;
458 43
        return $this;
459
    }
460
461
    /**
462
     * @param array|string $data
463
     * @return $this
464
     */
465 1
    public function parameters($data)
466
    {
467 1
        $this->parameters = $data;
468 1
        return $this;
469
    }
470
471
    /**
472
     * The number of seconds to wait when trying to connect. Use 0 for infinite waiting.
473
     *
474
     * @param int $seconds
475
     * @return $this
476
     */
477 52
    public function connectTimeOut($seconds = 1)
478
    {
479 52
        $this->options[CURLOPT_CONNECTTIMEOUT] = $seconds;
480 52
        return $this;
481
    }
482
483
    /**
484
     * The maximum number of seconds (float) allowed to execute cURL functions.
485
     *
486
     * @param float $seconds
487
     * @return $this
488
     */
489 43
    public function timeOut($seconds = 10)
490
    {
491 43
        return $this->timeOutMs(intval($seconds * 1000));
492
    }
493
494
    /**
495
     * The maximum allowed number of milliseconds to perform cURL functions.
496
     *
497
     * @param int $ms millisecond
498
     * @return $this
499
     */
500 43
    protected function timeOutMs($ms = 10000)
501
    {
502 43
        $this->options[CURLOPT_TIMEOUT_MS] = $ms;
503 43
        return $this;
504
    }
505
506
507
    /**
508
     * @param array|mixed $data
509
     * @return $this
510
     * @throws \ClickHouseDB\Exception\TransportException
511
     */
512 43
    public function parameters_json($data)
513
    {
514
515 43
        $this->header("Content-Type", "application/json, text/javascript; charset=utf-8");
516 43
        $this->header("Accept", "application/json, text/javascript, */*; q=0.01");
517
518 43
        if ($data === null) {
519
            $this->parameters = '{}';
520
            return $this;
521
        }
522
523 43
        if (is_string($data)) {
524 43
            $this->parameters = $data;
525 43
            return $this;
526
        }
527
528
        $this->parameters = json_encode($data);
529
530
        if (!$this->parameters && $data) {
531
            throw new \ClickHouseDB\Exception\TransportException('Cant json_encode: ' . strval($data));
532
        }
533
534
        return $this;
535
    }
536
537
    /**
538
     * @return resource
539
     */
540
    public function getResultFileHandle()
541
    {
542
        return $this->resultFileHandle;
543
    }
544
545
    /**
546
     * @return bool
547
     */
548
    public function isResultFile()
549
    {
550
        return ($this->resultFileHandle ? true : false);
551
    }
552
553
    /**
554
     * @param resource $h resource
555
     * @param bool $zlib
556
     * @return $this
557
     */
558 1
    public function setResultFileHandle($h, $zlib = false)
559
    {
560 1
        $this->resultFileHandle = $h;
561 1
        if ($zlib) {
562
            $params = array('level' => 6, 'window' => 15, 'memory' => 9);
563
            stream_filter_append($this->resultFileHandle, 'zlib.deflate', STREAM_FILTER_WRITE, $params);
564
        }
565 1
        return $this;
566
    }
567
568
    /**
569
     * @return CurlerRequest
570
     */
571
    public function PUT()
572
    {
573
        return $this->execute('PUT');
574
    }
575
576
    /**
577
     * @return CurlerRequest
578
     */
579 43
    public function POST()
580
    {
581 43
        return $this->execute('POST');
582
    }
583
584
    /**
585
     * @return CurlerRequest
586
     */
587
    public function OPTIONS()
588
    {
589
        return $this->execute('OPTIONS');
590
    }
591
592
    /**
593
     * @return CurlerRequest
594
     */
595 37
    public function GET()
596
    {
597 37
        return $this->execute('GET');
598
    }
599
600
    /**
601
     * The number of seconds that DNS records are stored in memory. By default this parameter is 120 (2 minutes).
602
     *
603
     * @param integer $set
604
     * @return $this
605
     */
606
    public function setDnsCache($set)
607
    {
608
        $this->_dns_cache = $set;
609
        return $this;
610
    }
611
612
    /**
613
     * The number of seconds that DNS records are stored in memory. By default this parameter is 120 (2 minutes).
614
     *
615
     * @return int
616
     */
617 47
    public function getDnsCache()
618
    {
619 47
        return $this->_dns_cache;
620
    }
621
622
    /**
623
     * Sets client certificate
624
     *
625
     * @param string $filePath
626
     */
627
    public function setSslCa($filePath)
628
    {
629
        $this->option(CURLOPT_SSL_VERIFYPEER, true);
630
        $this->option(CURLOPT_CAINFO, $filePath);
631
    }
632
633
    /**
634
     * @param string $method
635
     * @return $this
636
     */
637 52
    private function execute($method)
638
    {
639 52
        $this->method = $method;
640 52
        return $this;
641
    }
642
643
    /**
644
     * @return CurlerResponse
645
     * @throws \ClickHouseDB\Exception\TransportException
646
     */
647 47
    public function response()
648
    {
649 47
        if (!$this->response) {
650
            throw new \ClickHouseDB\Exception\TransportException('Can`t fetch response - is empty');
651
        }
652
653 47
        return $this->response;
654
    }
655
656 31
    public function isResponseExists(): bool
657
    {
658 31
        return $this->response !== null;
659
    }
660
661 47
    public function setResponse(CurlerResponse $response): void
662
    {
663 47
        $this->response = $response;
664 47
    }
665
666
    /**
667
     * @return mixed
668
     */
669 47
    public function handle()
670
    {
671 47
        $this->prepareRequest();
672 47
        return $this->handle;
673
    }
674
675
    /**
676
     * @param callable $callback
677
     * @throws \Exception
678
     */
679
    public function setFunctionProgress(callable $callback)
680
    {
681
        if (!is_callable($callback)) {
682
            throw new \Exception('setFunctionProgress not is_callable');
683
        }
684
685
        $this->option(CURLOPT_NOPROGRESS, false);
686
        $this->option(CURLOPT_PROGRESSFUNCTION, $callback); // version 5.5.0
687
    }
688
689
690
    /**
691
     * @return bool
692
     */
693 47
    private function prepareRequest()
694
    {
695 47
        if (!$this->handle) {
696 47
            $this->handle = curl_init();
697
        }
698
699 47
        $curl_opt = $this->options;
700 47
        $method = $this->method;
701
702 47
        if ($this->_attachFiles) {
703 1
            $curl_opt[CURLOPT_SAFE_UPLOAD] = true;
704
        }
705
706
707 47
        if (strtoupper($method) == 'GET') {
708 37
            $curl_opt[CURLOPT_HTTPGET] = true;
709 37
            $curl_opt[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
710 37
            $curl_opt[CURLOPT_POSTFIELDS] = false;
711
        } else {
712 38
            if (strtoupper($method) === 'POST') {
713 38
                $curl_opt[CURLOPT_POST] = true;
714
            }
715
716 38
            $curl_opt[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
717
718 38
            if ($this->parameters) {
719 38
                $curl_opt[CURLOPT_POSTFIELDS] = $this->parameters;
720
721 38
                if (!is_array($this->parameters)) {
722 38
                    $this->header('Content-Length', strlen($this->parameters));
723
                }
724
            }
725
        }
726
        // CURLOPT_DNS_CACHE_TIMEOUT - Количество секунд, в течение которых в памяти хранятся DNS-записи.
727 47
        $curl_opt[CURLOPT_DNS_CACHE_TIMEOUT] = $this->getDnsCache();
728 47
        $curl_opt[CURLOPT_URL] = $this->url;
729
730 47
        if (!empty($this->headers) && sizeof($this->headers)) {
731 47
            $curl_opt[CURLOPT_HTTPHEADER] = array();
732
733 47
            foreach ($this->headers as $key => $value) {
734 47
                $curl_opt[CURLOPT_HTTPHEADER][] = sprintf("%s: %s", $key, $value);
735
            }
736
        }
737
738 47
        if (!empty($curl_opt[CURLOPT_INFILE])) {
739
740 8
            $curl_opt[CURLOPT_PUT] = true;
741
        }
742
743 47
        if (!empty($curl_opt[CURLOPT_WRITEFUNCTION])) {
744 1
            $curl_opt[CURLOPT_HEADER] = false;
745
        }
746
747 47
        if ($this->resultFileHandle) {
748 1
            $curl_opt[CURLOPT_FILE] = $this->resultFileHandle;
749 1
            $curl_opt[CURLOPT_HEADER] = false;
750
        }
751
752 47
        if ($this->options[CURLOPT_VERBOSE]) {
753
            echo "\n-----------BODY REQUEST----------\n" . $curl_opt[CURLOPT_POSTFIELDS] . "\n------END--------\n";
754
        }
755 47
        curl_setopt_array($this->handle, $curl_opt);
756 47
        return true;
757
    }
758
}
759