Completed
Push — master ( 7b0c70...3d2834 )
by Lars
02:48
created

DB::prepare()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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