ILIAS  release_8 Revision v8.24
class.ilDBUpdate.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
28{
29 public string $DB_UPDATE_FILE;
30 public ?int $currentVersion = null;
31 public ?int $fileVersion = null;
32 public string $updateMsg;
33 protected ?ilIniFile $client_ini = null;
35 protected ?int $custom_updates_file_version = null;
36 protected ?bool $custom_updates_info_read = null;
37 protected string $error;
38 protected string $PATH = './';
39 protected ilDBInterface $db;
40 protected string $current_file;
41 protected string $LAST_UPDATE_FILE;
42 protected array $filecontent;
43 protected array $lastfilecontent;
44 protected int $db_update_running;
47 protected array $hotfix_version;
48 protected array $hotfix_content;
49 protected int $hotfix_file_version;
51 protected array $custom_updates_content;
52 protected Iterator $ctrl_structure_iterator;
53
54 public function __construct(ilDBInterface $a_db_handler, ilIniFile $client_ini = null)
55 {
56 // workaround to allow setup migration
57 $this->client_ini = $client_ini;
58 $this->db = $a_db_handler;
59 $this->PATH = "./";
60
61 $this->getCurrentVersion();
62
63 // get update file for current version
64 $updatefile = $this->getFileForStep($this->currentVersion + 1);
65
66 $this->current_file = $updatefile;
67 $this->DB_UPDATE_FILE = $this->PATH . "setup/sql/" . $updatefile;
68
69 //
70 // NOTE: IF YOU SET THIS TO THE NEWEST FILE, CHANGE ALSO getFileForStep()
71 //
72 $this->LAST_UPDATE_FILE = $this->PATH . "setup/sql/dbupdate_05.php";
73
74 $this->readDBUpdateFile();
75 $this->readLastUpdateFile();
76 $this->readFileVersion();
77
78 $class_map = require ILIAS_ABSOLUTE_PATH . '/libs/composer/vendor/composer/autoload_classmap.php';
79 $this->ctrl_structure_iterator = new ilCtrlArrayIterator($class_map);
80 }
81
85 public function getFileForStep(int $a_version): string
86 {
87 //
88 // NOTE: IF YOU ADD A NEW FILE HERE, CHANGE ALSO THE CONSTRUCTOR
89 //
90 switch (true) {
91 case ($a_version > 5431): // last number in previous file
92 return "dbupdate_05.php";
93 case ($a_version > 4182): // last number in previous file
94 return "dbupdate_04.php";
95 case ($a_version > 2948): // last number in previous file
96 return "dbupdate_03.php";
97 case ($a_version > 864): // last number in previous file
98 return "dbupdate_02.php";
99 default:
100 return "dbupdate.php";
101 }
102 }
103
104 public function initStep(int $i): void
105 {
106 //
107 }
108
109 public function readDBUpdateFile(): bool
110 {
111 if (!file_exists($this->DB_UPDATE_FILE)) {
112 $this->error = "no_db_update_file";
113 $this->filecontent = array();
114
115 return false;
116 }
117
118 $this->filecontent = @file($this->DB_UPDATE_FILE);
119
120 return true;
121 }
122
123 public function readLastUpdateFile(): bool
124 {
125 if (!file_exists($this->LAST_UPDATE_FILE)) {
126 $this->error = "no_last_update_file";
127 $this->lastfilecontent = array();
128
129 return false;
130 }
131
132 $this->lastfilecontent = @file($this->LAST_UPDATE_FILE);
133
134 return true;
135 }
136
137 public function getCurrentVersion(): int
138 {
139 $set = new ilSetting("common", true);
140 $this->currentVersion = (int) $set->get("db_version");
141
143 }
144
145 public function setCurrentVersion(int $a_version): void
146 {
147 $set = new ilSetting("common", true);
148 $set->set("db_version", (string) $a_version);
149 $this->currentVersion = $a_version;
150 }
151
156 public function setRunningStatus(int $a_nr): void
157 {
158 $set = new ilSetting("common", true);
159 $set->set("db_update_running", (string) $a_nr);
160 $this->db_update_running = $a_nr;
161 }
162
167 public function getRunningStatus(): int
168 {
169 $set = new ilSetting("common", true);
170 $this->db_update_running = (int) $set->get("db_update_running");
171
173 }
174
178 public function clearRunningStatus(): void
179 {
180 $set = new ilSetting("common", true);
181 $set->set("db_update_running", "0");
182 $this->db_update_running = 0;
183 }
184
185 public function readFileVersion(): int
186 {
187 //go through filecontent and search for last occurence of <#x>
188 reset($this->lastfilecontent);
189 $regs = array();
190 $version = 0;
191 foreach ($this->lastfilecontent as $row) {
192 if (preg_match('/^<\#([0-9]+)>/', $row, $regs)) {
193 $version = $regs[1];
194 }
195 }
196
197 $this->fileVersion = (int) $version;
198
199 return $this->fileVersion;
200 }
201
205 public function getFileVersion(): ?int
206 {
207 return $this->fileVersion;
208 }
209
214 public function execQuery(ilDBInterface $db, string $str): bool
215 {
216 $q = "";
217 $sql = explode("\n", trim($str));
218 foreach ($sql as $i => $statement) {
219 $sql[$i] = trim($statement);
220 if ($statement !== "" && $statement[0] !== "#") {
221 //take line per line, until last char is ";"
222 if (substr($statement, -1) === ";") {
223 //query is complete
225 $q .= " " . substr($statement, 0, -1);
226 $check = $this->checkQuery($q);
227 if ($check === true) {
228 $db->query($q);
229 } else {
230 $this->error = (string) $check;
231 return false;
232 }
233 unset($q);
234 } //if
235 else {
237 $q .= " " . $statement;
238 } //else
239 } //if
240 } //for
241 if (isset($q) && $q !== "") {
242 echo "incomplete_statement: " . $q . "<br>";
243
244 return false;
245 }
246
247 return true;
248 }
249
253 public function checkQuery(string $q): bool
254 {
255 return true;
256 }
257
259 ?ilCtrlStructureReader &$ilCtrlStructureReader,
261 ): void {
262 global $DIC;
263
264 // TODO: There is currently a huge mixup of globals, $DIC and dependencies, esprecially in setup and during DB-Updates. This leads to many problems. The following core tries to provide the needed dependencies for the dbupdate-script. The code hopefully will change in the future.
265
266 if (isset($GLOBALS['ilCtrlStructureReader'])) {
267 $ilCtrlStructureReader = $GLOBALS['ilCtrlStructureReader'];
268 } elseif ($DIC->offsetExists('ilCtrlStructureReader')) {
269 $ilCtrlStructureReader = $DIC['ilCtrlStructureReader'];
270 } else {
271 $ilCtrlStructureReader = new ilCtrlStructureReader(
272 $this->ctrl_structure_iterator,
274 );
275 $DIC->offsetSet('ilCtrlStructureReader', $ilCtrlStructureReader);
276 }
277
278 $GLOBALS['ilCtrlStructureReader'] = $ilCtrlStructureReader;
279
280 if ($this->client_ini) {
281 $ilCtrlStructureReader->setIniFile($this->client_ini);
282 }
283 $ilDB = $DIC->database();
284 }
285
290 public function applyUpdate(int $a_break = 0)
291 {
292 $ilCtrlStructureReader = null;
293 $ilDB = null;
294 $this->initGlobalsRequiredForUpdateSteps($ilCtrlStructureReader, $ilDB);
295
296 $f = $this->fileVersion;
297 $c = $this->currentVersion;
298
299 if ($a_break > $this->currentVersion
300 && $a_break < $this->fileVersion
301 ) {
302 $f = $a_break;
303 }
304
305 if ($c < $f) {
306 $msg = array();
307 for ($i = ($c + 1); $i <= $f; $i++) {
308 // check wether next update file must be loaded
309 if ($this->current_file != $this->getFileForStep($i)) {
310 $this->DB_UPDATE_FILE = $this->PATH . "setup/sql/" . $this->getFileForStep($i);
311 $this->readDBUpdateFile();
312 }
313
314 $this->initStep($i);
315
316 if ($this->applyUpdateNr($i) === false) {
317 $msg[] = "msg: update_error - " . $this->error . "; nr: " . $i . ";";
318 $this->updateMsg = implode("\n", $msg);
319
320 return false;
321 }
322
323 $msg[] = "msg: update_applied; nr: " . $i . ";";
324 }
325
326 $this->updateMsg = implode("\n", $msg);
327 } else {
328 $this->updateMsg = "no_changes";
329 }
330
331 if ($f < $this->fileVersion) {
332 return true;
333 }
334 }
335
341 public function applyUpdateNr(int $nr, $hotfix = false, $custom_update = false): bool
342 {
343 $ilCtrlStructureReader = null;
344 $ilMySQLAbstraction = null;
345 $ilDB = null;
346 $this->initGlobalsRequiredForUpdateSteps($ilCtrlStructureReader, $ilDB);
347
348 //search for desired $nr
349 reset($this->filecontent);
350
351 if (!$hotfix && !$custom_update) {
352 $this->setRunningStatus($nr);
353 }
354
355 //init
356 $i = 0;
357
358 //go through filecontent
359 while (!preg_match("/^<\#" . $nr . ">/", $this->filecontent[$i]) && $i < count($this->filecontent)) {
360 $i++;
361 }
362
363 //update not found
364 if ($i === count($this->filecontent)) {
365 $this->error = "update_not_found";
366
367 return false;
368 }
369
370 $i++;
371
372 //update found, now extract this update to a new array
373 $update = array();
374 while ($i < count($this->filecontent) && !preg_match("/^<#" . ($nr + 1) . ">/", $this->filecontent[$i])) {
375 $update[] = trim($this->filecontent[$i]);
376 $i++;
377 }
378
379 //now you have the update, now process it
380 $sql = array();
381 $php = array();
382 $mode = "sql";
383
384 foreach ($update as $row) {
385 if (preg_match("/<\?php/", $row)) {
386 if (count($sql) > 0) {
387 if ($this->execQuery($this->db, implode("\n", $sql)) === false) {
388 return false;
389 }
390 $sql = array();
391 }
392 $mode = "php";
393 } elseif (preg_match("/\?>/", $row)) {
394 if (count($php) > 0) {
395 $code = implode("\n", $php);
396 if (eval($code) === false) {
397 $this->error = "Parse error: " . $code;
398
399 return false;
400 }
401 $php = array();
402 }
403 $mode = "sql";
404 } else {
405 if ($mode === "sql") {
406 $sql[] = $row;
407 }
408
409 if ($mode === "php") {
410 $php[] = $row;
411 }
412 } //else
413 } //foreach
414
415 if ($mode === "sql" && count($sql) > 0) {
416 if ($this->execQuery($this->db, implode("\n", $sql)) === false) {
417 $this->error = "dump_error: " . $this->error;
418
419 return false;
420 }
421 }
422
423 //increase db_Version number
424 if (!$hotfix && !$custom_update) {
425 $this->setCurrentVersion($nr);
426 } elseif ($hotfix) {
427 $this->setHotfixCurrentVersion($nr);
428 } elseif ($custom_update) {
429 $this->setCustomUpdatesCurrentVersion($nr);
430 }
431
432 if (!$hotfix && !$custom_update) {
433 $this->clearRunningStatus();
434 }
435
436 //$this->currentVersion = $ilias->getSetting("db_version");
437
438 return true;
439 }
440
441 public function getDBVersionStatus(): bool
442 {
443 return !($this->fileVersion > $this->currentVersion);
444 }
445
449 public function getTables(): array
450 {
451 $a = array();
452
453 $query = "SHOW TABLES";
454 $res = $this->db->query($query);
455 while ($row = $res->fetchRow()) {
456 $status = $this->getTableStatus($row[0]);
457 $a[] = array("name" => $status["Table"],
458 "table" => $row[0],
459 "status" => $status["Msg_text"],
460 );
461 }
462
463 return $a;
464 }
465
469 public function getTableStatus(string $table)
470 {
471 $query = "ANALYZE TABLE " . $table;
472 return $this->db->query($query)->fetchRow(ilDBConstants::FETCHMODE_ASSOC);
473 }
474
475
479
482 public function getHotfixCurrentVersion(): ?int
483 {
484 $this->readHotfixInfo();
485
486 return $this->hotfix_current_version ?? null;
487 }
488
492 public function setHotfixCurrentVersion(int $a_version): bool
493 {
494 $this->readHotfixInfo();
495 $this->hotfix_setting->set(
496 "db_hotfixes_" . $this->hotfix_version[0],
497 (string) $a_version
498 );
499 $this->hotfix_current_version = $a_version;
500
501 return true;
502 }
503
507 public function getHotfixFileVersion(): ?int
508 {
509 $this->readHotfixInfo();
510
511 return $this->hotfix_file_version ?? null;
512 }
513
517 public function readHotfixFileVersion(array $a_file_content): int
518 {
519 //go through filecontent and search for last occurence of <#x>
520 reset($a_file_content);
521 $regs = [];
522 $version = '';
523 foreach ($a_file_content as $row) {
524 if (preg_match("/^<#([0-9]+)>/", $row, $regs)) {
525 $version = $regs[1];
526 }
527 }
528
529 return (int) $version;
530 }
531
535 public function readHotfixInfo(bool $a_force = false): void
536 {
537 if (isset($this->hotfix_info_read) && $this->hotfix_info_read && !$a_force) {
538 return;
539 }
540 $this->hotfix_setting = new ilSetting("common", true);
541 $ilias_version = ILIAS_VERSION_NUMERIC;
542 $version_array = explode(".", $ilias_version);
543 $this->hotfix_version[0] = $version_array[0];
544 $this->hotfix_version[1] = $version_array[1];
545 $hotfix_file = $this->PATH . "setup/sql/" . $this->hotfix_version[0] . "_hotfixes.php";
546 if (is_file($hotfix_file)) {
547 $this->hotfix_content = @file($hotfix_file);
548 $this->hotfix_current_version = (int) $this->hotfix_setting->get(
549 "db_hotfixes_" . $this->hotfix_version[0]
550 );
551 $this->hotfix_file_version = $this->readHotfixFileVersion($this->hotfix_content);
552 }
553 $this->hotfix_info_read = true;
554 }
555
559 public function hotfixAvailable(): bool
560 {
561 $this->readHotfixInfo();
562 return isset($this->hotfix_file_version) && $this->hotfix_file_version > $this->hotfix_current_version;
563 }
564
568 public function applyHotfix(): bool
569 {
570 $ilCtrlStructureReader = null;
571 $ilDB = null;
572 $this->initGlobalsRequiredForUpdateSteps($ilCtrlStructureReader, $ilDB);
573 $this->readHotfixInfo(true);
574
575 $f = $this->getHotfixFileVersion();
576 $c = $this->getHotfixCurrentVersion();
577
578 if ($c < $f) {
579 $msg = array();
580 for ($i = ($c + 1); $i <= $f; $i++) {
581 $this->filecontent = $this->hotfix_content;
582
583 if ($this->applyUpdateNr($i, true) === false) {
584 $msg[] = array("msg" => "update_error: " . $this->error,
585 "nr" => $i,
586 );
587 $this->updateMsg = implode("\n", $msg);
588
589 return false;
590 }
591
592 $msg[] = array("msg" => "hotfix_applied",
593 "nr" => $i,
594 );
595 }
596
597 $this->updateMsg = implode("\n", $msg);
598 } else {
599 $this->updateMsg = "no_changes";
600 }
601
602 return true;
603 }
604
606 {
607 $this->readCustomUpdatesInfo();
608
609 return $this->custom_updates_current_version;
610 }
611
612 public function setCustomUpdatesCurrentVersion(?int $a_version): bool
613 {
614 $this->readCustomUpdatesInfo();
615 $this->custom_updates_setting->set('db_version_custom', (string) $a_version);
616 $this->custom_updates_current_version = $a_version;
617
618 return true;
619 }
620
622 {
623 $this->readCustomUpdatesInfo();
624
625 return $this->custom_updates_file_version;
626 }
627
628 public function readCustomUpdatesFileVersion(array $a_file_content): int
629 {
630 //go through filecontent and search for last occurence of <#x>
631 reset($a_file_content);
632 $regs = [];
633 $version = '';
634 foreach ($a_file_content as $row) {
635 if (preg_match("/^<#([0-9]+)>/", $row, $regs)) {
636 $version = $regs[1];
637 }
638 }
639
640 return (int) $version;
641 }
642
643 public function readCustomUpdatesInfo(bool $a_force = false): void
644 {
645 if ($this->custom_updates_info_read && !$a_force) {
646 return;
647 }
648
649 $this->custom_updates_setting = new ilSetting();
650 $custom_updates_file = $this->PATH . "setup/sql/dbupdate_custom.php";
651 if (is_file($custom_updates_file)) {
652 $this->custom_updates_content = @file($custom_updates_file);
653 $this->custom_updates_current_version = (int) $this->custom_updates_setting->get('db_version_custom', "0");
654 $this->custom_updates_file_version = $this->readCustomUpdatesFileVersion($this->custom_updates_content);
655 }
656 $this->custom_updates_info_read = true;
657 }
658
659 public function customUpdatesAvailable(): bool
660 {
661 $this->readCustomUpdatesInfo();
662 return $this->custom_updates_file_version > $this->custom_updates_current_version;
663 }
664
665 public function applyCustomUpdates(): bool
666 {
667 $ilCtrlStructureReader = null;
668 $ilDB = null;
669 $this->initGlobalsRequiredForUpdateSteps($ilCtrlStructureReader, $ilDB);
670 $this->readCustomUpdatesInfo(true);
671
672 $f = $this->getCustomUpdatesFileVersion();
673 $c = $this->getCustomUpdatesCurrentVersion();
674
675 if ($c < $f) {
676 $msg = array();
677 for ($i = ($c + 1); $i <= $f; $i++) {
678 $this->filecontent = $this->custom_updates_content;
679
680 if ($this->applyUpdateNr($i, false, true) === false) {
681 $msg[] = array("msg" => "update_error: " . $this->error,
682 "nr" => $i,
683 );
684 $this->updateMsg = implode("\n", $msg);
685
686 return false;
687 }
688
689 $msg[] = array("msg" => "custom_update_applied",
690 "nr" => $i,
691 );
692 }
693
694 $this->updateMsg = implode("\n", $msg);
695 } else {
696 $this->updateMsg = "no_changes";
697 }
698
699 return true;
700 }
701
706 public function getUpdateSteps(int $a_break = 0): string
707 {
708 $ilCtrlStructureReader = null;
709 $ilMySQLAbstraction = null;
710 $ilDB = null;
711 $this->initGlobalsRequiredForUpdateSteps($ilCtrlStructureReader, $ilDB);
712
713 $str = "";
714
715 $f = $this->fileVersion;
716 $c = $this->currentVersion;
717
718 if ($a_break > $this->currentVersion
719 && $a_break < $this->fileVersion
720 ) {
721 $f = $a_break;
722 }
723
724 if ($c < $f) {
725 for ($i = ($c + 1); $i <= $f; $i++) {
726 // check wether next update file must be loaded
727 if ($this->current_file != $this->getFileForStep($i)) {
728 $this->DB_UPDATE_FILE = $this->PATH . "setup/sql/" . $this->getFileForStep($i);
729 $this->readDBUpdateFile();
730 }
731
732 $str .= $this->getUpdateStepNr($i);
733 }
734 }
735
736 return $str;
737 }
738
743 public function getHotfixSteps(): string
744 {
745 $this->readHotfixInfo(true);
746
747 $str = "";
748
749 $f = $this->getHotfixFileVersion();
750 $c = $this->getHotfixCurrentVersion();
751
752 if ($c < $f) {
753 for ($i = ($c + 1); $i <= $f; $i++) {
754 $this->filecontent = $this->hotfix_content;
755
756 $str .= $this->getUpdateStepNr($i, true);
757 }
758 }
759
760 return $str;
761 }
762
766 public function getUpdateStepNr(int $nr, bool $hotfix = false, bool $custom_update = false): string
767 {
768 $str = "";
769
770 //search for desired $nr
771 reset($this->filecontent);
772
773 //init
774 $i = 0;
775
776 //go through filecontent
777 while (!preg_match("/^<#" . $nr . ">/", $this->filecontent[$i]) && $i < count($this->filecontent)) {
778 $i++;
779 }
780
781 //update not found
782 if ($i === count($this->filecontent)) {
783 return '';
784 }
785
786 $i++;
787 while ($i < count($this->filecontent) && !preg_match("/^<#" . ($nr + 1) . ">/", $this->filecontent[$i])) {
788 $str .= $this->filecontent[$i];
789 $i++;
790 }
791
792 return "<pre><b><#" . $nr . "></b>\n" . htmlentities($str) . "</pre>";
793 }
794}
$version
Definition: plugin.php:24
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
$check
Definition: buildRTE.php:81
error(string $a_errmsg)
Class ilCtrlArrayIterator.
Class ilCtrlStructureCidGenerator.
Class ilCtrlStructureReader is responsible for reading ilCtrl's control structure.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getTableStatus(string $table)
ilIniFile $client_ini
getFileVersion()
Get Version of file.
getHotfixSteps()
Get hotfix steps.
array $hotfix_content
readCustomUpdatesInfo(bool $a_force=false)
__construct(ilDBInterface $a_db_handler, ilIniFile $client_ini=null)
ilDBInterface $db
getCustomUpdatesCurrentVersion()
applyUpdate(int $a_break=0)
Apply update.
array $hotfix_version
readCustomUpdatesFileVersion(array $a_file_content)
getUpdateStepNr(int $nr, bool $hotfix=false, bool $custom_update=false)
Get single update step for presentation.
int $hotfix_file_version
getHotfixCurrentVersion()
Get current hotfix version.
int $custom_updates_file_version
getUpdateSteps(int $a_break=0)
Get update steps as string (for presentation)
readHotfixInfo(bool $a_force=false)
Get status of hotfix file.
setHotfixCurrentVersion(int $a_version)
Set current hotfix version.
initGlobalsRequiredForUpdateSteps(?ilCtrlStructureReader &$ilCtrlStructureReader, ?ilDBInterface &$ilDB)
getHotfixFileVersion()
Get current hotfix version.
int $hotfix_current_version
int $custom_updates_current_version
ilSetting $custom_updates_setting
hotfixAvailable()
Get status of hotfix file.
ilSetting $hotfix_setting
readHotfixFileVersion(array $a_file_content)
Set current hotfix version.
setCurrentVersion(int $a_version)
string $DB_UPDATE_FILE
checkQuery(string $q)
check query
array $lastfilecontent
execQuery(ilDBInterface $db, string $str)
execute a query
clearRunningStatus()
Clear running status.
array $custom_updates_content
getFileForStep(int $a_version)
Get db update file name for db step.
string $current_file
setCustomUpdatesCurrentVersion(?int $a_version)
Iterator $ctrl_structure_iterator
applyUpdateNr(int $nr, $hotfix=false, $custom_update=false)
apply an update
applyHotfix()
Apply hotfix.
bool $custom_updates_info_read
setRunningStatus(int $a_nr)
Set running status for a step.
getRunningStatus()
Get running status.
string $LAST_UPDATE_FILE
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$c
Definition: cli.php:38
global $DIC
Definition: feed.php:28
$update
Definition: imgupload.php:92
const ILIAS_VERSION_NUMERIC
Interface ilDBInterface.
query(string $query)
Run a (read-only) Query on the database.
$res
Definition: ltiservices.php:69
$i
Definition: metadata.php:41
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
const PATH
Definition: proxy_ylocal.php:8
$query