This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Agavi\Config; |
||
3 | |||
4 | // +---------------------------------------------------------------------------+ |
||
5 | // | This file is part of the Agavi package. | |
||
6 | // | Copyright (c) 2005-2011 the Agavi Project. | |
||
7 | // | | |
||
8 | // | For the full copyright and license information, please view the LICENSE | |
||
9 | // | file that was distributed with this source code. You can also view the | |
||
10 | // | LICENSE file online at http://www.agavi.org/LICENSE.txt | |
||
11 | // | vi: set noexpandtab: | |
||
12 | // | Local Variables: | |
||
13 | // | indent-tabs-mode: t | |
||
14 | // | End: | |
||
15 | // +---------------------------------------------------------------------------+ |
||
16 | |||
17 | |||
18 | use Agavi\Util\SchematronProcessor; |
||
19 | use Agavi\Util\Toolkit; |
||
20 | use Agavi\Exception\ParseException; |
||
21 | use Agavi\Exception\UnreadableException; |
||
22 | use Agavi\Config\Util\Dom\XmlConfigDomDocument; |
||
23 | use Agavi\Config\Util\Dom\XmlConfigDomElement; |
||
24 | use Agavi\Util\XsltProcessor; |
||
25 | |||
26 | /** |
||
27 | * XmlConfigParser handles both Agavi and foreign XML configuration files, |
||
28 | * deals with XIncludes, XSL transformations and validation as well as filtering |
||
29 | * and ordering of configuration blocks and parent file resolution and parsing. |
||
30 | * |
||
31 | * @package agavi |
||
32 | * @subpackage config |
||
33 | * |
||
34 | * @author David Zülke <[email protected]> |
||
35 | * @author Noah Fontes <[email protected]> |
||
36 | * @copyright Authors |
||
37 | * @copyright The Agavi Project |
||
38 | * |
||
39 | * @since 0.11.0 |
||
40 | * |
||
41 | * @version $Id$ |
||
42 | */ |
||
43 | |||
44 | class XmlConfigParser |
||
45 | { |
||
46 | const NAMESPACE_AGAVI_ENVELOPE_0_11 = 'http://agavi.org/agavi/1.0/config'; |
||
47 | |||
48 | const NAMESPACE_AGAVI_ENVELOPE_1_0 = 'http://agavi.org/agavi/config/global/envelope/1.0'; |
||
49 | |||
50 | const NAMESPACE_AGAVI_ENVELOPE_1_1 = 'http://agavi.org/agavi/config/global/envelope/1.1'; |
||
51 | |||
52 | const NAMESPACE_AGAVI_ENVELOPE_LATEST = self::NAMESPACE_AGAVI_ENVELOPE_1_1; |
||
53 | |||
54 | const NAMESPACE_AGAVI_ANNOTATIONS_1_0 = 'http://agavi.org/agavi/config/global/annotations/1.0'; |
||
55 | |||
56 | const NAMESPACE_AGAVI_ANNOTATIONS_LATEST = self::NAMESPACE_AGAVI_ANNOTATIONS_1_0; |
||
57 | |||
58 | const VALIDATION_TYPE_XMLSCHEMA = 'xml_schema'; |
||
59 | |||
60 | const VALIDATION_TYPE_RELAXNG = 'relax_ng'; |
||
61 | |||
62 | const VALIDATION_TYPE_SCHEMATRON = 'schematron'; |
||
63 | |||
64 | const NAMESPACE_SCHEMATRON_ISO = 'http://purl.oclc.org/dsdl/schematron'; |
||
65 | |||
66 | const NAMESPACE_SVRL_ISO = 'http://purl.oclc.org/dsdl/svrl'; |
||
67 | |||
68 | const NAMESPACE_XML_1998 = 'http://www.w3.org/XML/1998/namespace'; |
||
69 | |||
70 | const NAMESPACE_XMLNS_2000 = 'http://www.w3.org/2000/xmlns/'; |
||
71 | |||
72 | const NAMESPACE_XSL_1999 = 'http://www.w3.org/1999/XSL/Transform'; |
||
73 | |||
74 | const NAMESPACE_XINCLUDE_2001 = 'http://www.w3.org/2001/XInclude'; |
||
75 | |||
76 | const STAGE_SINGLE = 'single'; |
||
77 | |||
78 | const STAGE_COMPILATION = 'compilation'; |
||
79 | |||
80 | const STEP_TRANSFORMATIONS_BEFORE = 'transformations_before'; |
||
81 | |||
82 | const STEP_TRANSFORMATIONS_AFTER = 'transformations_after'; |
||
83 | |||
84 | /** |
||
85 | * @var array A list of XML namespaces for Agavi configuration files as |
||
86 | * keys and their associated XPath namespace prefix (value). |
||
87 | */ |
||
88 | public static $agaviEnvelopeNamespaces = array( |
||
89 | self::NAMESPACE_AGAVI_ENVELOPE_0_11 => 'agavi_envelope_0_11', |
||
90 | self::NAMESPACE_AGAVI_ENVELOPE_1_0 => 'agavi_envelope_1_0', |
||
91 | self::NAMESPACE_AGAVI_ENVELOPE_1_1 => 'agavi_envelope_1_1', |
||
92 | ); |
||
93 | |||
94 | /** |
||
95 | * @var array A list of all XML namespaces that are used internally by |
||
96 | * the configuration parser. |
||
97 | */ |
||
98 | public static $agaviNamespaces = array( |
||
99 | self::NAMESPACE_AGAVI_ENVELOPE_0_11 => 'agavi_envelope_0_11', |
||
100 | self::NAMESPACE_AGAVI_ENVELOPE_1_0 => 'agavi_envelope_1_0', |
||
101 | self::NAMESPACE_AGAVI_ENVELOPE_1_1 => 'agavi_envelope_1_1', |
||
102 | self::NAMESPACE_AGAVI_ANNOTATIONS_1_0 => 'agavi_annotations_1_0', |
||
103 | ); |
||
104 | |||
105 | /** |
||
106 | * @var string Path to the config file we're parsing in this instance. |
||
107 | */ |
||
108 | protected $path = ''; |
||
109 | |||
110 | /** |
||
111 | * @var string The name of the current environment. |
||
112 | */ |
||
113 | protected $environment = ''; |
||
114 | |||
115 | /** |
||
116 | * @var string The name of the current context. |
||
117 | */ |
||
118 | protected $context = null; |
||
119 | |||
120 | /** |
||
121 | * @var \DOMDocument|XmlConfigDomDocument The document we're parsing here. |
||
122 | */ |
||
123 | protected $doc = null; |
||
124 | |||
125 | /** |
||
126 | * Test if the given document looks like an Agavi config file. |
||
127 | * |
||
128 | * @param \DOMDocument $doc The document to test. |
||
129 | * |
||
130 | * @return bool True, if it is an Agavi config document, false otherwise. |
||
131 | * |
||
132 | * @author David Zülke <[email protected]> |
||
133 | * @since 1.0.0 |
||
134 | */ |
||
135 | public static function isAgaviConfigurationDocument(\DOMDocument $doc) |
||
136 | { |
||
137 | return $doc->documentElement && $doc->documentElement->localName == 'configurations' && self::isAgaviEnvelopeNamespace($doc->documentElement->namespaceURI); |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Check if the given namespace URI is a valid Agavi envelope namespace. |
||
142 | * |
||
143 | * @param string $namespaceUri The namespace URI. |
||
144 | * |
||
145 | * @return bool True, if the given URI is a valid namespace URI, or false. |
||
146 | * |
||
147 | * @author David Zülke <[email protected]> |
||
148 | * @since 1.0.0 |
||
149 | */ |
||
150 | public static function isAgaviEnvelopeNamespace($namespaceUri) |
||
151 | { |
||
152 | return isset(self::$agaviEnvelopeNamespaces[$namespaceUri]); |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * Check if a given namespace URI is a valid Agavi namespace. |
||
157 | * |
||
158 | * @param string $namespaceUri The namespace URI. |
||
159 | * |
||
160 | * @return bool True if the given URI is a valid namespace URI, |
||
161 | * false otherwise. |
||
162 | * |
||
163 | * @author Noah Fontes <[email protected]> |
||
164 | * @since 1.0.0 |
||
165 | */ |
||
166 | public static function isAgaviNamespace($namespaceUri) |
||
167 | { |
||
168 | return isset(self::$agaviNamespaces[$namespaceUri]); |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Retrieves an XPath namespace prefix based on a given namespace URI. |
||
173 | * |
||
174 | * @param string $namespaceUri The namespace URI. |
||
175 | * |
||
176 | * @return string The prefix for the namespace URI, or null if none |
||
177 | * exists. |
||
178 | * |
||
179 | * @author Noah Fontes <[email protected]> |
||
180 | * @since 1.0.0 |
||
181 | */ |
||
182 | public static function getAgaviNamespacePrefix($namespaceUri) |
||
183 | { |
||
184 | if (self::isAgaviNamespace($namespaceUri)) { |
||
185 | return self::$agaviNamespaces[$namespaceUri]; |
||
186 | } |
||
187 | return null; |
||
188 | } |
||
189 | |||
190 | /** |
||
191 | * Register Agavi namespace prefixes in a given document. |
||
192 | * |
||
193 | * @param XmlConfigDomDocument $document The document. |
||
194 | * |
||
195 | * @author Noah Fontes <[email protected]> |
||
196 | * @since 1.0.0 |
||
197 | */ |
||
198 | public static function registerAgaviNamespaces(XmlConfigDomDocument $document) |
||
199 | { |
||
200 | $xpath = $document->getXpath(); |
||
201 | |||
202 | foreach (self::$agaviNamespaces as $namespaceUri => $prefix) { |
||
203 | $xpath->registerNamespace($prefix, $namespaceUri); |
||
204 | } |
||
205 | |||
206 | /* Register the latest namespaces. */ |
||
207 | $xpath->registerNamespace('agavi_envelope_latest', self::NAMESPACE_AGAVI_ENVELOPE_LATEST); |
||
208 | $xpath->registerNamespace('agavi_annotations_latest', self::NAMESPACE_AGAVI_ANNOTATIONS_LATEST); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @param string $path An absolute filesystem path to a configuration file. |
||
213 | * @param string $environment The environment name. |
||
214 | * @param string $context The optional context name. |
||
215 | * @param array $transformationInfo An associative array of transformation information. |
||
216 | * @param array $validationInfo An associative array of validation information. |
||
217 | * |
||
218 | * @return XmlConfigDomDocument A properly merged DOMDocument. |
||
219 | * |
||
220 | * @author David Zülke <[email protected]> |
||
221 | * @author Dominik del Bondio <[email protected]> |
||
222 | * @author Noah Fontes <[email protected]> |
||
223 | * @since 0.11.0 |
||
224 | */ |
||
225 | public static function run($path, $environment, $context = null, array $transformationInfo = array(), array $validationInfo = array()) |
||
226 | { |
||
227 | $isAgaviConfigFormat = true; |
||
228 | // build an array of documents (this one, and the parents) |
||
229 | $docs = array(); |
||
230 | $previousPaths = array(); |
||
231 | $nextPath = $path; |
||
232 | while ($nextPath !== null) { |
||
233 | // run the single stage parser |
||
234 | $parser = new XmlConfigParser($nextPath, $environment, $context); |
||
235 | $doc = $parser->execute($transformationInfo[self::STAGE_SINGLE], $validationInfo[self::STAGE_SINGLE]); |
||
236 | |||
237 | // put the new document in the list |
||
238 | $docs[] = $doc; |
||
239 | |||
240 | // make sure it (still) is a <configurations> file with the proper Agavi namespace |
||
241 | if ($isAgaviConfigFormat) { |
||
242 | $isAgaviConfigFormat = self::isAgaviConfigurationDocument($doc); |
||
243 | } |
||
244 | |||
245 | // is it an Agavi <configurations> element? does it have a parent attribute? yes? good. parse that next |
||
246 | // TODO: support future namespaces |
||
247 | if ($isAgaviConfigFormat && $doc->documentElement->hasAttribute('parent')) { |
||
248 | $theNextPath = Toolkit::literalize($doc->documentElement->getAttribute('parent')); |
||
249 | |||
250 | // no infinite loop plz, kthx |
||
251 | if ($nextPath === $theNextPath) { |
||
252 | throw new ParseException(sprintf("Agavi detected an infinite loop while processing parent configuration files of \n%s\n\nFile\n%s\nincludes itself as a parent.", $path, $theNextPath)); |
||
253 | } elseif (isset($previousPaths[$theNextPath])) { |
||
254 | throw new ParseException(sprintf("Agavi detected an infinite loop while processing parent configuration files of \n%s\n\nFile\n%s\nhas previously been included by\n%s", $path, $theNextPath, $previousPaths[$theNextPath])); |
||
255 | } else { |
||
256 | $previousPaths[$theNextPath] = $nextPath; |
||
257 | $nextPath = $theNextPath; |
||
258 | } |
||
259 | } else { |
||
260 | $nextPath = null; |
||
261 | } |
||
262 | } |
||
263 | |||
264 | // TODO: use our own classes here that extend DOM* |
||
265 | $retval = new XmlConfigDomDocument(); |
||
266 | foreach (self::$agaviEnvelopeNamespaces as $envelopeNamespaceUri => $envelopeNamespacePrefix) { |
||
267 | $retval->getXpath()->registerNamespace($envelopeNamespacePrefix, $envelopeNamespaceUri); |
||
268 | } |
||
269 | |||
270 | if ($isAgaviConfigFormat) { |
||
271 | // if it is an Agavi config, we'll create a new document with all files' <configuration> blocks inside |
||
272 | $retval->appendChild(new XmlConfigDomElement('configurations', null, self::NAMESPACE_AGAVI_ENVELOPE_LATEST)); |
||
273 | |||
274 | // reverse the array - we want the parents first! |
||
275 | $docs = array_reverse($docs); |
||
276 | |||
277 | $configurationElements = array(); |
||
278 | |||
279 | // TODO: I bet this leaks memory due to the nodes being taken out of the docs. beware circular refs! |
||
280 | /** @var XmlConfigDomDocument $doc */ |
||
281 | foreach ($docs as $doc) { |
||
282 | // iterate over all nodes (attributes, <sandbox>, <configuration> etc) inside the document element and append them to the <configurations> element in our final document |
||
283 | foreach ($doc->documentElement->childNodes as $node) { |
||
284 | if ($node->nodeType == XML_ELEMENT_NODE && $node->localName == 'configuration' && self::isAgaviEnvelopeNamespace($node->namespaceURI)) { |
||
285 | // it's a <configuration> element - put that on a stack for processing |
||
286 | $configurationElements[] = $node; |
||
287 | } else { |
||
288 | // import the node, recursively, and store the imported node |
||
289 | $importedNode = $retval->importNode($node, true); |
||
290 | // now append it to the <configurations> element |
||
291 | $retval->documentElement->appendChild($importedNode); |
||
292 | } |
||
293 | } |
||
294 | // if it's a <configurations> element, then we need to copy the attributes from there |
||
295 | if ($doc->isAgaviConfiguration()) { |
||
296 | $namespaces = $doc->getXPath()->query('namespace::*'); |
||
297 | foreach ($namespaces as $namespace) { |
||
298 | if ($namespace->localName !== 'xml' && $namespace->localName != 'xmlns') { |
||
299 | $retval->documentElement->setAttributeNS(self::NAMESPACE_XMLNS_2000, 'xmlns:' . $namespace->localName, $namespace->namespaceURI); |
||
300 | } |
||
301 | } |
||
302 | foreach ($doc->documentElement->attributes as $attribute) { |
||
303 | // but not the "parent" attributes... |
||
304 | if ($attribute->namespaceURI === null && $attribute->localName === 'parent') { |
||
305 | continue; |
||
306 | } |
||
307 | $importedAttribute = $retval->importNode($attribute, true); |
||
308 | $retval->documentElement->setAttributeNode($importedAttribute); |
||
309 | } |
||
310 | } |
||
311 | } |
||
312 | |||
313 | // generic <configuration> first, then those with an environment attribute, then those with context, then those with both |
||
314 | $configurationOrder = array( |
||
315 | 'count(self::node()[@agavi_annotations_latest:matched and not(@environment) and not(@context)])', |
||
316 | 'count(self::node()[@agavi_annotations_latest:matched and @environment and not(@context)])', |
||
317 | 'count(self::node()[@agavi_annotations_latest:matched and not(@environment) and @context])', |
||
318 | 'count(self::node()[@agavi_annotations_latest:matched and @environment and @context])', |
||
319 | ); |
||
320 | |||
321 | // now we sort the nodes according to the rules |
||
322 | foreach ($configurationOrder as $xpath) { |
||
323 | // append all matching nodes from the order array... |
||
324 | foreach ($configurationElements as &$element) { |
||
325 | // ... if the xpath matches, that is! |
||
326 | if ($element->ownerDocument->getXpath()->evaluate($xpath, $element)) { |
||
327 | // it did, so import the node and append it to the result doc |
||
328 | $importedNode = $retval->importNode($element, true); |
||
329 | $retval->documentElement->appendChild($importedNode); |
||
330 | } |
||
331 | } |
||
332 | } |
||
333 | |||
334 | // run the compilation stage parser |
||
335 | $retval = self::executeCompilation($retval, $environment, $context, $transformationInfo[self::STAGE_COMPILATION], $validationInfo[self::STAGE_COMPILATION]); |
||
336 | } else { |
||
337 | // it's not an agavi config file. just pass it through then |
||
338 | $retval->appendChild($retval->importNode($doc->documentElement, true)); |
||
0 ignored issues
–
show
|
|||
339 | } |
||
340 | |||
341 | // cleanup attempt |
||
342 | unset($docs); |
||
343 | |||
344 | // set the pseudo-document URI |
||
345 | $retval->documentURI = $path; |
||
346 | |||
347 | return $retval; |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Builds a proper regular expression from the input pattern to test against |
||
352 | * the given subject. This is for "environment" and "context" attributes of |
||
353 | * configuration blocks in the files. |
||
354 | * |
||
355 | * @param string $pattern A regular expression chunk without delimiters/anchors. |
||
356 | * @param string $subject The subject to test against |
||
357 | * |
||
358 | * @return bool Whether or not the subject matched the pattern. |
||
359 | * |
||
360 | * @author David Zülke <[email protected]> |
||
361 | * @since 1.0.0 |
||
362 | */ |
||
363 | public static function testPattern($pattern, $subject) |
||
364 | { |
||
365 | // four backslashes mean one literal backslash |
||
366 | $pattern = preg_replace('/\\\\+#/', '\\#', $pattern); |
||
367 | return (preg_match('#^(' . implode('|', array_map('trim', explode(' ', $pattern))) . ')$#', $subject) > 0); |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * The constructor. |
||
372 | * Will make a DOMDocument instance using the given path. |
||
373 | * |
||
374 | * @param string $path The path to the configuration file. |
||
375 | * @param string $environment The optional name of the current environment. |
||
376 | * @param string $context The optional name of the current context. |
||
377 | * |
||
378 | * @author David Zülke <[email protected]> |
||
379 | * @author Noah Fontes <[email protected]> |
||
380 | * @since 1.0.0 |
||
381 | */ |
||
382 | public function __construct($path, $environment = null, $context = null) |
||
383 | { |
||
384 | // store environment... |
||
385 | if ($environment === null) { |
||
386 | $environment = Config::get('core.environment'); |
||
387 | } |
||
388 | $this->environment = $environment; |
||
389 | // ... and context names |
||
390 | $this->context = $context; |
||
391 | |||
392 | View Code Duplication | if (!is_readable($path)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
393 | $error = 'Configuration file "' . $path . '" does not exist or is unreadable'; |
||
394 | throw new UnreadableException($error); |
||
395 | } |
||
396 | |||
397 | // store path to the config file |
||
398 | $this->path = $path; |
||
399 | |||
400 | // AgaviXmlConfigDomDocument has convenience methods! |
||
401 | try { |
||
402 | $this->doc = new XmlConfigDomDocument(); |
||
403 | $this->doc->substituteEntities = true; |
||
404 | $this->doc->load($path); |
||
405 | } catch (\DOMException $dome) { |
||
406 | throw new ParseException(sprintf('Configuration file "%s" could not be parsed: %s', $path, $dome->getMessage()), 0, $dome); |
||
407 | } |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * Destructor to do the cleaning up. |
||
412 | * |
||
413 | * @author David Zülke <[email protected]> |
||
414 | * @since 1.0.0 |
||
415 | */ |
||
416 | public function __destruct() |
||
417 | { |
||
418 | unset($this->doc); |
||
419 | } |
||
420 | |||
421 | /** |
||
422 | * @param array $transformationInfo An array of XSL paths for transformation. |
||
423 | * @param array $validationInfo An associative array of validation information. |
||
424 | * |
||
425 | * @return \DOMDocument Our DOMDocument. |
||
426 | * |
||
427 | * @author David Zülke <[email protected]> |
||
428 | * @author Dominik del Bondio <[email protected]> |
||
429 | * @author Noah Fontes <[email protected]> |
||
430 | * @since 0.11.0 |
||
431 | */ |
||
432 | public function execute(array $transformationInfo = array(), array $validationInfo = array()) |
||
433 | { |
||
434 | // resolve xincludes |
||
435 | self::xinclude($this->doc); |
||
0 ignored issues
–
show
It seems like
$this->doc can also be of type object<DOMDocument> ; however, Agavi\Config\XmlConfigParser::xinclude() does only seem to accept object<Agavi\Config\Util...m\XmlConfigDomDocument> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
436 | |||
437 | // validate XMLSchema-instance declarations |
||
438 | self::validateXsi($this->doc); |
||
0 ignored issues
–
show
It seems like
$this->doc can also be of type object<DOMDocument> ; however, Agavi\Config\XmlConfigParser::validateXsi() does only seem to accept object<Agavi\Config\Util...m\XmlConfigDomDocument> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
439 | |||
440 | // validate pre-transformation |
||
441 | self::validate($this->doc, $this->environment, $this->context, $validationInfo[XmlConfigParser::STEP_TRANSFORMATIONS_BEFORE]); |
||
0 ignored issues
–
show
It seems like
$this->doc can also be of type object<DOMDocument> ; however, Agavi\Config\XmlConfigParser::validate() does only seem to accept object<Agavi\Config\Util...m\XmlConfigDomDocument> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
442 | |||
443 | // mark document for merging |
||
444 | self::match($this->doc, $this->environment, $this->context); |
||
0 ignored issues
–
show
It seems like
$this->doc can also be of type object<DOMDocument> ; however, Agavi\Config\XmlConfigParser::match() does only seem to accept object<Agavi\Config\Util...m\XmlConfigDomDocument> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
445 | |||
446 | if (!Config::get('core.skip_config_transformations', false)) { |
||
0 ignored issues
–
show
false is of type boolean , but the function expects a string|null .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
447 | // run inline transformations |
||
448 | $this->doc = self::transformProcessingInstructions($this->doc, $this->environment, $this->context); |
||
0 ignored issues
–
show
It seems like
$this->doc can also be of type object<DOMDocument> ; however, Agavi\Config\XmlConfigPa...rocessingInstructions() does only seem to accept object<Agavi\Config\Util...m\XmlConfigDomDocument> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
449 | |||
450 | // perform XSL transformations |
||
451 | $this->doc = self::transform($this->doc, $this->environment, $this->context, $transformationInfo); |
||
452 | |||
453 | // resolve xincludes again, since transformations may have introduced some |
||
454 | self::xinclude($this->doc); |
||
455 | } |
||
456 | |||
457 | // validate post-transformation |
||
458 | self::validate($this->doc, $this->environment, $this->context, $validationInfo[XmlConfigParser::STEP_TRANSFORMATIONS_AFTER]); |
||
0 ignored issues
–
show
It seems like
$this->doc can also be of type object<DOMDocument> ; however, Agavi\Config\XmlConfigParser::validate() does only seem to accept object<Agavi\Config\Util...m\XmlConfigDomDocument> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
459 | |||
460 | // clean up the document |
||
461 | self::cleanup($this->doc); |
||
0 ignored issues
–
show
It seems like
$this->doc can also be of type object<DOMDocument> ; however, Agavi\Config\XmlConfigParser::cleanup() does only seem to accept object<Agavi\Config\Util...m\XmlConfigDomDocument> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
462 | |||
463 | return $this->doc; |
||
464 | } |
||
465 | |||
466 | /** |
||
467 | * Executes the parser for a compilation document. |
||
468 | * |
||
469 | * @param XmlConfigDomDocument $document The document to act upon. |
||
470 | * @param string $environment The environment name. |
||
471 | * @param string $context The context name. |
||
472 | * @param array $transformationInfo An array of XSL paths for transformation. |
||
473 | * @param array $validationInfo An associative array of validation information. |
||
474 | * |
||
475 | * @author Noah Fontes <[email protected]> |
||
476 | * @since 1.0.0 |
||
477 | */ |
||
478 | public static function executeCompilation(XmlConfigDomDocument $document, $environment, $context, array $transformationInfo = array(), array $validationInfo = array()) |
||
479 | { |
||
480 | // resolve xincludes |
||
481 | self::xinclude($document); |
||
482 | |||
483 | // validate pre-transformation |
||
484 | self::validate($document, $environment, $context, $validationInfo[XmlConfigParser::STEP_TRANSFORMATIONS_BEFORE]); |
||
485 | |||
486 | if (!Config::get('core.skip_config_transformations', false)) { |
||
0 ignored issues
–
show
false is of type boolean , but the function expects a string|null .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
487 | // perform XSL transformations |
||
488 | $document = self::transform($document, $environment, $context, $transformationInfo); |
||
489 | |||
490 | // resolve xincludes again, since transformations may have introduced some |
||
491 | self::xinclude($document); |
||
492 | } |
||
493 | |||
494 | // validate post-transformation |
||
495 | self::validate($document, $environment, $context, $validationInfo[XmlConfigParser::STEP_TRANSFORMATIONS_AFTER]); |
||
496 | |||
497 | return $document; |
||
498 | } |
||
499 | |||
500 | /** |
||
501 | * Resolve xinclude directives on a given document. |
||
502 | * |
||
503 | * @param XmlConfigDomDocument $document The document to act upon. |
||
504 | * |
||
505 | * @author David Zülke <[email protected]> |
||
506 | * @author Noah Fontes <[email protected]> |
||
507 | * @since 1.0.0 |
||
508 | */ |
||
509 | public static function xinclude(XmlConfigDomDocument $document) |
||
510 | { |
||
511 | // expand directives, resolve globs and encode paths in XInclude href attributes |
||
512 | $elements = $document->getElementsByTagNameNS(self::NAMESPACE_XINCLUDE_2001, 'include'); |
||
513 | $length = $elements->length; |
||
514 | // we can't foreach() over the DOMNodeList as we're modifying it further below |
||
515 | // see http://php.net/manual/en/class.domnodelist.php#83178 |
||
516 | for ($i = 0; $i < $length; $i++) { |
||
517 | $element = $elements->item($i); |
||
518 | if ($element->hasAttribute('href')) { |
||
0 ignored issues
–
show
|
|||
519 | $attribute = $element->getAttributeNode('href'); |
||
520 | $parts = explode('#', $attribute->nodeValue, 2); |
||
521 | $parts[0] = str_replace('\\', '/', Toolkit::expandDirectives($parts[0])); |
||
522 | $attribute->nodeValue = rawurlencode($parts[0]) . (isset($parts[1]) ? '#' . $parts[1] : ''); |
||
523 | if (strpos($parts[0], '*') !== false || strpos($parts[0], '{') !== false) { |
||
524 | $glob = glob($parts[0], GLOB_BRACE); |
||
525 | if ($glob) { |
||
0 ignored issues
–
show
The expression
$glob of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
526 | $glob = array_unique($glob); // it could be that someone used /path/to/{Foo,*}/burp.xml so Foo would come before all others, that's why we need to remove duplicates as the * would match Foo again |
||
527 | foreach ($glob as $path) { |
||
528 | $new = $element->cloneNode(true); |
||
529 | $new->setAttribute('href', rawurlencode($path) . (isset($parts[1]) ? '#' . $parts[1] : '')); |
||
530 | $element->parentNode->insertBefore($new, $element); |
||
531 | ++$i; |
||
532 | } |
||
533 | $element->parentNode->removeChild($element); |
||
534 | } |
||
535 | } |
||
536 | } |
||
537 | } |
||
538 | |||
539 | // perform xincludes |
||
540 | try { |
||
541 | $document->xinclude(); |
||
542 | } catch (\DOMException $dome) { |
||
543 | throw new ParseException(sprintf('Configuration file "%s" could not be parsed: %s', $document->documentURI, $dome->getMessage()), 0, $dome); |
||
544 | } |
||
545 | |||
546 | // remove all xml:base attributes inserted by XIncludes |
||
547 | $nodes = $document->getXpath()->query('//@xml:base', $document); |
||
548 | foreach ($nodes as $node) { |
||
549 | $node->ownerElement->removeAttributeNode($node); |
||
550 | } |
||
551 | } |
||
552 | |||
553 | /** |
||
554 | * Annotate the document with matched attributes against each configuration |
||
555 | * element that matches the given context and environment. |
||
556 | * |
||
557 | * @param XmlConfigDomDocument $document The document to act upon. |
||
558 | * @param string $environment The environment name. |
||
559 | * @param string $context The context name. |
||
560 | * |
||
561 | * @author David Zülke <[email protected]> |
||
562 | * @author Noah Fontes <[email protected]> |
||
563 | * @since 1.0.0 |
||
564 | */ |
||
565 | public static function match(XmlConfigDomDocument $document, $environment, $context) |
||
566 | { |
||
567 | if ($document->isAgaviConfiguration()) { |
||
568 | // it's an agavi config, so we need to set "matched" flags on all <configuration> elements where "context" and "environment" attributes match the values below |
||
569 | $testAttributes = array( |
||
570 | 'context' => $context, |
||
571 | 'environment' => $environment, |
||
572 | ); |
||
573 | |||
574 | foreach ($document->getConfigurationElements() as $configuration) { |
||
575 | // assume that the element counts as matched, in case it doesn't have "context" or "environment" attributes |
||
576 | $matched = true; |
||
577 | foreach ($testAttributes as $attributeName => $attributeValue) { |
||
578 | if ($configuration->hasAttribute($attributeName)) { |
||
579 | $matched = $matched && self::testPattern($configuration->getAttribute($attributeName), $attributeValue); |
||
580 | } |
||
581 | } |
||
582 | if ($matched) { |
||
583 | // if all was fine, we set the attribute. the element will then be kept in the merged result doc later |
||
584 | $configuration->setAttributeNS(self::NAMESPACE_AGAVI_ANNOTATIONS_LATEST, 'agavi_annotations_latest:matched', 'true'); |
||
585 | } |
||
586 | } |
||
587 | } |
||
588 | } |
||
589 | |||
590 | /** |
||
591 | * Transform the document using info from embedded processing instructions |
||
592 | * and given stylesheets. |
||
593 | * |
||
594 | * @param XmlConfigDomDocument $document The document to act upon. |
||
595 | * @param string $environment The environment name. |
||
596 | * @param string $context The context name. |
||
597 | * @param array $transformationInfo An array of transformation information. |
||
598 | * @param array $transformations An array of XSL stylesheets in DOMDocument instances. |
||
599 | * |
||
600 | * @return XmlConfigDomDocument The transformed document. |
||
601 | * |
||
602 | * @author David Zülke <[email protected]> |
||
603 | * @author Noah Fontes <[email protected]> |
||
604 | * @since 0.11.0 |
||
605 | */ |
||
606 | public static function transform(XmlConfigDomDocument $document, $environment, $context, array $transformationInfo = array(), $transformations = array()) |
||
607 | { |
||
608 | // loop over all the paths we found and load the files |
||
609 | foreach ($transformationInfo as $href) { |
||
610 | try { |
||
611 | $xsl = new XmlConfigDomDocument(); |
||
612 | $xsl->load($href); |
||
613 | } catch (\DOMException $dome) { |
||
614 | throw new ParseException(sprintf('Configuration file "%s" could not be parsed: Could not load XSL stylesheet "%s": %s', $document->documentURI, $href, $dome->getMessage()), 0, $dome); |
||
615 | } |
||
616 | |||
617 | // add them to the list of transformations to be done |
||
618 | $transformations[] = $xsl; |
||
619 | } |
||
620 | |||
621 | // now let's perform the transformations |
||
622 | foreach ($transformations as $xsl) { |
||
623 | // load the stylesheet document into an XSLTProcessor instance |
||
624 | try { |
||
625 | $proc = new XsltProcessor(); |
||
626 | $proc->registerPHPFunctions(); |
||
627 | $proc->importStylesheet($xsl); |
||
628 | } catch (\Exception $e) { |
||
629 | throw new ParseException(sprintf('Configuration file "%s" could not be parsed: Could not import XSL stylesheet "%s": %s', $document->documentURI, $xsl->documentURI, $e->getMessage()), 0, $e); |
||
630 | } |
||
631 | |||
632 | // set some info (config file path, context name, environment name) as params |
||
633 | // first arg is the namespace URI, which PHP doesn't support. awesome. see http://bugs.php.net/bug.php?id=30622 for the sad details |
||
634 | // we could use "agavi:context" etc, that does work even without such a prefix being declared in the stylesheet, but that would be completely non-XML-ish, confusing, and against the spec. so we use dots instead. |
||
635 | // the string casts are required for hhvm ($context could be null for example and hhvm bails out on that) |
||
636 | $proc->setParameter('', array( |
||
637 | 'agavi.config_path' => (string)$document->documentURI, |
||
638 | 'agavi.environment' => (string)$environment, |
||
639 | 'agavi.context' => (string)$context, |
||
640 | )); |
||
641 | |||
642 | try { |
||
643 | // transform the doc |
||
644 | $newdoc = $proc->transformToDoc($document); |
||
645 | } catch (\Exception $e) { |
||
646 | throw new ParseException(sprintf('Configuration file "%s" could not be parsed: Could not transform the document using the XSL stylesheet "%s": %s', $document->documentURI, $xsl->documentURI, $e->getMessage()), 0, $e); |
||
647 | } |
||
648 | |||
649 | // no errors and we got a document back? excellent. this will be our new baby from now. time to kill the old one |
||
650 | |||
651 | // get the old document URI |
||
652 | $documentUri = $document->documentURI; |
||
653 | |||
654 | // and assign the new document to the old one |
||
655 | $document = $newdoc; |
||
656 | |||
657 | // save the old document URI just in case |
||
658 | $document->documentURI = $documentUri; |
||
659 | } |
||
660 | |||
661 | return $document; |
||
662 | } |
||
663 | |||
664 | /** |
||
665 | * Transforms a given document according to xml-stylesheet processing |
||
666 | * instructions |
||
667 | * |
||
668 | * @param XmlConfigDomDocument $document The document to act upon. |
||
669 | * @param string $environment The environment name. |
||
670 | * @param string $context The context name. |
||
671 | * |
||
672 | * @return XmlConfigDomDocument The transformed document. |
||
673 | * |
||
674 | * @author David Zülke <[email protected]> |
||
675 | * @author Noah Fontes <[email protected]> |
||
676 | * @since 1.0.0 |
||
677 | */ |
||
678 | public static function transformProcessingInstructions(XmlConfigDomDocument $document, $environment, $context) |
||
679 | { |
||
680 | $transformations = array(); |
||
681 | $transformationInfo = array(); |
||
682 | |||
683 | $xpath = $document->getXpath(); |
||
684 | |||
685 | // see if there are <?xml-stylesheet... processing instructions |
||
686 | $stylesheetProcessingInstructions = $xpath->query("//processing-instruction('xml-stylesheet')", $document); |
||
687 | foreach ($stylesheetProcessingInstructions as $pi) { |
||
688 | // yes! alright. trick: we create a doc fragment with the contents so we don't have to parse things by hand... |
||
689 | $fragment = $document->createDocumentFragment(); |
||
690 | $fragment->appendXml('<foo ' . $pi->data . ' />'); |
||
691 | $type = $fragment->firstChild->getAttribute('type'); |
||
692 | // we process only the types below... |
||
693 | if (in_array($type, array('text/xml', 'text/xsl', 'application/xml', 'application/xsl+xml'))) { |
||
694 | $href = $href = $fragment->firstChild->getAttribute('href'); |
||
695 | |||
696 | if (strpos($href, '#') === 0) { |
||
697 | // the href points to an embedded XSL stylesheet (with ID reference), so let's see if we can find it |
||
698 | $stylesheets = $xpath->query("//*[@id='" . substr($href, 1) . "']", $document); |
||
699 | if ($stylesheets->length) { |
||
700 | // excellent. make a new doc from that element! |
||
701 | try { |
||
702 | $xsl = new XmlConfigDomDocument(); |
||
703 | $xsl->appendChild($xsl->importNode($stylesheets->item(0), true)); |
||
704 | } catch (\DOMException $dome) { |
||
705 | throw new ParseException(sprintf('Configuration file "%s" could not be parsed: Could not load XSL stylesheet "%s": %s', $document->documentURI, $href, $dome->getMessage()), 0, $dome); |
||
706 | } |
||
707 | |||
708 | // and append to the list of XSLs to process |
||
709 | // TODO: spec mandates that external XSLs be processed first! |
||
710 | $transformations[] = $xsl; |
||
711 | } else { |
||
712 | throw new ParseException(sprintf('Configuration file "%s" could not be parsed because the inline stylesheet "%s" referenced in the "xml-stylesheet" processing instruction could not be found in the document.', $document->documentURI, $href)); |
||
713 | } |
||
714 | } else { |
||
715 | // href references an xsl file, remember the path |
||
716 | $transformationInfo[] = Toolkit::expandDirectives($href); |
||
717 | } |
||
718 | |||
719 | // remove the processing instructions after we dealt with them |
||
720 | $pi->parentNode->removeChild($pi); |
||
721 | } |
||
722 | } |
||
723 | |||
724 | return self::transform($document, $environment, $context, $transformationInfo, $transformations); |
||
725 | } |
||
726 | |||
727 | /** |
||
728 | * Perform validation on a given document. |
||
729 | * |
||
730 | * @param XmlConfigDomDocument $document The document to act upon. |
||
731 | * @param string $environment The environment name. |
||
732 | * @param string $context The context name. |
||
733 | * @param array $validationInfo An array of validation information. |
||
734 | * |
||
735 | * @author David Zülke <[email protected]> |
||
736 | * @author Noah Fontes <[email protected]> |
||
737 | * @since 0.11.0 |
||
738 | */ |
||
739 | public static function validate(XmlConfigDomDocument $document, $environment, $context, array $validationInfo = array()) |
||
740 | { |
||
741 | // bail out right away if validation is disabled |
||
742 | if (Config::get('core.skip_config_validation', false)) { |
||
0 ignored issues
–
show
false is of type boolean , but the function expects a string|null .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
743 | return; |
||
744 | } |
||
745 | |||
746 | $errors = array(); |
||
747 | |||
748 | foreach ($validationInfo as $type => $files) { |
||
749 | try { |
||
750 | switch ($type) { |
||
751 | case self::VALIDATION_TYPE_XMLSCHEMA: |
||
752 | self::validateXmlschema($document, (array) $files); |
||
753 | break; |
||
754 | case self::VALIDATION_TYPE_RELAXNG: |
||
755 | self::validateRelaxng($document, (array) $files); |
||
756 | break; |
||
757 | case self::VALIDATION_TYPE_SCHEMATRON: |
||
758 | self::validateSchematron($document, $environment, $context, (array) $files); |
||
759 | break; |
||
760 | } |
||
761 | } catch (ParseException $e) { |
||
762 | $errors[] = $e->getMessage(); |
||
763 | } |
||
764 | } |
||
765 | |||
766 | if ($errors) { |
||
0 ignored issues
–
show
The expression
$errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
767 | throw new ParseException(sprintf('Validation of configuration file "%s" failed:' . "\n\n%s", $document->documentURI, implode("\n\n", $errors))); |
||
768 | } |
||
769 | } |
||
770 | |||
771 | /** |
||
772 | * Clean up a given document. |
||
773 | * |
||
774 | * @param XmlConfigDomDocument $document The document to clean up. |
||
775 | * |
||
776 | * @author David Zülke <[email protected]> |
||
777 | * @since 0.11.0 |
||
778 | */ |
||
779 | public static function cleanup(XmlConfigDomDocument $document) |
||
780 | { |
||
781 | // remove top-level <sandbox> element |
||
782 | if ($sandbox = $document->getSandbox()) { |
||
783 | $sandbox->parentNode->removeChild($sandbox); |
||
784 | } |
||
785 | } |
||
786 | |||
787 | /** |
||
788 | * Validate a given document according to XMLSchema-instance (xsi) |
||
789 | * declarations. |
||
790 | * |
||
791 | * @param XmlConfigDomDocument $document The document to act upon. |
||
792 | * |
||
793 | * @author David Zülke <[email protected]> |
||
794 | * @author Noah Fontes <[email protected]> |
||
795 | * @since 1.0.0 |
||
796 | */ |
||
797 | public static function validateXsi(XmlConfigDomDocument $document) |
||
798 | { |
||
799 | // next, find (and validate against) XML schema instance declarations |
||
800 | $sources = array(); |
||
801 | if ($document->documentElement->hasAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) { |
||
802 | // find locations. for namespaces, they are space separated pairs of a namespace URI and a schema location |
||
803 | $locations = preg_split('/\s+/', $document->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')); |
||
804 | for ($i = 1; $i < count($locations); $i = $i + 2) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
805 | $sources[] = $locations[$i]; |
||
806 | } |
||
807 | } |
||
808 | // no namespace? then it's only one schema location in this attribute |
||
809 | if ($document->documentElement->hasAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'noNamespaceSchemaLocation')) { |
||
810 | $sources[] = $document->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'noNamespaceSchemaLocation'); |
||
811 | } |
||
812 | if ($sources) { |
||
0 ignored issues
–
show
The expression
$sources of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
813 | // we have instances to validate against... |
||
814 | $schemas = array(); |
||
815 | foreach ($sources as &$source) { |
||
816 | // so for each location, we need to grab the file and validate against this grabbed source code, as libxml often has a hard time retrieving stuff over HTTP |
||
817 | $source = Toolkit::expandDirectives($source); |
||
818 | if (parse_url($source, PHP_URL_SCHEME) === null && !Toolkit::isPathAbsolute($source)) { |
||
819 | // the schema location is relative to the XML file |
||
820 | $source = dirname($document->documentURI) . DIRECTORY_SEPARATOR . $source; |
||
821 | } |
||
822 | $schema = @file_get_contents($source); |
||
823 | if ($schema === false) { |
||
824 | throw new UnreadableException(sprintf('XML Schema validation file "%s" for configuration file "%s" does not exist or is unreadable', $source, $document->documentURI)); |
||
825 | } |
||
826 | $schemas[] = $schema; |
||
827 | } |
||
828 | // now validate them all |
||
829 | self::validateXmlschemaSource($document, $schemas); |
||
830 | } |
||
831 | } |
||
832 | |||
833 | /** |
||
834 | * Validate the document against the given list of XML Schema files. |
||
835 | * |
||
836 | * @param XmlConfigDomDocument $document The document to act upon. |
||
837 | * @param array $validationFiles An array of file names to validate against. |
||
838 | * |
||
839 | * @author David Zülke <[email protected]> |
||
840 | * @author Noah Fontes <[email protected]> |
||
841 | * @since 0.11.0 |
||
842 | */ |
||
843 | View Code Duplication | public static function validateXmlschema(XmlConfigDomDocument $document, array $validationFiles = array()) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
844 | { |
||
845 | foreach ($validationFiles as $validationFile) { |
||
846 | if (!is_resource($validationFile) && !is_readable($validationFile)) { |
||
847 | throw new UnreadableException(sprintf('XML Schema validation file "%s" for configuration file "%s" does not exist or is unreadable', $validationFile, $document->documentURI)); |
||
848 | } |
||
849 | |||
850 | try { |
||
851 | $document->schemaValidate($validationFile); |
||
852 | } catch (\DOMException $dome) { |
||
853 | throw new ParseException(sprintf('XML Schema validation of configuration file "%s" failed:' . "\n\n%s", $document->documentURI, $dome->getMessage()), 0, $dome); |
||
854 | } |
||
855 | } |
||
856 | } |
||
857 | |||
858 | /** |
||
859 | * Validate the document against the given list of XML Schema documents. |
||
860 | * |
||
861 | * @param XmlConfigDomDocument $document The document to act upon. |
||
862 | * @param array $validationSources An array of schema documents to validate against. |
||
863 | * |
||
864 | * @author David Zülke <[email protected]> |
||
865 | * @author Noah Fontes <[email protected]> |
||
866 | * @since 1.0.0 |
||
867 | */ |
||
868 | public static function validateXmlschemaSource(XmlConfigDomDocument $document, array $validationSources = array()) |
||
869 | { |
||
870 | foreach ($validationSources as $validationSource) { |
||
871 | try { |
||
872 | $document->schemaValidateSource($validationSource); |
||
873 | } catch (\DOMException $dome) { |
||
874 | throw new ParseException(sprintf('XML Schema validation of configuration file "%s" failed:' . "\n\n%s", $document->documentURI, $dome->getMessage()), 0, $dome); |
||
875 | } |
||
876 | } |
||
877 | } |
||
878 | |||
879 | /** |
||
880 | * Validate the document against the given list of RELAX NG files. |
||
881 | * |
||
882 | * @param XmlConfigDomDocument $document The document to act upon. |
||
883 | * @param array $validationFiles An array of file names to validate against. |
||
884 | * |
||
885 | * @author David Zülke <[email protected]> |
||
886 | * @author Noah Fontes <[email protected]> |
||
887 | * @since 0.11.0 |
||
888 | */ |
||
889 | View Code Duplication | public static function validateRelaxng(XmlConfigDomDocument $document, array $validationFiles = array()) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
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. ![]() |
|||
890 | { |
||
891 | foreach ($validationFiles as $validationFile) { |
||
892 | if (!is_readable($validationFile)) { |
||
893 | throw new UnreadableException(sprintf('RELAX NG validation file "%s" for configuration file "%s" does not exist or is unreadable', $validationFile, $document->documentURI)); |
||
894 | } |
||
895 | |||
896 | try { |
||
897 | $document->relaxNGValidate($validationFile); |
||
898 | } catch (\DOMException $dome) { |
||
899 | throw new ParseException(sprintf('RELAX NG validation of configuration file "%s" failed:' . "\n\n%s", $document->documentURI, $dome->getMessage()), 0, $dome); |
||
900 | } |
||
901 | } |
||
902 | } |
||
903 | |||
904 | /** |
||
905 | * Validate the document against the given list of Schematron files. |
||
906 | * |
||
907 | * @param XmlConfigDomDocument $document The document to act upon. |
||
908 | * @param string $environment The environment name. |
||
909 | * @param string $context The context name. |
||
910 | * @param array $validationFiles An array of file names to validate against. |
||
911 | * |
||
912 | * @author David Zülke <[email protected]> |
||
913 | * @author Noah Fontes <[email protected]> |
||
914 | * @since 0.11.0 |
||
915 | */ |
||
916 | public static function validateSchematron(XmlConfigDomDocument $document, $environment, $context, array $validationFiles = array()) |
||
917 | { |
||
918 | if (Config::get('core.skip_config_transformations', false)) { |
||
0 ignored issues
–
show
false is of type boolean , but the function expects a string|null .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
919 | return; |
||
920 | } |
||
921 | |||
922 | // load the schematron processor |
||
923 | $schematron = new SchematronProcessor(); |
||
924 | $schematron->setNode($document); |
||
925 | // set some info (config file path, context name, environment name) as params |
||
926 | // first arg is the namespace URI, which PHP doesn't support. awesome. see http://bugs.php.net/bug.php?id=30622 for the sad details |
||
927 | // we could use "agavi:context" etc, that does work even without such a prefix being declared in the stylesheet, but that would be completely non-XML-ish, confusing, and against the spec. so we use dots instead. |
||
928 | $schematron->setParameters(array( |
||
929 | 'agavi.config_path' => $document->documentURI, |
||
930 | 'agavi.environment' => $environment, |
||
931 | 'agavi.context' => $context, |
||
932 | )); |
||
933 | |||
934 | // loop over all validation files. those are .sch schematron schemas, which we transform to an XSL document that is then used to validate the source document :) |
||
935 | foreach ($validationFiles as $href) { |
||
936 | if (!is_readable($href)) { |
||
937 | throw new UnreadableException(sprintf('Schematron validation file "%s" for configuration file "%s" does not exist or is unreadable', $href, $document->documentURI)); |
||
938 | } |
||
939 | |||
940 | // load the .sch file |
||
941 | try { |
||
942 | $sch = new XmlConfigDomDocument(); |
||
943 | $sch->load($href); |
||
944 | } catch (\DOMException $dome) { |
||
945 | throw new ParseException(sprintf('Schematron validation of configuration file "%s" failed: Could not load schema file "%s": %s', $document->documentURI, $href, $dome->getMessage()), 0, $dome); |
||
946 | } |
||
947 | |||
948 | // perform the validation transformation |
||
949 | try { |
||
950 | $result = $schematron->transform($sch); |
||
951 | } catch (\Exception $e) { |
||
952 | throw new ParseException(sprintf('Schematron validation of configuration file "%s" failed: Transformation failed: %s', $document->documentURI, $e->getMessage()), 0, $e); |
||
953 | } |
||
954 | |||
955 | // validation ran okay, now we need to look at the result document to see if there are errors |
||
956 | /** @var \DOMXPath $xpath */ |
||
957 | $xpath = $result->getXpath(); |
||
958 | $xpath->registerNamespace('svrl', self::NAMESPACE_SVRL_ISO); |
||
959 | |||
960 | $results = $xpath->query('/svrl:schematron-output/svrl:failed-assert/svrl:text'); |
||
961 | if ($results->length) { |
||
962 | $errors = array('Failed assertions:'); |
||
963 | |||
964 | foreach ($results as $result) { |
||
965 | $errors[] = $result->nodeValue; |
||
966 | } |
||
967 | |||
968 | $results = $xpath->query('/svrl:schematron-output/svrl:successful-report/svrl:text'); |
||
969 | if ($results->length) { |
||
970 | $errors[] = ''; |
||
971 | $errors[] = 'Successful reports:'; |
||
972 | foreach ($results as $result) { |
||
973 | $errors[] = $result->nodeValue; |
||
974 | } |
||
975 | } |
||
976 | |||
977 | throw new ParseException(sprintf('Schematron validation of configuration file "%s" failed:' . "\n\n%s", $document->documentURI, implode("\n", $errors))); |
||
978 | } |
||
979 | } |
||
980 | } |
||
981 | } |
||
982 |
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: