Completed
Pull Request — 2.x (#2251)
by Christian
03:46 queued 12s
created

RestXmlCollectionLoader::parseRoute()   C

Complexity

Conditions 13
Paths 108

Size

Total Lines 71

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 39
CRAP Score 13.1359

Importance

Changes 0
Metric Value
dl 0
loc 71
ccs 39
cts 43
cp 0.907
rs 5.8727
c 0
b 0
f 0
cc 13
nc 108
nop 3
crap 13.1359

How to fix   Long Method    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 1
@trigger_error(sprintf('The %s\RestXmlCollectionLoader class is deprecated since FOSRestBundle 2.8.', __NAMESPACE__), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
15
16
use FOS\RestBundle\Routing\RestRouteCollection;
17
use Symfony\Component\Config\FileLocatorInterface;
18
use Symfony\Component\Config\Util\XmlUtils;
19
use Symfony\Component\Routing\Loader\XmlFileLoader;
20
use Symfony\Component\Routing\RouteCollection;
21
22
/**
23
 * RestXmlCollectionLoader XML file collections loader.
24
 *
25
 * @author Donald Tyler <[email protected]>
26
 *
27
 * @deprecated since 2.8
28
 */
29
class RestXmlCollectionLoader extends XmlFileLoader
30
{
31
    protected $collectionParents = [];
32
    private $processor;
33
    private $includeFormat;
34
    private $formats;
35
    private $defaultFormat;
36
37
    /**
38
     * @param string[] $formats
39
     */
40 14 View Code Duplication
    public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
41
        FileLocatorInterface $locator,
42
        RestRouteProcessor $processor,
43
        bool $includeFormat = true,
44
        array $formats = [],
45
        string $defaultFormat = null
46
    ) {
47 14
        parent::__construct($locator);
48
49 14
        $this->processor = $processor;
50 14
        $this->includeFormat = $includeFormat;
51 14
        $this->formats = $formats;
52 14
        $this->defaultFormat = $defaultFormat;
53 14
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58 9
    protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file)
59
    {
60 9
        switch ($node->tagName) {
61 9
            case 'route':
62 6
                $this->parseRoute($collection, $node, $path);
63
64 6
                break;
65 3
            case 'import':
66 3
                $name = (string) $node->getAttribute('id');
67 3
                $resource = (string) $node->getAttribute('resource');
68 3
                $prefix = (string) $node->getAttribute('prefix');
69 3
                $namePrefix = (string) $node->getAttribute('name-prefix');
70 3
                $parent = (string) $node->getAttribute('parent');
71 3
                $type = (string) $node->getAttribute('type');
72 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...
73 3
                $currentDir = dirname($path);
74
75 3
                $parents = [];
76 3 View Code Duplication
                if (!empty($parent)) {
77 1
                    if (!isset($this->collectionParents[$parent])) {
78 1
                        throw new \InvalidArgumentException(sprintf('Cannot find parent resource with name %s', $parent));
79
                    }
80
81
                    $parents = $this->collectionParents[$parent];
82
                }
83
84 2
                $imported = $this->processor->importResource($this, $resource, $parents, $prefix, $namePrefix, $type, $currentDir);
85
86
                if (!empty($name) && $imported instanceof RestRouteCollection) {
87
                    $parents[] = (!empty($prefix) ? $prefix.'/' : '').$imported->getSingularName();
88
                    $prefix = null;
89
90
                    $this->collectionParents[$name] = $parents;
91
                }
92
93
                if (!empty($host)) {
94
                    $imported->setHost($host);
95
                }
96
97
                $imported->addPrefix((string) $prefix);
98
                $collection->addCollection($imported);
0 ignored issues
show
Documentation introduced by
$imported is of type object<Symfony\Component\Routing\RouteCollection>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
99
100
                break;
101
            default:
102
                throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
103
        }
104 6
    }
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
            }
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
126
                    break;
127
                }
128
            }
129
130 5
            if (null === $format && !empty($this->formats)) {
131 5
                $requirement = $node->ownerDocument->createElementNs(
132 5
                    self::NAMESPACE_URI,
133 5
                    'requirement',
134 5
                    implode('|', array_keys($this->formats))
135
                );
136 5
                $requirement->setAttribute('key', '_format');
137 5
                $node->appendChild($requirement);
138
            }
139
        }
140
141
        // set the default format if configured
142 6
        if (null !== $this->defaultFormat) {
143 1
            $defaultFormatNode = $node->ownerDocument->createElementNS(
144 1
                self::NAMESPACE_URI,
145 1
                'default',
146 1
                $this->defaultFormat
147
            );
148 1
            $defaultFormatNode->setAttribute('key', '_format');
149 1
            $node->appendChild($defaultFormatNode);
150
        }
