Completed
Push — work-fleets ( 22b024...7f5906 )
by SuperNova.WS
06:37
created

db_mysql::safeFields()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 13
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
nc 3
nop 1
dl 13
loc 13
rs 9.2
c 3
b 0
f 0
ccs 0
cts 6
cp 0
crap 20
1
<?php
2
3
/**
4
 * Created by Gorlum 01.09.2015 15:58
5
 */
6
class db_mysql {
7
  const TRANSACTION_SERIALIZABLE = 'SERIALIZABLE';
8
  const TRANSACTION_REPEATABLE_READ = 'REPEATABLE READ';
9
  const TRANSACTION_READ_COMMITTED = 'READ COMMITTED';
10
  const TRANSACTION_READ_UNCOMMITTED = 'READ UNCOMMITTED';
11
12
  /**
13
   * Статус соеднения с MySQL
14
   *
15
   * @var bool
16
   */
17
  public $connected = false;
18
  /**
19
   * Префикс названий таблиц в БД
20
   *
21
   * @var string
22
   */
23
  public $db_prefix = '';
24
  /**
25
   * Список таблиц в БД
26
   *
27
   * @var array
28
   */
29
  public $table_list = array();
30
31
  /**
32
   * Настройки БД
33
   *
34
   * @var array
35
   */
36
  protected $dbsettings = array();
37
  /**
38
   * Драйвер для прямого обращения к MySQL
39
   *
40
   * @var db_mysql_v5 $driver
41
   */
42
  public $driver = null;
43
44
  /**
45
   * Общее время запросов
46
   *
47
   * @var float $time_mysql_total
48
   */
49
  public $time_mysql_total = 0.0;
50
51
  /**
52
   * Amount of queries on this DB
53
   *
54
   * @var int
55
   */
56
  public $queryCount = 0;
57
58
  public $isWatching = false;
59
60
  /**
61
   * @var \DBAL\DbTransaction $transaction
62
   */
63
  protected $transaction;
64
65
  /**
66
   * Should query check be skipped?
67
   *
68
   * Used for altering scheme of DB
69
   *
70
   * @var bool $skipQueryCheck
71
   */
72
  protected $skipQueryCheck = false;
73
74
  /**
75
   * @var SnCache $snCache
76
   */
77
  public $snCache;
78
79
  /**
80
   * db_mysql constructor.
81
   *
82
   * @param \Common\GlobalContainer $gc
83
   */
84
  public function __construct($gc) {
85
    $this->transaction = new \DBAL\DbTransaction($gc, $this);
86
    $this->snCache = new $gc->snCacheClass($gc, $this);
87
  }
88
89
  public function load_db_settings($configFile = '') {
90
    $dbsettings = array();
91
92
    empty($configFile) ? $configFile = SN_ROOT_PHYSICAL . "config" . DOT_PHP_EX : false;
93
94
    require $configFile;
95
96
    $this->dbsettings = $dbsettings;
97
  }
98
99
  /**
100
   * @param null|array $external_db_settings
101
   *
102
   * @return bool
103
   */
104
  public function sn_db_connect($external_db_settings = null) {
105
    $this->db_disconnect();
106
107
    if (!empty($external_db_settings) && is_array($external_db_settings)) {
108
      $this->dbsettings = $external_db_settings;
109
    }
110
111
    if (empty($this->dbsettings)) {
112
      $this->load_db_settings(SN_ROOT_PHYSICAL . "config" . DOT_PHP_EX);
113
    }
114
115
    // TODO - фатальные (?) ошибки на каждом шагу. Хотя - скорее Эксепшны
116
    if (!empty($this->dbsettings)) {
117
      $driver_name = empty($this->dbsettings['sn_driver']) ? 'db_mysql_v5' : $this->dbsettings['sn_driver'];
118
      $this->driver = new $driver_name();
119
      $this->db_prefix = $this->dbsettings['prefix'];
120
121
      $this->connected = $this->connected || $this->driver_connect();
122
123
      if ($this->connected) {
124
        $this->table_list = $this->db_get_table_list();
125
        // TODO Проверка на пустоту
126
      }
127
    } else {
128
      $this->connected = false;
129
    }
130
131
    return $this->connected;
132
  }
133
134
  protected function driver_connect() {
135
    if (!is_object($this->driver)) {
136
      classSupernova::$debug->error_fatal('DB Error - No driver for MySQL found!');
137
    }
138
139
    if (!method_exists($this->driver, 'mysql_connect')) {
140
      classSupernova::$debug->error_fatal('DB Error - WRONG MySQL driver!');
141
    }
142
143
    return $this->driver->mysql_connect($this->dbsettings);
144
  }
145
146
  public function db_disconnect() {
147
    if ($this->connected) {
148
      $this->connected = !$this->driver_disconnect();
149
      $this->connected = false;
150
    }
151
152
    return !$this->connected;
153
  }
154
155
  /**
156
   * @param string $query
157
   *
158
   * @return mixed|string
159
   */
160
  public function replaceTablePlaceholders($query) {
161
    $sql = $query;
162
    if (strpos($sql, '{{') !== false) {
163
      foreach ($this->table_list as $tableName) {
164
        $sql = str_replace("{{{$tableName}}}", $this->db_prefix . $tableName, $sql);
165
      }
166
    }
167
168
    return $sql;
169
  }
170
171
  /**
172
   * @param $query
173
   */
174
  protected function logQuery($query) {
175
    if (!classSupernova::$config->debug) {
176
      return;
177
    }
178
179
    $this->queryCount++;
180
    $arr = debug_backtrace();
181
    $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...
182
    $line = $arr[0]['line'];
183
    classSupernova::$debug->add("<tr><th>Query {$this->queryCount}: </th><th>$query</th><th>{$file} @ {$line}</th><th>&nbsp;</th></tr>");
184
  }
185
186
187
  /**
188
   * @return string
189
   */
190
  public function queryTrace() {
191
    if (!defined('DEBUG_SQL_COMMENT') || constant('DEBUG_SQL_ERROR') !== true) {
192
      return '';
193
    }
194
195
    $backtrace = debug_backtrace();
196
    $sql_comment = classSupernova::$debug->compact_backtrace($backtrace, defined('DEBUG_SQL_COMMENT_LONG'));
197
198
    if (defined('DEBUG_SQL_ERROR') && constant('DEBUG_SQL_ERROR') === true) {
199
      classSupernova::$debug->add_to_array($sql_comment);
200
    }
201
202
    $sql_commented = implode("\r\n", $sql_comment);
203
    if (defined('DEBUG_SQL_ONLINE') && constant('DEBUG_SQL_ONLINE') === true) {
204
      classSupernova::$debug->warning($sql_commented, 'SQL Debug', LOG_DEBUG_SQL);
205
    }
206
207
    return $sql_commented;
208
  }
209
210
  /**
211
   * @param string $query
212
   *
213
   * @return array|bool|mysqli_result|null
214
   * @internal param bool $skip_query_check
215
   *
216
   */
217
  protected function doquery($query) {
218
    if (!$this->connected) {
219
      $this->sn_db_connect();
220
    }
221
222
    $stringQuery = $query;
223
    $stringQuery = trim($stringQuery);
224
    // You can't do it - 'cause you can break commented statement with line-end comments
225
    // $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...
226
227
    $this->security_watch_user_queries($stringQuery);
228
    $this->security_query_check_bad_words($stringQuery);
229
    $this->logQuery($stringQuery);
230
231
    $stringQuery = $this->replaceTablePlaceholders($stringQuery);
232
233
    $queryTrace = $this->queryTrace();
234
235
    $queryResult = null;
236
    try {
237
      $queryResult = $this->db_sql_query($stringQuery . DbSqlHelper::quoteComment($queryTrace));
238
      if (!$queryResult) {
239
        throw new Exception();
240
      }
241
    } catch (Exception $e) {
242
      classSupernova::$debug->error($this->db_error() . "<br />{$query}<br />", 'SQL Error');
243
    }
244
245
    return $queryResult;
246
  }
247
248
249
  // Just wrappers to distinguish query types
250
  /**
251
   * Executes non-data manipulation statements
252
   *
253
   * Can execute queries with check skip
254
   * Honor current state of query checking
255
   *
256
   * @param string $query
257
   * @param bool   $skip_query_check
258
   *
259
   * @return array|bool|mysqli_result|null
260
   */
261
  public function doExecute($query, $skip_query_check = false) {
262
    $prevState = false;
263
    if ($skip_query_check) {
264
      $prevState = $this->skipQueryCheck;
265
      $this->skipQueryCheck = true;
266
    }
267
    $result = $this->doquery($query);
268
    if ($skip_query_check) {
269
      $this->skipQueryCheck = $prevState;
270
    }
271
272
    return $result;
273
  }
274
275
276
  public function doSelect($query) {
277
    return $this->doExecute($query);
278
  }
279
280
  /**
281
   * @param string $query
282
   *
283
   * @return array|null
284
   */
285
  public function doSelectFetch($query) {
286
    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...
287
  }
288
289
  /**
290
   * @param string $query
291
   *
292
   * @return mixed|null
293
   */
294
  public function doSelectFetchValue($query) {
295
    $row = $this->doSelectFetch($query);
296
297
    return is_array($row) ? reset($row) : null;
298
  }
299
300
301
  public function doInsert($query) {
302
    return $this->doExecute($query);
303
  }
304
305
  protected function doSet($table, $fieldsAndValues, $replace = false) {
306
    $tableSafe = $this->db_escape($table);
307
    $safeFieldsAndValues = implode(',', $this->safeFieldsAndValues($fieldsAndValues));
308
    $command = $replace ? 'REPLACE' : 'INSERT';
309
    $query = "{$command} INTO `{{{$tableSafe}}}` SET {$safeFieldsAndValues}";
310
311
    return $this->doExecute($query);
312
  }
313
314
  public function doInsertSet($table, $fieldsAndValues) {
315
    return $this->doSet($table, $fieldsAndValues, false);
316
  }
317
318
  public function doReplaceSet($table, $fieldsAndValues) {
319
    return $this->doSet($table, $fieldsAndValues, true);
320
  }
321
322
  /**
323
   * Values should be passed as-is
324
   *
325
   * @param string   $table
326
   * @param array    $fields
327
   * @param string[] $values
328
   * @param bool     $replace
329
   *
330
   * @return array|bool|mysqli_result|null
331
   * @deprecated
332
   */
333
  protected function doValuesDeprecated($table, $fields, &$values, $replace = false) {
334
    $tableSafe = $this->db_escape($table);
335
    $safeFields = implode(',', $this->safeFields($fields));
336
    $safeValues = implode(',', $values);
337
    $command = $replace ? 'REPLACE' : 'INSERT';
338
    $query = "{$command} INTO `{{{$tableSafe}}}` ({$safeFields}) VALUES {$safeValues}";
339
340
    return $this->doExecute($query);
341
  }
342
343
  // TODO - batch insert and replace here
344
345
  // TODO - перед тем, как переделывать данные из депрекейтов - убедится, что
346
  // null - это null, а не строка'NULL'
347
348
  /**
349
   * Values should be passed as-is
350
   *
351
   * @param string   $table
352
   * @param array    $fields
353
   * @param string[] $values
354
   *
355
   * @return array|bool|mysqli_result|null
356
   * @deprecated
357
   */
358
  public function doInsertValuesDeprecated($table, $fields, &$values) {
359
    return $this->doValuesDeprecated($table, $fields, $values, false);
0 ignored issues
show
Deprecated Code introduced by
The method db_mysql::doValuesDeprecated() has been deprecated.

This method has been deprecated.

Loading history...
360
  }
361
362
  /**
363
   * Values should be passed as-is
364
   *
365
   * @param string   $table
366
   * @param array    $fields
367
   * @param string[] $values
368
   *
369
   * @return array|bool|mysqli_result|null
370
   * @deprecated
371
   */
372
  public function doReplaceValuesDeprecated($table, $fields, &$values) {
373
    return $this->doValuesDeprecated($table, $fields, $values, true);
0 ignored issues
show
Deprecated Code introduced by
The method db_mysql::doValuesDeprecated() has been deprecated.

This method has been deprecated.

Loading history...
374
  }
375
376
  public function doUpdate($query) {
377
    return $this->doExecute($query);
378
  }
379
380
381
  /**
382
   * @param string $query
383
   *
384
   * @return array|bool|mysqli_result|null
385
   */
386
  public function doDelete($query) {
387
    return $this->doExecute($query);
388
  }
389
390
  /**
391
   * @param string $table
392
   * @param array  $where - simple WHERE statement list which can be combined with AND
393
   * @param bool   $isOneRecord
394
   *
395
   * @return array|bool|mysqli_result|null
396
   */
397
  public function doDeleteWhere($table, $where, $isOneRecord = false) {
398
    $tableSafe = $this->db_escape($table);
399
    $safeWhere = implode(' AND ', $this->safeFieldsAndValues($where));
400
    $query = "DELETE FROM `{{{$tableSafe}}}` WHERE {$safeWhere}"
401
      . ($isOneRecord ? ' LIMIT 1' : '');
402
403
    return $this->doDelete($query);
404
  }
405
406
  /**
407
   * @param string $table
408
   * @param array  $where - simple WHERE statement list which can be combined with AND
409
   *
410
   * @return array|bool|mysqli_result|null
411
   */
412
  public function doDeleteRowWhere($table, $where) {
413
    return $this->doDeleteWhere($table, $where, true);
414
  }
415
416
  /**
417
   * @param string $query
418
   *
419
   * @return array|bool|mysqli_result|null
420
   */
421
  public function doDeleteComplex($query) {
422
    return $this->doDelete($query);
423
  }
424
425
  /**
426
   * Early deprecated function for complex delete conditions
427
   *
428
   * Usually used for mallformed $where conditions
429
   *
430
   * @param $table
431
   * @param $where
432
   *
433
   * @return array|bool|mysqli_result|null
434
   * @deprecated
435
   */
436
  public function doDeleteDeprecated($table, $where) {
437
    return $this->doDeleteWhere($table, $where, false);
438
  }
439
440
441
  protected function castAsDbValue($value) {
442
    switch (gettype($value)) {
443
      case TYPE_INTEGER:
444
      case TYPE_DOUBLE:
445
        // do nothing
446
      break;
447
448
      case TYPE_BOOLEAN:
449
        $value = $value ? 1 : 0;
450
      break;
451
452
      case TYPE_NULL:
453
        $value = 'NULL';
454
      break;
455
456
      /** @noinspection PhpMissingBreakStatementInspection */
457
      case TYPE_ARRAY:
458
        $value = serialize($value);
459
      // Continuing with serialized array value
460
      case TYPE_STRING:
461
        // Empty type is string
462
      case TYPE_EMPTY:
463
        // No-type defaults to string
464
      default:
465
        $value = "'" . $this->db_escape((string)$value) . "'";
466
      break;
467
    }
468
469
    return $value;
470
  }
471
472
  /**
473
   * Make field list safe
474
   *
475
   * Support expressions - expression index should be strictly integer!
476
   *
477
   * @param array $fields - array of pair $fieldName => $fieldValue
478
   *
479
   * @return array
480
   */
481
  protected function safeFieldsAndValues($fields) {
482
    $result = array();
483
484
    if (!is_array($fields) || empty($fields)) {
485
      return $result;
486
    }
487
488
    foreach ($fields as $fieldName => $fieldValue) {
489
      // Integer $fieldName means "leave as is" - for expressions and already processed fields
490
      if (is_int($fieldName)) {
491
        $result[$fieldName] = $fieldValue;
492
      } else {
493
        $result[$fieldName] = "`{$fieldName}` = " . $this->castAsDbValue($fieldValue);
494
      }
495
    }
496
497
    return $result;
498
  }
499
500
  // TODO - redo as callable usage with array_map/array_walk
501 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...
502
    $result = array();
503
504
    if (!is_array($values) || empty($values)) {
505
      return $result;
506
    }
507
508
    foreach ($values as $key => $value) {
509
      $result[$key] = $this->castAsDbValue($value);
510
    }
511
512
    return $result;
513
  }
