Completed
Pull Request — master (#8662)
by Joas
28:15 queued 11:04
created

SimpleContainer   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 144
rs 10
wmc 22
lcom 1
cbo 2

7 Methods

Rating   Name   Duplication   Size   Complexity  
A resolve() 0 14 3
A query() 0 12 2
A registerParameter() 0 3 1
A registerService() 0 11 3
A registerAlias() 0 5 1
A sanitizeName() 0 6 3
D buildClass() 0 39 9
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 Roeland Jago Douma <[email protected]>
11
 * @author Thomas Müller <[email protected]>
12
 *
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License, version 3,
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
29
namespace OC\AppFramework\Utility;
30
31
use ReflectionClass;
32
use ReflectionException;
33
use Closure;
34
use Pimple\Container;
35
use OCP\AppFramework\QueryException;
36
use OCP\IContainer;
37
38
/**
39
 * Class SimpleContainer
40
 *
41
 * SimpleContainer is a simple implementation of IContainer on basis of Pimple
42
 */
43
class SimpleContainer extends Container implements IContainer {
44
45
46
	/**
47
	 * @param ReflectionClass $class the class to instantiate
48
	 * @return \stdClass the created class
49
	 * @suppress PhanUndeclaredClassInstanceof
50
	 */
51
	private function buildClass(ReflectionClass $class) {
52
		$constructor = $class->getConstructor();
53
		if ($constructor === null) {
54
			return $class->newInstance();
55
		} else {
56
			$parameters = [];
57
			foreach ($constructor->getParameters() as $parameter) {
58
				$parameterClass = $parameter->getClass();
59
60
				// try to find out if it is a class or a simple parameter
61
				if ($parameterClass === null) {
62
					$resolveName = $parameter->getName();
63
				} else {
64
					$resolveName = $parameterClass->name;
65
				}
66
67
				try {
68
					$parameters[] = $this->query($resolveName);
69
				} catch (\Exception $e) {
70
					if (class_exists('PHPUnit_Framework_AssertionFailedError', false) &&
71
						$e instanceof \PHPUnit_Framework_AssertionFailedError) {
0 ignored issues
show
Bug introduced by
The class PHPUnit_Framework_AssertionFailedError does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
72
						// Easier debugging of "Your test case is not allowed to access the database."
73
						throw $e;
74
					}
75
76
					// Service not found, use the default value when available
77
					if ($parameter->isDefaultValueAvailable()) {
78
						$parameters[] = $parameter->getDefaultValue();
79
					} else if ($parameterClass !== null) {
80
						$resolveName = $parameter->getName();
81
						$parameters[] = $this->query($resolveName);
82
					} else {
83
						throw $e;
84
					}
85
				}
86
			}
87
			return $class->newInstanceArgs($parameters);
88
		}
89
	}
90
91
92
	/**
93
	 * If a parameter is not registered in the container try to instantiate it
94
	 * by using reflection to find out how to build the class
95
	 * @param string $name the class name to resolve
96
	 * @return \stdClass
97
	 * @throws QueryException if the class could not be found or instantiated
98
	 */
99
	public function resolve($name) {
100
		$baseMsg = 'Could not resolve ' . $name . '!';
101
		try {
102
			$class = new ReflectionClass($name);
103
			if ($class->isInstantiable()) {
104
				return $this->buildClass($class);
105
			} else {
106
				throw new QueryException($baseMsg .
107
					' Class can not be instantiated');
108
			}
109
		} catch(ReflectionException $e) {
110
			throw new QueryException($baseMsg . ' ' . $e->getMessage());
111
		}
112
	}
113
114
115
	/**
116
	 * @param string $name name of the service to query for
117
	 * @return mixed registered service for the given $name
118
	 * @throws QueryException if the query could not be resolved
119
	 */
120
	public function query($name) {
121
		$name = $this->sanitizeName($name);
122
		if ($this->offsetExists($name)) {
123
			return $this->offsetGet($name);
124
		} else {
125
			$object = $this->resolve($name);
126
			$this->registerService($name, function () use ($object) {
127
				return $object;
128
			});
129
			return $object;
130
		}
131
	}
132
133
	/**
134
	 * @param string $name
135
	 * @param mixed $value
136
	 */
137
	public function registerParameter($name, $value) {
138
		$this[$name] = $value;
139
	}
140
141
	/**
142
	 * The given closure is call the first time the given service is queried.
143
	 * The closure has to return the instance for the given service.
144
	 * Created instance will be cached in case $shared is true.
145
	 *
146
	 * @param string $name name of the service to register another backend for
147
	 * @param Closure $closure the closure to be called on service creation
148
	 * @param bool $shared
149
	 */
150
	public function registerService($name, Closure $closure, $shared = true) {
151
		$name = $this->sanitizeName($name);
152
		if (isset($this[$name]))  {
153
			unset($this[$name]);
154
		}
155
		if ($shared) {
156
			$this[$name] = $closure;
157
		} else {
158
			$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...
159
		}
160
	}
161
162
	/**
163
	 * Shortcut for returning a service from a service under a different key,
164
	 * e.g. to tell the container to return a class when queried for an
165
	 * interface
166
	 * @param string $alias the alias that should be registered
167
	 * @param string $target the target that should be resolved instead
168
	 */
169
	public function registerAlias($alias, $target) {
170
		$this->registerService($alias, function (IContainer $container) use ($target) {
171
			return $container->query($target);
172
		}, false);
173
	}
174
175
	/*
176
	 * @param string $name
177
	 * @return string
178
	 */
179
	protected function sanitizeName($name) {
180
		if (isset($name[0]) && $name[0] === '\\') {
181
			return ltrim($name, '\\');
182
		}
183
		return $name;
184
	}
185
186
}
187