Elgg  Version master
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 
37  public function __construct(
38  protected Locator $locator,
39  protected Translator $translator,
40  protected EventsService $events,
41  protected Config $config,
42  protected Mutex $mutex,
43  protected SystemMessagesService $system_messages,
44  protected Progress $progress
45  ) {
46  }
47 
53  protected function up(): Promise {
54  return new Promise(function ($resolve, $reject) {
55  Application::migrate();
56 
57  if (!$this->events->triggerBefore('upgrade', 'system', null)) {
58  return $reject(new \RuntimeException($this->translator->translate('upgrade:terminated')));
59  }
60 
61  // prevent someone from running the upgrade script in parallel (see #4643)
62  if (!$this->mutex->lock('upgrade')) {
63  return $reject(new \RuntimeException($this->translator->translate('upgrade:locked')));
64  }
65 
66  // Clear system caches
69 
70  return $resolve($resolve);
71  });
72  }
73 
79  protected function down(): Promise {
80  return new Promise(function ($resolve, $reject) {
81  if (!$this->events->trigger('upgrade', 'system', null)) {
82  return $reject();
83  }
84 
86 
87  $this->mutex->unlock('upgrade');
88 
89  $this->events->triggerAfter('upgrade', 'system', null);
90 
91  return $resolve($resolve);
92  });
93  }
94 
102  protected function runUpgrades($upgrades): Promise {
103  $promises = [];
104 
105  foreach ($upgrades as $upgrade) {
106  if (!$upgrade instanceof \ElggUpgrade) {
107  continue;
108  }
109 
110  $promises[] = new Promise(function ($resolve, $reject) use ($upgrade) {
111  try {
112  $result = $this->executeUpgrade($upgrade, 0);
113  } catch (\Throwable $ex) {
114  return $reject($ex);
115  }
116 
117  if ($result->getFailureCount()) {
118  $msg = elgg_echo('admin:upgrades:failed', [
119  $upgrade->getDisplayName(),
120  ]);
121 
122  return $reject(new \RuntimeException($msg));
123  } else {
124  return $resolve($result);
125  }
126  });
127  }
128 
129  return all($promises);
130  }
131 
139  public function run($upgrades = null): Promise {
140  // turn off time limit
141  set_time_limit(3600);
142 
143  $deferred = new Deferred();
144 
145  $promise = $deferred->promise();
146 
147  $resolve = function ($value) use ($deferred) {
148  $deferred->resolve($value);
149  };
150 
151  $reject = function ($error) use ($deferred) {
152  $deferred->reject($error);
153  };
154 
155  if (!isset($upgrades)) {
156  $upgrades = $this->getPendingUpgrades(false);
157  }
158 
159  $this->up()->then(
160  function () use ($resolve, $reject, $upgrades) {
161  all([
162  $this->runUpgrades($upgrades),
163  ])->then(
164  function () use ($resolve, $reject) {
165  $this->down()->then(
166  function ($result) use ($resolve) {
167  return $resolve($result);
168  },
169  $reject
170  );
171  },
172  $reject
173  );
174  },
175  $reject
176  );
177 
178  return $promise;
179  }
180 
188  public function getPendingUpgrades(bool $async = true): array {
189  $pending = [];
190 
191  $upgrades = $this->locator->locate();
192 
193  foreach ($upgrades as $upgrade) {
194  if ($upgrade->isCompleted()) {
195  continue;
196  }
197 
198  $batch = $upgrade->getBatch();
199  if (!$batch) {
200  continue;
201  }
202 
203  $pending[] = $upgrade;
204  }
205 
206  if (!$async) {
207  $pending = array_filter($pending, function(\ElggUpgrade $upgrade) {
208  return !$upgrade->isAsynchronous();
209  });
210  }
211 
212  return $pending;
213  }
214 
222  public function getCompletedUpgrades(bool $async = true): array {
223  // make sure always to return all upgrade entities
224  return elgg_call(
226  function () use ($async) {
227  $completed = [];
228 
229  $order_by_completed_time = new EntitySortByClause();
230  $order_by_completed_time->direction = 'DESC';
231  $order_by_completed_time->property = 'completed_time';
232 
234  'type' => 'object',
235  'subtype' => 'elgg_upgrade',
236  'metadata_name' => 'class', // filters old upgrades
237  'metadata_name_value_pairs' => [
238  'name' => 'is_completed',
239  'value' => true,
240  ],
241  'order_by' => $order_by_completed_time,
242  'limit' => false,
243  'batch' => true,
244  ]);
245  /* @var $upgrade \ElggUpgrade */
246  foreach ($upgrades as $upgrade) {
247  $batch = $upgrade->getBatch();
248  if (!$batch) {
249  continue;
250  }
251 
252  $completed[] = $upgrade;
253  }
254 
255  if (!$async) {
256  $completed = array_filter($completed, function(\ElggUpgrade $upgrade) {
257  return !$upgrade->isAsynchronous();
258  });
259  }
260 
261  return $completed;
262  }
263  );
264  }
265 
275  public function executeUpgrade(\ElggUpgrade $upgrade, int $max_duration = null): Result {
276  // Upgrade also disabled data, so the compatibility is
277  // preserved in case the data ever gets enabled again
278  return elgg_call(
280  function () use ($upgrade, $max_duration) {
281  return $this->events->triggerResultsSequence('upgrade:execute', 'system', ['object' => $upgrade], null, function() use ($upgrade, $max_duration) {
282  $result = new Result();
283 
284  $loop = new Loop(
285  $upgrade,
286  $result,
287  $this->progress,
288  $this->getLogger()
289  );
290 
291  $loop->loop($max_duration);
292 
293  return $result;
294  });
295  }
296  );
297  }
298 }
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:304
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:174
$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
if(empty($guid)) $upgrade
Definition: upgrade.php:11
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: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:136
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.
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:189
executeUpgrade(\ElggUpgrade $upgrade, int $max_duration=null)
Call the upgrade&#39;s run() for a specified period of time, or until it completes.
Upgrade loop Executes upgrade batches for a given duration of time.
Definition: Loop.php:15