Completed
Push — master ( f9b24c...50df29 )
by Morris
48:33 queued 23:11
created

Updater::waitForCronToFinish()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 * @copyright Copyright (c) 2016, Lukas Reschke <[email protected]>
5
 *
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Bjoern Schiessle <[email protected]>
8
 * @author Frank Karlitschek <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Lukas Reschke <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Robin Appelman <[email protected]>
13
 * @author Roeland Jago Douma <[email protected]>
14
 * @author Steffen Lindner <[email protected]>
15
 * @author Thomas Müller <[email protected]>
16
 * @author Victor Dubiniuk <[email protected]>
17
 * @author Vincent Petry <[email protected]>
18
 *
19
 * @license AGPL-3.0
20
 *
21
 * This code is free software: you can redistribute it and/or modify
22
 * it under the terms of the GNU Affero General Public License, version 3,
23
 * as published by the Free Software Foundation.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
 * GNU Affero General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Affero General Public License, version 3,
31
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
32
 *
33
 */
34
35
namespace OC;
36
37
use OC\DB\MigrationService;
38
use OC\Hooks\BasicEmitter;
39
use OC\IntegrityCheck\Checker;
40
use OC_App;
41
use OCP\BackgroundJob\IJobList;
42
use OCP\IConfig;
43
use OCP\ILogger;
44
use OCP\Util;
45
use Symfony\Component\EventDispatcher\GenericEvent;
46
47
/**
48
 * Class that handles autoupdating of ownCloud
49
 *
50
 * Hooks provided in scope \OC\Updater
51
 *  - maintenanceStart()
52
 *  - maintenanceEnd()
53
 *  - dbUpgrade()
54
 *  - failure(string $message)
55
 */
56
class Updater extends BasicEmitter {
57
58
	/** @var ILogger $log */
59
	private $log;
60
61
	/** @var IConfig */
62
	private $config;
63
64
	/** @var Checker */
65
	private $checker;
66
67
	/** @var Installer */
68
	private $installer;
69
70
	/** @var IJobList */
71
	private $jobList;
72
73
	private $logLevelNames = [
74
		0 => 'Debug',
75
		1 => 'Info',
76
		2 => 'Warning',
77
		3 => 'Error',
78
		4 => 'Fatal',
79
	];
80
81
	public function __construct(IConfig $config,
82
								Checker $checker,
83
								ILogger $log,
84
								Installer $installer,
85
								IJobList $jobList) {
86
		$this->log = $log;
87
		$this->config = $config;
88
		$this->checker = $checker;
89
		$this->installer = $installer;
90
		$this->jobList = $jobList;
91
	}
92
93
	/**
94
	 * runs the update actions in maintenance mode, does not upgrade the source files
95
	 * except the main .htaccess file
96
	 *
97
	 * @return bool true if the operation succeeded, false otherwise
98
	 */
99
	public function upgrade() {
100
		$this->emitRepairEvents();
101
		$this->logAllEvents();
102
103
		$logLevel = $this->config->getSystemValue('loglevel', ILogger::WARN);
104
		$this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
105
		$this->config->setSystemValue('loglevel', ILogger::DEBUG);
106
107
		$wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false);
108
109
		if(!$wasMaintenanceModeEnabled) {
110
			$this->config->setSystemValue('maintenance', true);
111
			$this->emit('\OC\Updater', 'maintenanceEnabled');
112
		}
113
114
		$this->waitForCronToFinish();
115
116
		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
117
		$currentVersion = implode('.', \OCP\Util::getVersion());
118
		$this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core'));
119
120
		$success = true;
121
		try {
122
			$this->doUpgrade($currentVersion, $installedVersion);
123
		} catch (HintException $exception) {
124
			$this->log->logException($exception, ['app' => 'core']);
0 ignored issues
show
Documentation introduced by
$exception is of type object<OC\HintException>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
125
			$this->emit('\OC\Updater', 'failure', array($exception->getMessage() . ': ' .$exception->getHint()));
126
			$success = false;
127
		} catch (\Exception $exception) {
128
			$this->log->logException($exception, ['app' => 'core']);
0 ignored issues
show
Documentation introduced by
$exception is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
129
			$this->emit('\OC\Updater', 'failure', array(get_class($exception) . ': ' .$exception->getMessage()));
130
			$success = false;
131
		}
132
133
		$this->emit('\OC\Updater', 'updateEnd', array($success));
134
135
		if(!$wasMaintenanceModeEnabled && $success) {
136
			$this->config->setSystemValue('maintenance', false);
137
			$this->emit('\OC\Updater', 'maintenanceDisabled');
138
		} else {
139
			$this->emit('\OC\Updater', 'maintenanceActive');
140
		}
141
142
		$this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
143
		$this->config->setSystemValue('loglevel', $logLevel);
144
		$this->config->setSystemValue('installed', true);
145
146
		return $success;
147
	}