514
515
  // TODO - redo as callable usage with array_map/array_walk
516 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...
517
    $result = array();
518
519
    if (!is_array($fields) || empty($fields)) {
520
      return $result;
521
    }
522
523
    foreach ($fields as $key => $value) {
524
      $result[$key] = "`" . $this->db_escape($value) . "`";
525
    }
526
527
    return $result;
528
  }
529
530
531
  /**
532
   * Returns iterator to iterate through mysqli_result
533
   *
534
   * @param string $query
535
   *
536
   * return DbResultIterator
537
   *
538
   * @return DbEmptyIterator|DbMysqliResultIterator
539
   */
540
  public function doSelectIterator($query) {
541
    $queryResult = $this->doSelect($query);
542
543
    if ($queryResult instanceof mysqli_result) {
544
      $result = new DbMysqliResultIterator($queryResult);
545
    } else {
546
      $result = new DbEmptyIterator();
547
    }
548
549
    return $result;
550
  }
551
552
  /**
553
   * @param DbQueryConstructor $stmt
554
   * @param bool               $skip_query_check
555
   */
556
  public function doStmtLockAll($stmt, $skip_query_check = false) {
557
    $this->doExecute(
558
      $stmt
559
        ->select()
560
        ->field(1)
561
        ->setForUpdate()
562
        ->__toString(),
563
      $skip_query_check
564
    );
565
  }
