Test Failed
Push — trunk ( 132e87...b3f953 )
by SuperNova.WS
11:54
created

Updater::isIndexExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */
2
3
/**
4
 * Created by Gorlum 01.03.2019 6:03
5
 */
6
7
namespace Core;
8
9
use classConfig;
10
use DBAL\db_mysql;
11
use DBAL\DbFieldDescription;
12
use DBAL\DbIndexDescription;
13
use mysqli_result;
14
use SN;
15
16
class Updater {
17
18
  protected $update_tables = [];
19
20
  /**
21
   * @var classConfig $config
22
   */
23
  protected $config;
24
  /**
25
   * @var db_mysql $db
26
   */
27
  protected $db;
28
29
  protected $upd_log = '';
30
31
  public $new_version = 0;
32
  protected $old_server_status;
33
34
  // Does updater terminate successfully
35
  public $successTermination = false;
36
37
  public function __construct() {
38
    $this->config = SN::$gc->config;
39
    $this->db     = SN::$gc->db;
40
41
    $this->new_version = floatval($this->config->db_version);
42
43
    // Closing any transaction that can be opened to this moment
44
    $this->upd_do_query('ROLLBACK;', true);
45
46
    $this->config->reset();
47
    $this->config->db_loadAll();
48
49
    $this->config->db_prefix    = $this->db->db_prefix; // Left for compatibility reasons. Deprecated
50
    $this->config->cache_prefix = SN::$cache_prefix;
51
52
    $this->config->debug = 0;
53
54
    $this->checkVersionSupport();
55
56
    ini_set('memory_limit', '1G');
57
    set_time_limit($this->config->upd_lock_time + 10);
58
    $this->upd_do_query('SET FOREIGN_KEY_CHECKS=0;', true);
59
60
    $this->upd_log_message('Update started. Disabling server');
61
    $this->old_server_status = $this->config->pass()->game_disable;
62
    $this->config->db_saveItem('game_disable', GAME_DISABLE_UPDATE);
63
    $this->upd_log_message('Server disabled.');
64
65
    $this->upd_log_message('Now looking DB for upgrades...');
66
  }
67
68
  public function __destruct() {
69
    if ($this->successTermination) {
70
      $this->upd_log_message('Upgrade complete.');
71
    } else {
72
      $this->upd_log_message('Upgrade was aborted! DB can be in invalid state!');
73
    }
74
75
    $this->upd_do_query('SET FOREIGN_KEY_CHECKS=1;', true);
76
77
    SN::$cache->unset_by_prefix('lng_');
78
79
    if ($this->new_version) {
80
      $this->config->pass()->db_version = $this->new_version;
81
      $this->upd_log_message("<span style='color: green;'>DB version is now {$this->new_version}</span>");
82
    } else {
83
      $this->upd_log_message("DB version didn't changed from {$this->config->db_version}");
84
    }
85
86
    $this->config->db_loadAll();
87
    /*
88
    if($user['authlevel'] >= 3) {
89
      print(str_replace("\r\n", '<br>', $upd_log));
90
    }
91
    */
92
    $this->db->schema()->clear();
93
94
    $this->upd_log_message('Restoring server status');
95
    $this->config->pass()->game_disable = $this->old_server_status;
96
  }
97
98
  public function upd_log_message($message) {
99
    global $sys_log_disabled, $debug;
100
101
    if (!$sys_log_disabled) {
102
      $this->upd_log .= "{$message}\r\n";
103
      $debug->warning($message, 'Database Update', 103);
104
    }
105
  }
106
107
  public function upd_log_version_update() {
108
    $this->upd_add_more_time();
109
    $this->upd_log_message("Detected outdated version {$this->new_version}. Upgrading...");
110
  }
111
112
  public function upd_check_key($key, $default_value, $condition = false) {
113
    global $sys_log_disabled;
114
115
    $this->config->pass()->$key;
116
    if ($condition || !isset($this->config->$key)) {
117
      $this->upd_add_more_time();
118
      if (!$sys_log_disabled) {
119
        $this->upd_log_message("Updating config key '{$key}' with value '{$default_value}'");
120
      }
121
      $this->config->pass()->$key = $default_value;
122
    } else {
123
      $this->config->pass()->$key = null;
124
    }
125
  }
126
127
128
  /**
129
   * @param string       $table_name
130
   * @param string|array $declaration
131
   * @param string       $tableOptions
132
   *
133
   * @return bool|mysqli_result
134
   */
135
  public function upd_create_table($table_name, $declaration, $tableOptions = '') {
136
    $result = null;
137
138
    if (!$this->isTableExists($table_name)) {
139
      if (is_array($declaration)) {
140
        $declaration = implode(',', $declaration);
141
      }
142
      $declaration = trim($declaration);
143
      if (substr($declaration, 0, 1) != '(') {
144
        $declaration = "($declaration)";
145
      }
146
      $tableOptions = trim($tableOptions);
147
      if (!empty($tableOptions)) {
148
        $declaration .= $tableOptions;
149
      }
150
      $result = $this->upd_do_query("CREATE TABLE IF NOT EXISTS `{$this->config->db_prefix}{$table_name}` {$declaration}");
151
      $error  = SN::$db->db_error();
152
      if ($error) {
153
        die("Creating error for table `{$table_name}`: {$error}<br />" . dump($declaration));
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
154
      }
155
156
      $this->db->schema()->clear();
157
    }
158
159
    return $result;
160
  }
161
162
  /**
163
   * @param string          $table
164
   * @param string|string[] $alters
165
   * @param bool            $condition
166
   *
167
   * @return bool|mysqli_result|null
168
   */
169
  public function upd_alter_table($table, $alters, $condition = true) {
170
    if (!$condition) {
171
      return null;
172
    }
173
174
    $this->upd_add_more_time();
175
    $alters_print = is_array($alters) ? dump($alters) : $alters;
176
    $this->upd_log_message("Altering table '{$table}' with alterations {$alters_print}");
177
178
    if (!is_array($alters)) {
179
      $alters = array($alters);
180
    }
181
182
    $alters = implode(',', $alters);
183
    // $alters = preg_replace('/({{(.+)}})/U', 'lvh1_$2', $alters);
184
    $qry = "ALTER TABLE {$this->config->db_prefix}{$table} {$alters};";
185
186
    $result = $this->upd_do_query($qry);
187
    $error  = SN::$db->db_error();
188
    if ($error) {
189
      die("Altering error for table `{$table}`: {$error}<br />{$alters_print}");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
190
    }
191
192
    $this->db->schema()->clear();
193
194
    return $result;
195
  }
196
197
  public function upd_drop_table($table_name) {
198
    $this->db->db_sql_query("DROP TABLE IF EXISTS {$this->config->db_prefix}{$table_name};");
199
    $this->db->schema()->clear();
200
  }
201
202
  /**
203
   * @param $table
204
   * @param $index
205
   *
206
   * @return bool|mysqli_result|null
207
   */
208
  public function indexDropIfExists($table, $index) {
209
    return $this->upd_alter_table($table, ["DROP KEY `{$index}`",], $this->isIndexExists($table, $index));
210
  }
211
212
  /**
213
   * @param $table
214
   * @param $constraint
215
   *
216
   * @return bool|mysqli_result|null
217
   */
218
  public function constraintDropIfExists($table, $constraint) {
219
    return $this->upd_alter_table($table, ["DROP FOREIGN KEY `{$constraint}`",], $this->isConstrainExists($table, $constraint));
220
  }
221
222
  /**
223
   * Replace index in table
224
   *
225
   * @param string    $table  Table name
226
   * @param string    $index  Index name
227
   * @param string[]  $fields Field names to make index on
228
   * @param ?callable $dropForeign
229
   * @param ?callable $createForeign
230
   *
231
   * @return void
232
   */
233
  public function indexReplace($table, $index, $fields, $dropForeign = null, $createForeign = null) {
234
    $this->transactionStart();
235
    $this->upd_do_query('SET FOREIGN_KEY_CHECKS=0;', true);
236
237
    if (is_callable($dropForeign)) {
238
      $dropForeign();
239
    }
240
241
    $this->indexDropIfExists($table, $index);
242
    $queryFields = implode(',', array_map(function ($value) { return "`{$value}`"; }, $fields));
243
    if ($queryFields) {
244
      $this->upd_alter_table(
245
        $table,
246
        ["ADD KEY `{$index}` (" . $queryFields . ")"],
247
        !$this->isIndexExists($table, $index)
248
      );
249
    }
250
251
    if (is_callable($createForeign)) {
252
      $createForeign();
253
    }
254
255
    $this->upd_do_query('SET FOREIGN_KEY_CHECKS=1;', true);
256
    $this->transactionCommit();
257
  }
258
259
  public function upd_add_more_time($time = 0) {
260
    global $sys_log_disabled;
261
262
    $time = $time ?: $this->config->upd_lock_time;
263
    if (!$sys_log_disabled) {
264
      $this->config->pass()->var_db_update_end = SN_TIME_NOW + $time;
265
    }
266
    set_time_limit($time);
267
  }
268
269
  /**
270
   * @param int $id
271
   *
272
   * @return bool
273
   */
274
  public function updPatchExists($id) {
275
    $q = $this->upd_do_query("SELECT 1 FROM `{{server_patches}}` WHERE `id` = " . intval($id), true);
276
277
    return !empty(db_fetch($q));
0 ignored issues
show
Deprecated Code introduced by
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 ignore-deprecated  annotation

277
    return !empty(/** @scrutinizer ignore-deprecated */ db_fetch($q));
Loading history...
278
  }
279
280
  /**
281
   * @param int $id
282
   */
283
  public function updPatchRegister($id) {
284
    $this->upd_do_query("INSERT INTO `{{server_patches}}` SET `id` = " . intval($id));
285
  }
286
287
  /**
288
   * @param int      $patchId
289
   * @param callable $callable
290
   * @param bool     $preCheck - [PATCH_REGISTER|PATCH_PRE_CHECK]
291
   */
292
  public function updPatchApply($patchId, $callable, $preCheck = PATCH_REGISTER) {
293
    if (!$this->updPatchExists($patchId)) {
294
      $callable();
295
296
      if ($preCheck == PATCH_REGISTER) {
297
        $this->updPatchRegister($patchId);
298
      }
299
    }
300
  }
301
302
  /**
303
   * @param string $query
304
   * @param bool   $no_log
305
   *
306
   * @return bool|mysqli_result
307
   */
308
  public function upd_do_query($query, $no_log = false) {
309
    $this->upd_add_more_time();
310
311
    if (!$no_log) {
312
      $this->upd_log_message("Performing query '{$query}'");
313
    }
314
315
    if (strpos($query, '{{') !== false) {
316
      foreach ($this->db->schema()->getSnTables() as $tableName => $cork) {
317
        $query = str_replace("{{{$tableName}}}", $this->db->db_prefix . $tableName, $query);
318
      }
319
    }
320
    $result = $this->db->db_sql_query($query) or die('Query error for ' . $query . ': ' . SN::$db->db_error());
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
321
322
    return $result;
323
  }
324
325
  protected function checkVersionSupport() {
326
    if ($this->config->db_version < DB_VERSION_MIN) {
327
//  print("This version does not supports upgrades from SN below v{$minVersion}. Please, use SN v42 to upgrade old database.<br />
328
//Эта версия игры не поддерживает обновление движка версий ниже v{$minVersion}. Пожалуйста, используйте SN v42 для апгрейда со старых версий игры.<br />");
329
      die(
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
330
        'Internal error! Updater detects DB version LESSER then can be handled!<br />
331
    Possible you have VERY out-of-date SuperNova version<br />
332
    Use first SuperNova version not greater then  ' . DB_VERSION_MIN . ' to make preliminary upgrade and then use newest version again<br />
333
    List of available releases <a href="https://github.com/supernova-ws/SuperNova/releases">GIT repository</a>'
334
      );
335
    } elseif ($this->config->db_version > DB_VERSION) {
336
      $this->config->pass()->var_db_update_end = SN_TIME_NOW;
337
      die(
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
338
      'Internal error! Updater detects DB version greater then can be handled!<br />
339
    Possible you have out-of-date SuperNova version<br />
340
    Please upgrade your server from <a href="https://github.com/supernova-ws/SuperNova">GIT repository</a>'
341
      );
342
    }
343
  }
344
345
  /**
346
   * @param $table
347
   * @param $field
348
   *
349
   * @return DbFieldDescription
350
   */
351
  public function getFieldDescription($table, $field) {
352
    return $this->db->schema()->getTableSchema($table)->fields[$field];
353
  }
354
355
  /**
356
   * @param $table
357
   * @param $index
358
   *
359
   * @return DbIndexDescription
360
   */
361
  public function getIndexDescription($table, $index) {
362
    return $this->db->schema()->getTableSchema($table)->indexes[$index];
363
  }
364
365
  public function isTableExists($table) {
366
    return $this->db->schema()->isSnTableExists($table);
367
  }
368
369
  public function isFieldExists($table, $field) {
370
    return $this->db->schema()->isFieldExists($table, $field);
371
  }
372
373
  public function isIndexExists($table, $index) {
374
    return $this->db->schema()->isIndexExists($table, $index);
375
  }
376
377
  public function isConstrainExists($table, $index) {
378
    return $this->db->schema()->isConstrainExists($table, $index);
379
  }
380
381
  public function transactionStart() {
382
//    $this->upd_do_query('START TRANSACTION;', true);
383
    $this->db->transactionStart();
384
  }
385
386
  public function transactionCommit() {
387
//    $this->upd_do_query('COMMIT;', true);
388
    $this->db->transactionCommit();
389
  }
390
391
}
392