148
149
	/**
150
	 * Return version from which this version is allowed to upgrade from
151
	 *
152
	 * @return array allowed previous versions per vendor
153
	 */
154
	private function getAllowedPreviousVersions() {
155
		// this should really be a JSON file
156
		require \OC::$SERVERROOT . '/version.php';
157
		/** @var array $OC_VersionCanBeUpgradedFrom */
158
		return $OC_VersionCanBeUpgradedFrom;
0 ignored issues
show
Bug introduced by
The variable $OC_VersionCanBeUpgradedFrom does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
159
	}
160
161
	/**
162
	 * Return vendor from which this version was published
163
	 *
164
	 * @return string Get the vendor
165
	 */
166
	private function getVendor() {
167
		// this should really be a JSON file
168
		require \OC::$SERVERROOT . '/version.php';
169
		/** @var string $vendor */
170
		return (string) $vendor;
0 ignored issues
show
Bug introduced by
The variable $vendor does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
171
	}
172
173
	/**
174
	 * Whether an upgrade to a specified version is possible
175
	 * @param string $oldVersion
176
	 * @param string $newVersion
177
	 * @param array $allowedPreviousVersions
178
	 * @return bool
179
	 */
180
	public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
181
		$version = explode('.', $oldVersion);
182
		$majorMinor = $version[0] . '.' . $version[1];
183
184
		$currentVendor = $this->config->getAppValue('core', 'vendor', '');
185
186
		// Vendor was not set correctly on install, so we have to white-list known versions
187
		if ($currentVendor === '' && isset($allowedPreviousVersions['owncloud'][$oldVersion])) {
188
			$currentVendor = 'owncloud';
189
		}
190
191
		if ($currentVendor === 'nextcloud') {
192
			return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
193
				&& (version_compare($oldVersion, $newVersion, '<=') ||
194
					$this->config->getSystemValue('debug', false));
195
		}
196
197
		// Check if the instance can be migrated
198
		return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) ||
199
			isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
200
	}
201
202
	/**
203
	 * runs the update actions in maintenance mode, does not upgrade the source files
204
	 * except the main .htaccess file
205
	 *
206
	 * @param string $currentVersion current version to upgrade to
207
	 * @param string $installedVersion previous version from which to upgrade from
208
	 *
209
	 * @throws \Exception
210
	 */
211
	private function doUpgrade($currentVersion, $installedVersion) {
212
		// Stop update if the update is over several major versions
213
		$allowedPreviousVersions = $this->getAllowedPreviousVersions();
214
		if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) {
215
			throw new \Exception('Updates between multiple major versions and downgrades are unsupported.');
216
		}
217
218
		// Update .htaccess files
219
		try {
220
			Setup::updateHtaccess();
221
			Setup::protectDataDirectory();
222
		} catch (\Exception $e) {
223
			throw new \Exception($e->getMessage());
224
		}
225
226
		// create empty file in data dir, so we can later find
227
		// out that this is indeed an ownCloud data directory
228
		// (in case it didn't exist before)
229
		file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
230
231
		// pre-upgrade repairs
232
		$repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
233
		$repair->run();
234
235
		$this->doCoreUpgrade();
236
237
		try {
238
			// TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378
239
			Setup::installBackgroundJobs();
240
		} catch (\Exception $e) {
241
			throw new \Exception($e->getMessage());
242
		}
243
244
		// update all shipped apps
245
		$this->checkAppsRequirements();
246
		$this->doAppUpgrade();
247
248
		// Update the appfetchers version so it downloads the correct list from the appstore
249
		\OC::$server->getAppFetcher()->setVersion($currentVersion);
250
251
		// upgrade appstore apps
252
		$this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps());
