[DependencyInjection] changed the extension mechanism to allow an extension to be inherit and merge from an existing configuration
This commit is contained in:
parent
a79ad894f9
commit
b057ef613f
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Components\DependencyInjection;
|
namespace Symfony\Components\DependencyInjection;
|
||||||
|
|
||||||
use Symfony\Components\DependencyInjection\Loader\Loader;
|
use Symfony\Components\DependencyInjection\Loader\LoaderExtensionInterface;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the Symfony framework.
|
* This file is part of the Symfony framework.
|
||||||
@ -22,13 +22,18 @@ use Symfony\Components\DependencyInjection\Loader\Loader;
|
|||||||
*/
|
*/
|
||||||
class BuilderConfiguration
|
class BuilderConfiguration
|
||||||
{
|
{
|
||||||
protected $definitions = array();
|
protected $definitions;
|
||||||
protected $parameters = array();
|
protected $parameters;
|
||||||
protected $aliases = array();
|
protected $aliases;
|
||||||
protected $resources = array();
|
protected $resources;
|
||||||
|
protected $extensions;
|
||||||
|
|
||||||
public function __construct(array $definitions = array(), array $parameters = array())
|
public function __construct(array $definitions = array(), array $parameters = array())
|
||||||
{
|
{
|
||||||
|
$this->aliases = array();
|
||||||
|
$this->resources = array();
|
||||||
|
$this->extensions = array();
|
||||||
|
|
||||||
$this->setDefinitions($definitions);
|
$this->setDefinitions($definitions);
|
||||||
$this->setParameters($parameters);
|
$this->setParameters($parameters);
|
||||||
}
|
}
|
||||||
@ -82,20 +87,42 @@ class BuilderConfiguration
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the configuration given by an extension.
|
* Loads the configuration for an extension.
|
||||||
*
|
*
|
||||||
* @param $key string The extension tag to load (namespace.tag)
|
* @param $extension LoaderExtensionInterface A LoaderExtensionInterface instance
|
||||||
* @param $values array An array of values to customize the extension
|
* @param $tag string The extension tag to load (without the namespace - namespace.tag)
|
||||||
|
* @param $values array An array of values that customizes the extension
|
||||||
*
|
*
|
||||||
* @return BuilderConfiguration The current instance
|
* @return BuilderConfiguration The current instance
|
||||||
*/
|
*/
|
||||||
public function mergeExtension($key, array $values = array())
|
public function loadFromExtension(LoaderExtensionInterface $extension, $tag, array $values = array())
|
||||||
{
|
{
|
||||||
list($namespace, $tag) = explode('.', $key);
|
$namespace = $extension->getAlias();
|
||||||
|
|
||||||
$config = Loader::getExtension($namespace)->load($tag, $values);
|
if (!isset($this->extensions[$namespace])) {
|
||||||
|
$this->extensions[$namespace] = new self();
|
||||||
|
|
||||||
$this->merge($config);
|
$r = new \ReflectionObject($extension);
|
||||||
|
$this->extensions[$namespace]->addResource(new FileResource($r->getFileName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->extensions[$namespace] = $extension->load($tag, $values, $this->extensions[$namespace]);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges the extension configuration.
|
||||||
|
*
|
||||||
|
* @return BuilderConfiguration The current instance
|
||||||
|
*/
|
||||||
|
public function mergeExtensionsConfiguration()
|
||||||
|
{
|
||||||
|
foreach ($this->extensions as $name => $configuration) {
|
||||||
|
$this->merge($configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->extensions = array();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -26,17 +26,21 @@ class IniFileLoader extends FileLoader
|
|||||||
/**
|
/**
|
||||||
* Loads a resource.
|
* Loads a resource.
|
||||||
*
|
*
|
||||||
* @param string $file An INI file path
|
* @param mixed $resource The resource
|
||||||
|
* @param Boolean $main Whether this is the main load() call
|
||||||
|
* @param BuilderConfiguration $configuration A BuilderConfiguration instance to use for the configuration
|
||||||
*
|
*
|
||||||
* @return BuilderConfiguration A BuilderConfiguration instance
|
* @return BuilderConfiguration A BuilderConfiguration instance
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When ini file is not valid
|
* @throws \InvalidArgumentException When ini file is not valid
|
||||||
*/
|
*/
|
||||||
public function load($file)
|
public function load($file, $main = true, BuilderConfiguration $configuration = null)
|
||||||
{
|
{
|
||||||
$path = $this->findFile($file);
|
$path = $this->findFile($file);
|
||||||
|
|
||||||
$configuration = new BuilderConfiguration();
|
if (null === $configuration) {
|
||||||
|
$configuration = new BuilderConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
$configuration->addResource(new FileResource($path));
|
$configuration->addResource(new FileResource($path));
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace Symfony\Components\DependencyInjection\Loader;
|
namespace Symfony\Components\DependencyInjection\Loader;
|
||||||
|
|
||||||
|
use Symfony\Components\DependencyInjection\BuilderConfiguration;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the Symfony framework.
|
* This file is part of the Symfony framework.
|
||||||
*
|
*
|
||||||
@ -36,19 +38,20 @@ abstract class LoaderExtension implements LoaderExtensionInterface
|
|||||||
/**
|
/**
|
||||||
* Loads a specific configuration.
|
* Loads a specific configuration.
|
||||||
*
|
*
|
||||||
* @param string The tag name
|
* @param string $tag The tag name
|
||||||
* @param array An array of configuration values
|
* @param array $config An array of configuration values
|
||||||
|
* @param BuilderConfiguration $configuration A BuilderConfiguration instance
|
||||||
*
|
*
|
||||||
* @return BuilderConfiguration A BuilderConfiguration instance
|
* @return BuilderConfiguration A BuilderConfiguration instance
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When provided tag is not defined in this extension
|
* @throws \InvalidArgumentException When provided tag is not defined in this extension
|
||||||
*/
|
*/
|
||||||
public function load($tag, array $config)
|
public function load($tag, array $config, BuilderConfiguration $configuration)
|
||||||
{
|
{
|
||||||
if (!method_exists($this, $method = $tag.'Load')) {
|
if (!method_exists($this, $method = $tag.'Load')) {
|
||||||
throw new \InvalidArgumentException(sprintf('The tag "%s" is not defined in the "%s" extension.', $tag, $this->getNamespace()));
|
throw new \InvalidArgumentException(sprintf('The tag "%s" is not defined in the "%s" extension.', $tag, $this->getNamespace()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->$method($config);
|
return $this->$method($config, $configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace Symfony\Components\DependencyInjection\Loader;
|
namespace Symfony\Components\DependencyInjection\Loader;
|
||||||
|
|
||||||
|
use Symfony\Components\DependencyInjection\BuilderConfiguration;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the Symfony framework.
|
* This file is part of the Symfony framework.
|
||||||
*
|
*
|
||||||
@ -31,12 +33,15 @@ interface LoaderExtensionInterface
|
|||||||
/**
|
/**
|
||||||
* Loads a specific configuration.
|
* Loads a specific configuration.
|
||||||
*
|
*
|
||||||
* @param string The tag name
|
* @param string $tag The tag name
|
||||||
* @param array An array of configuration values
|
* @param array $config An array of configuration values
|
||||||
|
* @param BuilderConfiguration $configuration A BuilderConfiguration instance
|
||||||
*
|
*
|
||||||
* @return BuilderConfiguration A BuilderConfiguration instance
|
* @return BuilderConfiguration A BuilderConfiguration instance
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException When provided tag is not defined in this extension
|
||||||
*/
|
*/
|
||||||
public function load($tag, array $config);
|
public function load($tag, array $config, BuilderConfiguration $configuration);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the namespace to be used for this extension (XML namespace).
|
* Returns the namespace to be used for this extension (XML namespace).
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace Symfony\Components\DependencyInjection\Loader;
|
namespace Symfony\Components\DependencyInjection\Loader;
|
||||||
|
|
||||||
|
use Symfony\Components\DependencyInjection\BuilderConfiguration;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the Symfony framework.
|
* This file is part of the Symfony framework.
|
||||||
*
|
*
|
||||||
@ -56,11 +58,13 @@ interface LoaderInterface
|
|||||||
* If you load file1.xml and file2.xml in this order, the value of complex
|
* If you load file1.xml and file2.xml in this order, the value of complex
|
||||||
* will be "foo".
|
* will be "foo".
|
||||||
*
|
*
|
||||||
* @param mixed $resource The resource
|
* @param mixed $resource The resource
|
||||||
|
* @param Boolean $main Whether this is the main load() call
|
||||||
|
* @param BuilderConfiguration $configuration A BuilderConfiguration instance to use for the configuration
|
||||||
*
|
*
|
||||||
* @return BuilderConfiguration A BuilderConfiguration instance
|
* @return BuilderConfiguration A BuilderConfiguration instance
|
||||||
*/
|
*/
|
||||||
function load($resource);
|
function load($resource, $main = true, BuilderConfiguration $configuration = null);
|
||||||
|
|
||||||
static function registerExtension(LoaderExtensionInterface $extension);
|
static function registerExtension(LoaderExtensionInterface $extension);
|
||||||
}
|
}
|
||||||
|
@ -29,17 +29,21 @@ class XmlFileLoader extends FileLoader
|
|||||||
/**
|
/**
|
||||||
* Loads an array of XML files.
|
* Loads an array of XML files.
|
||||||
*
|
*
|
||||||
* @param string $file An XML file path
|
* @param mixed $resource The resource
|
||||||
|
* @param Boolean $main Whether this is the main load() call
|
||||||
|
* @param BuilderConfiguration $configuration A BuilderConfiguration instance to use for the configuration
|
||||||
*
|
*
|
||||||
* @return BuilderConfiguration A BuilderConfiguration instance
|
* @return BuilderConfiguration A BuilderConfiguration instance
|
||||||
*/
|
*/
|
||||||
public function load($file)
|
public function load($file, $main = true, BuilderConfiguration $configuration = null)
|
||||||
{
|
{
|
||||||
$path = $this->findFile($file);
|
$path = $this->findFile($file);
|
||||||
|
|
||||||
$xml = $this->parseFile($path);
|
$xml = $this->parseFile($path);
|
||||||
|
|
||||||
$configuration = new BuilderConfiguration();
|
if (null === $configuration) {
|
||||||
|
$configuration = new BuilderConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
$configuration->addResource(new FileResource($path));
|
$configuration->addResource(new FileResource($path));
|
||||||
|
|
||||||
@ -58,6 +62,10 @@ class XmlFileLoader extends FileLoader
|
|||||||
// extensions
|
// extensions
|
||||||
$this->loadFromExtensions($configuration, $xml);
|
$this->loadFromExtensions($configuration, $xml);
|
||||||
|
|
||||||
|
if ($main) {
|
||||||
|
$configuration->mergeExtensionsConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
return $configuration;
|
return $configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,11 +85,11 @@ class XmlFileLoader extends FileLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($xml->imports->import as $import) {
|
foreach ($xml->imports->import as $import) {
|
||||||
$configuration->merge($this->parseImport($import, $file));
|
$this->parseImport($configuration, $import, $file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function parseImport($import, $file)
|
protected function parseImport(BuilderConfiguration $configuration, $import, $file)
|
||||||
{
|
{
|
||||||
$class = null;
|
$class = null;
|
||||||
if (isset($import['class']) && $import['class'] !== get_class($this)) {
|
if (isset($import['class']) && $import['class'] !== get_class($this)) {
|
||||||
@ -102,7 +110,7 @@ class XmlFileLoader extends FileLoader
|
|||||||
|
|
||||||
$importedFile = $this->getAbsolutePath((string) $import['resource'], dirname($file));
|
$importedFile = $this->getAbsolutePath((string) $import['resource'], dirname($file));
|
||||||
|
|
||||||
return $loader->load($importedFile);
|
return $loader->load($importedFile, false, $configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function parseDefinitions(BuilderConfiguration $configuration, $xml, $file)
|
protected function parseDefinitions(BuilderConfiguration $configuration, $xml, $file)
|
||||||
@ -325,12 +333,11 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
$values = static::convertDomElementToArray($node);
|
$values = static::convertDomElementToArray($node);
|
||||||
$config = $this->getExtension($node->namespaceURI)->load($node->localName, is_array($values) ? $values : array($values));
|
if (!is_array($values)) {
|
||||||
|
$values = array();
|
||||||
|
}
|
||||||
|
|
||||||
$r = new \ReflectionObject($this->getExtension($node->namespaceURI));
|
$configuration->loadFromExtension($this->getExtension($node->namespaceURI), $node->localName, $values);
|
||||||
$config->addResource(new FileResource($r->getFileName()));
|
|
||||||
|
|
||||||
$configuration->merge($config);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,17 +32,21 @@ class YamlFileLoader extends FileLoader
|
|||||||
/**
|
/**
|
||||||
* Loads an array of Yaml files.
|
* Loads an array of Yaml files.
|
||||||
*
|
*
|
||||||
* @param string $file A YAML file path
|
* @param mixed $resource The resource
|
||||||
|
* @param Boolean $main Whether this is the main load() call
|
||||||
|
* @param BuilderConfiguration $configuration A BuilderConfiguration instance to use for the configuration
|
||||||
*
|
*
|
||||||
* @return BuilderConfiguration A BuilderConfiguration instance
|
* @return BuilderConfiguration A BuilderConfiguration instance
|
||||||
*/
|
*/
|
||||||
public function load($file)
|
public function load($file, $main = true, BuilderConfiguration $configuration = null)
|
||||||
{
|
{
|
||||||
$path = $this->findFile($file);
|
$path = $this->findFile($file);
|
||||||
|
|
||||||
$content = $this->loadFile($path);
|
$content = $this->loadFile($path);
|
||||||
|
|
||||||
$configuration = new BuilderConfiguration();
|
if (null === $configuration) {
|
||||||
|
$configuration = new BuilderConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
$configuration->addResource(new FileResource($path));
|
$configuration->addResource(new FileResource($path));
|
||||||
|
|
||||||
@ -66,6 +70,10 @@ class YamlFileLoader extends FileLoader
|
|||||||
// extensions
|
// extensions
|
||||||
$this->loadFromExtensions($configuration, $content);
|
$this->loadFromExtensions($configuration, $content);
|
||||||
|
|
||||||
|
if ($main) {
|
||||||
|
$configuration->mergeExtensionsConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
return $configuration;
|
return $configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,11 +84,11 @@ class YamlFileLoader extends FileLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($content['imports'] as $import) {
|
foreach ($content['imports'] as $import) {
|
||||||
$configuration->merge($this->parseImport($import, $file));
|
$this->parseImport($configuration, $import, $file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function parseImport($import, $file)
|
protected function parseImport(BuilderConfiguration $configuration, $import, $file)
|
||||||
{
|
{
|
||||||
$class = null;
|
$class = null;
|
||||||
if (isset($import['class']) && $import['class'] !== get_class($this)) {
|
if (isset($import['class']) && $import['class'] !== get_class($this)) {
|
||||||
@ -101,7 +109,7 @@ class YamlFileLoader extends FileLoader
|
|||||||
|
|
||||||
$importedFile = $this->getAbsolutePath($import['resource'], dirname($file));
|
$importedFile = $this->getAbsolutePath($import['resource'], dirname($file));
|
||||||
|
|
||||||
return $loader->load($importedFile);
|
return $loader->load($importedFile, false, $configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function parseDefinitions(BuilderConfiguration $configuration, $content, $file)
|
protected function parseDefinitions(BuilderConfiguration $configuration, $content, $file)
|
||||||
@ -232,12 +240,7 @@ class YamlFileLoader extends FileLoader
|
|||||||
$values = array();
|
$values = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$config = static::getExtension($namespace)->load($tag, $values);
|
$configuration->loadFromExtension($this->getExtension($namespace), $tag, $values);
|
||||||
|
|
||||||
$r = new \ReflectionObject($this->getExtension($namespace));
|
|
||||||
$config->addResource(new FileResource($r->getFileName()));
|
|
||||||
|
|
||||||
$configuration->merge($config);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
namespace Symfony\Tests\Components\DependencyInjection\Loader;
|
namespace Symfony\Tests\Components\DependencyInjection\Loader;
|
||||||
|
|
||||||
use Symfony\Components\DependencyInjection\Builder;
|
use Symfony\Components\DependencyInjection\Builder;
|
||||||
|
use Symfony\Components\DependencyInjection\BuilderConfiguration;
|
||||||
use Symfony\Components\DependencyInjection\Loader\FileLoader;
|
use Symfony\Components\DependencyInjection\Loader\FileLoader;
|
||||||
|
|
||||||
class XmlDumperTest extends \PHPUnit_Framework_TestCase
|
class XmlDumperTest extends \PHPUnit_Framework_TestCase
|
||||||
@ -44,7 +45,7 @@ class ProjectLoader extends FileLoader
|
|||||||
{
|
{
|
||||||
public $paths;
|
public $paths;
|
||||||
|
|
||||||
public function load($resource)
|
public function load($resource, $main = true, BuilderConfiguration $configuration = null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ namespace Symfony\Tests\Components\DependencyInjection\Loader;
|
|||||||
|
|
||||||
require_once __DIR__.'/../../../../../fixtures/Symfony/Components/DependencyInjection/includes/ProjectExtension.php';
|
require_once __DIR__.'/../../../../../fixtures/Symfony/Components/DependencyInjection/includes/ProjectExtension.php';
|
||||||
|
|
||||||
|
use Symfony\Components\DependencyInjection\BuilderConfiguration;
|
||||||
|
|
||||||
class LoaderExtensionTest extends \PHPUnit_Framework_TestCase
|
class LoaderExtensionTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
public function testLoad()
|
public function testLoad()
|
||||||
@ -19,14 +21,14 @@ class LoaderExtensionTest extends \PHPUnit_Framework_TestCase
|
|||||||
$extension = new \ProjectExtension();
|
$extension = new \ProjectExtension();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$extension->load('foo', array());
|
$extension->load('foo', array(), new BuilderConfiguration());
|
||||||
$this->fail('->load() throws an InvalidArgumentException if the tag does not exist');
|
$this->fail('->load() throws an InvalidArgumentException if the tag does not exist');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag does not exist');
|
$this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag does not exist');
|
||||||
$this->assertEquals('The tag "foo" is not defined in the "http://www.example.com/schema/project" extension.', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag does not exist');
|
$this->assertEquals('The tag "foo" is not defined in the "http://www.example.com/schema/project" extension.', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag does not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
$config = $extension->load('bar', array('foo' => 'bar'));
|
$config = $extension->load('bar', array('foo' => 'bar'), new BuilderConfiguration());
|
||||||
$this->assertEquals(array('project.parameter.bar' => 'bar'), $config->getParameters(), '->load() calls the method tied to the given tag');
|
$this->assertEquals(array('project.parameter.bar' => 'bar'), $config->getParameters(), '->load() calls the method tied to the given tag');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,11 @@ namespace Symfony\Tests\Components\DependencyInjection\Loader;
|
|||||||
require_once __DIR__.'/../../../../../fixtures/Symfony/Components/DependencyInjection/includes/ProjectExtension.php';
|
require_once __DIR__.'/../../../../../fixtures/Symfony/Components/DependencyInjection/includes/ProjectExtension.php';
|
||||||
|
|
||||||
use Symfony\Components\DependencyInjection\Loader\Loader;
|
use Symfony\Components\DependencyInjection\Loader\Loader;
|
||||||
|
use Symfony\Components\DependencyInjection\BuilderConfiguration;
|
||||||
|
|
||||||
class ProjectLoader1 extends Loader
|
class ProjectLoader1 extends Loader
|
||||||
{
|
{
|
||||||
public function load($resource)
|
public function load($resource, $main = true, BuilderConfiguration $configuration = null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,8 @@ use Symfony\Components\DependencyInjection\Loader\LoaderExtension;
|
|||||||
|
|
||||||
class ProjectExtension extends LoaderExtension
|
class ProjectExtension extends LoaderExtension
|
||||||
{
|
{
|
||||||
public function barLoad(array $config)
|
public function barLoad(array $config, BuilderConfiguration $configuration)
|
||||||
{
|
{
|
||||||
$configuration = new BuilderConfiguration();
|
|
||||||
|
|
||||||
$configuration->setDefinition('project.service.bar', new Definition('FooClass'));
|
$configuration->setDefinition('project.service.bar', new Definition('FooClass'));
|
||||||
$configuration->setParameter('project.parameter.bar', isset($config['foo']) ? $config['foo'] : 'foobar');
|
$configuration->setParameter('project.parameter.bar', isset($config['foo']) ? $config['foo'] : 'foobar');
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user