feature #18728 deprecate get() for uncompiled container builders (xabbuh)

This PR was squashed before being merged into the 3.2-dev branch (closes #18728).

Discussion
----------

deprecate get() for uncompiled container builders

| Q             | A
| ------------- | ---
| Branch?       | 3.2
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | https://github.com/symfony/symfony/issues/18673#issuecomment-216183042
| License       | MIT
| Doc PR        |

Commits
-------

f2e35cb deprecate get() for uncompiled container builders
This commit is contained in:
Fabien Potencier 2016-06-08 13:34:19 +02:00
commit 27f4680294
7 changed files with 137 additions and 39 deletions

8
UPGRADE-3.2.md Normal file
View File

@ -0,0 +1,8 @@
UPGRADE FROM 3.1 to 3.2
=======================
DependencyInjection
-------------------
* Calling `get()` on a `ContainerBuilder` instance before compiling the
container is deprecated and will throw an exception in Symfony 4.0.

View File

@ -4,6 +4,9 @@ UPGRADE FROM 3.x to 4.0
DependencyInjection
-------------------
* Calling `get()` on a `ContainerBuilder` instance before compiling the
container is not supported anymore and will throw an exception.
* Using unsupported configuration keys in YAML configuration files raises an
exception.

View File

@ -96,11 +96,11 @@ class WebProfilerExtensionTest extends TestCase
$this->assertSame($listenerInjected, $this->container->has('web_profiler.debug_toolbar'));
$this->assertSaneContainer($this->getDumpedContainer());
if ($listenerInjected) {
$this->assertSame($listenerEnabled, $this->container->get('web_profiler.debug_toolbar')->isEnabled());
}
$this->assertSaneContainer($this->getDumpedContainer());
}
public function getDebugModes()

View File

