1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 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: 22: 23: 24: 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: 33: 34: 35: 36: 37: 38: 39: 40: 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:
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: 65: 66: 67: 68: 69: 70: 71: 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: 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: 101: 102: 103: 104: 105: 106: 107:
108: protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
109: {
110: if ('' === ($id = $node->getAttribute('id')) || (!$node->hasAttribute('pattern') && !$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: if ($node->hasAttribute('pattern')) {
115: if ($node->hasAttribute('path')) {
116: throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" cannot define both a "path" and a "pattern" attribute. Use only "path".', $path));
117: }
118:
119: @trigger_error(sprintf('The "pattern" option in file "%s" is deprecated since version 2.2 and will be removed in 3.0. Use the "path" option in the route definition instead.', $path), E_USER_DEPRECATED);
120:
121: $node->setAttribute('path', $node->getAttribute('pattern'));
122: $node->removeAttribute('pattern');
123: }
124:
125: $schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY);
126: $methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY);
127:
128: list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path);
129:
130: if (isset($requirements['_method'])) {
131: if (0 === count($methods)) {
132: $methods = explode('|', $requirements['_method']);
133: }
134:
135: unset($requirements['_method']);
136: @trigger_error(sprintf('The "_method" requirement of route "%s" in file "%s" is deprecated since version 2.2 and will be removed in 3.0. Use the "methods" attribute instead.', $id, $path), E_USER_DEPRECATED);
137: }
138:
139: if (isset($requirements['_scheme'])) {
140: if (0 === count($schemes)) {
141: $schemes = explode('|', $requirements['_scheme']);
142: }
143:
144: unset($requirements['_scheme']);
145: @trigger_error(sprintf('The "_scheme" requirement of route "%s" in file "%s" is deprecated since version 2.2 and will be removed in 3.0. Use the "schemes" attribute instead.', $id, $path), E_USER_DEPRECATED);
146: }
147:
148: $route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
149: $collection->add($id, $route);
150: }
151:
152: 153: 154: 155: 156: 157: 158: 159: 160: 161:
162: protected function parseImport(RouteCollection $collection, \DOMElement $node, $path, $file)
163: {
164: if ('' === $resource = $node->getAttribute('resource')) {
165: throw new \InvalidArgumentException(sprintf('The <import> element in file "%s" must have a "resource" attribute.', $path));
166: }
167:
168: $type = $node->getAttribute('type');
169: $prefix = $node->getAttribute('prefix');
170: $host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
171: $schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
172: $methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
173:
174: list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path);
175:
176: $this->setCurrentDir(dirname($path));
177:
178: $subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
179:
180: $subCollection->addPrefix($prefix);
181: if (null !== $host) {
182: $subCollection->setHost($host);
183: }
184: if (null !== $condition) {
185: $subCollection->setCondition($condition);
186: }
187: if (null !== $schemes) {
188: $subCollection->setSchemes($schemes);
189: }
190: if (null !== $methods) {
191: $subCollection->setMethods($methods);
192: }
193: $subCollection->addDefaults($defaults);
194: $subCollection->addRequirements($requirements);
195: $subCollection->addOptions($options);
196:
197: $collection->addCollection($subCollection);
198: }
199:
200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210:
211: protected function loadFile($file)
212: {
213: return XmlUtils::loadFile($file, __DIR__.static::SCHEME_PATH);
214: }
215:
216: 217: 218: 219: 220: 221: 222: 223: 224: 225:
226: private function parseConfigs(\DOMElement $node, $path)
227: {
228: $defaults = array();
229: $requirements = array();
230: $options = array();
231: $condition = null;
232:
233: foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
234: switch ($n->localName) {
235: case 'default':
236: if ($this->isElementValueNull($n)) {
237: $defaults[$n->getAttribute('key')] = null;
238: } else {
239: $defaults[$n->getAttribute('key')] = trim($n->textContent);
240: }
241:
242: break;
243: case 'requirement':
244: $requirements[$n->getAttribute('key')] = trim($n->textContent);
245: break;
246: case 'option':
247: $options[$n->getAttribute('key')] = trim($n->textContent);
248: break;
249: case 'condition':
250: $condition = trim($n->textContent);
251: break;
252: default:
253: throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement", "option" or "condition".', $n->localName, $path));
254: }
255: }
256:
257: return array($defaults, $requirements, $options, $condition);
258: }
259:
260: private function isElementValueNull(\DOMElement $element)
261: {
262: $namespaceUri = 'http://www.w3.org/2001/XMLSchema-instance';
263:
264: if (!$element->hasAttributeNS($namespaceUri, 'nil')) {
265: return false;
266: }
267:
268: return 'true' === $element->getAttributeNS($namespaceUri, 'nil') || '1' === $element->getAttributeNS($namespaceUri, 'nil');
269: }
270: }
271: