Completed
Push — work-fleets ( 961997...006942 )
by SuperNova.WS
06:22
created

db_mysql::getOperator()   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 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
3
use \DBAL\DbQuery;
4
use \DBAL\DbTransaction;
5
6
/**
7
 * Created by Gorlum 01.09.2015 15:58
8
 */
9
class db_mysql {
10
  const TRANSACTION_SERIALIZABLE = 'SERIALIZABLE';
11
  const TRANSACTION_REPEATABLE_READ = 'REPEATABLE READ';
12
  const TRANSACTION_READ_COMMITTED = 'READ COMMITTED';
13
  const TRANSACTION_READ_UNCOMMITTED = 'READ UNCOMMITTED';
14
15
  /**
16
   * Статус соеднения с MySQL
17
   *
18
   * @var bool
19
   */
20
  public $connected = false;
21
  /**
22
   * Префикс названий таблиц в БД
23
   *
24
   * @var string
25
   */
26
  public $db_prefix = '';
27
  /**
28
   * Список таблиц в БД
29
   *
30
   * @var array
31
   */
32
  public $table_list = array();
33
34
  /**
35
   * Настройки БД
36
   *
37
   * @var array
38
   */
39
  protected $dbsettings = array();
40
  /**
41
   * Драйвер для прямого обращения к MySQL
42
   *
43
   * @var db_mysql_v5 $driver
44
   */
45
  public $driver = null;
46
47
  /**
48
   * Общее время запросов
49
   *
50
   * @var float $time_mysql_total
51
   */
52
  public $time_mysql_total = 0.0;
53
54
  /**
55
   * Amount of queries on this DB
56
   *
57
   * @var int
58
   */
59
  public $queryCount = 0;
60
61
  public $isWatching = false;
62
63
  /**
64
   * @var \DBAL\DbTransaction $transaction
65
   */
66
  protected $transaction;
67
68
  /**
69
   * Should query check be skipped?
70
   *
71
   * Used for altering scheme of DB
72
   *
73
   * @var bool $skipQueryCheck
74
   */
75
  protected $skipQueryCheck = false;
76
77
  /**
78
   * @var SnCache $snCache
79
   */
80
  public $snCache;
81
82
  /**
83
   * @var DbRowDirectOperator $operator
84
   */
85
  protected $operator;
86
87
  /**
88
   * db_mysql constructor.
89
   *
90
   * @param \Common\GlobalContainer $gc
91
   */
92
  public function __construct($gc) {
93
    $this->transaction = new \DBAL\DbTransaction($gc, $this);
94
    $this->snCache = new $gc->snCacheClass($gc, $this);
95
    $this->operator = new DbRowDirectOperator($this);
96
  }
97
98
  public function load_db_settings($configFile = '') {
99
    $dbsettings = array();
100
101
    empty($configFile) ? $configFile = SN_ROOT_PHYSICAL . "config" . DOT_PHP_EX : false;
102
103
    require $configFile;
104
105
    $this->dbsettings = $dbsettings;
106
  }
107
108
  /**
109
   * @param null|array $external_db_settings
110
   *
111
   * @return bool
112
   */
113
  public function sn_db_connect($external_db_settings = null) {
114
    $this->db_disconnect();
115
116
    if (is_array($external_db_settings) && !empty($external_db_settings)) {
117
      $this->dbsettings = $external_db_settings;
118
    }
119
120
    if (empty($this->dbsettings)) {
121
      $this->load_db_settings(SN_ROOT_PHYSICAL . "config.php");
122
    }
123
124
    // TODO - фатальные (?) ошибки на каждом шагу. Хотя - скорее Эксепшны
125
    if (!empty($this->dbsettings)) {
126
      $driver_name = empty($this->dbsettings['sn_driver']) ? 'db_mysql_v5' : $this->dbsettings['sn_driver'];
127
      $this->driver = new $driver_name();
128
      $this->db_prefix = $this->dbsettings['prefix'];
129
130
      $this->connected = $this->connected || $this->driver_connect();
131
132
      if ($this->connected) {
133
        $this->table_list = $this->db_get_table_list();
134
        // TODO Проверка на пустоту
135
      }
136
    } else {
137
      $this->connected = false;
138
    }
139
140
    return $this->connected;
141
  }
142
143
  protected function driver_connect() {
144
    if (!is_object($this->driver)) {
145
      classSupernova::$debug->error_fatal('DB Error - No driver for MySQL found!');
146
    }
147
148
    if (!method_exists($this->driver, 'mysql_connect')) {
149
      classSupernova::$debug->error_fatal('DB Error - WRONG MySQL driver!');
150
    }
151
152
    return $this->driver->mysql_connect($this->dbsettings);
153
  }
154
155
  public function db_disconnect() {
156
    if ($this->connected) {
157
      $this->connected = !$this->driver_disconnect();
158
      $this->connected = false;
159
    }
160
161
    return !$this->connected;
162
  }
163
164
  /**
165
   * @param string $query
166
   *
167
   * @return mixed|string
168
   */
169
  public function replaceTablePlaceholders($query) {
170
    $sql = $query;
171
    if (strpos($sql, '{{') !== false) {
172
      foreach ($this->table_list as $tableName) {
173
        $sql = str_replace("{{{$tableName}}}", $this->db_prefix . $tableName, $sql);
174
      }
175
    }
176
177
    return $sql;
178
  }
179
180
  /**
181
   * @param $query
182
   */
183
  protected function logQuery($query) {
184
    if (!classSupernova::$config->debug) {
185
      return;
186
    }
187
188
    $this->queryCount++;
189
    $arr = debug_backtrace();
190
    $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...
191
    $line = $arr[0]['line'];
192
    classSupernova::$debug->add("<tr><th>Query {$this->queryCount}: </th><th>$query</th><th>{$file} @ {$line}</th><th>&nbsp;</th></tr>");
193
  }
194
195
196
  /**
197
   * @return string
198
   */
199
  public function traceQuery() {
200
    if (!defined('DEBUG_SQL_COMMENT') || constant('DEBUG_SQL_ERROR') !== true) {
201
      return '';
202
    }
203
204
    $backtrace = debug_backtrace();
205
    $sql_comment = classSupernova::$debug->compact_backtrace($backtrace, defined('DEBUG_SQL_COMMENT_LONG'));
206
207
    if (defined('DEBUG_SQL_ERROR') && constant('DEBUG_SQL_ERROR') === true) {
208
      classSupernova::$debug->add_to_array($sql_comment);
209
    }
210
211
    $sql_commented = implode("\r\n", $sql_comment);
212
    if (defined('DEBUG_SQL_ONLINE') && constant('DEBUG_SQL_ONLINE') === true) {
213
      classSupernova::$debug->warning($sql_commented, 'SQL Debug', LOG_DEBUG_SQL);
214
    }
215
216
    return $sql_commented;
217
  }
218
219
  /**
220
   * @param string $query
221
   *
222
   * @return array|bool|mysqli_result|null
223
   */
224
  protected function queryDriver($query) {
225
    if (!$this->connected) {
226
      $this->sn_db_connect();
227
    }
228
229
    $stringQuery = $query;
230
    $stringQuery = trim($stringQuery);
231
    // You can't do it - 'cause you can break commented statement with line-end comments
232
    // $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...
233
234
    $this->security_watch_user_queries($stringQuery);
235
    $this->security_query_check_bad_words($stringQuery);
236
    $this->logQuery($stringQuery);
237
238
    $stringQuery = $this->replaceTablePlaceholders($stringQuery);
239
240
    $queryTrace = $this->traceQuery();
241
242
    $queryResult = null;
243
    try {
244
      $queryResult = $this->db_sql_query($stringQuery . DbSqlHelper::quoteComment($queryTrace));
245
      if (!$queryResult) {
246
        throw new Exception();
247
      }
248
    } catch (Exception $e) {
249
      classSupernova::$debug->error($this->db_error() . "<br />{$query}<br />", 'SQL Error');
250
    }
251
252
    return $queryResult;
253
  }
254
255
256
  // Just wrappers to distinguish query types
257
  /**
258
   * Executes non-data manipulation statements
259
   *
260
   * Can execute queries with check skip
261
   * Honor current state of query checking
262
   *
263
   * @param string $query
264
   * @param bool   $skip_query_check
265
   *
266
   * @return array|bool|mysqli_result|null
267
   */
268
  public function doSql($query, $skip_query_check = false) {
269
    $prevState = $this->skipQueryCheck;
270
    $this->skipQueryCheck = $skip_query_check;
271
    // TODO - disable watch ??
272
    $result = $this->queryDriver($query);
273
    $this->skipQueryCheck = $prevState;
274
275
    return $result;
276
  }
277
278
279
  // TODO - should be in DbRowDirectOperator eventually
280
  // SQL operations ====================================================================================================
281
  // SELECTS -----------------------------------------------------------------------------------------------------------
282
  public function doSelect($query) {
283
    return $this->doSql($query);
284
  }
285
286
  /**
287
   * DANGER! Fields and Where can be danger
288
   *
289
   * @param string $table
290
   * @param array  $fields
291
   * @param array  $where
292
   * @param bool   $isOneRecord
293
   *
294
   * @return array|bool|mysqli_result|null
295
   *
296
   * TODO - replace with appropriate DbQuery when it's be ready
297
   */
298
  public function doSelectDanger($table, $fields, $where = array(), $isOneRecord = DB_RECORDS_ALL, $forUpdate = DB_SELECT_PLAIN) {
299
    // TODO - TEMPORARY UNTIL DbQuery
300
    if (!empty($where)) {
301
      foreach ($where as $key => &$value) {
302
        if (!is_int($key)) {
303
          $value = "`$key` = '" . $this->db_escape($value) . "'";
304
        }
305
      }
306
    }
307
308
    $query =
309
      "SELECT " . implode(',', $fields) .
310
      " FROM `{{{$table}}}`" .
311
      (!empty($where) ? ' WHERE ' . implode(' AND ', $where) : '') .
312
      ($isOneRecord == DB_RECORD_ONE ? ' LIMIT 1' : '') .
313
      ($forUpdate == DB_SELECT_FOR_UPDATE ? ' FOR UPDATE' : '');
314
315
    return $this->doSql($query);
316
  }
317
318
  /**
319
   * @param string $strSql
320
   *
321
   * @return array|null
322
   *
323
   * @deprecated
324
   * TODO - УДАЛИТЬ
325
   * Метод временно находится здесь - слишком много переписывать, что бы его отсюда вынести
326
   */
327
  public function doSelectFetchArray($strSql) {
328
    return $this->operator->doSelectFetchArray($strSql);
329
  }
330
331
332
333
  //
334
  // INSERT/REPLACE ----------------------------------------------------------------------------------------------------
335
  protected function doSet($table, $fieldsAndValues, $replace = DB_INSERT_PLAIN) {
336
    $query = DbQuery::build($this)
337
      ->setTable($table)
338
      ->setValues($fieldsAndValues)
339
      ->insertSet($replace);
340
341
    return $this->doSql($query);
342
  }
343
344
  // TODO - batch insert and replace here
345
  // перед тем, как переделывать данные из депрекейтов - убедится, что
346
  // null - это null, а не строка'NULL'
347
  /**
348
   * Values should be passed as-is
349
   *
350
   * DANGER! Values should be properly escaped before passing here
351
   *
352
   * @param string   $table
353
   * @param array    $fields
354
   * @param string[] $valuesDanger
355
   * @param int      $replace
356
   *
357
   * @return array|bool|mysqli_result|null
358
   * @deprecated
359
   */
360
  protected function doInsertBatchDanger($table, $fields, &$valuesDanger, $replace = DB_INSERT_PLAIN) {
361
    $query = DbQuery::build($this)
362
      ->setTable($table)
363
      ->setFields($fields)
364
      ->setValuesDanger($valuesDanger)
365
      ->insertBatch($replace);
366
367
    return $this->doSql($query);
368
  }
369
370
371
  // INSERTERS
372
  public function doInsertComplex($query) {
373
    return $this->doSql($query);
374
  }
375
376
  /**
377
   * @param string $table
378
   * @param array  $fieldsAndValues
379
   * @param int    $replace - DB_INSERT_PLAIN || DB_INSERT_IGNORE
380
   *
381
   * @return array|bool|mysqli_result|null
382
   */
383
  public function doInsertSet($table, $fieldsAndValues, $replace = DB_INSERT_PLAIN) {
384
    return $this->doSet($table, $fieldsAndValues, $replace);
385
  }
386
387
  /**
388
   * Values should be passed as-is
389
   *
390
   * @param string   $table
391
   * @param array    $fields
392
   * @param string[] $values
393
   *
394
   * @return array|bool|mysqli_result|null
395
   * @deprecated
396
   */
397
  public function doInsertValuesDeprecated($table, $fields, &$values) {
398
    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...
399
  }
400
401
402
  // REPLACERS
403
  /**
404
   * Replaces record in DB
405
   *
406
   * There are no DANGER replace operations
407
   *
408
   * @param string $table
409
   * @param array  $fieldsAndValues
410
   *
411
   * @return array|bool|mysqli_result|null
412
   */
413
  public function doReplaceSet($table, $fieldsAndValues) {
414
    return $this->doSet($table, $fieldsAndValues, DB_INSERT_REPLACE);
415
  }
416
417
  /**
418
   * Values should be passed as-is
419
   *
420
   * @param string   $table
421
   * @param array    $fields
422
   * @param string[] $values
423
   *
424
   * @return array|bool|mysqli_result|null
425
   * @deprecated
426
   */
427
  public function doReplaceValuesDeprecated($table, $fields, &$values) {
428
    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...
429
  }
430
431
432
  // UPDATERS
433
  public function doUpdateReallyComplex($query) {
434
    return $this->doSql($query);
435
  }
436
437
  /**
438
   * Executes self-contained SQL UPDATE query
439
   *
440
   * Self-contained - means no params used
441
   * Such queries usually used to make large amount of in-base calculations
442
   *
443
   * @param $query
444
   *
445
   * @return array|bool|mysqli_result|null
446
   */
447
  public function doUpdateSqlNoParam($query) {
448
    return $this->doSql($query);
449
  }
450
451
452
  /**
453
   * @param $DbQuery DbQuery
454
   */
455
  protected function doUpdateDbQuery($DbQuery) {
456
    return $this->doSql($DbQuery->update());
457
  }
458
459
  /**
460
   * @param $DbQuery DbQuery
461
   */
462
  public function doUpdateDbQueryAdjust($DbQuery) {
463
    return $this->doUpdateDbQuery($DbQuery);
464
  }
465
466
467
  protected function doUpdateWhere($table, $fieldsSet, $fieldsAdjust = array(), $where = array(), $isOneRecord = DB_RECORDS_ALL, $whereDanger = array()) {
468
    $query = DbQuery::build($this)
469
      ->setTable($table)
470
      ->setValues($fieldsSet)
471
      ->setAdjust($fieldsAdjust)
472
      // TODO - separate danger WHEREs
473
      ->setWhereArray($where)
474
      ->setWhereArrayDanger($whereDanger)
475
      ->setOneRow($isOneRecord)
476
      ->update();
477
478
    return $this->doSql($query);
479
  }
480
481
  public function doUpdateRowSet($table, $fieldsAndValues, $where) {
482
    return $this->doUpdateWhere($table, $fieldsAndValues, array(), $where, DB_RECORD_ONE);
483
  }
484
485
  public function doUpdateTableSet($table, $fieldsAndValues, $where = array()) {
486
    return $this->doUpdateWhere($table, $fieldsAndValues, array(), $where, DB_RECORDS_ALL);
487
  }
488
489
  public function doUpdateRowAdjust($table, $fieldsSet, $fieldsAdjust, $where) {
490
    return $this->doUpdateWhere($table, $fieldsSet, $fieldsAdjust, $where, DB_RECORD_ONE);
491
  }
492
493
  public function doUpdateTableAdjust($table, $fieldsSet, $fieldsAdjust, $where, $whereDanger = array()) {
494
    return $this->doUpdateWhere($table, $fieldsSet, $fieldsAdjust, $where, DB_RECORDS_ALL, $whereDanger);
495
  }
496
497
498
  //
499
  // DELETERS ----------------------------------------------------------------------------------------------------------
500
  /**
501
   * @param string $table
502
   * @param array  $where
503
   * @param bool   $isOneRecord
504
   *
505
   * @return DbQuery
506
   */
507
  protected function buildDeleteQuery($table, $where, $isOneRecord = DB_RECORDS_ALL) {
508
    return
509
      DbQuery::build($this)
510
        ->setTable($table)
511
        ->setWhereArray($where)
512
        ->setOneRow($isOneRecord);
513
  }
514
515
  /**
516
   * @param string $table
517
   * @param array  $where - simple WHERE statement list which can be combined with AND
518
   * @param bool   $isOneRecord
519
   *
520
   * @return array|bool|mysqli_result|null
521
   */
522
  public function doDeleteWhere($table, $where, $isOneRecord = DB_RECORDS_ALL) {
523
    return
524
      $this->doSql(
525
        $this->buildDeleteQuery($table, $where, $isOneRecord)
526
          ->delete()
527
      );
528
  }
529
530
  /**
531
   * Early deprecated function for complex delete conditions
532
   *
533
   * Used for malformed $where conditions
534
   * Also whereDanger can contain references for other {{tables}}
535
   *
536
   * @param string $table
537
   * @param array  $where
538
   * @param array  $whereDanger
539
   *
540
   * @return array|bool|mysqli_result|null
541
   * @deprecated
542
   */
543
  public function doDeleteDanger($table, $where, $whereDanger) {
544
    return
545
      $this->doSql(
546
        $this->buildDeleteQuery($table, $where, DB_RECORDS_ALL)
547
          ->setWhereArrayDanger($whereDanger)
548
          ->delete()
549
      );
550
  }
551
552
  /**
553
   * @param string $table
554
   * @param array  $where - simple WHERE statement list which can be combined with AND
555
   *
556
   * @return array|bool|mysqli_result|null
557
   */
558
  public function doDeleteRow($table, $where) {
559
    return $this->doDeleteWhere($table, $where, DB_RECORD_ONE);
560
  }
561
562
  /**
563
   * Perform simple delete queries on fixed tables w/o params
564
   *
565
   * @param string $query
566
   *
567
   * @return array|bool|mysqli_result|null
568
   */
569
  public function doDeleteSql($query) {
570
    return $this->doSql($query);
571
  }
572
573
574
  // LOCKERS ----------------------------------------------------------------------------------------------------------
575
  /**
576
   * @param DbQueryConstructor $stmt
577
   * @param bool               $skip_query_check
578
   */
579
  public function doStmtLockAll($stmt, $skip_query_check = false) {
580
    $this->doSql(
581
      $stmt
582
        ->select()
583
        ->field(1)
584
        ->setForUpdate()
585
        ->__toString(),
586
      $skip_query_check
587
    );
588
  }
589
590
591
  //
592
  // OTHER FUNCTIONS ----------------------------------------------------------------------------------------------------------
593
  // TODO Заменить это на новый логгер
594
  protected function security_watch_user_queries($query) {
595
    global $user;
596
597
    if (
598
      !$this->isWatching // Not already watching
599
      && !empty(classSupernova::$config->game_watchlist_array) // There is some players in watchlist
600
      && in_array($user['id'], classSupernova::$config->game_watchlist_array) // Current player is in watchlist
601
      && !preg_match('/^(select|commit|rollback|start transaction)/i', $query) // Current query should be watched
602
    ) {
603
      $this->isWatching = true;
604
      $msg = "\$query = \"{$query}\"\n\r";
605
      if (!empty($_POST)) {
606
        $msg .= "\n\r" . dump($_POST, '$_POST');
607
      }
608
      if (!empty($_GET)) {
609
        $msg .= "\n\r" . dump($_GET, '$_GET');
610
      }
611
      classSupernova::$debug->warning($msg, "Watching user {$user['id']}", 399, array('base_dump' => true));
612
      $this->isWatching = false;
613
    }
614
  }
615
616
617
  public function security_query_check_bad_words($query) {
618
    if ($this->skipQueryCheck) {
619
      return;
620
    }
621
622
    global $user, $dm_change_legit, $mm_change_legit;
623
624
    switch(true) {
625
      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...
626
      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...
627
      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...
628
      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...
629
      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...
630
      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...
631
      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...
632
      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...
633
      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...
634
      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...
635
        $report = "Hacking attempt (" . date("d.m.Y H:i:s") . " - [" . time() . "]):\n";
636
        $report .= ">Database Inforamation\n";
637
        $report .= "\tID - " . $user['id'] . "\n";
638
        $report .= "\tUser - " . $user['username'] . "\n";
639
        $report .= "\tAuth level - " . $user['authlevel'] . "\n";
640
        $report .= "\tAdmin Notes - " . $user['adminNotes'] . "\n";
641
        $report .= "\tCurrent Planet - " . $user['current_planet'] . "\n";
642
        $report .= "\tUser IP - " . $user['user_lastip'] . "\n";
643
        $report .= "\tUser IP at Reg - " . $user['ip_at_reg'] . "\n";
644
        $report .= "\tUser Agent- " . $_SERVER['HTTP_USER_AGENT'] . "\n";
645
        $report .= "\tCurrent Page - " . $user['current_page'] . "\n";
646
        $report .= "\tRegister Time - " . $user['register_time'] . "\n";
647
        $report .= "\n";
648
649
        $report .= ">Query Information\n";
650
        $report .= "\tQuery - " . $query . "\n";
651
        $report .= "\n";
652
653
        $report .= ">\$_SERVER Information\n";
654
        $report .= "\tIP - " . $_SERVER['REMOTE_ADDR'] . "\n";
655
        $report .= "\tHost Name - " . $_SERVER['HTTP_HOST'] . "\n";
656
        $report .= "\tUser Agent - " . $_SERVER['HTTP_USER_AGENT'] . "\n";
657
        $report .= "\tRequest Method - " . $_SERVER['REQUEST_METHOD'] . "\n";
658
        $report .= "\tCame From - " . $_SERVER['HTTP_REFERER'] . "\n";
659
        $report .= "\tPage is - " . $_SERVER['SCRIPT_NAME'] . "\n";
660
        $report .= "\tUses Port - " . $_SERVER['REMOTE_PORT'] . "\n";
661
        $report .= "\tServer Protocol - " . $_SERVER['SERVER_PROTOCOL'] . "\n";
662
663
        $report .= "\n--------------------------------------------------------------------------------------------------\n";
664
665
        $fp = fopen(SN_ROOT_PHYSICAL . 'badqrys.txt', 'a');
666
        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 658
  1. Fetching key HTTP_REFERER from $_SERVER, and $report is assigned
    in classes/db_mysql.php on line 658
  2. Path: Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned in classes/db_mysql.php on line 656
  1. Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned
    in classes/db_mysql.php on line 656
  2. $report is assigned
    in classes/db_mysql.php on line 658
  3. Path: Fetching key HTTP_HOST from $_SERVER, and $report is assigned in classes/db_mysql.php on line 655
  1. Fetching key HTTP_HOST from $_SERVER, and $report is assigned
    in classes/db_mysql.php on line 655
  2. $report is assigned
    in classes/db_mysql.php on line 656
  3. $report is assigned
    in classes/db_mysql.php on line 658
  4. Path: Read from $_GET in classes/db_mysql.php on line 609
  1. Read from $_GET
    in classes/db_mysql.php on line 609
  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 609
  4. $msg is passed to debug::warning()
    in classes/db_mysql.php on line 611
  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 272
  9. $stringQuery is assigned
    in classes/db_mysql.php on line 229
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in classes/db_mysql.php on line 230
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in classes/db_mysql.php on line 235
  12. $report is assigned
    in classes/db_mysql.php on line 650
  13. $report is assigned
    in classes/db_mysql.php on line 655
  14. $report is assigned
    in classes/db_mysql.php on line 656
  15. $report is assigned
    in classes/db_mysql.php on line 658
  5. Path: Read from $_POST in classes/db_mysql.php on line 606
  1. Read from $_POST
    in classes/db_mysql.php on line 606
  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 606
  4. $msg is passed to debug::warning()
    in classes/db_mysql.php on line 611
  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 272
  9. $stringQuery is assigned
    in classes/db_mysql.php on line 229
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in classes/db_mysql.php on line 230
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in classes/db_mysql.php on line 235
  12. $report is assigned
    in classes/db_mysql.php on line 650
  13. $report is assigned
    in classes/db_mysql.php on line 655
  14. $report is assigned
    in classes/db_mysql.php on line 656
  15. $report is assigned
    in classes/db_mysql.php on line 658
  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 272
  9. $stringQuery is assigned
    in classes/db_mysql.php on line 229
  10. $stringQuery is passed through trim(), and $stringQuery is assigned
    in classes/db_mysql.php on line 230
  11. $stringQuery is passed to db_mysql::security_query_check_bad_words()
    in classes/db_mysql.php on line 235
  12. $report is assigned
    in classes/db_mysql.php on line 650
  13. $report is assigned
    in classes/db_mysql.php on line 655
  14. $report is assigned
    in classes/db_mysql.php on line 656
  15. $report is assigned
    in classes/db_mysql.php on line 658
  7. Path: Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned in classes/db_mysql.php on line 644
  1. Fetching key HTTP_USER_AGENT from $_SERVER, and $report is assigned
    in classes/db_mysql.php on line 644
  2. $report is assigned
    in classes/db_mysql.php on line 650
  3. $report is assigned
    in classes/db_mysql.php on line 655
  4. $report is assigned
    in classes/db_mysql.php on line 656
  5. $report is assigned
    in classes/db_mysql.php on line 658

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