Completed
Push — master ( 331f74...a01c40 )
by Lars
09:50
created

DB::getInstance()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 46
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 5

Importance

Changes 7
Bugs 2 Features 3
Metric Value
c 7
b 2
f 3
dl 0
loc 46
ccs 29
cts 29
cp 1
rs 8.4751
cc 5
eloc 26
nc 4
nop 11
crap 5

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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