[DI] Track changes at the "Definition" level

This commit is contained in:
Ryan Weaver 2017-04-05 08:12:51 -04:00 committed by Nicolas Grekas
parent bc93526731
commit cbaee55223
13 changed files with 150 additions and 166 deletions

View File

@ -23,15 +23,12 @@ class ChildDefinition extends Definition
{
private $parent;
private $inheritTags = false;
private $changes = array();
/**
* @param string $parent The id of Definition instance to decorate
*/
public function __construct($parent)
{
parent::__construct();
$this->parent = $parent;
}
@ -46,13 +43,17 @@ class ChildDefinition extends Definition
}
/**
* Returns all changes tracked for the Definition object.
* Sets the Definition being decorated.
*
* @return array An array of changes for this Definition
* @param string $parent
*
* @return $this
*/
public function getChanges()
public function setParent($parent)
{
return $this->changes;
$this->parent = $parent;
return $this;
}
/**
@ -79,116 +80,6 @@ class ChildDefinition extends Definition
return $this->inheritTags;
}
/**
* {@inheritdoc}
*/
public function setClass($class)
{
$this->changes['class'] = true;
return parent::setClass($class);
}
/**
* {@inheritdoc}
*/
public function setFactory($callable)
{
$this->changes['factory'] = true;
return parent::setFactory($callable);
}
/**
* {@inheritdoc}
*/
public function setConfigurator($callable)
{
$this->changes['configurator'] = true;
return parent::setConfigurator($callable);
}
/**
* {@inheritdoc}
*/
public function setFile($file)
{
$this->changes['file'] = true;
return parent::setFile($file);
}
/**
* {@inheritdoc}
*/
public function setShared($boolean)
{
$this->changes['shared'] = true;
return parent::setShared($boolean);
}
/**
* {@inheritdoc}
*/
public function setPublic($boolean)
{
$this->changes['public'] = true;
return parent::setPublic($boolean);
}
/**
* {@inheritdoc}
*/
public function setLazy($boolean)
{
$this->changes['lazy'] = true;
return parent::setLazy($boolean);
}
/**
* {@inheritdoc}
*/
public function setAbstract($boolean)
{
$this->changes['abstract'] = true;
return parent::setAbstract($boolean);
}
/**
* {@inheritdoc}
*/
public function setDecoratedService($id, $renamedId = null, $priority = 0)
{
$this->changes['decorated_service'] = true;
return parent::setDecoratedService($id, $renamedId, $priority);
}
/**
* {@inheritdoc}
*/
public function setDeprecated($boolean = true, $template = null)
{
$this->changes['deprecated'] = true;
return parent::setDeprecated($boolean, $template);
}
/**
* {@inheritdoc}
*/
public function setAutowired($autowired)
{
$this->changes['autowired'] = true;
return parent::setAutowired($autowired);
}
/**
* Gets an argument to pass to the service constructor/factory method.
*

View File

@ -65,11 +65,12 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
$value->setProperties($this->processValue($value->getProperties()));
$value->setMethodCalls($this->processValue($value->getMethodCalls()));
if ($v = $value->getFactory()) {
$value->setFactory($this->processValue($v));
$changes = $value->getChanges();
if (isset($changes['factory'])) {
$value->setFactory($this->processValue($value->getFactory()));
}
if ($v = $value->getConfigurator()) {
$value->setConfigurator($this->processValue($v));
if (isset($changes['configurator'])) {
$value->setConfigurator($this->processValue($value->getConfigurator()));
}
}

View File

@ -58,8 +58,13 @@ class ResolveParameterPlaceHoldersPass extends AbstractRecursivePass
return $this->bag->resolveValue($value);
}
if ($value instanceof Definition) {
$value->setClass($this->bag->resolveValue($value->getClass()));
$value->setFile($this->bag->resolveValue($value->getFile()));
$changes = $value->getChanges();
if (isset($changes['class'])) {
$value->setClass($this->bag->resolveValue($value->getClass()));
}
if (isset($changes['file'])) {
$value->setFile($this->bag->resolveValue($value->getFile()));
}
$value->setProperties($this->bag->resolveValue($value->getProperties()));
$value->setMethodCalls($this->bag->resolveValue($value->getMethodCalls()));
}

View File

@ -43,7 +43,9 @@ class ResolveReferencesToAliasesPass implements CompilerPassInterface
$definition->setArguments($this->processArguments($definition->getArguments()));
$definition->setMethodCalls($this->processArguments($definition->getMethodCalls()));
$definition->setProperties($this->processArguments($definition->getProperties()));
$definition->setFactory($this->processFactory($definition->getFactory()));
if (isset($definition->getChanges()['factory'])) {
$definition->setFactory($this->processFactory($definition->getFactory()));
}
}
foreach ($container->getAliases() as $id => $alias) {

View File

@ -39,8 +39,9 @@ class Definition
private $decoratedService;
private $autowired = false;
private $autowiringTypes = array();
private $changes = array();
protected $arguments;
protected $arguments = array();
/**
* @param string|null $class The service class
@ -48,10 +49,34 @@ class Definition
*/
public function __construct($class = null, array $arguments = array())
{
$this->class = $class;
if (null !== $class) {
$this->setClass($class);
}
$this->arguments = $arguments;
}
/**
* Returns all changes tracked for the Definition object.
*
* @return array An array of changes for this Definition
*/
public function getChanges()
{
return $this->changes;
}
/**
* Sets the tracked changes for the Definition object.
*
* @return $this
*/
public function setChanges(array $changes)
{
$this->changes = $changes;
return $this;
}
/**
* Sets a factory.
*
@ -61,6 +86,8 @@ class Definition
*/
public function setFactory($factory)
{
$this->changes['factory'] = true;
if (is_string($factory) && strpos($factory, '::') !== false) {
$factory = explode('::', $factory, 2);
}
@ -97,6 +124,8 @@ class Definition
throw new InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id));
}
$this->changes['decorated_service'] = true;
if (null === $id) {
$this->decoratedService = null;
} else {
@ -125,6 +154,8 @@ class Definition
*/
public function setClass($class)
{
$this->changes['class'] = true;
$this->class = $class;
return $this;
@ -448,6 +479,8 @@ class Definition
*/
public function setFile($file)
{
$this->changes['file'] = true;
$this->file = $file;
return $this;
@ -472,6 +505,8 @@ class Definition
*/
public function setShared($shared)
{
$this->changes['shared'] = true;
$this->shared = (bool) $shared;
return $this;
@ -496,6 +531,8 @@ class Definition
*/
public function setPublic($boolean)
{
$this->changes['public'] = true;
$this->public = (bool) $boolean;
return $this;
@ -520,6 +557,8 @@ class Definition
*/
public function setLazy($lazy)
{
$this->changes['lazy'] = true;
$this->lazy = (bool) $lazy;
return $this;
@ -612,6 +651,8 @@ class Definition
$this->deprecationTemplate = $template;
}
$this->changes['deprecated'] = true;
$this->deprecated = (bool) $status;
return $this;
@ -649,6 +690,8 @@ class Definition
*/
public function setConfigurator($configurator)
{
$this->changes['configurator'] = true;
if (is_string($configurator) && strpos($configurator, '::') !== false) {
$configurator = explode('::', $configurator, 2);
}
@ -709,6 +752,8 @@ class Definition
*/
public function setAutowired($autowired)
{
$this->changes['autowired'] = true;
$this->autowired = (bool) $autowired;
return $this;

View File

@ -222,12 +222,19 @@ class XmlFileLoader extends FileLoader
$defaults = array();
} else {
$definition = new Definition();
if (isset($defaults['public'])) {
$definition->setPublic($defaults['public']);
}
if (isset($defaults['autowire'])) {
$definition->setAutowired($defaults['autowire']);
}
$definition->setChanges(array());
}
if ($publicAttr = $service->getAttribute('public')) {
$definition->setPublic(XmlUtils::phpize($publicAttr));
} elseif (isset($defaults['public'])) {
$definition->setPublic($defaults['public']);
}
foreach (array('class', 'shared', 'synthetic', 'lazy', 'abstract') as $key) {
@ -239,8 +246,6 @@ class XmlFileLoader extends FileLoader
if ($value = $service->getAttribute('autowire')) {
$definition->setAutowired(XmlUtils::phpize($value));
} elseif (isset($defaults['autowire'])) {
$definition->setAutowired($defaults['autowire']);
}
if ($files = $this->getChildren($service, 'file')) {

View File

@ -81,10 +81,6 @@ class YamlFileLoader extends FileLoader
'shared' => 'shared',
'lazy' => 'lazy',
'public' => 'public',
'abstract' => 'abstract',
'deprecated' => 'deprecated',
'factory' => 'factory',
'arguments' => 'arguments',
'properties' => 'properties',
'configurator' => 'configurator',
'calls' => 'calls',
@ -366,6 +362,15 @@ class YamlFileLoader extends FileLoader
$defaults = array();
} else {
$definition = new Definition();
if (isset($defaults['public'])) {
$definition->setPublic($defaults['public']);
}
if (isset($defaults['autowire'])) {
$definition->setAutowired($defaults['autowire']);
}
$definition->setChanges(array());
}
if (isset($service['class'])) {
@ -384,9 +389,8 @@ class YamlFileLoader extends FileLoader
$definition->setLazy($service['lazy']);
}
$public = isset($service['public']) ? $service['public'] : (isset($defaults['public']) ? $defaults['public'] : null);
if (null !== $public) {
$definition->setPublic($public);
if (isset($service['public'])) {
$definition->setPublic($service['public']);
}
if (isset($service['abstract'])) {
@ -484,9 +488,8 @@ class YamlFileLoader extends FileLoader
$definition->setDecoratedService($service['decorates'], $renameId, $priority);
}
$autowire = isset($service['autowire']) ? $service['autowire'] : (isset($defaults['autowire']) ? $defaults['autowire'] : null);
if (null !== $autowire) {
$definition->setAutowired($autowire);
if (isset($service['autowire'])) {
$definition->setAutowired($service['autowire']);
}
if (isset($service['autowiring_types'])) {

View File

@ -136,10 +136,7 @@
<xsd:complexType name="instanceof">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
<xsd:element name="factory" type="callable" minOccurs="0" maxOccurs="1" />
<xsd:element name="deprecated" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
@ -148,7 +145,6 @@
<xsd:attribute name="shared" type="boolean" />
<xsd:attribute name="public" type="boolean" />
<xsd:attribute name="lazy" type="boolean" />
<xsd:attribute name="abstract" type="boolean" />
<xsd:attribute name="autowire" type="boolean" />
</xsd:complexType>

View File

@ -59,21 +59,6 @@ class ResolveDefinitionInheritancePassTest extends TestCase
), $container->getDefinition('parent')->getMethodCalls());
}
public function testProcessDoesReplaceAbstract()
{
$container = new ContainerBuilder();
$def = $container->register('parent', 'stdClass');
$def->setInstanceofConditionals(array(
'stdClass' => (new ChildDefinition(''))->setAbstract(true),
));
$this->process($container);
$this->assertTrue($def->isAbstract());
}
public function testProcessDoesReplaceShared()
{
$container = new ContainerBuilder();

View File

@ -20,6 +20,7 @@ class DefinitionTest extends TestCase
{
$def = new Definition('stdClass');
$this->assertEquals('stdClass', $def->getClass(), '__construct() takes the class name as its first argument');
$this->assertSame(array('class' => true), $def->getChanges());
$def = new Definition('stdClass', array('foo'));
$this->assertEquals(array('foo'), $def->getArguments(), '__construct() takes an optional array of arguments as its second argument');
@ -27,13 +28,14 @@ class DefinitionTest extends TestCase
public function testSetGetFactory()
{
$def = new Definition('stdClass');
$def = new Definition();
$this->assertSame($def, $def->setFactory('foo'), '->setFactory() implements a fluent interface');
$this->assertEquals('foo', $def->getFactory(), '->getFactory() returns the factory');
$def->setFactory('Foo::bar');
$this->assertEquals(array('Foo', 'bar'), $def->getFactory(), '->setFactory() converts string static method call to the array');
$this->assertSame(array('factory' => true), $def->getChanges());
}
public function testSetGetClass()
@ -315,6 +317,51 @@ class DefinitionTest extends TestCase
$this->assertFalse($def->isAutowired());
}
public function testChangesNoChanges()
{
$def = new Definition();
$this->assertSame(array(), $def->getChanges());
}
public function testGetChangesWithChanges()
{
$def = new Definition('stdClass', array('fooarg'));
$def->setAbstract(true);
$def->setAutowired(true);
$def->setConfigurator('configuration_func');
$def->setDecoratedService(null);
$def->setDeprecated(true);
$def->setFactory('factory_func');
$def->setFile('foo.php');
$def->setLazy(true);
$def->setPublic(true);
$def->setShared(true);
$def->setSynthetic(true);
// changes aren't tracked for these, class or arguments
$def->setInstanceofConditionals(array());
$def->addTag('foo_tag');
$def->addMethodCall('methodCall');
$def->setProperty('fooprop', true);
$this->assertSame(array(
'class' => true,
'autowired' => true,
'configurator' => true,
'decorated_service' => true,
'deprecated' => true,
'factory' => true,
'file' => true,
'lazy' => true,
'public' => true,
'shared' => true,
), $def->getChanges());
$def->setChanges(array());
$this->assertSame(array(), $def->getChanges());
}
/**
* @group legacy
*/

View File

@ -5,7 +5,8 @@ services:
autowire: true
DummyInterface:
arguments: [ !service { class: Anonymous } ]
properties:
foo: !service { class: Anonymous }
# Ensure next conditionals are not considered as services
Bar:

View File

@ -648,6 +648,8 @@ class XmlFileLoaderTest extends TestCase
$this->assertFalse($container->getDefinition('with_defaults')->isPublic());
$this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags());
$this->assertTrue($container->getDefinition('with_defaults')->isAutowired());
$this->assertArrayNotHasKey('public', $container->getDefinition('with_defaults')->getChanges());
$this->assertArrayNotHasKey('autowire', $container->getDefinition('with_defaults')->getChanges());
$this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges());
$this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges());

View File

@ -24,7 +24,6 @@ use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
use Symfony\Component\ExpressionLanguage\Expression;
@ -398,6 +397,8 @@ class YamlFileLoaderTest extends TestCase
$this->assertFalse($container->getDefinition('with_defaults')->isPublic());
$this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags());
$this->assertTrue($container->getDefinition('with_defaults')->isAutowired());
$this->assertArrayNotHasKey('public', $container->getDefinition('with_defaults')->getChanges());
$this->assertArrayNotHasKey('autowire', $container->getDefinition('with_defaults')->getChanges());
$this->assertFalse($container->getAlias('with_defaults_aliased')->isPublic());
$this->assertFalse($container->getAlias('with_defaults_aliased_short')->isPublic());
@ -532,12 +533,12 @@ class YamlFileLoaderTest extends TestCase
$this->assertCount(3, $instanceof);
$this->assertArrayHasKey('DummyInterface', $instanceof);
$args = $instanceof['DummyInterface']->getArguments();
$args = $instanceof['DummyInterface']->getProperties();
$this->assertCount(1, $args);
$this->assertInstanceOf(Reference::class, $args[0]);
$this->assertTrue($container->has((string) $args[0]));
$this->assertInstanceOf(Reference::class, $args['foo']);
$this->assertTrue($container->has((string) $args['foo']));
$anonymous = $container->getDefinition((string) $args[0]);
$anonymous = $container->getDefinition((string) $args['foo']);
$this->assertEquals('Anonymous', $anonymous->getClass());
$this->assertFalse($anonymous->isPublic());
$this->assertEmpty($anonymous->getInstanceofConditionals());