Completed
Push — master ( 1cc6cb...bd0260 )
by Lars
05:56 queued 03:15
created

DB::ping()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 7
Bugs 1 Features 2
Metric Value
c 7
b 1
f 2
dl 0
loc 13
ccs 6
cts 7
cp 0.8571
rs 9.4285
cc 3
eloc 8
nc 2
nop 0
crap 3.0261
1
<?php
2
3
namespace voku\db;
4
5
use voku\cache\Cache;
6
use voku\helper\Bootup;
7
use voku\helper\UTF8;
8
9
/**
10
 * DB: this handles DB queries via MySQLi
11
 *
12
 * @package   voku\db
13
 */
14
class DB
15
{
16
17
  /**
18
   * @var int
19
   */
20
  public $query_count = 0;
21
22
  /**
23
   * @var bool
24
   */
25
  protected $exit_on_error = true;
26
27
  /**
28
   * @var bool
29
   */
30
  protected $echo_on_error = true;
31
32
  /**
33
   * @var string
34
   */
35
  protected $css_mysql_box_border = '3px solid red';
36
37
  /**
38
   * @var string
39
   */
40
  protected $css_mysql_box_bg = '#FFCCCC';
41
42
  /**
43
   * @var \mysqli
44
   */
45
  protected $link = false;
46
47
  /**
48
   * @var bool
49
   */
50
  protected $connected = false;
51
52
  /**
53
   * @var array
54
   */
55
  protected $mysqlDefaultTimeFunctions;
56
57
  /**
58
   * @var string
59
   */
60
  private $logger_class_name;
61
62
  /**
63
   * @var string
64
   *
65
   * 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'
66
   */
67
  private $logger_level;
68
69
  /**
70
   * @var string
71
   */
72
  private $hostname = '';
73
74
  /**
75
   * @var string
76
   */
77
  private $username = '';
78
79
  /**
80
   * @var string
81
   */
82
  private $password = '';
83
84
  /**
85
   * @var string
86
   */
87
  private $database = '';
88
89
  /**
90
   * @var int
91
   */
92
  private $port = 3306;
93
94
  /**
95
   * @var string
96
   */
97
  private $charset = 'utf8';
98
99
  /**
100
   * @var string
101
   */
102
  private $socket = '';
103
104
  /**
105
   * @var array
106
   */
107
  private $_errors = array();
108
109
  /**
110
   * @var bool
111
   */
112
  private $session_to_db = false;
113
114
  /**
115
   * @var bool
116
   */
117
  private $_in_transaction = false;
118
119
  /**
120
   * __construct()
121
   *
122
   * @param string         $hostname
123
   * @param string         $username
124
   * @param string         $password
125
   * @param string         $database
126
   * @param int            $port
127
   * @param string         $charset
128
   * @param boolean|string $exit_on_error use a empty string "" or false to disable it
129
   * @param boolean|string $echo_on_error use a empty string "" or false to disable it
130
   * @param string         $logger_class_name
131
   * @param string         $logger_level  'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'
132
   * @param boolean|string $session_to_db use a empty string "" or false to disable it
133
   */
134 10
  protected function __construct($hostname, $username, $password, $database, $port, $charset, $exit_on_error, $echo_on_error, $logger_class_name, $logger_level, $session_to_db)
135
  {
136 10
    $this->connected = false;
137
138 10
    $this->_loadConfig(
139 10
        $hostname,
140 10
        $username,
141 10
        $password,
142 10
        $database,
143 10
        $port,
144 10
        $charset,
145 10
        $exit_on_error,
146 10
        $echo_on_error,
147 10
        $logger_class_name,
148 10
        $logger_level,
149
        $session_to_db
150 10
    );
151
152 7
    $this->connect();
153
154 4
    $this->mysqlDefaultTimeFunctions = array(
155
      // Returns the current date.
156 4
      'CURDATE()',
157
      // CURRENT_DATE	| Synonyms for CURDATE()
158 4
      'CURRENT_DATE()',
159
      // CURRENT_TIME	| Synonyms for CURTIME()
160 4
      'CURRENT_TIME()',
161
      // CURRENT_TIMESTAMP | Synonyms for NOW()
162 4
      'CURRENT_TIMESTAMP()',
163
      // Returns the current time.
164 4
      'CURTIME()',
165
      // 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...
166 4
      'LOCALTIME()',
167
      // 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...
168 4
      'LOCALTIMESTAMP()',
169
      // Returns the current date and time.
170 4
      'NOW()',
171
      // Returns the time at which the function executes.
172 4
      'SYSDATE()',
173
      // Returns a UNIX timestamp.
174 4
      'UNIX_TIMESTAMP()',
175
      // Returns the current UTC date.
176 4
      'UTC_DATE()',
177
      // Returns the current UTC time.
178 4
      'UTC_TIME()',
179
      // Returns the current UTC date and time.
180 4
      'UTC_TIMESTAMP()',
181
    );
182 4
  }
183
184
  /**
185
   * Load the config from the constructor.
186
   *
187
   * @param string         $hostname
188
   * @param string         $username
189
   * @param string         $password
190
   * @param string         $database
191
   * @param int            $port
192
   * @param string         $charset
193
   * @param boolean|string $exit_on_error use a empty string "" or false to disable it
194
   * @param boolean|string $echo_on_error use a empty string "" or false to disable it
195
   * @param string         $logger_class_name
196
   * @param string         $logger_level
197
   * @param boolean|string $session_to_db use a empty string "" or false to disable it
198
   *
199
   * @return bool
200
   */
201 10
  private function _loadConfig($hostname, $username, $password, $database, $port, $charset, $exit_on_error, $echo_on_error, $logger_class_name, $logger_level, $session_to_db)
202
  {
203 10
    $this->hostname = (string)$hostname;
204 10
    $this->username = (string)$username;
205 10
    $this->password = (string)$password;
206 10
    $this->database = (string)$database;
207
208 10
    if ($charset) {
209 4
      $this->charset = (string)$charset;
210 4
    }
211
212 10
    if ($port) {
213 4
      $this->port = (int)$port;
214 4
    } else {
215
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
216 7
      $this->port = @ini_get('mysqli.default_port');
217
    }
218
219 10
    if (!$this->socket) {
220
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
221 10
      $this->socket = @ini_get('mysqli.default_socket');
222 10
    }
223
224 10
    if ($exit_on_error === true || $exit_on_error === false) {
225 10
      $this->exit_on_error = (boolean)$exit_on_error;
226 10
    }
227
228 10
    if ($echo_on_error === true || $echo_on_error === false) {
229 10
      $this->echo_on_error = (boolean)$echo_on_error;
230 10
    }
231
232 10
    $this->logger_class_name = (string)$logger_class_name;
233 10
    $this->logger_level = (string)$logger_level;
234
235 10
    $this->session_to_db = (boolean)$session_to_db;
236
237 10
    return $this->showConfigError();
238
  }
239
240
  /**
241
   * Show config errors by throw exceptions.
242
   *
243
   * @return bool
244
   *
245
   * @throws \Exception
246
   */
247 10
  public function showConfigError()
248
  {
249
250
    if (
251 10
        !$this->hostname
252 10
        ||
253 9
        !$this->username
254 9
        ||
255 8
        !$this->database
256 10
    ) {
257
258 3
      if (!$this->hostname) {
259 1
        throw new \Exception('no-sql-hostname');
260
      }
261
262 2
      if (!$this->username) {
263 1
        throw new \Exception('no-sql-username');
264
      }
265
266 1
      if (!$this->database) {
267 1
        throw new \Exception('no-sql-database');
268
      }
269
270
      return false;
271
    }
272
273 7
    return true;
274
  }
275
276
  /**
277
   * Open a new connection to the MySQL server.
278
   *
279
   * @return boolean
280
   */
281 8
  public function connect()
282
  {
283 8
    if ($this->isReady()) {
284 1
      return true;
285
    }
286
287 8
    mysqli_report(MYSQLI_REPORT_STRICT);
288
    try {
289
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
290 8
      $this->link = @mysqli_connect(
291 8
          $this->hostname,
292 8
          $this->username,
293 8
          $this->password,
294 8
          $this->database,
295 8
          $this->port,
296 8
          $this->socket
297 8
      );
298 8
    } catch (\Exception $e) {
299 3
      $this->_displayError('Error connecting to mysql server: ' . $e->getMessage(), true);
300
    }
301 5
    mysqli_report(MYSQLI_REPORT_OFF);
302
303 5
    if (!$this->link) {
304
      $this->_displayError('Error connecting to mysql server: ' . mysqli_connect_error(), true);
305
    } else {
306 5
      $this->set_charset($this->charset);
307 5
      $this->connected = true;
308
    }
309
310 5
    return $this->isReady();
311
  }
312
313
  /**
314
   * Check if db-connection is ready.
315
   *
316
   * @return boolean
317
   */
318 31
  public function isReady()
319
  {
320 31
    return $this->connected ? true : false;
321
  }
322
323
  /**
324
   * Display SQL-Errors or throw Exceptions (for dev).
325
   *
326
   * @param string       $error
327
   * @param null|boolean $force_exception_after_error
328
   *
329
   * @throws \Exception
330
   */
331 18
  private function _displayError($error, $force_exception_after_error = null)
332
  {
333 18
    $fileInfo = $this->getFileAndLineFromSql();
334
335 18
    $this->logger(
336
        array(
337 18
            'error',
338 18
            '<strong>' . date(
339
                'd. m. Y G:i:s'
340 18
            ) . ' (' . $fileInfo['file'] . ' line: ' . $fileInfo['line'] . ') (sql-error):</strong> ' . $error . '<br>',
341
        )
342 18
    );
343
344 18
    $this->_errors[] = $error;
345
346 18
    if ($this->checkForDev() === true) {
347
348 18
      if ($this->echo_on_error) {
349 4
        $box_border = $this->css_mysql_box_border;
350 4
        $box_bg = $this->css_mysql_box_bg;
351
352
        echo '
353 4
        <div class="OBJ-mysql-box" style="border:' . $box_border . '; background:' . $box_bg . '; padding:10px; margin:10px;">
354
          <b style="font-size:14px;">MYSQL Error:</b>
355
          <code style="display:block;">
356 4
            file / line: ' . $fileInfo['file'] . ' / ' . $fileInfo['line'] . '
357 4
            ' . $error . '
358
          </code>
359
        </div>
360 4
        ';
361 4
      }
362
363 18
      if ($force_exception_after_error === true) {
364 4
        throw new \Exception($error);
365 14
      } elseif ($force_exception_after_error === false) {
366
        // nothing
367 14
      } elseif ($force_exception_after_error === null) {
368
        // default
369 11
        if ($this->exit_on_error === true) {
370 2
          throw new \Exception($error);
371
        }
372 9
      }
373 12
    }
374 12
  }
375
376
  /**
377
   * Try to get the file & line from the current sql-query.
378
   *
379
   * @return array will return array['file'] and array['line']
380
   */
381 19
  private function getFileAndLineFromSql()
382
  {
383
    // init
384 19
    $return = array();
385 19
    $file = '';
386 19
    $line = '';
387
388 19
    if (Bootup::is_php('5.4') === true) {
389
      $referrer = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
390
    } else {
391 19
      $referrer = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
392
    }
393
394 19
    foreach ($referrer as $key => $ref) {
395
396
      if (
397 19
          $ref['function'] == 'execSQL'
398 19
          ||
399 19
          $ref['function'] == 'query'
400 19
          ||
401 19
          $ref['function'] == 'qry'
402 19
          ||
403 19
          $ref['function'] == 'insert'
404 19
          ||
405 19
          $ref['function'] == 'update'
406 19
          ||
407 19
          $ref['function'] == 'replace'
408 19
          ||
409 19
          $ref['function'] == 'delete'
410 19
      ) {
411 14
        $file = $referrer[$key]['file'];
412 14
        $line = $referrer[$key]['line'];
413 14
      }
414
415 19
    }
416
417 19
    $return['file'] = $file;
418 19
    $return['line'] = $line;
419
420 19
    return $return;
421
  }
422
423
  /**
424
   * Wrapper-Function for a "Logger"-Class.
425
   *
426
   * INFO:
427
   * The "Logger"-ClassName is set by "$this->logger_class_name",<br />
428
   * the "Logger"-Method is the [0] element from the "$log"-parameter,<br />
429
   * the text you want to log is the [1] element and<br />
430
   * the type you want to log is the next [2] element.
431
   *
432
   * @param string[] $log [method, text, type]<br />e.g.: array('error', 'this is a error', 'sql')
433
   */
434 20
  private function logger(array $log)
435
  {
436 20
    $logMethod = '';
437 20
    $logText = '';
438 20
    $logType = '';
439 20
    $logClass = $this->logger_class_name;
440
441 20
    if (isset($log[0])) {
442 20
      $logMethod = $log[0];
443 20
    }
444 20
    if (isset($log[1])) {
445 20
      $logText = $log[1];
446 20
    }
447 20
    if (isset($log[2])) {
448 1
      $logType = $log[2];
449 1
    }
450
451
    if (
452
        $logClass
453 20
        &&
454
        class_exists($logClass)
455 20
        &&
456
        method_exists($logClass, $logMethod)
457 20
    ) {
458
      $logClass::$logMethod($logText, $logType);
459
    }
460 20
  }
461
462
  /**
463
   * Check is the current user is a developer.
464
   *
465
   * INFO:
466
   * By default we will return "true" if the remote-ip-address is localhost or
467
   * if the script is called via CLI. But you can also overwrite this method or
468
   * you can implement a global "checkForDev()"-function.
469
   *
470
   * @return bool
471
   */
472 18
  protected function checkForDev()
473
  {
474
    // init
475 18
    $return = false;
476
477 18
    if (function_exists('checkForDev')) {
478
      $return = checkForDev();
479
    } else {
480
481
      // for testing with dev-address
482 18
      $noDev = isset($_GET['noDev']) ? (int)$_GET['noDev'] : 0;
483 18
      $remoteIpAddress = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false;
484
485
      if (
486
          $noDev != 1
487 18
          &&
488
          (
489
              $remoteIpAddress == '127.0.0.1'
490 18
              ||
491
              $remoteIpAddress == '::1'
492 18
              ||
493 18
              PHP_SAPI == 'cli'
494 18
          )
495 18
      ) {
496 18
        $return = true;
497 18
      }
498
    }
499
500 18
    return $return;
501
  }
502
503
  /**
504
   * Execute a sql-query and return the result-array for select-statements.
505
   *
506
   * @param $query
507
   *
508
   * @return mixed
509
   * @deprecated
510
   * @throws \Exception
511
   */
512
  public static function qry($query)
513
  {
514
    $db = self::getInstance();
515
516
    $args = func_get_args();
517
    $query = array_shift($args);
518
    $query = str_replace('?', '%s', $query);
519
    $args = array_map(
520
        array(
521
            $db,
522
            'escape',
523
        ),
524
        $args
525
    );
526
    array_unshift($args, $query);
527
    $query = call_user_func_array('sprintf', $args);
528
    $result = $db->query($query);
529
530
    if ($result instanceof Result) {
531
      $return = $result->fetchAllArray();
532
    } else {
533
      $return = $result;
534
    }
535
536
    if ($return || is_array($return)) {
537
      return $return;
538
    } else {
539
      return false;
540
    }
541
  }
542
543
  /**
544
   * getInstance()
545
   *
546
   * @param string      $hostname
547
   * @param string      $username
548
   * @param string      $password
549
   * @param string      $database
550
   * @param string      $port          default is (int)3306
551
   * @param string      $charset       default is 'utf8', but if you need 4-byte chars, then your tables need
552
   *                                   the 'utf8mb4'-charset
553
   * @param bool|string $exit_on_error use a empty string "" or false to disable it
554
   * @param bool|string $echo_on_error use a empty string "" or false to disable it
555
   * @param string      $logger_class_name
556
   * @param string      $logger_level
557
   * @param bool|string $session_to_db use a empty string "" or false to disable it
558
   *
559
   * @return \voku\db\DB
560
   */
561 41
  public static function getInstance($hostname = '', $username = '', $password = '', $database = '', $port = '', $charset = '', $exit_on_error = '', $echo_on_error = '', $logger_class_name = '', $logger_level = '', $session_to_db = '')
562
  {
563
    /**
564
     * @var $instance DB[]
565
     */
566 41
    static $instance = array();
567
568
    /**
569
     * @var $firstInstance DB
570
     */
571 41
    static $firstInstance = null;
572
573
    if (
574 41
        $hostname . $username . $password . $database . $port . $charset == ''
575 41
        &&
576 9
        null !== $firstInstance
577 41
    ) {
578 9
      return $firstInstance;
579
    }
580
581 41
    $connection = md5(
582 41
        $hostname . $username . $password . $database . $port . $charset . (int)$exit_on_error . (int)$echo_on_error . $logger_class_name . $logger_level . (int)$session_to_db
583 41
    );
584
585 41
    if (!isset($instance[$connection])) {
586 10
      $instance[$connection] = new self(
587 10
          $hostname,
588 10
          $username,
589 10
          $password,
590 10
          $database,
591 10
          $port,
592 10
          $charset,
593 10
          $exit_on_error,
594 10
          $echo_on_error,
595 10
          $logger_class_name,
596 10
          $logger_level,
597
          $session_to_db
598 10
      );
599
600 4
      if (null === $firstInstance) {
601 1
        $firstInstance = $instance[$connection];
602 1
      }
603 4
    }
604
605 41
    return $instance[$connection];
606
  }
607
608
  /**
609
   * Execute a sql-query.
610
   *
611
   * @param string        $sql            sql-query
612
   *
613
   * @param array|boolean $params         "array" of sql-query-parameters
614
   *                                      "false" if you don't need any parameter (default)
615
   *
616
   * @return bool|int|Result              "Result" by "<b>SELECT</b>"-queries<br />
617
   *                                      "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
618
   *                                      "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
619
   *                                      "true" by e.g. "DROP"-queries<br />
620
   *                                      "false" on error
621
   *
622
   * @throws \Exception
623
   */
624 24
  public function query($sql = '', $params = false)
625
  {
626 24
    if (!$this->isReady()) {
627
      return false;
628
    }
629
630 24
    if (!$sql || $sql === '') {
631 4
      $this->_displayError('Can\'t execute an empty Query', false);
632
633 4
      return false;
634
    }
635
636
    if (
637
        $params !== false
638 22
        &&
639 2
        is_array($params)
640 22
        &&
641 2
        count($params) > 0
642 22
    ) {
643 2
      $sql = $this->_parseQueryParams($sql, $params);
644 2
    }
645
646 22
    $query_start_time = microtime(true);
647 22
    $result = mysqli_query($this->link, $sql);
648 22
    $query_duration = microtime(true) - $query_start_time;
649 22
    $this->query_count++;
650
651 22
    $resultCount = 0;
652 22
    if ($result instanceof \mysqli_result) {
653 19
      $resultCount = (int)$result->num_rows;
654 19
    }
655 22
    $this->_logQuery($sql, $query_duration, $resultCount);
656
657
    if (
658
        $result !== null
659 22
        &&
660
        $result instanceof \mysqli_result
661 22
    ) {
662
663
      // return query result object
664 19
      return new Result($sql, $result);
665
666
    } else {
667
      // is the query successful
668 18
      if ($result === true) {
669
670 16
        if (preg_match('/^\s*"?(INSERT|UPDATE|DELETE|REPLACE)\s+/i', $sql)) {
671
672
          // it is an "INSERT" || "REPLACE"
673 16
          if ($this->insert_id() > 0) {
674 15
            return (int)$this->insert_id();
675
          }
676
677
          // it is an "UPDATE" || "DELETE"
678 6
          if ($this->affected_rows() > 0) {
679 6
            return (int)$this->affected_rows();
680
          }
681
        }
682
683
        return true;
684
      } else {
685 8
        $this->queryErrorHandling(mysqli_error($this->link), $sql, $params);
686
      }
687
    }
688
689 8
    return false;
690
  }
691
692
  /**
693
   * _parseQueryParams
694
   *
695
   * @param string $sql
696
   * @param array  $params
697
   *
698
   * @return string
699
   */
700 2
  private function _parseQueryParams($sql, array $params)
701
  {
702
703
    // is there anything to parse?
704 2
    if (strpos($sql, '?') === false) {
705
      return $sql;
706
    }
707
708 2
    if (count($params) > 0) {
709 2
      $parseKey = md5(uniqid(mt_rand(), true));
710 2
      $sql = str_replace('?', $parseKey, $sql);
711
712 2
      $k = 0;
713 2
      while (strpos($sql, $parseKey) !== false) {
714 2
        $value = $this->secure($params[$k]);
715 2
        $sql = preg_replace("/$parseKey/", $value, $sql, 1);
716 2
        $k++;
717 2
      }
718 2
    }
719
720 2
    return $sql;
721
  }
722
723
  /**
724
   * Try to secure a variable, so can you use it in sql-queries.
725
   *
726
   * int: (also strings that contains only an int-value)
727
   * 1. parse into (int)
728
   *
729
   * strings:
730
   * 1. check if the string isn't a default mysql-time-function e.g. 'CURDATE()'
731
   * 2. trim whitespace
732
   * 3. trim '
733
   * 4. escape the string (and remove non utf-8 chars)
734
   * 5. trim ' again (because we maybe removed some chars)
735
   * 6. add ' around the new string
736
   *
737
   * @param mixed $var
738
   *
739
   * @return string | null
740
   */
741 15
  public function secure($var)
742
  {
743
    // save the current value as int (for later usage)
744 15
    if (!is_object($var)) {
745 15
      $varInt = (int)$var;
746 15
    }
747
748
    if (
749 15
        (isset($varInt, $var[0]) && $var[0] != '0' && "$varInt" == $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...
750
        ||
751 15
        is_int($var)
752 15
        ||
753 15
        is_bool($var)
754 15
    ) {
755
756
      // "int" || int || bool
757
758 13
      $var = (int)$var;
759
760 15
    } elseif (is_string($var)) {
761
762
      // "string"
763
764 15
      if (!in_array($var, $this->mysqlDefaultTimeFunctions, true)) {
765 15
        $var = "'" . trim($this->escape(trim(trim((string)$var), "'")), "'") . "'";
766 15
      }
767
768 15 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...
769
770
      // float
771
772 2
      $var = number_format((float)str_replace(',', '.', $var), 8, '.', '');
773
774 2
    } elseif (is_array($var)) {
775
776
      // array
777
778 1
      $var = null;
779
780 1
    } elseif ($var instanceof \DateTime) {
781
782
      // "DateTime"-object
783
784
      try {
785 1
        $var = "'" . $this->escape($var->format('Y-m-d H:i:s'), false, false) . "'";
786 1
      } catch (\Exception $e) {
787
        $var = null;
788
      }
789
790 1
    } else {
791
792
      // fallback ...
793
794
      $var = "'" . trim($this->escape(trim(trim((string)$var), "'")), "'") . "'";
795
796
    }
797
798 15
    return $var;
799
  }
800
801
  /**
802
   * Escape
803
   *
804
   * @param mixed $var boolean: convert into "integer"<br />
805
   *                   int: convert into "integer"<br />
806
   *                   float: convert into "float" and replace "," with "."<br />
807
   *                   array: run escape() for every key => value<br />
808
   *                   string: run UTF8::cleanup() and mysqli_real_escape_string()<br />
809
   * @param bool  $stripe_non_utf8
810
   * @param bool  $html_entity_decode
811
   * @param bool  $array_to_string
812
   *
813
   * @return array|bool|float|int|string
814
   */
815 19
  public function escape($var = '', $stripe_non_utf8 = true, $html_entity_decode = true, $array_to_string = false)
816
  {
817
    // save the current value as int (for later usage)
818 19
    if (!is_object($var)) {
819 19
      $varInt = (int)$var;
820 19
    }
821
822
    if (
823 19
        (isset($varInt, $var[0]) && $var[0] != '0' && "$varInt" == $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...
824
        ||
825 19
        is_int($var)
826 19
        ||
827 19
        is_bool($var)
828 19
    ) {
829
830
      // "int" || int || bool
831
832 4
      return (int)$var;
833
834 19 View Code Duplication
    } elseif (is_float($var)) {
835
836
      // float
837
838 1
      return number_format((float)str_replace(',', '.', $var), 8, '.', '');
839
840 19
    } elseif (is_array($var)) {
841
842
      // array
843
844 1
      $varCleaned = array();
845 1
      foreach ($var as $key => $value) {
846
847 1
        $key = (string)$this->escape($key, $stripe_non_utf8, $html_entity_decode);
848 1
        $value = (string)$this->escape($value, $stripe_non_utf8, $html_entity_decode);
849
850 1
        $varCleaned[$key] = $value;
851 1
      }
852
853 1
      if ($array_to_string === true) {
854 1
        $varCleaned = implode(',', $varCleaned);
855
856 1
        return $varCleaned;
857
      } else {
858 1
        return (array)$varCleaned;
859
      }
860
    }
861
862 19
    if (is_string($var)) {
863
864
      // "string"
865
866 19
      if ($stripe_non_utf8 === true) {
867 19
        $var = UTF8::cleanup($var);
868 19
      }
869
870 19
      if ($html_entity_decode === true) {
871
        // use no-html-entity for db
872 19
        $var = UTF8::html_entity_decode($var);
873 19
      }
874
875 19
      $var = get_magic_quotes_gpc() ? stripslashes($var) : $var;
876
877 19
      $var = mysqli_real_escape_string($this->getLink(), $var);
878
879 19
      return (string)$var;
880
    } else {
881
      return false;
882
    }
883
  }
884
885
  /**
886
   * Get the mysqli-link (link identifier returned by mysqli-connect).
887
   *
888
   * @return \mysqli
889
   */
890 19
  public function getLink()
891
  {
892 19
    return $this->link;
893
  }
894
895
  /**
896
   * Log the current query via "$this->logger".
897
   *
898
   * @param string $sql     sql-query
899
   * @param int    $duration
900
   * @param int    $results result counter
901
   *
902
   * @return bool
903
   */
904 23
  private function _logQuery($sql, $duration, $results)
905
  {
906 23
    $logLevelUse = strtolower($this->logger_level);
907
908
    if (
909
        $logLevelUse != 'trace'
910 23
        &&
911
        $logLevelUse != 'debug'
912 23
    ) {
913 22
      return false;
914
    }
915
916 1
    $info = 'time => ' . round($duration, 5) . ' - ' . 'results => ' . $results . ' - ' . 'SQL => ' . UTF8::htmlentities($sql);
917 1
    $fileInfo = $this->getFileAndLineFromSql();
918
919 1
    $this->logger(
920
        array(
921 1
            'debug',
922 1
            '<strong>' . date('d. m. Y G:i:s') . ' (' . $fileInfo['file'] . ' line: ' . $fileInfo['line'] . '):</strong> ' . $info . '<br>',
923 1
            'sql',
924
        )
925 1
    );
926
927 1
    return true;
928
  }
929
930
  /**
931
   * Returns the auto generated id used in the last query.
932
   *
933
   * @return int|string
934
   */
935 16
  public function insert_id()
936
  {
937 16
    return mysqli_insert_id($this->link);
938
  }
939
940
  /**
941
   * Gets the number of affected rows in a previous MySQL operation.
942
   *
943
   * @return int
944
   */
945 6
  public function affected_rows()
946
  {
947 6
    return mysqli_affected_rows($this->link);
948
  }
949
950
  /**
951
   * Error-handling for the sql-query.
952
   *
953
   * @param string     $errorMsg
954
   * @param string     $sql
955
   * @param array|bool $sqlParams false if there wasn't any parameter
956
   *
957
   * @throws \Exception
958
   */
959 9
  protected function queryErrorHandling($errorMsg, $sql, $sqlParams = false)
960
  {
961 9
    if ($errorMsg == 'DB server has gone away' || $errorMsg == 'MySQL server has gone away') {
962 1
      static $reconnectCounter;
963
964
      // exit if we have more then 3 "DB server has gone away"-errors
965 1
      if ($reconnectCounter > 3) {
966
        $this->mailToAdmin('SQL-Fatal-Error', $errorMsg . ":\n<br />" . $sql, 5);
967
        throw new \Exception($errorMsg);
968
      } else {
969 1
        $this->mailToAdmin('SQL-Error', $errorMsg . ":\n<br />" . $sql);
970
971
        // reconnect
972 1
        $reconnectCounter++;
973 1
        $this->reconnect(true);
974
975
        // re-run the current query
976 1
        $this->query($sql, $sqlParams);
977
      }
978 1
    } else {
979 8
      $this->mailToAdmin('SQL-Warning', $errorMsg . ":\n<br />" . $sql);
980
981
      // this query returned an error, we must display it (only for dev) !!!
982 8
      $this->_displayError($errorMsg . ' | ' . $sql);
983
    }
984 9
  }
985
986
  /**
987
   * send a error mail to the admin / dev
988
   *
989
   * @param string $subject
990
   * @param string $htmlBody
991
   * @param int    $priority
992
   */
993 9
  private function mailToAdmin($subject, $htmlBody, $priority = 3)
994
  {
995 9
    if (function_exists('mailToAdmin')) {
996
      mailToAdmin($subject, $htmlBody, $priority);
997
    } else {
998
999 9
      if ($priority == 3) {
1000 9
        $this->logger(
1001
            array(
1002 9
                'debug',
1003 9
                $subject . ' | ' . $htmlBody,
1004
            )
1005 9
        );
1006 9
      } elseif ($priority > 3) {
1007
        $this->logger(
1008
            array(
1009
                'error',
1010
                $subject . ' | ' . $htmlBody,
1011
            )
1012
        );
1013
      } elseif ($priority < 3) {
1014
        $this->logger(
1015
            array(
1016
                'info',
1017
                $subject . ' | ' . $htmlBody,
1018
            )
1019
        );
1020
      }
1021
1022
    }
1023 9
  }
1024
1025
  /**
1026
   * Reconnect to the MySQL-Server.
1027
   *
1028
   * @param bool $checkViaPing
1029
   *
1030
   * @return bool
1031
   */
1032 2
  public function reconnect($checkViaPing = false)
1033
  {
1034 2
    $ping = false;
1035
1036 2
    if ($checkViaPing === true) {
1037 2
      $ping = $this->ping();
1038 2
    }
1039
1040 2
    if ($ping !== true) {
1041 2
      $this->connected = false;
1042 2
      $this->connect();
1043 2
    }
1044
1045 2
    return $this->isReady();
1046
  }
1047
1048
  /**
1049
   * Pings a server connection, or tries to reconnect
1050
   * if the connection has gone down.
1051
   *
1052
   * @return boolean
1053
   */
1054 3
  public function ping()
1055
  {
1056
    if (
1057 3
        $this->link
1058 3
        &&
1059 3
        $this->link instanceof \mysqli
1060 3
    ) {
1061
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
1062 3
      return @mysqli_ping($this->link);
1063
    } else {
1064
      return false;
1065
    }
1066
  }
1067
1068
  /**
1069
   * Execute select/insert/update/delete sql-queries.
1070
   *
1071
   * @param string $query    sql-query
1072
   * @param bool   $useCache use cache?
1073
   * @param int    $cacheTTL cache-ttl in seconds
1074
   *
1075
   * @return mixed "array" by "<b>SELECT</b>"-queries<br />
1076
   *               "int" (insert_id) by "<b>INSERT</b>"-queries<br />
1077
   *               "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
1078
   *               "true" by e.g. "DROP"-queries<br />
1079
   *               "false" on error
1080
   *
1081
   */
1082 3
  public static function execSQL($query, $useCache = false, $cacheTTL = 3600)
1083
  {
1084 3
    $db = self::getInstance();
1085
1086 3
    if ($useCache === true) {
1087 1
      $cache = new Cache(null, null, false, $useCache);
1088 1
      $cacheKey = 'sql-' . md5($query);
1089
1090
      if (
1091 1
          $cache->getCacheIsReady() === true
1092 1
          &&
1093 1
          $cache->existsItem($cacheKey)
1094 1
      ) {
1095 1
        return $cache->getItem($cacheKey);
1096
      }
1097
1098 1
    } else {
1099 3
      $cache = false;
1100
    }
1101
1102 3
    $result = $db->query($query);
1103
1104 3
    if ($result instanceof Result) {
1105
1106 1
      $return = $result->fetchAllArray();
1107
1108
      if (
1109 1
          isset($cacheKey)
1110 1
          &&
1111
          $useCache === true
1112 1
          &&
1113
          $cache instanceof Cache
1114 1
          &&
1115 1
          $cache->getCacheIsReady() === true
1116 1
      ) {
1117 1
        $cache->setItem($cacheKey, $return, $cacheTTL);
1118 1
      }
1119
1120 1
    } else {
1121 2
      $return = $result;
1122
    }
1123
1124 3
    return $return;
1125
  }
1126
1127
  /**
1128
   * Get the current charset.
1129
   *
1130
   * @return string
1131
   */
1132 1
  public function get_charset()
1133
  {
1134 1
    return $this->charset;
1135
  }
1136
1137
  /**
1138
   * Set the current charset.
1139
   *
1140
   * @param string $charset
1141
   *
1142
   * @return bool
1143
   */
1144 6
  public function set_charset($charset)
1145
  {
1146 6
    $this->charset = (string)$charset;
1147
1148 6
    $return = mysqli_set_charset($this->link, $charset);
1149
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
1150 6
    @mysqli_query($this->link, 'SET CHARACTER SET ' . $charset);
1151
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
1152 6
    @mysqli_query($this->link, "SET NAMES '" . ($charset == 'utf8' ? 'utf8mb4' : $charset) . "'");
1153
1154 6
    return $return;
1155
  }
1156
1157
  /**
1158
   * __wakeup
1159
   *
1160
   * @return void
1161
   */
1162 1
  public function __wakeup()
1163
  {
1164 1
    $this->reconnect();
1165 1
  }
1166
1167
  /**
1168
   * Get all table-names via "SHOW TABLES".
1169
   *
1170
   * @return array
1171
   */
1172 1
  public function getAllTables()
1173
  {
1174 1
    $query = 'SHOW TABLES';
1175 1
    $result = $this->query($query);
1176
1177 1
    return $result->fetchAllArray();
1178
  }
1179
1180
  /**
1181
   * Execute a sql-multi-query.
1182
   *
1183
   * @param string $sql
1184
   *
1185
   * @return false|Result[] "Result"-Array by "<b>SELECT</b>"-queries<br />
1186
   *                        "boolean" by only "<b>INSERT</b>"-queries<br />
1187
   *                        "boolean" by only (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
1188
   *                        "boolean" by only by e.g. "DROP"-queries<br />
1189
   *
1190
   * @throws \Exception
1191
   */
1192 1
  public function multi_query($sql)
1193
  {
1194 1
    if (!$this->isReady()) {
1195
      return false;
1196
    }
1197
1198 1
    if (!$sql || $sql === '') {
1199 1
      $this->_displayError('Can\'t execute an empty Query', false);
1200
1201 1
      return false;
1202
    }
1203
1204 1
    $query_start_time = microtime(true);
1205 1
    $resultTmp = mysqli_multi_query($this->link, $sql);
1206 1
    $query_duration = microtime(true) - $query_start_time;
1207
1208 1
    $this->_logQuery($sql, $query_duration, 0);
1209
1210 1
    $returnTheResult = false;
1211 1
    $result = array();
1212 1
    if ($resultTmp) {
1213
      do {
1214 1
        $resultTmpInner = mysqli_store_result($this->link);
1215
1216
        if (
1217 1
            null !== $resultTmpInner
1218 1
            &&
1219
            $resultTmpInner instanceof \mysqli_result
1220 1
        ) {
1221 1
          $returnTheResult = true;
1222 1
          $result[] = new Result($sql, $resultTmpInner);
1223 1
        } else {
1224 1
          $errorMsg = mysqli_error($this->link);
1225
1226
          // is the query successful
1227 1
          if ($resultTmpInner === true || !$errorMsg) {
1228 1
            $result[] = true;
1229 1
          } else {
1230
            $result[] = false;
1231
1232
            $this->queryErrorHandling($errorMsg, $sql);
1233
          }
1234
        }
1235 1
      } while (mysqli_more_results($this->link) === true ? mysqli_next_result($this->link) : false);
1236
1237 1
    } else {
1238
1239
      $errorMsg = mysqli_error($this->link);
1240
1241
      if ($this->checkForDev() === true) {
1242
        echo "Info: maybe you have to increase your 'max_allowed_packet = 30M' in the config: 'my.conf' \n<br />";
1243
        echo 'Error:' . $errorMsg;
1244
      }
1245
1246
      $this->mailToAdmin('SQL-Error in mysqli_multi_query', $errorMsg . ":\n<br />" . $sql);
1247
    }
1248
1249
    // return the result only if there was a "SELECT"-query
1250 1
    if ($returnTheResult === true) {
1251 1
      return $result;
1252
    }
1253
1254 1
    if (!in_array(false, $result, true)) {
1255 1
      return true;
1256
    } else {
1257
      return false;
1258
    }
1259
  }
1260
1261
  /**
1262
   * alias: "beginTransaction()"
1263
   */
1264 1
  public function startTransaction()
1265
  {
1266 1
    $this->beginTransaction();
1267 1
  }
1268
1269
  /**
1270
   * Begins a transaction, by turning off auto commit.
1271
   *
1272
   * @return boolean this will return true or false indicating success of transaction
1273
   */
1274 4
  public function beginTransaction()
1275
  {
1276 4
    $this->clearErrors();
1277
1278 4
    if ($this->inTransaction() === true) {
1279 1
      $this->_displayError('Error mysql server already in transaction!', true);
1280
1281
      return false;
1282 4
    } elseif (mysqli_connect_errno()) {
1283
      $this->_displayError('Error connecting to mysql server: ' . mysqli_connect_error(), true);
1284
1285
      return false;
1286
    } else {
1287 4
      $this->_in_transaction = true;
1288 4
      mysqli_autocommit($this->link, false);
1289
1290 4
      return true;
1291
1292
    }
1293
  }
1294
1295
  /**
1296
   * Clear the errors in "$this->_errors".
1297
   *
1298
   * @return bool
1299
   */
1300 4
  public function clearErrors()
1301
  {
1302 4
    $this->_errors = array();
1303
1304 4
    return true;
1305
  }
1306
1307
  /**
1308
   * Check if we are in a transaction.
1309
   *
1310
   * @return boolean
1311
   */
1312 4
  public function inTransaction()
1313
  {
1314 4
    return $this->_in_transaction;
1315
  }
1316
1317
  /**
1318
   * Ends a transaction and commits if no errors, then ends autocommit.
1319
   *
1320
   * @return boolean this will return true or false indicating success of transactions
1321
   */
1322 2
  public function endTransaction()
1323
  {
1324
1325 2
    if (!$this->errors()) {
1326 1
      mysqli_commit($this->link);
1327 1
      $return = true;
1328 1
    } else {
1329 1
      $this->rollback();
1330 1
      $return = false;
1331
    }
1332
1333 2
    mysqli_autocommit($this->link, true);
1334 2
    $this->_in_transaction = false;
1335
1336 2
    return $return;
1337
  }
1338
1339
  /**
1340
   * Get all errors from "$this->_errors".
1341
   *
1342
   * @return array|false false === on errors
1343
   */
1344 2
  public function errors()
1345
  {
1346 2
    return count($this->_errors) > 0 ? $this->_errors : false;
1347
  }
1348
1349
  /**
1350
   * Rollback in a transaction.
1351
   */
1352 2
  public function rollback()
1353
  {
1354
    // init
1355 2
    $return = false;
1356
1357 2
    if ($this->_in_transaction === true) {
1358 2
      $return = mysqli_rollback($this->link);
1359 2
      mysqli_autocommit($this->link, true);
1360 2
      $this->_in_transaction = false;
1361 2
    }
1362
1363 2
    return $return;
1364
  }
1365
1366
  /**
1367
   * Execute a "insert"-query.
1368
   *
1369
   * @param string $table
1370
   * @param array  $data
1371
   *
1372
   * @return false|int false on error
1373
   */
1374 15
  public function insert($table, $data = array())
1375
  {
1376 15
    $table = trim($table);
1377
1378 15
    if ($table === '') {
1379 2
      $this->_displayError('invalid-table-name');
1380
1381 1
      return false;
1382
    }
1383
1384 14
    if (count($data) == 0) {
1385 3
      $this->_displayError('empty-data-for-INSERT');
1386
1387 2
      return false;
1388
    }
1389
1390 12
    $SET = $this->_parseArrayPair($data);
1391
1392 12
    $sql = 'INSERT INTO ' . $this->quote_string($table) . " SET $SET;";
1393
1394 12
    return $this->query($sql);
1395
  }
1396
1397
  /**
1398
   * Parses arrays with value pairs and generates SQL to use in queries.
1399
   *
1400
   * @param array  $arrayPair
1401
   * @param string $glue this is the separator
1402
   *
1403
   * @return string
1404
   */
1405 13
  private function _parseArrayPair($arrayPair, $glue = ',')
1406
  {
1407
    // init
1408 13
    $sql = '';
1409 13
    $pairs = array();
1410
1411 13
    if (!empty($arrayPair)) {
1412
1413 13
      foreach ($arrayPair as $_key => $_value) {
1414 13
        $_connector = '=';
1415 13
        $_key_upper = strtoupper($_key);
1416
1417 13
        if (strpos($_key_upper, ' NOT') !== false) {
1418 2
          $_connector = 'NOT';
1419 2
        }
1420
1421 13
        if (strpos($_key_upper, ' IS') !== false) {
1422 1
          $_connector = 'IS';
1423 1
        }
1424
1425 13
        if (strpos($_key_upper, ' IS NOT') !== false) {
1426 1
          $_connector = 'IS NOT';
1427 1
        }
1428
1429 13
        if (strpos($_key_upper, ' IN') !== false) {
1430 1
          $_connector = 'IN';
1431 1
        }
1432
1433 13
        if (strpos($_key_upper, ' NOT IN') !== false) {
1434 1
          $_connector = 'NOT IN';
1435 1
        }
1436
1437 13
        if (strpos($_key_upper, ' BETWEEN') !== false) {
1438 1
          $_connector = 'BETWEEN';
1439 1
        }
1440
1441 13
        if (strpos($_key_upper, ' NOT BETWEEN') !== false) {
1442 1
          $_connector = 'NOT BETWEEN';
1443 1
        }
1444
1445 13
        if (strpos($_key_upper, ' LIKE') !== false) {
1446 2
          $_connector = 'LIKE';
1447 2
        }
1448
1449 13
        if (strpos($_key_upper, ' NOT LIKE') !== false) {
1450 2
          $_connector = 'NOT LIKE';
1451 2
        }
1452
1453 13 View Code Duplication
        if (strpos($_key_upper, ' >') !== false && strpos($_key_upper, ' =') === false) {
1454 2
          $_connector = '>';
1455 2
        }
1456
1457 13 View Code Duplication
        if (strpos($_key_upper, ' <') !== false && strpos($_key_upper, ' =') === false) {
1458 1
          $_connector = '<';
1459 1
        }
1460
1461 13
        if (strpos($_key_upper, ' >=') !== false) {
1462 2
          $_connector = '>=';
1463 2
        }
1464
1465 13
        if (strpos($_key_upper, ' <=') !== false) {
1466 1
          $_connector = '<=';
1467 1
        }
1468
1469 13
        if (strpos($_key_upper, ' <>') !== false) {
1470 1
          $_connector = '<>';
1471 1
        }
1472
1473
        if (
1474 13
            is_array($_value)
1475 13
            &&
1476
            (
1477
                $_connector == 'NOT IN'
1478 1
                ||
1479
                $_connector == 'IN'
1480 1
            )
1481 13
        ) {
1482 1
          foreach ($_value as $oldKey => $oldValue) {
1483
            /** @noinspection AlterInForeachInspection */
1484 1
            $_value[$oldKey] = $this->secure($oldValue);
1485 1
          }
1486 1
          $_value = '(' . implode(',', $_value) . ')';
1487 1
        } elseif (
1488 13
            is_array($_value)
1489 13
            &&
1490
            (
1491
                $_connector == 'NOT BETWEEN'
1492 1
                ||
1493
                $_connector == 'BETWEEN'
1494
            )
1495 13
        ) {
1496 1
          foreach ($_value as $oldKey => $oldValue) {
1497
            /** @noinspection AlterInForeachInspection */
1498 1
            $_value[$oldKey] = $this->secure($oldValue);
1499 1
          }
1500 1
          $_value = '(' . implode(' AND ', $_value) . ')';
1501 1
        } else {
1502 13
          $_value = $this->secure($_value);
1503
        }
1504
1505 13
        $quoteString = $this->quote_string(trim(str_ireplace($_connector, '', $_key)));
1506 13
        $pairs[] = ' ' . $quoteString . ' ' . $_connector . ' ' . $_value . " \n";
1507 13
      }
1508
1509 13
      $sql = implode($glue, $pairs);
1510 13
    }
1511
1512 13
    return $sql;
1513
  }
1514
1515
  /**
1516
   * Quote && Escape e.g. a table name string.
1517
   *
1518
   * @param string $str
1519
   *
1520
   * @return string
1521
   */
1522 15
  public function quote_string($str)
1523
  {
1524 15
    return '`' . $this->escape($str, false, false) . '`';
1525
  }
1526
1527
  /**
1528
   * Get errors from "$this->_errors".
1529
   *
1530
   * @return array
1531
   */
1532 1
  public function getErrors()
1533
  {
1534 1
    return $this->_errors;
1535
  }
1536
1537
  /**
1538
   * Execute a "replace"-query.
1539
   *
1540
   * @param string $table
1541
   * @param array  $data
1542
   *
1543
   * @return false|int false on error
1544
   */
1545 1
  public function replace($table, $data = array())
1546
  {
1547
1548 1
    $table = trim($table);
1549
1550 1
    if ($table === '') {
1551 1
      $this->_displayError('invalid table name');
1552
1553 1
      return false;
1554
    }
1555
1556 1
    if (count($data) == 0) {
1557 1
      $this->_displayError('empty data for REPLACE');
1558
1559 1
      return false;
1560
    }
1561
1562
    // extracting column names
1563 1
    $columns = array_keys($data);
1564 1
    foreach ($columns as $k => $_key) {
1565
      /** @noinspection AlterInForeachInspection */
1566 1
      $columns[$k] = $this->quote_string($_key);
1567 1
    }
1568
1569 1
    $columns = implode(',', $columns);
1570
1571
    // extracting values
1572 1
    foreach ($data as $k => $_value) {
1573
      /** @noinspection AlterInForeachInspection */
1574 1
      $data[$k] = $this->secure($_value);
1575 1
    }
1576 1
    $values = implode(',', $data);
1577
1578 1
    $sql = 'REPLACE INTO ' . $this->quote_string($table) . " ($columns) VALUES ($values);";
1579
1580 1
    return $this->query($sql);
1581
  }
1582
1583
  /**
1584
   * Execute a "update"-query.
1585
   *
1586
   * @param string       $table
1587
   * @param array        $data
1588
   * @param array|string $where
1589
   *
1590
   * @return false|int false on error
1591
   */
1592 5
  public function update($table, $data = array(), $where = '1=1')
1593
  {
1594 5
    $table = trim($table);
1595
1596 5
    if ($table === '') {
1597 1
      $this->_displayError('invalid table name');
1598
1599 1
      return false;
1600
    }
1601
1602 5
    if (count($data) == 0) {
1603 1
      $this->_displayError('empty data for UPDATE');
1604
1605 1
      return false;
1606
    }
1607
1608 5
    $SET = $this->_parseArrayPair($data);
1609
1610 5
    if (is_string($where)) {
1611 1
      $WHERE = $this->escape($where, false, false);
1612 5
    } elseif (is_array($where)) {
1613 4
      $WHERE = $this->_parseArrayPair($where, 'AND');
1614 4
    } else {
1615 1
      $WHERE = '';
1616
    }
1617
1618 5
    $sql = 'UPDATE ' . $this->quote_string($table) . " SET $SET WHERE ($WHERE);";
1619
1620 5
    return $this->query($sql);
1621
  }
1622
1623
  /**
1624
   * Execute a "delete"-query.
1625
   *
1626
   * @param string       $table
1627
   * @param string|array $where
1628
   *
1629
   * @return false|int false on error
1630
   */
1631 1 View Code Duplication
  public function delete($table, $where)
1632
  {
1633
1634 1
    $table = trim($table);
1635
1636 1
    if ($table === '') {
1637 1
      $this->_displayError('invalid table name');
1638
1639 1
      return false;
1640
    }
1641
1642 1
    if (is_string($where)) {
1643 1
      $WHERE = $this->escape($where, false, false);
1644 1
    } elseif (is_array($where)) {
1645 1
      $WHERE = $this->_parseArrayPair($where, 'AND');
1646 1
    } else {
1647 1
      $WHERE = '';
1648
    }
1649
1650 1
    $sql = 'DELETE FROM ' . $this->quote_string($table) . " WHERE ($WHERE);";
1651
1652 1
    return $this->query($sql);
1653
  }
1654
1655
  /**
1656
   * Execute a "select"-query.
1657
   *
1658
   * @param string       $table
1659
   * @param string|array $where
1660
   *
1661
   * @return false|Result false on error
1662
   */
1663 14 View Code Duplication
  public function select($table, $where = '1=1')
1664
  {
1665
1666 14
    if ($table === '') {
1667 1
      $this->_displayError('invalid table name');
1668
1669 1
      return false;
1670
    }
1671
1672 14
    if (is_string($where)) {
1673 3
      $WHERE = $this->escape($where, false, false);
1674 14
    } elseif (is_array($where)) {
1675 12
      $WHERE = $this->_parseArrayPair($where, 'AND');
1676 12
    } else {
1677 1
      $WHERE = '';
1678
    }
1679
1680 14
    $sql = 'SELECT * FROM ' . $this->quote_string($table) . " WHERE ($WHERE);";
1681
1682 14
    return $this->query($sql);
1683
  }
1684
1685
  /**
1686
   * Get the last sql-error.
1687
   *
1688
   * @return string false on error
1689
   */
1690 1
  public function lastError()
1691
  {
1692 1
    return count($this->_errors) > 0 ? end($this->_errors) : false;
1693
  }
1694
1695
  /**
1696
   * __destruct
1697
   *
1698
   */
1699 1
  public function __destruct()
1700
  {
1701
    // close the connection only if we don't save PHP-SESSION's in DB
1702 1
    if ($this->session_to_db === false) {
1703 1
      $this->close();
1704 1
    }
1705 1
  }
1706
1707
  /**
1708
   * Closes a previously opened database connection.
1709
   */
1710 3
  public function close()
1711
  {
1712 3
    $this->connected = false;
1713
1714 3
    if ($this->link) {
1715 3
      mysqli_close($this->link);
1716 3
    }
1717 3
  }
1718
1719
  /**
1720
   * prevent the instance from being cloned
1721
   *
1722
   * @return void
1723
   */
1724
  private function __clone()
1725
  {
1726
  }
1727
1728
}
1729