Completed
Push — master ( 59cd4c...8588a0 )
by Dimas
10:00
created

pdo::row_array()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 14
nc 6
nop 3
dl 0
loc 23
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
namespace DB;
4
5
use JSON\json;
6
use MVC\Exception;
7
use PDO as GlobalPDO;
8
use PDOException;
9
10
/*
11
 function SQL_Connect    ($user, $pass, $db, $host = "localhost", $charset = "utf8mb4");
12
 function SQL_Exec       ($pdo, $query, $values = false);
13
 function SQL_Fetch      ($pdo, $query, $values = false);
14
 function SQL_MultiFetch ($pdo, $query, $values = false);
15
 function SQL_LastID     ($pdo);
16
 function SQL_Error      ($e, $query);
17
18
 ------------------------------------------------------------
19
20
 Define PDO_DEBUG for detailed error messages.
21
22
 PDO_DEBUG values:
23
24
  1: Print the error message.
25
  2: Print also the SQL Statement.
26
  3: Print SQL Statement and traceback with detailed information where the error occurs.
27
28
 Example: define ("PDO_DEBUG", "2");
29
30
*/
31
32
if (!defined('PDO_DEBUG')) {
33
  $env = \MVC\router::get_env();
34
  if ('development' == $env) {
35
    $env = '3';
36
  } else {
37
    $env = '1';
38
  }
39
  define('PDO_DEBUG', $env);
40
}
41
42
/**
43
 * Database Management.
44
 *
45
 * @author DimasLanjaka <[email protected]>
46
 */
