Completed
Push — master ( e401e5...bd7189 )
by Julien
01:48
created

Container.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Fwk
4
 *
5
 * Copyright (c) 2011-2012, Julien Ballestracci <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
12
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
13
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
15
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
16
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
17
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
21
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
 * POSSIBILITY OF SUCH DAMAGE.
23
 *
24
 * PHP Version 5.3
25
 *
26
 * @category  DependencyInjection
27
 * @package   Fwk\Di
28
 * @author    Julien Ballestracci <[email protected]>
29
 * @copyright 2011-2014 Julien Ballestracci <[email protected]>
30
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
31
 * @link      http://www.nitronet.org/fwk
32
 */
33
namespace Fwk\Di;
34
35
use \stdClass;
36
use \ArrayAccess;
37
38
/**
39
 * Container
40
 * 
41
 * THE Dependency Injection Container.
42
 *
43
 * @category Container
44
 * @package  Fwk\Di
45
 * @author   Julien Ballestracci <[email protected]>
46
 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
47
 * @link     http://www.nitronet.org/fwk
48
 */
49
class Container implements ArrayAccess
50
{
51
    /**
52
     * The objects store
53
     * @var array
54
     */
55
    protected $store = array();
56
57
    /**
58
     * Informations about stored objects
59
     * @var array
60
     */
61
    protected $storeData = array();
62
    
63
    /**
64
     * Container Properties
65
     * @var array
66
     */
67
    protected $properties = array();
68
69
    /**
70
     * Properties Keys (cached)
71
     * @var array
72
     */
73
    protected $propertiesMap = array();
74
    
75
    /**
76
     * Constructor
77
     * 
78
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
79
     */
80
    public function __construct()
81
    {
82
        $this->set('self', $this, true);
83
    }
84
    
85
    /**
86
     * Registers a definition
87
     * 
88
     * @param string  $name       Identifier
89
     * @param mixed   $definition Definition, callable or value
90
     * @param boolean $shared     Should the instance be "shared" (singleton)
91
     * @param array   $data       Meta-data associated with this definition
92
     * 
93
     * @return Container 
94
     */
95
    public function set($name, $definition, $shared = false, 
96
        array $data = array()
97
    ) {
98
        $data = array_merge(
99
            array(
100
                '__fwk_di_shared'   => $shared
101
            ), 
102
            $data
103
        );
104
105
        $this->store[$name] = $definition;
106
        $this->storeData[$name] = $data;
107
108
        return $this;
109
    }
110
    
111
    /**
112
     * Load and returns a definition
113
     * 
114
     * @param string $name Identifier
115
     * 
116
     * @throws Exceptions\DefinitionNotFound if $name isn't a valid identifier
117
     * @return mixed
118
     */
119
    public function get($name)
120
    {
121
        if ($name instanceof Reference) {
122
            $name = $name->getName();
123
        }
124
        
125
        if (!$this->exists($name)) {
126
            throw new Exceptions\DefinitionNotFound($name);
127
        }
128
        
129
        $data       = $this->storeData[$name];
130
        
131
        if ($data['__fwk_di_shared'] === true 
132
            && isset($data['__fwk_di_shared_inst'])
133
        ) {
134
            return $data['__fwk_di_shared_inst'];
135
        }
136
        
137
        $definition = $this->store[$name];
138
         
139
        if ($definition instanceof Invokable) {
140
            $return = $definition->invoke($this, $name);
141
        } elseif (is_callable($definition)) {
142
            $return = call_user_func_array($definition, array($this));
143
        } else {
144
            $return = $definition;
145
        }
146
        
147
        if ($data['__fwk_di_shared'] === true) {
148
            $this->storeData[$name]['__fwk_di_shared_inst'] = $return;
149
        }
150
        
151
        return $return;
152
    }
153
    
154
    /**
155
     * Loads properties from an INI file as definitions. 
156
     * Theses properties can then be referenced like @propName in other 
157
     * definitions.
158
     * 
159
     * @param string      $iniFile  Path/to/file.ini
160
     * @param null|string $category The INI category to be parsed
161
     * 
162
     * @return Container
163
     * @throws Exception
164
     */
165
    public function iniProperties($iniFile, $category = null)
166
    {
167
        if (!is_file($iniFile) || !is_readable($iniFile)) {
168
            throw new Exception('INI file not found/readable: '. $iniFile);
169
        }
170
        
171
        $props = parse_ini_file($iniFile, ($category !== null));
172
        if ($category !== null) {
173
            $props = (isset($props[$category]) ? $props[$category] : false);
174
        }
175
        
176
        if (!is_array($props)) {
177
            throw new Exception("No properties found in: $iniFile [$category]");
178
        }
179
        
180
        foreach ($props as $key => $prop) {
181
            $this->properties[$key] = str_replace(':packageDir', dirname($iniFile), $prop);
182
            $this->propertiesMap[$key] = ":". $key;
183
        }
184
        
185
        return $this;
186
    }
187
    
188
    /**
189
     * Returns a property (or $default if not defined) 
190
     * 
191
     * @param string $propName The property name
192
     * @param mixed  $default  Default value if the property is not defined
193
     * 
194
     * @return mixed
0 ignored issues
show
Consider making the return type a bit more specific; maybe use object|integer|double|null|array|boolean|string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
195
     */
196
    public function getProperty($propName, $default = null)
197
    {
198
        return (array_key_exists($propName, $this->properties) ? 
199
            $this->propertizeString($this->properties[$propName]) : 
200
            (is_string($default) ?
201
                $this->propertizeString($default) :
202
                $default
203
            )
204
        );
205
    }
206
    
207
    /**
208
     * Defines a property.
209
     * 
210
     * If the $value is null, the property will be unset.
211
     * 
212
     * It recommended to store only strings as property values. Register a
213
     * new Di definition for any other type.
214
     * 
215
     * @param string      $propName Property name
216
     * @param null|string $value    The prop value
217
     * 
218
     * @return Container
219
     */
220
    public function setProperty($propName, $value = null)
221
    {
222
        if (array_key_exists($propName, $this->properties) && $value === null) {
223
            unset($this->properties[$propName]);
224
            unset($this->propertiesMap[$propName]);
225
            return $this;
226
        }
227
        
228
        $this->properties[(string)$propName] = (string)$value;
229
        $this->propertiesMap[(string)$propName] = ":". (string)$propName;
230
        
231
        return $this;
232
    }
233
    
234
    
235
    /**
236
     * Transform properties references to their respective value
237
     * 
238
     * @param string $str String to be transformed
239
     * 
240
     * @return string
241
     */
242
    public function propertizeString($str)
243
    {
244
        return str_replace(
245
            array_values($this->propertiesMap),
246
            array_values($this->properties),
247
            $str
248
        );
249
    }
250
    
251
    /**
252
     * Unregisters a definition
253
     * 
254
     * @param string $name Identifier
255
     * 
256
     * @throws Exceptions\DefinitionNotFound if $name isn't a valid identifier
257
     * @return boolean true on success
258
     */
259
    public function unregister($name)
260
    {
261
        if (!$this->exists($name)) {
262
            throw new Exceptions\DefinitionNotFound($name);
263
        }
264
        
265
        unset($this->storeData[$name]);
266
        unset($this->store[$name]);
267
        
268
        return true;
269
    }
270
    
271
    /**
272
     * Tells if a definition has been flagged has "shared" (singleton)
273
     * 
274
     * @param string $name Identifier
275
     * 
276
     * @throws Exceptions\DefinitionNotFound if $name isn't a valid identifier
277
     * @return boolean
278
     */
279
    public function isShared($name)
280
    {
281
        if (!$this->exists($name)) {
282
            throw new Exceptions\DefinitionNotFound($name);
283
        }
284
        
285
        $data = $this->storeData[$name];
286
        
287
        return (bool)$data['__fwk_di_shared'];
288
    }
289
    
290
    /**
291
     * Tells if a definition exists at $offset
292
     * 
293
     * @param string $name Identifier
294
     * 
295
     * @return boolean
296
     */
297
    public function exists($name)
298
    {
299
        return array_key_exists($name, $this->store);
300
    }
301
    
302
    /**
303
     * Tells if a definition is registered at $offset
304
     * 
305
     * @param string $offset Identifier
306
     * 
307
     * @return boolean
308
     */
309
    public function offsetExists($offset)
310
    {
311
        return $this->exists($offset);
312
    }
313
    
314
    /**
315
     * Loads and returns a definition 
316
     * 
317
     * @param string $offset Identifier
318
     * 
319
     * @return mixed
320
     */
321
    public function offsetGet($offset)
322
    {
323
        return $this->get($offset);
324
    }
325
    
326
    /**
327
     * Registers a definition
328
     * 
329
     * @param string $offset Identifier
330
     * @param mixed  $value  Definition
331
     * 
332
     * @return Container
333
     */
334
    public function offsetSet($offset, $value)
335
    {
336
        return $this->set($offset, $value);
337
    }
338
    
339
    /**
340
     * Unregisters a Definition
341
     * 
342
     * @param string $offset Identifier
343
     * 
344
     * @return boolean
345
     */
346
    public function offsetUnset($offset)
347
    {
348
        return $this->unregister($offset);
349
    }
350
}