Completed
Push — master ( 3d203f...f18ebe )
by Lars
02:19
created

Result   F

Complexity

Total Complexity 215

Size/Duplication

Total Lines 1556
Duplicated Lines 14.01 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 71.51%

Importance

Changes 0
Metric Value
wmc 215
lcom 1
cbo 6
dl 218
loc 1556
ccs 344
cts 481
cp 0.7151
rs 0.8
c 0
b 0
f 0

56 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 4 1
B __construct() 0 38 6
A __invoke() 0 8 2
A __toString() 0 4 1
D cast() 10 76 24
A count() 0 4 1
A current() 0 4 1
A key() 0 4 1
A next() 0 4 1
A rewind() 0 6 2
B seek() 0 28 9
A valid() 0 4 1
A fetch() 0 17 5
A fetchAll() 0 17 5
A fetchAllArray() 18 18 3
A fetchAllArrayy() 18 18 3
A fetchAllArrayyYield() 0 17 3
A fetchAllColumn() 0 12 1
A fetchAllObject() 0 11 2
C fetchAllYield() 0 47 12
A fetchArray() 0 21 5
A fetchArrayPair() 0 20 4
A fetchArrayy() 0 17 5
B fetchCallable() 0 30 7
B fetchColumn() 24 46 10
A fetchFields() 0 22 3
A fetchGroups() 26 26 5
C fetchObject() 49 49 12
A fetchPairs() 26 26 5
A fetchTranspose() 0 26 5
C fetchYield() 47 47 12
B fetch_assoc() 0 37 7
B fetch_fields() 0 47 9
A first() 0 8 1
A free() 0 21 4
A get() 0 4 1
A getAll() 0 4 1
A getAllColumn() 0 4 1
A getArray() 0 4 1
A getArrayy() 0 4 1
A getColumn() 0 11 1
A getDefaultResultType() 0 4 1
A getObject() 0 4 1
A getYield() 0 4 1
A is_empty() 0 4 1
A json() 0 6 1
A last() 0 8 1
A map() 0 6 1
A num_rows() 0 4 1
A offsetExists() 0 4 3
A offsetGet() 0 8 2
A offsetSet() 0 3 1
A offsetUnset() 0 3 1
A reset() 0 16 4
A setDefaultResultType() 0 14 5
B slice() 0 29 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Result often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Result, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace voku\db;
6
7
use Arrayy\Arrayy;
8
use Symfony\Component\PropertyAccess\PropertyAccess;
9
use voku\helper\UTF8;
10
11
/**
12
 * Result: This class can handle the results from the "DB"-class.
13
 */