@ -94,6 +94,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private $usedTags = array();
private $compiled = false;
/**
* Sets the track resources flag.
*
@ -409,6 +411,10 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
{
if (!$this->compiled) {
@trigger_error(sprintf('Calling %s() before compiling the container is deprecated since version 3.2 and will throw an exception in 4.0.', __METHOD__), E_USER_DEPRECATED);
}
$id = strtolower($id);
if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
@ -543,6 +549,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
}
$compiler->compile($this);
$this->compiled = true;
if ($this->trackResources) {
foreach ($this->definitions as $definition) {

View File

@ -180,14 +180,17 @@ class GraphvizDumper extends Dumper
}
foreach ($container->getServiceIds() as $id) {
$service = $container->get($id);
if (array_key_exists($id, $container->getAliases())) {
continue;
}
if (!$container->hasDefinition($id)) {
$class = ('service_container' === $id) ? get_class($this->container) : get_class($service);
if ('service_container' === $id) {
$class = get_class($this->container);
} else {
$class = get_class($container->get($id));
}
$nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => $this->options['node.instance']);
}
}

View File

@ -57,7 +57,6 @@ class AutoAliasServicePassTest extends \PHPUnit_Framework_TestCase
$pass->process($container);
$this->assertEquals('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault', $container->getDefinition('example')->getClass());
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault', $container->get('example'));
}
public function testProcessWithExistingAlias()
@ -75,7 +74,7 @@ class AutoAliasServicePassTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($container->hasAlias('example'));
$this->assertEquals('mysql.example', $container->getAlias('example'));
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql', $container->get('example'));
$this->assertSame('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql', $container->getDefinition('mysql.example')->getClass());
}
public function testProcessWithManualAlias()
@ -86,7 +85,7 @@ class AutoAliasServicePassTest extends \PHPUnit_Framework_TestCase
->addTag('auto_alias', array('format' => '%existing%.example'));
$container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql');
$container->register('mariadb.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariadb');
$container->register('mariadb.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb');
$container->setAlias('example', 'mariadb.example');
$container->setParameter('existing', 'mysql');
@ -95,7 +94,7 @@ class AutoAliasServicePassTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($container->hasAlias('example'));
$this->assertEquals('mariadb.example', $container->getAlias('example'));
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb', $container->get('example'));
$this->assertSame('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb', $container->getDefinition('mariadb.example')->getClass());
}
}

View File

@ -20,8 +20,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\Reference;
@ -76,6 +74,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder = new ContainerBuilder();
$builder->setDefinition('deprecated_foo', $definition);
$builder->compile();
$builder->get('deprecated_foo');
restore_error_handler();
@ -102,41 +103,80 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($builder->has('bar'), '->has() returns true if a service exists');
}
public function testGet()
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
* @expectedExceptionMessage You have requested a non-existent service "foo".
*/
public function testGetThrowsExceptionIfServiceDoesNotExist()
{
$builder = new ContainerBuilder();
try {
$builder->get('foo');
$this->fail('->get() throws a ServiceNotFoundException if the service does not exist');
} catch (ServiceNotFoundException $e) {
$this->assertEquals('You have requested a non-existent service "foo".', $e->getMessage(), '->get() throws a ServiceNotFoundException if the service does not exist');
}
$builder->compile();
$builder->get('foo');
}
public function testGetReturnsNullIfServiceDoesNotExistAndInvalidReferenceIsUsed()
{
$builder = new ContainerBuilder();
$builder->compile();
$this->assertNull($builder->get('foo', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service does not exist and NULL_ON_INVALID_REFERENCE is passed as a second argument');
}
$builder->register('foo', 'stdClass');
$this->assertInternalType('object', $builder->get('foo'), '->get() returns the service definition associated with the id');
$builder->set('bar', $bar = new \stdClass());
$this->assertEquals($bar, $builder->get('bar'), '->get() returns the service associated with the id');
$builder->register('bar', 'stdClass');
$this->assertEquals($bar, $builder->get('bar'), '->get() returns the service associated with the id even if a definition has been defined');
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testGetThrowsCircularReferenceExceptionIfServiceHasReferenceToItself()
{
$builder = new ContainerBuilder();
$builder->register('baz', 'stdClass')->setArguments(array(new Reference('baz')));
try {
@$builder->get('baz');
$this->fail('->get() throws a ServiceCircularReferenceException if the service has a circular reference to itself');
} catch (\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException $e) {
$this->assertEquals('Circular reference detected for service "baz", path: "baz".', $e->getMessage(), '->get() throws a LogicException if the service has a circular reference to itself');
}
$builder->compile();
$builder->get('baz');
}
public function testGetReturnsSameInstanceWhenServiceIsShared()
{
$builder = new ContainerBuilder();
$builder->register('bar', 'stdClass');
$builder->compile();
$this->assertTrue($builder->get('bar') === $builder->get('bar'), '->get() always returns the same instance if the service is shared');
}
public function testGetCreatesServiceBasedOnDefinition()
{
$builder = new ContainerBuilder();
$builder->register('foo', 'stdClass');
$builder->compile();
$this->assertInternalType('object', $builder->get('foo'), '->get() returns the service definition associated with the id');
}
public function testGetReturnsRegisteredService()
{
$builder = new ContainerBuilder();
$builder->set('bar', $bar = new \stdClass());
$builder->compile();
$this->assertSame($bar, $builder->get('bar'), '->get() returns the service associated with the id');
}
public function testRegisterDoesNotOverrideExistingService()
{
$builder = new ContainerBuilder();
$builder->set('bar', $bar = new \stdClass());
$builder->register('bar', 'stdClass');
$builder->compile();
$this->assertSame($bar, $builder->get('bar'), '->get() returns the service associated with the id even if a definition has been defined');
}
public function testNonSharedServicesReturnsDifferentInstances()
{
$builder = new ContainerBuilder();
$builder->register('bar', 'stdClass')->setShared(false);
$builder->compile();
$this->assertNotSame($builder->get('bar'), $builder->get('bar'));
}
@ -149,6 +189,8 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder = new ContainerBuilder();
$builder->register('foo', 'stdClass')->setSynthetic(true);
$builder->compile();
// we expect a RuntimeException here as foo is synthetic
try {
$builder->get('foo');
@ -177,6 +219,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($builder->hasAlias('foobar'), '->hasAlias() returns false if the alias does not exist');
$this->assertEquals('foo', (string) $builder->getAlias('bar'), '->getAlias() returns the aliased service');
$this->assertTrue($builder->has('bar'), '->setAlias() defines a new service');
$builder->compile();
$this->assertTrue($builder->get('bar') === $builder->get('foo'), '->setAlias() creates a service that is an alias to another one');
try {
@ -244,6 +289,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->set('aliased', new \stdClass());
$builder->set('alias', $foo = new \stdClass());
$builder->compile();
$this->assertSame($foo, $builder->get('alias'), '->set() replaces an existing alias');
}
@ -261,9 +309,12 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
{
$builder = new ContainerBuilder();
$builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php');
$this->assertInstanceOf('\Bar\FooClass', $builder->get('foo1'), '->createService() requires the file defined by the service definition');
$builder->register('foo2', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/%file%.php');
$builder->setParameter('file', 'foo');
$builder->compile();
$this->assertInstanceOf('\Bar\FooClass', $builder->get('foo1'), '->createService() requires the file defined by the service definition');
$this->assertInstanceOf('\Bar\FooClass', $builder->get('foo2'), '->createService() replaces parameters in the file provided by the service definition');
}
@ -274,6 +325,8 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php');
$builder->getDefinition('foo1')->setLazy(true);
$builder->compile();
$foo1 = $builder->get('foo1');
$this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls');
@ -285,6 +338,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder = new ContainerBuilder();
$builder->register('foo1', '%class%');
$builder->setParameter('class', 'stdClass');
$builder->compile();
$this->assertInstanceOf('\stdClass', $builder->get('foo1'), '->createService() replaces parameters in the class provided by the service definition');
}
@ -294,6 +350,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->register('bar', 'stdClass');
$builder->register('foo1', 'Bar\FooClass')->addArgument(array('foo' => '%value%', '%value%' => 'foo', new Reference('bar'), '%%unescape_it%%'));
$builder->setParameter('value', 'bar');
$builder->compile();
$this->assertEquals(array('foo' => 'bar', 'bar' => 'foo', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->arguments, '->createService() replaces parameters and service references in the arguments provided by the service definition');
}
@ -305,6 +364,8 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->register('bar', 'Bar\FooClass')->setFactory(array(new Definition('Bar\FooClass'), 'getInstance'));
$builder->register('baz', 'Bar\FooClass')->setFactory(array(new Reference('bar'), 'getInstance'));
$builder->compile();
$this->assertTrue($builder->get('foo')->called, '->createService() calls the factory method to create the service instance');
$this->assertTrue($builder->get('qux')->called, '->createService() calls the factory method to create the service instance');
$this->assertTrue($builder->get('bar')->called, '->createService() uses anonymous service as factory');
@ -317,6 +378,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->register('bar', 'stdClass');
$builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%value%', new Reference('bar'))));
$builder->setParameter('value', 'bar');
$builder->compile();
$this->assertEquals(array('bar', $builder->get('bar')), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments');
}
@ -326,6 +390,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->register('bar', 'stdClass');
$builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%%unescape_it%%')));
$builder->setParameter('value', 'bar');
$builder->compile();
$this->assertEquals(array('%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments');
}
@ -335,6 +402,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->register('bar', 'stdClass');
$builder->register('foo1', 'Bar\FooClass')->setProperty('bar', array('%value%', new Reference('bar'), '%%unescape_it%%'));
$builder->setParameter('value', 'bar');
$builder->compile();
$this->assertEquals(array('bar', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the properties');
}
@ -342,20 +412,20 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
{
$builder = new ContainerBuilder();
$builder->register('foo1', 'Bar\FooClass')->setConfigurator('sc_configure');
$this->assertTrue($builder->get('foo1')->configured, '->createService() calls the configurator');
$builder->register('foo2', 'Bar\FooClass')->setConfigurator(array('%class%', 'configureStatic'));
$builder->setParameter('class', 'BazClass');
$this->assertTrue($builder->get('foo2')->configured, '->createService() calls the configurator');
$builder->register('baz', 'BazClass');
$builder->register('foo3', 'Bar\FooClass')->setConfigurator(array(new Reference('baz'), 'configure'));
$this->assertTrue($builder->get('foo3')->configured, '->createService() calls the configurator');
$builder->register('foo4', 'Bar\FooClass')->setConfigurator(array($builder->getDefinition('baz'), 'configure'));
$builder->register('foo5', 'Bar\FooClass')->setConfigurator('foo');
$builder->compile();
$this->assertTrue($builder->get('foo1')->configured, '->createService() calls the configurator');
$this->assertTrue($builder->get('foo2')->configured, '->createService() calls the configurator');
$this->assertTrue($builder->get('foo3')->configured, '->createService() calls the configurator');
$this->assertTrue($builder->get('foo4')->configured, '->createService() calls the configurator');
$builder->register('foo5', 'Bar\FooClass')->setConfigurator('foo');
try {
$builder->get('foo5');
$this->fail('->createService() throws an InvalidArgumentException if the configure callable is not a valid callable');
@ -371,6 +441,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
{
$builder = new ContainerBuilder();
$builder->register('foo', 'Bar\FooClass')->setSynthetic(true);
$builder->compile();
$builder->get('foo');
}
@ -380,6 +453,9 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$builder->setParameter('bar', 'bar');
$builder->register('bar', 'BarClass');
$builder->register('foo', 'Bar\FooClass')->addArgument(array('foo' => new Expression('service("bar").foo ~ parameter("bar")')));
$builder->compile();
$this->assertEquals('foobar', $builder->get('foo')->arguments['foo']);
}
@ -387,6 +463,8 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
{
$builder = new ContainerBuilder();
$builder->register('foo', 'Bar\FooClass');
$builder->compile();
$this->assertEquals($builder->get('foo'), $builder->resolveServices(new Reference('foo')), '->resolveServices() resolves service references to service instances');
$this->assertEquals(array('foo' => array('foo', $builder->get('foo'))), $builder->resolveServices(array('foo' => array('foo', new Reference('foo')))), '->resolveServices() resolves service references to service instances in nested arrays');
$this->assertEquals($builder->get('foo'), $builder->resolveServices(new Expression('service("foo")')), '->resolveServices() resolves expressions');