Elgg  Version 5.1
UpgradeService.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
13 use function React\Promise\all;
16 
23 
24  use Loggable;
25 
29  protected $locator;
30 
34  private $translator;
35 
39  private $events;
40 
44  private $config;
45 
49  private $mutex;
50 
54  private $system_messages;
55 
59  protected $progress;
60 
72  public function __construct(
73  Locator $locator,
74  Translator $translator,
75  EventsService $events,
77  Mutex $mutex,
78  SystemMessagesService $system_messages,
79  Progress $progress
80  ) {
81  $this->locator = $locator;
82  $this->translator = $translator;
83  $this->events = $events;
84  $this->config = $config;
85  $this->mutex = $mutex;
86  $this->system_messages = $system_messages;
87  $this->progress = $progress;
88  }
89 
94  protected function up() {
95  return new Promise(function ($resolve, $reject) {
96  Application::migrate();
97 
98  if (!$this->events->triggerBefore('upgrade', 'system', null)) {
99  return $reject(new \RuntimeException($this->translator->translate('upgrade:terminated')));
100  }
101 
102  // prevent someone from running the upgrade script in parallel (see #4643)
103  if (!$this->mutex->lock('upgrade')) {
104  return $reject(new \RuntimeException($this->translator->translate('upgrade:locked')));
105  }
106 
107  // Clear system caches
110 
111  return $resolve();
112  });
113  }
114 
119  protected function down() {
120  return new Promise(function ($resolve, $reject) {
121  if (!$this->events->trigger('upgrade', 'system', null)) {
122  return $reject();
123  }
124 
126 
127  $this->mutex->unlock('upgrade');
128 
129  $this->events->triggerAfter('upgrade', 'system', null);
130 
131  return $resolve();
132  });
133  }
134 
142  protected function runUpgrades($upgrades) {
143  $promises = [];
144 
145  foreach ($upgrades as $upgrade) {
146  if (!$upgrade instanceof \ElggUpgrade) {
147  continue;
148  }
149 
150  $promises[] = new Promise(function ($resolve, $reject) use ($upgrade) {
151  try {
152  $result = $this->executeUpgrade($upgrade, false);
153  } catch (\Throwable $ex) {
154  return $reject($ex);
155  }
156 
157  if ($result->getFailureCount()) {
158  $msg = elgg_echo('admin:upgrades:failed', [
159  $upgrade->getDisplayName(),
160  ]);
161 
162  return $reject(new \RuntimeException($msg));
163  } else {
164  return $resolve($result);
165  }
166  });
167  }
168 
169  return all($promises);
170  }
171 
179  public function run($upgrades = null) {
180  // turn off time limit
181  set_time_limit(3600);
182 
183  $deferred = new Deferred();
184 
185  $promise = $deferred->promise();
186 
187  $resolve = function ($value) use ($deferred) {
188  $deferred->resolve($value);
189  };
190 
191  $reject = function ($error) use ($deferred) {
192  $deferred->reject($error);
193  };
194 
195  if (!isset($upgrades)) {
196  $upgrades = $this->getPendingUpgrades(false);
197  }
198 
199  $this->up()->done(
200  function () use ($resolve, $reject, $upgrades) {
201  all([
202  $this->runUpgrades($upgrades),
203  ])->done(
204  function () use ($resolve, $reject) {
205  $this->down()->done(
206  function ($result) use ($resolve) {
207  return $resolve($result);
208  },
209  $reject
210  );
211  },
212  $reject
213  );
214  },
215  $reject
216  );
217 
218  return $promise;
219  }
220 
228  public function getPendingUpgrades($async = true) {
229  $pending = [];
230 
231  $upgrades = $this->locator->locate();
232 
233  foreach ($upgrades as $upgrade) {
234  if ($upgrade->isCompleted()) {
235  continue;
236  }
237 
238  $batch = $upgrade->getBatch();
239  if (!$batch) {
240  continue;
241  }
242 
243  $pending[] = $upgrade;
244  }
245 
246  if (!$async) {
247  $pending = array_filter($pending, function(\ElggUpgrade $upgrade) {
248  return !$upgrade->isAsynchronous();
249  });
250  }
251 
252  return $pending;
253  }
254 
262  public function getCompletedUpgrades($async = true) {
263  // make sure always to return all upgrade entities
264  return elgg_call(
266  function () use ($async) {
267  $completed = [];
268 
269  $order_by_completed_time = new EntitySortByClause();
270  $order_by_completed_time->direction = 'DESC';
271  $order_by_completed_time->property = 'completed_time';
272 
274  'type' => 'object',
275  'subtype' => 'elgg_upgrade',
276  'metadata_name' => 'class', // filters old upgrades
277  'metadata_name_value_pairs' => [
278  'name' => 'is_completed',
279  'value' => true,
280  ],
281  'order_by' => $order_by_completed_time,
282  'limit' => false,
283  'batch' => true,
284  ]);
285  /* @var $upgrade \ElggUpgrade */
286  foreach ($upgrades as $upgrade) {
287  $batch = $upgrade->getBatch();
288  if (!$batch) {
289  continue;
290  }
291 
292  $completed[] = $upgrade;
293  }
294 
295  if (!$async) {
296  $completed = array_filter($completed, function(\ElggUpgrade $upgrade) {
297  return !$upgrade->isAsynchronous();
298  });
299  }
300 
301  return $completed;
302  }
303  );
304  }
305 
315  public function executeUpgrade(\ElggUpgrade $upgrade, $max_duration = null) {
316  // Upgrade also disabled data, so the compatibility is
317  // preserved in case the data ever gets enabled again
318  return elgg_call(
320  function () use ($upgrade, $max_duration) {
321  return $this->events->triggerResultsSequence('upgrade:execute', 'system', ['object' => $upgrade], null, function() use ($upgrade, $max_duration) {
322  $result = new Result();
323 
324  $loop = new Loop(
325  $upgrade,
326  $result,
327  $this->progress,
328  $this->getLogger()
329  );
330 
331  $loop->loop($max_duration);
332 
333  return $result;
334  });
335  }
336  );
337  }
338 }
elgg_call(int $flags, Closure $closure)
Calls a callable autowiring the arguments using public DI services and applying logic based on flags...
Definition: elgglib.php:299
Exception thrown if an error which can only be found on runtime occurs.
Locates and registers both core and plugin upgrades.
Definition: Locator.php:16
getCompletedUpgrades($async=true)
Get completed (async) upgrades ordered by recently completed first.
$mutex
Unlocks the upgrade script.
static disable()
Disables the caches in the system.
elgg_echo(string $message_key, array $args=[], string $language= '')
Elgg language module Functions to manage language and translations.
Definition: languages.php:17
Events service.
Provides database mutex that can be used to prevent race conditions between two processes that affect...
Definition: Mutex.php:18
Extends QueryBuilder with clauses necesary to sort entity lists by entity properties.
isAsynchronous()
Check if the upgrade should be run asynchronously.
Definition: ElggUpgrade.php:98
Upgrade service for Elgg.
$value
Definition: generic.php:51
elgg_invalidate_caches()
Invalidate all the registered caches.
Definition: cache.php:183
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
$upgrades
Lists pending upgrades.
Definition: upgrades.php:11
trait Loggable
Enables adding a logger.
Definition: Loggable.php:14
const ELGG_IGNORE_ACCESS
elgg_call() flags
Definition: constants.php:130
$error
Bad request error.
Definition: 400.php:6
executeUpgrade(\ElggUpgrade $upgrade, $max_duration=null)
Call the upgrade&#39;s run() for a specified period of time, or until it completes.
if(empty($guid)) $upgrade
Definition: upgrade.php:11
getPendingUpgrades($async=true)
Get pending async upgrades.
const ELGG_SHOW_DISABLED_ENTITIES
Definition: constants.php:132
Represents an upgrade that runs outside of the upgrade.php script.
Definition: ElggUpgrade.php:26
elgg_get_entities(array $options=[])
Fetches/counts entities or performs a calculation on their properties.
Definition: entities.php:504
runUpgrades($upgrades)
Run system and async upgrades.
CLI Progress reporter.
Definition: Progress.php:11
__construct(Locator $locator, Translator $translator, EventsService $events, Config $config, Mutex $mutex, SystemMessagesService $system_messages, Progress $progress)
Constructor.
Result of a single BatchUpgrade run.
Definition: Result.php:10
down()
Finish an upgrade process.
up()
Start an upgrade process.
getLogger()
Returns logger.
Definition: Loggable.php:37
System messages service.
run($upgrades=null)
Run the upgrade process.
elgg_clear_caches()
Clear all the registered caches.
Definition: cache.php:198
Upgrade loop Executes upgrade batches for a given duration of time.
Definition: Loop.php:15