Elgg  Version 2.2
 All Classes Namespaces Files Functions Variables Pages
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,
57  \Elgg\Database\Mutex $mutex) {
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 (!@self::includeCode("$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 (!self::includeCode("$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 
179  protected static function includeCode($file) {
180  return include $file;
181  }
182 
190  protected function setProcessedUpgrade($upgrade) {
191  $processed_upgrades = $this->getProcessedUpgrades();
192  $processed_upgrades[] = $upgrade;
193  $processed_upgrades = array_unique($processed_upgrades);
194  return $this->datalist->set('processed_upgrades', serialize($processed_upgrades));
195  }
196 
202  protected function getProcessedUpgrades() {
203  $upgrades = $this->datalist->get('processed_upgrades');
204  $unserialized = unserialize($upgrades);
205  return $unserialized;
206  }
207 
215  protected function getUpgradeFileVersion($filename) {
216  preg_match('/^([0-9]{10})([\.a-z0-9-_]+)?\.(php)$/i', $filename, $matches);
217 
218  if (isset($matches[1])) {
219  return (int) $matches[1];
220  }
221 
222  return false;
223  }
224 
231  protected function getUpgradeFiles($upgrade_path = null) {
232  if (!$upgrade_path) {
233  $upgrade_path = elgg_get_engine_path() . '/lib/upgrades/';
234  }
235  $upgrade_path = sanitise_filepath($upgrade_path);
236  $handle = opendir($upgrade_path);
237 
238  if (!$handle) {
239  return false;
240  }
241 
242  $upgrade_files = array();
243 
244  while ($upgrade_file = readdir($handle)) {
245  // make sure this is a wellformed upgrade.
246  if (is_dir($upgrade_path . '$upgrade_file')) {
247  continue;
248  }
249  $upgrade_version = $this->getUpgradeFileVersion($upgrade_file);
250  if (!$upgrade_version) {
251  continue;
252  }
253  $upgrade_files[] = $upgrade_file;
254  }
255 
256  sort($upgrade_files);
257 
258  return $upgrade_files;
259  }
260 
269  protected function getUnprocessedUpgrades($upgrade_files = null, $processed_upgrades = null) {
270  if ($upgrade_files === null) {
271  $upgrade_files = $this->getUpgradeFiles();
272  }
273 
274  if ($processed_upgrades === null) {
275  $processed_upgrades = unserialize($this->datalist->get('processed_upgrades'));
276  if (!is_array($processed_upgrades)) {
277  $processed_upgrades = array();
278  }
279  }
280 
281  $unprocessed = array_diff($upgrade_files, $processed_upgrades);
282  return $unprocessed;
283  }
284 
290  protected function processUpgrades() {
291  $dbversion = (int) $this->datalist->get('version');
292 
293  if ($this->upgradeCode($dbversion)) {
294  system_message($this->translator->translate('upgrade:core'));
295 
296  // Now we trigger an event to give the option for plugins to do something
297  $upgrade_details = new \stdClass;
298  $upgrade_details->from = $dbversion;
299  $upgrade_details->to = elgg_get_version();
300 
301  $this->events->trigger('upgrade', 'upgrade', $upgrade_details);
302 
303  return true;
304  }
305 
306  return false;
307  }
308 }
309 
getUpgradeFiles($upgrade_path=null)
Returns a list of upgrade files relative to the $upgrade_path dir.
if(!array_key_exists($filename, $text_files)) $file
$e
Definition: metadata.php:12
sanitise_filepath($path, $append_slash=true)
Sanitise file paths ensuring that they begin and end with slashes etc.
Definition: elgglib.php:399
$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
getUnprocessedUpgrades($upgrade_files=null, $processed_upgrades=null)
Checks if any upgrades need to be run.
static includeCode($file)
PHP include a file with a very limited scope.
elgg_get_version($human_readable=false)
Get the current Elgg version information.
Definition: elgglib.php:1043
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.
$version
Definition: version.php:14
system_message($message)
Display a system message on next page load.
Definition: elgglib.php:438
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.