Completed
Push — master ( ae7a6b...04cd6f )
by Lars
01:51
created

Result::offsetSet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 2
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 2
1
<?php
2
3
namespace voku\db;
4
5
use Arrayy\Arrayy;
6
use Symfony\Component\PropertyAccess\PropertyAccess;
7
use voku\helper\Bootup;
8
use voku\helper\UTF8;
9
10
/**
11
 * Result: This class can handle the results from the "DB"-class.
12
 *
13
 * @package   voku\db
14
 */
15
final class Result implements \Countable, \SeekableIterator, \ArrayAccess
16
{
17
18
  /**
19
   * @var int
20
   */
21
  public $num_rows;
22
23
  /**
24
   * @var string
25
   */
26
  public $sql;
27
28
  /**
29
   * @var \mysqli_result
30
   */
31
  private $_result;
32
33
  /**
34
   * @var int
35
   */
36
  private $current_row;
37
38
  /**
39
   * @var \Closure|null
40
   */
41
  private $_mapper;
42
43
  /**
44
   * @var string
45
   */
46
  private $_default_result_type = 'object';
47
48
  /**
49
   * Result constructor.
50
   *
51
   * @param string         $sql
52
   * @param \mysqli_result $result
53
   * @param \Closure       $mapper Optional callback mapper for the "fetchCallable()" method
54
   */
55 64
  public function __construct($sql = '', \mysqli_result $result, $mapper = null)
56
  {
57 64
    $this->sql = $sql;
58
59 64
    $this->_result = $result;
60
61 64
    $this->current_row = 0;
62 64
    $this->num_rows = (int)$this->_result->num_rows;
63
64 64
    $this->_mapper = $mapper;
65 64
  }
66
67
  /**
68
   * __destruct
69
   */
70 63
  public function __destruct()
71
  {
72 63
    $this->free();
73 63
  }
74
75
  /**
76
   * Runs a user-provided callback with the MySQLi_Result object given as
77
   * argument and returns the result, or returns the MySQLi_Result object if
78
   * called without an argument.
79
   *
80
   * @param callable $callback User-provided callback (optional)
81
   *
82
   * @return mixed|\mysqli_result
83
   */
84 2
  public function __invoke($callback = null)
85
  {
86 2
    if (isset($callback)) {
87 2
      return call_user_func($callback, $this->_result);
88
    }
89
90 1
    return $this->_result;
91
  }
92
93
  /**
94
   * Get the current "num_rows" as string.
95
   *
96
   * @return string
97
   */
98
  public function __toString()
99
  {
100
    return (string)$this->num_rows;
101
  }
102
103
  /**
104
   * Cast data into int, float or string.
105
   *
106
   * <p>
107
   *   <br />
108
   *   INFO: install / use "mysqlnd"-driver for better performance
109
   * </p>
110
   *
111
   * @param array|object $data
112
   *
113
   * @return array|object|false <p><strong>false</strong> on error</p>
114
   */
115 46
  private function cast(&$data)
116
  {
117 46
    if (Helper::isMysqlndIsUsed() === true) {
118 46
      return $data;
119
    }
120
121
    // init
122
    if (Bootup::is_php('5.4')) {
123
      static $FIELDS_CACHE = array();
124
      static $TYPES_CACHE = array();
125
    } else {
126
      $FIELDS_CACHE = array();
127
      $TYPES_CACHE = array();
128
    }
129
130
    $result_hash = spl_object_hash($this->_result);
131
132
    if (!isset($FIELDS_CACHE[$result_hash])) {
133
      $FIELDS_CACHE[$result_hash] = \mysqli_fetch_fields($this->_result);
134
    }
135
136
    if ($FIELDS_CACHE[$result_hash] === false) {
137
      return false;
138
    }
139
140
    if (!isset($TYPES_CACHE[$result_hash])) {
141
      foreach ($FIELDS_CACHE[$result_hash] as $field) {
142
        switch ($field->type) {
143
          case 3:
144
            $TYPES_CACHE[$result_hash][$field->name] = 'int';
145
            break;
146
          case 4:
147
            $TYPES_CACHE[$result_hash][$field->name] = 'float';
148
            break;
149
          default:
150
            $TYPES_CACHE[$result_hash][$field->name] = 'string';
151
            break;
152
        }
153
      }
154
    }
155
156
    if (is_array($data) === true) {
157 View Code Duplication
      foreach ($TYPES_CACHE[$result_hash] as $type_name => $type) {
158
        if (isset($data[$type_name])) {
159
          settype($data[$type_name], $type);
160
        }
161
      }
162
    } elseif (is_object($data)) {
163 View Code Duplication
      foreach ($TYPES_CACHE[$result_hash] as $type_name => $type) {
164
        if (isset($data->{$type_name})) {
165
          settype($data->{$type_name}, $type);
166
        }
167
      }
168
    }
169
170
    return $data;
171
  }
172
173
  /**
174
   * Countable interface implementation.
175
   *
176
   * @return int The number of rows in the result
177
   */
178 2
  public function count()
179
  {
180 2
    return $this->num_rows;
181
  }
182
183
  /**
184
   * Iterator interface implementation.
185
   *
186
   * @return mixed The current element
187
   */
188 6
  public function current()
189
  {
190 6
    return $this->fetchCallable($this->current_row);
191
  }
192
193
  /**
194
   * Iterator interface implementation.
195
   *
196
   * @return int The current element key (row index; zero-based)
197
   */
198 1
  public function key()
199
  {
200 1
    return $this->current_row;
201
  }
202
203
  /**
204
   * Iterator interface implementation.
205
   *
206
   * @return void
207
   */
208 6
  public function next()
209
  {
210 6
    $this->current_row++;
211 6
  }
212
213
  /**
214
   * Iterator interface implementation.
215
   *
216
   * @param int $row Row position to rewind to; defaults to 0
217
   *
218
   * @return void
219
   */
220 10
  public function rewind($row = 0)
221
  {
222 10
    if ($this->seek($row)) {
223 8
      $this->current_row = $row;
224 8
    }
225 10
  }
226
227
  /**
228
   * Moves the internal pointer to the specified row position.
229
   *
230
   * @param int $row Row position; zero-based and set to 0 by default
231
   *
232
   * @return bool Boolean true on success, false otherwise
233
   */
234 18
  public function seek($row = 0)
235
  {
236 18
    if (is_int($row) && $row >= 0 && $row < $this->num_rows) {
237 14
      return mysqli_data_seek($this->_result, $row);
238
    }
239
240 4
    return false;
241
  }
242
243
  /**
244
   * Iterator interface implementation.
245
   *
246
   * @return bool Boolean true if the current index is valid, false otherwise
247
   */
248 6
  public function valid()
249
  {
250 6
    return $this->current_row < $this->num_rows;
251
  }
252
253
  /**
254
   * Fetch.
255
   *
256
   * <p>
257
   *   <br />
258
   *   INFO: this will return an object by default, not an array<br />
259
   *   and you can change the behaviour via "Result->setDefaultResultType()"
260
   * </p>
261
   *
262
   * @param bool $reset optional <p>Reset the \mysqli_result counter.</p>
263
   *
264
   * @return array|object|false <p><strong>false</strong> on error</p>
265
   */
266 2
  public function fetch($reset = false)
267
  {
268 2
    $return = false;
269
270 2
    if ($this->_default_result_type === 'object') {
271 2
      $return = $this->fetchObject('', '', $reset);
272 2
    } elseif ($this->_default_result_type === 'array') {
273 2
      $return = $this->fetchArray($reset);
274 2
    } elseif ($this->_default_result_type === 'Arrayy') {
275
      $return = $this->fetchArrayy($reset);
276
    }
277
278 2
    return $return;
279
  }
280
281
  /**
282
   * Fetch all results.
283
   *
284
   * <p>
285
   *   <br />
286
   *   INFO: this will return an object by default, not an array<br />
287
   *   and you can change the behaviour via "Result->setDefaultResultType()"
288
   * </p>
289
   *
290
   * @return array
291
   */
292 2
  public function fetchAll()
293
  {
294 2
    $return = array();
295
296 2
    if ($this->_default_result_type === 'object') {
297 2
      $return = $this->fetchAllObject();
298 2
    } elseif ($this->_default_result_type === 'array') {
299 1
      $return = $this->fetchAllArray();
300 1
    } elseif ($this->_default_result_type === 'Arrayy') {
301
      $return = $this->fetchAllArray();
302
    }
303
304 2
    return $return;
305
  }
306
307
  /**
308
   * Fetch all results as array.
309
   *
310
   * @return array
311
   */
312 14 View Code Duplication
  public function fetchAllArray()
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...
313
  {
314
    // init
315 14
    $data = array();
316
317
    if (
318 14
        $this->_result
319 14
        &&
320 14
        !$this->is_empty()
321 14
    ) {
322 14
      $this->reset();
323
324
      /** @noinspection PhpAssignmentInConditionInspection */
325 14
      while ($row = \mysqli_fetch_assoc($this->_result)) {
326 14
        $data[] = $this->cast($row);
327 14
      }
328 14
    }
329
330 14
    return $data;
331
  }
332
333
  /**
334
   * Fetch all results as "Arrayy"-object.
335
   *
336
   * @return Arrayy
337
   */
338 4 View Code Duplication
  public function fetchAllArrayy()
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...
339
  {
340
    // init
341 4
    $data = array();
342
343
    if (
344 4
        $this->_result
345 4
        &&
346 4
        !$this->is_empty()
347 4
    ) {
348 4
      $this->reset();
349
350
      /** @noinspection PhpAssignmentInConditionInspection */
351 4
      while ($row = \mysqli_fetch_assoc($this->_result)) {
352 4
        $data[] = $this->cast($row);
353 4
      }
354 4
    }
355
356 4
    return Arrayy::create($data);
357
  }
358
359
  /**
360
   * Fetch a single column as an 1-dimension array.
361
   *
362
   * @param string $column
363
   * @param bool   $skipNullValues <p>Skip "NULL"-values. | default: false</p>
364
   *
365
   * @return array <p>Return an empty array if the "$column" wasn't found</p>
366
   */
367 3
  public function fetchAllColumn($column, $skipNullValues = false)
368
  {
369 3
    return $this->fetchColumn($column, $skipNullValues, true);
370
  }
371
372
  /**
373
   * Fetch all results as array with objects.
374
   *
375
   * @param object|string $class  <p>
376
   *                              <strong>string</strong>: create a new object (with optional constructor
377
   *                              parameter)<br>
378
   *                              <strong>object</strong>: use a object and fill the the data into
379
   *                              </p>
380
   * @param null|array    $params optional
381
   *                              <p>
382
   *                              An array of parameters to pass to the constructor, used if $class is a
383
   *                              string.
384
   *                              </p>
385
   *
386
   * @return array
387
   */
388 11
  public function fetchAllObject($class = '', $params = null)
389
  {
390
391 11
    if ($this->is_empty()) {
392 1
      return array();
393
    }
394
395
    // init
396 10
    $data = array();
397 10
    $this->reset();
398
399 10
    if ($class && is_object($class)) {
400 7
      $propertyAccessor = PropertyAccess::createPropertyAccessor();
401
      /** @noinspection PhpAssignmentInConditionInspection */
402 7
      while ($row = \mysqli_fetch_assoc($this->_result)) {
403 7
        $classTmp = clone $class;
404 7
        $row = $this->cast($row);
405 7
        foreach ($row as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $row of type array|object|false is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
406 7
          $propertyAccessor->setValue($classTmp, $key, $value);
407 7
        }
408 7
        $data[] = $classTmp;
409 7
      }
410
411 7
      return $data;
412
    }
413
414 3
    if ($class && $params) {
415
      /** @noinspection PhpAssignmentInConditionInspection */
416 1
      while ($row = \mysqli_fetch_object($this->_result, $class, $params)) {
417 1
        $data[] = $this->cast($row);
418 1
      }
419
420 1
      return $data;
421
    }
422
423 3 View Code Duplication
    if ($class) {
424
      /** @noinspection PhpAssignmentInConditionInspection */
425 1
      while ($row = \mysqli_fetch_object($this->_result, $class)) {
426 1
        $data[] = $this->cast($row);
427 1
      }
428
429 1
      return $data;
430
    }
431
432
    /** @noinspection PhpAssignmentInConditionInspection */
433 3
    while ($row = \mysqli_fetch_object($this->_result)) {
434 3
      $data[] = $this->cast($row);
435 3
    }
436
437 3
    return $data;
438
  }
439
440
  /**
441
   * Fetch as array.
442
   *
443
   * @param bool $reset
444
   *
445
   * @return array|false <p><strong>false</strong> on error</p>
446
   */
447 16 View Code Duplication
  public function fetchArray($reset = false)
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...
448
  {
449 16
    if ($reset === true) {
450 1
      $this->reset();
451 1
    }
452
453 16
    $row = \mysqli_fetch_assoc($this->_result);
454 16
    if ($row) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
455 15
      return $this->cast($row);
456
    }
457
458 3
    if ($row === null) {
459 3
      return array();
460
    }
461
462
    return false;
463
  }