566
567
  // TODO Заменить это на новый логгер
568
  protected function security_watch_user_queries($query) {
569
    global $user;
570
571
    if (
572
      !$this->isWatching // Not already watching
573
      && !empty(classSupernova::$config->game_watchlist_array) // There is some players in watchlist
574
      && in_array($user['id'], classSupernova::$config->game_watchlist_array) // Current player is in watchlist
575
      && !preg_match('/^(select|commit|rollback|start transaction)/i', $query) // Current query should be watched
576
    ) {
577
      $this->isWatching = true;
578
      $msg = "\$query = \"{$query}\"\n\r";
579
      if (!empty($_POST)) {
580
        $msg .= "\n\r" . dump($_POST, '$_POST');
581
      }
582
      if (!empty($_GET)) {
583
        $msg .= "\n\r" . dump($_GET, '$_GET');
584
      }
585
      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...
586
      $this->isWatching = false;
587
    }
588
  }
589
590
591
  public function security_query_check_bad_words($query) {
592
    if ($this->skipQueryCheck) {
593
      return;
594
    }
595
596
    global $user, $dm_change_legit, $mm_change_legit;
597
598
    switch (true) {
599
      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...
600
      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...
601
      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...
602
      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...
603
      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...
604
      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...
605
      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...
606
      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...
607
      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...
608
      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...
609
        $report = "Hacking attempt (" . date("d.m.Y H:i:s") . " - [" . time() . "]):\n";
610
        $report .= ">Database Inforamation\n";
611
        $report .= "\tID - " . $user['id'] . "\n";
612
        $report .= "\tUser - " . $user['username'] . "\n";
613
        $report .= "\tAuth level - " . $user['authlevel'] . "\n";
614
        $report .= "\tAdmin Notes - " . $user['adminNotes'] . "\n";
615
        $report .= "\tCurrent Planet - " . $user['current_planet'] . "\n";
616
        $report .= "\tUser IP - " . $user['user_lastip'] . "\n";
617
        $report .= "\tUser IP at Reg - " . $user['ip_at_reg'] . "\n";
618
        $report .= "\tUser Agent- " . $_SERVER['HTTP_USER_AGENT'] . "\n";
619
        $report .= "\tCurrent Page - " . $user['current_page'] . "\n";
620
        $report .= "\tRegister Time - " . $user['register_time'] . "\n";
621
        $report .= "\n";
622
623
        $report .= ">Query Information\n";
624
        $report .= "\tQuery - " . $query . "\n";
625
        $report .= "\n";
626
627
        $report .= ">\$_SERVER Information\n";
628
        $report .= "\tIP - " . $_SERVER['REMOTE_ADDR'] . "\n";
629
        $report .= "\tHost Name - " . $_SERVER['HTTP_HOST'] . "\n";
630
        $report .= "\tUser Agent - " . $_SERVER['HTTP_USER_AGENT'] . "\n";
631
        $report .= "\tRequest Method - " . $_SERVER['REQUEST_METHOD'] . "\n";
632
        $report .= "\tCame From - " . $_SERVER['HTTP_REFERER'] . "\n";
633
        $report .= "\tPage is - " . $_SERVER['SCRIPT_NAME'] . "\n";
634
        $report .= "\tUses Port - " . $_SERVER['REMOTE_PORT'] . "\n";
635
        $report .= "\tServer Protocol - " . $_SERVER['SERVER_PROTOCOL'] . "\n";
636
637
        $report .= "\n--------------------------------------------------------------------------------------------------\n";
638
639
        $fp = fopen(SN_ROOT_PHYSICAL . 'badqrys.txt', 'a');
640
        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 632
  1. Fetching key HTTP_REFERER from $_SERVER, and $report is assigned
    in includes/classes/db_mysql.php on line 632
  2. Path: Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned in includes/classes/db_mysql.php on line 630
  1. Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned
    in includes/classes/db_mysql.php on line 630
  2. $report is assigned
    in includes/classes/db_mysql.php on line 632
  3. Path: Fetching key HTTP_HOST from $_SERVER, and $report is assigned in includes/classes/db_mysql.php on line 629
  1. Fetching key HTTP_HOST from $_SERVER, and $report is assigned
    in includes/classes/db_mysql.php on line 629
  2. $report is assigned
    in includes/classes/db_mysql.php on line 630
  3. $report is assigned
    in includes/classes/db_mysql.php on line 632
  4. 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, and $value is assigned
    in includes/general.php on line 266
  3. sys_get_param_id() returns tainted data, and $fleetid is assigned
    in includes/includes/flt_page4.inc on line 9
  4. $fleetid is passed to DBStaticFleetACS::db_acs_insert()
    in includes/includes/flt_page4.inc on line 50
  5. ``'INSERT INTO `{{aks}}` SET `name` = \'' . db_escape(classLocale::$lang['flt_acs_prefix'] . $fleetid) . '\', `teilnehmer` = \'' . $user['id'] . '\', `flotten` = \'' . $fleetid . '\', `ankunft` = \'' . $objFleet->time_arrive_to_target . '\', `galaxy` = \'' . $objFleet->fleet_end_galaxy . '\', `system` = \'' . $objFleet->fleet_end_system . '\', `planet` = \'' . $objFleet->fleet_end_planet . '\', `planet_type` = \'' . $objFleet->fleet_end_type . '\', `eingeladen` = \'' . $user['id'] . '\', `fleet_end_time` = \'' . $objFleet->time_return_to_source . '\''`` is passed to db_mysql::doInsert()
    in includes/classes/DBStaticFleetACS.php on line 53
  6. $query is passed to db_mysql::doExecute()
    in includes/classes/db_mysql.php on line 302
  7. $query is passed to db_mysql::doquery()
    in includes/classes/db_mysql.php on line 267
  8. $stringQuery is assigned
    in includes/classes/db_mysql.php on line 222
  9. $stringQuery is passed through trim(), and $stringQuery is assigned
    in includes/classes/db_mysql.php on line 223
  10. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in includes/classes/db_mysql.php on line 228
  11. $report is assigned
    in includes/classes/db_mysql.php on line 624
  12. $report is assigned
    in includes/classes/db_mysql.php on line 629
  13. $report is assigned
    in includes/classes/db_mysql.php on line 630
  14. $report is assigned
    in includes/classes/db_mysql.php on line 632
  5. Path: Read from $_GET in includes/classes/db_mysql.php on line 583
  1. Read from $_GET
    in includes/classes/db_mysql.php on line 583
  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 583
  4. $msg is passed to debug::warning()
    in includes/classes/db_mysql.php on line 585
  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 267
  9. $stringQuery is assigned
    in includes/classes/db_mysql.php on line 222
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in includes/classes/db_mysql.php on line 223
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in includes/classes/db_mysql.php on line 228
  12. $report is assigned
    in includes/classes/db_mysql.php on line 624
  13. $report is assigned
    in includes/classes/db_mysql.php on line 629
  14. $report is assigned
    in includes/classes/db_mysql.php on line 630
  15. $report is assigned
    in includes/classes/db_mysql.php on line 632
  6. Path: Read from $_POST in includes/classes/db_mysql.php on line 580
  1. Read from $_POST
    in includes/classes/db_mysql.php on line 580
  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 580
  4. $msg is passed to debug::warning()
    in includes/classes/db_mysql.php on line 585
  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 267
  9. $stringQuery is assigned
    in includes/classes/db_mysql.php on line 222
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in includes/classes/db_mysql.php on line 223
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in includes/classes/db_mysql.php on line 228
  12. $report is assigned
    in includes/classes/db_mysql.php on line 624
  13. $report is assigned
    in includes/classes/db_mysql.php on line 629
  14. $report is assigned
    in includes/classes/db_mysql.php on line 630
  15. $report is assigned
    in includes/classes/db_mysql.php on line 632
  7. Path: Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned in includes/classes/db_mysql.php on line 618
  1. Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned
    in includes/classes/db_mysql.php on line 618
  2. $report is assigned
    in includes/classes/db_mysql.php on line 624
  3. $report is assigned
    in includes/classes/db_mysql.php on line 629
  4. $report is assigned
    in includes/classes/db_mysql.php on line 630
  5. $report is assigned
    in includes/classes/db_mysql.php on line 632

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...
641
        fclose($fp);
642
643
        $message = 'Привет, я не знаю то, что Вы пробовали сделать, но команда, которую Вы только послали базе данных, не выглядела очень дружественной и она была заблокированна.<br /><br />Ваш IP, и другие данные переданны администрации сервера. Удачи!.';
644
        die($message);
645
      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...
646
    }
647
  }
