Completed
Push — work-fleets ( 04acf9...8f8df9 )
by SuperNova.WS
07:02
created

db_mysql::doInsertComplex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
3
define('DB_INSERT_PLAIN', 0);
4
define('DB_INSERT_REPLACE', 1);
5
define('DB_INSERT_IGNORE', 2);
6
7
/**
8
 * Created by Gorlum 01.09.2015 15:58
9
 */
10
class db_mysql {
11
  const TRANSACTION_SERIALIZABLE = 'SERIALIZABLE';
12
  const TRANSACTION_REPEATABLE_READ = 'REPEATABLE READ';
13
  const TRANSACTION_READ_COMMITTED = 'READ COMMITTED';
14
  const TRANSACTION_READ_UNCOMMITTED = 'READ UNCOMMITTED';
15
16
  /**
17
   * Статус соеднения с MySQL
18
   *
19
   * @var bool
20
   */
21
  public $connected = false;
22
  /**
23
   * Префикс названий таблиц в БД
24
   *
25
   * @var string
26
   */
27
  public $db_prefix = '';
28
  /**
29
   * Список таблиц в БД
30
   *
31
   * @var array
32
   */
33
  public $table_list = array();
34
35
  /**
36
   * Настройки БД
37
   *
38
   * @var array
39
   */
40
  protected $dbsettings = array();
41
  /**
42
   * Драйвер для прямого обращения к MySQL
43
   *
44
   * @var db_mysql_v5 $driver
45
   */
46
  public $driver = null;
47
48
  /**
49
   * Общее время запросов
50
   *
51
   * @var float $time_mysql_total
52
   */
53
  public $time_mysql_total = 0.0;
54
55
  /**
56
   * Amount of queries on this DB
57
   *
58
   * @var int
59
   */
60
  public $queryCount = 0;
61
62
  public $isWatching = false;
63
64
  /**
65
   * @var \DBAL\DbTransaction $transaction
66
   */
67
  protected $transaction;
68
69
  /**
70
   * Should query check be skipped?
71
   *
72
   * Used for altering scheme of DB
73
   *
74
   * @var bool $skipQueryCheck
75
   */
76
  protected $skipQueryCheck = false;
77
78
  /**
79
   * @var SnCache $snCache
80
   */
81
  public $snCache;
82
83
  /**
84
   * db_mysql constructor.
85
   *
86
   * @param \Common\GlobalContainer $gc
87
   */
88
  public function __construct($gc) {
89
    $this->transaction = new \DBAL\DbTransaction($gc, $this);
90
    $this->snCache = new $gc->snCacheClass($gc, $this);
91
  }
92
93
  public function load_db_settings($configFile = '') {
94
    $dbsettings = array();
95
96
    empty($configFile) ? $configFile = SN_ROOT_PHYSICAL . "config" . DOT_PHP_EX : false;
97
98
    require $configFile;
99
100
    $this->dbsettings = $dbsettings;
101
  }
102
103
  /**
104
   * @param null|array $external_db_settings
105
   *
106
   * @return bool
107
   */
108
  public function sn_db_connect($external_db_settings = null) {
109
    $this->db_disconnect();
110
111
    if (!empty($external_db_settings) && is_array($external_db_settings)) {
112
      $this->dbsettings = $external_db_settings;
113
    }
114
115
    if (empty($this->dbsettings)) {
116
      $this->load_db_settings(SN_ROOT_PHYSICAL . "config" . DOT_PHP_EX);
117
    }
118
119
    // TODO - фатальные (?) ошибки на каждом шагу. Хотя - скорее Эксепшны
120
    if (!empty($this->dbsettings)) {
121
      $driver_name = empty($this->dbsettings['sn_driver']) ? 'db_mysql_v5' : $this->dbsettings['sn_driver'];
122
      $this->driver = new $driver_name();
123
      $this->db_prefix = $this->dbsettings['prefix'];
124
125
      $this->connected = $this->connected || $this->driver_connect();
126
127
      if ($this->connected) {
128
        $this->table_list = $this->db_get_table_list();
129
        // TODO Проверка на пустоту
130
      }
131
    } else {
132
      $this->connected = false;
133
    }
134
135
    return $this->connected;
136
  }
137
138
  protected function driver_connect() {
139
    if (!is_object($this->driver)) {
140
      classSupernova::$debug->error_fatal('DB Error - No driver for MySQL found!');
141
    }
142
143
    if (!method_exists($this->driver, 'mysql_connect')) {
144
      classSupernova::$debug->error_fatal('DB Error - WRONG MySQL driver!');
145
    }
146
147
    return $this->driver->mysql_connect($this->dbsettings);
148
  }
149
150
  public function db_disconnect() {
151
    if ($this->connected) {
152
      $this->connected = !$this->driver_disconnect();
153
      $this->connected = false;
154
    }
155
156
    return !$this->connected;
157
  }
158
159
  /**
160
   * @param string $query
161
   *
162
   * @return mixed|string
163
   */
164
  public function replaceTablePlaceholders($query) {
165
    $sql = $query;
166
    if (strpos($sql, '{{') !== false) {
167
      foreach ($this->table_list as $tableName) {
168
        $sql = str_replace("{{{$tableName}}}", $this->db_prefix . $tableName, $sql);
169
      }
170
    }
171
172
    return $sql;
173
  }
174
175
  /**
176
   * @param $query
177
   */
178
  protected function logQuery($query) {
179
    if (!classSupernova::$config->debug) {
180
      return;
181
    }
182
183
    $this->queryCount++;
184
    $arr = debug_backtrace();
185
    $file = end(explode('/', $arr[0]['file']));
0 ignored issues
show
Bug introduced by
explode('/', $arr[0]['file']) cannot be passed to end() as the parameter $array expects a reference.
Loading history...
186
    $line = $arr[0]['line'];
187
    classSupernova::$debug->add("<tr><th>Query {$this->queryCount}: </th><th>$query</th><th>{$file} @ {$line}</th><th>&nbsp;</th></tr>");
188
  }
189
190
191
  /**
192
   * @return string
193
   */
194
  public function queryTrace() {
195
    if (!defined('DEBUG_SQL_COMMENT') || constant('DEBUG_SQL_ERROR') !== true) {
196
      return '';
197
    }
198
199
    $backtrace = debug_backtrace();
200
    $sql_comment = classSupernova::$debug->compact_backtrace($backtrace, defined('DEBUG_SQL_COMMENT_LONG'));
201
202
    if (defined('DEBUG_SQL_ERROR') && constant('DEBUG_SQL_ERROR') === true) {
203
      classSupernova::$debug->add_to_array($sql_comment);
204
    }
205
206
    $sql_commented = implode("\r\n", $sql_comment);
207
    if (defined('DEBUG_SQL_ONLINE') && constant('DEBUG_SQL_ONLINE') === true) {
208
      classSupernova::$debug->warning($sql_commented, 'SQL Debug', LOG_DEBUG_SQL);
209
    }
210
211
    return $sql_commented;
212
  }
213
214
  /**
215
   * @param string $query
216
   *
217
   * @return array|bool|mysqli_result|null
218
   * @internal param bool $skip_query_check
219
   *
220
   */
221
  protected function doquery($query) {
222
    if (!$this->connected) {
223
      $this->sn_db_connect();
224
    }
225
226
    $stringQuery = $query;
227
    $stringQuery = trim($stringQuery);
228
    // You can't do it - 'cause you can break commented statement with line-end comments
229
    // $stringQuery = preg_replace("/\s+/", ' ', $stringQuery);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
230
231
    $this->security_watch_user_queries($stringQuery);
232
    $this->security_query_check_bad_words($stringQuery);
233
    $this->logQuery($stringQuery);
234
235
    $stringQuery = $this->replaceTablePlaceholders($stringQuery);
236
237
    $queryTrace = $this->queryTrace();
238
239
    $queryResult = null;
240
    try {
241
      $queryResult = $this->db_sql_query($stringQuery . DbSqlHelper::quoteComment($queryTrace));
242
      if (!$queryResult) {
243
        throw new Exception();
244
      }
245
    } catch (Exception $e) {
246
      classSupernova::$debug->error($this->db_error() . "<br />{$query}<br />", 'SQL Error');
247
    }
248
249
    return $queryResult;
250
  }
251
252
253
  // Just wrappers to distinguish query types
254
  /**
255
   * Executes non-data manipulation statements
256
   *
257
   * Can execute queries with check skip
258
   * Honor current state of query checking
259
   *
260
   * @param string $query
261
   * @param bool   $skip_query_check
262
   *
263
   * @return array|bool|mysqli_result|null
264
   */
265
  public function doExecute($query, $skip_query_check = false) {
266
    $prevState = false;
267
    if ($skip_query_check) {
268
      $prevState = $this->skipQueryCheck;
269
      $this->skipQueryCheck = true;
270
    }
271
    $result = $this->doquery($query);
272
    if ($skip_query_check) {
273
      $this->skipQueryCheck = $prevState;
274
    }
275
276
    return $result;
277
  }
278
279
280
  public function doSelect($query) {
281
    return $this->doExecute($query);
282
  }
283
284
  /**
285
   * @param string $query
286
   *
287
   * @return array|null
288
   */
289
  public function doSelectFetch($query) {
290
    return $this->db_fetch($this->doSelect($query));
0 ignored issues
show
Bug introduced by
$this->doSelect($query) cannot be passed to db_fetch() as the parameter $query expects a reference.
Loading history...
Bug introduced by
It seems like $this->doSelect($query) targeting db_mysql::doSelect() can also be of type boolean; however, db_mysql::db_fetch() does only seem to accept object<mysqli_result>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
291
  }
292
293
  /**
294
   * @param string $query
295
   *
296
   * @return mixed|null
297
   */
298
  public function doSelectFetchValue($query) {
299
    $row = $this->doSelectFetch($query);
300
301
    return is_array($row) ? reset($row) : null;
302
  }
303
304
305
  public function doInsertComplex($query) {
306
    return $this->doExecute($query);
307
  }
308
309
310
  protected function doSet($table, $fieldsAndValues, $replace = DB_INSERT_PLAIN) {
311
    $tableSafe = $this->db_escape($table);
312
    $safeFieldsAndValues = implode(',', $this->safeFieldsAndValues($fieldsAndValues));
313
//    $command = $replace == DB_INSERT_REPLACE ? 'REPLACE' : 'INSERT';
314
//    $command .= $replace == DB_INSERT_IGNORE ? ' IGNORE' : '';
315
    switch($replace) {
316
      case DB_INSERT_IGNORE:
317
        $command = 'INSERT IGNORE';
318
      break;
319
      case DB_INSERT_REPLACE:
320
        $command = 'REPLACE';
321
      break;
322
      default:
323
        $command = 'INSERT';
324
      break;
325
    }
326
    $query = "{$command} INTO `{{{$tableSafe}}}` SET {$safeFieldsAndValues}";
327
328
    return $this->doExecute($query);
329
  }
330
331
  /**
332
   * @param string $table
333
   * @param array  $fieldsAndValues
334
   * @param int    $replace - DB_INSERT_PLAIN || DB_INSERT_IGNORE
335
   *
336
   * @return array|bool|mysqli_result|null
337
   */
338
  public function doInsertSet($table, $fieldsAndValues, $replace = DB_INSERT_PLAIN) {
339
    return $this->doSet($table, $fieldsAndValues, $replace);
340
  }
341
342
  public function doReplaceSet($table, $fieldsAndValues) {
343
    return $this->doSet($table, $fieldsAndValues, DB_INSERT_REPLACE);
344
  }
345
346
  /**
347
   * Values should be passed as-is
348
   *
349
   * @param string   $table
350
   * @param array    $fields
351
   * @param string[] $values
352
   * @param bool     $replace
353
   *
354
   * @return array|bool|mysqli_result|null
355
   * @deprecated
356
   */
357
  protected function doValuesDeprecated($table, $fields, &$values, $replace = DB_INSERT_PLAIN) {
358
    $tableSafe = $this->db_escape($table);
359
    $safeFields = implode(',', $this->safeFields($fields));
360
    $safeValues = implode(',', $values);
361
    $command = $replace == DB_INSERT_REPLACE ? 'REPLACE' : 'INSERT';
362
    $query = "{$command} INTO `{{{$tableSafe}}}` ({$safeFields}) VALUES {$safeValues}";
363
364
    return $this->doExecute($query);
365
  }
366
367
  // TODO - batch insert and replace here
368
369
  // TODO - перед тем, как переделывать данные из депрекейтов - убедится, что
370
  // null - это null, а не строка'NULL'
371
372
  /**
373
   * Values should be passed as-is
374
   *
375
   * @param string   $table
376
   * @param array    $fields
377
   * @param string[] $values
378
   *
379
   * @return array|bool|mysqli_result|null
380
   * @deprecated
381
   */
382
  public function doInsertValuesDeprecated($table, $fields, &$values) {
383
    return $this->doValuesDeprecated($table, $fields, $values, DB_INSERT_PLAIN);
0 ignored issues
show
Deprecated Code introduced by
The method db_mysql::doValuesDeprecated() has been deprecated.

This method has been deprecated.

Loading history...
Documentation introduced by
DB_INSERT_PLAIN is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
384
  }
385
386
  /**
387
   * Values should be passed as-is
388
   *
389
   * @param string   $table
390
   * @param array    $fields
391
   * @param string[] $values
392
   *
393
   * @return array|bool|mysqli_result|null
394
   * @deprecated
395
   */
396
  public function doReplaceValuesDeprecated($table, $fields, &$values) {
397
    return $this->doValuesDeprecated($table, $fields, $values, DB_INSERT_REPLACE);
0 ignored issues
show
Documentation introduced by
DB_INSERT_REPLACE is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The method db_mysql::doValuesDeprecated() has been deprecated.

This method has been deprecated.

Loading history...
398
  }
399
400
  public function doUpdate($query) {
401
    return $this->doExecute($query);
402
  }
403
404
405
  /**
406
   * @param string $query
407
   *
408
   * @return array|bool|mysqli_result|null
409
   */
410
  public function doDelete($query) {
411
    return $this->doExecute($query);
412
  }
413
414
  /**
415
   * @param string $table
416
   * @param array  $where - simple WHERE statement list which can be combined with AND
417
   * @param bool   $isOneRecord
418
   *
419
   * @return array|bool|mysqli_result|null
420
   */
421
  public function doDeleteWhere($table, $where, $isOneRecord = false) {
422
    $tableSafe = $this->db_escape($table);
423
    $safeWhere = implode(' AND ', $this->safeFieldsAndValues($where));
424
    $query = "DELETE FROM `{{{$tableSafe}}}` WHERE {$safeWhere}"
425
      . ($isOneRecord ? ' LIMIT 1' : '');
426
427
    return $this->doDelete($query);
428
  }
429
430
  /**
431
   * @param string $table
432
   * @param array  $where - simple WHERE statement list which can be combined with AND
433
   *
434
   * @return array|bool|mysqli_result|null
435
   */
436
  public function doDeleteRowWhere($table, $where) {
437
    return $this->doDeleteWhere($table, $where, true);
438
  }
439
440
  /**
441
   * @param string $query
442
   *
443
   * @return array|bool|mysqli_result|null
444
   */
445
  public function doDeleteComplex($query) {
446
    return $this->doDelete($query);
447
  }
448
449
  /**
450
   * Early deprecated function for complex delete conditions
451
   *
452
   * Usually used for mallformed $where conditions
453
   *
454
   * @param $table
455
   * @param $where
456
   *
457
   * @return array|bool|mysqli_result|null
458
   * @deprecated
459
   */
460
  public function doDeleteDeprecated($table, $where) {
461
    return $this->doDeleteWhere($table, $where, false);
462
  }
463
464
465
  protected function castAsDbValue($value) {
466
    switch(gettype($value)) {
467
      case TYPE_INTEGER:
468
      case TYPE_DOUBLE:
469
        // do nothing
470
      break;
471
472
      case TYPE_BOOLEAN:
473
        $value = $value ? 1 : 0;
474
      break;
475
476
      case TYPE_NULL:
477
        $value = 'NULL';
478
      break;
479
480
      /** @noinspection PhpMissingBreakStatementInspection */
481
      case TYPE_ARRAY:
482
        $value = serialize($value);
483
      // Continuing with serialized array value
484
      case TYPE_STRING:
485
        // Empty type is string
486
      case TYPE_EMPTY:
487
        // No-type defaults to string
488
      default:
489
        $value = "'" . $this->db_escape((string)$value) . "'";
490
      break;
491
    }
492
493
    return $value;
494
  }
495
496
  /**
497
   * Make field list safe
498
   *
499
   * Support expressions - expression index should be strictly integer!
500
   *
501
   * @param array $fields - array of pair $fieldName => $fieldValue
502
   *
503
   * @return array
504
   */
505
  protected function safeFieldsAndValues($fields) {
506
    $result = array();
507
508
    if (!is_array($fields) || empty($fields)) {
509
      return $result;
510
    }
511
512
    foreach ($fields as $fieldName => $fieldValue) {
513
      // Integer $fieldName means "leave as is" - for expressions and already processed fields
514
      if (is_int($fieldName)) {
515
        $result[$fieldName] = $fieldValue;
516
      } else {
517
        $result[$fieldName] = "`{$fieldName}` = " . $this->castAsDbValue($fieldValue);
518
      }
519
    }
520
521
    return $result;
522
  }
523
524
  // TODO - redo as callable usage with array_map/array_walk
525 View Code Duplication
  public function safeValues($values) {
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...
526
    $result = array();
527
528
    if (!is_array($values) || empty($values)) {
529
      return $result;
530
    }
531
532
    foreach ($values as $key => $value) {
533
      $result[$key] = $this->castAsDbValue($value);
534
    }
535
536
    return $result;
537
  }
538
539
  // TODO - redo as callable usage with array_map/array_walk
540 View Code Duplication
  public function safeFields($fields) {
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...
541
    $result = array();
542
543
    if (!is_array($fields) || empty($fields)) {
544
      return $result;
545
    }
546
547
    foreach ($fields as $key => $value) {
548
      $result[$key] = "`" . $this->db_escape($value) . "`";
549
    }
550
551
    return $result;
552
  }
553
554
555
  /**
556
   * Returns iterator to iterate through mysqli_result
557
   *
558
   * @param string $query
559
   *
560
   * return DbResultIterator
561
   *
562
   * @return DbEmptyIterator|DbMysqliResultIterator
563
   */
564
  public function doSelectIterator($query) {
565
    $queryResult = $this->doSelect($query);
566
567
    if ($queryResult instanceof mysqli_result) {
568
      $result = new DbMysqliResultIterator($queryResult);
569
    } else {
570
      $result = new DbEmptyIterator();
571
    }
572
573
    return $result;
574
  }
575
576
  /**
577
   * @param DbQueryConstructor $stmt
578
   * @param bool               $skip_query_check
579
   */
580
  public function doStmtLockAll($stmt, $skip_query_check = false) {
581
    $this->doExecute(
582
      $stmt
583
        ->select()
584
        ->field(1)
585
        ->setForUpdate()
586
        ->__toString(),
587
      $skip_query_check
588
    );
589
  }
590
591
  // TODO Заменить это на новый логгер
592
  protected function security_watch_user_queries($query) {
593
    global $user;
594
595
    if (
596
      !$this->isWatching // Not already watching
597
      && !empty(classSupernova::$config->game_watchlist_array) // There is some players in watchlist
598
      && in_array($user['id'], classSupernova::$config->game_watchlist_array) // Current player is in watchlist
599
      && !preg_match('/^(select|commit|rollback|start transaction)/i', $query) // Current query should be watched
600
    ) {
601
      $this->isWatching = true;
602
      $msg = "\$query = \"{$query}\"\n\r";
603
      if (!empty($_POST)) {
604
        $msg .= "\n\r" . dump($_POST, '$_POST');
605
      }
606
      if (!empty($_GET)) {
607
        $msg .= "\n\r" . dump($_GET, '$_GET');
608
      }
609
      classSupernova::$debug->warning($msg, "Watching user {$user['id']}", 399, array('base_dump' => true));
0 ignored issues
show
Documentation introduced by
array('base_dump' => true) is of type array<string,boolean,{"base_dump":"boolean"}>, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
610
      $this->isWatching = false;
611
    }
612
  }
613
614
615
  public function security_query_check_bad_words($query) {
616
    if ($this->skipQueryCheck) {
617
      return;
618
    }
619
620
    global $user, $dm_change_legit, $mm_change_legit;
621
622
    switch(true) {
623
      case stripos($query, 'RUNCATE TABL') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'RUNCATE TABL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
624
      case stripos($query, 'ROP TABL') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'ROP TABL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
625
      case stripos($query, 'ENAME TABL') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'ENAME TABL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
626
      case stripos($query, 'REATE DATABAS') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'REATE DATABAS') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
627
      case stripos($query, 'REATE TABL') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'REATE TABL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
628
      case stripos($query, 'ET PASSWOR') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'ET PASSWOR') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
629
      case stripos($query, 'EOAD DAT') != false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'EOAD DAT') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