151
152 6
        $options = $this->getOptions($node);
153
154 6
        foreach ($options as $option) {
155 1
            $node->appendChild($option);
156
        }
157
158 6
        $length = $node->childNodes->length;
159 6
        for ($i = 0; $i < $length; ++$i) {
160 6
            $loopNode = $node->childNodes->item($i);
161 6
            if (XML_TEXT_NODE === $loopNode->nodeType) {
162 6
                continue;
163
            }
164
165 6
            $newNode = $node->ownerDocument->createElementNS(
166 6
                self::NAMESPACE_URI,
167 6
                $loopNode->nodeName,
168 6
                $loopNode->nodeValue
169
            );
170
171 6
            foreach ($loopNode->attributes as $value) {
172 6
                $newNode->setAttribute($value->name, $value->value);
173
            }
174
175 6
            $node->appendChild($newNode);
176
        }
177
178 6
        parent::parseRoute($collection, $node, $path);
179 6
    }
180
181 6
    private function getOptions(\DOMElement $node)
182
    {
183 6
        $options = [];
184 6
        foreach ($node->childNodes as $child) {
185 6
            if ($child instanceof \DOMElement && 'option' === $child->tagName) {
186 1
                $option = $node->ownerDocument->createElementNs(
187 1
                    self::NAMESPACE_URI,
188 1
                    'option',
189 1
                    $child->nodeValue
190
                );
191 1
                $option->setAttribute('key', $child->getAttribute('key'));
192 1
                $options[] = $option;
193
            }
194
        }
195
196 6
        return $options;
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 5
    public function supports($resource, $type = null)
203
    {
204 5
        return is_string($resource) &&
205 5
            'xml' === pathinfo($resource, PATHINFO_EXTENSION) &&
206 5
            'rest' === $type;
207
    }
208
209 10
    protected function validate(\DOMDocument $dom)
210
    {
211 10
        $restRoutinglocation = realpath(__DIR__.'/../../Resources/config/schema/routing/rest_routing-1.0.xsd');
212 10
        $restRoutinglocation = rawurlencode(str_replace('\\', '/', $restRoutinglocation));
213 10
        $routinglocation = realpath(__DIR__.'/../../Resources/config/schema/routing-1.0.xsd');
214 10
        $routinglocation = rawurlencode(str_replace('\\', '/', $routinglocation));
215
        $source = <<<EOF
216 10
<?xml version="1.0" encoding="utf-8" ?>
217
<xsd:schema xmlns="http://symfony.com/schema"
218
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
219
    targetNamespace="http://symfony.com/schema"
220
    elementFormDefault="qualified">
221
222
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
223 10
    <xsd:import namespace="http://friendsofsymfony.github.com/schema/rest" schemaLocation="$restRoutinglocation" />
224 10
    <xsd:import namespace="http://symfony.com/schema/routing" schemaLocation="$routinglocation" />
225
</xsd:schema>
226
EOF;
227
228 10
        $current = libxml_use_internal_errors(true);
229 10
        libxml_clear_errors();
230
231 10
        if (!$dom->schemaValidateSource($source)) {
232 1
            throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors_($current)));
233
        }
234 9
        libxml_use_internal_errors($current);
235 9
    }
236
237
    /**
238
     * {@inheritdoc}
239
     *
240
     * @internal
241
     */
242 10
    protected function loadFile($file)
243
    {
244 10
        if (class_exists(XmlUtils::class)) {
245 10
            $dom = XmlUtils::loadFile($file);
246 10
            $this->validate($dom);
247
248 9
            return $dom;
249
        }
250
251
        return parent::loadFile($file);
252
    }
253
254
    /**
255
     * Retrieves libxml errors and clears them.
256
     *
257
     * Note: The underscore postfix on the method name is to ensure compatibility with versions
258
     *       before 2.0.16 while working around a bug in PHP https://bugs.php.net/bug.php?id=62956
259
     */
260 1
    private function getXmlErrors_(bool $internalErrors): array
261
    {
262 1
        $errors = [];
263 1
        foreach (libxml_get_errors() as $error) {
264 1
            $errors[] = sprintf(
265 1
                '[%s %s] %s (in %s - line %d, column %d)',
266 1
                LIBXML_ERR_WARNING === $error->level ? 'WARNING' : 'ERROR',
267 1
                $error->code,
268 1
                trim($error->message),
269 1
                $error->file ? $error->file : 'n/a',
270 1
                $error->line,
271 1
                $error->column
272
            );
273
        }
274
275 1
        libxml_clear_errors();
276 1
        libxml_use_internal_errors($internalErrors);
277
278 1
        return $errors;
279
    }
280
}
281