Completed
Push — work-fleets ( 73ed38...ce5b3a )
by SuperNova.WS
05:53
created

Container::offsetGet()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 7
eloc 16
nc 4
nop 1
dl 0
loc 27
rs 6.7272
c 1
b 0
f 1
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 View Code Duplication
    public function factory($callable)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function protect($callable)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
}
283