630
      case stripos($query, 'RPG_POINTS') != false && stripos(trim($query), 'UPDATE ') === 0 && !$dm_change_legit:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'RPG_POINTS') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
631
      case stripos($query, 'METAMATTER') != false && stripos(trim($query), 'UPDATE ') === 0 && !$mm_change_legit:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'METAMATTER') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
632
      case stripos($query, 'AUTHLEVEL') != false && $user['authlevel'] < 3 && stripos($query, 'SELECT') !== 0:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stripos($query, 'AUTHLEVEL') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
633
        $report = "Hacking attempt (" . date("d.m.Y H:i:s") . " - [" . time() . "]):\n";
634
        $report .= ">Database Inforamation\n";
635
        $report .= "\tID - " . $user['id'] . "\n";
636
        $report .= "\tUser - " . $user['username'] . "\n";
637
        $report .= "\tAuth level - " . $user['authlevel'] . "\n";
638
        $report .= "\tAdmin Notes - " . $user['adminNotes'] . "\n";
639
        $report .= "\tCurrent Planet - " . $user['current_planet'] . "\n";
640
        $report .= "\tUser IP - " . $user['user_lastip'] . "\n";
641
        $report .= "\tUser IP at Reg - " . $user['ip_at_reg'] . "\n";
642
        $report .= "\tUser Agent- " . $_SERVER['HTTP_USER_AGENT'] . "\n";
