Completed
Pull Request — master (#22)
by Lars
14:42
created

Result::get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace voku\db;
4
5
use Arrayy\Arrayy;
6
use voku\helper\Bootup;
7
use voku\helper\UTF8;
8
9
/**
10
 * Result: this handles the result from "DB"-Class
11
 *
12
 * @package   voku\db
13
 */
14
final class Result
15
{
16
17
  /**
18
   * @var int
19
   */
20
  public $num_rows;
21
22
  /**
23
   * @var string
24
   */
25
  public $sql;
26
27
  /**
28
   * @var \mysqli_result
29
   */
30
  private $_result;
31
32
  /**
33
   * @var string
34
   */
35
  private $_default_result_type = 'object';
36
37
  /**
38
   * Result constructor.
39
   *
40
   * @param string         $sql
41
   * @param \mysqli_result $result
42
   */
43 29
  public function __construct($sql = '', \mysqli_result $result)
44
  {
45 29
    $this->sql = $sql;
46
47 29
    $this->_result = $result;
48 29
    $this->num_rows = (int)$this->_result->num_rows;
49 29
  }
50
51
  /**
52
   * @return string
53
   */
54 1
  public function getDefaultResultType()
55
  {
56 1
    return $this->_default_result_type;
57
  }
58
59
  /**
60
   * You can set the default result-type to 'object', 'array' or 'Arrayy'.
61
   *
62
   * INFO: used for "fetch()" and "fetchAll()"
63
   *
64
   * @param string $default_result_type
65
   */
66 2
  public function setDefaultResultType($default_result_type = 'object')
67
  {
68
    if (
69
        $default_result_type === 'object'
70 2
        ||
71
        $default_result_type === 'array'
72 2
        ||
73
        $default_result_type === 'Arrayy'
74 2
    ) {
75 2
      $this->_default_result_type = $default_result_type;
76 2
    }
77 2
  }
78
79
  /**
80
   * Fetch data as a key/value pair array.
81
   *
82
   * <p>
83
   *   <br />
84
   *   INFO: both "key" and "value" must exists in the fetched data
85
   *   the key will be the new key of the result-array
86
   *   <br /><br />
87
   * </p>
88
   *
89
   * e.g.:
90
   * <code>
91
   *    fetchArrayPair('some_id', 'some_value');
92
   *    // array(127 => 'some value', 128 => 'some other value')
93
   * </code>
94
   *
95
   * @param string $key
96
   * @param string $value
97
   *
98
   * @return array
99
   */
100 1
  public function fetchArrayPair($key, $value)
101
  {
102 1
    $arrayPair = array();
103 1
    $data = $this->fetchAllArray();
104
105 1
    foreach ($data as $_row) {
106
      if (
107 1
          array_key_exists($key, $_row) === true
108 1
          &&
109 1
          array_key_exists($value, $_row) === true
110 1
      ) {
111 1
        $_key = $_row[$key];
112 1
        $_value = $_row[$value];
113 1
        $arrayPair[$_key] = $_value;
114 1
      }
115 1
    }
116
117 1
    return $arrayPair;
118
  }
119
120
  /**
121
   * Cast data into int, float or string.
122
   *
123
   * <p>
124
   *   <br />
125
   *   INFO: install / use "mysqlnd"-driver for better performance
126
   * </p>
127
   *
128
   * @param array|object $data
129
   *
130
   * @return array|object|false <p><strong>false</strong> on error</p>
131
   */
132 25
  private function cast(&$data)
133
  {
134 25
    if (Helper::isMysqlndIsUsed() === true) {
135 25
      return $data;
136
    }
137
138
    // init
139
    if (Bootup::is_php('5.4')) {
140
      static $FIELDS_CACHE = array();
141
      static $TYPES_CACHE = array();
142
    } else {
143
      $FIELDS_CACHE = array();
144
      $TYPES_CACHE = array();
145
    }
146
147
    $result_hash = spl_object_hash($this->_result);
148
149
    if (!isset($FIELDS_CACHE[$result_hash])) {
150
      $FIELDS_CACHE[$result_hash] = \mysqli_fetch_fields($this->_result);
151
    }
152
153
    if ($FIELDS_CACHE[$result_hash] === false) {
154
      return false;
155
    }
156
157
    if (!isset($TYPES_CACHE[$result_hash])) {
158
      foreach ($FIELDS_CACHE[$result_hash] as $field) {
159
        switch ($field->type) {
160
          case 3:
161
            $TYPES_CACHE[$result_hash][$field->name] = 'int';
162
            break;
163
          case 4:
164
            $TYPES_CACHE[$result_hash][$field->name] = 'float';
165
            break;
166
          default:
167
            $TYPES_CACHE[$result_hash][$field->name] = 'string';
168 1
            break;
169
        }
170
      }
171
    }
172
173
    if (is_array($data) === true) {
174 View Code Duplication
      foreach ($TYPES_CACHE[$result_hash] as $type_name => $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
175
        if (isset($data[$type_name])) {
176
          settype($data[$type_name], $type);
177
        }
178
      }
179
    } elseif (is_object($data)) {
180 View Code Duplication
      foreach ($TYPES_CACHE[$result_hash] as $type_name => $type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
181
        if (isset($data->{$type_name})) {
182
          settype($data->{$type_name}, $type);
183
        }
184
      }
185
    }
186
187
    return $data;
188
  }
189
190
  /**
191
   * Fetch all results as array.
192
   *
193
   * @return array
194
   */
195 10 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...
196
  {
197
    // init
198 10
    $data = array();
199
200
    if (
201 10
        $this->_result
202 10
        &&
203 10
        !$this->is_empty()
204 10
    ) {
205 10
      $this->reset();
206
207
      /** @noinspection PhpAssignmentInConditionInspection */
208 10
      while ($row = \mysqli_fetch_assoc($this->_result)) {
209 10
        $data[] = $this->cast($row);
210 10
      }
211 10
    }
212
213 10
    return $data;
214
  }
215
216
  /**
217
   * Fetch all results as "Arrayy"-object.
218
   *
219
   * @return Arrayy
220
   */
221 3 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...
222
  {
223
    // init
224 3
    $data = array();
225
226
    if (
227 3
        $this->_result
228 3
        &&
229 3
        !$this->is_empty()
230 3
    ) {
231 3
      $this->reset();
232
233
      /** @noinspection PhpAssignmentInConditionInspection */
234 3
      while ($row = \mysqli_fetch_assoc($this->_result)) {
235 3
        $data[] = $this->cast($row);
236 3
      }
237 3
    }
238
239 3
    return Arrayy::create($data);
240
  }
241
242
  /**
243
   * Check if the result is empty.
244
   *
245
   * @return bool
246
   */
247 12
  public function is_empty()
248
  {
249 12
    if ($this->num_rows > 0) {
250 12
      return false;
251
    }
252
253 1
    return true;
254
  }
255
256
  /**
257
   * Reset the offset (data_seek) for the results.
258
   *
259
   * @return Result
260
   */
261 11
  public function reset()
262
  {
263 11
    if (!$this->is_empty()) {
264 11
      \mysqli_data_seek($this->_result, 0);
265 11
    }
266
267 11
    return $this;
268
  }
269
270
  /**
271
   * Fetch all results as "json"-string.
272
   *
273
   * @return string
274
   */
275 1
  public function json()
276
  {
277 1
    $data = $this->fetchAllArray();
278
279 1
    return UTF8::json_encode($data);
280
  }
281
282
  /**
283
   * __destruct
284
   *
285
   */
286 29
  public function __destruct()
287
  {
288 29
    $this->free();
289 29
  }
290
291
  /**
292
   * free the memory
293
   */
294 29
  public function free()
295
  {
296 29
    \mysqli_free_result($this->_result);
297 29
  }
298
299
  /**
300
   * alias for "Result->fetch()"
301
   *
302
   * @see Result::fetch()
303
   *
304
   * @return array|object|false <p><strong>false</strong> on error</p>
305
   */
306 1
  public function get()
307
  {
308 1
    return $this->fetch();
309
  }
310
311
  /**
312
   * Fetch.
313
   *
314
   * <p>
315
   *   <br />
316
   *   INFO: this will return an object by default, not an array<br />
317
   *   and you can change the behaviour via "Result->setDefaultResultType()"
318
   * </p>
319
   *
320
   * @param $reset
321
   *
322
   * @return array|object|false <p><strong>false</strong> on error</p>
323
   */
324 2
  public function fetch($reset = false)
325
  {
326 2
    $return = false;
327
328 2
    if ($this->_default_result_type === 'object') {
329 2
      $return = $this->fetchObject('', '', $reset);
330 2
    } elseif ($this->_default_result_type === 'array') {
331 2
      $return = $this->fetchArray($reset);
332 2
    } elseif ($this->_default_result_type === 'Arrayy') {
333
      $return = $this->fetchArrayy($reset);
334
    }
335
336 2
    return $return;
337
  }
338
339
  /**
340
   * Fetch as object.
341
   *
342
   * @param string     $class
343
   * @param null|array $params
344
   * @param bool       $reset
345
   *
346
   * @return object|false <p><strong>false</strong> on error</p>
347
   */
348 7
  public function fetchObject($class = '', $params = null, $reset = false)
349
  {
350 7
    if ($reset === true) {
351 1
      $this->reset();
352 1
    }
353
354 7
    if ($class && $params) {
355 1
      return ($row = \mysqli_fetch_object($this->_result, $class, $params)) ? $row : false;
356
    }
357
358 7
    if ($class) {
359 1
      return ($row = \mysqli_fetch_object($this->_result, $class)) ? $row : false;
360
    }
361
362 7
    return ($row = \mysqli_fetch_object($this->_result)) ? $this->cast($row) : false;
363
  }
364
365
  /**
366
   * Fetch as array.
367
   *
368
   * @param bool $reset
369
   *
370
   * @return array|false <p><strong>false</strong> on error</p>
371
   */
372 12 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...
373
  {
374 12
    if ($reset === true) {
375 1
      $this->reset();
376 1
    }
377
378 12
    $row = \mysqli_fetch_assoc($this->_result);
379 12
    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...
380 12
      return $this->cast($row);
381
    }
382
383 1
    return false;
384
  }
385
386
  /**
387
   * Fetch as "Arrayy"-object.
388
   *
389
   * @param bool $reset
390
   *
391
   * @return Arrayy|false <p><strong>false</strong> on error</p>
392
   */
393 1 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...
394
  {
395 1
    if ($reset === true) {
396
      $this->reset();
397
    }
398
399 1
    $row = \mysqli_fetch_assoc($this->_result);
400 1
    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...
401 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...
402
    }
403
404
    return false;
405
  }