253
254
		// install new shipped apps on upgrade
255
		OC_App::loadApps(['authentication']);
256
		$errors = Installer::installShippedApps(true);
257
		foreach ($errors as $appId => $exception) {
258
			/** @var \Exception $exception */
259
			$this->log->logException($exception, ['app' => $appId]);
0 ignored issues
show
Documentation introduced by
$exception is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
260
			$this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
261
		}
262
263
		// post-upgrade repairs
264
		$repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher());
265
		$repair->run();
266
267
		//Invalidate update feed
268
		$this->config->setAppValue('core', 'lastupdatedat', 0);
269
270
		// Check for code integrity if not disabled
271
		if(\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
272
			$this->emit('\OC\Updater', 'startCheckCodeIntegrity');
273
			$this->checker->runInstanceVerification();
274
			$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
275
		}
276
277
		// only set the final version if everything went well
278
		$this->config->setSystemValue('version', implode('.', Util::getVersion()));
279
		$this->config->setAppValue('core', 'vendor', $this->getVendor());
280
	}
281
282
	protected function doCoreUpgrade() {
283
		$this->emit('\OC\Updater', 'dbUpgradeBefore');
284
285
		// execute core migrations
286
		$ms = new MigrationService('core', \OC::$server->getDatabaseConnection());
287
		$ms->migrate();
288
289
		$this->emit('\OC\Updater', 'dbUpgrade');
290
	}
291
292
	/**
293
	 * @param string $version the oc version to check app compatibility with
294
	 */
295
	protected function checkAppUpgrade($version) {
296
		$apps = \OC_App::getEnabledApps();
297
		$this->emit('\OC\Updater', 'appUpgradeCheckBefore');
298
299
		$appManager = \OC::$server->getAppManager();
300
		foreach ($apps as $appId) {
301
			$info = \OC_App::getAppInfo($appId);
302
			$compatible = \OC_App::isAppCompatible($version, $info);
0 ignored issues
show
Bug introduced by
It seems like $info defined by \OC_App::getAppInfo($appId) on line 301 can also be of type null; however, OC_App::isAppCompatible() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
303
			$isShipped = $appManager->isShipped($appId);
304
305
			if ($compatible && $isShipped && \OC_App::shouldUpgrade($appId)) {
306
				/**
307
				 * FIXME: The preupdate check is performed before the database migration, otherwise database changes
308
				 * are not possible anymore within it. - Consider this when touching the code.
309
				 * @link https://github.com/owncloud/core/issues/10980
310
				 * @see \OC_App::updateApp
311
				 */
312
				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/preupdate.php')) {
313
					$this->includePreUpdate($appId);
314
				}
315
				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
316
					$this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
317
					\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
0 ignored issues
show
Bug introduced by
The method simulateUpdateDbFromStructure() does not exist on OC_DB. Did you maybe mean updateDbFromStructure()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
318
				}
319
			}
320
		}
321
322
		$this->emit('\OC\Updater', 'appUpgradeCheck');
323
	}
324
325
	/**
326
	 * Includes the pre-update file. Done here to prevent namespace mixups.
327
	 * @param string $appId
328
	 */
329
	private function includePreUpdate($appId) {
330
		include \OC_App::getAppPath($appId) . '/appinfo/preupdate.php';
331
	}
332
333
	/**
334
	 * upgrades all apps within a major ownCloud upgrade. Also loads "priority"
335
	 * (types authentication, filesystem, logging, in that order) afterwards.
336
	 *
337
	 * @throws NeedsUpdateException
338
	 */
339
	protected function doAppUpgrade() {
340
		$apps = \OC_App::getEnabledApps();
341
		$priorityTypes = array('authentication', 'filesystem', 'logging');
342
		$pseudoOtherType = 'other';
343
		$stacks = array($pseudoOtherType => array());
344
345
		foreach ($apps as $appId) {
346
			$priorityType = false;
347
			foreach ($priorityTypes as $type) {
348
				if(!isset($stacks[$type])) {
349
					$stacks[$type] = array();
350
				}
351
				if (\OC_App::isType($appId, [$type])) {
352
					$stacks[$type][] = $appId;
353
					$priorityType = true;
354
					break;
355
				}
356
			}
357
			if (!$priorityType) {
358
				$stacks[$pseudoOtherType][] = $appId;
359
			}
360
		}
361
		foreach ($stacks as $type => $stack) {
362
			foreach ($stack as $appId) {
363
				if (\OC_App::shouldUpgrade($appId)) {
364
					$this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]);
365
					\OC_App::updateApp($appId);
366
					$this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
367
				}
368
				if($type !== $pseudoOtherType) {
369
					// load authentication, filesystem and logging apps after
370
					// upgrading them. Other apps my need to rely on modifying
371
					// user and/or filesystem aspects.
372
					\OC_App::loadApp($appId);
373
				}
374
			}