643
        $report .= "\tCurrent Page - " . $user['current_page'] . "\n";
644
        $report .= "\tRegister Time - " . $user['register_time'] . "\n";
645
        $report .= "\n";
646
647
        $report .= ">Query Information\n";
648
        $report .= "\tQuery - " . $query . "\n";
649
        $report .= "\n";
650
651
        $report .= ">\$_SERVER Information\n";
652
        $report .= "\tIP - " . $_SERVER['REMOTE_ADDR'] . "\n";
653
        $report .= "\tHost Name - " . $_SERVER['HTTP_HOST'] . "\n";
654
        $report .= "\tUser Agent - " . $_SERVER['HTTP_USER_AGENT'] . "\n";
655
        $report .= "\tRequest Method - " . $_SERVER['REQUEST_METHOD'] . "\n";
656
        $report .= "\tCame From - " . $_SERVER['HTTP_REFERER'] . "\n";
657
        $report .= "\tPage is - " . $_SERVER['SCRIPT_NAME'] . "\n";
658
        $report .= "\tUses Port - " . $_SERVER['REMOTE_PORT'] . "\n";
659
        $report .= "\tServer Protocol - " . $_SERVER['SERVER_PROTOCOL'] . "\n";
660
661
        $report .= "\n--------------------------------------------------------------------------------------------------\n";
