XmlDumper   C
last analyzed

Complexity

Total Complexity 65

Size/Duplication

Total Lines 327
Duplicated Lines 14.98 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 65
c 1
b 0
f 0
lcom 1
cbo 1
dl 49
loc 327
rs 5.7894

9 Methods

Rating   Name   Duplication   Size   Complexity  
F addService() 28 100 29
A escape() 15 15 4
A dump() 0 18 1
A addParameters() 0 15 3
A addMethodCalls() 0 11 3
A addServiceAlias() 0 10 2
B addServices() 6 21 5
C convertParameters() 0 41 11
B phpToXml() 0 17 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like XmlDumper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use XmlDumper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Symfony\Component\DependencyInjection\Dumper;
13
14
use Symfony\Component\DependencyInjection\ContainerInterface;
15
use Symfony\Component\DependencyInjection\Parameter;
16
use Symfony\Component\DependencyInjection\Reference;
17
use Symfony\Component\DependencyInjection\Definition;
18
use Symfony\Component\DependencyInjection\Alias;
19
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
20
use Symfony\Component\ExpressionLanguage\Expression;
21
22
/**
23
 * XmlDumper dumps a service container as an XML string.
24
 *
25
 * @author Fabien Potencier <[email protected]>
26
 * @author Martin Hasoň <[email protected]>
27
 *
28
 * @api
29
 */
