Completed
Push — work-fleets ( 4ec5b3...fe2ede )
by SuperNova.WS
10:06
created

db_mysql::doUpdateDbQuery()   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 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 2
b 0
f 0
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
3
use \DBAL\DbQuery;
4
5
/**
6
 * Created by Gorlum 01.09.2015 15:58
7
 */
8
class db_mysql {
9
  const TRANSACTION_SERIALIZABLE = 'SERIALIZABLE';
10
  const TRANSACTION_REPEATABLE_READ = 'REPEATABLE READ';
11
  const TRANSACTION_READ_COMMITTED = 'READ COMMITTED';
12
  const TRANSACTION_READ_UNCOMMITTED = 'READ UNCOMMITTED';
13
14
  /**
15
   * Статус соеднения с MySQL
16
   *
17
   * @var bool
18
   */
19
  public $connected = false;
20
  /**
21
   * Префикс названий таблиц в БД
22
   *
23
   * @var string
24
   */
25
  public $db_prefix = '';
26
  /**
27
   * Список таблиц в БД
28
   *
29
   * @var array
30
   */
31
  public $table_list = array();
32
33
  /**
34
   * Настройки БД
35
   *
36
   * @var array
37
   */
38
  protected $dbsettings = array();
39
  /**
40
   * Драйвер для прямого обращения к MySQL
41
   *
42
   * @var db_mysql_v5 $driver
43
   */
44
  public $driver = null;
45
46
  /**
47
   * Общее время запросов
48
   *
49
   * @var float $time_mysql_total
50
   */
51
  public $time_mysql_total = 0.0;
52
53
  /**
54
   * Amount of queries on this DB
55
   *
56
   * @var int
57
   */
58
  public $queryCount = 0;
59
60
  public $isWatching = false;
61
62
  /**
63
   * @var \DBAL\DbTransaction $transaction
64
   */
65
  protected $transaction;
66
67
  /**
68
   * Should query check be skipped?
69
   *
70
   * Used for altering scheme of DB
71
   *
72
   * @var bool $skipQueryCheck
73
   */
74
  protected $skipQueryCheck = false;
75
76
  /**
77
   * @var SnCache $snCache
78
   */
79
  public $snCache;
80
81
  /**
82
   * db_mysql constructor.
83
   *
84
   * @param \Common\GlobalContainer $gc
85
   */
86
  public function __construct($gc) {
87
    $this->transaction = new \DBAL\DbTransaction($gc, $this);
88
    $this->snCache = new $gc->snCacheClass($gc, $this);
89
  }
90
91
  public function load_db_settings($configFile = '') {
92
    $dbsettings = array();
93
94
    empty($configFile) ? $configFile = SN_ROOT_PHYSICAL . "config" . DOT_PHP_EX : false;
95
96
    require $configFile;
97
98
    $this->dbsettings = $dbsettings;
99
  }
100
101
  /**
102
   * @param null|array $external_db_settings
103
   *
104
   * @return bool
105
   */
106
  public function sn_db_connect($external_db_settings = null) {
107
    $this->db_disconnect();
108
109
    if (is_array($external_db_settings) && !empty($external_db_settings)) {
110
      $this->dbsettings = $external_db_settings;
111
    }
112
113
    if (empty($this->dbsettings)) {
114
      $this->load_db_settings(SN_ROOT_PHYSICAL . "config.php");
115
    }
116
117
    // TODO - фатальные (?) ошибки на каждом шагу. Хотя - скорее Эксепшны
118
    if (!empty($this->dbsettings)) {
119
      $driver_name = empty($this->dbsettings['sn_driver']) ? 'db_mysql_v5' : $this->dbsettings['sn_driver'];
120
      $this->driver = new $driver_name();
121
      $this->db_prefix = $this->dbsettings['prefix'];
122
123
      $this->connected = $this->connected || $this->driver_connect();
124
125
      if ($this->connected) {
126
        $this->table_list = $this->db_get_table_list();
127
        // TODO Проверка на пустоту
128
      }
129
    } else {
130
      $this->connected = false;
131
    }
132
133
    return $this->connected;
134
  }
135
136
  protected function driver_connect() {
137
    if (!is_object($this->driver)) {
138
      classSupernova::$debug->error_fatal('DB Error - No driver for MySQL found!');
139
    }
140
141
    if (!method_exists($this->driver, 'mysql_connect')) {
142
      classSupernova::$debug->error_fatal('DB Error - WRONG MySQL driver!');
143
    }
144
145
    return $this->driver->mysql_connect($this->dbsettings);
146
  }
147
148
  public function db_disconnect() {
149
    if ($this->connected) {
150
      $this->connected = !$this->driver_disconnect();
151
      $this->connected = false;
152
    }
153
154
    return !$this->connected;
155
  }
156
157
  /**
158
   * @param string $query
159
   *
160
   * @return mixed|string
161
   */
162
  public function replaceTablePlaceholders($query) {
163
    $sql = $query;
164
    if (strpos($sql, '{{') !== false) {
165
      foreach ($this->table_list as $tableName) {
166
        $sql = str_replace("{{{$tableName}}}", $this->db_prefix . $tableName, $sql);
167
      }
168
    }
169
170
    return $sql;
171
  }
172
173
  /**
174
   * @param $query
175
   */
176
  protected function logQuery($query) {
177
    if (!classSupernova::$config->debug) {
178
      return;
179
    }
180
181
    $this->queryCount++;
182
    $arr = debug_backtrace();
183
    $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...
184
    $line = $arr[0]['line'];
185
    classSupernova::$debug->add("<tr><th>Query {$this->queryCount}: </th><th>$query</th><th>{$file} @ {$line}</th><th>&nbsp;</th></tr>");
186
  }
187
188
189
  /**
190
   * @return string
191
   */
192
  public function traceQuery() {
193
    if (!defined('DEBUG_SQL_COMMENT') || constant('DEBUG_SQL_ERROR') !== true) {
194
      return '';
195
    }
196
197
    $backtrace = debug_backtrace();
198
    $sql_comment = classSupernova::$debug->compact_backtrace($backtrace, defined('DEBUG_SQL_COMMENT_LONG'));
199
200
    if (defined('DEBUG_SQL_ERROR') && constant('DEBUG_SQL_ERROR') === true) {
201
      classSupernova::$debug->add_to_array($sql_comment);
202
    }
203
204
    $sql_commented = implode("\r\n", $sql_comment);
205
    if (defined('DEBUG_SQL_ONLINE') && constant('DEBUG_SQL_ONLINE') === true) {
206
      classSupernova::$debug->warning($sql_commented, 'SQL Debug', LOG_DEBUG_SQL);
207
    }
208
209
    return $sql_commented;
210
  }
211
212
  /**
213
   * @param string $query
214
   *
215
   * @return array|bool|mysqli_result|null
216
   */
217
  protected function queryDriver($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->traceQuery();
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 doSql($query, $skip_query_check = false) {
262
    $prevState = $this->skipQueryCheck;
263
    $this->skipQueryCheck = $skip_query_check;
264
    // TODO - disable watch ??
265
    $result = $this->queryDriver($query);
266
    $this->skipQueryCheck = $prevState;
267
268
    return $result;
269
  }
270
271
272
  // SELECTS
273
  public function doSelect($query) {
274
    return $this->doSql($query);
275
  }
276
277
  /**
278
   * DANGER! Fields and Where can be danger
279
   *
280
   * @param string $table
281
   * @param array  $fields
282
   * @param array  $where
283
   * @param bool   $isOneRecord
284
   *
285
   * @return array|bool|mysqli_result|null
286
   */
287
  public function doSelectDanger($table, $fields, $where = array(), $isOneRecord = DB_RECORDS_ALL, $forUpdate = DB_SELECT_PLAIN) {
288
    // TODO - TEMPORARY UNTIL DbQuery
289
    if (!empty($where)) {
290
      foreach ($where as $key => &$value) {
291
        if (!is_int($key)) {
292
          $value = "`$key` = '" . $this->db_escape($value) . "'";
293
        }
294
      }
295
    }
296
297
    $query =
298
      "SELECT " . implode(',', $fields) .
299
      " FROM `{{{$table}}}`" .
300
      (!empty($where) ? ' WHERE ' . implode(' AND ', $where) : '') .
301
      ($isOneRecord == DB_RECORD_ONE ? ' LIMIT 1' : '') .
302
      ($forUpdate == DB_SELECT_FOR_UPDATE ? ' FOR UPDATE' : '');
303
304
    return $this->doSql($query);
305
  }
306
307
  /**
308
   * @param string $query
309
   *
310
   * @return array|null
311
   */
312
  public function doSelectFetch($query) {
313
    return $this->db_fetch($this->doSelect($query));
0 ignored issues
show
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...
314
  }
315
316
  /**
317
   * @param string $query
318
   *
319
   * @return mixed|null
320
   */
321
  public function doSelectFetchValue($query) {
322
    $row = $this->doSelectFetch($query);
323
324
    return is_array($row) ? reset($row) : null;
325
  }
326
327
  /**
328
   * @param DbResultIterator $iterator
329
   *
330
   * @return mixed
331
   */
332
  public function getDbIteratorFirstValue($iterator) {
333
    $iterator->rewind();
334
    $row = $iterator->current();
335
    return array_pop($row);
336
  }
337
338
339
  // INSERT/REPLACE
340
  protected function doSet($table, $fieldsAndValues, $replace = DB_INSERT_PLAIN) {
341
    $query = DbQuery::build($this)
342
      ->setTable($table)
343
      ->setValues($fieldsAndValues)
344
      ->insertSet($replace);
345
346
    return $this->doSql($query);
347
  }
348
349
  // TODO - batch insert and replace here
350
  // TODO - перед тем, как переделывать данные из депрекейтов - убедится, что
351
  // null - это null, а не строка'NULL'
352
  /**
353
   * Values should be passed as-is
354
   *
355
   * DANGER! Values should be properly escaped before passing here
356
   *
357
   * @param string   $table
358
   * @param array    $fields
359
   * @param string[] $valuesDanger
360
   * @param int      $replace
361
   *
362
   * @return array|bool|mysqli_result|null
363
   * @deprecated
364
   */
365
  protected function doInsertBatchDanger($table, $fields, &$valuesDanger, $replace = DB_INSERT_PLAIN) {
366
    $query = DbQuery::build($this)
367
      ->setTable($table)
368
      ->setFields($fields)
369
      ->setValuesDanger($valuesDanger)
370
      ->insertBatch($replace);
371
372
    return $this->doSql($query);
373
  }
374
375
376
  // INSERTERS
377
  public function doInsertComplex($query) {
378
    return $this->doSql($query);
379
  }
380
381
  /**
382
   * @param string $table
383
   * @param array  $fieldsAndValues
384
   * @param int    $replace - DB_INSERT_PLAIN || DB_INSERT_IGNORE
385
   *
386
   * @return array|bool|mysqli_result|null
387
   */
388
  public function doInsertSet($table, $fieldsAndValues, $replace = DB_INSERT_PLAIN) {
389
    return $this->doSet($table, $fieldsAndValues, $replace);
390
  }
391
392
  /**
393
   * Values should be passed as-is
394
   *
395
   * @param string   $table
396
   * @param array    $fields
397
   * @param string[] $values
398
   *
399
   * @return array|bool|mysqli_result|null
400
   * @deprecated
401
   */
402
  public function doInsertValuesDeprecated($table, $fields, &$values) {
403
    return $this->doInsertBatchDanger($table, $fields, $values, DB_INSERT_PLAIN);
0 ignored issues
show
Deprecated Code introduced by
The method db_mysql::doInsertBatchDanger() has been deprecated.

This method has been deprecated.

Loading history...
404
  }
405
406
407
408
  // REPLACERS
409
  /**
410
   * Replaces record in DB
411
   *
412
   * There are no DANGER replace operations
413
   *
414
   * @param string $table
415
   * @param array  $fieldsAndValues
416
   *
417
   * @return array|bool|mysqli_result|null
418
   */
419
  public function doReplaceSet($table, $fieldsAndValues) {
420
    return $this->doSet($table, $fieldsAndValues, DB_INSERT_REPLACE);
421
  }
422
423
  /**
424
   * Values should be passed as-is
425
   *
426
   * @param string   $table
427
   * @param array    $fields
428
   * @param string[] $values
429
   *
430
   * @return array|bool|mysqli_result|null
431
   * @deprecated
432
   */
433
  public function doReplaceValuesDeprecated($table, $fields, &$values) {
434
    return $this->doInsertBatchDanger($table, $fields, $values, DB_INSERT_REPLACE);
0 ignored issues
show
Deprecated Code introduced by
The method db_mysql::doInsertBatchDanger() has been deprecated.

This method has been deprecated.

Loading history...
435
  }
436
437
438
  // UPDATERS
439
  public function doUpdateReallyComplex($query) {
440
    return $this->doSql($query);
441
  }
442
443
  /**
444
   * Executes self-contained SQL UPDATE query
445
   *
446
   * Self-contained - means no params used
447
   * Such queries usually used to make large amount of in-base calculations
448
   *
449
   * @param $query
450
   *
451
   * @return array|bool|mysqli_result|null
452
   */
453
  public function doUpdateSqlNoParam($query) {
454
    return $this->doSql($query);
455
  }
456
457
458
  /**
459
   * @param $DbQuery DbQuery
460
   */
461
  protected function doUpdateDbQuery($DbQuery) {
462
    return $this->doSql($DbQuery->update());
463
  }
464
465
  /**
466
   * @param $DbQuery DbQuery
467
   */
468
  public function doUpdateDbQueryAdjust($DbQuery) {
469
    return $this->doUpdateDbQuery($DbQuery);
470
  }
471
472
473
  protected function doUpdateWhere($table, $fieldsSet, $fieldsAdjust = array(), $where = array(), $isOneRecord = DB_RECORDS_ALL, $whereDanger = array()) {
474
    $query = DbQuery::build($this)
475
      ->setTable($table)
476
      ->setValues($fieldsSet)
477
      ->setAdjust($fieldsAdjust)
478
      // TODO - separate danger WHEREs
479
      ->setWhereArray($where)
480
      ->setWhereArrayDanger($whereDanger)
481
      ->setOneRow($isOneRecord)
482
      ->update();
483
484
    return $this->doSql($query);
485
  }
486
487
  public function doUpdateRowSet($table, $fieldsAndValues, $where) {
488
    return $this->doUpdateWhere($table, $fieldsAndValues, array(), $where, DB_RECORD_ONE);
489
  }
490
491
  public function doUpdateTableSet($table, $fieldsAndValues, $where = array()) {
492
    return $this->doUpdateWhere($table, $fieldsAndValues, array(), $where, DB_RECORDS_ALL);
493
  }
494
495
  public function doUpdateRowAdjust($table, $fieldsSet, $fieldsAdjust, $where) {
496
    return $this->doUpdateWhere($table, $fieldsSet, $fieldsAdjust, $where, DB_RECORD_ONE);
497
  }
498
499
  public function doUpdateTableAdjust($table, $fieldsSet, $fieldsAdjust, $where, $whereDanger = array()) {
500
    return $this->doUpdateWhere($table, $fieldsSet, $fieldsAdjust, $where, DB_RECORDS_ALL, $whereDanger);
501
  }
502
503
504
  // DELETERS
505
  /**
506
   * @param string $table
507
   * @param array  $where
508
   * @param bool   $isOneRecord
509
   *
510
   * @return DbQuery
511
   */
512
  protected function buildDeleteQuery($table, $where, $isOneRecord = DB_RECORDS_ALL) {
513
    return
514
      DbQuery::build($this)
515
        ->setTable($table)
516
        ->setWhereArray($where)
517
        ->setOneRow($isOneRecord);
518
  }
519
520
  /**
521
   * @param string $table
522
   * @param array  $where - simple WHERE statement list which can be combined with AND
523
   * @param bool   $isOneRecord
524
   *
525
   * @return array|bool|mysqli_result|null
526
   */
527
  public function doDeleteWhere($table, $where, $isOneRecord = DB_RECORDS_ALL) {
528
    return
529
      $this->doSql(
530
        $this->buildDeleteQuery($table, $where, $isOneRecord)
531
          ->delete()
532
      );
533
  }
534
535
  /**
536
   * Early deprecated function for complex delete conditions
537
   *
538
   * Used for malformed $where conditions
539
   * Also whereDanger can contain references for other {{tables}}
540
   *
541
   * @param string $table
542
   * @param array  $where
543
   * @param array  $whereDanger
544
   *
545
   * @return array|bool|mysqli_result|null
546
   * @deprecated
547
   */
548
  public function doDeleteDanger($table, $where, $whereDanger) {
549
    return
550
      $this->doSql(
551
        $this->buildDeleteQuery($table, $where, DB_RECORDS_ALL)
552
          ->setWhereArrayDanger($whereDanger)
553
          ->delete()
554
      );
555
  }
556
557
  /**
558
   * @param string $table
559
   * @param array  $where - simple WHERE statement list which can be combined with AND
560
   *
561
   * @return array|bool|mysqli_result|null
562
   */
563
  public function doDeleteRow($table, $where) {
564
    return $this->doDeleteWhere($table, $where, DB_RECORD_ONE);
565
  }
566
567
  /**
568
   * Perform simple delete queries on fixed tables w/o params
569
   *
570
   * @param string $query
571
   *
572
   * @return array|bool|mysqli_result|null
573
   */
574
  public function doDeleteSql($query) {
575
    return $this->doSql($query);
576
  }
577
578
579
  /**
580
   * Returns iterator to iterate through mysqli_result
581
   *
582
   * @param string $query
583
   *
584
   * return DbResultIterator
585
   *
586
   * @return DbEmptyIterator|DbMysqliResultIterator
587
   */
588
  public function doSelectIterator($query) {
589
    $queryResult = $this->doSelect($query);
590
591
    if ($queryResult instanceof mysqli_result) {
592
      $result = new DbMysqliResultIterator($queryResult);
593
    } else {
594
      $result = new DbEmptyIterator();
595
    }
596
597
    return $result;
598
  }
599
600
  /**
601
   * @param DbQueryConstructor $stmt
602
   * @param bool               $skip_query_check
603
   */
604
  public function doStmtLockAll($stmt, $skip_query_check = false) {
605
    $this->doSql(
606
      $stmt
607
        ->select()
608
        ->field(1)
609
        ->setForUpdate()
610
        ->__toString(),
611
      $skip_query_check
612
    );
613
  }
614
615
  // TODO Заменить это на новый логгер
616
  protected function security_watch_user_queries($query) {
617
    global $user;
618
619
    if (
620
      !$this->isWatching // Not already watching
621
      && !empty(classSupernova::$config->game_watchlist_array) // There is some players in watchlist
622
      && in_array($user['id'], classSupernova::$config->game_watchlist_array) // Current player is in watchlist
623
      && !preg_match('/^(select|commit|rollback|start transaction)/i', $query) // Current query should be watched
624
    ) {
625
      $this->isWatching = true;
626
      $msg = "\$query = \"{$query}\"\n\r";
627
      if (!empty($_POST)) {
628
        $msg .= "\n\r" . dump($_POST, '$_POST');
629
      }
630
      if (!empty($_GET)) {
631
        $msg .= "\n\r" . dump($_GET, '$_GET');
632
      }
633
      classSupernova::$debug->warning($msg, "Watching user {$user['id']}", 399, array('base_dump' => true));
634
      $this->isWatching = false;
635
    }
636
  }
637
638
639
  public function security_query_check_bad_words($query) {
640
    if ($this->skipQueryCheck) {
641
      return;
642
    }
643
644
    global $user, $dm_change_legit, $mm_change_legit;
645
646
    switch (true) {
647
      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...
648
      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...
649
      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...
650
      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...
651
      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...
652
      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...
653
      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...
654
      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...
655
      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...
656
      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...
657
        $report = "Hacking attempt (" . date("d.m.Y H:i:s") . " - [" . time() . "]):\n";
658
        $report .= ">Database Inforamation\n";
659
        $report .= "\tID - " . $user['id'] . "\n";
660
        $report .= "\tUser - " . $user['username'] . "\n";
661
        $report .= "\tAuth level - " . $user['authlevel'] . "\n";
662
        $report .= "\tAdmin Notes - " . $user['adminNotes'] . "\n";
663
        $report .= "\tCurrent Planet - " . $user['current_planet'] . "\n";
664
        $report .= "\tUser IP - " . $user['user_lastip'] . "\n";
665
        $report .= "\tUser IP at Reg - " . $user['ip_at_reg'] . "\n";
666
        $report .= "\tUser Agent- " . $_SERVER['HTTP_USER_AGENT'] . "\n";
667
        $report .= "\tCurrent Page - " . $user['current_page'] . "\n";
668
        $report .= "\tRegister Time - " . $user['register_time'] . "\n";
669
        $report .= "\n";
670
671
        $report .= ">Query Information\n";
672
        $report .= "\tQuery - " . $query . "\n";
673
        $report .= "\n";
674
675
        $report .= ">\$_SERVER Information\n";
676
        $report .= "\tIP - " . $_SERVER['REMOTE_ADDR'] . "\n";
677
        $report .= "\tHost Name - " . $_SERVER['HTTP_HOST'] . "\n";
678
        $report .= "\tUser Agent - " . $_SERVER['HTTP_USER_AGENT'] . "\n";
679
        $report .= "\tRequest Method - " . $_SERVER['REQUEST_METHOD'] . "\n";
680
        $report .= "\tCame From - " . $_SERVER['HTTP_REFERER'] . "\n";
681
        $report .= "\tPage is - " . $_SERVER['SCRIPT_NAME'] . "\n";
682
        $report .= "\tUses Port - " . $_SERVER['REMOTE_PORT'] . "\n";
683
        $report .= "\tServer Protocol - " . $_SERVER['SERVER_PROTOCOL'] . "\n";
684
685
        $report .= "\n--------------------------------------------------------------------------------------------------\n";
686
687
        $fp = fopen(SN_ROOT_PHYSICAL . 'badqrys.txt', 'a');
688
        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 classes/db_mysql.php on line 680
  1. Fetching key HTTP_REFERER from $_SERVER, and $report is assigned
    in classes/db_mysql.php on line 680
  2. Path: Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned in classes/db_mysql.php on line 678
  1. Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned
    in classes/db_mysql.php on line 678
  2. $report is assigned
    in classes/db_mysql.php on line 680
  3. Path: Fetching key HTTP_HOST from $_SERVER, and $report is assigned in classes/db_mysql.php on line 677
  1. Fetching key HTTP_HOST from $_SERVER, and $report is assigned
    in classes/db_mysql.php on line 677
  2. $report is assigned
    in classes/db_mysql.php on line 678
  3. $report is assigned
    in classes/db_mysql.php on line 680
  4. Path: Read from $_GET in classes/db_mysql.php on line 631
  1. Read from $_GET
    in classes/db_mysql.php on line 631
  2. Data is passed through gettype()
    in vendor/classes/debug.php on line 306
  3. $msg is assigned
    in classes/db_mysql.php on line 631
  4. $msg is passed to debug::warning()
    in classes/db_mysql.php on line 633
  5. Data is escaped by mysqli_real_escape_string() for sql context(s)
    in vendor/classes/db_mysql_v5.php on line 87
  6. $query is assigned
    in classes/debug.php on line 271
  7. $query is passed to db_mysql::doSql()
    in classes/debug.php on line 276
  8. $query is passed to db_mysql::queryDriver()
    in classes/db_mysql.php on line 265
  9. $stringQuery is assigned
    in classes/db_mysql.php on line 222
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in classes/db_mysql.php on line 223
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in classes/db_mysql.php on line 228
  12. $report is assigned
    in classes/db_mysql.php on line 672
  13. $report is assigned
    in classes/db_mysql.php on line 677
  14. $report is assigned
    in classes/db_mysql.php on line 678
  15. $report is assigned
    in classes/db_mysql.php on line 680
  5. Path: Read from $_POST in classes/db_mysql.php on line 628
  1. Read from $_POST
    in classes/db_mysql.php on line 628
  2. Data is passed through gettype()
    in vendor/classes/debug.php on line 306
  3. $msg is assigned
    in classes/db_mysql.php on line 628
  4. $msg is passed to debug::warning()
    in classes/db_mysql.php on line 633
  5. Data is escaped by mysqli_real_escape_string() for sql context(s)
    in vendor/classes/db_mysql_v5.php on line 87
  6. $query is assigned
    in classes/debug.php on line 271
  7. $query is passed to db_mysql::doSql()
    in classes/debug.php on line 276
  8. $query is passed to db_mysql::queryDriver()
    in classes/db_mysql.php on line 265
  9. $stringQuery is assigned
    in classes/db_mysql.php on line 222
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in classes/db_mysql.php on line 223
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in classes/db_mysql.php on line 228
  12. $report is assigned
    in classes/db_mysql.php on line 672
  13. $report is assigned
    in classes/db_mysql.php on line 677
  14. $report is assigned
    in classes/db_mysql.php on line 678
  15. $report is assigned
    in classes/db_mysql.php on line 680
  6. Path: Read from $_POST in includes/general.php on line 253
  1. Read from $_POST
    in includes/general.php on line 253
  2. sys_get_param() returns tainted data
    in includes/general.php on line 285
  3. Data is passed through strip_tags(), and Data is passed through trim()
    in vendor/includes/general.php on line 1297
  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/classes/db_mysql_v5.php on line 87
  6. $query is assigned
    in classes/debug.php on line 271
  7. $query is passed to db_mysql::doSql()
    in classes/debug.php on line 276
  8. $query is passed to db_mysql::queryDriver()
    in classes/db_mysql.php on line 265
  9. $stringQuery is assigned
    in classes/db_mysql.php on line 222
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in classes/db_mysql.php on line 223
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in classes/db_mysql.php on line 228
  12. $report is assigned
    in classes/db_mysql.php on line 672
  13. $report is assigned
    in classes/db_mysql.php on line 677
  14. $report is assigned
    in classes/db_mysql.php on line 678
  15. $report is assigned
    in classes/db_mysql.php on line 680
  7. Path: Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned in classes/db_mysql.php on line 666
  1. Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned
    in classes/db_mysql.php on line 666
  2. $report is assigned
    in classes/db_mysql.php on line 672
  3. $report is assigned
    in classes/db_mysql.php on line 677
  4. $report is assigned
    in classes/db_mysql.php on line 678
  5. $report is assigned
    in classes/db_mysql.php on line 680

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...
689
        fclose($fp);
690
691
        $message = 'Привет, я не знаю то, что Вы пробовали сделать, но команда, которую Вы только послали базе данных, не выглядела очень дружественной и она была заблокированна.<br /><br />Ваш IP, и другие данные переданны администрации сервера. Удачи!.';
692
        die($message);
693
      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...
694
    }
695
  }
696
697
  /**
698
   * @param bool $prefixed_only
699
   *
700
   * @return array
701
   */
702
  public function db_get_table_list($prefixed_only = true) {
703
    $query = $this->mysql_get_table_list();
704
705
    $prefix_length = strlen($this->db_prefix);
706
707
    $tl = array();
708
    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 703 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...
709
      foreach ($row as $table_name) {
710
        if (strpos($table_name, $this->db_prefix) === 0) {
711
          $table_name = substr($table_name, $prefix_length);
712
        } elseif ($prefixed_only) {
713
          continue;
714
        }
715
        // $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...
716
        $tl[$table_name] = $table_name;
717
      }
718
    }
719
720
    return $tl;
721
  }
722
723
  /**
724
   * @param string $statement
725
   *
726
   * @return bool|mysqli_stmt
727
   */
728 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...
729
    $microtime = microtime(true);
730
    $result = $this->driver->mysql_prepare($statement);
731
    $this->time_mysql_total += microtime(true) - $microtime;
732
733
    return $result;
734
  }
