4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Routing\Loader
;
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
;
21 * XmlFileLoader loads XML routing files.
23 * @author Fabien Potencier <fabien@symfony.com>
24 * @author Tobias Schultze <http://tobion.de>
28 class XmlFileLoader
extends FileLoader
30 const NAMESPACE_URI
= 'http://symfony.com/schema/routing';
31 const SCHEME_PATH
= '/schema/routing/routing-1.0.xsd';
36 * @param string $file An XML file path
37 * @param string|null $type The resource type
39 * @return RouteCollection A RouteCollection instance
41 * @throws \InvalidArgumentException When the file cannot be loaded or when the XML cannot be
42 * parsed because it does not validate against the scheme.
46 public function load($file, $type = null)
48 $path = $this->locator
->locate($file);
50 $xml = $this->loadFile($path);
52 $collection = new RouteCollection();
53 $collection->addResource(new FileResource($path));
55 // process routes and imports
56 foreach ($xml->documentElement
->childNodes
as $node) {
57 if (!$node instanceof \DOMElement
) {
61 $this->parseNode($collection, $node, $path, $file);
68 * Parses a node from a loaded XML file.
70 * @param RouteCollection $collection Collection to associate with the node
71 * @param \DOMElement $node Element to parse
72 * @param string $path Full path of the XML file being processed
73 * @param string $file Loaded file name
75 * @throws \InvalidArgumentException When the XML is invalid
77 protected function parseNode(RouteCollection
$collection, \DOMElement
$node, $path, $file)
79 if (self
::NAMESPACE_URI
!== $node->namespaceURI
) {
83 switch ($node->localName
) {
85 $this->parseRoute($collection, $node, $path);
88 $this->parseImport($collection, $node, $path, $file);
91 throw new \
InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName
, $path));
100 public function supports($resource, $type = null)
102 return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION
) && (!$type || 'xml' === $type);
106 * Parses a route and adds it to the RouteCollection.
108 * @param RouteCollection $collection RouteCollection instance
109 * @param \DOMElement $node Element to parse that represents a Route
110 * @param string $path Full path of the XML file being processed
112 * @throws \InvalidArgumentException When the XML is invalid
114 protected function parseRoute(RouteCollection
$collection, \DOMElement
$node, $path)
116 if ('' === ($id = $node->getAttribute('id')) || (!$node->hasAttribute('pattern') && !$node->hasAttribute('path'))) {
117 throw new \
InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" attribute.', $path));
120 if ($node->hasAttribute('pattern')) {
121 if ($node->hasAttribute('path')) {
122 throw new \
InvalidArgumentException(sprintf('The <route> element in file "%s" cannot define both a "path" and a "pattern" attribute. Use only "path".', $path));
125 $node->setAttribute('path', $node->getAttribute('pattern'));
126 $node->removeAttribute('pattern');
129 $schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY
);
130 $methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY
);
132 list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
134 $route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods);
135 $collection->add($id, $route);
139 * Parses an import and adds the routes in the resource to the RouteCollection.
141 * @param RouteCollection $collection RouteCollection instance
142 * @param \DOMElement $node Element to parse that represents a Route
143 * @param string $path Full path of the XML file being processed
144 * @param string $file Loaded file name
146 * @throws \InvalidArgumentException When the XML is invalid
148 protected function parseImport(RouteCollection
$collection, \DOMElement
$node, $path, $file)
150 if ('' === $resource = $node->getAttribute('resource')) {
151 throw new \
InvalidArgumentException(sprintf('The <import> element in file "%s" must have a "resource" attribute.', $path));
154 $type = $node->getAttribute('type');
155 $prefix = $node->getAttribute('prefix');
156 $host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
157 $schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY
) : null;
158 $methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY
) : null;
160 list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
162 $this->setCurrentDir(dirname($path));
164 $subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
165 /* @var $subCollection RouteCollection */
166 $subCollection->addPrefix($prefix);
167 if (null !== $host) {
168 $subCollection->setHost($host);
170 if (null !== $schemes) {
171 $subCollection->setSchemes($schemes);
173 if (null !== $methods) {
174 $subCollection->setMethods($methods);
176 $subCollection->addDefaults($defaults);
177 $subCollection->addRequirements($requirements);
178 $subCollection->addOptions($options);
180 $collection->addCollection($subCollection);
186 * @param string $file An XML file path
188 * @return \DOMDocument
190 * @throws \InvalidArgumentException When loading of XML file fails because of syntax errors
191 * or when the XML structure is not as expected by the scheme -
194 protected function loadFile($file)
196 return XmlUtils
::loadFile($file, __DIR__
.static::SCHEME_PATH
);
200 * Parses the config elements (default, requirement, option).
202 * @param \DOMElement $node Element to parse that contains the configs
203 * @param string $path Full path of the XML file being processed
205 * @return array An array with the defaults as first item, requirements as second and options as third.
207 * @throws \InvalidArgumentException When the XML is invalid
209 private function parseConfigs(\DOMElement
$node, $path)
212 $requirements = array();
215 foreach ($node->getElementsByTagNameNS(self
::NAMESPACE_URI
, '*') as $n) {
216 switch ($n->localName
) {
218 if ($n->hasAttribute('xsi:nil') && 'true' == $n->getAttribute('xsi:nil')) {
219 $defaults[$n->getAttribute('key')] = null;
221 $defaults[$n->getAttribute('key')] = trim($n->textContent
);
226 $requirements[$n->getAttribute('key')] = trim($n->textContent
);
229 $options[$n->getAttribute('key')] = trim($n->textContent
);
232 throw new \
InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement" or "option".', $n->localName
, $path));
236 return array($defaults, $requirements, $options);