Completed
Push — master ( 866be0...ac25c9 )
by Lars
11:39 queued 06:06
created

DB::__wakeup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
ccs 3
cts 3
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 10
        $hostname,
109 10
        $username,
110 10
        $password,
111 10
        $database,
112 10
        $port,
113 10
        $charset,
114 10
        $exit_on_error,
115 10
        $echo_on_error,
116 10
        $logger_class_name,
117 10
        $logger_level,
118
        $session_to_db
119 10
    );
120
121 7
    $this->connect();
122
123 4
    $this->mysqlDefaultTimeFunctions = array(
124
      // Returns the current date.
125 4
      'CURDATE()',
126
      // CURRENT_DATE	| Synonyms for CURDATE()
127 4
      'CURRENT_DATE()',
128
      // CURRENT_TIME	| Synonyms for CURTIME()
129 4
      'CURRENT_TIME()',
130
      // CURRENT_TIMESTAMP | Synonyms for NOW()
131 4
      'CURRENT_TIMESTAMP()',
132
      // Returns the current time.
133 4
      '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 4
      '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 4
      'LOCALTIMESTAMP()',
138
      // Returns the current date and time.
139 4
      'NOW()',
140
      // Returns the time at which the function executes.
141 4
      'SYSDATE()',
142
      // Returns a UNIX timestamp.
143 4
      'UNIX_TIMESTAMP()',
144
      // Returns the current UTC date.
145 4
      'UTC_DATE()',
146
      // Returns the current UTC time.
147 4
      'UTC_TIME()',
148
      // Returns the current UTC date and time.
149 4
      '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 4
    }
180
181 10
    if ($port) {
182 4
      $this->port = (int)$port;
183 4
    } 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 10
    }
192
193 10
    if ($exit_on_error === true || $exit_on_error === false) {
194 10
      $this->_debug->setExitOnError($exit_on_error);
195 10
    }
196
197 10
    if ($echo_on_error === true || $echo_on_error === false) {
198 10
      $this->_debug->setEchoOnError($echo_on_error);
199 10
    }
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 10
        ||
222 9
        !$this->username
223 9
        ||
224 8
        !$this->database
225 10
    ) {
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 8
      );
272 8
    } 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
342
  /**
343
   * getInstance()
344
   *
345
   * @param string      $hostname
346
   * @param string      $username
347
   * @param string      $password
348
   * @param string      $database
349
   * @param string      $port          default is (int)3306
350
   * @param string      $charset       default is 'utf8', but if you need 4-byte chars, then your tables need
351
   *                                   the 'utf8mb4'-charset
352
   * @param bool|string $exit_on_error use a empty string "" or false to disable it
353
   * @param bool|string $echo_on_error use a empty string "" or false to disable it
354
   * @param string      $logger_class_name
355
   * @param string      $logger_level
356
   * @param bool|string $session_to_db use a empty string "" or false to disable it
357
   *
358
   * @return \voku\db\DB
359
   */
360 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 = '')
361
  {
362
    /**
363
     * @var $instance DB[]
364
     */
365 48
    static $instance = array();
366
367
    /**
368
     * @var $firstInstance DB
369
     */
370 48
    static $firstInstance = null;
371
372
    if (
373 48
        $hostname . $username . $password . $database . $port . $charset == ''
374 48
        &&
375 9
        null !== $firstInstance
376 48
    ) {
377 9
      return $firstInstance;
378
    }
379
380 48
    $connection = md5(
381 48
        $hostname . $username . $password . $database . $port . $charset . (int)$exit_on_error . (int)$echo_on_error . $logger_class_name . $logger_level . (int)$session_to_db
382 48
    );
383
384 48
    if (!isset($instance[$connection])) {
385 10
      $instance[$connection] = new self(
386 10
          $hostname,
387 10
          $username,
388 10
          $password,
389 10
          $database,
390 10
          $port,
391 10
          $charset,
392 10
          $exit_on_error,
393 10
          $echo_on_error,
394 10
          $logger_class_name,
395 10
          $logger_level,
396
          $session_to_db
397 10
      );
398
399 4
      if (null === $firstInstance) {
400 1
        $firstInstance = $instance[$connection];
401 1
      }
402 4
    }
403
404 48
    return $instance[$connection];
405
  }
406
407
  /**
408
   * Execute a sql-query.
409
   *
410
   * @param string        $sql            sql-query
411
   *
412
   * @param array|boolean $params         "array" of sql-query-parameters
413
   *                                      "false" if you don't need any parameter (default)
414
   *
415
   * @return bool|int|Result              "Result" by "<b>SELECT</b>"-queries<br />
416
   *                                      "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
417
   *                                      "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
418
   *                                      "true" by e.g. "DROP"-queries<br />
419
   *                                      "false" on error
420
   *
421
   * @throws \Exception
422
   */
