merged branch hason/config (PR #5875)
This PR was merged into the master branch. Commits -------45aab0f
[Config] Added parameter to check prefix in an element or an attribute name in XmlUtils::convertDomElementToArray7f5a7fd
[DependencyInjection] Using class from Config component to loading XML filesd6f0880
[Validator] Added dependency to Config component to loading XML files34471a6
[Routing] Using class from Config component to loading XML filesfa8b0d8
[Config] Added a utils class for XML manipulations Discussion ---------- [Config] Added a utils class for XML manipulations Bug fix: no Feature addition: yes Backwards compatibility break: no Symfony2 tests pass: yes License of the code: MIT --------------------------------------------------------------------------- by fabpot at 2012-12-14T13:59:59Z Looks good to me. Can you rebase on master as there is a conflict right now? Thanks. --------------------------------------------------------------------------- by hason at 2012-12-14T14:44:11Z Done.
This commit is contained in:
commit
f68ae0e57b
@ -6,6 +6,7 @@ CHANGELOG
|
|||||||
|
|
||||||
* added numerical type handling for config definitions
|
* added numerical type handling for config definitions
|
||||||
* added convenience methods for optional configuration sections to ArrayNodeDefinition
|
* added convenience methods for optional configuration sections to ArrayNodeDefinition
|
||||||
|
* added a utils class for XML manipulations
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE scan [<!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource={{ resource }}">]>
|
||||||
|
<scan></scan>
|
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root2 xmlns="http://example.com/schema" />
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<xsd:schema xmlns="http://example.com/schema"
|
||||||
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
targetNamespace="http://example.com/schema"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
|
||||||
|
<xsd:element name="root" />
|
||||||
|
</xsd:schema>
|
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root xmlns="http://example.com/schema">
|
||||||
|
</root>
|
129
src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php
Normal file
129
src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Config\Tests\Loader;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Util\XmlUtils;
|
||||||
|
|
||||||
|
class XmlUtilsTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testLoadFile()
|
||||||
|
{
|
||||||
|
$fixtures = __DIR__.'/../Fixtures/Util/';
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'invalid.xml');
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('ERROR 77', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'document_type.xml');
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('Document types are not allowed', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'invalid_schema.xml', $fixtures.'schema.xsd');
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('ERROR 1845', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'invalid_schema.xml', 'invalid_callback_or_file');
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('XSD file or callable', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$mock = $this->getMock(__NAMESPACE__.'\Validator');
|
||||||
|
$mock->expects($this->exactly(2))->method('validate')->will($this->onConsecutiveCalls(false, true));
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'));
|
||||||
|
$this->fail();
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->assertContains('is not valid', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getDataForConvertDomToArray
|
||||||
|
*/
|
||||||
|
public function testConvertDomToArray($expected, $xml, $root = false, $checkPrefix = true)
|
||||||
|
{
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->loadXML($root ? $xml : '<root>'.$xml.'</root>');
|
||||||
|
|
||||||
|
$this->assertSame($expected, XmlUtils::convertDomElementToArray($dom->documentElement, $checkPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDataForConvertDomToArray()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(null, ''),
|
||||||
|
array('bar', 'bar'),
|
||||||
|
array(array('bar' => 'foobar'), '<foo bar="foobar" />', true),
|
||||||
|
array(array('foo' => null), '<foo />'),
|
||||||
|
array(array('foo' => 'bar'), '<foo>bar</foo>'),
|
||||||
|
array(array('foo' => array('foo' => 'bar')), '<foo foo="bar"/>'),
|
||||||
|
array(array('foo' => array('foo' => 'bar')), '<foo><foo>bar</foo></foo>'),
|
||||||
|
array(array('foo' => array('foo' => 'bar', 'value' => 'text')), '<foo foo="bar">text</foo>'),
|
||||||
|
array(array('foo' => array('attr' => 'bar', 'foo' => 'text')), '<foo attr="bar"><foo>text</foo></foo>'),
|
||||||
|
array(array('foo' => array('bar', 'text')), '<foo>bar</foo><foo>text</foo>'),
|
||||||
|
array(array('foo' => array(array('foo' => 'bar'), array('foo' => 'text'))), '<foo foo="bar"/><foo foo="text" />'),
|
||||||
|
array(array('foo' => array('foo' => array('bar', 'text'))), '<foo foo="bar"><foo>text</foo></foo>'),
|
||||||
|
array(array('foo' => 'bar'), '<foo><!-- Comment -->bar</foo>'),
|
||||||
|
array(array('foo' => 'text'), '<foo xmlns:h="http://www.example.org/bar" h:bar="bar">text</foo>'),
|
||||||
|
array(array('foo' => array('bar' => 'bar', 'value' => 'text')), '<foo xmlns:h="http://www.example.org/bar" h:bar="bar">text</foo>', false, false),
|
||||||
|
array(array('attr' => 1, 'b' => 'hello'), '<foo:a xmlns:foo="http://www.example.org/foo" xmlns:h="http://www.example.org/bar" attr="1" h:bar="bar"><foo:b>hello</foo:b><h:c>2</h:c></foo:a>', true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getDataForPhpize
|
||||||
|
*/
|
||||||
|
public function testPhpize($expected, $value)
|
||||||
|
{
|
||||||
|
$this->assertSame($expected, XmlUtils::phpize($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDataForPhpize()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(null, 'null'),
|
||||||
|
array(true, 'true'),
|
||||||
|
array(false, 'false'),
|
||||||
|
array(null, 'Null'),
|
||||||
|
array(true, 'True'),
|
||||||
|
array(false, 'False'),
|
||||||
|
array(0, '0'),
|
||||||
|
array(1, '1'),
|
||||||
|
array(0777, '0777'),
|
||||||
|
array(255, '0xFF'),
|
||||||
|
array(100.0, '1e2'),
|
||||||
|
array(-120.0, '-1.2E2'),
|
||||||
|
array(-10100.1, '-10100.1'),
|
||||||
|
array(-10100.1, '-10,100.1'),
|
||||||
|
array('foo', 'foo'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Validator
|
||||||
|
{
|
||||||
|
public function validate();
|
||||||
|
}
|
222
src/Symfony/Component/Config/Util/XmlUtils.php
Normal file
222
src/Symfony/Component/Config/Util/XmlUtils.php
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Config\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XMLUtils is a bunch of utility methods to XML operations.
|
||||||
|
*
|
||||||
|
* This class contains static methods only and is not meant to be instantiated.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||||
|
*/
|
||||||
|
class XmlUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This class should not be instantiated
|
||||||
|
*/
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an XML file.
|
||||||
|
*
|
||||||
|
* @param string $file An XML file path
|
||||||
|
* @param string|callable $schemaOrCallable An XSD schema file path or callable
|
||||||
|
*
|
||||||
|
* @return \DOMDocument
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException When loading of XML file returns error
|
||||||
|
*/
|
||||||
|
public static function loadFile($file, $schemaOrCallable = null)
|
||||||
|
{
|
||||||
|
$internalErrors = libxml_use_internal_errors(true);
|
||||||
|
$disableEntities = libxml_disable_entity_loader(true);
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->validateOnParse = true;
|
||||||
|
if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$dom->normalizeDocument();
|
||||||
|
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
libxml_disable_entity_loader($disableEntities);
|
||||||
|
|
||||||
|
foreach ($dom->childNodes as $child) {
|
||||||
|
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
|
||||||
|
throw new \InvalidArgumentException('Document types are not allowed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $schemaOrCallable) {
|
||||||
|
$internalErrors = libxml_use_internal_errors(true);
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
|
$e = null;
|
||||||
|
if (is_callable($schemaOrCallable)) {
|
||||||
|
try {
|
||||||
|
$valid = call_user_func($schemaOrCallable, $dom, $internalErrors);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
} elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) {
|
||||||
|
$valid = @$dom->schemaValidate($schemaOrCallable);
|
||||||
|
} else {
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$valid) {
|
||||||
|
$messages = static::getXmlErrors($internalErrors);
|
||||||
|
if (empty($messages)) {
|
||||||
|
$messages = array(sprintf('The XML file "%s" is not valid.', $file));
|
||||||
|
}
|
||||||
|
throw new \InvalidArgumentException(implode("\n", $messages), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a \DomElement object to a PHP array.
|
||||||
|
*
|
||||||
|
* The following rules applies during the conversion:
|
||||||
|
*
|
||||||
|
* * Each tag is converted to a key value or an array
|
||||||
|
* if there is more than one "value"
|
||||||
|
*
|
||||||
|
* * The content of a tag is set under a "value" key (<foo>bar</foo>)
|
||||||
|
* if the tag also has some nested tags
|
||||||
|
*
|
||||||
|
* * The attributes are converted to keys (<foo foo="bar"/>)
|
||||||
|
*
|
||||||
|
* * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>)
|
||||||
|
*
|
||||||
|
* @param \DomElement $element A \DomElement instance
|
||||||
|
* @param Boolean $checkPrefix Check prefix in an element or an attribute name
|
||||||
|
*
|
||||||
|
* @return array A PHP array
|
||||||
|
*/
|
||||||
|
public static function convertDomElementToArray(\DomElement $element, $checkPrefix = true)
|
||||||
|
{
|
||||||
|
$prefix = (string) $element->prefix;
|
||||||
|
$empty = true;
|
||||||
|
$config = array();
|
||||||
|
foreach ($element->attributes as $name => $node) {
|
||||||
|
if ($checkPrefix && !in_array((string) $node->prefix, array('', $prefix), true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$config[$name] = static::phpize($node->value);
|
||||||
|
$empty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nodeValue = false;
|
||||||
|
foreach ($element->childNodes as $node) {
|
||||||
|
if ($node instanceof \DOMText) {
|
||||||
|
if (trim($node->nodeValue)) {
|
||||||
|
$nodeValue = trim($node->nodeValue);
|
||||||
|
$empty = false;
|
||||||
|
}
|
||||||
|
} elseif ($checkPrefix && $prefix != (string) $node->prefix) {
|
||||||
|
continue;
|
||||||
|
} elseif (!$node instanceof \DOMComment) {
|
||||||
|
$value = static::convertDomElementToArray($node, $checkPrefix);
|
||||||
|
|
||||||
|
$key = $node->localName;
|
||||||
|
if (isset($config[$key])) {
|
||||||
|
if (!is_array($config[$key]) || !is_int(key($config[$key]))) {
|
||||||
|
$config[$key] = array($config[$key]);
|
||||||
|
}
|
||||||
|
$config[$key][] = $value;
|
||||||
|
} else {
|
||||||
|
$config[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$empty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $nodeValue) {
|
||||||
|
$value = static::phpize($nodeValue);
|
||||||
|
if (count($config)) {
|
||||||
|
$config['value'] = $value;
|
||||||
|
} else {
|
||||||
|
$config = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !$empty ? $config : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an xml value to a php type.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function phpize($value)
|
||||||
|
{
|
||||||
|
$value = (string) $value;
|
||||||
|
$lowercaseValue = strtolower($value);
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case 'null' === $lowercaseValue:
|
||||||
|
return null;
|
||||||
|
case ctype_digit($value):
|
||||||
|
$raw = $value;
|
||||||
|
$cast = intval($value);
|
||||||
|
|
||||||
|
return '0' == $value[0] ? octdec($value) : (((string) $raw == (string) $cast) ? $cast : $raw);
|
||||||
|
case 'true' === $lowercaseValue:
|
||||||
|
return true;
|
||||||
|
case 'false' === $lowercaseValue:
|
||||||
|
return false;
|
||||||
|
case is_numeric($value):
|
||||||
|
return '0x' == $value[0].$value[1] ? hexdec($value) : floatval($value);
|
||||||
|
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $value):
|
||||||
|
return floatval(str_replace(',', '', $value));
|
||||||
|
default:
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function getXmlErrors($internalErrors)
|
||||||
|
{
|
||||||
|
$errors = array();
|
||||||
|
foreach (libxml_get_errors() as $error) {
|
||||||
|
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
|
||||||
|
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
|
||||||
|
$error->code,
|
||||||
|
trim($error->message),
|
||||||
|
$error->file ? $error->file : 'n/a',
|
||||||
|
$error->line,
|
||||||
|
$error->column
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
libxml_clear_errors();
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\DependencyInjection\Loader;
|
namespace Symfony\Component\DependencyInjection\Loader;
|
||||||
|
|
||||||
use Symfony\Component\Config\Resource\FileResource;
|
use Symfony\Component\Config\Resource\FileResource;
|
||||||
|
use Symfony\Component\Config\Util\XmlUtils;
|
||||||
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\DependencyInjection\Alias;
|
use Symfony\Component\DependencyInjection\Alias;
|
||||||
@ -202,33 +203,17 @@ class XmlFileLoader extends FileLoader
|
|||||||
*
|
*
|
||||||
* @return SimpleXMLElement
|
* @return SimpleXMLElement
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When loading of XML file returns error
|
* @throws InvalidArgumentException When loading of XML file returns error
|
||||||
*/
|
*/
|
||||||
protected function parseFile($file)
|
protected function parseFile($file)
|
||||||
{
|
{
|
||||||
$internalErrors = libxml_use_internal_errors(true);
|
try {
|
||||||
$disableEntities = libxml_disable_entity_loader(true);
|
$dom = XmlUtils::loadFile($file, array($this, 'validateSchema'));
|
||||||
libxml_clear_errors();
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||||
$dom = new \DOMDocument();
|
|
||||||
$dom->validateOnParse = true;
|
|
||||||
if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
|
|
||||||
throw new InvalidArgumentException(implode("\n", $this->getXmlErrors($internalErrors)));
|
|
||||||
}
|
|
||||||
$dom->normalizeDocument();
|
|
||||||
|
|
||||||
libxml_use_internal_errors($internalErrors);
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
|
|
||||||
foreach ($dom->childNodes as $child) {
|
|
||||||
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
|
|
||||||
throw new InvalidArgumentException('Document types are not allowed.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->validate($dom, $file);
|
$this->validateExtensions($dom, $file);
|
||||||
|
|
||||||
return simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement');
|
return simplexml_import_dom($dom, 'Symfony\\Component\\DependencyInjection\\SimpleXMLElement');
|
||||||
}
|
}
|
||||||
@ -285,28 +270,14 @@ class XmlFileLoader extends FileLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates an XML document.
|
|
||||||
*
|
|
||||||
* @param \DOMDocument $dom
|
|
||||||
* @param string $file
|
|
||||||
*/
|
|
||||||
private function validate(\DOMDocument $dom, $file)
|
|
||||||
{
|
|
||||||
$this->validateSchema($dom, $file);
|
|
||||||
$this->validateExtensions($dom, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates a documents XML schema.
|
* Validates a documents XML schema.
|
||||||
*
|
*
|
||||||
* @param \DOMDocument $dom
|
* @param \DOMDocument $dom
|
||||||
* @param string $file
|
|
||||||
*
|
*
|
||||||
* @throws RuntimeException When extension references a non-existent XSD file
|
* @throws RuntimeException When extension references a non-existent XSD file
|
||||||
* @throws InvalidArgumentException When xml doesn't validate its xsd schema
|
|
||||||
*/
|
*/
|
||||||
private function validateSchema(\DOMDocument $dom, $file)
|
public function validateSchema(\DOMDocument $dom)
|
||||||
{
|
{
|
||||||
$schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
|
$schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd'));
|
||||||
|
|
||||||
@ -360,18 +331,13 @@ $imports
|
|||||||
EOF
|
EOF
|
||||||
;
|
;
|
||||||
|
|
||||||
$current = libxml_use_internal_errors(true);
|
|
||||||
libxml_clear_errors();
|
|
||||||
|
|
||||||
$valid = @$dom->schemaValidateSource($source);
|
$valid = @$dom->schemaValidateSource($source);
|
||||||
|
|
||||||
foreach ($tmpfiles as $tmpfile) {
|
foreach ($tmpfiles as $tmpfile) {
|
||||||
@unlink($tmpfile);
|
@unlink($tmpfile);
|
||||||
}
|
}
|
||||||
if (!$valid) {
|
|
||||||
throw new InvalidArgumentException(implode("\n", $this->getXmlErrors($current)));
|
return $valid;
|
||||||
}
|
|
||||||
libxml_use_internal_errors($current);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -403,33 +369,6 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of XML errors.
|
|
||||||
*
|
|
||||||
* @param Boolean $internalErrors
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getXmlErrors($internalErrors)
|
|
||||||
{
|
|
||||||
$errors = array();
|
|
||||||
foreach (libxml_get_errors() as $error) {
|
|
||||||
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
|
|
||||||
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
|
|
||||||
$error->code,
|
|
||||||
trim($error->message),
|
|
||||||
$error->file ? $error->file : 'n/a',
|
|
||||||
$error->line,
|
|
||||||
$error->column
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
libxml_clear_errors();
|
|
||||||
libxml_use_internal_errors($internalErrors);
|
|
||||||
|
|
||||||
return $errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads from an extension.
|
* Loads from an extension.
|
||||||
*
|
*
|
||||||
@ -472,50 +411,6 @@ EOF
|
|||||||
*/
|
*/
|
||||||
public static function convertDomElementToArray(\DomElement $element)
|
public static function convertDomElementToArray(\DomElement $element)
|
||||||
{
|
{
|
||||||
$empty = true;
|
return XmlUtils::convertDomElementToArray($element);
|
||||||
$config = array();
|
|
||||||
foreach ($element->attributes as $name => $node) {
|
|
||||||
$config[$name] = SimpleXMLElement::phpize($node->value);
|
|
||||||
$empty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$nodeValue = false;
|
|
||||||
foreach ($element->childNodes as $node) {
|
|
||||||
if ($node instanceof \DOMText) {
|
|
||||||
if (trim($node->nodeValue)) {
|
|
||||||
$nodeValue = trim($node->nodeValue);
|
|
||||||
$empty = false;
|
|
||||||
}
|
|
||||||
} elseif (!$node instanceof \DOMComment) {
|
|
||||||
if ($node instanceof \DOMElement && '_services' === $node->nodeName) {
|
|
||||||
$value = new Reference($node->getAttribute('id'));
|
|
||||||
} else {
|
|
||||||
$value = static::convertDomElementToArray($node);
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = $node->localName;
|
|
||||||
if (isset($config[$key])) {
|
|
||||||
if (!is_array($config[$key]) || !is_int(key($config[$key]))) {
|
|
||||||
$config[$key] = array($config[$key]);
|
|
||||||
}
|
|
||||||
$config[$key][] = $value;
|
|
||||||
} else {
|
|
||||||
$config[$key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$empty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false !== $nodeValue) {
|
|
||||||
$value = SimpleXMLElement::phpize($nodeValue);
|
|
||||||
if (count($config)) {
|
|
||||||
$config['value'] = $value;
|
|
||||||
} else {
|
|
||||||
$config = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !$empty ? $config : null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\DependencyInjection;
|
namespace Symfony\Component\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Util\XmlUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SimpleXMLElement class.
|
* SimpleXMLElement class.
|
||||||
*
|
*
|
||||||
@ -101,27 +103,6 @@ class SimpleXMLElement extends \SimpleXMLElement
|
|||||||
*/
|
*/
|
||||||
public static function phpize($value)
|
public static function phpize($value)
|
||||||
{
|
{
|
||||||
$value = (string) $value;
|
return XmlUtils::phpize($value);
|
||||||
$lowercaseValue = strtolower($value);
|
|
||||||
|
|
||||||
switch (true) {
|
|
||||||
case 'null' === $lowercaseValue:
|
|
||||||
return null;
|
|
||||||
case ctype_digit($value):
|
|
||||||
$raw = $value;
|
|
||||||
$cast = intval($value);
|
|
||||||
|
|
||||||
return '0' == $value[0] ? octdec($value) : (((string) $raw == (string) $cast) ? $cast : $raw);
|
|
||||||
case 'true' === $lowercaseValue:
|
|
||||||
return true;
|
|
||||||
case 'false' === $lowercaseValue:
|
|
||||||
return false;
|
|
||||||
case is_numeric($value):
|
|
||||||
return '0x' == $value[0].$value[1] ? hexdec($value) : floatval($value);
|
|
||||||
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $value):
|
|
||||||
return floatval(str_replace(',', '', $value));
|
|
||||||
default:
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ use Symfony\Component\Routing\RouteCollection;
|
|||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
use Symfony\Component\Config\Resource\FileResource;
|
use Symfony\Component\Config\Resource\FileResource;
|
||||||
use Symfony\Component\Config\Loader\FileLoader;
|
use Symfony\Component\Config\Loader\FileLoader;
|
||||||
|
use Symfony\Component\Config\Util\XmlUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XmlFileLoader loads XML routing files.
|
* XmlFileLoader loads XML routing files.
|
||||||
@ -172,74 +173,7 @@ class XmlFileLoader extends FileLoader
|
|||||||
*/
|
*/
|
||||||
protected function loadFile($file)
|
protected function loadFile($file)
|
||||||
{
|
{
|
||||||
$internalErrors = libxml_use_internal_errors(true);
|
return XmlUtils::loadFile($file, __DIR__ . static::SCHEME_PATH);
|
||||||
$disableEntities = libxml_disable_entity_loader(true);
|
|
||||||
libxml_clear_errors();
|
|
||||||
|
|
||||||
$dom = new \DOMDocument();
|
|
||||||
$dom->validateOnParse = true;
|
|
||||||
if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
|
|
||||||
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors($internalErrors)));
|
|
||||||
}
|
|
||||||
$dom->normalizeDocument();
|
|
||||||
|
|
||||||
libxml_use_internal_errors($internalErrors);
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
|
|
||||||
foreach ($dom->childNodes as $child) {
|
|
||||||
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
|
|
||||||
throw new \InvalidArgumentException('Document types are not allowed.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->validate($dom);
|
|
||||||
|
|
||||||
return $dom;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates a loaded XML file.
|
|
||||||
*
|
|
||||||
* @param \DOMDocument $dom A loaded XML file
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException When XML doesn't validate its XSD schema
|
|
||||||
*/
|
|
||||||
protected function validate(\DOMDocument $dom)
|
|
||||||
{
|
|
||||||
$current = libxml_use_internal_errors(true);
|
|
||||||
libxml_clear_errors();
|
|
||||||
|
|
||||||
if (!$dom->schemaValidate(__DIR__ . static::SCHEME_PATH)) {
|
|
||||||
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors($current)));
|
|
||||||
}
|
|
||||||
libxml_use_internal_errors($current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves libxml errors and clears them.
|
|
||||||
*
|
|
||||||
* @return array An array of libxml error strings
|
|
||||||
*/
|
|
||||||
private function getXmlErrors($internalErrors)
|
|
||||||
{
|
|
||||||
$errors = array();
|
|
||||||
foreach (libxml_get_errors() as $error) {
|
|
||||||
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
|
|
||||||
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
|
|
||||||
$error->code,
|
|
||||||
trim($error->message),
|
|
||||||
$error->file ? $error->file : 'n/a',
|
|
||||||
$error->line,
|
|
||||||
$error->column
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
libxml_clear_errors();
|
|
||||||
libxml_use_internal_errors($internalErrors);
|
|
||||||
|
|
||||||
return $errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Validator\Mapping\Loader;
|
|||||||
|
|
||||||
use Symfony\Component\Validator\Exception\MappingException;
|
use Symfony\Component\Validator\Exception\MappingException;
|
||||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||||
|
use Symfony\Component\Config\Util\XmlUtils;
|
||||||
|
|
||||||
class XmlFileLoader extends FileLoader
|
class XmlFileLoader extends FileLoader
|
||||||
{
|
{
|
||||||
@ -185,54 +186,12 @@ class XmlFileLoader extends FileLoader
|
|||||||
*/
|
*/
|
||||||
protected function parseFile($file)
|
protected function parseFile($file)
|
||||||
{
|
{
|
||||||
$internalErrors = libxml_use_internal_errors(true);
|
try {
|
||||||
$disableEntities = libxml_disable_entity_loader(true);
|
$dom = XmlUtils::loadFile($file, __DIR__.'/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd');
|
||||||
libxml_clear_errors();
|
} catch (\Exception $e) {
|
||||||
|
throw new MappingException($e->getMessage(), $e->getCode(), $e);
|
||||||
$dom = new \DOMDocument();
|
|
||||||
$dom->validateOnParse = true;
|
|
||||||
if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
|
|
||||||
throw new MappingException(implode("\n", $this->getXmlErrors($internalErrors)));
|
|
||||||
}
|
|
||||||
|
|
||||||
libxml_disable_entity_loader($disableEntities);
|
|
||||||
|
|
||||||
if (!$dom->schemaValidate(__DIR__.'/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd')) {
|
|
||||||
throw new MappingException(implode("\n", $this->getXmlErrors($internalErrors)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$dom->normalizeDocument();
|
|
||||||
|
|
||||||
libxml_use_internal_errors($internalErrors);
|
|
||||||
|
|
||||||
foreach ($dom->childNodes as $child) {
|
|
||||||
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
|
|
||||||
throw new MappingException('Document types are not allowed.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return simplexml_import_dom($dom);
|
return simplexml_import_dom($dom);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getXmlErrors($internalErrors)
|
|
||||||
{
|
|
||||||
$errors = array();
|
|
||||||
foreach (libxml_get_errors() as $error) {
|
|
||||||
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
|
|
||||||
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
|
|
||||||
$error->code,
|
|
||||||
trim($error->message),
|
|
||||||
$error->file ? $error->file : 'n/a',
|
|
||||||
$error->line,
|
|
||||||
$error->column
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
libxml_clear_errors();
|
|
||||||
libxml_use_internal_errors($internalErrors);
|
|
||||||
|
|
||||||
return $errors;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,14 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/http-foundation": "2.2.*",
|
"symfony/http-foundation": "2.2.*",
|
||||||
"symfony/locale": "2.2.*",
|
"symfony/locale": "2.2.*",
|
||||||
"symfony/yaml": "2.2.*"
|
"symfony/yaml": "2.2.*",
|
||||||
|
"symfony/config": "2.2.*"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"doctrine/common": ">=2.1,<2.4-dev",
|
"doctrine/common": ">=2.1,<2.4-dev",
|
||||||
"symfony/http-foundation": "2.2.*",
|
"symfony/http-foundation": "2.2.*",
|
||||||
"symfony/yaml": "2.2.*"
|
"symfony/yaml": "2.2.*",
|
||||||
|
"symfony/config": "2.2.*"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": { "Symfony\\Component\\Validator\\": "" }
|
"psr-0": { "Symfony\\Component\\Validator\\": "" }
|
||||||
|
Reference in New Issue
Block a user