464
465
  /**
466
   * Fetch data as a key/value pair array.
467
   *
468
   * <p>
469
   *   <br />
470
   *   INFO: both "key" and "value" must exists in the fetched data
471
   *   the key will be the new key of the result-array
472
   *   <br /><br />
473
   * </p>
474
   *
475
   * e.g.:
476
   * <code>
477
   *    fetchArrayPair('some_id', 'some_value');
478
   *    // array(127 => 'some value', 128 => 'some other value')
479
   * </code>
480
   *
481
   * @param string $key
482
   * @param string $value
483
   *
484
   * @return array
485
   */
486 1
  public function fetchArrayPair($key, $value)
487
  {
488 1
    $arrayPair = array();
489 1
    $data = $this->fetchAllArray();
490
491 1
    foreach ($data as $_row) {
492
      if (
493 1
          array_key_exists($key, $_row) === true
494 1
          &&
495 1
          array_key_exists($value, $_row) === true
496 1
      ) {
497 1
        $_key = $_row[$key];
498 1
        $_value = $_row[$value];
499 1
        $arrayPair[$_key] = $_value;
500 1
      }
501 1
    }
502
503 1
    return $arrayPair;
504
  }
505
506
  /**
507
   * Fetch as "Arrayy"-object.
508
   *
509
   * @param bool $reset optional <p>Reset the \mysqli_result counter.</p>
510
   *
511
   * @return Arrayy|false <p><strong>false</strong> on error</p>
512
   */
