Completed
Push — master ( 420f16...68b944 )
by Alexander
03:24
created

Container::offsetGet()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
ccs 17
cts 17
cp 1
rs 6.7272
cc 7
eloc 15
nc 4
nop 1
crap 7
1
<?php
2
3
/*
4
 * This file is part of Pimple.
5
 *
6
 * Copyright (c) 2009 Fabien Potencier
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to deal
10
 * in the Software without restriction, including without limitation the rights
11
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the Software is furnished
13
 * to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in all
16
 * copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
 * THE SOFTWARE.
25
 */
26
27
namespace PimpleCopy\Pimple;
28
29
30
/**
31
 * Container main class.
32
 *
33
 * @author Fabien Potencier
34
 */
35
class Container implements \ArrayAccess
36
{
37
38
	private $values = array();
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
39
40
	private $factories;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
41
42
	private $protected;
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
43
44
	private $frozen = array();
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
45
46
	private $raw = array();
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
47
48
	private $keys = array();
0 ignored issues
show
Coding Style Documentation introduced by
Missing member variable doc comment
Loading history...
49
50
	/**
51
	 * Instantiate the container.
52
	 *
53
	 * Objects and parameters can be passed as argument to the constructor.
54
	 *
55
	 * @param array $values The parameters or objects.
56
	 */
57 65
	public function __construct(array $values = array())
58
	{
59 65
		$this->factories = new \SplObjectStorage();
60 65
		$this->protected = new \SplObjectStorage();
61
62 65
		foreach ( $values as $key => $value ) {
63 1
			$this->offsetSet($key, $value);
64 65
		}
65 65
	}
66
67
	/**
68
	 * Sets a parameter or an object.
69
	 *
70
	 * Objects must be defined as Closures.
71
	 *
72
	 * Allowing any PHP callable leads to difficult to debug problems
73
	 * as function names (strings) are callable (creating a function with
74
	 * the same name as an existing parameter would break your container).
75
	 *
76
	 * @param string $id	The unique identifier for the parameter or object.
77
	 * @param mixed  $value The value of the parameter or a closure to define an object.
78
	 *
79
	 * @return void
80
	 * @throws \RuntimeException Prevent override of a frozen service.
81
	 */
82 57
	public function offsetSet($id, $value)
83 5
	{
84 57
		if ( isset($this->frozen[$id]) ) {
85 1
			throw new \RuntimeException(sprintf('Cannot override frozen service "%s".', $id));
86
		}
87
88 57
		$this->values[$id] = $value;
89 57
		$this->keys[$id] = true;
90 57
	}
91
92
	/**
93
	 * Gets a parameter or an object.
94
	 *
95
	 * @param string $id The unique identifier for the parameter or object.
96
	 *
97
	 * @return mixed The value of the parameter or an object
98
	 * @throws \InvalidArgumentException If the identifier is not defined.
99
	 */
100 48
	public function offsetGet($id)
101
	{
102 48
		if ( !isset($this->keys[$id]) ) {
103 1
			throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
104
		}
105
106 47
		if ( isset($this->raw[$id])
107 47
			|| !is_object($this->values[$id])
108 47
			|| isset($this->protected[$this->values[$id]])
109 43
			|| !method_exists($this->values[$id], '__invoke')
110 47
		) {
111 25
			return $this->values[$id];
112
		}
113
114 38
		if ( isset($this->factories[$this->values[$id]]) ) {
115 13
			return $this->values[$id]($this);
116 5
		}
117
118 35
		$raw = $this->values[$id];
119 35
		$val = $this->values[$id] = $raw($this);
120 35
		$this->raw[$id] = $raw;
121
122 35
		$this->frozen[$id] = true;
123
124 35
		return $val;
125
	}
126
127
	/**
128
	 * Checks if a parameter or an object is set.
129
	 *
130
	 * @param string $id The unique identifier for the parameter or object.
131
	 *
132
	 * @return boolean
133
	 */
134 4
	public function offsetExists($id)
135
	{
136 4
		return isset($this->keys[$id]);
137
	}
138
139
	/**
140
	 * Unsets a parameter or an object.
141
	 *
142
	 * @param string $id The unique identifier for the parameter or object.
143
	 *
144
	 * @return void
145
	 */
146 9
	public function offsetUnset($id)
147
	{
148 4
		if ( isset($this->keys[$id]) ) {
149 9
			if ( is_object($this->values[$id]) ) {
150 3
				unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]);
151 3
			}
152
153 4
			unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]);