375
		}
376
	}
377
378
	/**
379
	 * check if the current enabled apps are compatible with the current
380
	 * ownCloud version. disable them if not.
381
	 * This is important if you upgrade ownCloud and have non ported 3rd
382
	 * party apps installed.
383
	 *
384
	 * @return array
385
	 * @throws \Exception
386
	 */
387
	private function checkAppsRequirements() {
388
		$isCoreUpgrade = $this->isCodeUpgrade();
389
		$apps = OC_App::getEnabledApps();
390
		$version = implode('.', Util::getVersion());
391
		$disabledApps = [];
392
		$appManager = \OC::$server->getAppManager();
393
		foreach ($apps as $app) {
394
			// check if the app is compatible with this version of ownCloud
395
			$info = OC_App::getAppInfo($app);
396
			if($info === null || !OC_App::isAppCompatible($version, $info)) {
397
				if ($appManager->isShipped($app)) {
398
					throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
399
				}
400
				\OC::$server->getAppManager()->disableApp($app);
401
				$this->emit('\OC\Updater', 'incompatibleAppDisabled', array($app));
402
			}
403
			// no need to disable any app in case this is a non-core upgrade
404
			if (!$isCoreUpgrade) {
405
				continue;
406
			}
407
			// shipped apps will remain enabled
408
			if ($appManager->isShipped($app)) {
409
				continue;
410
			}
411
			// authentication and session apps will remain enabled as well
412
			if (OC_App::isType($app, ['session', 'authentication'])) {
413
				continue;
414
			}
415
		}
416
		return $disabledApps;
417
	}
418
419
	/**
420
	 * @return bool
421
	 */
422
	private function isCodeUpgrade() {
423
		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
424
		$currentVersion = implode('.', Util::getVersion());
425
		if (version_compare($currentVersion, $installedVersion, '>')) {
426
			return true;
427
		}
428
		return false;
429
	}
430
431
	/**
432
	 * @param array $disabledApps
433
	 * @throws \Exception
434
	 */
435
	private function upgradeAppStoreApps(array $disabledApps) {
436
		foreach($disabledApps as $app) {
437
			try {
438
				$this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
439
				if ($this->installer->isUpdateAvailable($app)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->installer->isUpdateAvailable($app) of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
440
					$this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]);
441
					$this->installer->updateAppstoreApp($app);
442
				}
443
				$this->emit('\OC\Updater', 'checkAppStoreApp', [$app]);
444
			} catch (\Exception $ex) {
445
				$this->log->logException($ex, ['app' => 'core']);
0 ignored issues
show
Documentation introduced by
$ex is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
446
			}
447
		}
448
	}
449
450
	/**
451
	 * Forward messages emitted by the repair routine
452
	 */
453
	private function emitRepairEvents() {
454
		$dispatcher = \OC::$server->getEventDispatcher();
455
		$dispatcher->addListener('\OC\Repair::warning', function ($event) {
456
			if ($event instanceof GenericEvent) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\EventDispatcher\GenericEvent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
457
				$this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
458
			}
459
		});
460
		$dispatcher->addListener('\OC\Repair::error', function ($event) {
461
			if ($event instanceof GenericEvent) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\EventDispatcher\GenericEvent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
462
				$this->emit('\OC\Updater', 'repairError', $event->getArguments());
463
			}
464
		});
465
		$dispatcher->addListener('\OC\Repair::info', function ($event) {
466
			if ($event instanceof GenericEvent) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\EventDispatcher\GenericEvent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
467
				$this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
468
			}
469
		});
470
		$dispatcher->addListener('\OC\Repair::step', function ($event) {
471
			if ($event instanceof GenericEvent) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\EventDispatcher\GenericEvent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
472
				$this->emit('\OC\Updater', 'repairStep', $event->getArguments());
473
			}
474
		});