14
final class Result implements \Countable, \SeekableIterator, \ArrayAccess
15
{
16
    const MYSQL_TYPE_BIT = 16;
17
18
    const MYSQL_TYPE_BLOB = 252;
19
20
    const MYSQL_TYPE_DATE = 10;
21
22
    const MYSQL_TYPE_DATETIME = 12;
23
24
    const MYSQL_TYPE_DECIMAL = 0;
25
26
    const MYSQL_TYPE_DOUBLE = 5;
27
28
    const MYSQL_TYPE_ENUM = 247;
29
30
    const MYSQL_TYPE_FLOAT = 4;
31
32
    const MYSQL_TYPE_GEOMETRY = 255;
33
34
    const MYSQL_TYPE_INT24 = 9;
35
36
    const MYSQL_TYPE_JSON = 245;
37
38
    const MYSQL_TYPE_LONG = 3;
39
40
    const MYSQL_TYPE_LONGLONG = 8;
41
42
    const MYSQL_TYPE_LONG_BLOB = 251;
43
44
    const MYSQL_TYPE_MEDIUM_BLOB = 250;
45
46
    const MYSQL_TYPE_NEWDATE = 14;
47
48
    const MYSQL_TYPE_NEWDECIMAL = 246;
49
50
    const MYSQL_TYPE_NULL = 6;
51
52
    const MYSQL_TYPE_SET = 248;
53
54
    const MYSQL_TYPE_SHORT = 2;
55
56
    const MYSQL_TYPE_STRING = 254;
57
58
    const MYSQL_TYPE_TIME = 11;
59
60
    const MYSQL_TYPE_TIMESTAMP = 7;
61
62
    const MYSQL_TYPE_TINY = 1;
63
64
    const MYSQL_TYPE_TINY_BLOB = 249;
65
66
    const MYSQL_TYPE_VARCHAR = 15;
67
68
    const MYSQL_TYPE_VAR_STRING = 253;
69
70
    const MYSQL_TYPE_YEAR = 13;
71
72
    const RESULT_TYPE_ARRAY = 'array';
73
74
    const RESULT_TYPE_ARRAYY = 'Arrayy';
75
76
    const RESULT_TYPE_OBJECT = 'object';
77
78
    const RESULT_TYPE_YIELD = 'yield';
79
80
    /**
81
     * @var int
82
     */
83
    public $num_rows;
84
85
    /**
86
     * @var string
87
     */
88
    public $sql;
89
90
    /**
91
     * @var \Doctrine\DBAL\Statement|\mysqli_result
92
     */
93
    private $_result;
94
95
    /**
96
     * @var int
97
     */
98
    private $current_row;
99
100
    /**
101
     * @var \Closure|null
102
     */
103
    private $_mapper;
104
105
    /**
106
     * @var string
107
     */
108
    private $_default_result_type = self::RESULT_TYPE_OBJECT;
109
110
    /**
111
     * @var \mysqli_stmt|null
112
     */
113
    private $doctrineMySQLiStmt;
114
115
    /**
116
     * @var \Doctrine\DBAL\Driver\PDOStatement|null
117
     */
118
    private $doctrinePdoStmt;
119
120
    /**
121
     * @var int
122
     */
123
    private $doctrinePdoStmtDataSeekFake = 0;
124
125
    /**
126
     * @var bool
127
     */
128
    private $doctrinePdoStmtDataSeekInit = false;
129
130
    /**
131
     * @var array
132
     */
133
    private $doctrinePdoStmtDataSeekFakeCache = [];
134
135
    /**
136
     * Result constructor.
137
     *
138
     * @param string                                  $sql
139
     * @param \Doctrine\DBAL\Statement|\mysqli_result $result
140
     * @param \Closure                                $mapper Optional callback mapper for the "fetchCallable()" method
141
     */
142 101
    public function __construct(string $sql, $result, \Closure $mapper = null)
143
    {
144 101
        $this->sql = $sql;
145
146
        if (
147 101
            !$result instanceof \mysqli_result
148
            &&
149 101
            !$result instanceof \Doctrine\DBAL\Statement
150
        ) {
151
            throw new \InvalidArgumentException('$result must be ' . \mysqli_result::class . ' or ' . \Doctrine\DBAL\Statement::class . ' !');
152
        }
153
154 101
        $this->_result = $result;
155
156 101
        if ($this->_result instanceof \Doctrine\DBAL\Statement) {
157
            $doctrineDriver = $this->_result->getWrappedStatement();
158
159
            if ($doctrineDriver instanceof \Doctrine\DBAL\Driver\PDOStatement) {
160
                $this->doctrinePdoStmt = $doctrineDriver;
161
            }
162
163
            if ($doctrineDriver instanceof \Doctrine\DBAL\Driver\Mysqli\MysqliStatement) {
164
                // try to get the mysqli driver from doctrine
165
                $reflectionTmp = new \ReflectionClass($doctrineDriver);
166
                $propertyTmp = $reflectionTmp->getProperty('_stmt');
167
                $propertyTmp->setAccessible(true);
168
                $this->doctrineMySQLiStmt = $propertyTmp->getValue($doctrineDriver);
169
            }
170
171
            $this->num_rows = $this->_result->rowCount();
172
        } else {
173 101
            $this->num_rows = (int) $this->_result->num_rows;
174
        }
175
176 101
        $this->current_row = 0;
177
178 101
        $this->_mapper = $mapper;
179 101
    }
180
181
    /**
182
     * __destruct
183
     */
184 100
    public function __destruct()
185
    {
186 100
        $this->free();
187 100
    }
188
189
    /**
190
     * Runs a user-provided callback with the MySQLi_Result object given as
191
     * argument and returns the result, or returns the MySQLi_Result object if
192
     * called without an argument.
193
     *
194
     * @param callable $callback User-provided callback (optional)
195
     *
196
     * @return \Doctrine\DBAL\Statement|mixed|\mysqli_result
197
     */
198 2
    public function __invoke(callable $callback = null)
199
    {
200 2
        if ($callback !== null) {
201 2
            return $callback($this->_result);
202
        }
203
204 1
        return $this->_result;
205
    }
206
207
    /**
208
     * Get the current "num_rows" as string.
209
     *
210
     * @return string
211
     */
212
    public function __toString()
213
    {
214
        return (string) $this->num_rows;
215
    }
216
217
    /**
218
     * Cast data into int, float or string.
219
     *
220
     * <p>
221
     *   <br />
222
     *   INFO: install / use "mysqlnd"-driver for better performance
223
     * </p>
224
     *
225
     * @param array|object $data
226
     *
227
     * @return array|false|object
228
     *                            <p><strong>false</strong> on error</p>
229
     */
230 70
    private function &cast(&$data)
231
    {
232
        if (
233 70
            !$this->doctrinePdoStmt // pdo only have limited support for types, so we try to improve it
234
            &&
235 70
            Helper::isMysqlndIsUsed()
236
        ) {
237 70
            return $data;
238
        }
239
240
        // init
241
        static $FIELDS_CACHE = [];
242
        static $TYPES_CACHE = [];
243
244
        $result_hash = \spl_object_hash($this->_result);
245
        if (!isset($FIELDS_CACHE[$result_hash])) {
246
            $FIELDS_CACHE[$result_hash] = $this->fetch_fields();
247
        }
248
249
        if (
250
            !isset($FIELDS_CACHE[$result_hash])
251
            ||
252
            $FIELDS_CACHE[$result_hash] === false
253
        ) {
254
            /** @noinspection PhpUnnecessaryLocalVariableInspection */
255
            $dataTmp = false;
256
257
            return $dataTmp;
258
        }
259
260
        if (!isset($TYPES_CACHE[$result_hash])) {
261
            foreach ($FIELDS_CACHE[$result_hash] as $field) {
262
                switch ($field->type) {
263
                    case self::MYSQL_TYPE_BIT:
264
                        $TYPES_CACHE[$result_hash][$field->name] = 'boolean';
265
266
                        break;
267
                    case self::MYSQL_TYPE_TINY:
268
                    case self::MYSQL_TYPE_SHORT:
269
                    case self::MYSQL_TYPE_LONG:
270
                    case self::MYSQL_TYPE_LONGLONG:
271
                    case self::MYSQL_TYPE_INT24:
272
                        $TYPES_CACHE[$result_hash][$field->name] = 'integer';
273
274
                        break;
275
                    case self::MYSQL_TYPE_DOUBLE:
276
                    case self::MYSQL_TYPE_FLOAT:
277
                        $TYPES_CACHE[$result_hash][$field->name] = 'float';
278
279
                        break;
280
                    case self::MYSQL_TYPE_DECIMAL: // INFO: DECIMAL is a "string"-format for numbers
281
                    case self::MYSQL_TYPE_NEWDECIMAL:
282
                    default:
283
                        $TYPES_CACHE[$result_hash][$field->name] = 'string';
284
285
                        break;
286
                }
287
            }
288
        }
289
290
        if (\is_array($data)) {
291 View Code Duplication
            foreach ($TYPES_CACHE[$result_hash] as $type_name => $type) {
292
                if (isset($data[$type_name])) {
293
                    \settype($data[$type_name], $type);
294
                }
295
            }
296
        } elseif (\is_object($data)) {
297 View Code Duplication
            foreach ($TYPES_CACHE[$result_hash] as $type_name => $type) {
298
                if (isset($data->{$type_name})) {
299
                    \settype($data->{$type_name}, $type);
300
                }
301
            }
302
        }
303
304
        return $data;
305
    }
306
307
    /**
308
     * Countable interface implementation.
309
     *
310
     * @return int The number of rows in the result
311
     */
312 2
    public function count(): int
313
    {
314 2
        return $this->num_rows;
315
    }
316
317
    /**
318
     * Iterator interface implementation.
319
     *
320
     * @return mixed The current element
321
     */
322 5
    public function current()
323
    {
324 5
        return $this->fetchCallable($this->current_row);
325
    }
326
327
    /**
328
     * Iterator interface implementation.
329
     *
330
     * @return int The current element key (row index; zero-based)
331
     */
332 1
    public function key(): int
333
    {
334 1
        return $this->current_row;
335
    }
336
337
    /**
338
     * Iterator interface implementation.
339
     *
340
     * @return void
341
     */
342 5
    public function next()
343
    {
344 5
        $this->current_row++;
345 5
    }
346
347
    /**
348
     * Iterator interface implementation.
349
     *
350
     * @param int $row Row position to rewind to; defaults to 0
351
     *
352
     * @return void
353
     */
354 12
    public function rewind($row = 0)
355
    {
356 12
        if ($this->seek($row)) {
357 10
            $this->current_row = $row;
358
        }
359 12
    }
360
361
    /**
362
     * Moves the internal pointer to the specified row position.
363
     *
364
     * @param int $row <p>Row position; zero-based and set to 0 by default</p>
365
     *
366
     * @return bool
367
     *              <p>true on success, false otherwise</p>
368
     */
369 20
    public function seek($row = 0): bool
370
    {
371 20
        if (\is_int($row) && $row >= 0 && $row < $this->num_rows) {
372
            if (
373 16
                $this->doctrineMySQLiStmt
374
                &&
375 16
                $this->doctrineMySQLiStmt instanceof \mysqli_stmt
376
            ) {
377
                $this->doctrineMySQLiStmt->data_seek($row);
378
379
                return true;
380
            }
381
382
            if (
383 16
                $this->doctrinePdoStmt
384
                &&
385 16
                $this->doctrinePdoStmt instanceof \Doctrine\DBAL\Driver\PDOStatement
386
            ) {
387
                return true;
388
            }
389
390 16
            if ($this->_result instanceof \mysqli_result) {
391 16
                return \mysqli_data_seek($this->_result, $row);
392
            }
393
        }
394
395 4
        return false;
396
    }
397
398
    /**
399
     * Iterator interface implementation.
400
     *
401
     * @return bool
402
     *              <p>true if the current index is valid, false otherwise</p>
403
     */
404 5
    public function valid(): bool
405
    {
406 5
        return $this->current_row < $this->num_rows;
407
    }
408
409
    /**
410
     * Fetch.
411
     *
412
     * <p>
413
     *   <br />
414
     *   INFO: this will return an object by default, not an array<br />
415
     *   and you can change the behaviour via "Result->setDefaultResultType()"
416
     * </p>
417
     *
418
     * @param bool $reset optional <p>Reset the \mysqli_result counter.</p>
419
     *
420
     * @return array|false|object
421
     *                            <p><strong>false</strong> on error</p>
422
     */
423 6
    public function &fetch(bool $reset = false)
424
    {
425
        // init
426 6
        $return = false;
427
428 6
        if ($this->_default_result_type === self::RESULT_TYPE_OBJECT) {
429 6
            $return = $this->fetchObject('', null, $reset);
430 6
        } elseif ($this->_default_result_type === self::RESULT_TYPE_ARRAY) {
431 6
            $return = $this->fetchArray($reset);
432
        } elseif ($this->_default_result_type === self::RESULT_TYPE_ARRAYY) {
433
            $return = $this->fetchArrayy($reset);
434
        } elseif ($this->_default_result_type === self::RESULT_TYPE_YIELD) {
435
            $return = $this->fetchYield('', null, $reset);
436
        }
437
438 6
        return $return;
439
    }
440
441
    /**
442
     * Fetch all results.
443
     *
444
     * <p>
445
     *   <br />
446
     *   INFO: this will return an object by default, not an array<br />
447
     *   and you can change the behaviour via "Result->setDefaultResultType()"
448
     * </p>
449
     *
450
     * @return array
451
     */
452 6
    public function &fetchAll(): array
453
    {
454
        // init
455 6
        $return = [];
456
457 6
        if ($this->_default_result_type === self::RESULT_TYPE_OBJECT) {
458 6
            $return = $this->fetchAllObject();
459 3
        } elseif ($this->_default_result_type === self::RESULT_TYPE_ARRAY) {
460 3
            $return = $this->fetchAllArray();
461
        } elseif ($this->_default_result_type === self::RESULT_TYPE_ARRAYY) {
462
            $return = $this->fetchAllArrayy();
463
        } elseif ($this->_default_result_type === self::RESULT_TYPE_YIELD) {
464
            $return = $this->fetchAllYield();
465
        }
466
467 6
        return $return;
468
    }
469
470
    /**
471
     * Fetch all results as array.
472
     *
473
     * @return array
474
     */
475 19 View Code Duplication
    public function &fetchAllArray(): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
476
    {
477
        // init
478 19
        $data = [];
479
480 19
        if ($this->is_empty()) {
481
            return $data;
482
        }
483
484 19
        $this->reset();
485
486
        /** @noinspection PhpAssignmentInConditionInspection */
487 19
        while ($row = $this->fetch_assoc()) {
488 19
            $data[] = $this->cast($row);
489
        }
490
491 19
        return $data;
492
    }
493
494
    /**
495
     * Fetch all results as "Arrayy"-object.
496
     *
497
     * @return Arrayy
498
     */
499 10 View Code Duplication
    public function &fetchAllArrayy(): Arrayy
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
500
    {
501
        // init
502 10
        $arrayy = Arrayy::create();
503
504 10
        if ($this->is_empty()) {
505
            return $arrayy;
506
        }
507
508 10
        $this->reset();
509
510
        /** @noinspection PhpAssignmentInConditionInspection */
511 10
        while ($row = $this->fetch_assoc()) {
512 10
            $arrayy[] = $this->cast($row);
513
        }
514
515 10
        return $arrayy;
516
    }
517
518
    /**
519
     * Fetch all results as "Arrayy"-object.
520
     *
521
     * @return Arrayy
522
     */
523 15
    public function fetchAllArrayyYield(): Arrayy
524
    {
525 15
        if ($this->is_empty()) {
526
            return Arrayy::create([]);
527
        }
528
529 15
        return Arrayy::createFromGeneratorFunction(
530
            function () {
531 15
                $this->reset();
532
533
                /** @noinspection PhpAssignmentInConditionInspection */
534 15
                while ($row = $this->fetch_assoc()) {
535 15
                    yield $this->cast($row);
536
                }
537 15
            }
538
        );
539
    }
540
541
    /**
542
     * Fetch a single column as an 1-dimension array.
543
     *
544
     * @param string $column
545
     * @param bool   $skipNullValues <p>Skip "NULL"-values. | default: false</p>
546
     *
547
     * @return array
548
     *               <p>Return an empty array if the "$column" wasn't found</p>
549
     */
550 5
    public function fetchAllColumn(string $column, bool $skipNullValues = false): array
551
    {
552 5
        $return = $this->fetchColumn(
553 5
            $column,
554 5
            $skipNullValues,
555 5
            true
556
        );
557
558 5
        \assert(\is_array($return));
559
560 5
        return $return;
561
    }
562
563
    /**
564
     * Fetch all results as array with objects.
565
     *
566
     * @param object|string $class  <p>
567
     *                              <strong>string</strong>: create a new object (with optional constructor
568
     *                              parameter)<br>
569
     *                              <strong>object</strong>: use a object and fill the the data into
570
     *                              </p>
571
     * @param array|null    $params optional
572
     *                              <p>
573
     *                              An array of parameters to pass to the constructor, used if $class is a
574
     *                              string.
575
     *                              </p>
576
     *
577
     * @return object[]
578
     */
579 9
    public function &fetchAllObject($class = '', array $params = null): array
580
    {
581
        // init
582 9
        $data = [];
583
584 9
        foreach ($this->fetchAllYield($class, $params) as $object) {
585 9
            $data[] = $object;
586
        }
587
588 9
        return $data;
589
    }
590
591
    /**
592
     * Fetch all results as "\Generator" via yield.
593
     *
594
     * @param object|string $class  <p>
595
     *                              <strong>string</strong>: create a new object (with optional constructor
596
     *                              parameter)<br>
597
     *                              <strong>object</strong>: use a object and fill the the data into
598
     *                              </p>
599
     * @param array|null    $params optional
600
     *                              <p>
601
     *                              An array of parameters to pass to the constructor, used if $class is a
602
     *                              string.
603
     *                              </p>
604
     *
605
     * @return \Generator
606
     */
607 17
    public function &fetchAllYield($class = '', array $params = null): \Generator
608
    {
609 17
        if ($this->is_empty()) {
610
            return;
611
        }
612
613
        // init
614 17
        $this->reset();
615
616
        // fallback
617 17
        if (!$class || $class === 'stdClass') {
618 17
            $class = \stdClass::class;
619
        }
620
621 17
        if (\is_object($class)) {
622
            $classTmpOrig = $class;
623 17
        } elseif ($class && $params) {
624 3
            $reflectorTmp = new \ReflectionClass($class);
625 3
            $classTmpOrig = $reflectorTmp->newInstanceArgs($params);
626
        } else {
627 17
            $classTmpOrig = new $class();
628
        }
629
630 17
        if ($class === \stdClass::class) {
631 17
            $propertyAccessor = null;
632
        } else {
633 6
            $propertyAccessor = PropertyAccess::createPropertyAccessor();
634
        }
635
636
        /** @noinspection PhpAssignmentInConditionInspection */
637 17
        while ($row = $this->fetch_assoc()) {
638 17
            $classTmp = clone $classTmpOrig;
639
640 17
            $row = $this->cast($row);
641 17
            if ($row !== false) {
642 17
                foreach ($row as $key => $value) {
643 17
                    if ($class === \stdClass::class) {
644 17
                        $classTmp->{$key} = $value;
645
                    } else {
646 17
                        $propertyAccessor->setValue($classTmp, $key, $value);
647
                    }
648
                }
649
            }
650
651 17
            yield $classTmp;
652
        }
653 17
    }
654
655
    /**
656
     * Fetch as array.
657
     *
658
     * @param bool $reset
659
     *
660
     * @return array|false
661
     *                     <p><strong>false</strong> on error</p>
662
     */
663 30
    public function fetchArray(bool $reset = false)
664
    {
665 30
        if ($reset) {
666 3
            $this->reset();
667
        }
668
669 30
        $row = $this->fetch_assoc();
670 30
        if ($row) {
671 27
            $return = $this->cast($row);
672
673 27
            \assert(\is_array($return));
674
675 27
            return $return;
676
        }
677
678 5
        if ($row === null || $row === false) {
679 5
            return [];
680
        }
681
682
        return false;
683
    }
684
685
    /**
686
     * Fetch data as a key/value pair array.
687
     *
688
     * <p>
689
     *   <br />
690
     *   INFO: both "key" and "value" must exists in the fetched data
691
     *   the key will be the new key of the result-array
692
     *   <br /><br />
693
     * </p>
694
     *
695
     * e.g.:
696
     * <code>
697
     *    fetchArrayPair('some_id', 'some_value');
698
     *    // array(127 => 'some value', 128 => 'some other value')
699
     * </code>
700
     *
701
     * @param string $key
702
     * @param string $value
703
     *
704
     * @return Arrayy
705
     */
706 3
    public function fetchArrayPair(string $key, string $value): Arrayy
707
    {
708
        // init
709 3
        $arrayPair = new Arrayy();
710 3
        $data = $this->fetchAllArrayyYield();
711
712 3
        foreach ($data as $_row) {
713
            if (
714 3
                \array_key_exists($key, $_row)
715
                &&
716 3
                \array_key_exists($value, $_row)
717
            ) {
718 3
                $_key = $_row[$key];
719 3
                $_value = $_row[$value];
720 3
                $arrayPair[$_key] = $_value;
721
            }
722
        }
723
724 3
        return $arrayPair;
725
    }
726
727
    /**
728
     * Fetch as "Arrayy"-object.
729
     *
730
     * @param bool $reset optional <p>Reset the \mysqli_result counter.</p>
731
     *
732
     * @return Arrayy|false
733
     *                      <p><strong>false</strong> on error</p>
734
     */
735 6
    public function fetchArrayy(bool $reset = false)
736
    {
737 6
        if ($reset) {
738
            $this->reset();
739
        }
740
741 6
        $row = $this->fetch_assoc();
742 6
        if ($row) {
743 3
            return Arrayy::create($this->cast($row));
744
        }
745
746 3
        if ($row === null || $row === false) {
747 3
            return Arrayy::create();
748
        }
749
750
        return false;
751
    }
752
753
    /**
754
     * Fetches a row or a single column within a row. Returns null if there are
755
     * no more rows in the result.
756
     *
757
     * @param int    $row    The row number (optional)
758
     * @param string $column The column name (optional)
759
     *
760
     * @return mixed An associative array or a scalar value
761
     */
762 14
    public function fetchCallable(int $row = null, string $column = null)
763
    {
764 14
        if (!$this->num_rows) {
765 2
            return null;
766
        }
767
768 12
        if ($row !== null) {
769 11
            $this->seek($row);
770
        }
771
772 12
        $rows = $this->fetch_assoc();
773
774 12
        if ($column) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $column of type null|string is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
775
            if (
776 5
                \is_array($rows)
777
                &&
778 5
                isset($rows[$column])
779
            ) {
780 5
                return $rows[$column];
781
            }
782
783
            return null;
784
        }
785
786 11
        if (\is_callable($this->_mapper)) {
787 1
            return \call_user_func($this->_mapper, $rows);
788
        }
789
790 11
        return $rows;
791
    }