648
649
  /**
650
   * @param bool $prefixed_only
651
   *
652
   * @return array
653
   */
654
  public function db_get_table_list($prefixed_only = true) {
655
    $query = $this->mysql_get_table_list();
656
657
    $prefix_length = strlen($this->db_prefix);
658
659
    $tl = array();
660
    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 655 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...
661
      foreach ($row as $table_name) {
662
        if (strpos($table_name, $this->db_prefix) === 0) {
663
          $table_name = substr($table_name, $prefix_length);
664
        } elseif ($prefixed_only) {
665
          continue;
666
        }
667
        // $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...
668
        $tl[$table_name] = $table_name;
669
      }
670
    }
671
672
    return $tl;
673
  }
674
675
  /**
676
   * @param string $statement
677
   *
678
   * @return bool|mysqli_stmt
679
   */
680 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...
681
    $microtime = microtime(true);
682
    $result = $this->driver->mysql_prepare($statement);
683
    $this->time_mysql_total += microtime(true) - $microtime;
684
685
    return $result;
686
  }
687
688
689
  /**
690
   * L1 perform the query
691
   *
692
   * @param $query_string
693
   *
694
   * @return bool|mysqli_result
695
   */
696 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...
697
    $microtime = microtime(true);
698
    $result = $this->driver->mysql_query($query_string);