475
	}
476
477
	private function logAllEvents() {
478
		$log = $this->log;
479
480
		$dispatcher = \OC::$server->getEventDispatcher();
481 View Code Duplication
		$dispatcher->addListener('\OC\DB\Migrator::executeSql', function($event) use ($log) {
482
			if (!$event instanceof GenericEvent) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\EventDispatcher\GenericEvent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
483
				return;
484
			}
485
			$log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
486
		});
487 View Code Duplication
		$dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($log) {
488
			if (!$event instanceof GenericEvent) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\EventDispatcher\GenericEvent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
489
				return;
490
			}
491
			$log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
492
		});
493
494
		$repairListener = function($event) use ($log) {
495
			if (!$event instanceof GenericEvent) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\EventDispatcher\GenericEvent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
496
				return;
497
			}
498
			switch ($event->getSubject()) {
499
				case '\OC\Repair::startProgress':
500
					$log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
501
					break;
502
				case '\OC\Repair::advance':
503
					$desc = $event->getArgument(1);
504
					if (empty($desc)) {
505
						$desc = '';
506
					}
507
					$log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
508
509
					break;
510
				case '\OC\Repair::finishProgress':
511
					$log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
512
					break;
513
				case '\OC\Repair::step':
514
					$log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
515
					break;
516
				case '\OC\Repair::info':
517
					$log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
518
					break;
519
				case '\OC\Repair::warning':
520
					$log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
521
					break;
522
				case '\OC\Repair::error':
523
					$log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
524
					break;
525
			}
526
		};
527
528
		$dispatcher->addListener('\OC\Repair::startProgress', $repairListener);
529
		$dispatcher->addListener('\OC\Repair::advance', $repairListener);
530
		$dispatcher->addListener('\OC\Repair::finishProgress', $repairListener);
531
		$dispatcher->addListener('\OC\Repair::step', $repairListener);
532
		$dispatcher->addListener('\OC\Repair::info', $repairListener);
533
		$dispatcher->addListener('\OC\Repair::warning', $repairListener);
534
		$dispatcher->addListener('\OC\Repair::error', $repairListener);
535
536
537
		$this->listen('\OC\Updater', 'maintenanceEnabled', function () use($log) {
538
			$log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
539
		});
540
		$this->listen('\OC\Updater', 'maintenanceDisabled', function () use($log) {
541
			$log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
542
		});
543
		$this->listen('\OC\Updater', 'maintenanceActive', function () use($log) {
544
			$log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
545
		});
546
		$this->listen('\OC\Updater', 'updateEnd', function ($success) use($log) {
547
			if ($success) {
548
				$log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
549
			} else {
550
				$log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
551
			}
552
		});
553
		$this->listen('\OC\Updater', 'dbUpgradeBefore', function () use($log) {
554
			$log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
555
		});
556
		$this->listen('\OC\Updater', 'dbUpgrade', function () use($log) {
557
			$log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
558
		});
559
		$this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($log) {
560
			$log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
561
		});
562
		$this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($log) {
563
			$log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
564
		});
565
		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($log) {
566
			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
567
		});
568
		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use($log) {
569
			$log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
570
		});
571
		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($log) {
572
			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
573
		});
574
		$this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use($log) {
575
			$log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
576
		});
577
		$this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
578
			$log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
579
		});
580
		$this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
581
			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
582
		});
583
		$this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
584
			$log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
585
		});
586
		$this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
587
			$log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
588
		});
589
		$this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
590
			$log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
591
		});
592
		$this->listen('\OC\Updater', 'failure', function ($message) use($log) {
593
			$log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
594
		});
595
		$this->listen('\OC\Updater', 'setDebugLogLevel', function () use($log) {
596
			$log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
597
		});
598
		$this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($log) {
599
			$log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
600
		});
601
		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($log) {
602
			$log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
603
		});
604
		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($log) {
605
			$log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
606
		});
607
608
	}
609
	private function waitForCronToFinish() {
610
		while ($this->jobList->isAnyJobRunning()) {
611
			$this->emit('\OC\Updater', 'waitForCronToFinish');
612
			sleep(5);
613
		}
614
	}
615
616
}
617
618