Completed
Push — master ( 9c7626...3df618 )
by Lars
02:36
created

DB::escape()   C

Complexity

Conditions 14
Paths 30

Size

Total Lines 63
Code Lines 28

Duplication

Lines 7
Ratio 11.11 %

Code Coverage

Tests 29
CRAP Score 14.0072

Importance

Changes 12
Bugs 2 Features 5
Metric Value
c 12
b 2
f 5
dl 7
loc 63
ccs 29
cts 30
cp 0.9667
rs 6.0952
cc 14
eloc 28
nc 30
nop 4
crap 14.0072

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace voku\db;
4
5
use voku\cache\Cache;
6
use voku\helper\UTF8;
7
8
/**
9
 * DB: this handles DB queries via MySQLi
10
 *
11
 * @package   voku\db
12
 */
13
class DB
14
{
15
16
  /**
17
   * @var int
18
   */
19
  public $query_count = 0;
20
21
  /**
22
   * @var bool
23
   */
24
  protected $exit_on_error = true;
25
26
  /**
27
   * @var bool
28
   */
29
  protected $echo_on_error = true;
30
31
  /**
32
   * @var string
33
   */
34
  protected $css_mysql_box_border = '3px solid red';
35
36
  /**
37
   * @var string
38
   */
39
  protected $css_mysql_box_bg = '#FFCCCC';
40
41
  /**
42
   * @var \mysqli
43
   */
44
  protected $link = false;
45
46
  /**
47
   * @var bool
48
   */
49
  protected $connected = false;
50
51
  /**
52
   * @var array
53
   */
54
  protected $mysqlDefaultTimeFunctions;
55
56
  /**
57
   * @var string
58
   */
59
  private $logger_class_name;
60
61
  /**
62
   * @var string
63
   *
64
   * 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'
65
   */
66
  private $logger_level;
67
68
  /**
69
   * @var string
70
   */
71
  private $hostname = '';
72
73
  /**
74
   * @var string
75
   */
76
  private $username = '';
77
78
  /**
79
   * @var string
80
   */
81
  private $password = '';
82
83
  /**
84
   * @var string
85
   */
86
  private $database = '';
87
88
  /**
89
   * @var int
90
   */
91
  private $port = 3306;
92
93
  /**
94
   * @var string
95
   */
96
  private $charset = 'utf8';
97
98
  /**
99
   * @var string
100
   */
101
  private $socket = '';
102
103
  /**
104
   * @var array
105
   */
106
  private $_errors = array();
107
108
  /**
109
   * @var bool
110
   */
111
  private $session_to_db = false;
112
113
  /**
114
   * @var bool
115
   */
116
  private $_in_transaction = false;
117
118
  /**
119
   * __construct()
120
   *
121
   * @param string         $hostname
122
   * @param string         $username
123
   * @param string         $password
124
   * @param string         $database
125
   * @param int            $port
126
   * @param string         $charset
127
   * @param boolean|string $exit_on_error use a empty string "" or false to disable it
128
   * @param boolean|string $echo_on_error use a empty string "" or false to disable it
129
   * @param string         $logger_class_name
130
   * @param string         $logger_level  'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'
131
   * @param boolean|string $session_to_db use a empty string "" or false to disable it
132
   */
133 10
  protected function __construct($hostname, $username, $password, $database, $port, $charset, $exit_on_error, $echo_on_error, $logger_class_name, $logger_level, $session_to_db)
134
  {
135 10
    $this->connected = false;
136
137 10
    $this->_loadConfig(
138 10
        $hostname,
139 10
        $username,
140 10
        $password,
141 10
        $database,
142 10
        $port,
143 10
        $charset,
144 10
        $exit_on_error,
145 10
        $echo_on_error,
146 10
        $logger_class_name,
147 10
        $logger_level,
148
        $session_to_db
149 10
    );
150
151 7
    $this->connect();
152
153 4
    $this->mysqlDefaultTimeFunctions = array(
154
      // Returns the current date.
155 4
      'CURDATE()',
156
      // CURRENT_DATE	| Synonyms for CURDATE()
157 4
      'CURRENT_DATE()',
158
      // CURRENT_TIME	| Synonyms for CURTIME()
159 4
      'CURRENT_TIME()',
160
      // CURRENT_TIMESTAMP | Synonyms for NOW()
161 4
      'CURRENT_TIMESTAMP()',
162
      // Returns the current time.
163 4
      'CURTIME()',
164
      // Synonym for NOW()
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
165 4
      'LOCALTIME()',
166
      // Synonym for NOW()
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
167 4
      'LOCALTIMESTAMP()',
168
      // Returns the current date and time.
169 4
      'NOW()',
170
      // Returns the time at which the function executes.
171 4
      'SYSDATE()',
172
      // Returns a UNIX timestamp.
173 4
      'UNIX_TIMESTAMP()',
174
      // Returns the current UTC date.
175 4
      'UTC_DATE()',
176
      // Returns the current UTC time.
177 4
      'UTC_TIME()',
178
      // Returns the current UTC date and time.
179 4
      'UTC_TIMESTAMP()',
180
    );
181 4
  }
182
183
  /**
184
   * Load the config from the constructor.
185
   *
186
   * @param string         $hostname
187
   * @param string         $username
188
   * @param string         $password
189
   * @param string         $database
190
   * @param int            $port
191
   * @param string         $charset
192
   * @param boolean|string $exit_on_error use a empty string "" or false to disable it
193
   * @param boolean|string $echo_on_error use a empty string "" or false to disable it
194
   * @param string         $logger_class_name
195
   * @param string         $logger_level
196
   * @param boolean|string $session_to_db use a empty string "" or false to disable it
197
   *
198
   * @return bool
199
   */
200 10
  private function _loadConfig($hostname, $username, $password, $database, $port, $charset, $exit_on_error, $echo_on_error, $logger_class_name, $logger_level, $session_to_db)
201
  {
202 10
    $this->hostname = (string)$hostname;
203 10
    $this->username = (string)$username;
204 10
    $this->password = (string)$password;
205 10
    $this->database = (string)$database;
206
207 10
    if ($charset) {
208 4
      $this->charset = (string)$charset;
209 4
    }
210
211 10
    if ($port) {
212 4
      $this->port = (int)$port;
213 4
    } else {
214
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
215 7
      $this->port = @ini_get('mysqli.default_port');
216
    }
217
218 10
    if (!$this->socket) {
219
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
220 10
      $this->socket = @ini_get('mysqli.default_socket');
221 10
    }
222
223 10
    if ($exit_on_error === true || $exit_on_error === false) {
224 10
      $this->exit_on_error = (boolean)$exit_on_error;
225 10
    }
226
227 10
    if ($echo_on_error === true || $echo_on_error === false) {
228 10
      $this->echo_on_error = (boolean)$echo_on_error;
229 10
    }
230
231 10
    $this->logger_class_name = (string)$logger_class_name;
232 10
    $this->logger_level = (string)$logger_level;
233
234 10
    $this->session_to_db = (boolean)$session_to_db;
235
236 10
    return $this->showConfigError();
237
  }
238
239
  /**
240
   * Show config errors by throw exceptions.
241
   *
242
   * @return bool
243
   *
244
   * @throws \Exception
245
   */
246 10
  public function showConfigError()
247
  {
248
249
    if (
250 10
        !$this->hostname
251 10
        ||
252 9
        !$this->username
253 9
        ||
254 8
        !$this->database
255 10
    ) {
256
257 3
      if (!$this->hostname) {
258 1
        throw new \Exception('no-sql-hostname');
259
      }
260
261 2
      if (!$this->username) {
262 1
        throw new \Exception('no-sql-username');
263
      }
264
265 1
      if (!$this->database) {
266 1
        throw new \Exception('no-sql-database');
267
      }
268
269
      return false;
270
    }
271
272 7
    return true;
273
  }
274
275
  /**
276
   * Open a new connection to the MySQL server.
277
   *
278
   * @return boolean
279
   */
280 8
  public function connect()
281
  {
282 8
    if ($this->isReady()) {
283 1
      return true;
284
    }
285
286 8
    mysqli_report(MYSQLI_REPORT_STRICT);
287
    try {
288
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
289 8
      $this->link = @mysqli_connect(
290 8
          $this->hostname,
291 8
          $this->username,
292 8
          $this->password,
293 8
          $this->database,
294 8
          $this->port,
295 8
          $this->socket
296 8
      );
297 8
    } catch (\Exception $e) {
298 3
      $this->_displayError('Error connecting to mysql server: ' . $e->getMessage(), true);
299
    }
300 5
    mysqli_report(MYSQLI_REPORT_OFF);
301
302 5
    if (!$this->link) {
303
      $this->_displayError('Error connecting to mysql server: ' . mysqli_connect_error(), true);
304
    } else {
305 5
      $this->set_charset($this->charset);
306 5
      $this->connected = true;
307
    }
308
309 5
    return $this->isReady();
310
  }
311
312
  /**
313
   * Check if db-connection is ready.
314
   *
315
   * @return boolean
316
   */
317 29
  public function isReady()
318
  {
319 29
    return $this->connected ? true : false;
320
  }
321
322
  /**
323
   * Display SQL-Errors or throw Exceptions (for dev).
324
   *
325
   * @param string       $error
326
   * @param null|boolean $force_exception_after_error
327
   *
328
   * @throws \Exception
329
   */
330 18
  private function _displayError($error, $force_exception_after_error = null)
331
  {
332 18
    $fileInfo = $this->getFileAndLineFromSql();
333
334 18
    $this->logger(
335
        array(
336 18
            'error',
337 18
            '<strong>' . date(
338
                'd. m. Y G:i:s'
339 18
            ) . ' (' . $fileInfo['file'] . ' line: ' . $fileInfo['line'] . ') (sql-error):</strong> ' . $error . '<br>',
340
        )
341 18
    );
342
343 18
    $this->_errors[] = $error;
344
345 18
    if ($this->checkForDev() === true) {
346
347 18
      if ($this->echo_on_error) {
348 4
        $box_border = $this->css_mysql_box_border;
349 4
        $box_bg = $this->css_mysql_box_bg;
350
351
        echo '
352 4
        <div class="OBJ-mysql-box" style="border:' . $box_border . '; background:' . $box_bg . '; padding:10px; margin:10px;">
353
          <b style="font-size:14px;">MYSQL Error:</b>
354
          <code style="display:block;">
355 4
            file / line: ' . $fileInfo['file'] . ' / ' . $fileInfo['line'] . '
356 4
            ' . $error . '
357
          </code>
358
        </div>
359 4
        ';
360 4
      }
361
362 18
      if ($force_exception_after_error === true) {
363 4
        throw new \Exception($error);
364 14
      } elseif ($force_exception_after_error === false) {
365
        // nothing
366 14
      } elseif ($force_exception_after_error === null) {
367
        // default
368 11
        if ($this->exit_on_error === true) {
369 2
          throw new \Exception($error);
370
        }
371 9
      }
372 12
    }
373 12
  }
374
375
  /**
376
   * Try to get the file & line from the current sql-query.
377
   *
378
   * @return array will return array['file'] and array['line']
379
   */
380 19
  private function getFileAndLineFromSql()
381
  {
382
    // init
383 19
    $return = array();
384 19
    $file = '';
385 19
    $line = '';
386
387 19
    $referrer = debug_backtrace(false, 10);
388
389 19
    foreach ($referrer as $key => $ref) {
390
391
      if (
392 19
          $ref['function'] == 'execSQL'
393 19
          ||
394 19
          $ref['function'] == 'query'
395 19
          ||
396 19
          $ref['function'] == 'qry'
397 19
          ||
398 19
          $ref['function'] == 'insert'
399 19
          ||
400 19
          $ref['function'] == 'update'
401 19
          ||
402 19
          $ref['function'] == 'replace'
403 19
          ||
404 19
          $ref['function'] == 'delete'
405 19
      ) {
406 14
        $file = $referrer[$key]['file'];
407 14
        $line = $referrer[$key]['line'];
408 14
      }
409
410 19
    }
411
412 19
    $return['file'] = $file;
413 19
    $return['line'] = $line;
414
415 19
    return $return;
416
  }
417
418
  /**
419
   * Wrapper-Function for a "Logger"-Class.
420
   *
421
   * INFO:
422
   * The "Logger"-ClassName is set by "$this->logger_class_name",<br />
423
   * the "Logger"-Method is the [0] element from the "$log"-parameter,<br />
424
   * the text you want to log is the [1] element and<br />
425
   * the type you want to log is the next [2] element.
426
   *
427
   * @param string[] $log [method, text, type]<br />e.g.: array('error', 'this is a error', 'sql')
428
   */
429 20
  private function logger(array $log)
430
  {
431 20
    $logMethod = '';
432 20
    $logText = '';
433 20
    $logType = '';
434 20
    $logClass = $this->logger_class_name;
435
436 20
    if (isset($log[0])) {
437 20
      $logMethod = $log[0];
438 20
    }
439 20
    if (isset($log[1])) {
440 20
      $logText = $log[1];
441 20
    }
442 20
    if (isset($log[2])) {
443 1
      $logType = $log[2];
444 1
    }
445
446
    if (
447
        $logClass
448 20
        &&
449
        class_exists($logClass)
450 20
        &&
451
        method_exists($logClass, $logMethod)
452 20
    ) {
453
      $logClass::$logMethod($logText, $logType);
454
    }
455 20
  }
456
457
  /**
458
   * Check is the current user is a developer.
459
   *
460
   * INFO:
461
   * By default we will return "true" if the remote-ip-address is localhost or
462
   * if the script is called via CLI. But you can also overwrite this method or
463
   * you can implement a global "checkForDev()"-function.
464
   *
465
   * @return bool
466
   */
467 18
  protected function checkForDev()
468
  {
469
    // init
470 18
    $return = false;
471
472 18
    if (function_exists('checkForDev')) {
473
      $return = checkForDev();
474
    } else {
475
476
      // for testing with dev-address
477 18
      $noDev = isset($_GET['noDev']) ? (int)$_GET['noDev'] : 0;
478 18
      $remoteIpAddress = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false;
479
480
      if (
481
          $noDev != 1
482 18
          &&
483
          (
484
              $remoteIpAddress == '127.0.0.1'
485 18
              ||
486
              $remoteIpAddress == '::1'
487 18
              ||
488 18
              PHP_SAPI == 'cli'
489 18
          )
490 18
      ) {
491 18
        $return = true;
492 18
      }
493
    }
494
495 18
    return $return;
496
  }
497
498
  /**
499
   * Execute a sql-query and return the result-array for select-statements.
500
   *
501
   * @param $query
502
   *
503
   * @return mixed
504
   * @deprecated
505
   * @throws \Exception
506
   */
507
  public static function qry($query)
508
  {
509
    $db = self::getInstance();
510
511
    $args = func_get_args();
512
    $query = array_shift($args);
513
    $query = str_replace('?', '%s', $query);
514
    $args = array_map(
515
        array(
516
            $db,
517
            'escape',
518
        ),
519
        $args
520
    );
521
    array_unshift($args, $query);
522
    $query = call_user_func_array('sprintf', $args);
523
    $result = $db->query($query);
524
525
    if ($result instanceof Result) {
526
      $return = $result->fetchAllArray();
527
    } else {
528
      $return = $result;
529
    }
530
531
    if ($return || is_array($return)) {
532
      return $return;
533
    } else {
534
      return false;
535
    }
536
  }
537
538
  /**
539
   * getInstance()
540
   *
541
   * @param string      $hostname
542
   * @param string      $username
543
   * @param string      $password
544
   * @param string      $database
545
   * @param string      $port          default is (int)3306
546
   * @param string      $charset       default is 'utf8', but if you need 4-byte chars, then your tables need
547
   *                                   the 'utf8mb4'-charset
548
   * @param bool|string $exit_on_error use a empty string "" or false to disable it
549
   * @param bool|string $echo_on_error use a empty string "" or false to disable it
550
   * @param string      $logger_class_name
551
   * @param string      $logger_level
552
   * @param bool|string $session_to_db use a empty string "" or false to disable it
553
   *
554
   * @return \voku\db\DB
555
   */
556 39
  public static function getInstance($hostname = '', $username = '', $password = '', $database = '', $port = '', $charset = '', $exit_on_error = '', $echo_on_error = '', $logger_class_name = '', $logger_level = '', $session_to_db = '')
557
  {
558
    /**
559
     * @var $instance DB[]
560
     */
561 39
    static $instance = array();
562
563
    /**
564
     * @var $firstInstance DB
565
     */
566 39
    static $firstInstance = null;
567
568
    if (
569 39
        $hostname . $username . $password . $database . $port . $charset == ''
570 39
        &&
571 7
        null !== $firstInstance
572 39
    ) {
573 7
      return $firstInstance;
574
    }
575
576 39
    $connection = md5(
577 39
        $hostname . $username . $password . $database . $port . $charset . (int)$exit_on_error . (int)$echo_on_error . $logger_class_name . $logger_level . (int)$session_to_db
578 39
    );
579
580 39
    if (!isset($instance[$connection])) {
581 10
      $instance[$connection] = new self(
582 10
          $hostname,
583 10
          $username,
584 10
          $password,
585 10
          $database,
586 10
          $port,
587 10
          $charset,
588 10
          $exit_on_error,
589 10
          $echo_on_error,
590 10
          $logger_class_name,
591 10
          $logger_level,
592
          $session_to_db
593 10
      );
594
595 4
      if (null === $firstInstance) {
596 1
        $firstInstance = $instance[$connection];
597 1
      }
598 4
    }
599
600 39
    return $instance[$connection];
601
  }
602
603
  /**
604
   * Execute a sql-query.
605
   *
606
   * @param string        $sql            sql-query
607
   *
608
   * @param array|boolean $params         "array" of sql-query-parameters
609
   *                                      "false" if you don't need any parameter (default)
610
   *
611
   * @return bool|int|Result              "Result" by "<b>SELECT</b>"-queries<br />
612
   *                                      "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
613
   *                                      "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
614
   *                                      "true" by e.g. "DROP"-queries<br />
615
   *                                      "false" on error
616
   *
617
   * @throws \Exception
618
   */
619 22
  public function query($sql = '', $params = false)
620
  {
621 22
    if (!$this->isReady()) {
622
      return false;
623
    }
624
625 22
    if (!$sql || $sql === '') {
626 4
      $this->_displayError('Can\'t execute an empty Query', false);
627
628 4
      return false;
629
    }
630
631
    if (
632
        $params !== false
633 20
        &&
634 1
        is_array($params)
635 20
        &&
636 1
        count($params) > 0
637 20
    ) {
638 1
      $sql = $this->_parseQueryParams($sql, $params);
639 1
    }
640
641 20
    $query_start_time = microtime(true);
642 20
    $result = mysqli_query($this->link, $sql);
643 20
    $query_duration = microtime(true) - $query_start_time;
644 20
    $this->query_count++;
645
646 20
    $resultCount = 0;
647 20
    if ($result instanceof \mysqli_result) {
648 17
      $resultCount = (int)$result->num_rows;
649 17
    }
650 20
    $this->_logQuery($sql, $query_duration, $resultCount);
651
652
    if (
653
        $result !== null
654 20
        &&
655
        $result instanceof \mysqli_result
656 20
    ) {
657
658
      // return query result object
659 17
      return new Result($sql, $result);
660
661
    } else {
662
      // is the query successful
663 17
      if ($result === true) {
664
665 15
        if (preg_match('/^\s*"?(INSERT|UPDATE|DELETE|REPLACE)\s+/i', $sql)) {
666
667
          // it is an "INSERT" || "REPLACE"
668 15
          if ($this->insert_id() > 0) {
669 14
            return (int)$this->insert_id();
670
          }
671
672
          // it is an "UPDATE" || "DELETE"
673 6
          if ($this->affected_rows() > 0) {
674 6
            return (int)$this->affected_rows();
675
          }
676
        }
677
678
        return true;
679
      } else {
680 8
        $this->queryErrorHandling(mysqli_error($this->link), $sql, $params);
681
      }
682
    }
683
684 8
    return false;
685
  }
686
687
  /**
688
   * _parseQueryParams
689
   *
690
   * @param string $sql
691
   * @param array  $params
692
   *
693
   * @return string
694
   */
695 1
  private function _parseQueryParams($sql, array $params)
696
  {
697
698
    // is there anything to parse?
699 1
    if (strpos($sql, '?') === false) {
700
      return $sql;
701
    }
702
703 1
    if (count($params) > 0) {
704 1
      $parseKey = md5(uniqid(mt_rand(), true));
705 1
      $sql = str_replace('?', $parseKey, $sql);
706
707 1
      $k = 0;
708 1
      while (strpos($sql, $parseKey) !== false) {
709 1
        $value = $this->secure($params[$k]);
710 1
        $sql = preg_replace("/$parseKey/", $value, $sql, 1);
711 1
        $k++;
712 1
      }
713 1
    }
714
715 1
    return $sql;
716
  }
717
718
  /**
719
   * Try to secure a variable, so can you use it in sql-queries.
720
   *
721
   * int: (also strings that contains only an int-value)
722
   * 1. parse into (int)
723
   *
724
   * strings:
725
   * 1. check if the string isn't a default mysql-time-function e.g. 'CURDATE()'
726
   * 2. trim whitespace
727
   * 3. trim '
728
   * 4. escape the string (and remove non utf-8 chars)
729
   * 5. trim ' again (because we maybe removed some chars)
730
   * 6. add ' around the new string
731
   *
732
   * @param mixed $var
733
   *
734
   * @return string | null
735
   */
736 14
  public function secure($var)
737
  {
738
    // save the current value as int (for later usage)
739 14
    if (!is_object($var)) {
740 14
      $varInt = (int)$var;
741 14
    }
742
743 14
    if ((isset($varInt) && "$varInt" == $var) || is_int($var) || is_bool($var)) {
0 ignored issues
show
Bug introduced by
The variable $varInt does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
744
745
      // "int" || int || bool
746
747 12
      $var = (int)$var;
748
749 14
    } elseif (is_string($var)) {
750
751
      // "string"
752
753 14
      if (!in_array($var, $this->mysqlDefaultTimeFunctions, true)) {
754 14
        $var = "'" . trim($this->escape(trim(trim((string)$var), "'")), "'") . "'";
755 14
      }
756
757 14 View Code Duplication
    } elseif (is_float($var)) {
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...
758
759
      // float
760
761 2
      $var = number_format((float)str_replace(',', '.', $var), 8, '.', '');
762
763 2
    } elseif (is_array($var)) {
764
765
      // array
766
767 1
      $var = null;
768
769 1
    } elseif ($var instanceof \DateTime) {
770
771
      // "DateTime"-object
772
773
      try {
774 1
        $var = "'" . $this->escape($var->format('Y-m-d H:i:s'), false, false) . "'";
775 1
      } catch (\Exception $e) {
776
        $var = null;
777
      }
778
779 1
    } else {
780
781
      // fallback ...
782
783
      $var = "'" . trim($this->escape(trim(trim((string)$var), "'")), "'") . "'";
784
785
    }
786
787 14
    return $var;
788
  }
789
790
  /**
791
   * Escape
792
   *
793
   * @param mixed $var boolean: convert into "integer"<br />
794
   *                   int: convert into "integer"<br />
795
   *                   float: convert into "float" and replace "," with "."<br />
796
   *                   array: run escape() for every key => value<br />
797
   *                   string: run UTF8::cleanup() and mysqli_real_escape_string()<br />
798
   * @param bool  $stripe_non_utf8
799
   * @param bool  $html_entity_decode
800
   * @param bool  $array_to_string
801
   *
802
   * @return array|bool|float|int|string
803
   */
804 17
  public function escape($var = '', $stripe_non_utf8 = true, $html_entity_decode = true, $array_to_string = false)
805
  {
806
    // save the current value as int (for later usage)
807 17
    if (!is_object($var)) {
808 17
      $varInt = (int)$var;
809 17
    }
810
811 17
    if ((isset($varInt) && "$varInt" == $var) || is_int($var) || is_bool($var)) {
0 ignored issues
show
Bug introduced by
The variable $varInt does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
812
813
      // "int" || int || bool
814
815 3
      return (int)$var;
816
817 17 View Code Duplication
    } elseif (is_float($var)) {
818
819
      // float
820
821 1
      return number_format((float)str_replace(',', '.', $var), 8, '.', '');
822
823 17
    } elseif (is_array($var)) {
824
825
      // array
826
827 1
      $varCleaned = array();
828 1
      foreach ($var as $key => $value) {
829
830 1
        $key = (string)$this->escape($key, $stripe_non_utf8, $html_entity_decode);
831 1
        $value = (string)$this->escape($value, $stripe_non_utf8, $html_entity_decode);
832
833 1
        $varCleaned[$key] = $value;
834 1
      }
835
836 1
      if ($array_to_string === true) {
837 1
        $varCleaned = implode(',', $varCleaned);
838
839 1
        return $varCleaned;
840
      } else {
841 1
        return (array)$varCleaned;
842
      }
843
    }
844
845 17
    if (is_string($var)) {
846
847
      // "string"
848
849 17
      if ($stripe_non_utf8 === true) {
850 17
        $var = UTF8::cleanup($var);
851 17
      }
852
853 17
      if ($html_entity_decode === true) {
854
        // use no-html-entity for db
855 17
        $var = UTF8::html_entity_decode($var);
856 17
      }
857
858 17
      $var = get_magic_quotes_gpc() ? stripslashes($var) : $var;
859
860 17
      $var = mysqli_real_escape_string($this->getLink(), $var);
861
862 17
      return (string)$var;
863
    } else {
864
      return false;
865
    }
866
  }
867
868
  /**
869
   * Get the mysqli-link (link identifier returned by mysqli-connect).
870
   *
871
   * @return \mysqli
872
   */
873 17
  public function getLink()
874
  {
875 17
    return $this->link;
876
  }
877
878
  /**
879
   * Log the current query via "$this->logger".
880
   *
881
   * @param string $sql     sql-query
882
   * @param int    $duration
883
   * @param int    $results result counter
884
   *
885
   * @return bool
886
   */
887 21
  private function _logQuery($sql, $duration, $results)
888
  {
889 21
    $logLevelUse = strtolower($this->logger_level);
890
891
    if (
892
        $logLevelUse != 'trace'
893 21
        &&
894
        $logLevelUse != 'debug'
895 21
    ) {
896 20
      return false;
897
    }
898
899 1
    $info = 'time => ' . round($duration, 5) . ' - ' . 'results => ' . $results . ' - ' . 'SQL => ' . UTF8::htmlentities($sql);
900 1
    $fileInfo = $this->getFileAndLineFromSql();
901
902 1
    $this->logger(
903
        array(
904 1
            'debug',
905 1
            '<strong>' . date('d. m. Y G:i:s') . ' (' . $fileInfo['file'] . ' line: ' . $fileInfo['line'] . '):</strong> ' . $info . '<br>',
906 1
            'sql',
907
        )
908 1
    );
909
910 1
    return true;
911
  }
912
913
  /**
914
   * Returns the auto generated id used in the last query.
915
   *
916
   * @return int|string
917
   */
918 15
  public function insert_id()
919
  {
920 15
    return mysqli_insert_id($this->link);
921
  }
922
923
  /**
924
   * Gets the number of affected rows in a previous MySQL operation.
925
   *
926
   * @return int
927
   */
928 6
  public function affected_rows()
929
  {
930 6
    return mysqli_affected_rows($this->link);
931
  }
932
933
  /**
934
   * Error-handling for the sql-query.
935
   *
936
   * @param string     $errorMsg
937
   * @param string     $sql
938
   * @param array|bool $sqlParams false if there wasn't any parameter
939
   *
940
   * @throws \Exception
941
   */
942 9
  protected function queryErrorHandling($errorMsg, $sql, $sqlParams = false)
943
  {
944 9
    if ($errorMsg == 'DB server has gone away' || $errorMsg == 'MySQL server has gone away') {
945 1
      static $reconnectCounter;
946
947
      // exit if we have more then 3 "DB server has gone away"-errors
948 1
      if ($reconnectCounter > 3) {
949
        $this->mailToAdmin('SQL-Fatal-Error', $errorMsg . ":\n<br />" . $sql, 5);
950
        throw new \Exception($errorMsg);
951
      } else {
952 1
        $this->mailToAdmin('SQL-Error', $errorMsg . ":\n<br />" . $sql);
953
954
        // reconnect
955 1
        $reconnectCounter++;
956 1
        $this->reconnect(true);
957
958
        // re-run the current query
959 1
        $this->query($sql, $sqlParams);
960
      }
961 1
    } else {
962 8
      $this->mailToAdmin('SQL-Warning', $errorMsg . ":\n<br />" . $sql);
963
964
      // this query returned an error, we must display it (only for dev) !!!
965 8
      $this->_displayError($errorMsg . ' | ' . $sql);
966
    }
967 9
  }
968
969
  /**
970
   * send a error mail to the admin / dev
971
   *
972
   * @param string $subject
973
   * @param string $htmlBody
974
   * @param int    $priority
975
   */
976 9
  private function mailToAdmin($subject, $htmlBody, $priority = 3)
977
  {
978 9
    if (function_exists('mailToAdmin')) {
979
      mailToAdmin($subject, $htmlBody, $priority);
980
    } else {
981
982 9
      if ($priority == 3) {
983 9
        $this->logger(
984
            array(
985 9
                'debug',
986 9
                $subject . ' | ' . $htmlBody,
987
            )
988 9
        );
989 9
      } elseif ($priority > 3) {
990
        $this->logger(
991
            array(
992
                'error',
993
                $subject . ' | ' . $htmlBody,
994
            )
995
        );
996
      } elseif ($priority < 3) {
997
        $this->logger(
998
            array(
999
                'info',
1000
                $subject . ' | ' . $htmlBody,
1001
            )
1002
        );
1003
      }
1004
1005
    }
1006 9
  }
1007
1008
  /**
1009
   * Reconnect to the MySQL-Server.
1010
   *
1011
   * @param bool $checkViaPing
1012
   *
1013
   * @return bool
1014
   */
1015 2
  public function reconnect($checkViaPing = false)
1016
  {
1017 2
    $ping = false;
1018
1019 2
    if ($checkViaPing === true) {
1020 2
      $ping = $this->ping();
1021 2
    }
1022
1023 2
    if ($ping !== true) {
1024 2
      $this->connected = false;
1025 2
      $this->connect();
1026 2
    }
1027
1028 2
    return $this->isReady();
1029
  }
1030
1031
  /**
1032
   * Pings a server connection, or tries to reconnect
1033
   * if the connection has gone down.
1034
   *
1035
   * @return boolean
1036
   */
1037 3
  public function ping()
1038
  {
1039
    if (
1040 3
        $this->link
1041 3
        &&
1042 3
        $this->link instanceof \mysqli
1043 3
    ) {
1044
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
1045 3
      return @mysqli_ping($this->link);
1046
    } else {
1047
      return false;
1048
    }
1049
  }
1050
1051
  /**
1052
   * Execute select/insert/update/delete sql-queries.
1053
   *
1054
   * @param string $query    sql-query
1055
   * @param bool   $useCache use cache?
1056
   * @param int    $cacheTTL cache-ttl in seconds
1057
   *
1058
   * @return mixed "array" by "<b>SELECT</b>"-queries<br />
1059
   *               "int" (insert_id) by "<b>INSERT</b>"-queries<br />
1060
   *               "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
1061
   *               "true" by e.g. "DROP"-queries<br />
1062
   *               "false" on error
1063
   *
1064
   */
1065 3
  public static function execSQL($query, $useCache = false, $cacheTTL = 3600)
1066
  {
1067 3
    $db = self::getInstance();
1068
1069 3
    if ($useCache === true) {
1070 1
      $cache = new Cache(null, null, false, $useCache);
1071 1
      $cacheKey = 'sql-' . md5($query);
1072
1073
      if (
1074 1
          $cache->getCacheIsReady() === true
1075 1
          &&
1076 1
          $cache->existsItem($cacheKey)
1077 1
      ) {
1078 1
        return $cache->getItem($cacheKey);
1079
      }
1080
1081 1
    } else {
1082 3
      $cache = false;
1083
    }
1084
1085 3
    $result = $db->query($query);
1086
1087 3
    if ($result instanceof Result) {
1088
1089 1
      $return = $result->fetchAllArray();
1090
1091
      if (
1092 1
          isset($cacheKey)
1093 1
          &&
1094
          $useCache === true
1095 1
          &&
1096
          $cache instanceof Cache
1097 1
          &&
1098 1
          $cache->getCacheIsReady() === true
1099 1
      ) {
1100 1
        $cache->setItem($cacheKey, $return, $cacheTTL);
1101 1
      }
1102
1103 1
    } else {
1104 2
      $return = $result;
1105
    }
1106
1107 3
    return $return;
1108
  }
1109
1110
  /**
1111
   * Get the current charset.
1112
   *
1113
   * @return string
1114
   */
1115 1
  public function get_charset()
1116
  {
1117 1
    return $this->charset;
1118
  }
1119
1120
  /**
1121
   * Set the current charset.
1122
   *
1123
   * @param string $charset
1124
   *
1125
   * @return bool
1126
   */
1127 6
  public function set_charset($charset)
1128
  {
1129 6
    $this->charset = (string)$charset;
1130
1131 6
    $return = mysqli_set_charset($this->link, $charset);
1132
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
1133 6
    @mysqli_query($this->link, 'SET CHARACTER SET ' . $charset);
1134
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
1135 6
    @mysqli_query($this->link, "SET NAMES '" . ($charset == 'utf8' ? 'utf8mb4' : $charset) . "'");
1136
1137 6
    return $return;
1138
  }
1139
1140
  /**
1141
   * __wakeup
1142
   *
1143
   * @return void
1144
   */
1145 1
  public function __wakeup()
1146
  {
1147 1
    $this->reconnect();
1148 1
  }
1149
1150
  /**
1151
   * Get all table-names via "SHOW TABLES".
1152
   *
1153
   * @return array
1154
   */
1155 1
  public function getAllTables()
1156
  {
1157 1
    $query = 'SHOW TABLES';
1158 1
    $result = $this->query($query);
1159
1160 1
    return $result->fetchAllArray();
1161
  }
1162
1163
  /**
1164
   * Execute a sql-multi-query.
1165
   *
1166
   * @param string $sql
1167
   *
1168
   * @return false|Result[] "Result"-Array by "<b>SELECT</b>"-queries<br />
1169
   *                        "boolean" by only "<b>INSERT</b>"-queries<br />
1170
   *                        "boolean" by only (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
1171
   *                        "boolean" by only by e.g. "DROP"-queries<br />
1172
   *
1173
   * @throws \Exception
1174
   */
1175 1
  public function multi_query($sql)
1176
  {
1177 1
    if (!$this->isReady()) {
1178
      return false;
1179
    }
1180
1181 1
    if (!$sql || $sql === '') {
1182 1
      $this->_displayError('Can\'t execute an empty Query', false);
1183
1184 1
      return false;
1185
    }
1186
1187 1
    $query_start_time = microtime(true);
1188 1
    $resultTmp = mysqli_multi_query($this->link, $sql);
1189 1
    $query_duration = microtime(true) - $query_start_time;
1190
1191 1
    $this->_logQuery($sql, $query_duration, 0);
1192
1193 1
    $returnTheResult = false;
1194 1
    $result = array();
1195 1
    if ($resultTmp) {
1196
      do {
1197 1
        $resultTmpInner = mysqli_store_result($this->link);
1198
1199
        if (
1200 1
            null !== $resultTmpInner
1201 1
            &&
1202
            $resultTmpInner instanceof \mysqli_result
1203 1
        ) {
1204 1
          $returnTheResult = true;
1205 1
          $result[] = new Result($sql, $resultTmpInner);
1206 1
        } else {
1207 1
          $errorMsg = mysqli_error($this->link);
1208
1209
          // is the query successful
1210 1
          if ($resultTmpInner === true || !$errorMsg) {
1211 1
            $result[] = true;
1212 1
          } else {
1213
            $result[] = false;
1214
1215
            $this->queryErrorHandling($errorMsg, $sql);
1216
          }
1217
        }
1218 1
      } while (mysqli_more_results($this->link) === true ? mysqli_next_result($this->link) : false);
1219
1220 1
    } else {
1221
1222
      $errorMsg = mysqli_error($this->link);
1223
1224
      if ($this->checkForDev() === true) {
1225
        echo "Info: maybe you have to increase your 'max_allowed_packet = 30M' in the config: 'my.conf' \n<br />";
1226
        echo 'Error:' . $errorMsg;
1227
      }
1228
1229
      $this->mailToAdmin('SQL-Error in mysqli_multi_query', $errorMsg . ":\n<br />" . $sql);
1230
    }
1231
1232
    // return the result only if there was a "SELECT"-query
1233 1
    if ($returnTheResult === true) {
1234 1
      return $result;
1235
    }
1236
1237 1
    if (!in_array(false, $result, true)) {
1238 1
      return true;
1239
    } else {
1240
      return false;
1241
    }
1242
  }
1243
1244
  /**
1245
   * alias: "beginTransaction()"
1246
   */
1247 1
  public function startTransaction()
1248
  {
1249 1
    $this->beginTransaction();
1250 1
  }
1251
1252
  /**
1253
   * Begins a transaction, by turning off auto commit.
1254
   *
1255
   * @return boolean this will return true or false indicating success of transaction
1256
   */
1257 4
  public function beginTransaction()
1258
  {
1259 4
    $this->clearErrors();
1260
1261 4
    if ($this->inTransaction() === true) {
1262 1
      $this->_displayError('Error mysql server already in transaction!', true);
1263
1264
      return false;
1265 4
    } elseif (mysqli_connect_errno()) {
1266
      $this->_displayError('Error connecting to mysql server: ' . mysqli_connect_error(), true);
1267
1268
      return false;
1269
    } else {
1270 4
      $this->_in_transaction = true;
1271 4
      mysqli_autocommit($this->link, false);
1272
1273 4
      return true;
1274
1275
    }
1276
  }
1277
1278
  /**
1279
   * Clear the errors in "$this->_errors".
1280
   *
1281
   * @return bool
1282
   */
1283 4
  public function clearErrors()
1284
  {
1285 4
    $this->_errors = array();
1286
1287 4
    return true;
1288
  }
1289
1290
  /**
1291
   * Check if we are in a transaction.
1292
   *
1293
   * @return boolean
1294
   */
1295 4
  public function inTransaction()
1296
  {
1297 4
    return $this->_in_transaction;
1298
  }
1299
1300
  /**
1301
   * Ends a transaction and commits if no errors, then ends autocommit.
1302
   *
1303
   * @return boolean this will return true or false indicating success of transactions
1304
   */
1305 2
  public function endTransaction()
1306
  {
1307
1308 2
    if (!$this->errors()) {
1309 1
      mysqli_commit($this->link);
1310 1
      $return = true;
1311 1
    } else {
1312 1
      $this->rollback();
1313 1
      $return = false;
1314
    }
1315
1316 2
    mysqli_autocommit($this->link, true);
1317 2
    $this->_in_transaction = false;
1318
1319 2
    return $return;
1320
  }
1321
1322
  /**
1323
   * Get all errors from "$this->_errors".
1324
   *
1325
   * @return array|false false === on errors
1326
   */
1327 2
  public function errors()
1328
  {
1329 2
    return count($this->_errors) > 0 ? $this->_errors : false;
1330
  }
1331
1332
  /**
1333
   * Rollback in a transaction.
1334
   */
1335 2
  public function rollback()
1336
  {
1337
    // init
1338 2
    $return = false;
1339
1340 2
    if ($this->_in_transaction === true) {
1341 2
      $return = mysqli_rollback($this->link);
1342 2
      mysqli_autocommit($this->link, true);
1343 2
      $this->_in_transaction = false;
1344 2
    }
1345
1346 2
    return $return;
1347
  }
1348
1349
  /**
1350
   * Execute a "insert"-query.
1351
   *
1352
   * @param string $table
1353
   * @param array  $data
1354
   *
1355
   * @return false|int false on error
1356
   */
1357 14
  public function insert($table, $data = array())
1358
  {
1359 14
    $table = trim($table);
1360
1361 14
    if ($table === '') {
1362 2
      $this->_displayError('invalid-table-name');
1363
1364 1
      return false;
1365
    }
1366
1367 13
    if (count($data) == 0) {
1368 3
      $this->_displayError('empty-data-for-INSERT');
1369
1370 2
      return false;
1371
    }
1372
1373 11
    $SET = $this->_parseArrayPair($data);
1374
1375 11
    $sql = 'INSERT INTO ' . $this->quote_string($table) . " SET $SET;";
1376
1377 11
    return $this->query($sql);
1378
  }
1379
1380
  /**
1381
   * Parses arrays with value pairs and generates SQL to use in queries.
1382
   *
1383
   * @param array  $arrayPair
1384
   * @param string $glue this is the separator
1385
   *
1386
   * @return string
1387
   */
1388 12
  private function _parseArrayPair($arrayPair, $glue = ',')
1389
  {
1390
    // init
1391 12
    $sql = '';
1392 12
    $pairs = array();
1393
1394 12
    if (!empty($arrayPair)) {
1395
1396 12
      foreach ($arrayPair as $_key => $_value) {
1397 12
        $_connector = '=';
1398 12
        $_key_upper = strtoupper($_key);
1399
1400 12
        if (strpos($_key_upper, ' NOT') !== false) {
1401 2
          $_connector = 'NOT';
1402 2
        }
1403
1404 12
        if (strpos($_key_upper, ' IS') !== false) {
1405 1
          $_connector = 'IS';
1406 1
        }
1407
1408 12
        if (strpos($_key_upper, ' IS NOT') !== false) {
1409 1
          $_connector = 'IS NOT';
1410 1
        }
1411
1412 12
        if (strpos($_key_upper, ' IN') !== false) {
1413 1
          $_connector = 'IN';
1414 1
        }
1415
1416 12
        if (strpos($_key_upper, ' NOT IN') !== false) {
1417 1
          $_connector = 'NOT IN';
1418 1
        }
1419
1420 12
        if (strpos($_key_upper, ' BETWEEN') !== false) {
1421 1
          $_connector = 'BETWEEN';
1422 1
        }
1423
1424 12
        if (strpos($_key_upper, ' NOT BETWEEN') !== false) {
1425 1
          $_connector = 'NOT BETWEEN';
1426 1
        }
1427
1428 12
        if (strpos($_key_upper, ' LIKE') !== false) {
1429 2
          $_connector = 'LIKE';
1430 2
        }
1431
1432 12
        if (strpos($_key_upper, ' NOT LIKE') !== false) {
1433 2
          $_connector = 'NOT LIKE';
1434 2
        }
1435
1436 12 View Code Duplication
        if (strpos($_key_upper, ' >') !== false && strpos($_key_upper, ' =') === false) {
1437 2
          $_connector = '>';
1438 2
        }
1439
1440 12 View Code Duplication
        if (strpos($_key_upper, ' <') !== false && strpos($_key_upper, ' =') === false) {
1441 1
          $_connector = '<';
1442 1
        }
1443
1444 12
        if (strpos($_key_upper, ' >=') !== false) {
1445 2
          $_connector = '>=';
1446 2
        }
1447
1448 12
        if (strpos($_key_upper, ' <=') !== false) {
1449 1
          $_connector = '<=';
1450 1
        }
1451
1452 12
        if (strpos($_key_upper, ' <>') !== false) {
1453 1
          $_connector = '<>';
1454 1
        }
1455
1456
        if (
1457 12
            is_array($_value)
1458 12
            &&
1459
            (
1460
                $_connector == 'NOT IN'
1461 1
                ||
1462
                $_connector == 'IN'
1463 1
            )
1464 12
        ) {
1465 1
          foreach ($_value as $oldKey => $oldValue) {
1466
            /** @noinspection AlterInForeachInspection */
1467 1
            $_value[$oldKey] = $this->secure($oldValue);
1468 1
          }
1469 1
          $_value = '(' . implode(',', $_value) . ')';
1470 1
        } elseif (
1471 12
            is_array($_value)
1472 12
            &&
1473
            (
1474
                $_connector == 'NOT BETWEEN'
1475 1
                ||
1476
                $_connector == 'BETWEEN'
1477
            )
1478 12
        ) {
1479 1
          foreach ($_value as $oldKey => $oldValue) {
1480
            /** @noinspection AlterInForeachInspection */
1481 1
            $_value[$oldKey] = $this->secure($oldValue);
1482 1
          }
1483 1
          $_value = '(' . implode(' AND ', $_value) . ')';
1484 1
        } else {
1485 12
          $_value = $this->secure($_value);
1486
        }
1487
1488 12
        $quoteString = $this->quote_string(trim(str_ireplace($_connector, '', $_key)));
1489 12
        $pairs[] = ' ' . $quoteString . ' ' . $_connector . ' ' . $_value . " \n";
1490 12
      }
1491
1492 12
      $sql = implode($glue, $pairs);
1493 12
    }
1494
1495 12
    return $sql;
1496
  }
1497
1498
  /**
1499
   * Quote && Escape e.g. a table name string.
1500
   *
1501
   * @param string $str
1502
   *
1503
   * @return string
1504
   */
1505 14
  public function quote_string($str)
1506
  {
1507 14
    return '`' . $this->escape($str, false, false) . '`';
1508
  }
1509
1510
  /**
1511
   * Get errors from "$this->_errors".
1512
   *
1513
   * @return array
1514
   */
1515 1
  public function getErrors()
1516
  {
1517 1
    return $this->_errors;
1518
  }
1519
1520
  /**
1521
   * Execute a "replace"-query.
1522
   *
1523
   * @param string $table
1524
   * @param array  $data
1525
   *
1526
   * @return false|int false on error
1527
   */
1528 1
  public function replace($table, $data = array())
1529
  {
1530
1531 1
    $table = trim($table);
1532
1533 1
    if ($table === '') {
1534 1
      $this->_displayError('invalid table name');
1535
1536 1
      return false;
1537
    }
1538
1539 1
    if (count($data) == 0) {
1540 1
      $this->_displayError('empty data for REPLACE');
1541
1542 1
      return false;
1543
    }
1544
1545
    // extracting column names
1546 1
    $columns = array_keys($data);
1547 1
    foreach ($columns as $k => $_key) {
1548
      /** @noinspection AlterInForeachInspection */
1549 1
      $columns[$k] = $this->quote_string($_key);
1550 1
    }
1551
1552 1
    $columns = implode(',', $columns);
1553
1554
    // extracting values
1555 1
    foreach ($data as $k => $_value) {
1556
      /** @noinspection AlterInForeachInspection */
1557 1
      $data[$k] = $this->secure($_value);
1558 1
    }
1559 1
    $values = implode(',', $data);
1560
1561 1
    $sql = 'REPLACE INTO ' . $this->quote_string($table) . " ($columns) VALUES ($values);";
1562
1563 1
    return $this->query($sql);
1564
  }
1565
1566
  /**
1567
   * Execute a "update"-query.
1568
   *
1569
   * @param string       $table
1570
   * @param array        $data
1571
   * @param array|string $where
1572
   *
1573
   * @return false|int false on error
1574
   */
1575 5
  public function update($table, $data = array(), $where = '1=1')
1576
  {
1577 5
    $table = trim($table);
1578
1579 5
    if ($table === '') {
1580 1
      $this->_displayError('invalid table name');
1581
1582 1
      return false;
1583
    }
1584
1585 5
    if (count($data) == 0) {
1586 1
      $this->_displayError('empty data for UPDATE');
1587
1588 1
      return false;
1589
    }
1590
1591 5
    $SET = $this->_parseArrayPair($data);
1592
1593 5
    if (is_string($where)) {
1594 1
      $WHERE = $this->escape($where, false, false);
1595 5
    } elseif (is_array($where)) {
1596 4
      $WHERE = $this->_parseArrayPair($where, 'AND');
1597 4
    } else {
1598 1
      $WHERE = '';
1599
    }
1600
1601 5
    $sql = 'UPDATE ' . $this->quote_string($table) . " SET $SET WHERE ($WHERE);";
1602
1603 5
    return $this->query($sql);
1604
  }
1605
1606
  /**
1607
   * Execute a "delete"-query.
1608
   *
1609
   * @param string       $table
1610
   * @param string|array $where
1611
   *
1612
   * @return false|int false on error
1613
   */
1614 1 View Code Duplication
  public function delete($table, $where)
1615
  {
1616
1617 1
    $table = trim($table);
1618
1619 1
    if ($table === '') {
1620 1
      $this->_displayError('invalid table name');
1621
1622 1
      return false;
1623
    }
1624
1625 1
    if (is_string($where)) {
1626 1
      $WHERE = $this->escape($where, false, false);
1627 1
    } elseif (is_array($where)) {
1628 1
      $WHERE = $this->_parseArrayPair($where, 'AND');
1629 1
    } else {
1630 1
      $WHERE = '';
1631
    }
1632
1633 1
    $sql = 'DELETE FROM ' . $this->quote_string($table) . " WHERE ($WHERE);";
1634
1635 1
    return $this->query($sql);
1636
  }
1637
1638
  /**
1639
   * Execute a "select"-query.
1640
   *
1641
   * @param string       $table
1642
   * @param string|array $where
1643
   *
1644
   * @return false|Result false on error
1645
   */
1646 13 View Code Duplication
  public function select($table, $where = '1=1')
1647
  {
1648
1649 13
    if ($table === '') {
1650 1
      $this->_displayError('invalid table name');
1651
1652 1
      return false;
1653
    }
1654
1655 13
    if (is_string($where)) {
1656 3
      $WHERE = $this->escape($where, false, false);
1657 13
    } elseif (is_array($where)) {
1658 11
      $WHERE = $this->_parseArrayPair($where, 'AND');
1659 11
    } else {
1660 1
      $WHERE = '';
1661
    }
1662
1663 13
    $sql = 'SELECT * FROM ' . $this->quote_string($table) . " WHERE ($WHERE);";
1664
1665 13
    return $this->query($sql);
1666
  }
1667
1668
  /**
1669
   * Get the last sql-error.
1670
   *
1671
   * @return string false on error
1672
   */
1673 1
  public function lastError()
1674
  {
1675 1
    return count($this->_errors) > 0 ? end($this->_errors) : false;
1676
  }
1677
1678
  /**
1679
   * __destruct
1680
   *
1681
   */
1682 1
  public function __destruct()
1683
  {
1684
    // close the connection only if we don't save PHP-SESSION's in DB
1685 1
    if ($this->session_to_db === false) {
1686 1
      $this->close();
1687 1
    }
1688 1
  }
1689
1690
  /**
1691
   * Closes a previously opened database connection.
1692
   */
1693 3
  public function close()
1694
  {
1695 3
    $this->connected = false;
1696
1697 3
    if ($this->link) {
1698 3
      mysqli_close($this->link);
1699 3
    }
1700 3
  }
1701
1702
  /**
1703
   * prevent the instance from being cloned
1704
   *
1705
   * @return void
1706
   */
1707
  private function __clone()
1708
  {
1709
  }
1710
1711
}
1712