Passed
Push — master ( 12da69...b976cd )
by Roeland
20:03 queued 05:06
created

InitialStateService::loadLazyStates()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 17
nc 5
nop 0
dl 0
loc 30
c 0
b 0
f 0
cc 5
rs 9.3888
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2019, Roeland Jago Douma <[email protected]>
7
 *
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Roeland Jago Douma <[email protected]>
10
 *
11
 * @license GNU AGPL version 3 or any later version
12
 *
13
 * This program is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License as
15
 * published by the Free Software Foundation, either version 3 of the
16
 * License, or (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License
24
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25
 *
26
 */
27
28
namespace OC;
29
30
use Closure;
31
use OC\AppFramework\Bootstrap\Coordinator;
32
use OCP\AppFramework\QueryException;
33
use OCP\AppFramework\Services\InitialStateProvider;
34
use OCP\IInitialStateService;
35
use OCP\ILogger;
36
use OCP\IServerContainer;
37
38
class InitialStateService implements IInitialStateService {
39
40
	/** @var ILogger */
41
	private $logger;
42
43
	/** @var string[][] */
44
	private $states = [];
45
46
	/** @var Closure[][] */
47
	private $lazyStates = [];
48
49
	/** @var Coordinator */
50
	private $bootstrapCoordinator;
51
52
	/** @var IServerContainer */
53
	private $container;
54
55
	public function __construct(ILogger $logger, Coordinator $bootstrapCoordinator, IServerContainer $container) {
56
		$this->logger = $logger;
57
		$this->bootstrapCoordinator = $bootstrapCoordinator;
58
		$this->container = $container;
59
	}
60
61
	public function provideInitialState(string $appName, string $key, $data): void {
62
		// Scalars and JsonSerializable are fine
63
		if (is_scalar($data) || $data instanceof \JsonSerializable || is_array($data)) {
64
			if (!isset($this->states[$appName])) {
65
				$this->states[$appName] = [];
66
			}
67
			$this->states[$appName][$key] = json_encode($data);
68
			return;
69
		}
70
71
		$this->logger->warning('Invalid data provided to provideInitialState by ' . $appName);
72
	}
73
74
	public function provideLazyInitialState(string $appName, string $key, Closure $closure): void {
75
		if (!isset($this->lazyStates[$appName])) {
76
			$this->lazyStates[$appName] = [];
77
		}
78
		$this->lazyStates[$appName][$key] = $closure;
79
	}
80
81
	/**
82
	 * Invoke all callbacks to populate the `states` property
83
	 */
84
	private function invokeLazyStateCallbacks(): void {
85
		foreach ($this->lazyStates as $app => $lazyStates) {
86
			foreach ($lazyStates as $key => $lazyState) {
87
				$startTime = microtime(true);
88
				$this->provideInitialState($app, $key, $lazyState());
89
				$endTime = microtime(true);
90
				$duration = $endTime - $startTime;
91
				if ($duration > 1) {
92
					$this->logger->warning('Lazy initial state provider for {key} took {duration} seconds.', [
93
						'app' => $app,
94
						'key' => $key,
95
						'duration' => round($duration, 2),
96
					]);
97
				}
98
			}
99
		}
100
		$this->lazyStates = [];
101
	}
102
103
	/**
104
	 * Load the lazy states via the IBootstrap mechanism
105
	 */
106
	private function loadLazyStates(): void {
107
		$context = $this->bootstrapCoordinator->getRegistrationContext();
108
109
		if ($context === null) {
110
			// To early, nothing to do yet
111
			return;
112
		}
113
114
		$initialStates = $context->getInitialStates();
115
		foreach ($initialStates as $initialState) {
116
			try {
117
				$provider = $this->container->query($initialState['class']);
118
			} catch (QueryException $e) {
119
				// Log an continue. We can be fault tolerant here.
120
				$this->logger->logException($e, [
121
					'message' => 'Could not load initial state provider dynamically: ' . $e->getMessage(),
122
					'level' => ILogger::ERROR,
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

122
					'level' => /** @scrutinizer ignore-deprecated */ ILogger::ERROR,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
123
					'app' => $initialState['appId'],
124
				]);
125
				continue;
126
			}
127
128
			if (!($provider instanceof InitialStateProvider)) {
129
				// Log an continue. We can be fault tolerant here.
130
				$this->logger->error('Initial state provider is not an InitialStateProvider instance: ' . $initialState['class'], [
131
					'app' => $initialState['appId'],
132
				]);
133
			}
134
135
			$this->provideInitialState($initialState['appId'], $provider->getKey(), $provider);
136
		}
137
	}
138
139
	public function getInitialStates(): array {
140
		$this->invokeLazyStateCallbacks();
141
		$this->loadLazyStates();
142
143
		$appStates = [];
144
		foreach ($this->states as $app => $states) {
145
			foreach ($states as $key => $value) {
146
				$appStates["$app-$key"] = $value;
147
			}
148
		}
149
		return $appStates;
150
	}
151
}
152