Completed
Push — master ( 3df618...a0bab0 )
by Lars
02:50
created

DB::errors()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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