Completed
Push — master ( 52cb63...513572 )
by Christian
18:28 queued 08:10
created

RestXmlCollectionLoader::parseNode()   C

Complexity

Conditions 10
Paths 28

Size

Total Lines 47
Code Lines 31

Duplication

Lines 7
Ratio 14.89 %

Code Coverage

Tests 32
CRAP Score 10.1371

Importance

Changes 0
Metric Value
dl 7
loc 47
ccs 32
cts 36
cp 0.8889
rs 5.1578
c 0
b 0
f 0
cc 10
eloc 31
nc 28
nop 4
crap 10.1371

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