30
class XmlDumper extends Dumper
31
{
32
    /**
33
     * @var \DOMDocument
34
     */
35
    private $document;
36
37
    /**
38
     * Dumps the service container as an XML string.
39
     *
40
     * @param array $options An array of options
41
     *
42
     * @return string An xml string representing of the service container
43
     *
44
     * @api
45
     */
46
    public function dump(array $options = array())
47
    {
48
        $this->document = new \DOMDocument('1.0', 'utf-8');
49
        $this->document->formatOutput = true;
50
51
        $container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container');
52
        $container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
53
        $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd');
54
55
        $this->addParameters($container);
56
        $this->addServices($container);
57
58
        $this->document->appendChild($container);
59
        $xml = $this->document->saveXML();
60
        $this->document = null;
61
62
        return $xml;
63
    }
64
65
    /**
66
     * Adds parameters.
67
     *
68
     * @param \DOMElement $parent
69
     */
70
    private function addParameters(\DOMElement $parent)
71
    {
72
        $data = $this->container->getParameterBag()->all();
73
        if (!$data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
74
            return;
75
        }
76
77
        if ($this->container->isFrozen()) {
78
            $data = $this->escape($data);
79
        }
80
81
        $parameters = $this->document->createElement('parameters');
82
        $parent->appendChild($parameters);
83
        $this->convertParameters($data, 'parameter', $parameters);
84
    }
85
86
    /**
87
     * Adds method calls.
88
     *
89
     * @param array       $methodcalls
90
     * @param \DOMElement $parent
91
     */
92
    private function addMethodCalls(array $methodcalls, \DOMElement $parent)
93
    {
94
        foreach ($methodcalls as $methodcall) {
95
            $call = $this->document->createElement('call');
96
            $call->setAttribute('method', $methodcall[0]);
97
            if (count($methodcall[1])) {
98
                $this->convertParameters($methodcall[1], 'argument', $call);
99
            }
100
            $parent->appendChild($call);
101
        }
102
    }
103
104
    /**
105
     * Adds a service.
106
     *
107
     * @param Definition  $definition
108
     * @param string      $id
109
     * @param \DOMElement $parent
110
     */
111
    private function addService($definition, $id, \DOMElement $parent)
112
    {
113
        $service = $this->document->createElement('service');
114
        if (null !== $id) {
115
            $service->setAttribute('id', $id);
116
        }
117
        if ($definition->getClass()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $definition->getClass() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
118
            $service->setAttribute('class', $definition->getClass());
119
        }
120
        if ($definition->getFactoryMethod(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Bug Best Practice introduced by
The expression $definition->getFactoryMethod(false) of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
121
            $service->setAttribute('factory-method', $definition->getFactoryMethod(false));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
122
        }
123
        if ($definition->getFactoryClass(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Bug Best Practice introduced by
The expression $definition->getFactoryClass(false) of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
124
            $service->setAttribute('factory-class', $definition->getFactoryClass(false));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
125
        }
126
        if ($definition->getFactoryService(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Bug Best Practice introduced by
The expression $definition->getFactoryService(false) of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
127
            $service->setAttribute('factory-service', $definition->getFactoryService(false));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
128
        }
129
        if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) {
130
            $service->setAttribute('scope', $scope);
131
        }
132
        if (!$definition->isPublic()) {
133
            $service->setAttribute('public', 'false');
134
        }
135
        if ($definition->isSynthetic()) {
136
            $service->setAttribute('synthetic', 'true');
137
        }
138
        if ($definition->isSynchronized(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ition::isSynchronized() has been deprecated with message: since version 2.7, will be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
139
            $service->setAttribute('synchronized', 'true');
140
        }
141
        if ($definition->isLazy()) {
142
            $service->setAttribute('lazy', 'true');
143
        }
144
        if (null !== $decorated = $definition->getDecoratedService()) {
145
            list($decorated, $renamedId) = $decorated;
146
            $service->setAttribute('decorates', $decorated);
147
            if (null !== $renamedId) {
148
                $service->setAttribute('decoration-inner-name', $renamedId);
149
            }
150
        }
151
152
        foreach ($definition->getTags() as $name => $tags) {
153
            foreach ($tags as $attributes) {
154
                $tag = $this->document->createElement('tag');
155
                $tag->setAttribute('name', $name);
156
                foreach ($attributes as $key => $value) {
157
                    $tag->setAttribute($key, $value);
158
                }
159
                $service->appendChild($tag);
160
            }
161
        }
162
163
        if ($definition->getFile()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $definition->getFile() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
164
            $file = $this->document->createElement('file');
165
            $file->appendChild($this->document->createTextNode($definition->getFile()));
166
            $service->appendChild($file);
167
        }
168
169
        if ($parameters = $definition->getArguments()) {
170
            $this->convertParameters($parameters, 'argument', $service);
171
        }
172
173
        if ($parameters = $definition->getProperties()) {
174
            $this->convertParameters($parameters, 'property', $service, 'name');
175
        }
176
177
        $this->addMethodCalls($definition->getMethodCalls(), $service);
178
179 View Code Duplication
        if ($callable = $definition->getFactory()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
180
            $factory = $this->document->createElement('factory');
181
182
            if (is_array($callable) && $callable[0] instanceof Definition) {
183
                $this->addService($callable[0], null, $factory);
184
                $factory->setAttribute('method', $callable[1]);
185
            } elseif (is_array($callable)) {
186
                $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]);
187
                $factory->setAttribute('method', $callable[1]);
188
            } else {
189
                $factory->setAttribute('function', $callable);
190
            }
191
            $service->appendChild($factory);
192
        }
193
194 View Code Duplication
        if ($callable = $definition->getConfigurator()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
195
            $configurator = $this->document->createElement('configurator');
196
197
            if (is_array($callable) && $callable[0] instanceof Definition) {
198
                $this->addService($callable[0], null, $configurator);
199
                $configurator->setAttribute('method', $callable[1]);
200
            } elseif (is_array($callable)) {
201
                $configurator->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]);
202
                $configurator->setAttribute('method', $callable[1]);
203
            } else {
204
                $configurator->setAttribute('function', $callable);
205
            }
206
            $service->appendChild($configurator);
207
        }
208
209
        $parent->appendChild($service);
210
    }
211
212
    /**
213
     * Adds a service alias.
214
     *
215
     * @param string      $alias
216
     * @param Alias       $id
217
     * @param \DOMElement $parent
218
     */
219
    private function addServiceAlias($alias, Alias $id, \DOMElement $parent)
220
    {
221
        $service = $this->document->createElement('service');
222
        $service->setAttribute('id', $alias);
223
        $service->setAttribute('alias', $id);
224
        if (!$id->isPublic()) {
225
            $service->setAttribute('public', 'false');
226
        }
227
        $parent->appendChild($service);
228
    }
229
230
    /**
231
     * Adds services.
232
     *
233
     * @param \DOMElement $parent
234
     */
