Total Complexity | 69 |
Total Lines | 524 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like Updater often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Updater, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
57 | class Updater extends BasicEmitter { |
||
|
|||
58 | |||
59 | /** @var ILogger $log */ |
||
60 | private $log; |
||
61 | |||
62 | /** @var IConfig */ |
||
63 | private $config; |
||
64 | |||
65 | /** @var Checker */ |
||
66 | private $checker; |
||
67 | |||
68 | /** @var Installer */ |
||
69 | private $installer; |
||
70 | |||
71 | private $logLevelNames = [ |
||
72 | 0 => 'Debug', |
||
73 | 1 => 'Info', |
||
74 | 2 => 'Warning', |
||
75 | 3 => 'Error', |
||
76 | 4 => 'Fatal', |
||
77 | ]; |
||
78 | |||
79 | /** |
||
80 | * @param IConfig $config |
||
81 | * @param Checker $checker |
||
82 | * @param ILogger $log |
||
83 | * @param Installer $installer |
||
84 | */ |
||
85 | public function __construct(IConfig $config, |
||
86 | Checker $checker, |
||
87 | ILogger $log = null, |
||
88 | Installer $installer) { |
||
89 | $this->log = $log; |
||
90 | $this->config = $config; |
||
91 | $this->checker = $checker; |
||
92 | $this->installer = $installer; |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * runs the update actions in maintenance mode, does not upgrade the source files |
||
97 | * except the main .htaccess file |
||
98 | * |
||
99 | * @return bool true if the operation succeeded, false otherwise |
||
100 | */ |
||
101 | public function upgrade() { |
||
102 | $this->emitRepairEvents(); |
||
103 | $this->logAllEvents(); |
||
104 | |||
105 | $logLevel = $this->config->getSystemValue('loglevel', ILogger::WARN); |
||
106 | $this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); |
||
107 | $this->config->setSystemValue('loglevel', ILogger::DEBUG); |
||
108 | |||
109 | $wasMaintenanceModeEnabled = $this->config->getSystemValueBool('maintenance'); |
||
110 | |||
111 | if (!$wasMaintenanceModeEnabled) { |
||
112 | $this->config->setSystemValue('maintenance', true); |
||
113 | $this->emit('\OC\Updater', 'maintenanceEnabled'); |
||
114 | } |
||
115 | |||
116 | // Clear CAN_INSTALL file if not on git |
||
117 | if (\OC_Util::getChannel() !== 'git' && is_file(\OC::$configDir.'/CAN_INSTALL')) { |
||
118 | if (!unlink(\OC::$configDir . '/CAN_INSTALL')) { |
||
119 | $this->log->error('Could not cleanup CAN_INSTALL from your config folder. Please remove this file manually.'); |
||
120 | } |
||
121 | } |
||
122 | |||
123 | $installedVersion = $this->config->getSystemValue('version', '0.0.0'); |
||
124 | $currentVersion = implode('.', \OCP\Util::getVersion()); |
||
125 | |||
126 | $this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, ['app' => 'core']); |
||
127 | |||
128 | $success = true; |
||
129 | try { |
||
130 | $this->doUpgrade($currentVersion, $installedVersion); |
||
131 | } catch (HintException $exception) { |
||
132 | $this->log->logException($exception, ['app' => 'core']); |
||
133 | $this->emit('\OC\Updater', 'failure', [$exception->getMessage() . ': ' .$exception->getHint()]); |
||
134 | $success = false; |
||
135 | } catch (\Exception $exception) { |
||
136 | $this->log->logException($exception, ['app' => 'core']); |
||
137 | $this->emit('\OC\Updater', 'failure', [get_class($exception) . ': ' .$exception->getMessage()]); |
||
138 | $success = false; |
||
139 | } |
||
140 | |||
141 | $this->emit('\OC\Updater', 'updateEnd', [$success]); |
||
142 | |||
143 | if (!$wasMaintenanceModeEnabled && $success) { |
||
144 | $this->config->setSystemValue('maintenance', false); |
||
145 | $this->emit('\OC\Updater', 'maintenanceDisabled'); |
||
146 | } else { |
||
147 | $this->emit('\OC\Updater', 'maintenanceActive'); |
||
148 | } |
||
149 | |||
150 | $this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); |
||
151 | $this->config->setSystemValue('loglevel', $logLevel); |
||
152 | $this->config->setSystemValue('installed', true); |
||
153 | |||
154 | return $success; |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * Return version from which this version is allowed to upgrade from |
||
159 | * |
||
160 | * @return array allowed previous versions per vendor |
||
161 | */ |
||
162 | private function getAllowedPreviousVersions() { |
||
163 | // this should really be a JSON file |
||
164 | require \OC::$SERVERROOT . '/version.php'; |
||
165 | /** @var array $OC_VersionCanBeUpgradedFrom */ |
||
166 | return $OC_VersionCanBeUpgradedFrom; |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * Return vendor from which this version was published |
||
171 | * |
||
172 | * @return string Get the vendor |
||
173 | */ |
||
174 | private function getVendor() { |
||
175 | // this should really be a JSON file |
||
176 | require \OC::$SERVERROOT . '/version.php'; |
||
177 | /** @var string $vendor */ |
||
178 | return (string) $vendor; |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * Whether an upgrade to a specified version is possible |
||
183 | * @param string $oldVersion |
||
184 | * @param string $newVersion |
||
185 | * @param array $allowedPreviousVersions |
||
186 | * @return bool |
||
187 | */ |
||
188 | public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) { |
||
189 | $version = explode('.', $oldVersion); |
||
190 | $majorMinor = $version[0] . '.' . $version[1]; |
||
191 | |||
192 | $currentVendor = $this->config->getAppValue('core', 'vendor', ''); |
||
193 | |||
194 | // Vendor was not set correctly on install, so we have to white-list known versions |
||
195 | if ($currentVendor === '' && isset($allowedPreviousVersions['owncloud'][$oldVersion])) { |
||
196 | $currentVendor = 'owncloud'; |
||
197 | } |
||
198 | |||
199 | if ($currentVendor === 'nextcloud') { |
||
200 | return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) |
||
201 | && (version_compare($oldVersion, $newVersion, '<=') || |
||
202 | $this->config->getSystemValue('debug', false)); |
||
203 | } |
||
204 | |||
205 | // Check if the instance can be migrated |
||
206 | return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) || |
||
207 | isset($allowedPreviousVersions[$currentVendor][$oldVersion]); |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * runs the update actions in maintenance mode, does not upgrade the source files |
||
212 | * except the main .htaccess file |
||
213 | * |
||
214 | * @param string $currentVersion current version to upgrade to |
||
215 | * @param string $installedVersion previous version from which to upgrade from |
||
216 | * |
||
217 | * @throws \Exception |
||
218 | */ |
||
219 | private function doUpgrade($currentVersion, $installedVersion) { |
||
290 | } |
||
291 | |||
292 | protected function doCoreUpgrade() { |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * upgrades all apps within a major ownCloud upgrade. Also loads "priority" |
||
304 | * (types authentication, filesystem, logging, in that order) afterwards. |
||
305 | * |
||
306 | * @throws NeedsUpdateException |
||
307 | */ |
||
308 | protected function doAppUpgrade() { |
||
309 | $apps = \OC_App::getEnabledApps(); |
||
310 | $priorityTypes = ['authentication', 'filesystem', 'logging']; |
||
311 | $pseudoOtherType = 'other'; |
||
312 | $stacks = [$pseudoOtherType => []]; |
||
313 | |||
314 | foreach ($apps as $appId) { |
||
315 | $priorityType = false; |
||
316 | foreach ($priorityTypes as $type) { |
||
317 | if (!isset($stacks[$type])) { |
||
318 | $stacks[$type] = []; |
||
319 | } |
||
320 | if (\OC_App::isType($appId, [$type])) { |
||
321 | $stacks[$type][] = $appId; |
||
322 | $priorityType = true; |
||
323 | break; |
||
324 | } |
||
325 | } |
||
326 | if (!$priorityType) { |
||
327 | $stacks[$pseudoOtherType][] = $appId; |
||
328 | } |
||
329 | } |
||
330 | foreach ($stacks as $type => $stack) { |
||
331 | foreach ($stack as $appId) { |
||
332 | if (\OC_App::shouldUpgrade($appId)) { |
||
333 | $this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]); |
||
334 | \OC_App::updateApp($appId); |
||
335 | $this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]); |
||
336 | } |
||
337 | if ($type !== $pseudoOtherType) { |
||
338 | // load authentication, filesystem and logging apps after |
||
339 | // upgrading them. Other apps my need to rely on modifying |
||
340 | // user and/or filesystem aspects. |
||
341 | \OC_App::loadApp($appId); |
||
342 | } |
||
343 | } |
||
344 | } |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * check if the current enabled apps are compatible with the current |
||
349 | * ownCloud version. disable them if not. |
||
350 | * This is important if you upgrade ownCloud and have non ported 3rd |
||
351 | * party apps installed. |
||
352 | * |
||
353 | * @return array |
||
354 | * @throws \Exception |
||
355 | */ |
||
356 | private function checkAppsRequirements() { |
||
357 | $isCoreUpgrade = $this->isCodeUpgrade(); |
||
358 | $apps = OC_App::getEnabledApps(); |
||
359 | $version = implode('.', Util::getVersion()); |
||
360 | $disabledApps = []; |
||
361 | $appManager = \OC::$server->getAppManager(); |
||
362 | foreach ($apps as $app) { |
||
363 | // check if the app is compatible with this version of ownCloud |
||
364 | $info = OC_App::getAppInfo($app); |
||
365 | if ($info === null || !OC_App::isAppCompatible($version, $info)) { |
||
366 | if ($appManager->isShipped($app)) { |
||
367 | throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update'); |
||
368 | } |
||
369 | \OC::$server->getAppManager()->disableApp($app, true); |
||
370 | $this->emit('\OC\Updater', 'incompatibleAppDisabled', [$app]); |
||
371 | } |
||
372 | // no need to disable any app in case this is a non-core upgrade |
||
373 | if (!$isCoreUpgrade) { |
||
374 | continue; |
||
375 | } |
||
376 | // shipped apps will remain enabled |
||
377 | if ($appManager->isShipped($app)) { |
||
378 | continue; |
||
379 | } |
||
380 | // authentication and session apps will remain enabled as well |
||
381 | if (OC_App::isType($app, ['session', 'authentication'])) { |
||
382 | continue; |
||
383 | } |
||
384 | } |
||
385 | return $disabledApps; |
||
386 | } |
||
387 | |||
388 | /** |
||
389 | * @return bool |
||
390 | */ |
||
391 | private function isCodeUpgrade() { |
||
392 | $installedVersion = $this->config->getSystemValue('version', '0.0.0'); |
||
393 | $currentVersion = implode('.', Util::getVersion()); |
||
394 | if (version_compare($currentVersion, $installedVersion, '>')) { |
||
395 | return true; |
||
396 | } |
||
397 | return false; |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * @param array $disabledApps |
||
402 | * @param bool $reenable |
||
403 | * @throws \Exception |
||
404 | */ |
||
405 | private function upgradeAppStoreApps(array $disabledApps, $reenable = false) { |
||
406 | foreach ($disabledApps as $app) { |
||
407 | try { |
||
408 | $this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]); |
||
409 | if ($this->installer->isUpdateAvailable($app)) { |
||
410 | $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]); |
||
411 | $this->installer->updateAppstoreApp($app); |
||
412 | } |
||
413 | $this->emit('\OC\Updater', 'checkAppStoreApp', [$app]); |
||
414 | |||
415 | if ($reenable) { |
||
416 | $ocApp = new \OC_App(); |
||
417 | $ocApp->enable($app); |
||
418 | } |
||
419 | } catch (\Exception $ex) { |
||
420 | $this->log->logException($ex, ['app' => 'core']); |
||
421 | } |
||
422 | } |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Forward messages emitted by the repair routine |
||
427 | */ |
||
428 | private function emitRepairEvents() { |
||
429 | $dispatcher = \OC::$server->getEventDispatcher(); |
||
430 | $dispatcher->addListener('\OC\Repair::warning', function ($event) { |
||
431 | if ($event instanceof GenericEvent) { |
||
432 | $this->emit('\OC\Updater', 'repairWarning', $event->getArguments()); |
||
433 | } |
||
434 | }); |
||
435 | $dispatcher->addListener('\OC\Repair::error', function ($event) { |
||
436 | if ($event instanceof GenericEvent) { |
||
437 | $this->emit('\OC\Updater', 'repairError', $event->getArguments()); |
||
438 | } |
||
439 | }); |
||
440 | $dispatcher->addListener('\OC\Repair::info', function ($event) { |
||
441 | if ($event instanceof GenericEvent) { |
||
442 | $this->emit('\OC\Updater', 'repairInfo', $event->getArguments()); |
||
443 | } |
||
444 | }); |
||
445 | $dispatcher->addListener('\OC\Repair::step', function ($event) { |
||
446 | if ($event instanceof GenericEvent) { |
||
447 | $this->emit('\OC\Updater', 'repairStep', $event->getArguments()); |
||
448 | } |
||
449 | }); |
||
450 | } |
||
451 | |||
452 | private function logAllEvents() { |
||
581 | }); |
||
582 | } |
||
583 | } |
||
584 |