423 28
  public function query($sql = '', $params = false)
424
  {
425 28
    if (!$this->isReady()) {
426
      return false;
427
    }
428
429 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...
430 4
      $this->_debug->displayError('Can\'t execute an empty Query', false);
431
432 4
      return false;
433
    }
434
435
    if (
436
        $params !== false
437 26
        &&
438 2
        is_array($params)
439 26
        &&
440 2
        count($params) > 0
441 26
    ) {
442 2
      $sql = $this->_parseQueryParams($sql, $params);
443 2
    }
444
445 26
    $query_start_time = microtime(true);
446 26
    $query_result = mysqli_real_query($this->link, $sql);
447 26
    $query_duration = microtime(true) - $query_start_time;
448
449 26
    $this->query_count++;
450
451 26
    $mysqli_field_count = mysqli_field_count($this->link);
452 26
    if ($mysqli_field_count) {
453 24
      $result = mysqli_store_result($this->link);
454 24
    } else {
455 17
      $result = $query_result;
456
    }
457
458 26
    if ($result instanceof \mysqli_result) {
459
460
      // log the select query
461 23
      $this->_debug->logQuery($sql, $query_duration, $mysqli_field_count);
462
463
      // return query result object
464 23
      return new Result($sql, $result);
465
466 18
    } elseif ($query_result === true) {
467
468
      // "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...
469 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...
470 15
        $insert_id = (int)$this->insert_id();
471 15
        $this->_debug->logQuery($sql, $query_duration, $insert_id);
472
473 15
        return $insert_id;
474
      }
475
476
      // "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...
477 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...
478 7
        $affected_rows = (int)$this->affected_rows();
479 7
        $this->_debug->logQuery($sql, $query_duration, $affected_rows);
480
481 7
        return $affected_rows;
482
      }
483
484
      // log the ? query
485
      $this->_debug->logQuery($sql, $query_duration, 0);
486
487
      return true;
488
    }
489
490
    // log the error query
491 8
    $this->_debug->logQuery($sql, $query_duration, 0, true);
492
493 8
    return $this->queryErrorHandling(mysqli_error($this->link), $sql, $params);
494
  }
495
496
  /**
497
   * _parseQueryParams
498
   *
499
   * @param string $sql
500
   * @param array  $params
501
   *
502
   * @return string
503
   */
504 2
  private function _parseQueryParams($sql, array $params)
505
  {
506
    // is there anything to parse?
507 2
    if (strpos($sql, '?') === false) {
508
      return $sql;
509
    }
510
511 2
    if (count($params) > 0) {
512 2
      $parseKey = md5(uniqid(mt_rand(), true));
513 2
      $sql = str_replace('?', $parseKey, $sql);
514
515 2
      $k = 0;
516 2
      while (strpos($sql, $parseKey) !== false) {
517 2
        $value = $this->secure($params[$k]);
518 2
        $sql = preg_replace("/$parseKey/", $value, $sql, 1);
519 2
        $k++;
520 2
      }
521 2
    }
522
523 2
    return $sql;
524
  }
525
526
  /**
527
   * Try to secure a variable, so can you use it in sql-queries.
528
   *
529
   * int: (also strings that contains only an int-value)
530
   * 1. parse into (int)
531
   *
532
   * strings:
533
   * 1. check if the string isn't a default mysql-time-function e.g. 'CURDATE()'
534
   * 2. trim whitespace
535
   * 3. trim '
536
   * 4. escape the string (and remove non utf-8 chars)
537
   * 5. trim ' again (because we maybe removed some chars)
538
   * 6. add ' around the new string
539
   *
540
   * @param mixed $var
541
   *
542
   * @return string | null
543
   */
544 19
  public function secure($var)
545
  {
546
    // save the current value as int (for later usage)
547 19
    if (!is_object($var)) {
548 19
      $varInt = (int)$var;
549 19
    }
550
551
    /** @noinspection TypeUnsafeComparisonInspection */
552
    if (
553 19
        is_int($var)
554
        ||
555 15
        is_bool($var)
556 15
        ||
557 15
        (isset($varInt, $var[0]) && $var[0] != '0' && "$varInt" == $var)
0 ignored issues
show
Bug introduced by
The variable $varInt does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
558 19
    ) {
559
560
      // "int" || int || bool
561
562 17
      $var = (int)$var;
563
564 19
    } elseif (is_string($var)) {
565
566
      // "string"
567
568 15
      if (!in_array($var, $this->mysqlDefaultTimeFunctions, true)) {
569 15
        $var = "'" . trim($this->escape(trim(trim((string)$var), "'")), "'") . "'";
570 15
      }
571
572 15 View Code Duplication
    } elseif (is_float($var)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

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