792
793
    /**
794
     * Fetch a single column as string (or as 1-dimension array).
795
     *
796
     * @param string $column
797
     * @param bool   $skipNullValues <p>Skip "NULL"-values. | default: true</p>
798
     * @param bool   $asArray        <p>Get all values and not only the last one. | default: false</p>
799
     *
800
     * @return array|string
801
     *                      <p>Return a empty string or an empty array if the "$column" wasn't found, depend on
802
     *                      "$asArray"</p>
803
     */
804 9
    public function &fetchColumn(
805
        string $column = '',
806
        bool $skipNullValues = true,
807
        bool $asArray = false
808
    ) {
809 9
        if (!$asArray) {
810
            // init
811 7
            $columnData = '';
812
813 7
            $data = $this->fetchAllArrayy()->reverse()->getArray();
814 7 View Code Duplication
            foreach ($data as &$_row) {
815 7
                if ($skipNullValues) {
816 7
                    if (!isset($_row[$column])) {
817 7
                        continue;
818
                    }
819 3
                } elseif (!\array_key_exists($column, $_row)) {
820 3
                    break;
821
                }
822
823 7
                $columnData = $_row[$column];
824
825 7
                break;
826
            }
827
828 7
            return $columnData;
829
        }
830
831
        // -- return as array -->
832
833
        // init
834 5
        $columnData = [];
835
836 5 View Code Duplication
        foreach ($this->fetchAllYield() as $_row) {
837 5
            if ($skipNullValues) {
838 3
                if (!isset($_row->{$column})) {
839 3
                    continue;
840
                }
841 5
            } elseif (!\property_exists($_row, $column)) {
842 3
                break;
843
            }
844
845 5
            $columnData[] = $_row->{$column};
846
        }
847
848 5
        return $columnData;
849
    }