154 4
		}
155 4
	}
156
157
	/**
158
	 * Marks a callable as being a factory service.
159
	 *
160
	 * @param callable $callable A service definition to be used as a factory.
161
	 *
162
	 * @return callable The passed callable
163
	 * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object.
164
	 */
165 34
	public function factory($callable)
0 ignored issues
show
introduced by
Type hint "callable" missing for $callable
Loading history...
166
	{
167 34
		if ( !method_exists($callable, '__invoke') ) {
168 2
			throw new \InvalidArgumentException('Service definition is not a Closure or invokable object.');
169
		}
170
171 32
		$this->factories->attach($callable);
172
173 32
		return $callable;
174
	}
175
176
	/**
177
	 * Protects a callable from being interpreted as a service.
178
	 *
179
	 * This is useful when you want to store a callable as a parameter.
180
	 *
181
	 * @param callable $callable A callable to protect from being evaluated.
182
	 *
183
	 * @return callable The passed callable
184
	 * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object.
185
	 */
186 4
	public function protect($callable)
0 ignored issues
show
introduced by
Type hint "callable" missing for $callable
Loading history...
187
	{
188 4
		if ( !method_exists($callable, '__invoke') ) {
189 2
			throw new \InvalidArgumentException('Callable is not a Closure or invokable object.');
190
		}
191
192 2
		$this->protected->attach($callable);
193
194 2
		return $callable;
195
	}
196
197
	/**
198
	 * Gets a parameter or the closure defining an object.
199
	 *
200
	 * @param string $id The unique identifier for the parameter or object.
201
	 *
202
	 * @return mixed The value of the parameter or the closure defining an object
203
	 * @throws \InvalidArgumentException If the identifier is not defined.
204
	 */
205 4
	public function raw($id)
206
	{
207 4
		if ( !isset($this->keys[$id]) ) {
208 1
			throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
209
		}
210
211 3
		if ( isset($this->raw[$id]) ) {
212
			return $this->raw[$id];
213
		}
214
215 3
		return $this->values[$id];
216
	}
217
218
	/**
219
	 * Extends an object definition.
220
	 *
221
	 * Useful when you want to extend an existing object definition,
222
	 * without necessarily loading that object.
223
	 *
224
	 * @param string   $id	     The unique identifier for the object.
225
	 * @param callable $callable A service definition to extend the original.
226
	 *
227
	 * @return callable The wrapped callable
228
	 * @throws \InvalidArgumentException If the identifier is not defined or not a service definition.
229
	 */
230 10
	public function extend($id, $callable)
0 ignored issues
show
introduced by
Type hint "callable" missing for $callable
Loading history...
231
	{
232 10
		if ( !isset($this->keys[$id]) ) {
233 1
			throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
234
		}
235
236 9
		if ( !is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke') ) {
237 2
			throw new \InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id));
238
		}
239
240 7
		if ( !is_object($callable) || !method_exists($callable, '__invoke') ) {
241 2
			throw new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.');
242
		}
243
244 5
		$factory = $this->values[$id];
245
246 5
		$extended = function ($c) use ($callable, $factory) {
247 4
			return $callable($factory($c), $c);
248 5
		};
249
250 5
		if ( isset($this->factories[$factory]) ) {
251 3
			$this->factories->detach($factory);
252 3
			$this->factories->attach($extended);
253 3
		}
254
255 5
		return $this[$id] = $extended;
256
	}
257
258
	/**
259
	 * Returns all defined value names.
260
	 *
261
	 * @return array An array of value names
262
	 */
263 1
	public function keys()
264
	{
265 1
		return array_keys($this->values);
266
	}
267
268
	/**
269
	 * Registers a service provider.
270
	 *
271
	 * @param ServiceProviderInterface $provider A ServiceProviderInterface instance.
272
	 * @param array					   $values   An array of values that customizes the provider.
273
	 *
274
	 * @return static
275
	 */
276 2
	public function register(ServiceProviderInterface $provider, array $values = array())
277
	{
278 2
		$provider->register($this);
279
280 2
		foreach ( $values as $key => $value ) {
281 1
			$this[$key] = $value;
282 2
		}
283
284 2
		return $this;
285
	}
286
287
}
288