]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - vendor/pimple/pimple/src/Pimple/Container.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / vendor / pimple / pimple / src / Pimple / Container.php
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 Pimple;
28
29 /**
30  * Container main class.
31  *
32  * @author  Fabien Potencier
33  */
34 class Container implements \ArrayAccess
35 {
36     private $values = array();
37     private $factories;
38     private $protected;
39     private $frozen = array();
40     private $raw = array();
41     private $keys = array();
42
43     /**
44      * Instantiate the container.
45      *
46      * Objects and parameters can be passed as argument to the constructor.
47      *
48      * @param array $values The parameters or objects.
49      */
50     public function __construct(array $values = array())
51     {
52         $this->factories = new \SplObjectStorage();
53         $this->protected = new \SplObjectStorage();
54
55         foreach ($values as $key => $value) {
56             $this->offsetSet($key, $value);
57         }
58     }
59
60     /**
61      * Sets a parameter or an object.
62      *
63      * Objects must be defined as Closures.
64      *
65      * Allowing any PHP callable leads to difficult to debug problems
66      * as function names (strings) are callable (creating a function with
67      * the same name as an existing parameter would break your container).
68      *
69      * @param string $id    The unique identifier for the parameter or object
70      * @param mixed  $value The value of the parameter or a closure to define an object
71      *
72      * @throws \RuntimeException Prevent override of a frozen service
73      */
74     public function offsetSet($id, $value)
75     {
76         if (isset($this->frozen[$id])) {
77             throw new \RuntimeException(sprintf('Cannot override frozen service "%s".', $id));
78         }
79
80         $this->values[$id] = $value;
81         $this->keys[$id] = true;
82     }
83
84     /**
85      * Gets a parameter or an object.
86      *
87      * @param string $id The unique identifier for the parameter or object
88      *
89      * @return mixed The value of the parameter or an object
90      *
91      * @throws \InvalidArgumentException if the identifier is not defined
92      */
93     public function offsetGet($id)
94     {
95         if (!isset($this->keys[$id])) {
96             throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
97         }
98
99         if (
100             isset($this->raw[$id])
101             || !is_object($this->values[$id])
102             || isset($this->protected[$this->values[$id]])
103             || !method_exists($this->values[$id], '__invoke')
104         ) {
105             return $this->values[$id];
106         }
107
108         if (isset($this->factories[$this->values[$id]])) {
109             return $this->values[$id]($this);
110         }
111
112         $raw = $this->values[$id];
113         $val = $this->values[$id] = $raw($this);
114         $this->raw[$id] = $raw;
115
116         $this->frozen[$id] = true;
117
118         return $val;
119     }
120
121     /**
122      * Checks if a parameter or an object is set.
123      *
124      * @param string $id The unique identifier for the parameter or object
125      *
126      * @return bool
127      */
128     public function offsetExists($id)
129     {
130         return isset($this->keys[$id]);
131     }
132
133     /**
134      * Unsets a parameter or an object.
135      *
136      * @param string $id The unique identifier for the parameter or object
137      */
138     public function offsetUnset($id)
139     {
140         if (isset($this->keys[$id])) {
141             if (is_object($this->values[$id])) {
142                 unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]);
143             }
144
145             unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]);
146         }
147     }
148
149     /**
150      * Marks a callable as being a factory service.
151      *
152      * @param callable $callable A service definition to be used as a factory
153      *
154      * @return callable The passed callable
155      *
156      * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object
157      */
158     public function factory($callable)
159     {
160         if (!method_exists($callable, '__invoke')) {
161             throw new \InvalidArgumentException('Service definition is not a Closure or invokable object.');
162         }
163
164         $this->factories->attach($callable);
165
166         return $callable;
167     }
168
169     /**
170      * Protects a callable from being interpreted as a service.
171      *
172      * This is useful when you want to store a callable as a parameter.
173      *
174      * @param callable $callable A callable to protect from being evaluated
175      *
176      * @return callable The passed callable
177      *
178      * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object
179      */
180     public function protect($callable)
181     {
182         if (!method_exists($callable, '__invoke')) {
183             throw new \InvalidArgumentException('Callable is not a Closure or invokable object.');
184         }
185
186         $this->protected->attach($callable);
187
188         return $callable;
189     }
190
191     /**
192      * Gets a parameter or the closure defining an object.
193      *
194      * @param string $id The unique identifier for the parameter or object
195      *
196      * @return mixed The value of the parameter or the closure defining an object
197      *
198      * @throws \InvalidArgumentException if the identifier is not defined
199      */
200     public function raw($id)
201     {
202         if (!isset($this->keys[$id])) {
203             throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
204         }
205
206         if (isset($this->raw[$id])) {
207             return $this->raw[$id];
208         }
209
210         return $this->values[$id];
211     }
212
213     /**
214      * Extends an object definition.
215      *
216      * Useful when you want to extend an existing object definition,
217      * without necessarily loading that object.
218      *
219      * @param string   $id       The unique identifier for the object
220      * @param callable $callable A service definition to extend the original
221      *
222      * @return callable The wrapped callable
223      *
224      * @throws \InvalidArgumentException if the identifier is not defined or not a service definition
225      */
226     public function extend($id, $callable)
227     {
228         if (!isset($this->keys[$id])) {
229             throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
230         }
231
232         if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) {
233             throw new \InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id));
234         }
235
236         if (!is_object($callable) || !method_exists($callable, '__invoke')) {
237             throw new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.');
238         }
239
240         $factory = $this->values[$id];
241
242         $extended = function ($c) use ($callable, $factory) {
243             return $callable($factory($c), $c);
244         };
245
246         if (isset($this->factories[$factory])) {
247             $this->factories->detach($factory);
248             $this->factories->attach($extended);
249         }
250
251         return $this[$id] = $extended;
252     }
253
254     /**
255      * Returns all defined value names.
256      *
257      * @return array An array of value names
258      */
259     public function keys()
260     {
261         return array_keys($this->values);
262     }
263
264     /**
265      * Registers a service provider.
266      *
267      * @param ServiceProviderInterface $provider A ServiceProviderInterface instance
268      * @param array                    $values   An array of values that customizes the provider
269      *
270      * @return static
271      */
272     public function register(ServiceProviderInterface $provider, array $values = array())
273     {
274         $provider->register($this);
275
276         foreach ($values as $key => $value) {
277             $this[$key] = $value;
278         }
279
280         return $this;
281     }
282 }