1 | <?php /** @noinspection PhpRedundantOptionalArgumentInspection */ |
||||
2 | |||||
3 | /** @noinspection PhpDeprecationInspection */ |
||||
4 | /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */ |
||||
5 | |||||
6 | namespace DBAL; |
||||
7 | |||||
8 | use classConfig; |
||||
9 | use Core\GlobalContainer; |
||||
10 | use debug; |
||||
11 | use mysqli; |
||||
12 | use mysqli_result; |
||||
13 | use SN; |
||||
14 | use Unit\DBStaticUnit; |
||||
15 | |||||
16 | /** |
||||
17 | * User: Gorlum |
||||
18 | * Date: 01.09.2015 |
||||
19 | * Time: 15:58 |
||||
20 | */ |
||||
21 | class db_mysql { |
||||
22 | const TRANSACTION_LEVEL_SERIALIZABLE = 'SERIALIZABLE'; |
||||
23 | const TRANSACTION_LEVEL_REPEATABLE_READ = 'REPEATABLE READ'; |
||||
24 | const TRANSACTION_LEVEL_READ_COMMITTED = 'READ COMMITTED'; |
||||
25 | const TRANSACTION_LEVEL_READ_UNCOMMITTED = 'READ UNCOMMITTED'; |
||||
26 | |||||
27 | const TRANSACTION_LEVELS_ALLOWED = [ |
||||
28 | self::TRANSACTION_LEVEL_SERIALIZABLE, |
||||
29 | self::TRANSACTION_LEVEL_REPEATABLE_READ, |
||||
30 | self::TRANSACTION_LEVEL_READ_COMMITTED, |
||||
31 | self::TRANSACTION_LEVEL_READ_UNCOMMITTED, |
||||
32 | ]; |
||||
33 | const DB_TRANSACTION_WHATEVER = false; |
||||
34 | const DB_TRANSACTION_SHOULD_BE = true; |
||||
35 | const DB_TRANSACTION_SHOULD_NOT_BE = null; |
||||
36 | public static $transaction_id = 0; |
||||
37 | public static $db_in_transaction = false; |
||||
38 | public static $transactionDepth = 0; |
||||
39 | |||||
40 | const TABLE_USERS = 'users'; |
||||
41 | |||||
42 | const TABLE_ID_FIELD = [ |
||||
43 | 'fleets' => 'fleet_id', |
||||
44 | ]; |
||||
45 | |||||
46 | // Order in which tables should be locked |
||||
47 | const TABLE_LOCK_ORDER = [ |
||||
48 | 'users', |
||||
49 | 'planets', |
||||
50 | 'que', |
||||
51 | 'unit', |
||||
52 | 'fleets', |
||||
53 | ]; |
||||
54 | |||||
55 | /** |
||||
56 | * DB schemes |
||||
57 | * |
||||
58 | * @var Schema|null $schema |
||||
59 | */ |
||||
60 | protected static $schema = null; |
||||
61 | |||||
62 | /** |
||||
63 | * Статус соединения с MySQL |
||||
64 | * |
||||
65 | * @var bool |
||||
66 | */ |
||||
67 | public $connected = false; |
||||
68 | /** |
||||
69 | * Префикс названий таблиц в БД |
||||
70 | * |
||||
71 | * @var string |
||||
72 | */ |
||||
73 | public $db_prefix = ''; |
||||
74 | public $dbName = ''; |
||||
75 | /** |
||||
76 | * Настройки БД |
||||
77 | * |
||||
78 | * @var array |
||||
79 | */ |
||||
80 | protected $dbsettings = []; |
||||
81 | // /** |
||||
82 | // * Драйвер для прямого обращения к MySQL |
||||
83 | // * |
||||
84 | // * @var db_mysql_v5 $driver |
||||
85 | // */ |
||||
86 | // public $driver = null; |
||||
87 | |||||
88 | /** |
||||
89 | * Общее время запросов |
||||
90 | * |
||||
91 | * @var float $time_mysql_total |
||||
92 | */ |
||||
93 | public $time_mysql_total = 0.0; |
||||
94 | |||||
95 | /** |
||||
96 | * @var bool $inTransaction |
||||
97 | */ |
||||
98 | protected $inTransaction = false; |
||||
99 | |||||
100 | |||||
101 | /** |
||||
102 | * Соединение с MySQL |
||||
103 | * |
||||
104 | * @var mysqli $link |
||||
105 | */ |
||||
106 | public $link; |
||||
107 | |||||
108 | |||||
109 | /** |
||||
110 | * DBAL\db_mysql constructor. |
||||
111 | * |
||||
112 | * @param GlobalContainer $gc |
||||
113 | */ |
||||
114 | public function __construct($gc) { |
||||
0 ignored issues
–
show
|
|||||
115 | // $this->transaction = new \DBAL\DbTransaction($gc, $this); |
||||
116 | // $this->snCache = new $gc->snCacheClass($gc, $this); |
||||
117 | // $this->operator = new DbRowDirectOperator($this); |
||||
118 | } |
||||
119 | |||||
120 | /** |
||||
121 | * @return Schema |
||||
122 | */ |
||||
123 | public function schema() { |
||||
124 | if (!isset(self::$schema)) { |
||||
125 | self::$schema = new Schema($this); |
||||
126 | } |
||||
127 | |||||
128 | return self::$schema; |
||||
129 | } |
||||
130 | |||||
131 | public function load_db_settings() { |
||||
132 | $dbsettings = array(); |
||||
133 | |||||
134 | require(SN_CONFIG_PATH); |
||||
135 | |||||
136 | $this->setDbSettings($dbsettings); |
||||
137 | } |
||||
138 | |||||
139 | public function sn_db_connect($external_db_settings = null) { |
||||
140 | $this->db_disconnect(); |
||||
141 | |||||
142 | if (!empty($external_db_settings) && is_array($external_db_settings)) { |
||||
143 | $this->setDbSettings($external_db_settings); |
||||
144 | } |
||||
145 | |||||
146 | if (empty($this->dbsettings)) { |
||||
147 | $this->load_db_settings(); |
||||
148 | } |
||||
149 | |||||
150 | // TODO - фатальные (?) ошибки на каждом шагу |
||||
151 | if (!empty($this->dbsettings)) { |
||||
152 | // $driver_name = 'DBAL\\' . (empty($this->dbsettings['sn_driver']) ? 'db_mysql_v5' : $this->dbsettings['sn_driver']); |
||||
153 | // $this->driver = new $driver_name(); |
||||
154 | $this->db_prefix = $this->dbsettings['prefix']; |
||||
155 | |||||
156 | $this->connected = $this->connected || $this->mysql_connect_driver($this->dbsettings); |
||||
157 | |||||
158 | if ($this->connected && empty($this->schema()->getSnTables())) { |
||||
159 | die('DB error - cannot find any table. Halting...'); |
||||
0 ignored issues
–
show
|
|||||
160 | } |
||||
161 | |||||
162 | $this->doQueryFast('SET SESSION TRANSACTION ISOLATION LEVEL ' . self::TRANSACTION_LEVEL_SERIALIZABLE); |
||||
163 | } else { |
||||
164 | $this->connected = false; |
||||
165 | } |
||||
166 | |||||
167 | return $this->connected; |
||||
168 | } |
||||
169 | |||||
170 | public function mysql_connect_driver($settings) { |
||||
171 | global $debug; |
||||
172 | |||||
173 | static $need_keys = array('server', 'user', 'pass', 'name', 'prefix'); |
||||
174 | |||||
175 | if ($this->connected) { |
||||
176 | return true; |
||||
177 | } |
||||
178 | |||||
179 | if (empty($settings) || !is_array($settings) || array_intersect($need_keys, array_keys($settings)) != $need_keys) { |
||||
180 | $debug->error_fatal('There is miss-configuration in your config.php. Check it again'); |
||||
181 | } |
||||
182 | |||||
183 | @$this->link = mysqli_connect($settings['server'], $settings['user'], $settings['pass'], $settings['name']); |
||||
0 ignored issues
–
show
The call to
mysqli_connect() has too few arguments starting with port .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
184 | if (!is_object($this->link) || $this->link->connect_error) { |
||||
185 | $debug->error_fatal('DB Error - cannot connect to server error #' . $this->link->connect_errno, $this->link->connect_error); |
||||
186 | } |
||||
187 | |||||
188 | $this->db_sql_query("/*!40101 SET NAMES 'utf8' */") |
||||
189 | or $debug->error_fatal('DB error - cannot set names 1 error #' . $this->link->errno, $this->link->error); |
||||
190 | $this->db_sql_query("SET NAMES 'utf8';") |
||||
191 | or $debug->error_fatal('DB error - cannot set names 2 error #' . $this->link->errno, $this->link->error); |
||||
192 | |||||
193 | //mysql_select_db($settings['name']) or $debug->error_fatal('DB error - cannot find DB on server', $this->mysql_error()); |
||||
194 | $this->db_sql_query('SET SESSION TRANSACTION ISOLATION LEVEL ' . self::TRANSACTION_LEVEL_SERIALIZABLE . ';') |
||||
195 | or $debug->error_fatal('DB error - cannot set desired isolation level error #' . $this->link->errno, $this->link->error); |
||||
196 | |||||
197 | $this->connected = true; |
||||
198 | |||||
199 | return true; |
||||
200 | } |
||||
201 | |||||
202 | public function db_disconnect() { |
||||
203 | if ($this->connected) { |
||||
204 | /** @noinspection PhpFieldImmediatelyRewrittenInspection */ |
||||
205 | $this->connected = !$this->driver_disconnect(); |
||||
206 | $this->connected = false; |
||||
207 | } |
||||
208 | |||||
209 | return !$this->connected; |
||||
210 | } |
||||
211 | |||||
212 | /** |
||||
213 | * @param int $errno |
||||
214 | * @param string $errStr |
||||
215 | * @param string $errFile |
||||
216 | * @param int $errLine |
||||
217 | * @param array $errContext |
||||
218 | * |
||||
219 | * @noinspection PhpUnusedParameterInspection |
||||
220 | */ |
||||
221 | public function handlerQueryWarning($errno, $errStr, $errFile, $errLine, $errContext) { |
||||
222 | static $alreadyHandled; |
||||
223 | |||||
224 | // Error was suppressed with the @-operator |
||||
225 | if (0 === error_reporting()) { |
||||
226 | return false; |
||||
227 | } |
||||
228 | |||||
229 | if (!$alreadyHandled) { |
||||
230 | print(SN_TIME_SQL . '<br />Server is busy. Please try again in several minutes...<br />Сервер занят. Попробуйте снова через несколько минут...<br />Server zanyat. Poprobujte snova cherez neskolko minut...'); |
||||
231 | $alreadyHandled = true; |
||||
232 | } |
||||
233 | |||||
234 | return true; |
||||
235 | } |
||||
236 | |||||
237 | public function prefixReplace($sql) { |
||||
238 | if (strpos($sql, '{{') !== false) { |
||||
239 | foreach ($this->schema()->getSnTables() as $tableName) { |
||||
240 | $sql = str_replace("{{{$tableName}}}", $this->db_prefix . $tableName, $sql); |
||||
241 | } |
||||
242 | } |
||||
243 | |||||
244 | return $sql; |
||||
245 | } |
||||
246 | |||||
247 | /** @noinspection SpellCheckingInspection */ |
||||
248 | public function doquery($query, $fetch = false, $skip_query_check = false) { |
||||
249 | /** |
||||
250 | * @var debug $debug |
||||
251 | * @var classConfig $config |
||||
252 | */ |
||||
253 | global $numqueries, $debug, $config; |
||||
254 | |||||
255 | if (!$this->connected) { |
||||
256 | $this->sn_db_connect(); |
||||
257 | } |
||||
258 | |||||
259 | $query = trim($query); |
||||
260 | $this->security_watch_user_queries($query); |
||||
261 | $skip_query_check or $this->security_query_check_bad_words($query); |
||||
262 | |||||
263 | $sql = $this->prefixReplace($query); |
||||
264 | |||||
265 | if ($config->debug) { |
||||
266 | $numqueries++; |
||||
267 | $arr = debug_backtrace(); |
||||
268 | $array = explode('/', $arr[0]['file']); |
||||
269 | $file = end($array); |
||||
270 | $line = $arr[0]['line']; |
||||
271 | $debug->add("<tr><th>Query $numqueries: </th><th>$query</th><th>$file($line)</th><th> </th><th>$fetch</th></tr>"); |
||||
272 | } |
||||
273 | |||||
274 | if (defined('DEBUG_SQL_COMMENT')) { |
||||
275 | $sql = $debug->comment_query(debug_backtrace(), $sql); |
||||
276 | } |
||||
277 | |||||
278 | set_error_handler([$this, 'handlerQueryWarning']); |
||||
279 | $sqlquery = $this->db_sql_query($sql); |
||||
280 | if (!$sqlquery) { |
||||
281 | $debug->error(SN::$db->db_error() . "\n$sql\n", 'SQL Error'); |
||||
282 | } |
||||
283 | restore_error_handler(); |
||||
284 | |||||
285 | return $fetch ? $this->db_fetch($sqlquery) : $sqlquery; |
||||
0 ignored issues
–
show
It seems like
$sqlquery can also be of type true ; however, parameter $query_result of DBAL\db_mysql::db_fetch() does only seem to accept mysqli_result , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
286 | } |
||||
287 | |||||
288 | /** |
||||
289 | * Get all records for a query as array of arrays or objects |
||||
290 | * |
||||
291 | * @param string $query SQL query to execute |
||||
292 | * @param bool $skipQueryCheck Should query skip security check |
||||
293 | * @param bool $asArray Should records be returned as array? If false - records would be returned as StdObject |
||||
294 | * |
||||
295 | * @return array |
||||
296 | */ |
||||
297 | public function dbGetAll($query, $skipQueryCheck = false, $asArray = true) { |
||||
298 | $queryResult = $this->doquery($query, false, $skipQueryCheck); |
||||
299 | |||||
300 | $result = []; |
||||
301 | while ($row = $this->db_fetch($queryResult)) { |
||||
0 ignored issues
–
show
It seems like
$queryResult can also be of type true ; however, parameter $query_result of DBAL\db_mysql::db_fetch() does only seem to accept mysqli_result , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
302 | $result[] = $asArray ? $row : (object)$row; |
||||
303 | } |
||||
304 | |||||
305 | return $result; |
||||
306 | } |
||||
307 | |||||
308 | public function doQueryAndFetch($query) { |
||||
309 | return $this->doquery($query, true); |
||||
310 | } |
||||
311 | |||||
312 | public function doQueryFast($query, $fetch = false) { |
||||
313 | $sql = $this->prefixReplace($query); |
||||
314 | |||||
315 | set_error_handler([$this, 'handlerQueryWarning']); |
||||
316 | $sqlQuery = $this->db_sql_query($sql) or SN::$debug->error(SN::$db->db_error() . "<br />$sql<br />", 'SQL Error'); |
||||
0 ignored issues
–
show
The method
error() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||
317 | restore_error_handler(); |
||||
318 | |||||
319 | return $fetch ? $this->db_fetch($sqlQuery) : $sqlQuery; |
||||
320 | } |
||||
321 | |||||
322 | /** |
||||
323 | * @param string $query |
||||
324 | * @param bool $skip_query_check |
||||
325 | * |
||||
326 | * @return DbMysqliResultIterator |
||||
327 | */ |
||||
328 | public function selectIterator($query, $skip_query_check = false) { |
||||
329 | return new DbMysqliResultIterator($this->doquery($query, false, $skip_query_check)); |
||||
330 | } |
||||
331 | |||||
332 | /** |
||||
333 | * @param string $query |
||||
334 | * @param bool $skip_query_check |
||||
335 | * |
||||
336 | * @return int|null |
||||
337 | */ |
||||
338 | public function selectValue($query, $skip_query_check = false) { |
||||
339 | $row = $this->doquery($query, true, $skip_query_check); |
||||
340 | |||||
341 | return !empty($row) ? intval(reset($row)) : null; |
||||
342 | } |
||||
343 | |||||
344 | /** |
||||
345 | * @param DbQuery $dbQuery |
||||
346 | * |
||||
347 | * @return array|null |
||||
348 | */ |
||||
349 | public function dbqSelectAndFetch(DbQuery $dbQuery) { |
||||
350 | return $this->doQueryAndFetch($dbQuery->select()); |
||||
351 | } |
||||
352 | |||||
353 | |||||
354 | public function security_watch_user_queries($query) { |
||||
355 | // TODO Заменить это на новый логгер |
||||
356 | global $config, $is_watching, $user, $debug; |
||||
357 | |||||
358 | if (!$is_watching && $config->game_watchlist_array && in_array($user['id'], $config->game_watchlist_array)) { |
||||
359 | if (!preg_match('/^(select|commit|rollback|start transaction)/i', $query)) { |
||||
360 | $is_watching = true; |
||||
361 | $msg = "\$query = \"{$query}\"\n\r"; |
||||
362 | if (!empty($_POST)) { |
||||
363 | $msg .= "\n\r" . dump($_POST, '$_POST'); |
||||
364 | } |
||||
365 | if (!empty($_GET)) { |
||||
366 | $msg .= "\n\r" . dump($_GET, '$_GET'); |
||||
367 | } |
||||
368 | $debug->warning($msg, "Watching user {$user['id']}", 399, array('base_dump' => true)); |
||||
369 | $is_watching = false; |
||||
370 | } |
||||
371 | } |
||||
372 | } |
||||
373 | |||||
374 | |||||
375 | /** @noinspection SpellCheckingInspection */ |
||||
376 | public function security_query_check_bad_words($query) { |
||||
377 | global $user, $dm_change_legit, $mm_change_legit; |
||||
378 | |||||
379 | switch (true) { |
||||
380 | case stripos($query, 'RUNCATE TABL') != false: |
||||
0 ignored issues
–
show
|
|||||
381 | case stripos($query, 'ROP TABL') != false: |
||||
0 ignored issues
–
show
|
|||||
382 | case stripos($query, 'ENAME TABL') != false: |
||||
0 ignored issues
–
show
|
|||||
383 | case stripos($query, 'REATE DATABAS') != false: |
||||
0 ignored issues
–
show
|
|||||
384 | case stripos($query, 'REATE TABL') != false: |
||||
0 ignored issues
–
show
|
|||||
385 | case stripos($query, 'ET PASSWOR') != false: |
||||
0 ignored issues
–
show
|
|||||
386 | case stripos($query, 'EOAD DAT') != false: |
||||
0 ignored issues
–
show
|
|||||
387 | case stripos($query, 'RPG_POINTS') != false && stripos(trim($query), 'UPDATE ') === 0 && !$dm_change_legit: |
||||
0 ignored issues
–
show
|
|||||
388 | case stripos($query, 'METAMATTER') != false && stripos(trim($query), 'UPDATE ') === 0 && !$mm_change_legit: |
||||
0 ignored issues
–
show
|
|||||
389 | case stripos($query, 'AUTHLEVEL') != false && $user['authlevel'] < 3 && stripos($query, 'SELECT') !== 0: |
||||
0 ignored issues
–
show
|
|||||
390 | $report = "Hacking attempt (" . date("d.m.Y H:i:s") . " - [" . time() . "]):\n"; |
||||
391 | $report .= ">Database Inforamation\n"; |
||||
392 | $report .= "\tID - " . $user['id'] . "\n"; |
||||
393 | $report .= "\tUser - " . $user['username'] . "\n"; |
||||
394 | $report .= "\tAuth level - " . $user['authlevel'] . "\n"; |
||||
395 | $report .= "\tAdmin Notes - " . $user['adminNotes'] . "\n"; |
||||
396 | $report .= "\tCurrent Planet - " . $user['current_planet'] . "\n"; |
||||
397 | $report .= "\tUser IP - " . $user['user_lastip'] . "\n"; |
||||
398 | $report .= "\tUser IP at Reg - " . $user['ip_at_reg'] . "\n"; |
||||
399 | $report .= "\tUser Agent- " . $_SERVER['HTTP_USER_AGENT'] . "\n"; |
||||
400 | $report .= "\tCurrent Page - " . $user['current_page'] . "\n"; |
||||
401 | $report .= "\tRegister Time - " . $user['register_time'] . "\n"; |
||||
402 | $report .= "\n"; |
||||
403 | |||||
404 | $report .= ">Query Information\n"; |
||||
405 | $report .= "\tQuery - " . $query . "\n"; |
||||
406 | $report .= "\n"; |
||||
407 | |||||
408 | $report .= ">\$_SERVER Information\n"; |
||||
409 | $report .= "\tIP - " . $_SERVER['REMOTE_ADDR'] . "\n"; |
||||
410 | $report .= "\tHost Name - " . $_SERVER['HTTP_HOST'] . "\n"; |
||||
411 | $report .= "\tUser Agent - " . $_SERVER['HTTP_USER_AGENT'] . "\n"; |
||||
412 | $report .= "\tRequest Method - " . $_SERVER['REQUEST_METHOD'] . "\n"; |
||||
413 | $report .= "\tCame From - " . $_SERVER['HTTP_REFERER'] . "\n"; |
||||
414 | $report .= "\tPage is - " . $_SERVER['SCRIPT_NAME'] . "\n"; |
||||
415 | $report .= "\tUses Port - " . $_SERVER['REMOTE_PORT'] . "\n"; |
||||
416 | $report .= "\tServer Protocol - " . $_SERVER['SERVER_PROTOCOL'] . "\n"; |
||||
417 | |||||
418 | $report .= "\n--------------------------------------------------------------------------------------------------\n"; |
||||
419 | |||||
420 | $fp = fopen(SN_ROOT_PHYSICAL . 'badqrys.txt', 'a'); |
||||
421 | fwrite($fp, $report); |
||||
422 | fclose($fp); |
||||
423 | |||||
424 | $message = 'Привет, я не знаю то, что Вы пробовали сделать, но команда, которую Вы только послали базе данных, не выглядела очень дружественной и она была заблокированна.<br /><br />Ваш IP, и другие данные переданны администрации сервера. Удачи!.'; |
||||
425 | die($message); |
||||
0 ignored issues
–
show
|
|||||
426 | /** @noinspection PhpUnreachableStatementInspection */ |
||||
427 | break; |
||||
428 | } |
||||
429 | } |
||||
430 | |||||
431 | public function mysql_get_table_list() { |
||||
432 | return $this->db_sql_query('SHOW TABLES;'); |
||||
433 | } |
||||
434 | |||||
435 | public function mysql_get_innodb_status() { |
||||
436 | return $this->db_sql_query('SHOW ENGINE INNODB STATUS;'); |
||||
437 | } |
||||
438 | |||||
439 | /** |
||||
440 | * @param string $tableName_unsafe |
||||
441 | * |
||||
442 | * @return array[] |
||||
443 | */ |
||||
444 | public function mysql_get_fields($tableName_unsafe) { |
||||
445 | $result = []; |
||||
446 | |||||
447 | $prefixedTableName_safe = $this->db_escape($this->db_prefix . $tableName_unsafe); |
||||
448 | $q1 = $this->db_sql_query("SHOW FULL COLUMNS FROM `{$prefixedTableName_safe}`;"); |
||||
449 | while ($r1 = db_fetch($q1)) { |
||||
0 ignored issues
–
show
The function
db_fetch() has been deprecated.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
450 | $dbf = new DbFieldDescription(); |
||||
451 | $dbf->fromMySqlDescription($r1); |
||||
452 | |||||
453 | $result[$r1['Field']] = $dbf; |
||||
454 | } |
||||
455 | |||||
456 | return $result; |
||||
457 | } |
||||
458 | |||||
459 | /** |
||||
460 | * @param string $tableName_unsafe |
||||
461 | * |
||||
462 | * @return DbIndexDescription[] |
||||
463 | */ |
||||
464 | public function mysql_get_indexes($tableName_unsafe) { |
||||
465 | /** |
||||
466 | * @var DbIndexDescription[] $result |
||||
467 | */ |
||||
468 | $result = []; |
||||
469 | |||||
470 | $prefixedTableName_safe = $this->db_escape($this->db_prefix . $tableName_unsafe); |
||||
471 | $q1 = $this->db_sql_query("SHOW INDEX FROM {$prefixedTableName_safe};"); |
||||
472 | while ($r1 = db_fetch($q1)) { |
||||
0 ignored issues
–
show
The function
db_fetch() has been deprecated.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
473 | $indexName = $r1['Key_name']; |
||||
474 | if (empty($result[$indexName])) { |
||||
475 | $result[$indexName] = new DbIndexDescription(); |
||||
476 | } |
||||
477 | $result[$indexName]->addField($r1); |
||||
478 | } |
||||
479 | |||||
480 | return $result; |
||||
481 | } |
||||
482 | |||||
483 | /** |
||||
484 | * @param string $tableName_unsafe |
||||
485 | * |
||||
486 | * @return array[] |
||||
487 | */ |
||||
488 | public function mysql_get_constraints($tableName_unsafe) { |
||||
489 | $result = []; |
||||
490 | |||||
491 | $prefixedTableName_safe = $this->db_escape($this->db_prefix . $tableName_unsafe); |
||||
492 | |||||
493 | $q1 = $this->db_sql_query("SELECT * FROM `information_schema`.`KEY_COLUMN_USAGE` WHERE `TABLE_SCHEMA` = '" . SN::$db->db_escape(SN::$db_name) . "' AND `TABLE_NAME` = '{$prefixedTableName_safe}' AND `REFERENCED_TABLE_NAME` IS NOT NULL;"); |
||||
494 | while ($r1 = db_fetch($q1)) { |
||||
0 ignored issues
–
show
The function
db_fetch() has been deprecated.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
495 | $indexName = $r1['CONSTRAINT_NAME']; |
||||
496 | |||||
497 | $table_referenced = str_replace($this->db_prefix, '', $r1['REFERENCED_TABLE_NAME']); |
||||
498 | |||||
499 | $result[$indexName]['name'] = $indexName; |
||||
500 | $result[$indexName]['signature'][] = "{$r1['COLUMN_NAME']}=>{$table_referenced}.{$r1['REFERENCED_COLUMN_NAME']}"; |
||||
501 | $r1['REFERENCED_TABLE_NAME'] = $table_referenced; |
||||
502 | $r1['TABLE_NAME'] = $tableName_unsafe; |
||||
503 | $result[$indexName]['fields'][$r1['COLUMN_NAME']] = $r1; |
||||
504 | } |
||||
505 | |||||
506 | foreach ($result as &$constraint) { |
||||
507 | $constraint['signature'] = implode(',', $constraint['signature']); |
||||
508 | } |
||||
509 | |||||
510 | return $result; |
||||
511 | } |
||||
512 | |||||
513 | |||||
514 | /** |
||||
515 | * @param $query_string |
||||
516 | * |
||||
517 | * @return bool|mysqli_result |
||||
518 | */ |
||||
519 | public function db_sql_query($query_string) { |
||||
520 | $mt = microtime(true); |
||||
521 | |||||
522 | $result = $this->link->query($query_string); |
||||
523 | |||||
524 | $this->time_mysql_total += microtime(true) - $mt; |
||||
525 | |||||
526 | return $result; |
||||
527 | // return $this->driver->mysql_query($query_string); |
||||
528 | } |
||||
529 | |||||
530 | /** |
||||
531 | * @param mysqli_result $query_result |
||||
532 | * |
||||
533 | * @return array|null |
||||
534 | */ |
||||
535 | public function db_fetch($query_result) { |
||||
536 | $mt = microtime(true); |
||||
537 | |||||
538 | $result = mysqli_fetch_assoc($query_result); |
||||
539 | |||||
540 | $this->time_mysql_total += microtime(true) - $mt; |
||||
541 | |||||
542 | return $result; |
||||
543 | } |
||||
544 | |||||
545 | // public function db_fetch_row(&$query) { |
||||
546 | // return mysqli_fetch_row($query); |
||||
547 | // } |
||||
548 | |||||
549 | public function db_escape($unescaped_string) { |
||||
550 | return mysqli_real_escape_string($this->link, $unescaped_string); |
||||
551 | } |
||||
552 | |||||
553 | public function driver_disconnect() { |
||||
554 | if (is_object($this->link)) { |
||||
555 | $this->link->close(); |
||||
556 | $this->connected = false; |
||||
557 | unset($this->link); |
||||
558 | } |
||||
559 | |||||
560 | return true; |
||||
561 | } |
||||
562 | |||||
563 | public function db_error() { |
||||
564 | return mysqli_error($this->link); |
||||
565 | } |
||||
566 | |||||
567 | /** |
||||
568 | * @return int|string |
||||
569 | */ |
||||
570 | public function db_insert_id() { |
||||
571 | return mysqli_insert_id($this->link); |
||||
572 | } |
||||
573 | |||||
574 | public function db_num_rows($result) { |
||||
575 | return mysqli_num_rows($result); |
||||
576 | } |
||||
577 | |||||
578 | public function db_affected_rows() { |
||||
579 | return mysqli_affected_rows($this->link); |
||||
580 | } |
||||
581 | |||||
582 | public function getClientInfo() { |
||||
583 | return mysqli_get_client_info($this->link); |
||||
584 | } |
||||
585 | |||||
586 | public function getServerInfo() { |
||||
587 | return mysqli_get_server_info($this->link); |
||||
588 | } |
||||
589 | |||||
590 | public function getHostInfo() { |
||||
591 | return mysqli_get_host_info($this->link); |
||||
592 | } |
||||
593 | |||||
594 | public function getServerStat() { |
||||
595 | return mysqli_stat($this->link); |
||||
596 | } |
||||
597 | |||||
598 | /** |
||||
599 | * @param array $dbSettings |
||||
600 | */ |
||||
601 | public function setDbSettings($dbSettings) { |
||||
602 | $this->dbsettings = $dbSettings; |
||||
603 | $this->dbName = $this->dbsettings['name']; |
||||
604 | |||||
605 | return $this; |
||||
606 | } |
||||
607 | |||||
608 | public function getDbSettings() { |
||||
609 | return $this->dbsettings; |
||||
610 | } |
||||
611 | |||||
612 | /** |
||||
613 | * @param $level |
||||
614 | * |
||||
615 | * @return void |
||||
616 | */ |
||||
617 | public function transactionStart($level = '') { |
||||
618 | $this->inTransaction = true; |
||||
619 | |||||
620 | if ($level && in_array($level, self::TRANSACTION_LEVELS_ALLOWED)) { |
||||
621 | $this->db_sql_query("SET TRANSACTION ISOLATION LEVEL {$level};"); |
||||
622 | } |
||||
623 | |||||
624 | $this->doquery('START TRANSACTION; /* transaction start */', false); |
||||
625 | } |
||||
626 | |||||
627 | /** |
||||
628 | * @return void |
||||
629 | */ |
||||
630 | public function transactionCommit() { |
||||
631 | $this->doquery('COMMIT; /* transaction commit */'); |
||||
632 | $this->inTransaction = false; |
||||
633 | } |
||||
634 | |||||
635 | /** |
||||
636 | * @return void |
||||
637 | */ |
||||
638 | public function transactionRollback() { |
||||
639 | $this->doquery('ROLLBACK; /* transaction rollback */'); |
||||
640 | $this->inTransaction = false; |
||||
641 | } |
||||
642 | |||||
643 | /** |
||||
644 | * Check if transaction started |
||||
645 | * |
||||
646 | * @return bool |
||||
647 | */ |
||||
648 | public function transactionCheck() { |
||||
649 | return $this->inTransaction; |
||||
650 | } |
||||
651 | |||||
652 | /** |
||||
653 | * Wrap callback in transaction |
||||
654 | * |
||||
655 | * @param callable $callback |
||||
656 | * @param string $level |
||||
657 | * |
||||
658 | * @return mixed |
||||
659 | */ |
||||
660 | public function transactionWrap($callback, $level = '') { |
||||
661 | $this->transactionStart($level); |
||||
662 | $result = $callback(); |
||||
663 | $this->transactionCommit(); |
||||
664 | |||||
665 | return $result; |
||||
666 | } |
||||
667 | |||||
668 | public static function db_transaction_start($level = '') { |
||||
669 | self::db_transaction_check(db_mysql::DB_TRANSACTION_SHOULD_NOT_BE); |
||||
670 | |||||
671 | SN::$gc->db->transactionStart($level); |
||||
672 | |||||
673 | self::$transaction_id++; |
||||
674 | |||||
675 | if (SN::$config->db_manual_lock_enabled) { |
||||
676 | SN::$config->db_loadItem('var_db_manually_locked'); |
||||
677 | SN::$config->db_saveItem('var_db_manually_locked', SN_TIME_SQL); |
||||
678 | } |
||||
679 | |||||
680 | self::$db_in_transaction = true; |
||||
681 | self::$transactionDepth++; |
||||
682 | DBStaticUnit::cache_clear(); |
||||
683 | |||||
684 | return self::$transaction_id; |
||||
685 | } |
||||
686 | |||||
687 | public static function db_transaction_rollback() { |
||||
688 | // static::db_transaction_check(true); // TODO - вообще-то тут тоже надо проверять есть ли транзакция |
||||
689 | DBStaticUnit::cache_clear(); |
||||
690 | |||||
691 | SN::$gc->db->transactionRollback(); |
||||
692 | |||||
693 | self::$db_in_transaction = false; |
||||
694 | self::$transactionDepth--; |
||||
695 | |||||
696 | return self::$transaction_id; |
||||
697 | } |
||||
698 | |||||
699 | /** |
||||
700 | * Блокирует указанные таблицу/список таблиц |
||||
701 | * |
||||
702 | * @param string|array $tables Таблица/список таблиц для блокировки. Названия таблиц - без префиксов |
||||
703 | * <p>string - название таблицы для блокировки</p> |
||||
704 | * <p>array - массив, где ключ - имя таблицы, а значение - условия блокировки элементов</p> |
||||
705 | */ |
||||
706 | public static function db_lock_tables($tables) { |
||||
707 | $tables = is_array($tables) ? $tables : array($tables => ''); |
||||
708 | foreach ($tables as $table_name => $condition) { |
||||
709 | SN::$db->doquery( |
||||
710 | "SELECT 1 FROM {{{$table_name}}}" . ($condition ? ' WHERE ' . $condition : '') |
||||
711 | ); |
||||
712 | } |
||||
713 | } |
||||
714 | |||||
715 | public static function db_transaction_commit() { |
||||
716 | self::db_transaction_check(db_mysql::DB_TRANSACTION_SHOULD_BE); |
||||
717 | |||||
718 | DBStaticUnit::cache_clear(); |
||||
719 | SN::$gc->db->transactionCommit(); |
||||
720 | |||||
721 | self::$db_in_transaction = false; |
||||
722 | self::$transactionDepth--; |
||||
723 | |||||
724 | return self::$transaction_id++; |
||||
725 | } |
||||
726 | |||||
727 | /** |
||||
728 | * Эта функция проверяет статус транзакции |
||||
729 | * |
||||
730 | * Это - низкоуровневая функция. В нормальном состоянии движка её сообщения никогда не будут видны |
||||
731 | * |
||||
732 | * @param null|true|false $status Должна ли быть запущена транзакция в момент проверки |
||||
733 | * <p>null - транзакция НЕ должна быть запущена</p> |
||||
734 | * <p>true - транзакция должна быть запущена - для совместимости с $for_update</p> |
||||
735 | * <p>false - всё равно - для совместимости с $for_update</p> |
||||
736 | * |
||||
737 | * @return bool Текущий статус транзакции |
||||
738 | */ |
||||
739 | public static function db_transaction_check($status = self::DB_TRANSACTION_WHATEVER) { |
||||
740 | $error_msg = false; |
||||
741 | if ($status && !self::$db_in_transaction) { |
||||
742 | $error_msg = 'No transaction started for current operation'; |
||||
743 | } elseif ($status === null && self::$db_in_transaction) { |
||||
744 | $error_msg = 'Transaction is already started'; |
||||
745 | } |
||||
746 | |||||
747 | if ($error_msg) { |
||||
748 | // TODO - Убрать позже |
||||
749 | print('<h1>СООБЩИТЕ ЭТО АДМИНУ: sn_db_transaction_check() - ' . $error_msg . '</h1>'); |
||||
750 | $backtrace = debug_backtrace(); |
||||
751 | array_shift($backtrace); |
||||
752 | pdump($backtrace); |
||||
753 | die($error_msg); |
||||
0 ignored issues
–
show
|
|||||
754 | } |
||||
755 | |||||
756 | return self::$db_in_transaction; |
||||
757 | } |
||||
758 | |||||
759 | /** |
||||
760 | * Lock specified records in specified tables |
||||
761 | * |
||||
762 | * @param array[] $locks ['$tableName' => [$idToLock, ...],], |
||||
763 | * |
||||
764 | * @return array|bool|mysqli_result|null |
||||
765 | */ |
||||
766 | public function lockRecords($locks) { |
||||
767 | // Reordering lock order for known tables |
||||
768 | $newLocks = []; |
||||
769 | foreach (self::TABLE_LOCK_ORDER as $tableName) { |
||||
770 | if (array_key_exists($tableName, $locks)) { |
||||
771 | $newLocks[$tableName] = $locks[$tableName]; |
||||
772 | unset($locks[$tableName]); |
||||
773 | } |
||||
774 | } |
||||
775 | // Adding tables left - their lock order is not important |
||||
776 | $newLocks += $locks; |
||||
777 | |||||
778 | // Compiling SQL queries to lock records |
||||
779 | $queries = []; |
||||
780 | foreach ($newLocks as $tableName => $lockedIds) { |
||||
781 | if (!empty($lockedIds)) { |
||||
782 | // Detecting primary key name |
||||
783 | $idFieldName = !empty(static::TABLE_ID_FIELD[$tableName]) ? static::TABLE_ID_FIELD[$tableName] : 'id'; |
||||
784 | // Making ID mysql-safe |
||||
785 | array_walk($lockedIds, function (&$id) { $id = idval($id); }); |
||||
786 | /** @noinspection SqlResolve */ |
||||
787 | $queries[] = "(SELECT 1 FROM `{{{$tableName}}}` WHERE `{$idFieldName}` IN (" . implode(',', $lockedIds) . ") FOR UPDATE)"; |
||||
788 | } |
||||
789 | } |
||||
790 | |||||
791 | // return !empty($query) ? doquery(implode(' UNION ', $query)) : false; |
||||
792 | foreach ($queries as $query) { |
||||
793 | doquery($query); |
||||
0 ignored issues
–
show
The function
doquery() has been deprecated.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
794 | } |
||||
795 | |||||
796 | return true; |
||||
797 | } |
||||
798 | |||||
799 | } |
||||
800 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.