513 2 View Code Duplication
  public function fetchArrayy($reset = false)
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...
514
  {
515 2
    if ($reset === true) {
516
      $this->reset();
517
    }
518
519 2
    $row = \mysqli_fetch_assoc($this->_result);
520 2
    if ($row) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
521 1
      return Arrayy::create($this->cast($row));
0 ignored issues
show
Bug introduced by
It seems like $this->cast($row) targeting voku\db\Result::cast() can also be of type false or object; however, Arrayy\Arrayy::create() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
522
    }
523
524 1
    if ($row === null) {
525 1
      return Arrayy::create();
526
    }
527
528
    return false;
529
  }
530
531
  /**
532
   * Fetch a single column as string (or as 1-dimension array).
533
   *
534
   * @param string $column
535
   * @param bool   $skipNullValues <p>Skip "NULL"-values. | default: true</p>
536
   * @param bool   $asArray        <p>Get all values and not only the last one. | default: false</p>
537
   *
538
   * @return string|array <p>Return a empty string or an empty array if the "$column" wasn't found, depend on
539
   *                      "$asArray"</p>
540
   */
541 5
  public function fetchColumn($column = '', $skipNullValues = true, $asArray = false)
542
  {
543 5
    if ($asArray === false) {
544 3
      $columnData = '';
545
546 3
      $data = $this->fetchAllArrayy()->reverse();
547 3 View Code Duplication
      foreach ($data as $_row) {
548
549 3
        if ($skipNullValues === true) {
550 3
          if (isset($_row[$column]) === false) {
551 1
            continue;
552
          }
553 3
        } else {
554 1
          if (array_key_exists($column, $_row) === false) {
555 1
            break;
556
          }
557
        }
558
559 3
        $columnData = $_row[$column];
560 3
        break;
561 3
      }
562
563 3
      return $columnData;
564
    }
565
566
    // -- return as array -->
567
568 3
    $columnData = array();
569
570 3
    $data = $this->fetchAllArray();
571
572 3 View Code Duplication
    foreach ($data as $_row) {
573
574 3
      if ($skipNullValues === true) {
575 1
        if (isset($_row[$column]) === false) {
576 1
          continue;
577
        }
578 1
      } else {
579 3
        if (array_key_exists($column, $_row) === false) {
580 1
          break;
581
        }
582
      }
583
584 3
      $columnData[] = $_row[$column];
585 3
    }
586
587 3
    return $columnData;
588
  }