735
736
737
  /**
738
   * L1 perform the query
739
   *
740
   * @param $query_string
741
   *
742
   * @return bool|mysqli_result
743
   */
744 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...
745
    $microtime = microtime(true);
746
    $result = $this->driver->mysql_query($query_string);
747
    $this->time_mysql_total += microtime(true) - $microtime;
748
749
    return $result;
750
  }
751
752
  /**
753
   * L1 fetch assoc array
754
   *
755
   * @param mysqli_result $query
756
   *
757
   * @return array|null
758
   */
759 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...
760
    $microtime = microtime(true);
761
    $result = $this->driver->mysql_fetch_assoc($query);
762
    $this->time_mysql_total += microtime(true) - $microtime;
763
764
    return $result;
765
  }
766
767
  public function db_fetch_row(&$query) {
768
    return $this->driver->mysql_fetch_row($query);
769
  }
770
771
  public function db_escape($unescaped_string) {
772
    return $this->driver->mysql_real_escape_string($unescaped_string);
773
  }
774
775
  public function driver_disconnect() {
776
    return $this->driver->mysql_close_link();
777
  }
778
779
  public function db_error() {
780
    return $this->driver->mysql_error();
781
  }
782
783
  public function db_insert_id() {
784
    return $this->driver->mysql_insert_id();
785
  }
