Completed
Push — master ( 0558ba...f9a35e )
by Guilh
07:04
created

RestXmlCollectionLoader::parseNode()   D

Complexity

Conditions 10
Paths 28

Size

Total Lines 45
Code Lines 31

Duplication

Lines 7
Ratio 15.56 %

Code Coverage

Tests 31
CRAP Score 10.2678

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 7
loc 45
ccs 31
cts 36
cp 0.8611
rs 4.8196
cc 10
eloc 31
nc 28
nop 4
crap 10.2678

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the FOSRestBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
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 FOS\RestBundle\Routing\Loader;
13
14
use FOS\RestBundle\Routing\RestRouteCollection;
15
use Symfony\Component\Config\FileLocatorInterface;
16
use Symfony\Component\Config\Util\XmlUtils;
17
use Symfony\Component\Routing\Loader\XmlFileLoader;
18
use Symfony\Component\Routing\RouteCollection;
19
20
/**
21
 * RestXmlCollectionLoader XML file collections loader.
22
 *
23
 * @author Donald Tyler <[email protected]>
24
 */
25
class RestXmlCollectionLoader extends XmlFileLoader
26
{
27
    protected $collectionParents = [];
28
    private $processor;
29
    private $includeFormat;
30
    private $formats;
31
    private $defaultFormat;
32
33
    /**
34
     * Initializes xml loader.
35
     *
36
     * @param FileLocatorInterface $locator
37
     * @param RestRouteProcessor   $processor
38
     * @param bool                 $includeFormat
39
     * @param string[]             $formats
40
     * @param string               $defaultFormat
41
     */
42 20
    public function __construct(
43
        FileLocatorInterface $locator,
44
        RestRouteProcessor $processor,
45
        $includeFormat = true,
46
        array $formats = [],
47
        $defaultFormat = null
48
    ) {
49 20
        parent::__construct($locator);
50
51 20
        $this->processor = $processor;
52 20
        $this->includeFormat = $includeFormat;
53 20
        $this->formats = $formats;
54 20
        $this->defaultFormat = $defaultFormat;
55 20
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60 8
    protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file)
61
    {
62 8
        switch ($node->tagName) {
63 8
            case 'route':
64 6
                $this->parseRoute($collection, $node, $path);
65 6
                break;
66 2
            case 'import':
67 2
                $name = (string) $node->getAttribute('id');
68 2
                $resource = (string) $node->getAttribute('resource');
69 2
                $prefix = (string) $node->getAttribute('prefix');
70 2
                $namePrefix = (string) $node->getAttribute('name-prefix');
71 2
                $parent = (string) $node->getAttribute('parent');
72 2
                $type = (string) $node->getAttribute('type');
73 2
                $host = isset($config['host']) ? $config['host'] : null;
0 ignored issues
show
Bug introduced by
The variable $config seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
74 2
                $currentDir = dirname($path);
75
76 2
                $parents = [];
77 2 View Code Duplication
                if (!empty($parent)) {
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...
78 2
                    if (!isset($this->collectionParents[$parent])) {
79
                        throw new \InvalidArgumentException(sprintf('Cannot find parent resource with name %s', $parent));
80
                    }
81
82 2
                    $parents = $this->collectionParents[$parent];
83 2
                }
84
85 2
                $imported = $this->processor->importResource($this, $resource, $parents, $prefix, $namePrefix, $type, $currentDir);
86
87 2
                if (!empty($name) && $imported instanceof RestRouteCollection) {
88 2
                    $parents[] = (!empty($prefix) ? $prefix.'/' : '').$imported->getSingularName();
89 2
                    $prefix = null;
90
91 2
                    $this->collectionParents[$name] = $parents;
92 2
                }
93
94 2
                if (!empty($host)) {
95
                    $imported->setHost($host);
96
                }
97
98 2
                $imported->addPrefix($prefix);
99 2
                $collection->addCollection($imported);
100 2
                break;
101
            default:
102
                throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
103 8
        }
104 8
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109 6
    protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
110
    {
111 6
        if ($this->includeFormat) {
112 5
            $path = $node->getAttribute('path');
113
            // append format placeholder if not present
114 5
            if (false === strpos($path, '{_format}')) {
115 5
                $node->setAttribute('path', $path.'.{_format}');
116 5
            }
117
118
            // set format requirement if configured globally
119 5
            $requirements = $node->getElementsByTagNameNS(self::NAMESPACE_URI, 'requirement');
120 5
            $format = null;
121 5
            for ($i = 0; $i < $requirements->length; ++$i) {
122
                $item = $requirements->item($i);
123
                if ($item instanceof \DOMElement && $item->hasAttribute('_format')) {
124
                    $format = $item->getAttribute('_format');
125
                    break;
126
                }
127
            }
128
129 5
            if (null === $format && !empty($this->formats)) {
130 5
                $requirement = $node->ownerDocument->createElementNs(
131 5
                    self::NAMESPACE_URI,
132 5
                    'requirement',
133 5
                    implode('|', array_keys($this->formats))
134 5
                );
135 5
                $requirement->setAttribute('key', '_format');
136 5
                $node->appendChild($requirement);
137 5
            }
138 5
        }
139
140
        // set the default format if configured
141 6
        if (null !== $this->defaultFormat) {
142 1
            $defaultFormatNode = $node->ownerDocument->createElementNS(
143 1
                self::NAMESPACE_URI,
144 1
                'default',
145 1
                $this->defaultFormat
146 1
            );
147 1
            $defaultFormatNode->setAttribute('key', '_format');
148 1
            $node->appendChild($defaultFormatNode);
149 1
        }
150
151 6
        $options = $this->getOptions($node);
152
153 6
        foreach ($options as $option) {
154 1
            $node->appendChild($option);
155 6
        }
156
157 6
        $length = $node->childNodes->length;
158 6
        for ($i = 0; $i < $length; ++$i) {
159 6
            $loopNode = $node->childNodes->item($i);
160 6
            if ($loopNode->nodeType === XML_TEXT_NODE) {
161 6
                continue;
162
            }
163
164 6
            $newNode = $node->ownerDocument->createElementNS(
165 6
                self::NAMESPACE_URI,
166 6
                $loopNode->nodeName,
167 6
                $loopNode->nodeValue
168 6
            );
169
170 6
            foreach ($loopNode->attributes as $value) {
171 6
                $newNode->setAttribute($value->name, $value->value);
172 6
            }
173
174 6
            $node->appendChild($newNode);
175 6
        }
176
177 6
        parent::parseRoute($collection, $node, $path);
178 6
    }
179
180 6
    private function getOptions(\DOMElement $node)
181
    {
182 6
        $options = [];
183 6
        foreach ($node->childNodes as $child) {
184 6
            if ($child instanceof \DOMElement && $child->tagName === 'option') {
185 1
                $option = $node->ownerDocument->createElementNs(
186 1
                    self::NAMESPACE_URI,
187 1
                    'option',
188 1
                    $child->nodeValue
189 1
                );
190 1
                $option->setAttribute('key', $child->getAttribute('key'));
191 1
                $options[] = $option;
192 1
            }
193 6
        }
194
195 6
        return $options;
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201 2
    public function supports($resource, $type = null)
202
    {
203 2
        return is_string($resource) &&
204 2
            'xml' === pathinfo($resource, PATHINFO_EXTENSION) &&
205 2
            'rest' === $type;
206
    }
207
208
    /**
209
     * @param \DOMDocument $dom
210
     *
211
     * @throws \InvalidArgumentException When xml doesn't validate its xsd schema
212
     */
213 8
    protected function validate(\DOMDocument $dom)
214
    {
215 8
        $restRoutinglocation = realpath(__DIR__.'/../../Resources/config/schema/routing/rest_routing-1.0.xsd');
216 8
        $restRoutinglocation = rawurlencode(str_replace('\\', '/', $restRoutinglocation));
217 8
        $routinglocation = realpath(__DIR__.'/../../Resources/config/schema/routing-1.0.xsd');
218 8
        $routinglocation = rawurlencode(str_replace('\\', '/', $routinglocation));
219
        $source = <<<EOF
220
<?xml version="1.0" encoding="utf-8" ?>
221
<xsd:schema xmlns="http://symfony.com/schema"
222
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
223
    targetNamespace="http://symfony.com/schema"
224
    elementFormDefault="qualified">
225
226
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
227
    <xsd:import namespace="http://friendsofsymfony.github.com/schema/rest" schemaLocation="$restRoutinglocation" />
228 8
    <xsd:import namespace="http://symfony.com/schema/routing" schemaLocation="$routinglocation" />
229 8
</xsd:schema>
230 8
EOF;
231
232 8
        $current = libxml_use_internal_errors(true);
233 8
        libxml_clear_errors();
234
235 8
        if (!$dom->schemaValidateSource($source)) {
236
            throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors_($current)));
237
        }
238 8
        libxml_use_internal_errors($current);
239 8
    }
240
241
    /**
242
     * {@inheritdoc}
243
     *
244
     * @internal
245
     */
246 8
    protected function loadFile($file)
247
    {
248 8
        if (class_exists('Symfony\Component\Config\Util\XmlUtils')) {
249 8
            $dom = XmlUtils::loadFile($file);
250 8
            $this->validate($dom);
251
252 8
            return $dom;
253
        }
254
255
        return parent::loadFile($file);
256
    }
257
258
    /**
259
     * Retrieves libxml errors and clears them.
260
     *
261
     * Note: The underscore postfix on the method name is to ensure compatibility with versions
262
     *       before 2.0.16 while working around a bug in PHP https://bugs.php.net/bug.php?id=62956
263
     *
264
     * @param bool $internalErrors The previous state of internal errors to reset it
265
     *
266
     * @return array An array of libxml error strings
267
     */
268
    private function getXmlErrors_($internalErrors)
269
    {
270
        $errors = [];
271
        foreach (libxml_get_errors() as $error) {
272
            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
273
                LIBXML_ERR_WARNING === $error->level ? 'WARNING' : 'ERROR',
274
                $error->code,
275
                trim($error->message),
276
                $error->file ? $error->file : 'n/a',
277
                $error->line,
278
                $error->column
279
            );
280
        }
281
282
        libxml_clear_errors();
283
        libxml_use_internal_errors($internalErrors);
284
285
        return $errors;
286
    }
287
}
288