8a6e8ce35989f8a8783a081ba4c11a43ffa7d319
[yaffs-website] / vendor / symfony / routing / Loader / XmlFileLoader.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.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 Symfony\Component\Routing\Loader;
13
14 use Symfony\Component\Routing\RouteCollection;
15 use Symfony\Component\Routing\Route;
16 use Symfony\Component\Config\Resource\FileResource;
17 use Symfony\Component\Config\Loader\FileLoader;
18 use Symfony\Component\Config\Util\XmlUtils;
19
20 /**
21  * XmlFileLoader loads XML routing files.
22  *
23  * @author Fabien Potencier <fabien@symfony.com>
24  * @author Tobias Schultze <http://tobion.de>
25  */
26 class XmlFileLoader extends FileLoader
27 {
28     const NAMESPACE_URI = 'http://symfony.com/schema/routing';
29     const SCHEME_PATH = '/schema/routing/routing-1.0.xsd';
30
31     /**
32      * Loads an XML file.
33      *
34      * @param string      $file An XML file path
35      * @param string|null $type The resource type
36      *
37      * @return RouteCollection A RouteCollection instance
38      *
39      * @throws \InvalidArgumentException When the file cannot be loaded or when the XML cannot be
40      *                                   parsed because it does not validate against the scheme.
41      */
42     public function load($file, $type = null)
43     {
44         $path = $this->locator->locate($file);
45
46         $xml = $this->loadFile($path);
47
48         $collection = new RouteCollection();
49         $collection->addResource(new FileResource($path));
50
51         // process routes and imports
52         foreach ($xml->documentElement->childNodes as $node) {
53             if (!$node instanceof \DOMElement) {
54                 continue;
55             }
56
57             $this->parseNode($collection, $node, $path, $file);
58         }
59
60         return $collection;
61     }
62
63     /**
64      * Parses a node from a loaded XML file.
65      *
66      * @param RouteCollection $collection Collection to associate with the node
67      * @param \DOMElement     $node       Element to parse
68      * @param string          $path       Full path of the XML file being processed
69      * @param string          $file       Loaded file name
70      *
71      * @throws \InvalidArgumentException When the XML is invalid
72      */
73     protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file)
74     {
75         if (self::NAMESPACE_URI !== $node->namespaceURI) {
76             return;
77         }
78
79         switch ($node->localName) {
80             case 'route':
81                 $this->parseRoute($collection, $node, $path);
82                 break;
83             case 'import':
84                 $this->parseImport($collection, $node, $path, $file);
85                 break;
86             default:
87                 throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path));
88         }
89     }
90
91     /**
92      * {@inheritdoc}
93      */
94     public function supports($resource, $type = null)
95     {
96         return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'xml' === $type);
97     }
98
99     /**
100      * Parses a route and adds it to the RouteCollection.
101      *
102      * @param RouteCollection $collection RouteCollection instance
103      * @param \DOMElement     $node       Element to parse that represents a Route
104      * @param string          $path       Full path of the XML file being processed
105      *
106      * @throws \InvalidArgumentException When the XML is invalid
107      */
108     protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
109     {
110         if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('path')) {
111             throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" attribute.', $path));
112         }
113
114         $schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY);
115         $methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY);
116
117         list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path);
118
119         $route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
120         $collection->add($id, $route);
121     }
122
123     /**
124      * Parses an import and adds the routes in the resource to the RouteCollection.
125      *
126      * @param RouteCollection $collection RouteCollection instance
127      * @param \DOMElement     $node       Element to parse that represents a Route
128      * @param string          $path       Full path of the XML file being processed
129      * @param string          $file       Loaded file name
130      *
131      * @throws \InvalidArgumentException When the XML is invalid
132      */
133     protected function parseImport(RouteCollection $collection, \DOMElement $node, $path, $file)
134     {
135         if ('' === $resource = $node->getAttribute('resource')) {
136             throw new \InvalidArgumentException(sprintf('The <import> element in file "%s" must have a "resource" attribute.', $path));
137         }
138
139         $type = $node->getAttribute('type');
140         $prefix = $node->getAttribute('prefix');
141         $host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
142         $schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
143         $methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
144
145         list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path);
146
147         $this->setCurrentDir(dirname($path));
148
149         $subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
150         /* @var $subCollection RouteCollection */
151         $subCollection->addPrefix($prefix);
152         if (null !== $host) {
153             $subCollection->setHost($host);
154         }
155         if (null !== $condition) {
156             $subCollection->setCondition($condition);
157         }
158         if (null !== $schemes) {
159             $subCollection->setSchemes($schemes);
160         }
161         if (null !== $methods) {
162             $subCollection->setMethods($methods);
163         }
164         $subCollection->addDefaults($defaults);
165         $subCollection->addRequirements($requirements);
166         $subCollection->addOptions($options);
167
168         $collection->addCollection($subCollection);
169     }
170
171     /**
172      * Loads an XML file.
173      *
174      * @param string $file An XML file path
175      *
176      * @return \DOMDocument
177      *
178      * @throws \InvalidArgumentException When loading of XML file fails because of syntax errors
179      *                                   or when the XML structure is not as expected by the scheme -
180      *                                   see validate()
181      */
182     protected function loadFile($file)
183     {
184         return XmlUtils::loadFile($file, __DIR__.static::SCHEME_PATH);
185     }
186
187     /**
188      * Parses the config elements (default, requirement, option).
189      *
190      * @param \DOMElement $node Element to parse that contains the configs
191      * @param string      $path Full path of the XML file being processed
192      *
193      * @return array An array with the defaults as first item, requirements as second and options as third
194      *
195      * @throws \InvalidArgumentException When the XML is invalid
196      */
197     private function parseConfigs(\DOMElement $node, $path)
198     {
199         $defaults = array();
200         $requirements = array();
201         $options = array();
202         $condition = null;
203
204         foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
205             if ($node !== $n->parentNode) {
206                 continue;
207             }
208
209             switch ($n->localName) {
210                 case 'default':
211                     if ($this->isElementValueNull($n)) {
212                         $defaults[$n->getAttribute('key')] = null;
213                     } else {
214                         $defaults[$n->getAttribute('key')] = $this->parseDefaultsConfig($n, $path);
215                     }
216
217                     break;
218                 case 'requirement':
219                     $requirements[$n->getAttribute('key')] = trim($n->textContent);
220                     break;
221                 case 'option':
222                     $options[$n->getAttribute('key')] = trim($n->textContent);
223                     break;
224                 case 'condition':
225                     $condition = trim($n->textContent);
226                     break;
227                 default:
228                     throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement", "option" or "condition".', $n->localName, $path));
229             }
230         }
231
232         return array($defaults, $requirements, $options, $condition);
233     }
234
235     /**
236      * Parses the "default" elements.
237      *
238      * @param \DOMElement $element The "default" element to parse
239      * @param string      $path    Full path of the XML file being processed
240      *
241      * @return array|bool|float|int|string|null The parsed value of the "default" element
242      */
243     private function parseDefaultsConfig(\DOMElement $element, $path)
244     {
245         if ($this->isElementValueNull($element)) {
246             return;
247         }
248
249         // Check for existing element nodes in the default element. There can
250         // only be a single element inside a default element. So this element
251         // (if one was found) can safely be returned.
252         foreach ($element->childNodes as $child) {
253             if (!$child instanceof \DOMElement) {
254                 continue;
255             }
256
257             if (self::NAMESPACE_URI !== $child->namespaceURI) {
258                 continue;
259             }
260
261             return $this->parseDefaultNode($child, $path);
262         }
263
264         // If the default element doesn't contain a nested "bool", "int", "float",
265         // "string", "list", or "map" element, the element contents will be treated
266         // as the string value of the associated default option.
267         return trim($element->textContent);
268     }
269
270     /**
271      * Recursively parses the value of a "default" element.
272      *
273      * @param \DOMElement $node The node value
274      * @param string      $path Full path of the XML file being processed
275      *
276      * @return array|bool|float|int|string The parsed value
277      *
278      * @throws \InvalidArgumentException when the XML is invalid
279      */
280     private function parseDefaultNode(\DOMElement $node, $path)
281     {
282         if ($this->isElementValueNull($node)) {
283             return;
284         }
285
286         switch ($node->localName) {
287             case 'bool':
288                 return 'true' === trim($node->nodeValue) || '1' === trim($node->nodeValue);
289             case 'int':
290                 return (int) trim($node->nodeValue);
291             case 'float':
292                 return (float) trim($node->nodeValue);
293             case 'string':
294                 return trim($node->nodeValue);
295             case 'list':
296                 $list = array();
297
298                 foreach ($node->childNodes as $element) {
299                     if (!$element instanceof \DOMElement) {
300                         continue;
301                     }
302
303                     if (self::NAMESPACE_URI !== $element->namespaceURI) {
304                         continue;
305                     }
306
307                     $list[] = $this->parseDefaultNode($element, $path);
308                 }
309
310                 return $list;
311             case 'map':
312                 $map = array();
313
314                 foreach ($node->childNodes as $element) {
315                     if (!$element instanceof \DOMElement) {
316                         continue;
317                     }
318
319                     if (self::NAMESPACE_URI !== $element->namespaceURI) {
320                         continue;
321                     }
322
323                     $map[$element->getAttribute('key')] = $this->parseDefaultNode($element, $path);
324                 }
325
326                 return $map;
327             default:
328                 throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "bool", "int", "float", "string", "list", or "map".', $node->localName, $path));
329         }
330     }
331
332     private function isElementValueNull(\DOMElement $element)
333     {
334         $namespaceUri = 'http://www.w3.org/2001/XMLSchema-instance';
335
336         if (!$element->hasAttributeNS($namespaceUri, 'nil')) {
337             return false;
338         }
339
340         return 'true' === $element->getAttributeNS($namespaceUri, 'nil') || '1' === $element->getAttributeNS($namespaceUri, 'nil');
341     }
342 }