662
663
        $fp = fopen(SN_ROOT_PHYSICAL . 'badqrys.txt', 'a');
664
        fwrite($fp, $report);
0 ignored issues
show
Security File Manipulation introduced by
$report can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

7 paths for user data to reach this point

  1. Path: Fetching key HTTP_REFERER from $_SERVER, and $report is assigned in includes/classes/db_mysql.php on line 656
  1. Fetching key HTTP_REFERER from $_SERVER, and $report is assigned
    in includes/classes/db_mysql.php on line 656
  2. Path: Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned in includes/classes/db_mysql.php on line 654
  1. Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned
    in includes/classes/db_mysql.php on line 654
  2. $report is assigned
    in includes/classes/db_mysql.php on line 656
  3. Path: Fetching key HTTP_HOST from $_SERVER, and $report is assigned in includes/classes/db_mysql.php on line 653
  1. Fetching key HTTP_HOST from $_SERVER, and $report is assigned
    in includes/classes/db_mysql.php on line 653
  2. $report is assigned
    in includes/classes/db_mysql.php on line 654
  3. $report is assigned
    in includes/classes/db_mysql.php on line 656
  4. Path: Read from $_GET in includes/classes/db_mysql.php on line 607
  1. Read from $_GET
    in includes/classes/db_mysql.php on line 607
  2. Data is passed through gettype()
    in vendor/docs/txt2html.php on line 18
  3. $msg is assigned
    in includes/classes/db_mysql.php on line 607
  4. $msg is passed to debug::warning()
    in includes/classes/db_mysql.php on line 609
  5. Data is escaped by mysqli_real_escape_string() for sql context(s)
    in vendor/includes/classes/db_mysql_v5.php on line 87
  6. $query is assigned
    in includes/classes/debug.php on line 258
  7. $query is passed to db_mysql::doExecute()
    in includes/classes/debug.php on line 263
  8. $query is passed to db_mysql::doquery()
    in includes/classes/db_mysql.php on line 271
  9. $stringQuery is assigned
    in includes/classes/db_mysql.php on line 226
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in includes/classes/db_mysql.php on line 227
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in includes/classes/db_mysql.php on line 232
  12. $report is assigned
    in includes/classes/db_mysql.php on line 648
  13. $report is assigned
    in includes/classes/db_mysql.php on line 653
  14. $report is assigned
    in includes/classes/db_mysql.php on line 654
  15. $report is assigned
    in includes/classes/db_mysql.php on line 656
  5. Path: Read from $_POST in includes/classes/db_mysql.php on line 604
  1. Read from $_POST
    in includes/classes/db_mysql.php on line 604
  2. Data is passed through gettype()
    in vendor/docs/txt2html.php on line 18
  3. $msg is assigned
    in includes/classes/db_mysql.php on line 604
  4. $msg is passed to debug::warning()
    in includes/classes/db_mysql.php on line 609
  5. Data is escaped by mysqli_real_escape_string() for sql context(s)
    in vendor/includes/classes/db_mysql_v5.php on line 87
  6. $query is assigned
    in includes/classes/debug.php on line 258
  7. $query is passed to db_mysql::doExecute()
    in includes/classes/debug.php on line 263
  8. $query is passed to db_mysql::doquery()
    in includes/classes/db_mysql.php on line 271
  9. $stringQuery is assigned
    in includes/classes/db_mysql.php on line 226
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in includes/classes/db_mysql.php on line 227
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in includes/classes/db_mysql.php on line 232
  12. $report is assigned
    in includes/classes/db_mysql.php on line 648
  13. $report is assigned
    in includes/classes/db_mysql.php on line 653
  14. $report is assigned
    in includes/classes/db_mysql.php on line 654
  15. $report is assigned
    in includes/classes/db_mysql.php on line 656
  6. Path: Read from $_POST in includes/general.php on line 258
  1. Read from $_POST
    in includes/general.php on line 258
  2. sys_get_param() returns tainted data
    in includes/general.php on line 290
  3. Data is passed through strip_tags(), and Data is passed through trim()
    in vendor/includes/general.php on line 1289
  4. sys_get_param_str_unsafe() returns tainted data, and sys_get_param_str_unsafe('uni_name') is passed through strip_tags(), and strip_tags(sys_get_param_str_unsafe('uni_name')) is passed through sprintf(), and sprintf(\classLocale::$lang['uni_msg_admin_rename'], $user['id'], $user['username'], $uni_price, $uni_system ? \classLocale::$lang['uni_system_of'] : \classLocale::$lang['uni_galaxy_of'], $uni_galaxy, $uni_system ? ":{$uni_system}" : '', strip_tags(sys_get_param_str_unsafe('uni_name'))) is passed to debug::warning()
    in includes/includes/uni_rename.php on line 55
  5. Data is escaped by mysqli_real_escape_string() for sql context(s)
    in vendor/includes/classes/db_mysql_v5.php on line 87
  6. $query is assigned
    in includes/classes/debug.php on line 258
  7. $query is passed to db_mysql::doExecute()
    in includes/classes/debug.php on line 263
  8. $query is passed to db_mysql::doquery()
    in includes/classes/db_mysql.php on line 271
  9. $stringQuery is assigned
    in includes/classes/db_mysql.php on line 226
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in includes/classes/db_mysql.php on line 227
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in includes/classes/db_mysql.php on line 232
  12. $report is assigned
    in includes/classes/db_mysql.php on line 648
  13. $report is assigned
    in includes/classes/db_mysql.php on line 653
  14. $report is assigned
    in includes/classes/db_mysql.php on line 654
  15. $report is assigned
    in includes/classes/db_mysql.php on line 656
  7. Path: Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned in includes/classes/db_mysql.php on line 642
  1. Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned
    in includes/classes/db_mysql.php on line 642
  2. $report is assigned
    in includes/classes/db_mysql.php on line 648
  3. $report is assigned
    in includes/classes/db_mysql.php on line 653
  4. $report is assigned
    in includes/classes/db_mysql.php on line 654
  5. $report is assigned
    in includes/classes/db_mysql.php on line 656

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
665
        fclose($fp);
