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

ContainerBuilder::applyClassDefinitions()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 1 Features 4
Metric Value
c 6
b 1
f 4
dl 0
loc 24
rs 8.6845
cc 4
eloc 16
nc 5
nop 2
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\Xml;
34
35
use Fwk\Di\Definitions\ClassDefinition;
36
use Fwk\Di\Definitions\LazyClassDefinition;
37
use Fwk\Di\Exception;
38
use Fwk\Xml\Map;
39
use Fwk\Di\Container;
40
use Fwk\Xml\XmlFile;
41
use Fwk\Di\Definitions\ArrayDefinition;
42
43
/**
44
 * ContainerBuilder
45
 * 
46
 * Builds or extends an existing Container using an Xml Map
47
 *
48
 * @category Xml
49
 * @package  Fwk\Di
50
 * @author   Julien Ballestracci <[email protected]>
51
 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
52
 * @link     http://www.nitronet.org/fwk
53
 */
54
class ContainerBuilder
55
{
56
    /**
57
     * The Map that should return definitions from an Xml file
58
     * @var Map
59
     */
60
    protected $map;
61
    
62
    /**
63
     * Constructor
64
     * 
65
     * If no Map is specified, the builder will use the ContainerXmlMap.
66
     * 
67
     * @param null|Map $map The Xml Map used to parse the Xml file
68
     * 
69
     * @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...
70
     */
71
    public function __construct(Map $map = null)
72
    {
73
        if (is_null($map)) {
74
            $map = new ContainerXmlMap();
75
        }
76
        
77
        $this->map = $map;
78
    }
79
    
80
    /**
81
     * Registers Xml definitions to the provided Container.
82
     * If no Container is provided, a new one will be created.
83
     * 
84
     * @param string|XmlFile $file      Path to Xml (or XmlFile instance)
85
     * @param null|Container $container Container where definitions are added
86
     * 
87
     * @return Container
88
     */
89
    public function execute($file, Container $container = null)
90
    {
91
        if (!$file instanceof XmlFile) {
92
            $file = new XmlFile($file);
93
        }
94
        if (null === $container) {
95
            $container = new Container();
96
        }
97
        
98
        $results = $this->map->execute($file);
99
        
100
        $container->setProperty('packageDir', dirname($file->getRealPath()));
101
        $this->applyIniFiles($results['ini'], $container, $file);
102
        $this->applyDefinitions($results['definitions'], $container);
103
        $this->applyArrayDefinitions($results['arrayDefs'], $container);
104
        $this->applyClassDefinitions($results['classDefs'], $container);
105
        $this->applyListeners($results['listeners'], $container);
106
107
        return $container;
108
    }
109
    
110
    
111
    /**
112
     * Converts XML definitions from parsing results
113
     * 
114
     * @param array     $inis      Parsing results
115
     * @param Container $container The Di Container
116
     * @param XmlFile   $file      The XmlFile instance
117
     * 
118
     * @return void
119
     */
120
    protected function applyIniFiles(array $inis, Container $container, 
121
        XmlFile $file
122
    ) {
123
        foreach ($inis as $infos) {
124
            $container->iniProperties(
125
                str_replace(
126
                    ':baseDir', 
127
                    dirname($file->getRealPath()), 
128
                    $infos['value']
129
                ), 
130
                $infos['category']
131
            );
132
        }
133
    }
134
    
135
    /**
136
     * Converts XML definitions from parsing results
137
     * 
138
     * @param array     $definitions Parsing results
139
     * @param Container $container   The Di Container
140
     * 
141
     * @return void
142
     */
143
    protected function applyDefinitions(array $definitions, 
144
        Container $container
145
    ) {
146
        foreach ($definitions as $name => $infos) {
147
            $container->set(
148
                $container->propertizeString($name), 
149
                $container->propertizeString($infos['value'])
150
            );
151
        }
152
    }
153
    
154
    /**
155
     * Converts XML class definitions from parsing results
156
     * 
157
     * @param array     $classDefs Parsing results
158
     * @param Container $container The Di Container
159
     * 
160
     * @return void
161
     */
162
    protected function applyClassDefinitions(array $classDefs, 
163
        Container $container
164
    ) {
165
        foreach ($classDefs as $name => $infos) {
166
            $shared = (bool)$this->transformValueType($infos['shared']);
167
            $lazy = (bool)$this->transformValueType($infos['lazy']);
168
            $defClass = ($lazy ? LazyClassDefinition::class : ClassDefinition::class);
169
            $def = new $defClass(
170
                $infos['className'], 
171
                $infos['arguments']
172
            );
173
            foreach ($infos['methodsCalls'] as $mnfos) {
174
                $def->addMethodCall(
175
                    $container->propertizeString($mnfos['method']), 
176
                    $mnfos['arguments']
177
                );
178
            }
179
180
            $def->setShared($shared)
181
                ->setData($infos['data']);
182
            
183
            $container->set($name, $def);
184
        }
185
    }
186
    
187
    /**
188
     * Converts XML Array definitions from parsing results
189
     * 
190
     * @param array     $arrayDefs Parsing results
191
     * @param Container $container The Di Container
192
     * 
193
     * @return void
194
     */
195
    protected function applyArrayDefinitions(array $arrayDefs, 
196
        Container $container
197
    ) {
198
        foreach ($arrayDefs as $name => $infos) {
199
            $shared = (bool)$this->transformValueType($infos['shared']);
200
            $array  = array();
201
            foreach ($infos['params'] as $mnfos) {
202
                $key = (empty($mnfos['key']) ? null : $mnfos['key']);
203
                $val = $this->transformValueType($mnfos['value']);
204
                
205
                if (!empty($key)) {
206
                    $array[$key] = $val;
207
                } else {
208
                    $array[] = $val;
209
                }
210
            }
211
212
            $def = ArrayDefinition::factory($array)
213
                    ->setShared($shared)
214
                    ->setData($infos['data']);
215
216
            $container->set($name, $def);
217
        }
218
    }
219
220
    /**
221
     * Converts XML definitions from parsing results
222
     *
223
     * @param array     $listeners Parsing results
224
     * @param Container $container The Di Container
225
     *
226
     * @return void
227
     */
228
    protected function applyListeners(array $listeners, Container $container)
229
    {
230
        foreach ($listeners as $infos) {
231
            $class = $infos['class'];
232
            $service = $infos['service'];
233
234
            if (empty($class) && empty($service)) {
235
                throw new Exception('Invalid Xml Listener: either "class" or "service" attribute must be defined.');
236
            }
237
238
            if (!empty($class)) {
239
                $def = new ClassDefinition($class);
240
                $container->addListener($def->invoke($container));
241
                continue;
242
            }
243
244
            if (!$container->has($service)) {
245
                throw new Exception(sprintf('Invalid Xml Listener service ID: "%s"', $service));
246
            }
247
            $container->addListener($container->get($service));
248
        }
249
    }
250
    
251
    /**
252
     * Transforms a string to a type, if known:
253
     * 
254
     * - boolean: true / false
255
     * - null: null
256
     * 
257
     * @param string $value The initial string value
258
     * 
259
     * @return null|string|boolean
260
     */
261
    protected function transformValueType($value)
262
    {
263
        $value = trim($value);
264
        if (strtolower($value) === "true") {
265
            $value = true;
266
        } elseif (strtolower($value) === "false") {
267
            $value = false;
268
        } elseif (strtolower($value) === "null") {
269
            $value = null;
270
        }
271
        
272
        return $value;
273
    }
274
    
275
    /**
276
     * Returns the Xml Map
277
     * 
278
     * @return Map
279
     */
280
    public function getMap()
281
    {
282
        return $this->map;
283
    }
284
285
    /**
286
     * Defines the Xml Map used to parse definitions from the Xml file
287
     * 
288
     * @param Map $map The Xml Map 
289
     * 
290
     * @return void
291
     */
292
    public function setMap(Map $map)
293
    {
294
        $this->map = $map;
295
    }
296
}