850
851
    /**
852
     * Return rows of field information in a result set.
853
     *
854
     * @param bool $as_array Return each field info as array; defaults to false
855
     *
856
     * @return array
857
     *               <p>Array of field information each as an associative array.</p>
858
     */
859 1
    public function &fetchFields(bool $as_array = false): array
860
    {
861 1
        $fields = $this->fetch_fields();
862 1
        if ($fields === false) {
863
            $fields = [];
864
865
            return $fields;
866
        }
867
868 1
        if ($as_array) {
869 1
            $fields = \array_map(
870
                static function ($object) {
871 1
                    return (array) $object;
872 1
                },
873 1
                $fields
874
            );
875
876 1
            return $fields;
877
        }
878
879
        return $fields;
880
    }
881
882
    /**
883
     * Returns all rows at once as a grouped array of scalar values or arrays.
884
     *
885
     * @param string $group  The column name to use for grouping
886
     * @param string $column The column name to use as values (optional)
887
     *
888
     * @return array
889
     *               <p>A grouped array of scalar values or arrays.</p>
890
     */
891 1 View Code Duplication
    public function &fetchGroups(string $group, string $column = null): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
892
    {
893
        // init
894 1
        $groups = [];
895 1
        $pos = $this->current_row;
896
897 1
        foreach ($this->fetchAllArrayyYield() as $row) {
898 1
            if (!\array_key_exists($group, $row)) {
899
                continue;
900
            }
901
902 1
            if ($column !== null) {
903 1
                if (!\array_key_exists($column, $row)) {
904
                    continue;
905
                }
906
907 1
                $groups[$row[$group]][] = $row[$column];
908
            } else {
909 1
                $groups[$row[$group]][] = $row;
910
            }
911
        }
912
913 1
        $this->rewind($pos);
914
915 1
        return $groups;
916
    }
