Passed
Push — master ( b12d36...33aeef )
by Christoph
12:02 queued 11s
created

ServerContainer::getAppContainerForService()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 1
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
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, version 3,
24
 * along with this program. If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC;
29
30
use OC\AppFramework\App;
31
use OC\AppFramework\DependencyInjection\DIContainer;
32
use OC\AppFramework\Utility\SimpleContainer;
33
use OCP\AppFramework\QueryException;
34
use function explode;
35
use function strtolower;
36
37
/**
38
 * Class ServerContainer
39
 *
40
 * @package OC
41
 */
42
class ServerContainer extends SimpleContainer {
43
	/** @var DIContainer[] */
44
	protected $appContainers;
45
46
	/** @var string[] */
47
	protected $hasNoAppContainer;
48
49
	/** @var string[] */
50
	protected $namespaces;
51
52
	/**
53
	 * ServerContainer constructor.
54
	 */
55
	public function __construct() {
56
		parent::__construct();
57
		$this->appContainers = [];
58
		$this->namespaces = [];
59
		$this->hasNoAppContainer = [];
60
	}
61
62
	/**
63
	 * @param string $appName
64
	 * @param string $appNamespace
65
	 */
66
	public function registerNamespace(string $appName, string $appNamespace): void {
67
		// Cut of OCA\ and lowercase
68
		$appNamespace = strtolower(substr($appNamespace, strrpos($appNamespace, '\\') + 1));
69
		$this->namespaces[$appNamespace] = $appName;
70
	}
71
72
	/**
73
	 * @param string $appName
74
	 * @param DIContainer $container
75
	 */
76
	public function registerAppContainer(string $appName, DIContainer $container): void {
77
		$this->appContainers[strtolower(App::buildAppNamespace($appName, ''))] = $container;
78
	}
79
80
	/**
81
	 * @param string $appName
82
	 * @return DIContainer
83
	 * @throws QueryException
84
	 */
85
	public function getRegisteredAppContainer(string $appName): DIContainer {
86
		if (isset($this->appContainers[strtolower(App::buildAppNamespace($appName, ''))])) {
87
			return $this->appContainers[strtolower(App::buildAppNamespace($appName, ''))];
88
		}
89
90
		throw new QueryException();
0 ignored issues
show
Deprecated Code introduced by
The class OCP\AppFramework\QueryException has been deprecated: 20.0.0 catch \Psr\Container\ContainerExceptionInterface ( Ignorable by Annotation )

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

90
		throw /** @scrutinizer ignore-deprecated */ new QueryException();
Loading history...
91
	}
92
93
	/**
94
	 * @param string $namespace
95
	 * @param string $sensitiveNamespace
96
	 * @return DIContainer
97
	 * @throws QueryException
98
	 */
99
	protected function getAppContainer(string $namespace, string $sensitiveNamespace): DIContainer {
100
		if (isset($this->appContainers[$namespace])) {
101
			return $this->appContainers[$namespace];
102
		}
103
104
		if (isset($this->namespaces[$namespace])) {
105
			if (!isset($this->hasNoAppContainer[$namespace])) {
106
				$applicationClassName = 'OCA\\' . $sensitiveNamespace . '\\AppInfo\\Application';
107
				if (class_exists($applicationClassName)) {
108
					$app = new $applicationClassName();
109
					if (isset($this->appContainers[$namespace])) {
110
						$this->appContainers[$namespace]->offsetSet($applicationClassName, $app);
111
						return $this->appContainers[$namespace];
112
					}
113
				}
114
				$this->hasNoAppContainer[$namespace] = true;
115
			}
116
117
			return new DIContainer($this->namespaces[$namespace]);
0 ignored issues
show
Deprecated Code introduced by
The class OC\AppFramework\DependencyInjection\DIContainer 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

117
			return /** @scrutinizer ignore-deprecated */ new DIContainer($this->namespaces[$namespace]);
Loading history...
118
		}
119
		throw new QueryException();
0 ignored issues
show
Deprecated Code introduced by
The class OCP\AppFramework\QueryException has been deprecated: 20.0.0 catch \Psr\Container\ContainerExceptionInterface ( Ignorable by Annotation )

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

119
		throw /** @scrutinizer ignore-deprecated */ new QueryException();
Loading history...
120
	}
121
122
	public function has($id, bool $noRecursion = false): bool {
123
		if (!$noRecursion && ($appContainer = $this->getAppContainerForService($id)) !== null) {
124
			return $appContainer->has($id);
125
		}
126
127
		return parent::has($id);
128
	}
129
130
	public function query(string $name, bool $autoload = true) {
131
		$name = $this->sanitizeName($name);
132
133
		// In case the service starts with OCA\ we try to find the service in
134
		// the apps container first.
135
		if (($appContainer = $this->getAppContainerForService($name)) !== null) {
136
			try {
137
				return $appContainer->queryNoFallback($name);
138
			} catch (QueryException $e) {
139
				// Didn't find the service or the respective app container,
140
				// ignore it and fall back to the core container.
141
			}
142
		} elseif (strpos($name, 'OC\\Settings\\') === 0 && substr_count($name, '\\') >= 3) {
143
			$segments = explode('\\', $name);
144
			try {
145
				$appContainer = $this->getAppContainer(strtolower($segments[1]), $segments[1]);
146
				return $appContainer->queryNoFallback($name);
147
			} catch (QueryException $e) {
148
				// Didn't find the service or the respective app container,
149
				// ignore it and fall back to the core container.
150
			}
151
		}
152
153
		return parent::query($name, $autoload);
154
	}
155
156
	private function getAppContainerForService(string $id): ?DIContainer {
157
		if (strpos($id, 'OCA\\') !== 0 || substr_count($id, '\\') < 2) {
158
			return null;
159
		}
160
161
		try {
162
			[,$namespace,] = explode('\\', $id);
163
			return $this->getAppContainer(strtolower($namespace), $namespace);
164
		} catch (QueryException $e) {
165
			return null;
166
		}
167
	}
168
}
169