These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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 | View Code Duplication | public function __construct( |
0 ignored issues
–
show
|
|||
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 | break; |
|
66 | 3 | case 'import': |
|
67 | 3 | $name = (string) $node->getAttribute('id'); |
|
68 | 3 | $resource = (string) $node->getAttribute('resource'); |
|
69 | 3 | $prefix = (string) $node->getAttribute('prefix'); |
|
70 | 3 | $namePrefix = (string) $node->getAttribute('name-prefix'); |
|
71 | 3 | $parent = (string) $node->getAttribute('parent'); |
|
72 | 3 | $type = (string) $node->getAttribute('type'); |
|
73 | 3 | $host = isset($config['host']) ? $config['host'] : null; |
|
74 | 3 | $currentDir = dirname($path); |
|
75 | |||
76 | 3 | $parents = []; |
|
77 | 3 | View Code Duplication | if (!empty($parent)) { |
78 | 3 | if (!isset($this->collectionParents[$parent])) { |
|
79 | 1 | 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 | 10 | protected function validate(\DOMDocument $dom) |
|
214 | { |
||
215 | 10 | $restRoutinglocation = realpath(__DIR__.'/../../Resources/config/schema/routing/rest_routing-1.0.xsd'); |
|
216 | 10 | $restRoutinglocation = rawurlencode(str_replace('\\', '/', $restRoutinglocation)); |
|
217 | 10 | $routinglocation = realpath(__DIR__.'/../../Resources/config/schema/routing-1.0.xsd'); |
|
218 | 10 | $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 | 10 | <xsd:import namespace="http://symfony.com/schema/routing" schemaLocation="$routinglocation" /> |
|
229 | 10 | </xsd:schema> |
|
230 | 10 | EOF; |
|
231 | |||
232 | 10 | $current = libxml_use_internal_errors(true); |
|
233 | 10 | libxml_clear_errors(); |
|
234 | |||
235 | 10 | if (!$dom->schemaValidateSource($source)) { |
|
236 | 1 | throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors_($current))); |
|
237 | } |
||
238 | 9 | libxml_use_internal_errors($current); |
|
239 | 9 | } |
|
240 | |||
241 | /** |
||
242 | * {@inheritdoc} |
||
243 | * |
||
244 | * @internal |
||
245 | */ |
||
246 | 10 | protected function loadFile($file) |
|
247 | { |
||
248 | 10 | if (class_exists('Symfony\Component\Config\Util\XmlUtils')) { |
|
249 | 10 | $dom = XmlUtils::loadFile($file); |
|
250 | 10 | $this->validate($dom); |
|
251 | |||
252 | 9 | 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 | 1 | private function getXmlErrors_($internalErrors) |
|
269 | { |
||
270 | 1 | $errors = []; |
|
271 | 1 | foreach (libxml_get_errors() as $error) { |
|
272 | 1 | $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', |
|
273 | 1 | LIBXML_ERR_WARNING === $error->level ? 'WARNING' : 'ERROR', |
|
274 | 1 | $error->code, |
|
275 | 1 | trim($error->message), |
|
276 | 1 | $error->file ? $error->file : 'n/a', |
|
277 | 1 | $error->line, |
|
278 | 1 | $error->column |
|
279 | 1 | ); |
|
280 | 1 | } |
|
281 | |||
282 | 1 | libxml_clear_errors(); |
|
283 | 1 | libxml_use_internal_errors($internalErrors); |
|
284 | |||
285 | 1 | return $errors; |
|
286 | } |
||
287 | } |
||
288 |
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.