917
918
    /**
919
     * Fetch as object.
920
     *
921
     * @param object|string $class  <p>
922
     *                              <strong>string</strong>: create a new object (with optional constructor
923
     *                              parameter)<br>
924
     *                              <strong>object</strong>: use a object and fill the the data into
925
     *                              </p>
926
     * @param array|null    $params optional
927
     *                              <p>
928
     *                              An array of parameters to pass to the constructor, used if $class is a
929
     *                              string.
930
     *                              </p>
931
     * @param bool          $reset  optional <p>Reset the \mysqli_result counter.</p>
932
     *
933
     * @return false|object
934
     *                      <p><strong>false</strong> on error</p>
935
     */
936 21 View Code Duplication
    public function &fetchObject(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
937
        $class = '',
938
        array $params = null,
939
        bool $reset = false
940
    ) {
941 21
        if ($reset) {
942 9
            $this->reset();
943
        }
944
945
        // fallback
946 21
        if (!$class || $class === 'stdClass') {
947 21
            $class = \stdClass::class;
948
        }
949
950 21
        $row = $this->fetch_assoc();
951 21
        $row = $row ? $this->cast($row) : false;
952
953 21
        if (!$row) {
954
            /** @noinspection PhpUnnecessaryLocalVariableInspection */
955 3
            $dataTmp = false;
956
957 3
            return $dataTmp;
958
        }
959
960 21
        if (\is_object($class)) {
961 3
            $classTmp = $class;
962 21
        } elseif ($class && $params) {
963 3
            $reflectorTmp = new \ReflectionClass($class);
964 3
            $classTmp = $reflectorTmp->newInstanceArgs($params);
965
        } else {
966 21
            $classTmp = new $class();
967
        }
968
969 21
        if ($class === \stdClass::class) {
970 21
            $propertyAccessor = null;
971
        } else {
972 3
            $propertyAccessor = PropertyAccess::createPropertyAccessor();
973
        }
974
975 21
        foreach ($row as $key => &$value) {
976 21
            if ($class === \stdClass::class) {
977 21
                $classTmp->{$key} = $value;
978
            } else {
979 21
                $propertyAccessor->setValue($classTmp, $key, $value);
980
            }
981
        }
982
983 21
        return $classTmp;
984
    }
