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