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

SimpleContainer::has()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bernhard Posselt <[email protected]>
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Robin McCorkell <[email protected]>
12
 * @author Roeland Jago Douma <[email protected]>
13
 * @author Thomas Müller <[email protected]>
14
 *
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
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, version 3,
27
 * along with this program. If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
31
namespace OC\AppFramework\Utility;
32
33
use ArrayAccess;
34
use Closure;
35
use OCP\AppFramework\QueryException;
36
use OCP\IContainer;
37
use Pimple\Container;
38
use Psr\Container\ContainerInterface;
39
use ReflectionClass;
40
use ReflectionException;
41
use ReflectionParameter;
42
use function class_exists;
43
44
/**
45
 * SimpleContainer is a simple implementation of a container on basis of Pimple
46
 */
47
class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
0 ignored issues
show
Deprecated Code introduced by
The interface OCP\IContainer has been deprecated: 20.0.0 use \Psr\Container\ContainerInterface ( Ignorable by Annotation )

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

47
class SimpleContainer implements ArrayAccess, ContainerInterface, /** @scrutinizer ignore-deprecated */ IContainer {

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

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

Loading history...
48
49
	/** @var Container */
50
	private $container;
51
52
	public function __construct() {
53
		$this->container = new Container();
54
	}
55
56
	public function get($id) {
57
		return $this->query($id);
58
	}
59
60
	public function has($id): bool {
61
		// If a service is no registered but is an existing class, we can probably load it
62
		return isset($this->container[$id]) || class_exists($id);
63
	}
64
65
	/**
66
	 * @param ReflectionClass $class the class to instantiate
67
	 * @return \stdClass the created class
68
	 * @suppress PhanUndeclaredClassInstanceof
69
	 */
70
	private function buildClass(ReflectionClass $class) {
71
		$constructor = $class->getConstructor();
72
		if ($constructor === null) {
73
			return $class->newInstance();
74
		}
75
76
		return $class->newInstanceArgs(array_map(function (ReflectionParameter $parameter) {
77
			$parameterClass = $parameter->getClass();
78
79
			// try to find out if it is a class or a simple parameter
80
			if ($parameterClass === null) {
81
				$resolveName = $parameter->getName();
82
			} else {
83
				$resolveName = $parameterClass->name;
84
			}
85
86
			try {
87
				$builtIn = $parameter->hasType() && $parameter->getType()->isBuiltin();
88
				return $this->query($resolveName, !$builtIn);
89
			} catch (QueryException $e) {
90
				// Service not found, use the default value when available
91
				if ($parameter->isDefaultValueAvailable()) {
92
					return $parameter->getDefaultValue();
93
				}
94
95
				if ($parameterClass !== null) {
96
					$resolveName = $parameter->getName();
97
					return $this->query($resolveName);
98
				}
99
100
				throw $e;
101
			}
102
		}, $constructor->getParameters()));
103
	}
104
105
	public function resolve($name) {
106
		$baseMsg = 'Could not resolve ' . $name . '!';
107
		try {
108
			$class = new ReflectionClass($name);
109
			if ($class->isInstantiable()) {
110
				return $this->buildClass($class);
111
			} else {
112
				throw new QueryException($baseMsg .
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

112
				throw /** @scrutinizer ignore-deprecated */ new QueryException($baseMsg .
Loading history...
113
					' Class can not be instantiated');
114
			}
115
		} catch (ReflectionException $e) {
116
			throw new QueryException($baseMsg . ' ' . $e->getMessage());
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

116
			throw /** @scrutinizer ignore-deprecated */ new QueryException($baseMsg . ' ' . $e->getMessage());
Loading history...
117
		}
118
	}
119
120
	public function query(string $name, bool $autoload = true) {
121
		$name = $this->sanitizeName($name);
122
		if (isset($this->container[$name])) {
123
			return $this->container[$name];
124
		}
125
126
		if ($autoload) {
127
			$object = $this->resolve($name);
128
			$this->registerService($name, function () use ($object) {
129
				return $object;
130
			});
131
			return $object;
132
		}
133
134
		throw new QueryException('Could not resolve ' . $name . '!');
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

134
		throw /** @scrutinizer ignore-deprecated */ new QueryException('Could not resolve ' . $name . '!');
Loading history...
135
	}
136
137
	/**
138
	 * @param string $name
139
	 * @param mixed $value
140
	 */
141
	public function registerParameter($name, $value) {
142
		$this[$name] = $value;
143
	}
144
145
	/**
146
	 * The given closure is call the first time the given service is queried.
147
	 * The closure has to return the instance for the given service.
148
	 * Created instance will be cached in case $shared is true.
149
	 *
150
	 * @param string $name name of the service to register another backend for
151
	 * @param Closure $closure the closure to be called on service creation
152
	 * @param bool $shared
153
	 */
154
	public function registerService($name, Closure $closure, $shared = true) {
155
		$wrapped = function () use ($closure) {
156
			return $closure($this);
157
		};
158
		$name = $this->sanitizeName($name);
159
		if (isset($this[$name])) {
160
			unset($this[$name]);
161
		}
162
		if ($shared) {
163
			$this[$name] = $wrapped;
164
		} else {
165
			$this[$name] = $this->container->factory($wrapped);
166
		}
167
	}
168
169
	/**
170
	 * Shortcut for returning a service from a service under a different key,
171
	 * e.g. to tell the container to return a class when queried for an
172
	 * interface
173
	 * @param string $alias the alias that should be registered
174
	 * @param string $target the target that should be resolved instead
175
	 */
176
	public function registerAlias($alias, $target) {
177
		$this->registerService($alias, function (ContainerInterface $container) use ($target) {
178
			return $container->get($target);
179
		}, false);
180
	}
181
182
	/*
183
	 * @param string $name
184
	 * @return string
185
	 */
186
	protected function sanitizeName($name) {
187
		if (isset($name[0]) && $name[0] === '\\') {
188
			return ltrim($name, '\\');
189
		}
190
		return $name;
191
	}
192
193
	/**
194
	 * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::has
195
	 */
196
	public function offsetExists($id) {
197
		return $this->container->offsetExists($id);
198
	}
199
200
	/**
201
	 * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get
202
	 */
203
	public function offsetGet($id) {
204
		return $this->container->offsetGet($id);
205
	}
206
207
	/**
208
	 * @deprecated 20.0.0 use \OCP\IContainer::registerService
209
	 */
210
	public function offsetSet($id, $service) {
211
		$this->container->offsetSet($id, $service);
212
	}
213
214
	/**
215
	 * @deprecated 20.0.0
216
	 */
217
	public function offsetUnset($offset) {
218
		$this->container->offsetUnset($offset);
219
	}
220
}
221