Elgg  Version 2.3
UpgradeService.php
Go to the documentation of this file.
1 <?php
2 namespace Elgg;
3 
10 
14  private $translator;
15 
19  private $events;
20 
24  private $hooks;
25 
29  private $datalist;
30 
34  private $logger;
35 
39  private $mutex;
40 
51  public function __construct(
52  \Elgg\i18n\Translator $translator,
53  \Elgg\EventsService $events,
54  \Elgg\PluginHooksService $hooks,
55  \Elgg\Database\Datalist $datalist,
56  \Elgg\Logger $logger,
58  $this->translator = $translator;
59  $this->events = $events;
60  $this->hooks = $hooks;
61  $this->datalist = $datalist;
62  $this->mutex = $mutex;
63  }
64 
70  public function run() {
71  $result = array(
72  'failure' => false,
73  'reason' => '',
74  );
75 
76  // prevent someone from running the upgrade script in parallel (see #4643)
77  if (!$this->mutex->lock('upgrade')) {
78  $result['failure'] = true;
79  $result['reason'] = $this->translator->translate('upgrade:locked');
80  return $result;
81  }
82 
83  // disable the system log for upgrades to avoid exceptions when the schema changes.
84  $this->events->unregisterHandler('log', 'systemlog', 'system_log_default_logger');
85  $this->events->unregisterHandler('all', 'all', 'system_log_listener');
86 
87  // turn off time limit
88  set_time_limit(0);
89 
90  if ($this->getUnprocessedUpgrades()) {
91  $this->processUpgrades();
92  }
93 
94  $this->events->trigger('upgrade', 'system', null);
96 
97  $this->mutex->unlock('upgrade');
98 
99  return $result;
100  }
101 
110  protected function upgradeCode($version, $quiet = false) {
111  $version = (int) $version;
112  $upgrade_path = elgg_get_engine_path() . '/lib/upgrades/';
113  $processed_upgrades = $this->getProcessedUpgrades();
114 
115  $upgrade_files = $this->getUpgradeFiles($upgrade_path);
116 
117  if ($upgrade_files === false) {
118  return false;
119  }
120 
121  $upgrades = $this->getUnprocessedUpgrades($upgrade_files, $processed_upgrades);
122 
123  // Sort and execute
124  sort($upgrades);
125 
126  foreach ($upgrades as $upgrade) {
127  $upgrade_version = $this->getUpgradeFileVersion($upgrade);
128  $success = true;
129 
130  if ($upgrade_version <= $version) {
131  // skip upgrade files from before the installation version of Elgg
132  // because the upgrade files from before the installation version aren't
133  // added to the database.
134  continue;
135  }
136 
137  // hide all errors.
138  if ($quiet) {
139  // hide include errors as well as any exceptions that might happen
140  try {
141  if (!@Includer::includeFile("$upgrade_path/$upgrade")) {
142  $success = false;
143  $this->logger->error("Could not include $upgrade_path/$upgrade");
144  }
145  } catch (\Exception $e) {
146  $success = false;
147  $this->logger->error($e->getMessage());
148  }
149  } else {
150  if (!Includer::includeFile("$upgrade_path/$upgrade")) {
151  $success = false;
152  $this->logger->error("Could not include $upgrade_path/$upgrade");
153  }
154  }
155 
156  if ($success) {
157  // don't set the version to a lower number in instances where an upgrade
158  // has been merged from a lower version of Elgg
159  if ($upgrade_version > $version) {
160  $this->datalist->set('version', $upgrade_version);
161  }
162 
163  // incrementally set upgrade so we know where to start if something fails.
164  $this->setProcessedUpgrade($upgrade);
165  } else {
166  return false;
167  }
168  }
169 
170  return true;
171  }
172 
180  protected function setProcessedUpgrade($upgrade) {
181  $processed_upgrades = $this->getProcessedUpgrades();
182  $processed_upgrades[] = $upgrade;
183  $processed_upgrades = array_unique($processed_upgrades);
184  return $this->datalist->set('processed_upgrades', serialize($processed_upgrades));
185  }
186 
192  protected function getProcessedUpgrades() {
193  $upgrades = $this->datalist->get('processed_upgrades');
194  $unserialized = unserialize($upgrades);
195  return $unserialized;
196  }
197 
205  protected function getUpgradeFileVersion($filename) {
206  preg_match('/^([0-9]{10})([\.a-z0-9-_]+)?\.(php)$/i', $filename, $matches);
207 
208  if (isset($matches[1])) {
209  return (int) $matches[1];
210  }
211 
212  return false;
213  }
214 
221  protected function getUpgradeFiles($upgrade_path = null) {
222  if (!$upgrade_path) {
223  $upgrade_path = elgg_get_engine_path() . '/lib/upgrades/';
224  }
225  $upgrade_path = sanitise_filepath($upgrade_path);
226  $handle = opendir($upgrade_path);
227 
228  if (!$handle) {
229  return false;
230  }
231 
232  $upgrade_files = array();
233 
234  while ($upgrade_file = readdir($handle)) {
235  // make sure this is a wellformed upgrade.
236  if (is_dir($upgrade_path . '$upgrade_file')) {
237  continue;
238  }
239  $upgrade_version = $this->getUpgradeFileVersion($upgrade_file);
240  if (!$upgrade_version) {
241  continue;
242  }
243  $upgrade_files[] = $upgrade_file;
244  }
245 
246  sort($upgrade_files);
247 
248  return $upgrade_files;
249  }
250 
259  protected function getUnprocessedUpgrades($upgrade_files = null, $processed_upgrades = null) {
260  if ($upgrade_files === null) {
261  $upgrade_files = $this->getUpgradeFiles();
262  }
263 
264  if ($processed_upgrades === null) {
265  $processed_upgrades = unserialize($this->datalist->get('processed_upgrades'));
266  if (!is_array($processed_upgrades)) {
267  $processed_upgrades = array();
268  }
269  }
270 
271  $unprocessed = array_diff($upgrade_files, $processed_upgrades);
272  return $unprocessed;
273  }
274 
280  protected function processUpgrades() {
281  $dbversion = (int) $this->datalist->get('version');
282 
283  if ($this->upgradeCode($dbversion)) {
284  system_message($this->translator->translate('upgrade:core'));
285 
286  // Now we trigger an event to give the option for plugins to do something
287  $upgrade_details = new \stdClass;
288  $upgrade_details->from = $dbversion;
289  $upgrade_details->to = elgg_get_version();
290 
291  $this->events->trigger('upgrade', 'upgrade', $upgrade_details);
292 
293  return true;
294  }
295 
296  return false;
297  }
298 }
299 
getUpgradeFiles($upgrade_path=null)
Returns a list of upgrade files relative to the $upgrade_path dir.
$version
$e
Definition: metadata.php:12
The Elgg database.
Definition: Database.php:17
sanitise_filepath($path, $append_slash=true)
Sanitise file paths ensuring that they begin and end with slashes etc.
Definition: elgglib.php:411
$mutex
Unlocks the upgrade script.
setProcessedUpgrade($upgrade)
Saves a processed upgrade to a dataset.
run()
Run the upgrade process.
getUpgradeFileVersion($filename)
Returns the version of the upgrade filename.
WARNING: API IN FLUX.
Definition: Mutex.php:14
Upgrade service for Elgg.
$upgrades
Lists pending upgrades.
Definition: upgrades.php:6
WARNING: API IN FLUX.
Definition: Translator.php:11
Save menu items.
getUnprocessedUpgrades($upgrade_files=null, $processed_upgrades=null)
Checks if any upgrades need to be run.
elgg system_message
Wrapper function for system_messages.
Definition: elgglib.js:390
elgg_get_version($human_readable=false)
Get the current Elgg version information.
Definition: elgglib.php:1071
processUpgrades()
Upgrades Elgg Database and code.
$filename
elgg_get_engine_path()
/path/to/elgg/engine
__construct(\Elgg\i18n\Translator $translator,\Elgg\EventsService $events,\Elgg\PluginHooksService $hooks,\Elgg\Database\Datalist $datalist,\Elgg\Logger $logger,\Elgg\Database\Mutex $mutex)
Constructor.
elgg_flush_caches()
Flush all the registered caches.
Definition: cache.php:225
getProcessedUpgrades()
Gets a list of processes upgrades.
upgradeCode($version, $quiet=false)
Run any php upgrade scripts which are required.