666
667
        $message = 'Привет, я не знаю то, что Вы пробовали сделать, но команда, которую Вы только послали базе данных, не выглядела очень дружественной и она была заблокированна.<br /><br />Ваш IP, и другие данные переданны администрации сервера. Удачи!.';
668
        die($message);
669
      break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
670
    }
671
  }
672
673
  /**
674
   * @param bool $prefixed_only
675
   *
676
   * @return array
677
   */
678
  public function db_get_table_list($prefixed_only = true) {
679
    $query = $this->mysql_get_table_list();
680
681
    $prefix_length = strlen($this->db_prefix);
682
683
    $tl = array();
684
    while($row = $this->db_fetch($query)) {
0 ignored issues
show
Bug introduced by
It seems like $query defined by $this->mysql_get_table_list() on line 679 can also be of type boolean; however, db_mysql::db_fetch() does only seem to accept object<mysqli_result>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
685
      foreach ($row as $table_name) {
686
        if (strpos($table_name, $this->db_prefix) === 0) {
687
          $table_name = substr($table_name, $prefix_length);
688
        } elseif ($prefixed_only) {
689
          continue;
690
        }
691
        // $table_name = str_replace($db_prefix, '', $table_name);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
692
        $tl[$table_name] = $table_name;
693
      }
694
    }
695
696
    return $tl;
697
  }