406
407
  /**
408
   * alias for "Result->fetchAll()"
409
   *
410
   * @see Result::fetchAll()
411
   *
412
   * @return array
413
   */
414 1
  public function getAll()
415
  {
416 1
    return $this->fetchAll();
417
  }
418
419
  /**
420
   * Fetch all results.
421
   *
422
   * <p>
423
   *   <br />
424
   *   INFO: this will return an object by default, not an array<br />
425
   *   and you can change the behaviour via "Result->setDefaultResultType()"
426
   * </p>
427
   *
428
   * @return array
429
   */
430 2
  public function fetchAll()
431
  {
432 2
    $return = array();
433
434 2
    if ($this->_default_result_type === 'object') {
435 2
      $return = $this->fetchAllObject();
436 2
    } elseif ($this->_default_result_type === 'array') {
437 1
      $return = $this->fetchAllArray();
438 1
    } elseif ($this->_default_result_type === 'Arrayy') {
439
      $return = $this->fetchAllArray();
440
    }
441
442 2
    return $return;
443
  }
444
445
  /**
446
   * Fetch all results as array with objects.
447
   *
448
   * @param string     $class
449
   * @param null|array $params
450
   *
451
   * @return array
452
   */
453 3
  public function fetchAllObject($class = '', $params = null)
