From a0551d525c50707b46ea85edb3a147f95bff0bd9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 11 Jan 2010 15:56:49 +0100 Subject: [PATCH] [DependencyInjection] made the loader extensions much more reliable and robust --- .../DependencyInjection/Dumper/XmlDumper.php | 4 +- .../DependencyInjection/Loader/Loader.php | 14 +- .../Loader/LoaderExtensionInterface.php | 13 +- .../Loader/XmlFileLoader.php | 75 ++++++--- .../Loader/schema/services/services-1.0.xsd | 155 ++++++++++++++++++ .../DependencyInjection/Loader/services.xsd | 113 ------------- .../includes/ProjectExtension.php | 5 + .../DependencyInjection/xml/services1.xml | 4 +- .../DependencyInjection/xml/services10.xml | 4 +- .../DependencyInjection/xml/services11.xml | 4 +- .../DependencyInjection/xml/services12.xml | 4 +- .../DependencyInjection/xml/services2.xml | 4 +- .../DependencyInjection/xml/services3.xml | 4 +- .../DependencyInjection/xml/services4.xml | 4 +- .../DependencyInjection/xml/services5.xml | 4 +- .../DependencyInjection/xml/services6.xml | 4 +- .../DependencyInjection/xml/services7.xml | 4 +- .../DependencyInjection/xml/services8.xml | 4 +- .../DependencyInjection/xml/services9.xml | 4 +- 19 files changed, 276 insertions(+), 151 deletions(-) create mode 100644 src/Symfony/Components/DependencyInjection/Loader/schema/services/services-1.0.xsd delete mode 100644 src/Symfony/Components/DependencyInjection/Loader/services.xsd diff --git a/src/Symfony/Components/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Components/DependencyInjection/Dumper/XmlDumper.php index bfe0f27392..4c0ac3bb18 100644 --- a/src/Symfony/Components/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Components/DependencyInjection/Dumper/XmlDumper.php @@ -165,7 +165,9 @@ class XmlDumper extends Dumper return << - + EOF; } diff --git a/src/Symfony/Components/DependencyInjection/Loader/Loader.php b/src/Symfony/Components/DependencyInjection/Loader/Loader.php index 2b721b2550..aff4c24efc 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/Loader.php +++ b/src/Symfony/Components/DependencyInjection/Loader/Loader.php @@ -22,11 +22,23 @@ abstract class Loader implements LoaderInterface { static protected $extensions = array(); + /** + * Registers an extension. + * + * @param LoaderExtensionInterface $extension An extension instance + */ static public function registerExtension(LoaderExtensionInterface $extension) { - static::$extensions[$extension->getNamespace()] = $extension; + static::$extensions[$extension->getAlias()] = static::$extensions[$extension->getNamespace()] = $extension; } + /** + * Returns an extension by alias or namespace. + * + * @param string $name An alias or a namespace + * + * @return LoaderExtensionInterface An extension instance + */ static public function getExtension($name) { return isset(static::$extensions[$name]) ? static::$extensions[$name] : null; diff --git a/src/Symfony/Components/DependencyInjection/Loader/LoaderExtensionInterface.php b/src/Symfony/Components/DependencyInjection/Loader/LoaderExtensionInterface.php index a660bf4c8d..26b2f04cb1 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/LoaderExtensionInterface.php +++ b/src/Symfony/Components/DependencyInjection/Loader/LoaderExtensionInterface.php @@ -31,9 +31,18 @@ interface LoaderExtensionInterface public function load($tag, array $config); /** - * Returns the namespace to be used for this extension. + * Returns the namespace to be used for this extension (XML namespace). * - * @return string The namespace + * @return string The XML namespace */ public function getNamespace(); + + /** + * Returns the recommanded alias to use in XML. + * + * This alias is also the mandatory prefix to use when using YAML. + * + * @return string The alias + */ + public function getAlias(); } diff --git a/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php index 492f472bcd..688202067f 100644 --- a/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Components/DependencyInjection/Loader/XmlFileLoader.php @@ -195,10 +195,12 @@ class XmlFileLoader extends FileLoader { throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors())); } + $dom->validateOnParse = true; + $dom->normalizeDocument(); libxml_use_internal_errors(false); $this->validate($dom, $path); - $xmls[$path] = simplexml_import_dom($dom, 'Symfony\Components\DependencyInjection\SimpleXMLElement'); + $xmls[$path] = simplexml_import_dom($dom, 'Symfony\\Components\\DependencyInjection\\SimpleXMLElement'); } return $xmls; @@ -210,7 +212,7 @@ class XmlFileLoader extends FileLoader $count = 0; // find anonymous service definitions - $xml->registerXPathNamespace('container', 'http://symfony-project.org/2.0/container'); + $xml->registerXPathNamespace('container', 'http://www.symfony-project.org/schema/services'); $nodes = $xml->xpath('//container:argument[@type="service"][not(@id)]'); foreach ($nodes as $node) { @@ -236,14 +238,52 @@ class XmlFileLoader extends FileLoader protected function validate($dom, $file) { + $this->validateSchema($dom, $file); + $this->validateExtensions($dom, $file); + } + + protected function validateSchema($dom, $file) + { + $schemaLocations = array('http://www.symfony-project.org/schema/services' => __DIR__.'/schema/services/services-1.0.xsd'); + + if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) + { + $items = preg_split('/\s+/', $element); + for ($i = 0, $nb = count($items); $i < $nb; $i += 2) + { + $schemaLocations[$items[$i]] = str_replace('http://www.symfony-project.org/', __DIR__.'/', $items[$i + 1]); + } + } + + $imports = ''; + foreach ($schemaLocations as $namespace => $location) + { + $imports .= sprintf(' '."\n", $namespace, $location); + } + + $source = << + + + +$imports + +EOF + ; + libxml_use_internal_errors(true); - if (!$dom->schemaValidate(__DIR__.'/services.xsd')) + if (!$dom->schemaValidateSource($source)) { throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors())); } libxml_use_internal_errors(false); + } - // validate extensions + protected function validateExtensions($dom, $file) + { foreach ($dom->documentElement->childNodes as $node) { if (!$node instanceof \DOMElement || in_array($node->tagName, array('imports', 'parameters', 'services'))) @@ -251,19 +291,16 @@ class XmlFileLoader extends FileLoader continue; } - // can it be handled by an extension? - if (false !== strpos($node->tagName, ':')) + if ($node->namespaceURI === 'http://www.symfony-project.org/schema/services') { - list($namespace, $tag) = explode(':', $node->tagName); - if (!static::getExtension($namespace)) - { - throw new \InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in %s).', $node->tagName, $file)); - } - - continue; + throw new \InvalidArgumentException(sprintf('The "%s" tag is not valid (in %s).', $node->tagName, $file)); } - throw new \InvalidArgumentException(sprintf('The "%s" tag is not valid (in %s).', $node->tagName, $file)); + // can it be handled by an extension? + if (!static::getExtension($node->namespaceURI)) + { + throw new \InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in %s).', $node->tagName, $file)); + } } } @@ -289,15 +326,15 @@ class XmlFileLoader extends FileLoader protected function loadFromExtensions(BuilderConfiguration $configuration, $xml) { - foreach (dom_import_simplexml($xml)->getElementsByTagNameNS('*', '*') as $element) + foreach (dom_import_simplexml($xml)->childNodes as $node) { - if (!$element->prefix) + if (!$node instanceof \DOMElement || $node->namespaceURI === 'http://www.symfony-project.org/schema/services') { continue; } - $values = static::convertDomElementToArray($element); - $config = $this->getExtension($element->prefix)->load($element->localName, is_array($values) ? $values : array($values)); + $values = static::convertDomElementToArray($node); + $config = $this->getExtension($node->namespaceURI)->load($node->localName, is_array($values) ? $values : array($values)); $configuration->merge($config); } @@ -345,7 +382,7 @@ class XmlFileLoader extends FileLoader } elseif (!$node instanceof \DOMComment) { - $config[$node->tagName] = static::convertDomElementToArray($node); + $config[$node->localName] = static::convertDomElementToArray($node); $empty = false; } } diff --git a/src/Symfony/Components/DependencyInjection/Loader/schema/services/services-1.0.xsd b/src/Symfony/Components/DependencyInjection/Loader/schema/services/services-1.0.xsd new file mode 100644 index 0000000000..b86a54cb5b --- /dev/null +++ b/src/Symfony/Components/DependencyInjection/Loader/schema/services/services-1.0.xsd @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Components/DependencyInjection/Loader/services.xsd b/src/Symfony/Components/DependencyInjection/Loader/services.xsd deleted file mode 100644 index 45fbc989ab..0000000000 --- a/src/Symfony/Components/DependencyInjection/Loader/services.xsd +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/includes/ProjectExtension.php b/tests/fixtures/Symfony/Components/DependencyInjection/includes/ProjectExtension.php index 409d1aa003..82b636186c 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/includes/ProjectExtension.php +++ b/tests/fixtures/Symfony/Components/DependencyInjection/includes/ProjectExtension.php @@ -17,6 +17,11 @@ class ProjectExtension extends LoaderExtension } public function getNamespace() + { + return 'http://www.example.com/schema/project'; + } + + public function getAlias() { return 'project'; } diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services1.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services1.xml index 204c70eaa9..7fed40e139 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services1.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services1.xml @@ -1,4 +1,6 @@ - + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services10.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services10.xml index b0336ba996..da33906233 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services10.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services10.xml @@ -1,7 +1,7 @@ - + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services11.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services11.xml index d31cfc1aea..615efeae5f 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services11.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services11.xml @@ -1,7 +1,7 @@ - + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services12.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services12.xml index 97268df7d2..94ba0c6757 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services12.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services12.xml @@ -1,7 +1,7 @@ - + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services2.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services2.xml index e28d729625..11031cadb0 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services2.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services2.xml @@ -1,6 +1,8 @@ - + a string bar diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services3.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services3.xml index e10c772469..16bfb721d3 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services3.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services3.xml @@ -1,6 +1,8 @@ - + foo diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services4.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services4.xml index 1f865938c8..42f76abc31 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services4.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services4.xml @@ -1,6 +1,8 @@ - + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services5.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services5.xml index c2ef9ddd0b..f21ce2289d 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services5.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services5.xml @@ -1,6 +1,8 @@ - + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services6.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services6.xml index 03ceaf2653..c14e21f477 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services6.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services6.xml @@ -1,6 +1,8 @@ - + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services7.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services7.xml index edda0a1993..5387ff7f45 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services7.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services7.xml @@ -1,6 +1,8 @@ - + diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services8.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services8.xml index 342c7d4a23..ba3eeb36d6 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services8.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services8.xml @@ -1,6 +1,8 @@ - + bar foo is %%foo bar diff --git a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services9.xml b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services9.xml index 338a61ad28..6973da85ca 100644 --- a/tests/fixtures/Symfony/Components/DependencyInjection/xml/services9.xml +++ b/tests/fixtures/Symfony/Components/DependencyInjection/xml/services9.xml @@ -1,6 +1,8 @@ - + BazClass FooClass