589
590
  /**
591
   * Fetch as object.
592
   *
593
   * @param object|string $class  <p>
594
   *                              <strong>string</strong>: create a new object (with optional constructor
595
   *                              parameter)<br>
596
   *                              <strong>object</strong>: use a object and fill the the data into
597
   *                              </p>
598
   * @param null|array    $params optional
599
   *                              <p>
600
   *                              An array of parameters to pass to the constructor, used if $class is a
601
   *                              string.
602
   *                              </p>
603
   * @param bool          $reset  optional <p>Reset the \mysqli_result counter.</p>
604
   *
605
   * @return object|false <p><strong>false</strong> on error</p>
606
   */
607 18
  public function fetchObject($class = '', $params = null, $reset = false)
608
  {
609 18
    if ($reset === true) {
610 14
      $this->reset();
611 14
    }
612
613 18
    if ($class && is_object($class)) {
614 12
      $row = \mysqli_fetch_assoc($this->_result);
615 12
      $row = $row ? $this->cast($row) : false;
616
617 12
      if (!$row) {
618 3
        return false;
619
      }
620
621 10
      $propertyAccessor = PropertyAccess::createPropertyAccessor();
622 10
      foreach ($row as $key => $value) {
623 10
        $propertyAccessor->setValue($class, $key, $value);
624 10
      }
625
626 10
      return $class;
627
    }
628
629 7 View Code Duplication
    if ($class && $params) {
630 1
      $row = \mysqli_fetch_object($this->_result, $class, $params);
631
632 1
      return $row ? $this->cast($row) : false;
633
    }
634
635 7 View Code Duplication
    if ($class) {
636 1
      $row = \mysqli_fetch_object($this->_result, $class);
637
638 1
      return $row ? $this->cast($row) : false;
639
    }
640
641 7
    $row = \mysqli_fetch_object($this->_result);
642
643 7
    return $row ? $this->cast($row) : false;
644
  }