47
class pdo
48
{
49
  protected $userdb;
50
  protected $passdb;
51
  protected $namedb;
52
  protected $hostdb;
53
  protected $charsetdb;
54
  /**
55
   * PDO instance.
56
   *
57
   * @var GlobalPDO
58
   */
59
  protected $pdo;
60
61
  public function __construct($user = null, $pass = null, $db = null, $host = 'localhost', $charset = 'utf8mb4')
62
  {
63
    if (!empty($user) && !empty($db)) {
64
      $this->connect($user, $pass, $db, $host, $charset);
65
    } else {
66
      exit('Database wrong ' . json_encode(func_get_args()));
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
67
    }
68
  }
69
70
  /**
71
   * Check table exists.
72
   *
73
   * @return bool
74
   */
75
  public function check_table(string $table)
76
  {
77
    $check = $this->query("SHOW TABLES LIKE '$table';")->row_array();
78
79
    return !empty($check);
80
  }
81
82
  /**
83
   * Set MySQL Timezone.
84
   *
85
   * @requires superuser access
86
   *
87
   * @return array
88
   */
89
  public function setTimezone(string $gmt = '+7:00')
90
  {
91
    return $this->query("SET GLOBAL time_zone = '$gmt';
92
    SET SESSION time_zone = '$gmt';
93
    SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;")->exec();
94
  }
95
96
  /**
97
   * Connect PDO.
98
   *
99
   * @param string $user
100
   * @param string $pass
101
   * @param string $db
102
   * @param string $host
103
   * @param string $charset
104
   *
105
   * @return \PDO
106
   */
107
  public function connect($user, $pass, $db, $host = 'localhost', $charset = 'utf8mb4')
108
  {
109
    $this->userdb = $user;
110
    $this->passdb = $pass;
111
    $this->namedb = $db;
112
    $this->hostdb = $host;
113
    $this->charsetdb = $charset;
114
    $options = [
115
      GlobalPDO::ATTR_ERRMODE => GlobalPDO::ERRMODE_EXCEPTION,
116
      GlobalPDO::ATTR_DEFAULT_FETCH_MODE => GlobalPDO::FETCH_ASSOC,
117
      GlobalPDO::ATTR_EMULATE_PREPARES => false,
118
      GlobalPDO::ATTR_CASE => GlobalPDO::CASE_NATURAL,
119
      GlobalPDO::ATTR_ORACLE_NULLS => GlobalPDO::NULL_EMPTY_STRING,
120
      GlobalPDO::ATTR_STATEMENT_CLASS => ['\DB\EPDOStatement', []],
121
    ];
122
123
    $pdo = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $pdo is dead and can be removed.
Loading history...
124
    try {
125
      $pdo = new GlobalPDO("mysql:host=$host;dbname=$db;charset=$charset", $user, $pass, $options);
126
    } catch (PDOException $e) {
127
      exit($e->getMessage());
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
128
      $this->SQL_Error($e);
0 ignored issues
show
Unused Code introduced by
$this->SQL_Error($e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
129
    }
130
    $this->pdo = $pdo;
131
132
    return $pdo;
133
  }
134
135
  /**
136
   * Switch database.
137
   *
138
   * @todo Switch between databases
139
   *
140
   * @return $this
141
   */
142
  public function switch(string $dbname)
143
  {
144
    $this->query('USE ' . $dbname)->exec();
145
146
    return $this;
147
  }
148
149
  private $from;
150
  private $query = '';
151
152
  public function query(string $sql)
153
  {
154
    $this->query = $this->trim($sql);
155
156
    return $this;
157
  }
158
159
  public function prep(array $prep)
160
  {
161
    $stmt = $this->pdo->prepare($this->query);
162
    foreach ($prep as $key => $value) {
163
      $stmt->bindParam(':' . preg_replace('/^\:/s', '', $key), $value, GlobalPDO::PARAM_STR);
164
    }
165
    //return $stmt->execute();
166
  }
167
168
  /**
169
   * Get where parameter is equals.
170
   *
171
   * @return $this
172
   */
173
  public function get_where(string $tbl, array $param = [])
174
  {
175
    $this->query .= " SELECT * FROM `$tbl` WHERE ";
176
    foreach ($param as $key => $value) {
177
      $this->query .= " `$key` = '$value' ";
178
    }
179
180
    return $this;
181
  }
182
183
  /**
184
   * Reset Query.
185
   *
186
   * @return $this
187
   */
188
  public function resetQuery()
189
  {
190
    $this->query = '';
191
192
    return $this;
193
  }
194
195
  /**
196
   * SELECT tableName.
197
   *
198
   * @param string $tbl table_name
199
   * @param string $row separated with comma
200
   *                    * Ex: name, date
201
   *
202
   * @return $this
203
   */
204
  public function select(string $tbl, string $row = '*')
205
  {
206
    $this->from = $tbl;
207
    $this->query .= " SELECT $row FROM $tbl ";
208
209
    return $this;
210
  }
211
212
  /**
213
   * Search database by row and keyword.
214
   */
215
  public function search(array $data, string $separated = 'OR')
216
  {
217
    $i = 0;
218
    foreach ($data as $key => $value) {
219
      if (0 == $i) {
220
        $this->query .= " WHERE $key LIKE '%$value%' ";
221
      } else {
222
        $this->query .= " $separated $key LIKE '%$value%' ";
223
      }
224
      ++$i;
225
    }
226
227
    return $this;
228
  }
229
230
  public function adv_search(array $data = ['OR' => ['key' => 'keyword', 'key2' => 'keyword2'], 'AND' => ['key' => 'keyword', 'key2' => 'keyword2']])
231
  {
232
    if (isset($data['OR'], $data['or'])) {
233
      $or = isset($data['OR']) ? $data['OR'] : $data['or'];
234
      $i = 0;
235
      foreach ($or as $key => $value) {
236
        if (0 == $i) {
237
          $this->query .= " WHERE $key LIKE '%$value%' ";
238
        } else {
239
          $this->query .= " OR $key LIKE '%$value%' ";
240
        }
241
      }
242
      ++$i;
243
    }
244
  }
245
246
  public function insert($tbl, array $data = [])
247
  {
248
    $this->query .= 'INSERT INTO ';
249
    $dbkey = " `$tbl` ( ";
250
    $dbval = ' VALUES( ';
251
    $keys = array_keys($data);
252
    $values = array_values($data);
253
    for ($i = 0; $i < count($data); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
254
      if ($i == count($data) - 1) { // if last iteration
255
        $dbkey .= " `{$keys[$i]}` ) ";
256
        $this->query .= $dbkey;
257
        $dbval .= " '{$values[$i]}' ) ";
258
        $this->query .= $dbval;
259
      } else {
260
        $dbkey .= " `{$keys[$i]}`, ";
261
        $dbval .= " '{$values[$i]}', ";
262
      }
263
    }
264
265
    return $this;
266
  }
267
268
  public function update(string $tbl, array $data, array $is_equals)
269
  {
270
    $this->query .= " UPDATE `$tbl` SET ";
271
    foreach ($data as $key => $value) {
272
      $this->query .= " `$key`='$value' ";
273
    }
274
    $this->query .= ' WHERE ';
275
    foreach ($is_equals as $key => $value) {
276
      $this->query .= " `$key` = '$value' ";
277
    }
278
279
    return $this;
280
  }
281
282
  public function sum(string $tbl, array $data, array $is_equals)
283
  {
284
    $this->query .= " UPDATE `$tbl` SET ";
285
    foreach ($data as $key => $value) {
286
      if (!is_numeric($value)) {
287
        throw new Exception("$value must be instance of number, instead of " . gettype($value), 1);
288
      }
289
      $this->query .= " `$key`=$key + $value ";
290
    }
291
    $this->query .= ' WHERE ';
292
    foreach ($is_equals as $key => $value) {
293
      $this->query .= " `$key` = '$value' ";
294
    }
295
296
    return $this;
297
  }
298
299
  /**
300
   * PDO Instance.
301
   *
302
   * @return GlobalPDO
303
   */
304
  public function pdo()
305
  {
306
    return $this->pdo;
307
  }
308
309
  public function insert_update($tbl, array $data, array $onduplicate)
310
  {
311
    $this->query .= ' INSERT INTO ';
312
    $dbkey = " `$tbl` ( ";
313
    $dbval = ' VALUES( ';
314
    $keys = array_keys($data);
315
    $values = array_values($data);
316
    for ($i = 0; $i < count($data); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
317
      $V = $values[$i];
318
      $V = str_replace('\'', '\\\'', $V);
319
      if ($i == count($data) - 1) { // if last iteration
320
        $dbkey .= " `{$keys[$i]}` ) ";
321
        $this->query .= $dbkey;
322
        $dbval .= " '{$V}' ) ";
323
        $this->query .= $dbval;
324
      } else {
325
        $dbkey .= " `{$keys[$i]}`, ";
326
        $dbval .= " '{$V}', ";
327
      }
328
    }
329
    $this->query .= ' ON DUPLICATE KEY UPDATE ';
330
    $dup_keys = array_keys($onduplicate);
331
    $dup_values = array_values($onduplicate);
332
    for ($i = 0; $i < count($onduplicate); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
333
      if ($i == count($onduplicate) - 1) { // last iteration
334
        $this->query .= " `{$dup_keys[$i]}` = '{$dup_values[$i]}' ";
335
      } else {
336
        $this->query .= " `{$dup_keys[$i]}` = '{$dup_values[$i]}', ";
337
      }
338
    }
339
    //var_dump($this->query);
340
341
    return $this;
342
  }
343
344
  public function replace(string $tbl, array $data)
345
  {
346
    $this->query .= ' REPLACE INTO ';
347
    $dbkey = " `$tbl` ( ";
348
    $dbval = ' VALUES( ';
349
    $keys = array_keys($data);
350
    $values = array_values($data);
351
    for ($i = 0; $i < count($data); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
352
      if ($i == count($data) - 1) { // if last iteration
353
        $dbkey .= " `{$keys[$i]}` ) ";
354
        $this->query .= $dbkey;
355
        $dbval .= " `{$values[$i]}` ) ";
356
        $this->query .= $dbval;
357
      } else {
358
        $dbkey .= " `{$keys[$i]}`, ";
359
        $dbval .= " `{$values[$i]}`, ";
360
      }
361
    }
362
363
    return $this;
364
  }
365
366
  public function execute(array $value = [])
367
  {
368
    $result = ['query' => $this->query, 'error' => true];
369
    /**
370
     * @var statement
371
     */
372
    $stmt = $this->pdo->prepare($this->query);
373
    $exec = $stmt->execute($value);
374
    if ($exec) {
375
      $result['error'] = false;
376
    }
377
378
    return $result;
379
  }
380
381
  /**
382
   * Automated Executor.
383
   *
384
   * @return array
385
   */
386
  public function exec(array $value = [])
387
  {
388
    $exec = $this->SQL_Exec($this->trim($this->query), $value);
389
    $this->resetQuery();
390
391
    return $exec;
392
  }
393
394
  public function exec2(array $value)
395
  {
396
    $result = ['error' => true];
397
    /**
398
     * @var EPDOStatement
399
     */
400
    $stmt = $this->pdo->prepare($this->query);
401
    $bind = [];
402
    $i = 0;
403
    foreach ($value as $key => $value) {
404
      switch (gettype($value)) {
405
        case 'int':
406
          $type = GlobalPDO::PARAM_INT;
407
          break;
408
        case 'number':
409
          $type = GlobalPDO::PARAM_INT;
410
          break;
411
        case 'boolean':
412
          $type = GlobalPDO::PARAM_BOOL;
413
          break;
414
        case 'bool':
415
          $type = GlobalPDO::PARAM_BOOL;
416
          break;
417
418
        default:
419
          $type = GlobalPDO::PARAM_STR;
420
          break;
421
      }
422
      if (!isset($type)) {
423
        throw new Exception('Error Processing Code: var(type) not exists, and value instance of ' . gettype($value), 1);
424
      }
425
      $bind[$i][$key]['key'] = ":$key";
426
      $bind[$i][$key]['value'] = $value;
427
      $bind[$i][$key]['type'] = $type;
428
      $stmt->bindValue(":$key", $value, $type);
429
      ++$i;
430
    }
431
    try {
432
      $exec = $stmt->execute($value);
433
      if ($exec) {
434
        $result['error'] = false;
435
      }
436
      //$user = $stmt->fetch();
437
    } catch (PDOException $e) {
438
      $message = "{$e->getMessage()}
439
      <br>Code: {$stmt->errorCode()}
440
      <br>Info: " . serialize($stmt->errorInfo());
441
      $json = json::json($bind, false, false);
442
      if (PDO_DEBUG == 3) {
443
        $message .= "
444
        <br/>Query: {$this->query}
445
        <br/>Callback Query: {$stmt->queryString}
446
        <br/>Debug: {$stmt->_debugQuery()}
0 ignored issues
show
introduced by
The method _debugQuery() does not exist on PDOStatement. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

446
        <br/>Debug: {$stmt->/** @scrutinizer ignore-call */ _debugQuery()}
Loading history...
447
        <br/>{$stmt->debugDumpParams()}
448
        <br><pre>{$json}</pre>
449
        ";
450
      }
451
      throw new PDOException($message, (int) $e->getCode());
452
    }
453
  }
454
455
  public function trim(string $str)
456
  {
457
    return trim(preg_replace('/\s+/s', ' ', $str));
458
  }
459
460
  public function where($is_equals = [], $not_equals = [], $param = null)
0 ignored issues
show
Unused Code introduced by
The parameter $not_equals is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

460
  public function where($is_equals = [], /** @scrutinizer ignore-unused */ $not_equals = [], $param = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
461
  {
462
    $this->query .= ' WHERE ';
463
    if (!empty($is_equals)) {
464
      $this->equals($is_equals);
465
    }
466
    if ($param && empty($param)) {
467
      $this->query .= " $param ";
468
    }
469
470
    return $this;
471
  }
472
473
  /**
474
   * Order by row [ASC=ascending or DESC=descending].
475
   *
476
   * @param string $order
477
   *
478
   * @return $this
479
   */
480
  public function sort(array $by, $order = 'ASC')
481
  {
482
    $this->query .= ' ORDER BY ';
483
    for ($i = 0; $i < count($by); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
484
      $this->query .= " {$by[$i]} ";
485
      if (!($i == count($by) - 1)) {
486
        $this->query .= ',';
487
      }
488
    }
489
    $this->query .= " $order ";
490
491
    return $this;
492
  }
493
494
  public function or($is_equals = [], $not_equals = [])
0 ignored issues
show
Unused Code introduced by
The parameter $not_equals is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

494
  public function or($is_equals = [], /** @scrutinizer ignore-unused */ $not_equals = [])

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
495
  {
496
    $this->query .= ' OR ';
497
    if (!empty($is_equals)) {
498
      $this->equals($is_equals);
499
    }
500
501
    return $this;
502
  }
503
504
  public function and($is_equals = [], $not_equals = [])
0 ignored issues
show
Unused Code introduced by
The parameter $not_equals is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

504
  public function and($is_equals = [], /** @scrutinizer ignore-unused */ $not_equals = [])

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
505
  {
506
    $this->query .= ' AND ';
507
    if (!empty($is_equals)) {
508
      $this->equals($is_equals);
509
    }
510
511
    return $this;
512
  }
513
514
  /**
515
   * If array data is equals.
516
   */
517
  public function equals(array $is_equals)
518
  {
519
    if (!empty($is_equals)) {
520
      $i = count($is_equals);
521
      foreach ($is_equals as $key => $value) {
522
        if ('NULL' == $value || null === $value) {
523
          $this->query .= " `$key` IS NULL ";
524
        } elseif ($value) {
525
          //$value = "'$value'";
526
          $this->query .= " `$key` = '$value' ";
527
        }
528
        if ($i > 1) { // if condition more than 1, add AND command
529
          $this->query .= ' AND ';
530
        }
531
        --$i;
532
      }
533
    }
534
535
    return $this;
536
  }
537
538
  /**
539
   * Get query result.
540
   *
541
   * @todo check before execution
542
   *
543
   * @return array
544
   *
545
   * @author Dimas Lanjaka <[email protected]>
546
   */
547
  public function getQuery(array $data_value = [])
548
  {
549
    $query = $this->query;
550
    if (!empty($data_value)) {
551
      foreach ($data_value as $key => $value) {
552
        $query = str_replace(":$key", $this->pdo->quote($value), $this->query);
553
      }
554
    }
555
556
    $query = str_replace("\n", ' ', $query);
557
    $this->query = '';
558
559
    return $query;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query returns the type string which is incompatible with the documented return type array.
Loading history...
560
  }
561
562
  /**
563
   * Get last inserted id.
564
   *
565
   * @param GlobalPDO $pdo
566
   */
567
  public function SQL_LastID()
568
  {
569
    return $this->pdo->lastInsertId();
570
  }
571
572
  /**
573
   * PDO Error.
574
   *
575
   * @param PDOException $e
576
   * @param mixed        $query
577
   */
578
  public function SQL_Error($e, $query = null)
579
  {
580
    $result = ['error' => true];
581
    if (headers_sent()) {
582
      $result[] = '<pre>';
583
    }
584
585
    if (defined('PDO_DEBUG')) {
586
      if (headers_sent()) {
587
        $result[] = 'SQL Error: ' . $e->getMessage() . "\n\n";
588
      } else {
589
        $result['message'] = $e->getMessage();
590
      }
591
592
      if (PDO_DEBUG >= '2') {
593
        if (headers_sent()) {
594
          $result[] = "$query\n\n";
595
        } else {
596
          $result['query'] = $query;
597
        }
598
      }
599
600
      if (PDO_DEBUG == '3') {
601
        // Print details like script name and line.
602
        if (headers_sent()) {
603
          print_r($e);
604
        } else {
605
          $result['dump'] = $e;
606
        }
607
      }
608
    } else {
609
      if (headers_sent()) {
610
        $result[] = 'SQL Error! Please contact the administrator.';
611
      } else {
612
        $result['message'] = 'SQL Error! Please contact the administrator.';
613
      }
614
    }
615
    $result['title'] = __CLASS__ . '::' . __FUNCTION__;
616
617
    if (headers_sent()) {
618
      $result[] = '</pre>';
619
      echo implode("\n", $result);
620
    } else {
621
      json::json($result);
622
    }
623
624
    // Stop on error.
625
    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
626
  }
627
628
  public function SQL_Fetch($query, $values = false)
629
  {
630
    if (!$this->pdo) {
631
      throw new \MVC\Exception('Database not properly configured', 1);
632
    }
633
    try {
634
      if (!$values) {
635
        return $this->pdo->query($query)->fetch();
636
      } else {
637
        $stmt = $this->pdo->prepare($query);
638
        $stmt->execute($values);
639
        $arr = $stmt->fetch();
640
        $this->query = $this->parms($query, $values);
641
        //var_dump($this->query);
642
        $stmt = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $stmt is dead and can be removed.
Loading history...
643
644
        return $arr;
645
      }
646
    } catch (PDOException $e) {
647
      //var_dump('error true');
648
      $this->SQL_Error($e, $query);
649
    }
650
  }
651
652
  /**
653
   * Check before insert
654
   * * always equals validation.
655
   *
656
   * @return $this
657
   */
658
  public function insert_not_exists(string $tbl, array $data, array $where = null)
659
  {
660
    $keys = array_keys($data);
661
    $vals = array_values($data);
662
    $valstr = $keystr = $is_equals = '';
663
    for ($i = 0; $i < count($data); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
664
      $value = $vals[$i];
665
      if (!preg_match('/^\:/s', $value)) {
666
        $value = $this->pdo->quote($value);
667
      }
668
      $keystr .= " `{$keys[$i]}` ";
669
      $valstr .= " $value ";
670
      $is_equals .= " `{$keys[$i]}`=$value ";
671
      if ($i == count($data) - 1) { //last iteration
672
        //$keystr .= ')';
673
        //$valstr .= ')';
674
      } else {
675
        $keystr .= ', ';
676
        $valstr .= ', ';
677
        $is_equals .= ' AND ';
678
      }
679
    }
680
    if (null !== $where) {
681
      $is_equals = '';
682
      foreach ($where as $key => $value) {
683
        $is_equals .= " $key = $value ";
684
      }
685
    }
686
    //var_dump($data);
687
    $this->query .= " INSERT INTO $tbl ($keystr) ";
688
    $this->query .= " SELECT * FROM (SELECT $valstr) AS tmp
689
    WHERE NOT EXISTS (
690
      SELECT $keystr FROM $tbl WHERE $is_equals
691
    );";
692
693
    return $this;
694
  }
695
696
  public function parms($string, $data)
697
  {
698
    $indexed = $data == array_values($data);
699
    foreach ($data as $k => $v) {
700
      if (is_string($v)) {
701
        $v = "'$v'";
702
      }
703
      if ($indexed) {
704
        $string = preg_replace('/\?/', $v, $string, 1);
705
      } else {
706
        $string = str_replace(":$k", $v, $string);
707
      }
708
    }
709
710
    return $string;
711
  }
712
713
  /**
714
   * Get result as array.
715
   *
716
   * @param mixed   $value
717
   * @param string $filter filter array by key
718
   *
719
   * @return array|null
720
   */
721
  public function row_array($value = false, $filter = null, bool $unique_filter = false)
722
  {
723
    $exec = $this->SQL_MultiFetch($this->trim($this->query), $value);
724
    $this->query = '';
725
    if ($filter) {
726
      if (!empty($exec) && is_array($exec)) {
727
        $filtered = array_map(function ($data) use ($filter) {
728
          if (isset($data[$filter])) {
729
            return $data[$filter];
730
          }
731
732
          return $data;
733
        }, $exec);
734
        if ($unique_filter) {
735
          return array_unique($filtered);
736
        }
737
738
        return $filtered;
739
      }
740
    }
741
742
    if (!empty($exec) && is_array($exec)) {
743
      return $exec;
744
    }
745
  }
746
747
  /**
748
   * Multi fetch pdo.
749
   *
750
   * @param bool|array $values
751
   * @param bool       $assoc  always association array return
752
   *
753
   * @return void|array
754
   */
755
  public function SQL_MultiFetch(string $query, $values = false, $assoc = false)
756
  {
757
    if (!$this->pdo) {
758
      throw new \MVC\Exception('Database not properly configured', 1);
759
    }
760
    try {
761
      $stmt = $this->pdo->prepare($query);
762
      if (!$values) {
763
        $stmt->execute();
764
      } else {
765
        $stmt->execute($values);
0 ignored issues
show
Bug introduced by
It seems like $values can also be of type true; however, parameter $input_parameters of PDOStatement::execute() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

765
        $stmt->execute(/** @scrutinizer ignore-type */ $values);
Loading history...
766
      }
767
      $arr = $stmt->fetchAll();
768
      $stmt = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $stmt is dead and can be removed.
Loading history...
769
770
      if (1 == count($arr) && !$assoc) {
771
        $arr = $arr[0];
772
      }
773
774
      return $arr;
775
    } catch (PDOException $e) {
776
      $this->SQL_Error($e, $query);
777
    }
778
  }
779
780
  public function SQL_Exec($query, $values = false)
781
  {
782
    $result = ['query' => $query, 'error' => false, 'title' => 'Database Management'];
783
    try {
784
      if (!$values) {
785
        $status = $this->pdo->exec($query);
786
        //var_dump($status);
787
        if (!$status && 00000 != (int) $this->pdo->errorCode()) {
788
          $result['error'] = true;
789
          $result['code'] = $this->pdo->errorCode();
790
          $result['message'] = $this->pdo->errorInfo();
791
        }
792
      } else {
793
        $stmt = $this->pdo->prepare($query);
794
        $stmt->execute($values);
795
        $count = 0 == (int) $stmt->rowCount() ? true : false;
796
        $result['error'] = $count;
797
        $stmt = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $stmt is dead and can be removed.
Loading history...
798
      }
799
      if (preg_match('/^insert/s', strtolower($query))) {
800
        $result['id'] = $this->SQL_LastID($this->pdo);
0 ignored issues
show
Unused Code introduced by
The call to DB\pdo::SQL_LastID() has too many arguments starting with $this->pdo. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

800
        /** @scrutinizer ignore-call */ 
801
        $result['id'] = $this->SQL_LastID($this->pdo);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
801
      }
802
    } catch (PDOException $e) {
803
      $this->SQL_Error($e, $query);
804
    }
805
806
    return $result;
807
  }
808
809
  public function show_creation(string $table)
810
  {
811
    return $this->query("SHOW CREATE TABLE `$table`")->row_array();
812
  }
813
}
814