699
    $this->time_mysql_total += microtime(true) - $microtime;
700
701
    return $result;
702
  }
703
704
  /**
705
   * L1 fetch assoc array
706
   *
707
   * @param mysqli_result $query
708
   *
709
   * @return array|null
710
   */
711 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...
712
    $microtime = microtime(true);
713
    $result = $this->driver->mysql_fetch_assoc($query);
714
    $this->time_mysql_total += microtime(true) - $microtime;
715
716
    return $result;
717
  }
718
719
  public function db_fetch_row(&$query) {
720
    return $this->driver->mysql_fetch_row($query);
721
  }
722
723
  public function db_escape($unescaped_string) {
724
    return $this->driver->mysql_real_escape_string($unescaped_string);
725
  }
726
727
  public function driver_disconnect() {
728
    return $this->driver->mysql_close_link();
729
  }
730
731
  public function db_error() {
732
    return $this->driver->mysql_error();
733
  }
734
735
  public function db_insert_id() {
736
    return $this->driver->mysql_insert_id();
737
  }
738
739
  public function db_num_rows(&$result) {
740
    return $this->driver->mysql_num_rows($result);
741
  }
742
743
  public function db_affected_rows() {
744
    return $this->driver->mysql_affected_rows();
745
  }
746
747
  /**
748
   * @return string
749
   */