235
    private function addServices(\DOMElement $parent)
236
    {
237
        $definitions = $this->container->getDefinitions();
238
        if (!$definitions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $definitions of type Symfony\Component\DependencyInjection\Definition[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
239
            return;
240
        }
241
242
        $services = $this->document->createElement('services');
243
        foreach ($definitions as $id => $definition) {
244
            $this->addService($definition, $id, $services);
245
        }
246
247
        $aliases = $this->container->getAliases();
248 View Code Duplication
        foreach ($aliases as $alias => $id) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
249
            while (isset($aliases[(string) $id])) {
250
                $id = $aliases[(string) $id];
251
            }
252
            $this->addServiceAlias($alias, $id, $services);
253
        }
254
        $parent->appendChild($services);
255
    }
256
257
    /**
258
     * Converts parameters.
259
     *
260
     * @param array       $parameters
261
     * @param string      $type
262
     * @param \DOMElement $parent
263
     * @param string      $keyAttribute
264
     */
265
    private function convertParameters($parameters, $type, \DOMElement $parent, $keyAttribute = 'key')
266
    {
267
        $withKeys = array_keys($parameters) !== range(0, count($parameters) - 1);
268
        foreach ($parameters as $key => $value) {
269
            $element = $this->document->createElement($type);
270
            if ($withKeys) {
271
                $element->setAttribute($keyAttribute, $key);
272
            }
273
274
            if (is_array($value)) {
275
                $element->setAttribute('type', 'collection');
276
                $this->convertParameters($value, $type, $element, 'key');
277
            } elseif ($value instanceof Reference) {
278
                $element->setAttribute('type', 'service');
279
                $element->setAttribute('id', (string) $value);
280
                $behaviour = $value->getInvalidBehavior();
281
                if ($behaviour == ContainerInterface::NULL_ON_INVALID_REFERENCE) {
282
                    $element->setAttribute('on-invalid', 'null');
283
                } elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) {
284
                    $element->setAttribute('on-invalid', 'ignore');
285
                }
286
                if (!$value->isStrict()) {
287
                    $element->setAttribute('strict', 'false');
288
                }
289
            } elseif ($value instanceof Definition) {
290
                $element->setAttribute('type', 'service');
291
                $this->addService($value, null, $element);
292
            } elseif ($value instanceof Expression) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\ExpressionLanguage\Expression does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
293
                $element->setAttribute('type', 'expression');
294
                $text = $this->document->createTextNode(self::phpToXml((string) $value));
295
                $element->appendChild($text);
296
            } else {
297
                if (in_array($value, array('null', 'true', 'false'), true)) {
298
                    $element->setAttribute('type', 'string');
299
                }
300
                $text = $this->document->createTextNode(self::phpToXml($value));
301
                $element->appendChild($text);
302
            }
303
            $parent->appendChild($element);
304
        }
305
    }
306
307
    /**
308
     * Escapes arguments.
309
     *
310
     * @param array $arguments
311
     *
312
     * @return array
313
     */
314 View Code Duplication
    private function escape($arguments)
315
    {
316
        $args = array();
317
        foreach ($arguments as $k => $v) {
318
            if (is_array($v)) {
319
                $args[$k] = $this->escape($v);
320
            } elseif (is_string($v)) {
321
                $args[$k] = str_replace('%', '%%', $v);
322
            } else {
323
                $args[$k] = $v;
324
            }
325
        }
326
327
        return $args;
328
    }
329
330
    /**
331
     * Converts php types to xml types.
332
     *
333
     * @param mixed $value Value to convert
334
     *
335
     * @return string
336
     *
337
     * @throws RuntimeException When trying to dump object or resource
338
     */
339
    public static function phpToXml($value)
340
    {
341
        switch (true) {
342
            case null === $value:
343
                return 'null';
344
            case true === $value:
345
                return 'true';
346
            case false === $value:
347
                return 'false';
348
            case $value instanceof Parameter:
349
                return '%'.$value.'%';
350
            case is_object($value) || is_resource($value):
351
                throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
352
            default:
353
                return (string) $value;
354
        }
355
    }
356
}
357