Elgg  Version master
UpgradeService.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
9 use Elgg\Traits\Loggable;
13 use React\Promise\PromiseInterface;
14 use function React\Promise\all;
15 use React\Promise\Deferred;
16 use React\Promise\Promise;
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
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
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 }
$error
Bad request error.
Definition: 400.php:6
if(empty($guid)) $upgrade
Definition: upgrade.php:11
return[ 'admin/delete_admin_notices'=>['access'=> 'admin'], 'admin/menu/save'=>['access'=> 'admin'], 'admin/plugins/activate'=>['access'=> 'admin'], 'admin/plugins/activate_all'=>['access'=> 'admin'], 'admin/plugins/deactivate'=>['access'=> 'admin'], 'admin/plugins/deactivate_all'=>['access'=> 'admin'], 'admin/plugins/set_priority'=>['access'=> 'admin'], 'admin/security/security_txt'=>['access'=> 'admin'], 'admin/security/settings'=>['access'=> 'admin'], 'admin/security/regenerate_site_secret'=>['access'=> 'admin'], 'admin/site/cache/invalidate'=>['access'=> 'admin'], 'admin/site/flush_cache'=>['access'=> 'admin'], 'admin/site/icons'=>['access'=> 'admin'], 'admin/site/set_maintenance_mode'=>['access'=> 'admin'], 'admin/site/set_robots'=>['access'=> 'admin'], 'admin/site/theme'=>['access'=> 'admin'], 'admin/site/unlock_upgrade'=>['access'=> 'admin'], 'admin/site/settings'=>['access'=> 'admin'], 'admin/upgrade'=>['access'=> 'admin'], 'admin/upgrade/reset'=>['access'=> 'admin'], 'admin/user/ban'=>['access'=> 'admin'], 'admin/user/bulk/ban'=>['access'=> 'admin'], 'admin/user/bulk/delete'=>['access'=> 'admin'], 'admin/user/bulk/unban'=>['access'=> 'admin'], 'admin/user/bulk/validate'=>['access'=> 'admin'], 'admin/user/change_email'=>['access'=> 'admin'], 'admin/user/delete'=>['access'=> 'admin'], 'admin/user/login_as'=>['access'=> 'admin'], 'admin/user/logout_as'=>[], 'admin/user/makeadmin'=>['access'=> 'admin'], 'admin/user/resetpassword'=>['access'=> 'admin'], 'admin/user/removeadmin'=>['access'=> 'admin'], 'admin/user/unban'=>['access'=> 'admin'], 'admin/user/validate'=>['access'=> 'admin'], 'annotation/delete'=>[], 'avatar/upload'=>[], 'comment/save'=>[], 'diagnostics/download'=>['access'=> 'admin'], 'entity/chooserestoredestination'=>[], 'entity/delete'=>[], 'entity/mute'=>[], 'entity/restore'=>[], 'entity/subscribe'=>[], 'entity/trash'=>[], 'entity/unmute'=>[], 'entity/unsubscribe'=>[], 'login'=>['access'=> 'logged_out'], 'logout'=>[], 'notifications/mute'=>['access'=> 'public'], 'plugins/settings/remove'=>['access'=> 'admin'], 'plugins/settings/save'=>['access'=> 'admin'], 'plugins/usersettings/save'=>[], 'register'=>['access'=> 'logged_out', 'middleware'=>[\Elgg\Router\Middleware\RegistrationAllowedGatekeeper::class,],], 'river/delete'=>[], 'settings/notifications'=>[], 'settings/notifications/subscriptions'=>[], 'user/changepassword'=>['access'=> 'public'], 'user/requestnewpassword'=>['access'=> 'public'], 'useradd'=>['access'=> 'admin'], 'usersettings/save'=>[], 'widgets/add'=>[], 'widgets/delete'=>[], 'widgets/move'=>[], 'widgets/save'=>[],]
Definition: actions.php:73
elgg_clear_caches()
Clear all the registered caches.
Definition: cache.php:103
elgg_invalidate_caches()
Invalidate all the registered caches.
Definition: cache.php:88
Represents an upgrade that runs outside of the upgrade.php script.
Definition: ElggUpgrade.php:26
static disable()
Disables the caches in the system.
CLI Progress reporter.
Definition: Progress.php:11
Extends QueryBuilder with clauses necessary to sort entity lists by entity properties.
Provides database mutex that can be used to prevent race conditions between two processes that affect...
Definition: Mutex.php:18
Events service.
Exception thrown if an error which can only be found on runtime occurs.
System messages service.
Upgrade service for Elgg.
getCompletedUpgrades(bool $async=true)
Get completed (async) upgrades ordered by recently completed first.
run($upgrades=null)
Run the upgrade process.
down()
Finish an upgrade process.
runUpgrades($upgrades)
Run system and async upgrades.
getPendingUpgrades(bool $async=true)
Get pending async upgrades.
executeUpgrade(\ElggUpgrade $upgrade, ?int $max_duration=null)
Call the upgrade's run() for a specified period of time, or until it completes.
up()
Start an upgrade process.
__construct(protected Locator $locator, protected Translator $translator, protected EventsService $events, protected Config $config, protected Mutex $mutex, protected SystemMessagesService $system_messages, protected Progress $progress)
Constructor.
Locates and registers both core and plugin upgrades.
Definition: Locator.php:16
Upgrade loop Executes upgrade batches for a given duration of time.
Definition: Loop.php:15
Result of a single BatchUpgrade run.
Definition: Result.php:10
const ELGG_IGNORE_ACCESS
elgg_call() flags
Definition: constants.php:121
const ELGG_SHOW_DISABLED_ENTITIES
Definition: constants.php:123
const ELGG_SHOW_DELETED_ENTITIES
Definition: constants.php:127
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
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
elgg_get_entities(array $options=[])
Fetches/counts entities or performs a calculation on their properties.
Definition: entities.php:507
$value
Definition: generic.php:51
elgg_echo(string $message_key, array $args=[], string $language='')
Elgg language module Functions to manage language and translations.
Definition: languages.php:17
$mutex
Unlocks the upgrade script.
$upgrades
Lists pending upgrades.
Definition: upgrades.php:11