454
  {
455
    // init
456 3
    $data = array();
457
458 3
    if (!$this->is_empty()) {
459 3
      $this->reset();
460
461 3
      if ($class && $params) {
462
        /** @noinspection PhpAssignmentInConditionInspection */
463 1
        while ($row = \mysqli_fetch_object($this->_result, $class, $params)) {
464 1
          $data[] = $row;
465 1
        }
466 3
      } elseif ($class) {
467
        /** @noinspection PhpAssignmentInConditionInspection */
468 1
        while ($row = \mysqli_fetch_object($this->_result, $class)) {
469 1
          $data[] = $row;
470 1
        }
471 1
      } else {
472
        /** @noinspection PhpAssignmentInConditionInspection */
473 3
        while ($row = \mysqli_fetch_object($this->_result)) {
474 3
          $data[] = $this->cast($row);
475 3
        }
476
      }
477 3
    }
478
479 3
    return $data;
480
  }
481
482
  /**
483
   * alias for "Result->fetchAllObject()"
484
   *
485
   * @see Result::fetchAllObject()
486
   *
487
   * @return array of mysql-objects
488
   */
489 1
  public function getObject()
490
  {
491 1
    return $this->fetchAllObject();
492
  }
493
494
  /**
495
   * alias for "Result->fetchAllArrayy()"
496
   *
497
   * @see Result::fetchAllArrayy()
498
   *
499
   * @return Arrayy
500
   */
