Completed
Push — master ( 13015d...3633b6 )
by Lars
02:33
created

Result::fetchAll()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.128

Importance

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