786
787
  public function db_num_rows(&$result) {
788
    return $this->driver->mysql_num_rows($result);
789
  }
790
791
  /**
792
   * @return int -1 means error
793
   */
794
  public function db_affected_rows() {
795
    return $this->driver->mysql_affected_rows();
796
  }
797
798
  /**
799
   * @return string
800
   */
801
  public function db_get_client_info() {
802
    return $this->driver->mysql_get_client_info();
803
  }
804
805
  /**
806
   * @return string
807
   */
808
  public function db_get_server_info() {
809
    return $this->driver->mysql_get_server_info();
810
  }
811
812
  /**
813
   * @return string
814
   */
815
  public function db_get_host_info() {
816
    return $this->driver->mysql_get_host_info();
817
  }
818
819
  public function db_get_server_stat() {
820
    $result = array();
821
822
    $status = explode('  ', $this->driver->mysql_stat());
823
    foreach ($status as $value) {
824
      $row = explode(': ', $value);
825
      $result[$row[0]] = $row[1];
826
    }
827
828
    return $result;
829
  }
830
831
  /**
832
   * @return array
833
   * @throws Exception
834
   */
835
  public function db_core_show_status() {
836
    $result = array();
837
838
    $query = $this->db_sql_query('SHOW STATUS;');
839
    if (is_bool($query)) {
840
      throw new Exception('Result of SHOW STATUS command is boolean - which should never happen. Connection to DB is lost?');
841
    }
842
    while ($row = db_fetch($query)) {
843
      $result[$row['Variable_name']] = $row['Value'];
844
    }
845
846
    return $result;
847
  }
848
849
  public function mysql_get_table_list() {
850
    return $this->db_sql_query('SHOW TABLES;');
851
  }
852
853
  public function mysql_get_innodb_status() {
854
    return $this->db_sql_query('SHOW ENGINE INNODB STATUS;');
855
  }
856
857
  // Some wrappers to DbTransaction
858
  // Unused for now
859
  /**
860
   * @return \DBAL\DbTransaction
861
   */
862
  public function getTransaction() {
863
    return $this->transaction;
864
  }
865
866
  public function transactionCheck($status = null) {
867
    return $this->transaction->check($status);
868
  }
869
870
  public function transactionStart($level = '') {
871
    return $this->transaction->start($level);
872
  }
873
874
  public function transactionCommit() {
875
    return $this->transaction->commit();
876
  }
877
878
  public function transactionRollback() {
879
    return $this->transaction->rollback();
880
  }
881
882
}
883