985
986
    /**
987
     * Returns all rows at once as key-value pairs.
988
     *
989
     * @param string $key    The column name to use as keys
990
     * @param string $column The column name to use as values (optional)
991
     *
992
     * @return array
993
     *               <p>An array of key-value pairs.</p>
994
     */
995 1 View Code Duplication
    public function fetchPairs(string $key, string $column = null): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
996
    {
997
        // init
998 1
        $pairs = [];
999 1
        $pos = $this->current_row;
1000
1001 1
        foreach ($this->fetchAllArrayyYield() as $row) {
1002 1
            if (!\array_key_exists($key, $row)) {
1003
                continue;
1004
            }
1005
1006 1
            if ($column !== null) {
1007 1
                if (!\array_key_exists($column, $row)) {
1008
                    continue;
1009
                }
1010
1011 1
                $pairs[$row[$key]] = $row[$column];
1012
            } else {
1013 1
                $pairs[$row[$key]] = $row;
1014
            }
1015
        }
1016
1017 1
        $this->rewind($pos);
1018
1019 1
        return $pairs;
1020
    }
1021
1022
    /**
1023
     * Returns all rows at once, transposed as an array of arrays. Instead of
1024
     * returning rows of columns, this method returns columns of rows.
1025
     *
1026
     * @param string $column The column name to use as keys (optional)
1027
     *
1028
     * @return array
1029
     *               <p>A transposed array of arrays</p>
1030
     */
1031 1
    public function fetchTranspose(string $column = null)
1032
    {
1033
        // init
1034 1
        $keys = $column !== null ? $this->fetchAllColumn($column) : [];
1035 1
        $rows = [];
1036 1
        $pos = $this->current_row;
1037
1038 1
        foreach ($this->fetchAllYield() as $row) {
1039 1
            foreach ($row as $key => &$value) {
1040 1
                $rows[$key][] = $value;
1041
            }
1042
        }
1043
1044 1
        $this->rewind($pos);
1045
1046 1
        if (empty($keys)) {
1047 1
            return $rows;
1048
        }
1049
1050 1
        return \array_map(
1051
            static function ($values) use ($keys) {
1052 1
                return \array_combine($keys, $values);
1053 1
            },
1054 1
            $rows
1055
        );
1056
    }
1057
1058
    /**
1059
     * Fetch as "\Generator" via yield.
1060
     *
1061
     * @param object|string $class  <p>
1062
     *                              <strong>string</strong>: create a new object (with optional constructor
1063
     *                              parameter)<br>
1064
     *                              <strong>object</strong>: use a object and fill the the data into
1065
     *                              </p>
1066
     * @param array|null    $params optional
1067
     *                              <p>
1068
     *                              An array of parameters to pass to the constructor, used if $class is a
1069
     *                              string.
1070
     *                              </p>
1071
     * @param bool          $reset  optional <p>Reset the \mysqli_result counter.</p>
1072
     *
1073
     * @return \Generator
1074
     */
1075 4 View Code Duplication
    public function fetchYield(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1076
        $class = '',
1077
        array $params = null,
1078
        bool $reset = false
1079
    ): \Generator {
1080 4
        if ($reset) {
1081
            $this->reset();
1082
        }
1083
1084
        // fallback
1085 4
        if (!$class || $class === 'stdClass') {
1086 3
            $class = \stdClass::class;
1087
        }
1088
1089 4
        if (\is_object($class)) {
1090
            $classTmp = $class;
1091 4
        } elseif ($class && $params) {
1092
            $reflectorTmp = new \ReflectionClass($class);
1093
            $classTmp = $reflectorTmp->newInstanceArgs($params);
1094
        } else {
1095 4
            $classTmp = new $class();
1096
        }
1097
1098 4
        $row = $this->fetch_assoc();
1099 4
        $row = $row ? $this->cast($row) : false;
1100
1101 4
        if (!$row) {
1102
            return;
1103
        }
1104
1105 4
        if ($class === \stdClass::class) {
1106 3
            $propertyAccessor = null;
1107
        } else {
1108 1
            $propertyAccessor = PropertyAccess::createPropertyAccessor();
1109
        }
1110
1111
        /** @noinspection AlterInForeachInspection */
1112 4
        foreach ($row as $key => &$value) {
1113 4
            if ($class === \stdClass::class) {
1114 3
                $classTmp->{$key} = $value;
1115
            } else {
1116 4
                $propertyAccessor->setValue($classTmp, $key, $value);
1117
            }
1118
        }
1119
1120 4
        yield $classTmp;
1121 4
    }
1122
1123
    /**
1124
     * @return mixed|null
1125
     */
1126 78
    private function fetch_assoc()
1127
    {
1128 78
        if ($this->_result instanceof \Doctrine\DBAL\Statement) {
1129
            if (
1130
                $this->doctrinePdoStmt
1131
                &&
1132
                $this->doctrinePdoStmt instanceof \Doctrine\DBAL\Driver\PDOStatement
1133
            ) {
1134
                if ($this->doctrinePdoStmtDataSeekInit === false) {
1135
                    $this->doctrinePdoStmtDataSeekInit = true;
1136
1137
                    $this->doctrinePdoStmtDataSeekFakeCache = $this->_result->fetchAll(\Doctrine\DBAL\FetchMode::ASSOCIATIVE);
1138
                }
1139
1140
                $return = ($this->doctrinePdoStmtDataSeekFakeCache[$this->doctrinePdoStmtDataSeekFake] ?? null);
1141
1142
                $this->doctrinePdoStmtDataSeekFake++;
1143
1144
                return $return;
1145
            }
1146
1147
            if (
1148
                $this->doctrineMySQLiStmt
1149
                &&
1150
                $this->doctrineMySQLiStmt instanceof \mysqli_stmt
1151
            ) {
1152
                return $this->_result->fetch(
1153
                    \Doctrine\DBAL\FetchMode::ASSOCIATIVE,
1154
                    0 // FETCH_ORI_NEXT
1155
                );
1156
            }
1157
1158
            return null;
1159
        }
1160
1161 78
        return \mysqli_fetch_assoc($this->_result);
1162
    }