645
646
  /**
647
   * Fetches a row or a single column within a row. Returns null if there are
648
   * no more rows in the result.
649
   *
650
   * @param int    $row    The row number (optional)
651
   * @param string $column The column name (optional)
652
   *
653
   * @return mixed An associative array or a scalar value
654
   */
655 15
  public function fetchCallable($row = null, $column = null)
656
  {
657 15
    if (!$this->num_rows) {
658 2
      return null;
659
    }
660
661 13
    if (isset($row)) {
662 12
      $this->seek($row);
663 12
    }
664
665 13
    $rows = \mysqli_fetch_assoc($this->_result);
666
667 13
    if ($column) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $column of type string|null 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...
668 5
      return is_array($rows) && isset($rows[$column]) ? $rows[$column] : null;
669
    }
670
671 12
    return is_callable($this->_mapper) ? call_user_func($this->_mapper, $rows) : $rows;
672
  }
673
674
  /**
675
   * Return rows of field information in a result set. This function is a
676
   * basically a wrapper on the native mysqli_fetch_fields function.
677
   *
678
   * @param bool $as_array Return each field info as array; defaults to false
679
   *
680
   * @return array Array of field information each as an associative array
681
   */
682 1
  public function fetchFields($as_array = false)
683
  {
684 1
    if ($as_array) {
685 1
      return array_map(
686
          function ($object) {
687 1
            return (array)$object;
688 1
          },
689 1
          \mysqli_fetch_fields($this->_result)
690 1
      );
691
    }
692
693
    return \mysqli_fetch_fields($this->_result);
694
  }
