20
20
* XmlFileLoader loads XML routing files.
21
21
*
22
22
* @author Fabien Potencier <[email protected] >
23
+ * @author Tobias Schultze <http://tobion.de>
23
24
*
24
25
* @api
25
26
*/
26
27
class XmlFileLoader extends FileLoader
27
28
{
29
+ const NAMESPACE_URI = 'http://symfony.com/schema/routing ' ;
30
+ const SCHEME_PATH = '/schema/routing/routing-1.0.xsd ' ;
31
+
28
32
/**
29
33
* Loads an XML file.
30
34
*
@@ -33,7 +37,8 @@ class XmlFileLoader extends FileLoader
33
37
*
34
38
* @return RouteCollection A RouteCollection instance
35
39
*
36
- * @throws \InvalidArgumentException When a tag can't be parsed
40
+ * @throws \InvalidArgumentException When the file cannot be loaded or when the XML cannot be
41
+ * parsed because it does not validate against the scheme.
37
42
*
38
43
* @api
39
44
*/
@@ -61,65 +66,28 @@ public function load($file, $type = null)
61
66
/**
62
67
* Parses a node from a loaded XML file.
63
68
*
64
- * @param RouteCollection $collection the collection to associate with the node
65
- * @param \DOMElement $node the node to parse
66
- * @param string $path the path of the XML file being processed
67
- * @param string $file
69
+ * @param RouteCollection $collection Collection to associate with the node
70
+ * @param \DOMElement $node Element to parse
71
+ * @param string $path Full path of the XML file being processed
72
+ * @param string $file Loaded file name
68
73
*
69
- * @throws \InvalidArgumentException When a tag can't be parsed
74
+ * @throws \InvalidArgumentException When the XML is invalid
70
75
*/
71
76
protected function parseNode (RouteCollection $ collection , \DOMElement $ node , $ path , $ file )
72
77
{
73
- switch ($ node ->tagName ) {
78
+ if (self ::NAMESPACE_URI !== $ node ->namespaceURI ) {
79
+ return ;
80
+ }
81
+
82
+ switch ($ node ->localName ) {
74
83
case 'route ' :
75
84
$ this ->parseRoute ($ collection , $ node , $ path );
76
85
break ;
77
86
case 'import ' :
78
- $ resource = $ node ->getAttribute ('resource ' );
79
- $ type = $ node ->getAttribute ('type ' );
80
- $ prefix = $ node ->getAttribute ('prefix ' );
81
- $ hostnamePattern = $ node ->hasAttribute ('hostname-pattern ' ) ? $ node ->getAttribute ('hostname-pattern ' ) : null ;
82
-
83
- $ defaults = array ();
84
- $ requirements = array ();
85
- $ options = array ();
86
-
87
- foreach ($ node ->childNodes as $ n ) {
88
- if (!$ n instanceof \DOMElement) {
89
- continue ;
90
- }
91
-
92
- switch ($ n ->tagName ) {
93
- case 'default ' :
94
- $ defaults [$ n ->getAttribute ('key ' )] = trim ($ n ->nodeValue );
95
- break ;
96
- case 'requirement ' :
97
- $ requirements [$ n ->getAttribute ('key ' )] = trim ($ n ->nodeValue );
98
- break ;
99
- case 'option ' :
100
- $ options [$ n ->getAttribute ('key ' )] = trim ($ n ->nodeValue );
101
- break ;
102
- default :
103
- throw new \InvalidArgumentException (sprintf ('Unable to parse tag "%s" ' , $ n ->tagName ));
104
- }
105
- }
106
-
107
- $ this ->setCurrentDir (dirname ($ path ));
108
-
109
- $ subCollection = $ this ->import ($ resource , ('' !== $ type ? $ type : null ), false , $ file );
110
- /* @var $subCollection RouteCollection */
111
- $ subCollection ->addPrefix ($ prefix );
112
- if (null !== $ hostnamePattern ) {
113
- $ subCollection ->setHostnamePattern ($ hostnamePattern );
114
- }
115
- $ subCollection ->addDefaults ($ defaults );
116
- $ subCollection ->addRequirements ($ requirements );
117
- $ subCollection ->addOptions ($ options );
118
-
119
- $ collection ->addCollection ($ subCollection );
87
+ $ this ->parseImport ($ collection , $ node , $ path , $ file );
120
88
break ;
121
89
default :
122
- throw new \InvalidArgumentException (sprintf ('Unable to parse tag "%s" ' , $ node ->tagName ));
90
+ throw new \InvalidArgumentException (sprintf ('Unknown tag "%s" used in file "%s". Expected "route" or "import". ' , $ node ->localName , $ path ));
123
91
}
124
92
}
125
93
@@ -136,41 +104,59 @@ public function supports($resource, $type = null)
136
104
/**
137
105
* Parses a route and adds it to the RouteCollection.
138
106
*
139
- * @param RouteCollection $collection A RouteCollection instance
140
- * @param \DOMElement $definition Route definition
141
- * @param string $file An XML file path
107
+ * @param RouteCollection $collection RouteCollection instance
108
+ * @param \DOMElement $node Element to parse that represents a Route
109
+ * @param string $path Full path of the XML file being processed
142
110
*
143
- * @throws \InvalidArgumentException When the definition cannot be parsed
111
+ * @throws \InvalidArgumentException When the XML is invalid
144
112
*/
145
- protected function parseRoute (RouteCollection $ collection , \DOMElement $ definition , $ file )
113
+ protected function parseRoute (RouteCollection $ collection , \DOMElement $ node , $ path )
146
114
{
147
- $ defaults = array ();
148
- $ requirements = array ( );
149
- $ options = array ();
115
+ if ( '' === ( $ id = $ node -> getAttribute ( ' id ' )) || ! $ node -> hasAttribute ( ' pattern ' )) {
116
+ throw new \ InvalidArgumentException ( sprintf ( ' The <route> element in file "%s" must have an "id" and a "pattern" attribute. ' , $ path ) );
117
+ }
150
118
151
- foreach ($ definition ->childNodes as $ node ) {
152
- if (!$ node instanceof \DOMElement) {
153
- continue ;
154
- }
119
+ list ($ defaults , $ requirements , $ options ) = $ this ->parseConfigs ($ node , $ path );
155
120
156
- switch ($ node ->tagName ) {
157
- case 'default ' :
158
- $ defaults [$ node ->getAttribute ('key ' )] = trim ((string ) $ node ->nodeValue );
159
- break ;
160
- case 'option ' :
161
- $ options [$ node ->getAttribute ('key ' )] = trim ((string ) $ node ->nodeValue );
162
- break ;
163
- case 'requirement ' :
164
- $ requirements [$ node ->getAttribute ('key ' )] = trim ((string ) $ node ->nodeValue );
165
- break ;
166
- default :
167
- throw new \InvalidArgumentException (sprintf ('Unable to parse tag "%s" ' , $ node ->tagName ));
168
- }
121
+ $ route = new Route ($ node ->getAttribute ('pattern ' ), $ defaults , $ requirements , $ options , $ node ->getAttribute ('hostname-pattern ' ));
122
+ $ collection ->add ($ id , $ route );
123
+ }
124
+
125
+ /**
126
+ * Parses an import and adds the routes in the resource to the RouteCollection.
127
+ *
128
+ * @param RouteCollection $collection RouteCollection instance
129
+ * @param \DOMElement $node Element to parse that represents a Route
130
+ * @param string $path Full path of the XML file being processed
131
+ * @param string $file Loaded file name
132
+ *
133
+ * @throws \InvalidArgumentException When the XML is invalid
134
+ */
135
+ protected function parseImport (RouteCollection $ collection , \DOMElement $ node , $ path , $ file )
136
+ {
137
+ if ('' === $ resource = $ node ->getAttribute ('resource ' )) {
138
+ throw new \InvalidArgumentException (sprintf ('The <import> element in file "%s" must have a "resource" attribute. ' , $ path ));
169
139
}
170
140
171
- $ route = new Route ($ definition ->getAttribute ('pattern ' ), $ defaults , $ requirements , $ options , $ definition ->getAttribute ('hostname-pattern ' ));
141
+ $ type = $ node ->getAttribute ('type ' );
142
+ $ prefix = $ node ->getAttribute ('prefix ' );
143
+ $ hostnamePattern = $ node ->hasAttribute ('hostname-pattern ' ) ? $ node ->getAttribute ('hostname-pattern ' ) : null ;
144
+
145
+ list ($ defaults , $ requirements , $ options ) = $ this ->parseConfigs ($ node , $ path );
172
146
173
- $ collection ->add ($ definition ->getAttribute ('id ' ), $ route );
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 !== $ hostnamePattern ) {
153
+ $ subCollection ->setHostnamePattern ($ hostnamePattern );
154
+ }
155
+ $ subCollection ->addDefaults ($ defaults );
156
+ $ subCollection ->addRequirements ($ requirements );
157
+ $ subCollection ->addOptions ($ options );
158
+
159
+ $ collection ->addCollection ($ subCollection );
174
160
}
175
161
176
162
/**
@@ -180,7 +166,9 @@ protected function parseRoute(RouteCollection $collection, \DOMElement $definiti
180
166
*
181
167
* @return \DOMDocument
182
168
*
183
- * @throws \InvalidArgumentException When loading of XML file returns error
169
+ * @throws \InvalidArgumentException When loading of XML file fails because of syntax errors
170
+ * or when the XML structure is not as expected by the scheme -
171
+ * see validate()
184
172
*/
185
173
protected function loadFile ($ file )
186
174
{
@@ -220,12 +208,10 @@ protected function loadFile($file)
220
208
*/
221
209
protected function validate (\DOMDocument $ dom )
222
210
{
223
- $ location = __DIR__ .'/schema/routing/routing-1.0.xsd ' ;
224
-
225
211
$ current = libxml_use_internal_errors (true );
226
212
libxml_clear_errors ();
227
213
228
- if (!$ dom ->schemaValidate ($ location )) {
214
+ if (!$ dom ->schemaValidate (__DIR__ . static :: SCHEME_PATH )) {
229
215
throw new \InvalidArgumentException (implode ("\n" , $ this ->getXmlErrors ($ current )));
230
216
}
231
217
libxml_use_internal_errors ($ current );
@@ -255,4 +241,39 @@ private function getXmlErrors($internalErrors)
255
241
256
242
return $ errors ;
257
243
}
244
+
245
+ /**
246
+ * Parses the config elements (default, requirement, option).
247
+ *
248
+ * @param \DOMElement $node Element to parse that contains the configs
249
+ * @param string $path Full path of the XML file being processed
250
+ *
251
+ * @return array An array with the defaults as first item, requirements as second and options as third.
252
+ *
253
+ * @throws \InvalidArgumentException When the XML is invalid
254
+ */
255
+ private function parseConfigs (\DOMElement $ node , $ path )
256
+ {
257
+ $ defaults = array ();
258
+ $ requirements = array ();
259
+ $ options = array ();
260
+
261
+ foreach ($ node ->getElementsByTagNameNS (self ::NAMESPACE_URI , '* ' ) as $ n ) {
262
+ switch ($ n ->localName ) {
263
+ case 'default ' :
264
+ $ defaults [$ n ->getAttribute ('key ' )] = trim ($ n ->textContent );
265
+ break ;
266
+ case 'requirement ' :
267
+ $ requirements [$ n ->getAttribute ('key ' )] = trim ($ n ->textContent );
268
+ break ;
269
+ case 'option ' :
270
+ $ options [$ n ->getAttribute ('key ' )] = trim ($ n ->textContent );
271
+ break ;
272
+ default :
273
+ throw new \InvalidArgumentException (sprintf ('Unknown tag "%s" used in file "%s". Expected "default", "requirement" or "option". ' , $ n ->localName , $ path ));
274
+ }
275
+ }
276
+
277
+ return array ($ defaults , $ requirements , $ options );
278
+ }
258
279
}
0 commit comments