1163
1164
    /**
1165
     * @return array|false
1166
     */
1167 1
    private function fetch_fields()
1168
    {
1169 1
        if ($this->_result instanceof \mysqli_result) {
1170 1
            return \mysqli_fetch_fields($this->_result);
1171
        }
1172
1173
        if ($this->doctrineMySQLiStmt) {
1174
            $metadataTmp = $this->doctrineMySQLiStmt->result_metadata();
1175
            if ($metadataTmp === false) {
1176
                return [];
1177
            }
1178
1179
            return $metadataTmp->fetch_fields();
1180
        }
1181
1182
        if ($this->doctrinePdoStmt) {
1183
            $fields = [];
1184
1185
            static $THIS_CLASS_TMP = null;
1186
            if ($THIS_CLASS_TMP === null) {
1187
                $THIS_CLASS_TMP = new \ReflectionClass(__CLASS__);
1188
            }
1189
1190
            $totalColumnsTmp = $this->doctrinePdoStmt->columnCount();
1191
            for ($counterTmp = 0; $counterTmp < $totalColumnsTmp; $counterTmp++) {
1192
                $metadataTmp = $this->doctrinePdoStmt->getColumnMeta($counterTmp);
1193
                $fieldTmp = new \stdClass();
1194
                foreach ($metadataTmp as $metadataTmpKey => $metadataTmpValue) {
1195
                    $fieldTmp->{$metadataTmpKey} = $metadataTmpValue;
1196
                }
1197
1198
                $typeNativeTmp = 'MYSQL_TYPE_' . $metadataTmp['native_type'];
1199
                $typeTmp = $THIS_CLASS_TMP->getConstant($typeNativeTmp);
1200
                if ($typeTmp) {
1201
                    $fieldTmp->type = $typeTmp;
1202
                } else {
1203
                    $fieldTmp->type = '';
1204
                }
1205
1206
                $fields[] = $fieldTmp;
1207
            }
1208
1209
            return $fields;
1210
        }
1211
1212
        return false;
1213
    }
1214
1215
    /**
1216
     * Returns the first row element from the result.
1217
     *
1218
     * @param string $column The column name to use as value (optional)
1219
     *
1220
     * @return mixed
1221
     *               <p>A row array or a single scalar value</p>
1222
     */
1223 3
    public function first(string $column = null)
1224
    {
1225 3
        $pos = $this->current_row;
1226 3
        $first = $this->fetchCallable(0, $column);
1227 3
        $this->rewind($pos);
1228
1229 3
        return $first;
1230
    }
1231
1232
    /**
1233
     * free the memory
1234
     */
1235 100
    public function free()
1236
    {
1237 100
        if ($this->_result instanceof \mysqli_result) {
1238
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
1239 100
            @\mysqli_free_result($this->_result);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1240
1241 100
            return true;
1242
        }
1243
1244
        if (
1245
            $this->doctrineMySQLiStmt
1246
            &&
1247
            $this->doctrineMySQLiStmt instanceof \mysqli_stmt
1248
        ) {
1249
            $this->doctrineMySQLiStmt->free_result();
1250
1251
            return true;
1252
        }
1253
1254
        return false;
1255
    }
1256
1257
    /**
1258
     * alias for "Result->fetch()"
1259
     *
1260
     * @return array|false|object
1261
     *                            <p><strong>false</strong> on error</p>
1262
     *
1263
     * @see Result::fetch()
1264
     */
1265 3
    public function get()
1266
    {
1267 3
        return $this->fetch();
1268
    }
1269
1270
    /**
1271
     * alias for "Result->fetchAll()"
1272
     *
1273
     * @return array
1274
     *
1275
     * @see Result::fetchAll()
1276
     */
1277 3
    public function getAll(): array
1278
    {
1279 3
        return $this->fetchAll();
1280
    }
1281
1282
    /**
1283
     * alias for "Result->fetchAllColumn()"
1284
     *
1285
     * @param string $column
1286
     * @param bool   $skipNullValues
1287
     *
1288
     * @return array
1289
     *
1290
     * @see Result::fetchAllColumn()
1291
     */
1292
    public function getAllColumn(string $column, bool $skipNullValues = false): array
1293
    {
1294
        return $this->fetchAllColumn($column, $skipNullValues);
1295
    }
1296
1297
    /**
1298
     * alias for "Result->fetchAllArray()"
1299
     *
1300
     * @return array
1301
     *
1302
     * @see Result::fetchAllArray()
1303
     */
1304 3
    public function getArray(): array
1305
    {
1306 3
        return $this->fetchAllArray();
1307
    }
1308
1309
    /**
1310
     * alias for "Result->fetchAllArrayy()"
1311
     *
1312
     * @return Arrayy
1313
     *
1314
     * @see Result::fetchAllArrayy()
1315
     */
1316
    public function getArrayy(): Arrayy
1317
    {
1318
        return $this->fetchAllArrayy();
1319
    }
1320
1321
    /**
1322
     * alias for "Result->fetchColumn()"
1323
     *
1324
     * @param string $column
1325
     * @param bool   $asArray
1326
     * @param bool   $skipNullValues
1327
     *
1328
     * @return array|string
1329
     *                      <p>Return a empty string or an empty array if the "$column" wasn't found, depend on
1330
     *                      "$asArray"</p>
1331
     *
1332
     * @see Result::fetchColumn()
1333
     */
1334 3
    public function getColumn(
1335
        string $column,
1336
        bool $skipNullValues = true,
1337
        bool $asArray = false
1338
    ) {
1339 3
        return $this->fetchColumn(
1340 3
            $column,
1341 3
            $skipNullValues,
1342 3
            $asArray
1343
        );
1344
    }
1345
1346
    /**
1347
     * @return string
1348
     */
1349 3
    public function getDefaultResultType(): string
1350
    {
1351 3
        return $this->_default_result_type;
1352
    }
1353
1354
    /**
1355
     * alias for "Result->fetchAllObject()"
1356
     *
1357
     * @return array of mysql-objects
1358
     *
1359
     * @see Result::fetchAllObject()
1360
     */
