Completed
Push — master ( 6d688e...a0b34d )
by Joas
33:31 queued 19:01
created

SimpleContainer::query()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 12
rs 9.4285
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 Joas Schilling <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin McCorkell <[email protected]>
10
 * @author Thomas Müller <[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\AppFramework\Utility;
29
30
use ReflectionClass;
31
use ReflectionException;
32
use Closure;
33
use Pimple\Container;
34
use OCP\AppFramework\QueryException;
35
use OCP\IContainer;
36
37
/**
38
 * Class SimpleContainer
39
 *
40
 * SimpleContainer is a simple implementation of IContainer on basis of Pimple
41
 */
42
class SimpleContainer extends Container implements IContainer {
43
44
45
	/**
46
	 * @param ReflectionClass $class the class to instantiate
47
	 * @return \stdClass the created class
48
	 */
49
	private function buildClass(ReflectionClass $class) {
50
		$constructor = $class->getConstructor();
51
		if ($constructor === null) {
52
			return $class->newInstance();
53
		} else {
54
			$parameters = [];
55
			foreach ($constructor->getParameters() as $parameter) {
56
				$parameterClass = $parameter->getClass();
57
58
				// try to find out if it is a class or a simple parameter
59
				if ($parameterClass === null) {
60
					$resolveName = $parameter->getName();
61
				} else {
62
					$resolveName = $parameterClass->name;
63
				}
64
65
				try {
66
					$parameters[] = $this->query($resolveName);
67
				} catch (\Exception $e) {
68
					// Service not found, use the default value when available
69
					if ($parameter->isDefaultValueAvailable()) {
70
						$parameters[] = $parameter->getDefaultValue();
71
					} else if ($parameterClass !== null) {
72
						$resolveName = $parameter->getName();
73
						$parameters[] = $this->query($resolveName);
74
					} else {
75
						throw $e;
76
					}
77
				}
78
			}
79
			return $class->newInstanceArgs($parameters);
80
		}
81
	}
82
83
84
	/**
85
	 * If a parameter is not registered in the container try to instantiate it
86
	 * by using reflection to find out how to build the class
87
	 * @param string $name the class name to resolve
88
	 * @return \stdClass
89
	 * @throws QueryException if the class could not be found or instantiated
90
	 */
91
	public function resolve($name) {
92
		$baseMsg = 'Could not resolve ' . $name . '!';
93
		try {
94
			$class = new ReflectionClass($name);
95
			if ($class->isInstantiable()) {
96
				return $this->buildClass($class);
97
			} else {
98
				throw new QueryException($baseMsg .
99
					' Class can not be instantiated');
100
			}
101
		} catch(ReflectionException $e) {
102
			throw new QueryException($baseMsg . ' ' . $e->getMessage());
103
		}
104
	}
105
106
107
	/**
108
	 * @param string $name name of the service to query for
109
	 * @return mixed registered service for the given $name
110
	 * @throws QueryException if the query could not be resolved
111
	 */
112
	public function query($name) {
113
		$name = $this->sanitizeName($name);
114
		if ($this->offsetExists($name)) {
115
			return $this->offsetGet($name);
116
		} else {
117
			$object = $this->resolve($name);
118
			$this->registerService($name, function () use ($object) {
119
				return $object;
120
			});
121
			return $object;
122
		}
123
	}
124
125
	/**
126
	 * @param string $name
127
	 * @param mixed $value
128
	 */
129
	public function registerParameter($name, $value) {
130
		$this[$name] = $value;
131
	}
132
133
	/**
134
	 * The given closure is call the first time the given service is queried.
135
	 * The closure has to return the instance for the given service.
136
	 * Created instance will be cached in case $shared is true.
137
	 *
138
	 * @param string $name name of the service to register another backend for
139
	 * @param Closure $closure the closure to be called on service creation
140
	 * @param bool $shared
141
	 */
142
	public function registerService($name, Closure $closure, $shared = true) {
143
		$name = $this->sanitizeName($name);
144
		if (isset($this[$name]))  {
145
			unset($this[$name]);
146
		}
147
		if ($shared) {
148
			$this[$name] = $closure;
149
		} else {
150
			$this[$name] = parent::factory($closure);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (factory() instead of registerService()). Are you sure this is correct? If so, you might want to change this to $this->factory().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
151
		}
152
	}
153
154
	/**
155
	 * Shortcut for returning a service from a service under a different key,
156
	 * e.g. to tell the container to return a class when queried for an
157
	 * interface
158
	 * @param string $alias the alias that should be registered
159
	 * @param string $target the target that should be resolved instead
160
	 */
161
	public function registerAlias($alias, $target) {
162
		$this->registerService($alias, function (IContainer $container) use ($target) {
163
			return $container->query($target);
164
		}, false);
165
	}
166
167
	/*
168
	 * @param string $name
169
	 * @return string
170
	 */
171
	protected function sanitizeName($name) {
172
		if (isset($name[0]) && $name[0] === '\\') {
173
			return ltrim($name, '\\');
174
		}
175
		return $name;
176
	}
177
178
}
179