698
699
  /**
700
   * @param string $statement
701
   *
702
   * @return bool|mysqli_stmt
703
   */
704 View Code Duplication
  public function db_prepare($statement) {
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...
705
    $microtime = microtime(true);
706
    $result = $this->driver->mysql_prepare($statement);
707
    $this->time_mysql_total += microtime(true) - $microtime;
708
709
    return $result;
710
  }
711
712
713
  /**
714
   * L1 perform the query
715
   *
716
   * @param $query_string
717
   *
718
   * @return bool|mysqli_result
719
   */
720 View Code Duplication
  public function db_sql_query($query_string) {
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...
721
    $microtime = microtime(true);
722
    $result = $this->driver->mysql_query($query_string);
723
    $this->time_mysql_total += microtime(true) - $microtime;
724
725
    return $result;
726
  }
727
728
  /**
729
   * L1 fetch assoc array
730
   *
731
   * @param mysqli_result $query
732
   *
733
   * @return array|null
734
   */
735 View Code Duplication
  public function db_fetch(&$query) {
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...
736
    $microtime = microtime(true);
737
    $result = $this->driver->mysql_fetch_assoc($query);
738
    $this->time_mysql_total += microtime(true) - $microtime;
739
740
    return $result;
741
  }
742
743
  public function db_fetch_row(&$query) {
744
    return $this->driver->mysql_fetch_row($query);
745
  }