1361 3
    public function getObject(): array
1362
    {
1363 3
        return $this->fetchAllObject();
1364
    }
1365
1366
    /**
1367
     * alias for "Result->fetchAllYield()"
1368
     *
1369
     * @return \Generator
1370
     *
1371
     * @see Result::fetchAllYield()
1372
     */
1373 1
    public function getYield(): \Generator
1374
    {
1375 1
        return $this->fetchAllYield();
1376
    }
1377
1378
    /**
1379
     * Check if the result is empty.
1380
     *
1381
     * @return bool
1382
     */
1383 43
    public function is_empty(): bool
1384
    {
1385 43
        return !($this->num_rows > 0);
1386
    }
1387
1388
    /**
1389
     * Fetch all results as "json"-string.
1390
     *
1391
     * @return false|string
1392
     */
1393 3
    public function json()
1394
    {
1395 3
        $data = $this->fetchAllArray();
1396
1397 3
        return UTF8::json_encode($data);
1398
    }
1399
1400
    /**
1401
     * Returns the last row element from the result.
1402
     *
1403
     * @param string $column The column name to use as value (optional)
1404
     *
1405
     * @return mixed A row array or a single scalar value
1406
     */
1407 3
    public function last(string $column = null)
1408
    {
1409 3
        $pos = $this->current_row;
1410 3
        $last = $this->fetchCallable($this->num_rows - 1, $column);
1411 3
        $this->rewind($pos);
1412
1413 3
        return $last;
1414
    }
1415
1416
    /**
1417
     * Set the mapper...
1418
     *
1419
     * @param \Closure $callable
1420
     *
1421
     * @return $this
1422
     */
1423 1
    public function map(\Closure $callable): self
1424
    {
1425 1
        $this->_mapper = $callable;
1426
1427 1
        return $this;
1428
    }
1429
1430
    /**
1431
     * Alias of count(). Deprecated.
1432
     *
1433
     * @return int
1434
     *             <p>The number of rows in the result.</p>
1435
     */
1436 1
    public function num_rows(): int
1437
    {
1438 1
        return $this->count();
1439
    }
1440
1441
    /**
1442
     * ArrayAccess interface implementation.
1443
     *
1444
     * @param int $offset <p>Offset number</p>
1445
     *
1446
     * @return bool
1447
     *              <p>true if offset exists, false otherwise.</p>
1448
     */
1449 1
    public function offsetExists($offset): bool
1450
    {
1451 1
        return \is_int($offset) && $offset >= 0 && $offset < $this->num_rows;
1452
    }
1453
1454
    /**
1455
     * ArrayAccess interface implementation.
1456
     *
1457
     * @param int $offset Offset number
1458
     *
1459
     * @return mixed
1460
     */
1461 1
    public function offsetGet($offset)
1462
    {
1463 1
        if ($this->offsetExists($offset)) {
1464 1
            return $this->fetchCallable($offset);
1465
        }
1466
1467
        throw new \OutOfBoundsException("undefined offset (${offset})");
1468
    }
1469
1470
    /**
1471
     * ArrayAccess interface implementation. Not implemented by design.
1472
     *
1473
     * @param mixed $offset
1474
     * @param mixed $value
1475
     */
1476
    public function offsetSet($offset, $value)
1477
    {
1478
    }
1479
1480
    /**
1481
     * ArrayAccess interface implementation. Not implemented by design.
1482
     *
1483
     * @param mixed $offset
1484
     */
1485
    public function offsetUnset($offset)
1486
    {
1487
    }
1488
1489
    /**
1490
     * Reset the offset (data_seek) for the results.
1491
     *
1492
     * @return Result
1493
     */
1494 40
    public function reset(): self
1495
    {
1496 40
        $this->doctrinePdoStmtDataSeekFake = 0;
1497
1498 40
        if (!$this->is_empty()) {
1499 40
            if ($this->doctrineMySQLiStmt instanceof \mysqli_stmt) {
1500
                $this->doctrineMySQLiStmt->data_seek(0);
1501
            }
1502
1503 40
            if ($this->_result instanceof \mysqli_result) {
1504 40
                \mysqli_data_seek($this->_result, 0);
1505
            }
1506
        }
1507
1508 40
        return $this;
1509
    }
1510
1511
    /**
1512
     * You can set the default result-type to Result::RESULT_TYPE_*.
1513
     *
1514
     * INFO: used for "fetch()" and "fetchAll()"
1515
     *
1516
     * @param string $default_result_type
1517
     */
1518 6
    public function setDefaultResultType(string $default_result_type = self::RESULT_TYPE_OBJECT)
1519
    {
1520
        if (
1521 6
            $default_result_type === self::RESULT_TYPE_OBJECT
1522
            ||
1523 6
            $default_result_type === self::RESULT_TYPE_ARRAY
1524
            ||
1525
            $default_result_type === self::RESULT_TYPE_ARRAYY
1526
            ||
1527 6
            $default_result_type === self::RESULT_TYPE_YIELD
1528
        ) {
1529 6
            $this->_default_result_type = $default_result_type;
1530
        }
1531 6
    }
1532
1533
    /**
1534
     * @param int      $offset
1535
     * @param int|null $length
1536
     * @param bool     $preserve_keys
1537
     *
1538
     * @return array
1539
     */
1540 1
    public function &slice(
1541
        int $offset = 0,
1542
        int $length = null,
1543
        bool $preserve_keys = false
1544
    ): array {
1545
        // init
1546 1
        $slice = [];
1547
1548 1
        if ($offset < 0) {
1549 1
            if (\abs($offset) > $this->num_rows) {
1550 1
                $offset = 0;
1551
            } else {
1552 1
                $offset = $this->num_rows - (int) \abs($offset);
1553
            }
1554
        }
1555
1556 1
        $length = $length !== null ? (int) $length : $this->num_rows;
1557 1
        $n = 0;
1558 1
        for ($i = $offset; $i < $this->num_rows && $n < $length; $i++) {
1559 1
            if ($preserve_keys) {
1560 1
                $slice[$i] = $this->fetchCallable($i);
1561
            } else {
1562 1
                $slice[] = $this->fetchCallable($i);
1563
            }
1564 1
            ++$n;
1565
        }
1566
1567 1
        return $slice;
1568
    }
1569
}
1570