501
  public function getArrayy()
502
  {
503
    return $this->fetchAllArrayy();
504
  }
505
506
  /**
507
   * alias for "Result->fetchAllArray()"
508
   *
509
   * @see Result::fetchAllArray()
510
   *
511
   * @return array
512
   */
513 1
  public function getArray()
514
  {
515 1
    return $this->fetchAllArray();
516
  }
517
518
  /**
519
   * Fetch a single column as an 1-dimension array.
520
   *
521
   * @param string $column
522
   * @param bool   $skipNullValues <p>Skip "NULL"-values. | default: false</p>
523
   *
524
   * @return array <p>Return an empty array if the "$column" wasn't found</p>
525
   */
526 1
  public function fetchAllColumn($column, $skipNullValues = false)
527
  {
528 1
    return $this->fetchColumn($column, $skipNullValues, true);
529
  }
530
531
  /**
532
   * alias for "Result->fetchAllColumn()"
533
   *
534
   * @see Result::fetchAllColumn()
535
   *
536
   * @param string $column
537
   * @param bool   $skipNullValues
538
   *
539
   * @return array
540
   */
541
  public function getAllColumn($column, $skipNullValues = false)
542
  {
543
    return $this->fetchAllColumn($column, $skipNullValues);
544
  }
545
546
  /**
547
   * alias for "Result->fetchColumn()"
548
   *
549
   * @see Result::fetchColumn()
550
   *
551
   * @param $column
552
   * @param $asArray
553
   * @param $skipNullValues
554
   *
555
   * @return string|array <p>Return a empty string or an empty array if the "$column" wasn't found, depend on
556
   *                      "$asArray"</p>
557
   */
558 1
  public function getColumn($column, $skipNullValues = true, $asArray = false)
559
  {
560 1
    return $this->fetchColumn($column, $skipNullValues, $asArray);
561
  }
562
563
  /**
564
   * Fetch a single column as string (or as 1-dimension array).
565
   *
566
   * @param string $column
567
   * @param bool   $skipNullValues <p>Skip "NULL"-values. | default: true</p>
568
   * @param bool   $asArray        <p>Get all values and not only the last one. | default: false</p>
569
   *
570
   * @return string|array <p>Return a empty string or an empty array if the "$column" wasn't found, depend on
571
   *                      "$asArray"</p>
572
   */
573 2
  public function fetchColumn($column = '', $skipNullValues = true, $asArray = false)
574
  {
575 2
    if ($asArray === false) {
576 2
      $columnData = '';
577
578 2
      $data = $this->fetchAllArrayy()->reverse();
579 2 View Code Duplication
      foreach ($data as $_row) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
580
581 2
        if ($skipNullValues === true) {
582 2
          if (isset($_row[$column]) === false) {
583 1
            continue;
584
          }
585 2
        } else {
586 1
          if (array_key_exists($column, $_row) === false) {
587 1
            break;
588
          }
589
        }
590
591 2
        $columnData = $_row[$column];
592 2
        break;
593 2
      }
594
595 2
      return $columnData;
596
    }
597
598
    // -- return as array -->
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
599
600 1
    $columnData = array();
601
602 1
    $data = $this->fetchAllArray();
603
604 1 View Code Duplication
    foreach ($data as $_row) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
605
606 1
      if ($skipNullValues === true) {
607 1
        if (isset($_row[$column]) === false) {
608 1
          continue;
609
        }
610 1
      } else {
611 1
        if (array_key_exists($column, $_row) === false) {
612 1
          break;
613
        }
614
      }
615
616 1
      $columnData[] = $_row[$column];
617 1
    }
618
619 1
    return $columnData;
620
  }
621
622
  /**
623
   * Get the current "num_rows" as string.
624
   *
625
   * @return string
626
   */
627
  public function __toString()
628
  {
629
    return (string)$this->num_rows;
630
  }
631
}
632