746
747
  public function db_escape($unescaped_string) {
748
    return $this->driver->mysql_real_escape_string($unescaped_string);
749
  }
750
751
  public function driver_disconnect() {
752
    return $this->driver->mysql_close_link();
753
  }
754
755
  public function db_error() {
756
    return $this->driver->mysql_error();
757
  }
758
759
  public function db_insert_id() {
760
    return $this->driver->mysql_insert_id();
761
  }
762
763
  public function db_num_rows(&$result) {
764
    return $this->driver->mysql_num_rows($result);
765
  }
766
767
  public function db_affected_rows() {
768
    return $this->driver->mysql_affected_rows();
769
  }
770
771
  /**
772
   * @return string
773
   */
774
  public function db_get_client_info() {
775
    return $this->driver->mysql_get_client_info();
776
  }
777
778
  /**
779
   * @return string
780
   */
781
  public function db_get_server_info() {
782
    return $this->driver->mysql_get_server_info();
783
  }
784
785
  /**
786
   * @return string
787
   */
788
  public function db_get_host_info() {
789
    return $this->driver->mysql_get_host_info();
790
  }
791
792
  public function db_get_server_stat() {
793
    $result = array();
794
795
    $status = explode('  ', $this->driver->mysql_stat());
796
    foreach ($status as $value) {
797
      $row = explode(': ', $value);
798
      $result[$row[0]] = $row[1];
799
    }
800
801
    return $result;
802
  }