750
  public function db_get_client_info() {
751
    return $this->driver->mysql_get_client_info();
752
  }
753
754
  /**
755
   * @return string
756
   */
757
  public function db_get_server_info() {
758
    return $this->driver->mysql_get_server_info();
759
  }
760
761
  /**
762
   * @return string
763
   */
764
  public function db_get_host_info() {
765
    return $this->driver->mysql_get_host_info();
766
  }
767
768
  public function db_get_server_stat() {
769
    $result = array();
770
771
    $status = explode('  ', $this->driver->mysql_stat());
772
    foreach ($status as $value) {
773
      $row = explode(': ', $value);
774
      $result[$row[0]] = $row[1];
775
    }
776
777
    return $result;
778
  }
779
780
  /**
781
   * @return array
782
   * @throws Exception
783
   */
784
  public function db_core_show_status() {
785
    $result = array();
786
787
    $query = $this->db_sql_query('SHOW STATUS;');
788
    if (is_bool($query)) {
789
      throw new Exception('Result of SHOW STATUS command is boolean - which should never happen. Connection to DB is lost?');
790
    }
791
    while ($row = db_fetch($query)) {
792
      $result[$row['Variable_name']] = $row['Value'];
793
    }
794
795
    return $result;
796
  }
797
798
  public function mysql_get_table_list() {
799
    return $this->db_sql_query('SHOW TABLES;');
800
  }
801
802
  public function mysql_get_innodb_status() {
803
    return $this->db_sql_query('SHOW ENGINE INNODB STATUS;');
804
  }
805
806
  /**
807
   * @return \DBAL\DbTransaction
808
   */
809
  public function getTransaction() {
810
    return $this->transaction;
811
  }
812
813
  // Some wrappers to DbTransaction
814
  // Unused for now
815
  public function transactionCheck($status = null) {
816
    return $this->transaction->check($status);
817
  }
818
819
  public function transactionStart($level = '') {
820
    return $this->transaction->start($level);
821
  }
822
823
  public function transactionCommit() {
824
    return $this->transaction->commit();
825
  }
826
827
  public function transactionRollback() {
828
    return $this->transaction->rollback();
829
  }
830
831
}
832