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

DB::secure()   D

Complexity

Conditions 9
Paths 8

Size

Total Lines 26
Code Lines 18

Duplication

Lines 3
Ratio 11.54 %

Code Coverage

Tests 16
CRAP Score 9.111

Importance

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