695
696
  /**
697
   * Returns all rows at once as a grouped array of scalar values or arrays.
698
   *
699
   * @param string $group  The column name to use for grouping
700
   * @param string $column The column name to use as values (optional)
701
   *
702
   * @return array A grouped array of scalar values or arrays
703
   */
704 1 View Code Duplication
  public function fetchGroups($group, $column = null)
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...
705
  {
706
    // init
707 1
    $groups = array();
708 1
    $pos = $this->current_row;
709
710 1
    foreach ($this as $row) {
711
712 1
      if (!array_key_exists($group, $row)) {
713
        continue;
714
      }
715
716 1
      if (isset($column)) {
717
718 1
        if (!array_key_exists($column, $row)) {
719
          continue;
720
        }
721
722 1
        $groups[$row[$group]][] = $row[$column];
723 1
      } else {
724 1
        $groups[$row[$group]][] = $row;
725
      }
726 1
    }
727
728 1
    $this->rewind($pos);
729
730 1
    return $groups;
731
  }
732
733
  /**
734
   * Returns all rows at once as key-value pairs.
735
   *
736
   * @param string $key    The column name to use as keys
737
   * @param string $column The column name to use as values (optional)
738
   *
739
   * @return array An array of key-value pairs
740
   */
741 1 View Code Duplication
  public function fetchPairs($key, $column = null)
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...
742
  {
743
    // init
744 1
    $pairs = array();
745 1
    $pos = $this->current_row;
746
747 1
    foreach ($this as $row) {
748
749 1
      if (!array_key_exists($key, $row)) {
750
        continue;
751
      }
752
753 1
      if (isset($column)) {
754
755 1
        if (!array_key_exists($column, $row)) {
756
          continue;
757
        }
758
759 1
        $pairs[$row[$key]] = $row[$column];
760 1
      } else {
761 1
        $pairs[$row[$key]] = $row;
762
      }
763 1
    }
764
765 1
    $this->rewind($pos);
766
767 1
    return $pairs;
768
  }
769
770
  /**
771
   * Returns all rows at once, transposed as an array of arrays. Instead of
772
   * returning rows of columns, this method returns columns of rows.
773
   *
774
   * @param string $column The column name to use as keys (optional)
775
   *
776
   * @return mixed A transposed array of arrays
777
   */
778 1
  public function fetchTranspose($column = null)
779
  {
780
    // init
781 1
    $keys = isset($column) ? $this->fetchAllColumn($column) : array();
782 1
    $rows = array();
783 1
    $pos = $this->current_row;
784
785 1
    foreach ($this as $row) {
786 1
      foreach ($row as $key => $value) {
787 1
        $rows[$key][] = $value;
788 1
      }
789 1
    }
790
791 1
    $this->rewind($pos);
792
793 1
    if (empty($keys)) {
794 1
      return $rows;
795
    }
796
797 1
    return array_map(
798 1
        function ($values) use ($keys) {
799 1
          return array_combine($keys, $values);
800 1
        }, $rows
801 1
    );
802
  }
803
804
  /**
805
   * Returns the first row element from the result.
806
   *
807
   * @param string $column The column name to use as value (optional)
808
   *
809
   * @return mixed A row array or a single scalar value
810
   */
811 3
  public function first($column = null)
812
  {
813 3
    $pos = $this->current_row;
814 3
    $first = $this->fetchCallable(0, $column);
815 3
    $this->rewind($pos);
816
817 3
    return $first;
818
  }
819
820
  /**
821
   * free the memory
822
   */
823 63
  public function free()
