Passed
Push — master ( 18bafe...2ff0c9 )
by Julius
14:52 queued 13s
created

Coordinator::isBootable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright 2020 Christoph Wurst <[email protected]>
7
 *
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Julius Härtl <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Robin Appelman <[email protected]>
13
 *
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License
27
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
31
namespace OC\AppFramework\Bootstrap;
32
33
use OCP\Diagnostics\IEventLogger;
34
use function class_exists;
35
use function class_implements;
36
use function in_array;
37
use OC_App;
38
use OC\Support\CrashReport\Registry;
39
use OCP\AppFramework\App;
40
use OCP\AppFramework\Bootstrap\IBootstrap;
41
use OCP\AppFramework\QueryException;
42
use OCP\Dashboard\IManager;
43
use OCP\EventDispatcher\IEventDispatcher;
44
use OCP\IServerContainer;
45
use Psr\Log\LoggerInterface;
46
use Throwable;
47
48
class Coordinator {
49
50
	/** @var IServerContainer */
51
	private $serverContainer;
52
53
	/** @var Registry */
54
	private $registry;
55
56
	/** @var IManager */
57
	private $dashboardManager;
58
59
	/** @var IEventDispatcher */
60
	private $eventDispatcher;
61
62
	/** @var IEventLogger */
63
	private $eventLogger;
64
65
	/** @var LoggerInterface */
66
	private $logger;
67
68
	/** @var RegistrationContext|null */
69
	private $registrationContext;
70
71
	/** @var string[] */
72
	private $bootedApps = [];
73
74
	public function __construct(
75
		IServerContainer $container,
76
		Registry $registry,
77
		IManager $dashboardManager,
78
		IEventDispatcher $eventListener,
79
		IEventLogger $eventLogger,
80
		LoggerInterface $logger
81
	) {
82
		$this->serverContainer = $container;
83
		$this->registry = $registry;
84
		$this->dashboardManager = $dashboardManager;
85
		$this->eventDispatcher = $eventListener;
86
		$this->eventLogger = $eventLogger;
87
		$this->logger = $logger;
88
	}
89
90
	public function runInitialRegistration(): void {
91
		$this->registerApps(OC_App::getEnabledApps());
92
	}
93
94
	public function runLazyRegistration(string $appId): void {
95
		$this->registerApps([$appId]);
96
	}
97
98
	/**
99
	 * @param string[] $appIds
100
	 */
101
	private function registerApps(array $appIds): void {
102
		if ($this->registrationContext === null) {
103
			$this->registrationContext = new RegistrationContext($this->logger);
104
		}
105
		$apps = [];
106
		foreach ($appIds as $appId) {
107
			/*
108
			 * First, we have to enable the app's autoloader
109
			 *
110
			 * @todo use $this->appManager->getAppPath($appId) here
111
			 */
112
			$path = OC_App::getAppPath($appId);
113
			if ($path === false) {
114
				// Ignore
115
				continue;
116
			}
117
			OC_App::registerAutoloading($appId, $path);
118
119
			/*
120
			 * Next we check if there is an application class and it implements
121
			 * the \OCP\AppFramework\Bootstrap\IBootstrap interface
122
			 */
123
			$appNameSpace = App::buildAppNamespace($appId);
124
			$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
125
			try {
126
				if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) {
127
					try {
128
						/** @var IBootstrap|App $application */
129
						$apps[$appId] = $application = $this->serverContainer->query($applicationClassName);
130
					} catch (QueryException $e) {
131
						// Weird, but ok
132
						continue;
133
					}
134
135
					$this->eventLogger->start('bootstrap:register_app_' . $appId, '');
136
					$application->register($this->registrationContext->for($appId));
0 ignored issues
show
Bug introduced by
The method register() does not exist on OCP\AppFramework\App. Did you maybe mean registerRoutes()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

136
					$application->/** @scrutinizer ignore-call */ 
137
                   register($this->registrationContext->for($appId));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
137
					$this->eventLogger->end('bootstrap:register_app_' . $appId);
138
				}
139
			} catch (Throwable $e) {
140
				$this->logger->emergency('Error during app service registration: ' . $e->getMessage(), [
141
					'exception' => $e,
142
					'app' => $appId,
143
				]);
144
				continue;
145
			}
146
		}
147
148
		/**
149
		 * Now that all register methods have been called, we can delegate the registrations
150
		 * to the actual services
151
		 */
152
		$this->registrationContext->delegateCapabilityRegistrations($apps);
153
		$this->registrationContext->delegateCrashReporterRegistrations($apps, $this->registry);
154
		$this->registrationContext->delegateDashboardPanelRegistrations($apps, $this->dashboardManager);
155
		$this->registrationContext->delegateEventListenerRegistrations($this->eventDispatcher);
156
		$this->registrationContext->delegateContainerRegistrations($apps);
157
		$this->registrationContext->delegateMiddlewareRegistrations($apps);
158
	}
159
160
	public function getRegistrationContext(): ?RegistrationContext {
161
		return $this->registrationContext;
162
	}
163
164
	public function bootApp(string $appId): void {
165
		if (isset($this->bootedApps[$appId])) {
166
			return;
167
		}
168
		$this->bootedApps[$appId] = true;
169
170
		$appNameSpace = App::buildAppNamespace($appId);
171
		$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
172
		if (!class_exists($applicationClassName)) {
173
			// Nothing to boot
174
			return;
175
		}
176
177
		/*
178
		 * Now it is time to fetch an instance of the App class. For classes
179
		 * that implement \OCP\AppFramework\Bootstrap\IBootstrap this means
180
		 * the instance was already created for register, but any other
181
		 * (legacy) code will now do their magic via the constructor.
182
		 */
183
		$this->eventLogger->start('bootstrap:boot_app_' . $appId, '');
184
		try {
185
			/** @var App $application */
186
			$application = $this->serverContainer->query($applicationClassName);
187
			if ($application instanceof IBootstrap) {
188
				/** @var BootContext $context */
189
				$context = new BootContext($application->getContainer());
190
				$application->boot($context);
191
			}
192
		} catch (QueryException $e) {
193
			$this->logger->error("Could not boot $appId: " . $e->getMessage(), [
194
				'exception' => $e,
195
			]);
196
		} catch (Throwable $e) {
197
			$this->logger->emergency("Could not boot $appId: " . $e->getMessage(), [
198
				'exception' => $e,
199
			]);
200
		}
201
		$this->eventLogger->end('bootstrap:boot_app_' . $appId);
202
	}
203
204
	public function isBootable(string $appId) {
205
		$appNameSpace = App::buildAppNamespace($appId);
206
		$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
207
		return class_exists($applicationClassName) &&
208
			in_array(IBootstrap::class, class_implements($applicationClassName), true);
209
	}
210
}
211