ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilDBUpdate.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
29 {
30  protected string $updateMsg;
31  protected ilDBInterface $db;
32  protected ?ilIniFile $client_ini = null;
34 
35  protected string $error;
36  protected string $PATH = './';
37 
38  protected array $filecontent;
39 
41  private ?int $custom_updates_file_version = null;
42  private ?bool $custom_updates_info_read = null;
44  private array $custom_updates_content = [];
45 
46  public function __construct(ilDBInterface $a_db_handler, ilIniFile $client_ini = null)
47  {
48  $this->client_ini = $client_ini;
49  $this->db = $a_db_handler;
50  $this->PATH = "./";
51 
52  $class_map = require ILIAS_ABSOLUTE_PATH . '/libs/composer/vendor/composer/autoload_classmap.php';
53  $this->ctrl_structure_iterator = new ilCtrlArrayIterator($class_map);
54  }
55 
56  private function execQuery(ilDBInterface $db, string $str): bool
57  {
58  $q = "";
59  $sql = explode("\n", trim($str));
60  foreach ($sql as $i => $statement) {
61  $sql[$i] = trim($statement);
62  if ($statement !== "" && $statement[0] !== "#") {
63  //take line per line, until last char is ";"
64  if (substr($statement, -1) === ";") {
65  //query is complete
67  $q .= " " . substr($statement, 0, -1);
68  $check = $this->checkQuery($q);
69  if ($check === true) {
70  $db->query($q);
71  } else {
72  $this->error = (string) $check;
73  return false;
74  }
75  unset($q);
76  } else {
78  $q .= " " . $statement;
79  }
80  }
81  }
82  if (isset($q) && $q !== "") {
83  echo "incomplete_statement: " . $q . "<br>";
84 
85  return false;
86  }
87 
88  return true;
89  }
90 
91  protected function checkQuery(string $q): bool
92  {
93  return true;
94  }
95 
97  ?ilCtrlStructureReader &$ilCtrlStructureReader,
99  ): void {
100  global $DIC;
101 
102  // 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.
103 
104  if (isset($GLOBALS['ilCtrlStructureReader'])) {
105  $ilCtrlStructureReader = $GLOBALS['ilCtrlStructureReader'];
106  } elseif ($DIC->offsetExists('ilCtrlStructureReader')) {
107  $ilCtrlStructureReader = $DIC['ilCtrlStructureReader'];
108  } else {
109  $ilCtrlStructureReader = new ilCtrlStructureReader(
110  $this->ctrl_structure_iterator,
112  );
113  $DIC->offsetSet('ilCtrlStructureReader', $ilCtrlStructureReader);
114  }
115 
116  $GLOBALS['ilCtrlStructureReader'] = $ilCtrlStructureReader;
117 
118  if ($this->client_ini) {
119  $ilCtrlStructureReader->setIniFile($this->client_ini);
120  }
121  $ilDB = $DIC->database();
122  }
123 
129  protected function applyUpdateNr(int $nr, bool $custom_update = false): bool
130  {
131  $ilCtrlStructureReader = null;
132  $ilDB = null;
133  $this->initGlobalsRequiredForUpdateSteps($ilCtrlStructureReader, $ilDB);
134 
135  reset($this->filecontent);
136 
137  //init
138  $i = 0;
139 
140  //go through filecontent
141  while (!preg_match("/^<\#" . $nr . ">/", $this->filecontent[$i]) && $i < count($this->filecontent)) {
142  $i++;
143  }
144 
145  //update not found
146  if ($i === count($this->filecontent)) {
147  $this->error = 'update_not_found';
148 
149  return false;
150  }
151 
152  $i++;
153 
154  //update found, now extract this update to a new array
155  $update = [];
156  while ($i < count($this->filecontent) && !preg_match("/^<#" . ($nr + 1) . ">/", $this->filecontent[$i])) {
157  $update[] = trim($this->filecontent[$i]);
158  $i++;
159  }
160 
161  //now you have the update, now process it
162  $sql = [];
163  $php = [];
164  $mode = 'sql';
165 
166  foreach ($update as $row) {
167  if (preg_match("/<\?php/", $row)) {
168  if (count($sql) > 0) {
169  if ($this->execQuery($this->db, implode("\n", $sql)) === false) {
170  return false;
171  }
172  $sql = [];
173  }
174  $mode = 'php';
175  } elseif (preg_match("/\?>/", $row)) {
176  if (count($php) > 0) {
177  $code = implode("\n", $php);
178  if (eval($code) === false) {
179  $this->error = 'Parse error: ' . $code;
180 
181  return false;
182  }
183  $php = [];
184  }
185  $mode = 'sql';
186  } else {
187  if ($mode === 'sql') {
188  $sql[] = $row;
189  }
190 
191  if ($mode === 'php') {
192  $php[] = $row;
193  }
194  }
195  }
196 
197  if ($mode === 'sql' && count($sql) > 0 && $this->execQuery($this->db, implode("\n", $sql)) === false) {
198  $this->error = "dump_error: " . $this->error;
199 
200  return false;
201  }
202 
203  if ($custom_update) {
204  $this->setCustomUpdatesCurrentVersion($nr);
205  } else {
206  $this->setCurrentVersion($nr);
207  }
208 
209  return true;
210  }
211 
213  {
214  $this->readCustomUpdatesInfo();
215 
217  }
218 
219  private function setCustomUpdatesCurrentVersion(?int $a_version): void
220  {
221  $this->readCustomUpdatesInfo();
222  $this->custom_updates_setting->set('db_version_custom', (string) $a_version);
223  $this->custom_updates_current_version = $a_version;
224  }
225 
226  protected function setCurrentVersion(?int $a_version): void
227  {
228  }
229 
230  public function getCustomUpdatesFileVersion(): ?int
231  {
232  $this->readCustomUpdatesInfo();
233 
235  }
236 
237  private function readCustomUpdatesFileVersion(array $a_file_content): int
238  {
239  //go through file content and search for last occurrence of <#x>
240  reset($a_file_content);
241  $regs = [];
242  $version = 0;
243  foreach ($a_file_content as $row) {
244  if (preg_match("/^<#([0-9]+)>/", $row, $regs)) {
245  $version = $regs[1];
246  }
247  }
248 
249  return (int) $version;
250  }
251 
252  private function readCustomUpdatesInfo(bool $a_force = false): void
253  {
254  if ($this->custom_updates_info_read && !$a_force) {
255  return;
256  }
257 
258  $this->custom_updates_setting = new ilSetting();
259  $custom_updates_file = $this->PATH . 'setup/sql/dbupdate_custom.php';
260  if (is_file($custom_updates_file)) {
261  $this->custom_updates_content = @file($custom_updates_file);
262  $this->custom_updates_current_version = (int) $this->custom_updates_setting->get('db_version_custom', '0');
263  $this->custom_updates_file_version = $this->readCustomUpdatesFileVersion($this->custom_updates_content);
264  }
265  $this->custom_updates_info_read = true;
266  }
267 
268  public function applyCustomUpdates(): bool
269  {
270  $ilCtrlStructureReader = null;
271  $ilDB = null;
272  $this->initGlobalsRequiredForUpdateSteps($ilCtrlStructureReader, $ilDB);
273  $this->readCustomUpdatesInfo(true);
274 
275  $file_version = $this->getCustomUpdatesFileVersion();
276  $current_version = $this->getCustomUpdatesCurrentVersion();
277  $this->filecontent = $this->custom_updates_content;
278 
279  $this->updateMsg = 'no_changes';
280  if ($current_version < $file_version) {
281  $msg = [];
282  for ($i = ($current_version + 1); $i <= $file_version; $i++) {
283  if ($this->applyUpdateNr($i, true) === false) {
284  $msg[] = [
285  'msg' => 'update_error: ' . $this->error,
286  'nr' => $i,
287  ];
288  $this->updateMsg = implode("\n", $msg);
289 
290  return false;
291  }
292 
293  $msg[] = [
294  'msg' => 'custom_update_applied',
295  'nr' => $i,
296  ];
297  }
298 
299  $this->updateMsg = implode("\n", $msg);
300  }
301 
302  return true;
303  }
304 }
readCustomUpdatesFileVersion(array $a_file_content)
__construct(ilDBInterface $a_db_handler, ilIniFile $client_ini=null)
Class ilCtrlStructureCidGenerator.
ilSetting $custom_updates_setting
setCurrentVersion(?int $a_version)
array $custom_updates_content
checkQuery(string $q)
Class ilCtrlStructureReader is responsible for reading ilCtrl&#39;s control structure.
applyUpdateNr(int $nr, bool $custom_update=false)
Apply a custom database update or a plugin update.
global $DIC
Definition: feed.php:28
$GLOBALS["DIC"]
Definition: wac.php:31
readCustomUpdatesInfo(bool $a_force=false)
int $custom_updates_file_version
Iterator $ctrl_structure_iterator
int $custom_updates_current_version
execQuery(ilDBInterface $db, string $str)
bool $custom_updates_info_read
query(string $query)
Run a (read-only) Query on the database.
Class ilCtrlArrayIterator.
initGlobalsRequiredForUpdateSteps(?ilCtrlStructureReader &$ilCtrlStructureReader, ?ilDBInterface &$ilDB)
Database Update class.
setCustomUpdatesCurrentVersion(?int $a_version)
ilDBInterface $db
$q
Definition: shib_logout.php:21
$check
Definition: buildRTE.php:81
getCustomUpdatesCurrentVersion()
ilIniFile $client_ini
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$version
Definition: plugin.php:24