824
  {
825 63
    if (isset($this->_result) && $this->_result) {
826
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
827
      /** @noinspection UsageOfSilenceOperatorInspection */
828 63
      @\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...
829 63
      $this->_result = null;
830
831 63
      return true;
832
    }
833
834 1
    return false;
835
  }
836
837
  /**
838
   * alias for "Result->fetch()"
839
   *
840
   * @see Result::fetch()
841
   *
842
   * @return array|object|false <p><strong>false</strong> on error</p>
843
   */
844 1
  public function get()
845
  {
846 1
    return $this->fetch();
847
  }
848
849
  /**
850
   * alias for "Result->fetchAll()"
851
   *
852
   * @see Result::fetchAll()
853
   *
854
   * @return array
855
   */
856 1
  public function getAll()
857
  {
858 1
    return $this->fetchAll();
859
  }
860
861
  /**
862
   * alias for "Result->fetchAllColumn()"
863
   *
864
   * @see Result::fetchAllColumn()
865
   *
866
   * @param string $column
867
   * @param bool   $skipNullValues
868
   *
869
   * @return array
870
   */
871
  public function getAllColumn($column, $skipNullValues = false)
872
  {
873
    return $this->fetchAllColumn($column, $skipNullValues);
874
  }
875
876
  /**
877
   * alias for "Result->fetchAllArray()"
878
   *
879
   * @see Result::fetchAllArray()
880
   *
881
   * @return array
882
   */
883 1
  public function getArray()
884
  {
885 1
    return $this->fetchAllArray();
886
  }
887
888
  /**
889
   * alias for "Result->fetchAllArrayy()"
890
   *
891
   * @see Result::fetchAllArrayy()
892
   *
893
   * @return Arrayy
894
   */
895
  public function getArrayy()
896
  {
897
    return $this->fetchAllArrayy();
898
  }
899
900
  /**
901
   * alias for "Result->fetchColumn()"
902
   *
903
   * @see Result::fetchColumn()
904
   *
905
   * @param $column
906
   * @param $asArray
907
   * @param $skipNullValues
908
   *
909
   * @return string|array <p>Return a empty string or an empty array if the "$column" wasn't found, depend on
910
   *                      "$asArray"</p>
911
   */
912 1
  public function getColumn($column, $skipNullValues = true, $asArray = false)
913
  {
914 1
    return $this->fetchColumn($column, $skipNullValues, $asArray);
915
  }
916
917
  /**
918
   * @return string
919
   */
920 1
  public function getDefaultResultType()
921
  {
922 1
    return $this->_default_result_type;
923
  }
924
925
  /**
926
   * alias for "Result->fetchAllObject()"
927
   *
928
   * @see Result::fetchAllObject()
929
   *
930
   * @return array of mysql-objects
931
   */
932 1
  public function getObject()
933
  {
934 1
    return $this->fetchAllObject();
935
  }
936
937
  /**
938
   * Check if the result is empty.
939
   *
940
   * @return bool
941
   */
942 34
  public function is_empty()
943
  {
944 34
    if ($this->num_rows > 0) {
945 31
      return false;
946
    }
947
948 5
    return true;
949
  }
950
951
  /**
952
   * Fetch all results as "json"-string.
953
   *
954
   * @return string
955
   */
956 1
  public function json()
957
  {
958 1
    $data = $this->fetchAllArray();
959
960 1
    return UTF8::json_encode($data);
961
  }
962
963
  /**
964
   * Returns the last row element from the result.
965
   *
966
   * @param string $column The column name to use as value (optional)
967
   *
968
   * @return mixed A row array or a single scalar value
969
   */
970 3
  public function last($column = null)
971
  {
972 3
    $pos = $this->current_row;
973 3
    $last = $this->fetchCallable($this->num_rows - 1, $column);
974 3
    $this->rewind($pos);
975
976 3
    return $last;
977
  }
978
979
  /**
980
   * Set the mapper...
981
   *
982
   * @param \Closure $callable
983
   *
984
   * @return $this
985
   */
986 1
  public function map(\Closure $callable)
