diff --git a/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php b/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php index 161dc61721c12..a26a994d91050 100644 --- a/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php +++ b/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php @@ -55,13 +55,27 @@ public function testLoadFile() XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')); $this->fail(); } catch (\InvalidArgumentException $e) { - $this->assertContains('is not valid', $e->getMessage()); + $this->assertRegExp('/The XML file "[\w:\/\\\.]+" is not valid\./', $e->getMessage()); } $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'))); $this->assertSame(array(), libxml_get_errors()); } + /** + * @expectedException \Symfony\Component\Config\Util\Exception\InvalidXmlException + * @expectedExceptionMessage The XML is not valid + */ + public function testParseWithInvalidValidatorCallable() + { + $fixtures = __DIR__.'/../Fixtures/Util/'; + + $mock = $this->getMockBuilder(__NAMESPACE__.'\Validator')->getMock(); + $mock->expects($this->once())->method('validate')->willReturn(false); + + XmlUtils::parse(file_get_contents($fixtures.'valid.xml'), array($mock, 'validate')); + } + public function testLoadFileWithInternalErrorsEnabled() { $internalErrors = libxml_use_internal_errors(true); diff --git a/src/Symfony/Component/Config/Util/Exception/InvalidXmlException.php b/src/Symfony/Component/Config/Util/Exception/InvalidXmlException.php new file mode 100644 index 0000000000000..a335bbd2eed7c --- /dev/null +++ b/src/Symfony/Component/Config/Util/Exception/InvalidXmlException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated + * to the actual XML parsing. + * + * @author Ole Rößner + */ +class InvalidXmlException extends XmlParsingException +{ +} diff --git a/src/Symfony/Component/Config/Util/Exception/XmlParsingException.php b/src/Symfony/Component/Config/Util/Exception/XmlParsingException.php new file mode 100644 index 0000000000000..9bceed660baed --- /dev/null +++ b/src/Symfony/Component/Config/Util/Exception/XmlParsingException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML cannot be parsed properly. + * + * @author Ole Rößner + */ +class XmlParsingException extends \InvalidArgumentException +{ +} diff --git a/src/Symfony/Component/Config/Util/XmlUtils.php b/src/Symfony/Component/Config/Util/XmlUtils.php index 9c7ac126ef1ca..130384ea87030 100644 --- a/src/Symfony/Component/Config/Util/XmlUtils.php +++ b/src/Symfony/Component/Config/Util/XmlUtils.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Config\Util; +use Symfony\Component\Config\Util\Exception\InvalidXmlException; +use Symfony\Component\Config\Util\Exception\XmlParsingException; + /** * XMLUtils is a bunch of utility methods to XML operations. * @@ -18,6 +21,7 @@ * * @author Fabien Potencier * @author Martin Hasoň + * @author Ole Rößner */ class XmlUtils { @@ -29,27 +33,23 @@ private function __construct() } /** - * Loads an XML file. + * Parses an XML string. * - * @param string $file An XML file path + * @param string $content An XML string * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation * * @return \DOMDocument * - * @throws \InvalidArgumentException When loading of XML file returns error - * @throws \RuntimeException When DOM extension is missing + * @throws \Symfony\Component\Config\Util\Exception\XmlParsingException When parsing of XML file returns error + * @throws \Symfony\Component\Config\Util\Exception\InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself + * @throws \RuntimeException When DOM extension is missing */ - public static function loadFile($file, $schemaOrCallable = null) + public static function parse($content, $schemaOrCallable = null) { if (!extension_loaded('dom')) { throw new \RuntimeException('Extension DOM is required.'); } - $content = @file_get_contents($file); - if ('' === trim($content)) { - throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file)); - } - $internalErrors = libxml_use_internal_errors(true); $disableEntities = libxml_disable_entity_loader(true); libxml_clear_errors(); @@ -59,7 +59,7 @@ public static function loadFile($file, $schemaOrCallable = null) if (!$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) { libxml_disable_entity_loader($disableEntities); - throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors))); + throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors))); } $dom->normalizeDocument(); @@ -69,7 +69,7 @@ public static function loadFile($file, $schemaOrCallable = null) foreach ($dom->childNodes as $child) { if (XML_DOCUMENT_TYPE_NODE === $child->nodeType) { - throw new \InvalidArgumentException('Document types are not allowed.'); + throw new XmlParsingException('Document types are not allowed.'); } } @@ -90,15 +90,15 @@ public static function loadFile($file, $schemaOrCallable = null) } else { libxml_use_internal_errors($internalErrors); - throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); + throw new XmlParsingException('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 InvalidXmlException('The XML is not valid.', 0, $e); } - throw new \InvalidArgumentException(implode("\n", $messages), 0, $e); + throw new XmlParsingException(implode("\n", $messages), 0, $e); } } @@ -108,6 +108,32 @@ public static function loadFile($file, $schemaOrCallable = null) return $dom; } + /** + * Loads an XML file. + * + * @param string $file An XML file path + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file returns error + * @throws \Symfony\Component\Config\Util\Exception\XmlParsingException when XML parsing returns any errors + * @throws \RuntimeException When DOM extension is missing + */ + public static function loadFile($file, $schemaOrCallable = null) + { + $content = @file_get_contents($file); + if ('' === trim($content)) { + throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file)); + } + + try { + return static::parse($content, $schemaOrCallable); + } catch (InvalidXmlException $e) { + throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious()); + } + } + /** * Converts a \DomElement object to a PHP array. *