Completed
Push — master ( 7b5686...866be0 )
by Lars
06:48
created

DB::getDebugger()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

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 1
eloc 2
nc 1
nop 0
crap 1
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
final class DB
14
{
15
16
  /**
17
   * @var int
18
   */
19
  public $query_count = 0;
20
21
  /**
22
   * @var \mysqli
23
   */
24
  private $link = false;
25
26
  /**
27
   * @var bool
28
   */
29
  private $connected = false;
30
31
  /**
32
   * @var array
33
   */
34
  private $mysqlDefaultTimeFunctions;
35
36
  /**
37
   * @var string
38
   */
39
  private $hostname = '';
40
41
  /**
42
   * @var string
43
   */
44
  private $username = '';
45
46
  /**
47
   * @var string
48
   */
49
  private $password = '';
50
51
  /**
52
   * @var string
53
   */
54
  private $database = '';
55
56
  /**
57
   * @var int
58
   */
59
  private $port = 3306;
60
61
  /**
62
   * @var string
63
   */
64
  private $charset = 'utf8';
65
66
  /**
67
   * @var string
68
   */
69
  private $socket = '';
70
71
  /**
72
   * @var bool
73
   */
74
  private $session_to_db = false;
75
76
  /**
77
   * @var bool
78
   */
79
  private $_in_transaction = false;
80
81
  /**
82
   * @var Debug
83
   */
84
  private $_debug;
85
86
  /**
87
   * __construct()
88
   *
89
   * @param string         $hostname
90
   * @param string         $username
91
   * @param string         $password
92
   * @param string         $database
93
   * @param int            $port
94
   * @param string         $charset
95
   * @param boolean|string $exit_on_error use a empty string "" or false to disable it
96
   * @param boolean|string $echo_on_error use a empty string "" or false to disable it
97
   * @param string         $logger_class_name
98
   * @param string         $logger_level  'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'
99
   * @param boolean|string $session_to_db use a empty string "" or false to disable it
100
   */
101 10
  protected function __construct($hostname, $username, $password, $database, $port, $charset, $exit_on_error, $echo_on_error, $logger_class_name, $logger_level, $session_to_db)
102
  {
103 10
    $this->connected = false;
104
105 10
    $this->_debug = new Debug($this);
106
107 10
    $this->_loadConfig(
108
        $hostname,
109
        $username,
110
        $password,
111
        $database,
112
        $port,
113
        $charset,
114
        $exit_on_error,
115
        $echo_on_error,
116
        $logger_class_name,
117
        $logger_level,
118
        $session_to_db
119
    );
120
121 7
    $this->connect();
122
123 4
    $this->mysqlDefaultTimeFunctions = array(
124
      // Returns the current date.
125
      'CURDATE()',
126
      // CURRENT_DATE	| Synonyms for CURDATE()
127
      'CURRENT_DATE()',
128
      // CURRENT_TIME	| Synonyms for CURTIME()
129
      'CURRENT_TIME()',
130
      // CURRENT_TIMESTAMP | Synonyms for NOW()
131
      'CURRENT_TIMESTAMP()',
132
      // Returns the current time.
133
      'CURTIME()',
134
      // 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...
135
      'LOCALTIME()',
136
      // 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...
137
      'LOCALTIMESTAMP()',
138
      // Returns the current date and time.
139
      'NOW()',
140
      // Returns the time at which the function executes.
141
      'SYSDATE()',
142
      // Returns a UNIX timestamp.
143
      'UNIX_TIMESTAMP()',
144
      // Returns the current UTC date.
145
      'UTC_DATE()',
146
      // Returns the current UTC time.
147
      'UTC_TIME()',
148
      // Returns the current UTC date and time.
149
      'UTC_TIMESTAMP()',
150
    );
151 4
  }
152
153
  /**
154
   * Load the config from the constructor.
155
   *
156
   * @param string         $hostname
157
   * @param string         $username
158
   * @param string         $password
159
   * @param string         $database
160
   * @param int            $port
161
   * @param string         $charset
162
   * @param boolean|string $exit_on_error use a empty string "" or false to disable it
163
   * @param boolean|string $echo_on_error use a empty string "" or false to disable it
164
   * @param string         $logger_class_name
165
   * @param string         $logger_level
166
   * @param boolean|string $session_to_db use a empty string "" or false to disable it
167
   *
168
   * @return bool
169
   */
170 10
  private function _loadConfig($hostname, $username, $password, $database, $port, $charset, $exit_on_error, $echo_on_error, $logger_class_name, $logger_level, $session_to_db)
171
  {
172 10
    $this->hostname = (string)$hostname;
173 10
    $this->username = (string)$username;
174 10
    $this->password = (string)$password;
175 10
    $this->database = (string)$database;
176
177 10
    if ($charset) {
178 4
      $this->charset = (string)$charset;
179
    }
180
181 10
    if ($port) {
182 4
      $this->port = (int)$port;
183
    } else {
184
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
185 7
      $this->port = @ini_get('mysqli.default_port');
186
    }
187
188 10
    if (!$this->socket) {
189
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
190 10
      $this->socket = @ini_get('mysqli.default_socket');
191
    }
192
193 10
    if ($exit_on_error === true || $exit_on_error === false) {
194 10
      $this->_debug->setExitOnError($exit_on_error);
195
    }
196
197 10
    if ($echo_on_error === true || $echo_on_error === false) {
198 10
      $this->_debug->setEchoOnError($echo_on_error);
199
    }
200
201 10
    $this->_debug->setLoggerClassName($logger_class_name);
202 10
    $this->_debug->setLoggerLevel($logger_level);
203
204 10
    $this->session_to_db = (boolean)$session_to_db;
205
206 10
    return $this->showConfigError();
207
  }
208
209
  /**
210
   * Show config errors by throw exceptions.
211
   *
212
   * @return bool
213
   *
214
   * @throws \Exception
215
   */
216 10
  public function showConfigError()
217
  {
218
219
    if (
220 10
        !$this->hostname
221
        ||
222 9
        !$this->username
223
        ||
224 10
        !$this->database
225
    ) {
226
227 3
      if (!$this->hostname) {
228 1
        throw new \Exception('no-sql-hostname');
229
      }
230
231 2
      if (!$this->username) {
232 1
        throw new \Exception('no-sql-username');
233
      }
234
235 1
      if (!$this->database) {
236 1
        throw new \Exception('no-sql-database');
237
      }
238
239
      return false;
240
    }
241
242 7
    return true;
243
  }
244
245
  /**
246
   * Open a new connection to the MySQL server.
247
   *
248
   * @return boolean
249
   */
250 8
  public function connect()
251
  {
252 8
    if ($this->isReady()) {
253 1
      return true;
254
    }
255
256 8
    mysqli_report(MYSQLI_REPORT_STRICT);
257
    try {
258 8
      $this->link = mysqli_init();
0 ignored issues
show
Documentation Bug introduced by
It seems like mysqli_init() of type object<mysql> is incompatible with the declared type object<mysqli> of property $link.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
259
260 8
      mysqli_options($this->link, MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true);
261
262
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
263 8
      $this->connected = @mysqli_real_connect(
264 8
          $this->link,
265 8
          $this->hostname,
266 8
          $this->username,
267 8
          $this->password,
268 8
          $this->database,
269 8
          $this->port,
270 8
          $this->socket
271
      );
272 3
    } catch (\Exception $e) {
273 3
      $this->_debug->displayError('Error connecting to mysql server: ' . $e->getMessage(), true);
274
    }
275 5
    mysqli_report(MYSQLI_REPORT_OFF);
276
277 5
    if (!$this->connected) {
278
      $this->_debug->displayError('Error connecting to mysql server: ' . mysqli_connect_error(), true);
279
    } else {
280 5
      $this->set_charset($this->charset);
281
    }
282
283 5
    return $this->isReady();
284
  }
285
286
  /**
287
   * Check if db-connection is ready.
288
   *
289
   * @return boolean
290
   */
291 38
  public function isReady()
292
  {
293 38
    return $this->connected ? true : false;
294
  }
295
296
  /**
297
   * Get a new "Prepare"-Object for your sql-query.
298
   *
299
   * @param string $query
300
   *
301
   * @return Prepare
302
   */
303
  public function prepare($query)
304
  {
305
    return new Prepare($this, $query);
306
  }
307
308
  /**
309
   * Execute a sql-query and return the result-array for select-statements.
310
   *
311
   * @param $query
312
   *
313
   * @return mixed
314
   * @deprecated
315
   * @throws \Exception
316
   */
317
  public static function qry($query)
318
  {
319
    $db = self::getInstance();
320
321
    $args = func_get_args();
322
    $query = array_shift($args);
323
    $query = str_replace('?', '%s', $query);
324
    $args = array_map(
325
        array(
326
            $db,
327
            'escape',
328
        ),
329
        $args
330
    );
331
    array_unshift($args, $query);
332
    $query = call_user_func_array('sprintf', $args);
333
    $result = $db->query($query);
334
335
    if ($result instanceof Result) {
336
      $return = $result->fetchAllArray();
337
    } else {
338
      $return = $result;
339
    }
340
341
    return $return;
342
  }
343
344
  /**
345
   * getInstance()
346
   *
347
   * @param string      $hostname
348
   * @param string      $username
349
   * @param string      $password
350
   * @param string      $database
351
   * @param string      $port          default is (int)3306
352
   * @param string      $charset       default is 'utf8', but if you need 4-byte chars, then your tables need
353
   *                                   the 'utf8mb4'-charset
354
   * @param bool|string $exit_on_error use a empty string "" or false to disable it
355
   * @param bool|string $echo_on_error use a empty string "" or false to disable it
356
   * @param string      $logger_class_name
357
   * @param string      $logger_level
358
   * @param bool|string $session_to_db use a empty string "" or false to disable it
359
   *
360
   * @return \voku\db\DB
361
   */
362 48
  public static function getInstance($hostname = '', $username = '', $password = '', $database = '', $port = '', $charset = '', $exit_on_error = '', $echo_on_error = '', $logger_class_name = '', $logger_level = '', $session_to_db = '')
363
  {
364
    /**
365
     * @var $instance DB[]
366
     */
367 48
    static $instance = array();
368
369
    /**
370
     * @var $firstInstance DB
371
     */
372 48
    static $firstInstance = null;
373
374
    if (
375 48
        $hostname . $username . $password . $database . $port . $charset == ''
376
        &&
377 48
        null !== $firstInstance
378
    ) {
379 9
      return $firstInstance;
380
    }
381
382 48
    $connection = md5(
383 48
        $hostname . $username . $password . $database . $port . $charset . (int)$exit_on_error . (int)$echo_on_error . $logger_class_name . $logger_level . (int)$session_to_db
384
    );
385
386 48
    if (!isset($instance[$connection])) {
387 10
      $instance[$connection] = new self(
388
          $hostname,
389
          $username,
390
          $password,
391
          $database,
392
          $port,
393
          $charset,
394
          $exit_on_error,
395
          $echo_on_error,
396
          $logger_class_name,
397
          $logger_level,
398
          $session_to_db
399
      );
400
401 4
      if (null === $firstInstance) {
402 1
        $firstInstance = $instance[$connection];
403
      }
404
    }
405
406 48
    return $instance[$connection];
407
  }
408
409
  /**
410
   * Execute a sql-query.
411
   *
412
   * @param string        $sql            sql-query
413
   *
414
   * @param array|boolean $params         "array" of sql-query-parameters
415
   *                                      "false" if you don't need any parameter (default)
416
   *
417
   * @return bool|int|Result              "Result" by "<b>SELECT</b>"-queries<br />
418
   *                                      "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
419
   *                                      "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
420
   *                                      "true" by e.g. "DROP"-queries<br />
421
   *                                      "false" on error
422
   *
423
   * @throws \Exception
424
   */
425 28
  public function query($sql = '', $params = false)
426
  {
427 28
    if (!$this->isReady()) {
428
      return false;
429
    }
430
431 28 View Code Duplication
    if (!$sql || $sql === '') {
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...
432 4
      $this->_debug->displayError('Can\'t execute an empty Query', false);
433
434 4
      return false;
435
    }
436
437
    if (
438 26
        $params !== false
439
        &&
440 26
        is_array($params)
441
        &&
442 26
        count($params) > 0
443
    ) {
444 2
      $sql = $this->_parseQueryParams($sql, $params);
445
    }
446
447 26
    $query_start_time = microtime(true);
448 26
    $query_result = mysqli_real_query($this->link, $sql);
449 26
    $query_duration = microtime(true) - $query_start_time;
450
451 26
    $this->query_count++;
452
453 26
    $mysqli_field_count = mysqli_field_count($this->link);
454 26
    if ($mysqli_field_count) {
455 24
      $result = mysqli_store_result($this->link);
456
    } else {
457 17
      $result = $query_result;
458
    }
459
460 26
    if ($result instanceof \mysqli_result) {
461
462
      // log the select query
463 23
      $this->_debug->logQuery($sql, $query_duration, $mysqli_field_count);
464
465
      // return query result object
466 23
      return new Result($sql, $result);
467
468 18
    } elseif ($query_result === true) {
469
470
      // "INSERT" || "REPLACE"
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
471 16 View Code Duplication
      if (preg_match('/^\s*"?(INSERT|REPLACE)\s+/i', $sql)) {
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...
472 15
        $insert_id = (int)$this->insert_id();
473 15
        $this->_debug->logQuery($sql, $query_duration, $insert_id);
474
475 15
        return $insert_id;
476
      }
477
478
      // "UPDATE" || "DELETE"
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
479 7 View Code Duplication
      if (preg_match('/^\s*"?(UPDATE|DELETE)\s+/i', $sql)) {
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...
480 7
        $affected_rows = (int)$this->affected_rows();
481 7
        $this->_debug->logQuery($sql, $query_duration, $affected_rows);
482
483 7
        return $affected_rows;
484
      }
485
486
      // log the ? query
487
      $this->_debug->logQuery($sql, $query_duration, 0);
488
489
      return true;
490
    }
491
492
    // log the error query
493 8
    $this->_debug->logQuery($sql, $query_duration, 0, true);
494
495 8
    return $this->queryErrorHandling(mysqli_error($this->link), $sql, $params);
496
  }
497
498
  /**
499
   * _parseQueryParams
500
   *
501
   * @param string $sql
502
   * @param array  $params
503
   *
504
   * @return string
505
   */
506 2
  private function _parseQueryParams($sql, array $params)
507
  {
508
    // is there anything to parse?
509 2
    if (strpos($sql, '?') === false) {
510
      return $sql;
511
    }
512
513 2
    if (count($params) > 0) {
514 2
      $parseKey = md5(uniqid(mt_rand(), true));
515 2
      $sql = str_replace('?', $parseKey, $sql);
516
517 2
      $k = 0;
518 2
      while (strpos($sql, $parseKey) !== false) {
519 2
        $value = $this->secure($params[$k]);
520 2
        $sql = preg_replace("/$parseKey/", $value, $sql, 1);
521 2
        $k++;
522
      }
523
    }
524
525 2
    return $sql;
526
  }
527
528
  /**
529
   * Try to secure a variable, so can you use it in sql-queries.
530
   *
531
   * int: (also strings that contains only an int-value)
532
   * 1. parse into (int)
533
   *
534
   * strings:
535
   * 1. check if the string isn't a default mysql-time-function e.g. 'CURDATE()'
536
   * 2. trim whitespace
537
   * 3. trim '
538
   * 4. escape the string (and remove non utf-8 chars)
539
   * 5. trim ' again (because we maybe removed some chars)
540
   * 6. add ' around the new string
541
   *
542
   * @param mixed $var
543
   *
544
   * @return string | null
545
   */
546 19
  public function secure($var)
547
  {
548
    // save the current value as int (for later usage)
549 19
    if (!is_object($var)) {
550 19
      $varInt = (int)$var;
551
    }
552
553
    /** @noinspection TypeUnsafeComparisonInspection */
554
    if (
555 19
        is_int($var)
556
        ||
557 15
        is_bool($var)
558
        ||
559 19
        (isset($varInt, $var[0]) && $var[0] != '0' && "$varInt" == $var)
0 ignored issues
show
Bug introduced by
The variable $varInt does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
560
    ) {
561
562
      // "int" || int || bool
563
564 17
      $var = (int)$var;
565
566 15
    } elseif (is_string($var)) {
567
568
      // "string"
569
570 15
      if (!in_array($var, $this->mysqlDefaultTimeFunctions, true)) {
571 15
        $var = "'" . trim($this->escape(trim(trim((string)$var), "'")), "'") . "'";
572
      }
573
574 2 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...
575
576
      // float
577
578 2
      $var = number_format((float)str_replace(',', '.', $var), 8, '.', '');
579
580 1
    } elseif (is_array($var)) {
581
582
      // array
583
584 1
      $var = null;
585
586
    } elseif ($var instanceof \DateTime) {
587
588
      // "DateTime"-object
589
590
      try {
591 1
        $var = "'" . $this->escape($var->format('Y-m-d H:i:s'), false) . "'";
592
      } catch (\Exception $e) {
593 1
        $var = null;
594
      }
595
596
    } else {
597
598
      // fallback ...
599
600
      $var = "'" . trim($this->escape(trim(trim((string)$var), "'")), "'") . "'";
601
602
    }
603
604 19
    return $var;
605
  }
606
607
  /**
608
   * Escape
609
   *
610
   * @param mixed $var boolean: convert into "integer"<br />
611
   *                   int: convert into "integer"<br />
612
   *                   float: convert into "float" and replace "," with "."<br />
613
   *                   array: run escape() for every key => value<br />
614
   *                   string: run UTF8::cleanup() and mysqli_real_escape_string()<br />
615
   * @param bool  $stripe_non_utf8
616
   * @param bool  $html_entity_decode
617
   * @param bool  $array_to_string
618
   *
619
   * @return array|bool|float|int|string
620
   */
621 25
  public function escape($var = '', $stripe_non_utf8 = true, $html_entity_decode = false, $array_to_string = false)
622
  {
623
    // save the current value as int (for later usage)
624 25
    if (!is_object($var)) {
625 25
      $varInt = (int)$var;
626
    }
627
628
    /** @noinspection TypeUnsafeComparisonInspection */
629
    if (
630 25
        is_int($var)
631
        ||
632 25
        is_bool($var)
633
        ||
634 25
        (isset($varInt, $var[0]) && $var[0] != '0' && "$varInt" == $var)
0 ignored issues
show
Bug introduced by
The variable $varInt does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
635
    ) {
636
637
      // "int" || int || bool
638
639 5
      return (int)$var;
640
641 25 View Code Duplication
    } elseif (is_float($var)) {
642
643
      // float
644
645 2
      return number_format((float)str_replace(',', '.', $var), 8, '.', '');
646
647 25
    } elseif (is_array($var)) {
648
649
      // array
650
651 1
      $varCleaned = array();
652 1
      foreach ($var as $key => $value) {
653
654 1
        $key = (string)$this->escape($key, $stripe_non_utf8, $html_entity_decode);
655 1
        $value = (string)$this->escape($value, $stripe_non_utf8, $html_entity_decode);
656
657 1
        $varCleaned[$key] = $value;
658
      }
659
660 1
      if ($array_to_string === true) {
661 1
        $varCleaned = implode(',', $varCleaned);
662
663 1
        return $varCleaned;
664
      } else {
665 1
        return (array)$varCleaned;
666
      }
667
    }
668
669 25
    if (is_string($var)) {
670
671
      // "string"
672
673 25
      if ($stripe_non_utf8 === true) {
674 23
        $var = UTF8::cleanup($var);
675
      }
676
677 25
      if ($html_entity_decode === true) {
678
        // use no-html-entity for db
679 1
        $var = UTF8::html_entity_decode($var);
680
      }
681
682 25
      $var = get_magic_quotes_gpc() ? stripslashes($var) : $var;
683
684 25
      $var = mysqli_real_escape_string($this->getLink(), $var);
685
686 25
      return (string)$var;
687
    } else {
688
      return false;
689
    }
690
  }
691
692
  /**
693
   * Get the mysqli-link (link identifier returned by mysqli-connect).
694
   *
695
   * @return \mysqli
696
   */
697 27
  public function getLink()
698
  {
699 27
    return $this->link;
700
  }
701
702
  /**
703
   * Returns the auto generated id used in the last query.
704
   *
705
   * @return int|string
706
   */
707 15
  public function insert_id()
708
  {
709 15
    return mysqli_insert_id($this->link);
710
  }
711
712
  /**
713
   * Gets the number of affected rows in a previous MySQL operation.
714
   *
715
   * @return int
716
   */
717 7
  public function affected_rows()
718
  {
719 7
    return mysqli_affected_rows($this->link);
720
  }
721
722
  /**
723
   * Error-handling for the sql-query.
724
   *
725
   * @param string     $errorMsg
726
   * @param string     $sql
727
   * @param array|bool $sqlParams false if there wasn't any parameter
728
   *
729
   * @throws \Exception
730
   *
731
   * @return bool
732
   */
733 9 View Code Duplication
  protected function queryErrorHandling($errorMsg, $sql, $sqlParams = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
734
  {
735 9
    if ($errorMsg === 'DB server has gone away' || $errorMsg === 'MySQL server has gone away') {
736 1
      static $reconnectCounter;
737
738
      // exit if we have more then 3 "DB server has gone away"-errors
739 1
      if ($reconnectCounter > 3) {
740
        $this->_debug->mailToAdmin('SQL-Fatal-Error', $errorMsg . ":\n<br />" . $sql, 5);
741
        throw new \Exception($errorMsg);
742
      } else {
743 1
        $this->_debug->mailToAdmin('SQL-Error', $errorMsg . ":\n<br />" . $sql);
744
745
        // reconnect
746 1
        $reconnectCounter++;
747 1
        $this->reconnect(true);
748
749
        // re-run the current query
750 1
        return $this->query($sql, $sqlParams);
751
      }
752
    } else {
753 8
      $this->_debug->mailToAdmin('SQL-Warning', $errorMsg . ":\n<br />" . $sql);
754
755
      // this query returned an error, we must display it (only for dev) !!!
756 8
      $this->_debug->displayError($errorMsg . ' | ' . $sql);
757
    }
758
759 8
    return false;
760
  }
761
762
  /**
763
   * Reconnect to the MySQL-Server.
764
   *
765
   * @param bool $checkViaPing
766
   *
767
   * @return bool
768
   */
769 2
  public function reconnect($checkViaPing = false)
770
  {
771 2
    $ping = false;
772
773 2
    if ($checkViaPing === true) {
774 2
      $ping = $this->ping();
775
    }
776
777 2
    if ($ping !== true) {
778 2
      $this->connected = false;
779 2
      $this->connect();
780
    }
781
782 2
    return $this->isReady();
783
  }
784
785
  /**
786
   * Pings a server connection, or tries to reconnect
787
   * if the connection has gone down.
788
   *
789
   * @return boolean
790
   */
791 3
  public function ping()
792
  {
793
    if (
794 3
        $this->link
795
        &&
796 3
        $this->link instanceof \mysqli
797
    ) {
798
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
799 3
      return @mysqli_ping($this->link);
800
    } else {
801
      return false;
802
    }
803
  }
804
805
  /**
806
   * Execute select/insert/update/delete sql-queries.
807
   *
808
   * @param string $query    sql-query
809
   * @param bool   $useCache use cache?
810
   * @param int    $cacheTTL cache-ttl in seconds
811
   *
812
   * @return mixed "array" by "<b>SELECT</b>"-queries<br />
813
   *               "int" (insert_id) by "<b>INSERT</b>"-queries<br />
814
   *               "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
815
   *               "true" by e.g. "DROP"-queries<br />
816
   *               "false" on error
817
   */
818 3
  public static function execSQL($query, $useCache = false, $cacheTTL = 3600)
819
  {
820 3
    $db = self::getInstance();
821
822 3
    if ($useCache === true) {
823 1
      $cache = new Cache(null, null, false, $useCache);
824 1
      $cacheKey = 'sql-' . md5($query);
825
826
      if (
827 1
          $cache->getCacheIsReady() === true
828
          &&
829 1
          $cache->existsItem($cacheKey)
830
      ) {
831 1
        return $cache->getItem($cacheKey);
832
      }
833
834
    } else {
835 3
      $cache = false;
836
    }
837
838 3
    $result = $db->query($query);
839
840 3
    if ($result instanceof Result) {
841
842 1
      $return = $result->fetchAllArray();
843
844
      if (
845 1
          isset($cacheKey)
846
          &&
847 1
          $useCache === true
848
          &&
849 1
          $cache instanceof Cache
850
          &&
851 1
          $cache->getCacheIsReady() === true
852
      ) {
853 1
        $cache->setItem($cacheKey, $return, $cacheTTL);
854
      }
855
856
    } else {
857 2
      $return = $result;
858
    }
859
860 3
    return $return;
861
  }
862
863
  /**
864
   * Get the current charset.
865
   *
866
   * @return string
867
   */
868 1
  public function get_charset()
869
  {
870 1
    return $this->charset;
871
  }
872
873
  /**
874
   * Set the current charset.
875
   *
876
   * @param string $charset
877
   *
878
   * @return bool
879
   */
880 6
  public function set_charset($charset)
881
  {
882 6
    $this->charset = (string)$charset;
883
884 6
    $return = mysqli_set_charset($this->link, $charset);
885
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
886 6
    @mysqli_query($this->link, 'SET CHARACTER SET ' . $charset);
887
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
888 6
    @mysqli_query($this->link, "SET NAMES '" . ($charset === 'utf8' ? 'utf8mb4' : $charset) . "'");
889
890 6
    return $return;
891
  }
892
893
  /**
894
   * __wakeup
895
   *
896
   * @return void
897
   */
898 1
  public function __wakeup()
899
  {
900 1
    $this->reconnect();
901 1
  }
902
903
  /**
904
   * Get all table-names via "SHOW TABLES".
905
   *
906
   * @return array
907
   */
908 1
  public function getAllTables()
909
  {
910 1
    $query = 'SHOW TABLES';
911 1
    $result = $this->query($query);
912
913 1
    return $result->fetchAllArray();
914
  }
915
916
  /**
917
   * Execute a sql-multi-query.
918
   *
919
   * @param string $sql
920
   *
921
   * @return false|Result[] "Result"-Array by "<b>SELECT</b>"-queries<br />
922
   *                        "boolean" by only "<b>INSERT</b>"-queries<br />
923
   *                        "boolean" by only (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
924
   *                        "boolean" by only by e.g. "DROP"-queries<br />
925
   *
926
   * @throws \Exception
927
   */
928 1
  public function multi_query($sql)
929
  {
930 1
    if (!$this->isReady()) {
931
      return false;
932
    }
933
934 1 View Code Duplication
    if (!$sql || $sql === '') {
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...
935 1
      $this->_debug->displayError('Can\'t execute an empty Query', false);
936
937 1
      return false;
938
    }
939
940 1
    $query_start_time = microtime(true);
941 1
    $resultTmp = mysqli_multi_query($this->link, $sql);
942 1
    $query_duration = microtime(true) - $query_start_time;
943
944 1
    $this->_debug->logQuery($sql, $query_duration, 0);
945
946 1
    $returnTheResult = false;
947 1
    $result = array();
948 1
    if ($resultTmp) {
949
      do {
950 1
        $resultTmpInner = mysqli_store_result($this->link);
951
952 1
        if ($resultTmpInner instanceof \mysqli_result) {
953 1
          $returnTheResult = true;
954 1
          $result[] = new Result($sql, $resultTmpInner);
955
        } else {
956 1
          $errorMsg = mysqli_error($this->link);
957
958
          // is the query successful
959 1
          if ($resultTmpInner === true || !$errorMsg) {
960 1
            $result[] = true;
961
          } else {
962
            $result[] = $this->queryErrorHandling($errorMsg, $sql);
963
          }
964
        }
965 1
      } while (mysqli_more_results($this->link) === true ? mysqli_next_result($this->link) : false);
966
967
    } else {
968
969
      $errorMsg = mysqli_error($this->link);
970
971
      if ($this->_debug->checkForDev() === true) {
972
        echo "Info: maybe you have to increase your 'max_allowed_packet = 30M' in the config: 'my.conf' \n<br />";
973
        echo 'Error:' . $errorMsg;
974
      }
975
976
      $this->_debug->mailToAdmin('SQL-Error in mysqli_multi_query', $errorMsg . ":\n<br />" . $sql);
977
    }
978
979
    // return the result only if there was a "SELECT"-query
980 1
    if ($returnTheResult === true) {
981 1
      return $result;
982
    }
983
984 1
    if (!in_array(false, $result, true)) {
985 1
      return true;
986
    } else {
987
      return false;
988
    }
989
  }
990
991
  /**
992
   * alias: "beginTransaction()"
993
   */
994 1
  public function startTransaction()
995
  {
996 1
    $this->beginTransaction();
997 1
  }
998
999
  /**
1000
   * Begins a transaction, by turning off auto commit.
1001
   *
1002
   * @return boolean this will return true or false indicating success of transaction
1003
   */
1004 4
  public function beginTransaction()
1005
  {
1006 4
    $this->clearErrors();
1007
1008 4
    if ($this->inTransaction() === true) {
1009 1
      $this->_debug->displayError('Error mysql server already in transaction!', true);
1010
1011
      return false;
1012 4
    } elseif (mysqli_connect_errno()) {
1013
      $this->_debug->displayError('Error connecting to mysql server: ' . mysqli_connect_error(), true);
1014
1015
      return false;
1016
    } else {
1017 4
      $this->_in_transaction = true;
1018 4
      mysqli_autocommit($this->link, false);
1019
1020 4
      return true;
1021
1022
    }
1023
  }
1024
1025
  /**
1026
   * Clear the errors in "_debug->_errors".
1027
   *
1028
   * @return bool
1029
   */
1030 4
  public function clearErrors()
1031
  {
1032 4
    return $this->_debug->clearErrors();
1033
  }
1034
1035
  /**
1036
   * Check if we are in a transaction.
1037
   *
1038
   * @return boolean
1039
   */
1040 4
  public function inTransaction()
1041
  {
1042 4
    return $this->_in_transaction;
1043
  }
1044
1045
  /**
1046
   * Ends a transaction and commits if no errors, then ends autocommit.
1047
   *
1048
   * @return boolean this will return true or false indicating success of transactions
1049
   */
1050 2
  public function endTransaction()
1051
  {
1052
1053 2
    if (!$this->errors()) {
1054 1
      mysqli_commit($this->link);
1055 1
      $return = true;
1056
    } else {
1057 1
      $this->rollback();
1058 1
      $return = false;
1059
    }
1060
1061 2
    mysqli_autocommit($this->link, true);
1062 2
    $this->_in_transaction = false;
1063
1064 2
    return $return;
1065
  }
1066
1067
  /**
1068
   * Get all errors from "$this->_errors".
1069
   *
1070
   * @return array|false false === on errors
1071
   */
1072 2
  public function errors()
1073
  {
1074 2
    $errors = $this->_debug->getErrors();
1075
1076 2
    return count($errors) > 0 ? $errors : false;
1077
  }
1078
1079
  /**
1080
   * Rollback in a transaction.
1081
   */
1082 2
  public function rollback()
1083
  {
1084
    // init
1085 2
    $return = false;
1086
1087 2
    if ($this->_in_transaction === true) {
1088 2
      $return = mysqli_rollback($this->link);
1089 2
      mysqli_autocommit($this->link, true);
1090 2
      $this->_in_transaction = false;
1091
    }
1092
1093 2
    return $return;
1094
  }
1095
1096
  /**
1097
   * Execute a "insert"-query.
1098
   *
1099
   * @param string $table
1100
   * @param array  $data
1101
   *
1102
   * @return false|int false on error
1103
   */
1104 15
  public function insert($table, $data = array())
1105
  {
1106 15
    $table = trim($table);
1107
1108 15
    if ($table === '') {
1109 2
      $this->_debug->displayError('invalid table name');
1110
1111 1
      return false;
1112
    }
1113
1114 14
    if (count($data) === 0) {
1115 3
      $this->_debug->displayError('empty data for INSERT');
1116
1117 2
      return false;
1118
    }
1119
1120 12
    $SET = $this->_parseArrayPair($data);
1121
1122 12
    $sql = 'INSERT INTO ' . $this->quote_string($table) . " SET $SET;";
1123
1124 12
    return $this->query($sql);
1125
  }
1126
1127
  /**
1128
   * Parses arrays with value pairs and generates SQL to use in queries.
1129
   *
1130
   * @param array  $arrayPair
1131
   * @param string $glue this is the separator
1132
   *
1133
   * @return string
1134
   */
1135 17
  private function _parseArrayPair($arrayPair, $glue = ',')
1136
  {
1137
    // init
1138 17
    $sql = '';
1139 17
    $pairs = array();
1140
1141
    /** @noinspection IsEmptyFunctionUsageInspection */
1142 17
    if (!empty($arrayPair)) {
1143
1144 17
      foreach ($arrayPair as $_key => $_value) {
1145 17
        $_connector = '=';
1146 17
        $_key_upper = strtoupper($_key);
1147
1148 17
        if (strpos($_key_upper, ' NOT') !== false) {
1149 2
          $_connector = 'NOT';
1150
        }
1151
1152 17
        if (strpos($_key_upper, ' IS') !== false) {
1153 1
          $_connector = 'IS';
1154
        }
1155
1156 17
        if (strpos($_key_upper, ' IS NOT') !== false) {
1157 1
          $_connector = 'IS NOT';
1158
        }
1159
1160 17
        if (strpos($_key_upper, ' IN') !== false) {
1161 1
          $_connector = 'IN';
1162
        }
1163
1164 17
        if (strpos($_key_upper, ' NOT IN') !== false) {
1165 1
          $_connector = 'NOT IN';
1166
        }
1167
1168 17
        if (strpos($_key_upper, ' BETWEEN') !== false) {
1169 1
          $_connector = 'BETWEEN';
1170
        }
1171
1172 17
        if (strpos($_key_upper, ' NOT BETWEEN') !== false) {
1173 1
          $_connector = 'NOT BETWEEN';
1174
        }
1175
1176 17
        if (strpos($_key_upper, ' LIKE') !== false) {
1177 2
          $_connector = 'LIKE';
1178
        }
1179
1180 17
        if (strpos($_key_upper, ' NOT LIKE') !== false) {
1181 2
          $_connector = 'NOT LIKE';
1182
        }
1183
1184 17 View Code Duplication
        if (strpos($_key_upper, ' >') !== false && strpos($_key_upper, ' =') === false) {
1185 2
          $_connector = '>';
1186
        }
1187
1188 17 View Code Duplication
        if (strpos($_key_upper, ' <') !== false && strpos($_key_upper, ' =') === false) {
1189 1
          $_connector = '<';
1190
        }
1191
1192 17
        if (strpos($_key_upper, ' >=') !== false) {
1193 2
          $_connector = '>=';
1194
        }
1195
1196 17
        if (strpos($_key_upper, ' <=') !== false) {
1197 1
          $_connector = '<=';
1198
        }
1199
1200 17
        if (strpos($_key_upper, ' <>') !== false) {
1201 1
          $_connector = '<>';
1202
        }
1203
1204 17
        if (is_array($_value) === true) {
1205 1
          foreach ($_value as $oldKey => $oldValue) {
1206 1
            $_value[$oldKey] = $this->secure($oldValue);
1207
          }
1208
1209 1
          if ($_connector === 'NOT IN' || $_connector === 'IN') {
1210 1
            $_value = '(' . implode(',', $_value) . ')';
1211 1
          } elseif ($_connector === 'NOT BETWEEN' || $_connector === 'BETWEEN') {
1212 1
            $_value = '(' . implode(' AND ', $_value) . ')';
1213
          }
1214
1215
        } else {
1216 17
          $_value = $this->secure($_value);
1217
        }
1218
1219 17
        $quoteString = $this->quote_string(trim(str_ireplace($_connector, '', $_key)));
1220 17
        $pairs[] = ' ' . $quoteString . ' ' . $_connector . ' ' . $_value . " \n";
1221
      }
1222
1223 17
      $sql = implode($glue, $pairs);
1224
    }
1225
1226 17
    return $sql;
1227
  }
1228
1229
  /**
1230
   * Quote && Escape e.g. a table name string.
1231
   *
1232
   * @param string $str
1233
   *
1234
   * @return string
1235
   */
1236 19
  public function quote_string($str)
1237
  {
1238 19
    return '`' . $this->escape($str, false) . '`';
1239
  }
1240
1241
  /**
1242
   * Get errors from "$this->_errors".
1243
   *
1244
   * @return array
1245
   */
1246 1
  public function getErrors()
1247
  {
1248 1
    return $this->_debug->getErrors();
1249
  }
1250
1251
  /**
1252
   * Execute a "replace"-query.
1253
   *
1254
   * @param string $table
1255
   * @param array  $data
1256
   *
1257
   * @return false|int false on error
1258
   */
1259 1
  public function replace($table, $data = array())
1260
  {
1261
1262 1
    $table = trim($table);
1263
1264 1
    if ($table === '') {
1265 1
      $this->_debug->displayError('invalid table name');
1266
1267 1
      return false;
1268
    }
1269
1270 1
    if (count($data) === 0) {
1271 1
      $this->_debug->displayError('empty data for REPLACE');
1272
1273 1
      return false;
1274
    }
1275
1276
    // extracting column names
1277 1
    $columns = array_keys($data);
1278 1
    foreach ($columns as $k => $_key) {
1279
      /** @noinspection AlterInForeachInspection */
1280 1
      $columns[$k] = $this->quote_string($_key);
1281
    }
1282
1283 1
    $columns = implode(',', $columns);
1284
1285
    // extracting values
1286 1
    foreach ($data as $k => $_value) {
1287
      /** @noinspection AlterInForeachInspection */
1288 1
      $data[$k] = $this->secure($_value);
1289
    }
1290 1
    $values = implode(',', $data);
1291
1292 1
    $sql = 'REPLACE INTO ' . $this->quote_string($table) . " ($columns) VALUES ($values);";
1293
1294 1
    return $this->query($sql);
1295
  }
1296
1297
  /**
1298
   * Execute a "update"-query.
1299
   *
1300
   * @param string       $table
1301
   * @param array        $data
1302
   * @param array|string $where
1303
   *
1304
   * @return false|int false on error
1305
   */
1306 6
  public function update($table, $data = array(), $where = '1=1')
1307
  {
1308 6
    $table = trim($table);
1309
1310 6
    if ($table === '') {
1311 1
      $this->_debug->displayError('invalid table name');
1312
1313 1
      return false;
1314
    }
1315
1316 6
    if (count($data) === 0) {
1317 2
      $this->_debug->displayError('empty data for UPDATE');
1318
1319 2
      return false;
1320
    }
1321
1322 6
    $SET = $this->_parseArrayPair($data);
1323
1324 6
    if (is_string($where)) {
1325 2
      $WHERE = $this->escape($where, false);
1326 5
    } elseif (is_array($where)) {
1327 4
      $WHERE = $this->_parseArrayPair($where, 'AND');
1328
    } else {
1329 1
      $WHERE = '';
1330
    }
1331
1332 6
    $sql = 'UPDATE ' . $this->quote_string($table) . " SET $SET WHERE ($WHERE);";
1333
1334 6
    return $this->query($sql);
1335
  }
1336
1337
  /**
1338
   * Execute a "delete"-query.
1339
   *
1340
   * @param string       $table
1341
   * @param string|array $where
1342
   *
1343
   * @return false|int false on error
1344
   */
1345 1 View Code Duplication
  public function delete($table, $where)
1346
  {
1347
1348 1
    $table = trim($table);
1349
1350 1
    if ($table === '') {
1351 1
      $this->_debug->displayError('invalid table name');
1352
1353 1
      return false;
1354
    }
1355
1356 1
    if (is_string($where)) {
1357 1
      $WHERE = $this->escape($where, false);
1358 1
    } elseif (is_array($where)) {
1359 1
      $WHERE = $this->_parseArrayPair($where, 'AND');
1360
    } else {
1361 1
      $WHERE = '';
1362
    }
1363
1364 1
    $sql = 'DELETE FROM ' . $this->quote_string($table) . " WHERE ($WHERE);";
1365
1366 1
    return $this->query($sql);
1367
  }
1368
1369
  /**
1370
   * Execute a "select"-query.
1371
   *
1372
   * @param string       $table
1373
   * @param string|array $where
1374
   *
1375
   * @return false|Result false on error
1376
   */
1377 18 View Code Duplication
  public function select($table, $where = '1=1')
1378
  {
1379
1380 18
    if ($table === '') {
1381 1
      $this->_debug->displayError('invalid table name');
1382
1383 1
      return false;
1384
    }
1385
1386 18
    if (is_string($where)) {
1387 3
      $WHERE = $this->escape($where, false);
1388 16
    } elseif (is_array($where)) {
1389 16
      $WHERE = $this->_parseArrayPair($where, 'AND');
1390
    } else {
1391 1
      $WHERE = '';
1392
    }
1393
1394 18
    $sql = 'SELECT * FROM ' . $this->quote_string($table) . " WHERE ($WHERE);";
1395
1396 18
    return $this->query($sql);
1397
  }
1398
1399
  /**
1400
   * Get the last sql-error.
1401
   *
1402
   * @return string false on error
1403
   */
1404 1
  public function lastError()
1405
  {
1406 1
    $errors = $this->_debug->getErrors();
1407
1408 1
    return count($errors) > 0 ? end($errors) : false;
1409
  }
1410
1411
  /**
1412
   * @return Debug
1413
   */
1414 7
  public function getDebugger()
1415
  {
1416 7
    return $this->_debug;
1417
  }
1418
1419
  /**
1420
   * __destruct
1421
   *
1422
   */
1423
  public function __destruct()
1424
  {
1425
    // close the connection only if we don't save PHP-SESSION's in DB
1426
    if ($this->session_to_db === false) {
1427
      $this->close();
1428
    }
1429
  }
1430
1431
  /**
1432
   * Closes a previously opened database connection.
1433
   */
1434 2
  public function close()
1435
  {
1436 2
    $this->connected = false;
1437
1438 2
    if ($this->link) {
1439 2
      mysqli_close($this->link);
1440
    }
1441 2
  }
1442
1443
  /**
1444
   * prevent the instance from being cloned
1445
   *
1446
   * @return void
1447
   */
1448
  private function __clone()
1449
  {
1450
  }
1451
1452
}
1453