987
  {
988 1
    $this->_mapper = $callable;
989
990 1
    return $this;
991
  }
992
993
  /**
994
   * Alias of count(). Deprecated.
995
   *
996
   * @return int The number of rows in the result
997
   */
998 1
  public function num_rows()
999
  {
1000 1
    return $this->count();
1001
  }
1002
1003
  /**
1004
   * ArrayAccess interface implementation.
1005
   *
1006
   * @param int $offset Offset number
1007
   *
1008
   * @return bool Boolean true if offset exists, false otherwise
1009
   */
1010 1
  public function offsetExists($offset)
1011
  {
1012 1
    return is_int($offset) && $offset >= 0 && $offset < $this->num_rows;
1013
  }
1014
1015
  /**
1016
   * ArrayAccess interface implementation.
1017
   *
1018
   * @param int $offset Offset number
1019
   *
1020
   * @return mixed
1021
   */
1022 1
  public function offsetGet($offset)
1023
  {
1024 1
    if ($this->offsetExists($offset)) {
1025 1
      return $this->fetchCallable($offset);
1026
    }
1027
1028
    throw new \OutOfBoundsException("undefined offset ($offset)");
1029
  }
1030
1031
  /**
1032
   * ArrayAccess interface implementation. Not implemented by design.
1033
   *
1034
   * @param mixed $offset
1035
   * @param mixed $value
1036
   */
1037
  public function offsetSet($offset, $value)
1038
  {
1039
    /** @noinspection UselessReturnInspection */
1040
    return;
1041
  }
1042
1043
  /**
1044
   * ArrayAccess interface implementation. Not implemented by design.
1045
   *
1046
   * @param mixed $offset
1047
   */
1048
  public function offsetUnset($offset)
1049
  {
1050
    /** @noinspection UselessReturnInspection */
1051
    return;
1052
  }
1053
1054
  /**
1055
   * Reset the offset (data_seek) for the results.
1056
   *
1057
   * @return Result
1058
   */
1059 32
  public function reset()
1060
  {
1061 32
    if (!$this->is_empty()) {
1062 30
      \mysqli_data_seek($this->_result, 0);
1063 30
    }
1064
1065 32
    return $this;
1066
  }
1067
1068
  /**
1069
   * You can set the default result-type to 'object', 'array' or 'Arrayy'.
1070
   *
1071
   * INFO: used for "fetch()" and "fetchAll()"
1072
   *
1073
   * @param string $default_result_type
1074
   */
1075 2
  public function setDefaultResultType($default_result_type = 'object')
1076
  {
1077
    if (
1078
        $default_result_type === 'object'
1079 2
        ||
1080
        $default_result_type === 'array'
1081 2
        ||
1082
        $default_result_type === 'Arrayy'
1083 2
    ) {
1084 2
      $this->_default_result_type = $default_result_type;
1085 2
    }
1086 2
  }
1087
1088
  /**
1089
   * @param int      $offset
1090
   * @param null|int $length
1091
   * @param bool     $preserve_keys
1092
   *
1093
   * @return array
1094
   */
1095 1
  public function slice($offset = 0, $length = null, $preserve_keys = false)
1096
  {
1097
    // init
1098 1
    $slice = array();
1099 1
    $offset = (int)$offset;
1100
1101 1
    if ($offset < 0) {
1102 1
      if (abs($offset) > $this->num_rows) {
1103 1
        $offset = 0;
1104 1
      } else {
1105 1
        $offset = $this->num_rows - abs($offset);
1106
      }
1107 1
    }
1108
1109 1
    $length = isset($length) ? (int)$length : $this->num_rows;
1110 1
    $n = 0;
1111 1
    for ($i = $offset; $i < $this->num_rows && $n < $length; $i++) {
1112 1
      if ($preserve_keys) {
1113 1
        $slice[$i] = $this->fetchCallable($i);
1114 1
      } else {
1115 1
        $slice[] = $this->fetchCallable($i);
1116
      }
1117 1
      ++$n;
1118 1
    }
1119
1120 1
    return $slice;
1121
  }
1122
}
1123