803
804
  /**
805
   * @return array
806
   * @throws Exception
807
   */
808
  public function db_core_show_status() {
809
    $result = array();
810
811
    $query = $this->db_sql_query('SHOW STATUS;');
812
    if (is_bool($query)) {
813
      throw new Exception('Result of SHOW STATUS command is boolean - which should never happen. Connection to DB is lost?');
814
    }
815
    while($row = db_fetch($query)) {
816
      $result[$row['Variable_name']] = $row['Value'];
817
    }
818
819
    return $result;
820
  }
821
822
  public function mysql_get_table_list() {
823
    return $this->db_sql_query('SHOW TABLES;');
824
  }
825
826
  public function mysql_get_innodb_status() {
827
    return $this->db_sql_query('SHOW ENGINE INNODB STATUS;');
828
  }
829
830
  /**
831
   * @return \DBAL\DbTransaction
832
   */
833
  public function getTransaction() {
834
    return $this->transaction;
835
  }
836
837
  // Some wrappers to DbTransaction
838
  // Unused for now
839
  public function transactionCheck($status = null) {
840
    return $this->transaction->check($status);
841
  }
842
843
  public function transactionStart($level = '') {
844
    return $this->transaction->start($level);
845
  }
846
847
  public function transactionCommit() {
848
    return $this->transaction->commit();
849
  }
850
851
  public function transactionRollback() {
852
    return $this->transaction->rollback();
853
  }
854
855
}
856