Passed
Push — master ( fdf013...12bd2a )
by Igor
03:22
created

src/Statement.php (32 issues)

1
<?php
0 ignored issues
show
An error occurred during processing; checking has been aborted. The error message was: "if" without curly braces is not supported.
Loading history...
2
3
namespace ClickHouseDB;
4
5
use ClickHouseDB\Exception\DatabaseException;
6
use ClickHouseDB\Exception\QueryException;
7
use ClickHouseDB\Query\Query;
8
use ClickHouseDB\Transport\CurlerRequest;
9
use ClickHouseDB\Transport\CurlerResponse;
10
11
class Statement
12
{
13
    /**
14
     * @var string|mixed
15
     */
16
    private $_rawData;
17
18
    /**
19
     * @var int
20
     */
21
    private $_http_code = -1;
22
23
    /**
24
     * @var CurlerRequest
25
     */
26
    private $_request = null;
27
28
    /**
29
     * @var bool
30
     */
31
    private $_init = false;
32
33
    /**
34
     * @var Query
35
     */
36
    private $query;
37
38
    /**
39
     * @var mixed
40
     */
41
    private $format;
42
43
    /**
44
     * @var string
45
     */
46
    private $sql = '';
47
48
    /**
49
     * @var array
50
     */
51
    private $meta;
52
53
    /**
54
     * @var array
55
     */
56
    private $totals;
57
58
    /**
59
     * @var array
60
     */
61
    private $extremes;
62
63
    /**
64
     * @var int
65
     */
66
    private $rows;
67
68
    /**
69
     * @var bool|integer
70
     */
71
    private $rows_before_limit_at_least = false;
72
73
    /**
74
     * @var array
75
     */
76
    private $array_data = [];
77
78
    /**
79
     * @var array|null
80
     */
81
    private $statistics = null;
82
83
    /**
84
     * @var int
85
     */
86
    public $iterator=0;
87
88
89 44
    public function __construct(CurlerRequest $request)
0 ignored issues
show
Coding Style Documentation introduced by
Missing doc comment for function __construct()
Loading history...
90
    {
91 44
        $this->_request = $request;
92 44
        $this->format = $this->_request->getRequestExtendedInfo('format');
93 44
        $this->query = $this->_request->getRequestExtendedInfo('query');
94 44
        $this->sql = $this->_request->getRequestExtendedInfo('sql');
95 44
    }
96
97
    /**
98
     * @return CurlerRequest
99
     */
100
    public function getRequest()
101
    {
102
        return $this->_request;
103
    }
104
105
    /**
106
     * @return CurlerResponse
107
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
108
     */
109 38
    private function response()
110
    {
111 38
        return $this->_request->response();
112
    }
113
114
    /**
115
     * @return mixed
116
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
117
     */
118 1
    public function responseInfo()
119
    {
120 1
        return $this->response()->info();
121
    }
122
123
    /**
124
     * @return mixed|string
125
     */
126 11
    public function sql()
127
    {
128 11
        return $this->sql;
129
    }
130
131
    /**
132
     * @param string $body
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
133
     * @return array|bool
0 ignored issues
show
Expected "array|boolean" but found "array|bool" for function return type
Loading history...
134
     */
135 5
    private function parseErrorClickHouse($body)
0 ignored issues
show
Type hint "string" missing for $body
Loading history...
136
    {
137 5
        $body = trim($body);
138 5
        $mathes = [];
139
140
        // Code: 115, e.displayText() = DB::Exception: Unknown setting readonly[0], e.what() = DB::Exception
141
        // Code: 192, e.displayText() = DB::Exception: Unknown user x, e.what() = DB::Exception
142
        // Code: 60, e.displayText() = DB::Exception: Table default.ZZZZZ doesn't exist., e.what() = DB::Exception
143
144 5
        if (preg_match("%Code: (\d+),\se\.displayText\(\) \=\s*DB\:\:Exception\s*:\s*(.*)(?:\,\s*e\.what|\(version).*%ius", $body, $mathes)) {
145 5
            return ['code' => $mathes[1], 'message' => $mathes[2]];
0 ignored issues
show
Arrays with multiple values should not be declared on a single line.
Loading history...
146
        }
147
148
        return false;
149
    }
150
151
    /**
152
     * @return bool
0 ignored issues
show
Expected "boolean" but found "bool" for function return type
Loading history...
153
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
154
     */
155 8
    public function error()
156
    {
157 8
        if (!$this->isError()) {
158 2
            return false;
159
        }
160
161 6
        $body = $this->response()->body();
162 6
        $error_no = $this->response()->error_no();
163 6
        $error = $this->response()->error();
164
165 6
        if (!$error_no && !$error) {
166 5
            $parse = $this->parseErrorClickHouse($body);
167
168 5
            if ($parse) {
169 5
                throw new DatabaseException($parse['message'] . "\nIN:" . $this->sql(), $parse['code']);
170
            } else {
171
                $code = $this->response()->http_code();
172
                $message = "HttpCode:" . $this->response()->http_code() . " ; " . $this->response()->error() . " ;" . $body;
173
            }
174
        } else {
175 1
            $code = $error_no;
176 1
            $message = $this->response()->error();
177
        }
178
179 1
        throw new QueryException($message, $code);
180
    }
181
182
    /**
183
     * @return bool
0 ignored issues
show
Expected "boolean" but found "bool" for function return type
Loading history...
184
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
185
     */
186 38
    public function isError()
187
    {
188 38
        return ($this->response()->http_code() !== 200 || $this->response()->error_no());
189
    }
190
191 31
    private function check() : bool
0 ignored issues
show
Coding Style Documentation introduced by
Missing doc comment for function check()
Loading history...
192
    {
193 31
        if (!$this->_request->isResponseExists()) {
194
            throw QueryException::noResponse();
195
        }
196
197 31
        if ($this->isError()) {
198 1
            $this->error();
199
        }
200
201 30
        return true;
202
    }
203
204
    /**
205
     * @return bool
0 ignored issues
show
Expected "boolean" but found "bool" for function return type
Loading history...
206
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
207
     */
208 29
    private function init()
209
    {
210 29
        if ($this->_init) {
211
            return false;
212
        }
213
214
215 29
        $this->check();
216
217
218 28
        $this->_rawData = $this->response()->rawDataOrJson($this->format);
219
220 28
        if (!$this->_rawData) {
221
            $this->_init = true;
222
            return false;
223
        }
0 ignored issues
show
No blank line found after control structure
Loading history...
224 28
        $data=[];
225 28
        foreach (['meta', 'data', 'totals', 'extremes', 'rows', 'rows_before_limit_at_least', 'statistics'] as $key) {
226
227 28
            if (isset($this->_rawData[$key])) {
228 28
                if ($key=='data')
229
                {
230 28
                    $data=$this->_rawData[$key];
231
                }
232
                else{
233 28
                    $this->{$key} = $this->_rawData[$key];
234
                }
0 ignored issues
show
Blank line found after control structure
Loading history...
235
236
            }
237
        }
238
239 28
        if (empty($this->meta)) {
240
            throw  new QueryException('Can`t find meta');
241
        }
242
243 28
        $isJSONCompact=(stripos($this->format,'JSONCompact')!==false?true:false);
244 28
        $this->array_data = [];
245 28
        foreach ($data as $rows) {
246 28
            $r = [];
247
248
249 28
            if ($isJSONCompact)
250
            {
251 1
                $r[]=$rows;
252
            }
253
            else {
254 27
                foreach ($this->meta as $meta) {
255 27
                    $r[$meta['name']] = $rows[$meta['name']];
256
                }
257
            }
258
259 28
            $this->array_data[] = $r;
260
        }
261
262
263 28
        return true;
264
    }
265
266
    /**
267
     * @return array
268
     * @throws \Exception
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
269
     */
270
    public function extremes()
271
    {
272
        $this->init();
273
        return $this->extremes;
274
    }
275
276
    /**
277
     * @return mixed
278
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
279
     */
280 1
    public function totalTimeRequest()
281
    {
282 1
        $this->check();
283 1
        return $this->response()->total_time();
284
285
    }
286
287
    /**
288
     * @return array
289
     * @throws \Exception
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
290
     */
291 1
    public function extremesMin()
292
    {
293 1
        $this->init();
294
295 1
        if (empty($this->extremes['min'])) {
296
            return [];
297
        }
298
299 1
        return $this->extremes['min'];
300
    }
301
302
    /**
303
     * @return array
304
     * @throws \Exception
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
305
     */
306
    public function extremesMax()
307
    {
308
        $this->init();
309
310
        if (empty($this->extremes['max'])) {
311
            return [];
312
        }
313
314
        return $this->extremes['max'];
315
    }
316
317
    /**
318
     * @return array
319
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
320
     */
321 1
    public function totals()
322
    {
323 1
        $this->init();
324 1
        return $this->totals;
325
    }
326
327
    /**
328
     *
329
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @return tag in function comment
Loading history...
330
    public function dumpRaw()
331
    {
332
        print_r($this->_rawData);
333
    }
334
335
    /**
336
     *
337
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @return tag in function comment
Loading history...
338
    public function dump()
339
    {
340
        $this->_request->dump();
341
        $this->response()->dump();
342
    }
343
344
    /**
345
     * @return bool|int
0 ignored issues
show
Expected "boolean|integer" but found "bool|int" for function return type
Loading history...
346
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
347
     */
348 2
    public function countAll()
349
    {
350 2
        $this->init();
351 2
        return $this->rows_before_limit_at_least;
352
    }
353
354
    /**
355
     * @param bool $key
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter comment
Loading history...
Expected "boolean" but found "bool" for parameter type
Loading history...
356
     * @return array|mixed|null
357
     * @throws Exception\TransportException
0 ignored issues
show
Comment missing for @throws tag in function comment
Loading history...
358
     */
359
    public function statistics($key = false)
0 ignored issues
show
Type hint "bool" missing for $key
Loading history...
360
    {
361
        $this->init();
362
363
        if (!is_array($this->statistics)) {
364
            return null;
365
        }
366
367
        if (!$key) return $this->statistics;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
368
369
        if (!isset($this->statistics[$key])) {
370
            return null;
371
        }
372
373
        return $this->statistics[$key];
374
375
    }
0 ignored issues
show
Function closing brace must go on the next line following the body; found 1 blank lines before brace
Loading history...
376
377
    /**
378
     * @return int
379
     * @throws Exception\TransportException
380
     */
381 8
    public function count()
382
    {
383 8
        $this->init();
384 8
        return $this->rows;
385
    }
386
387
    /**
388
     * @return mixed|string
389
     * @throws Exception\TransportException
390
     */
391 3
    public function rawData()
392
    {
393 3
        if ($this->_init) {
394
            return $this->_rawData;
395
        }
396
397 3
        $this->check();
398
399 3
        return $this->response()->rawDataOrJson($this->format);
400
    }
401
402
    /**
403
     *
404
     */
405 2
    public function resetIterator()
406
    {
407 2
        $this->iterator=0;
408 2
    }
409
410 2
    public function fetchRow($key = null)
411
    {
412 2
        $this->init();
413
414 2
        $position=$this->iterator;
415
416 2
        if (!isset($this->array_data[$position])) {
417
            return null;
418
        }
419
420 2
        $this->iterator++;
421
422 2
        if (!$key) {
423
            return $this->array_data[$position];
424
        }
425 2
        if (!isset($this->array_data[$position][$key])) {
426 1
            return null;
427
        }
428
429 2
        return $this->array_data[$position][$key];
430
431
    }
432
    /**
433
     * @param string $key
434
     * @return mixed|null
435
     * @throws Exception\TransportException
436
     */
437 17
    public function fetchOne($key = null)
438
    {
439 17
        $this->init();
440 17
        if (!isset($this->array_data[0])) {
441
            return null;
442
        }
443
444 17
        if (!$key) {
445 2
            return $this->array_data[0];
446
        }
447
448 17
        if (!isset($this->array_data[0][$key])) {
449 2
            return null;
450
        }
451
452 16
        return $this->array_data[0][$key];
453
    }
454
455
    /**
456
     * @param string|null $path
457
     * @return array
458
     * @throws Exception\TransportException
459
     */
460 4
    public function rowsAsTree($path)
461
    {
462 4
        $this->init();
463
464 4
        $out = [];
465 4
        foreach ($this->array_data as $row) {
466 4
            $d = $this->array_to_tree($row, $path);
467 4
            $out = array_replace_recursive($d, $out);
468
        }
469
470 4
        return $out;
471
    }
472
473
    /**
474
     * Return size_upload,upload_content,speed_upload,time_request
475
     *
476
     * @return array
477
     * @throws Exception\TransportException
478
     */
479
    public function info_upload()
480
    {
481
        $this->check();
482
        return [
483
            'size_upload'    => $this->response()->size_upload(),
484
            'upload_content' => $this->response()->upload_content_length(),
485
            'speed_upload'   => $this->response()->speed_upload(),
486
            'time_request'   => $this->response()->total_time()
487
        ];
488
    }
489
490
    /**
491
     * Return size_upload,upload_content,speed_upload,time_request,starttransfer_time,size_download,speed_download
492
     *
493
     * @return array
494
     * @throws Exception\TransportException
495
     */
496 1
    public function info()
497
    {
498 1
        $this->check();
499
        return [
500 1
            'starttransfer_time'    => $this->response()->starttransfer_time(),
501 1
            'size_download'    => $this->response()->size_download(),
502 1
            'speed_download'    => $this->response()->speed_download(),
503 1
            'size_upload'    => $this->response()->size_upload(),
504 1
            'upload_content' => $this->response()->upload_content_length(),
505 1
            'speed_upload'   => $this->response()->speed_upload(),
506 1
            'time_request'   => $this->response()->total_time()
507
        ];
508
    }
509
510
    /**
511
     * get format in sql
512
     * @return mixed
513
     */
514 1
    public function getFormat()
515
    {
516 1
        return $this->format;
517
    }
518
519
    /**
520
     * @return array
521
     * @throws Exception\TransportException
522
     */
523 9
    public function rows()
524
    {
525 9
        $this->init();
526 8
        return $this->array_data;
527
    }
528
529
    /**
530
     * @param array|string $arr
531
     * @param null|string|array $path
532
     * @return array
533
     */
534 4
    private function array_to_tree($arr, $path = null)
535
    {
536 4
        if (is_array($path)) {
537
            $keys = $path;
538
        } else {
539 4
            $args = func_get_args();
540 4
            array_shift($args);
541
542 4
            if (sizeof($args) < 2) {
543 4
                $separator = '.';
544 4
                $keys = explode($separator, $path);
545
            } else {
546
                $keys = $args;
547
            }
548
        }
549
550
        //
551 4
        $tree = $arr;
552 4
        while (count($keys)) {
553 4
            $key = array_pop($keys);
554
555 4
            if (isset($arr[$key])) {
556 4
                $val = $arr[$key];
557
            } else {
558
                $val = $key;
559
            }
560
561 4
            $tree = array($val => $tree);
562
        }
563 4
        if (!is_array($tree)) {
564
            return [];
565
        }
566 4
        return $tree;
567
    }
568
}
569