Elgg  Version 6.2
UpgradeService.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
14 use function React\Promise\all;
17 
24 
25  use Loggable;
26 
38  public function __construct(
39  protected Locator $locator,
40  protected Translator $translator,
41  protected EventsService $events,
42  protected Config $config,
43  protected Mutex $mutex,
44  protected SystemMessagesService $system_messages,
45  protected Progress $progress
46  ) {
47  }
48 
54  protected function up(): Promise {
55  return new Promise(function ($resolve, $reject) {
56  Application::migrate();
57 
58  if (!$this->events->triggerBefore('upgrade', 'system', null)) {
59  return $reject(new \RuntimeException($this->translator->translate('upgrade:terminated')));
60  }
61 
62  // prevent someone from running the upgrade script in parallel (see #4643)
63  if (!$this->mutex->lock('upgrade')) {
64  return $reject(new \RuntimeException($this->translator->translate('upgrade:locked')));
65  }
66 
67  // Clear system caches
70 
71  return $resolve(true);
72  });
73  }
74 
80  protected function down(): Promise {
81  return new Promise(function ($resolve, $reject) {
82  if (!$this->events->trigger('upgrade', 'system', null)) {
83  return $reject(false);
84  }
85 
87 
88  $this->mutex->unlock('upgrade');
89 
90  $this->events->triggerAfter('upgrade', 'system', null);
91 
92  return $resolve(true);
93  });
94  }
95 
103  protected function runUpgrades($upgrades): PromiseInterface {
104  $promises = [];
105 
106  foreach ($upgrades as $upgrade) {
107  if (!$upgrade instanceof \ElggUpgrade) {
108  continue;
109  }
110 
111  $promises[] = new Promise(function ($resolve, $reject) use ($upgrade) {
112  try {
113  $result = $this->executeUpgrade($upgrade, 0);
114  } catch (\Throwable $ex) {
115  return $reject($ex);
116  }
117 
118  if ($result->getFailureCount()) {
119  $msg = elgg_echo('admin:upgrades:failed', [
120  $upgrade->getDisplayName(),
121  ]);
122 
123  return $reject(new \RuntimeException($msg));
124  }
125 
126  return $resolve($result);
127  });
128  }
129 
130  return all($promises);
131  }
132 
140  public function run($upgrades = null): PromiseInterface {
141  // turn off time limit
142  set_time_limit(3600);
143 
144  $deferred = new Deferred();
145 
146  $promise = $deferred->promise();
147 
148  $resolve = function ($value) use ($deferred) {
149  $deferred->resolve($value);
150  };
151 
152  $reject = function ($error) use ($deferred) {
153  $deferred->reject($error);
154  };
155 
156  if (!isset($upgrades)) {
157  $upgrades = $this->getPendingUpgrades(false);
158  }
159 
160  $this->up()->then(
161  function () use ($resolve, $reject, $upgrades) {
162  all([
163  $this->runUpgrades($upgrades),
164  ])->finally(
165  function () use ($resolve, $reject) {
166  $this->down()->then(
167  function ($result) use ($resolve) {
168  return $resolve($result);
169  },
170  $reject
171  );
172  },
173  $reject
174  );
175  },
176  $reject
177  );
178 
179  return $promise;
180  }
181 
189  public function getPendingUpgrades(bool $async = true): array {
190  $pending = [];
191 
192  $upgrades = $this->locator->locate();
193 
194  foreach ($upgrades as $upgrade) {
195  if ($upgrade->isCompleted()) {
196  continue;
197  }
198 
199  $batch = $upgrade->getBatch();
200  if (!$batch) {
201  continue;
202  }
203 
204  $pending[] = $upgrade;
205  }
206 
207  if (!$async) {
208  $pending = array_filter($pending, function(\ElggUpgrade $upgrade) {
209  return !$upgrade->isAsynchronous();
210  });
211  }
212 
213  return $pending;
214  }
215 
223  public function getCompletedUpgrades(bool $async = true): array {
224  // make sure always to return all upgrade entities
225  return elgg_call(
227  function () use ($async) {
228  $completed = [];
229 
230  $order_by_completed_time = new EntitySortByClause();
231  $order_by_completed_time->direction = 'DESC';
232  $order_by_completed_time->property = 'completed_time';
233 
235  'type' => 'object',
236  'subtype' => 'elgg_upgrade',
237  'metadata_name' => 'class', // filters old upgrades
238  'metadata_name_value_pairs' => [
239  'name' => 'is_completed',
240  'value' => true,
241  ],
242  'order_by' => $order_by_completed_time,
243  'limit' => false,
244  'batch' => true,
245  ]);
246  /* @var $upgrade \ElggUpgrade */
247  foreach ($upgrades as $upgrade) {
248  $batch = $upgrade->getBatch();
249  if (!$batch) {
250  continue;
251  }
252 
253  $completed[] = $upgrade;
254  }
255 
256  if (!$async) {
257  $completed = array_filter($completed, function(\ElggUpgrade $upgrade) {
258  return !$upgrade->isAsynchronous();
259  });
260  }
261 
262  return $completed;
263  }
264  );
265  }
266 
276  public function executeUpgrade(\ElggUpgrade $upgrade, ?int $max_duration = null): Result {
277  // Upgrade also disabled data, so the compatibility is
278  // preserved in case the data ever gets enabled again
279  return elgg_call(
281  function () use ($upgrade, $max_duration) {
282  return $this->events->triggerResultsSequence('upgrade:execute', 'system', ['object' => $upgrade], null, function() use ($upgrade, $max_duration) {
283  $result = new Result();
284 
285  $loop = new Loop(
286  $upgrade,
287  $result,
288  $this->progress,
289  $this->getLogger()
290  );
291 
292  $loop->loop($max_duration);
293 
294  return $result;
295  });
296  }
297  );
298  }
299 }
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:306
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
$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.
getPendingUpgrades(bool $async=true)
Get pending async upgrades.
Provides database mutex that can be used to prevent race conditions between two processes that affect...
Definition: Mutex.php:18
Extends QueryBuilder with clauses necessary 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:88
$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:121
$error
Bad request error.
Definition: 400.php:6
if(empty($guid)) $upgrade
Definition: upgrade.php:11
const ELGG_SHOW_DISABLED_ENTITIES
Definition: constants.php:123
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:507
runUpgrades($upgrades)
Run system and async upgrades.
CLI Progress reporter.
Definition: Progress.php:11
Result of a single BatchUpgrade run.
Definition: Result.php:10
const ELGG_SHOW_DELETED_ENTITIES
Definition: constants.php:127
down()
Finish an upgrade process.
up()
Start an upgrade process.
getLogger()
Returns logger.
Definition: Loggable.php:37
__construct(protected Locator $locator, protected Translator $translator, protected EventsService $events, protected Config $config, protected Mutex $mutex, protected SystemMessagesService $system_messages, protected Progress $progress)
Constructor.
executeUpgrade(\ElggUpgrade $upgrade,?int $max_duration=null)
Call the upgrade&#39;s run() for a specified period of time, or until it completes.
System messages service.
run($upgrades=null)
Run the upgrade process.
getCompletedUpgrades(bool $async=true)
Get completed (async) upgrades ordered by recently completed first.
elgg_clear_caches()
Clear all the registered caches.
Definition: cache.php:103
Upgrade loop Executes upgrade batches for a given duration of time.
Definition: Loop.php:15