[DependencyInjection] adds emulation of "exception-on-invalid-reference" behavior
This pass requires that all of references are valid at the end of the compilation process.
This commit is contained in:
parent
db90e0ab8d
commit
6d7a9d752d
@ -20,6 +20,11 @@ PR11 to PR12
|
|||||||
|
|
||||||
<bundle>MyBundle</bundle>
|
<bundle>MyBundle</bundle>
|
||||||
|
|
||||||
|
* The Dependency Injection Container now strongly validates the references of
|
||||||
|
all your services at the end of its compilation process. If you have invalid
|
||||||
|
references this will result in a compile-time exception instead of a run-time
|
||||||
|
exception (the previous behavior).
|
||||||
|
|
||||||
PR10 to PR11
|
PR10 to PR11
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -123,10 +123,10 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns information about the listener
|
* Returns information about the listener
|
||||||
*
|
*
|
||||||
* @param object $listener The listener
|
* @param object $listener The listener
|
||||||
* @param string $eventName The event name
|
* @param string $eventName The event name
|
||||||
*
|
*
|
||||||
* @return array Informations about the listener
|
* @return array Informations about the listener
|
||||||
*/
|
*/
|
||||||
private function getListenerInfo($listener, $eventName)
|
private function getListenerInfo($listener, $eventName)
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Exception\NonExistentServiceException;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that all references are pointing to a valid service.
|
||||||
|
*
|
||||||
|
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||||
|
*/
|
||||||
|
class CheckExceptionOnInvalidReferenceBehaviorPass implements CompilerPassInterface
|
||||||
|
{
|
||||||
|
private $container;
|
||||||
|
private $sourceId;
|
||||||
|
|
||||||
|
public function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$this->container = $container;
|
||||||
|
|
||||||
|
foreach ($container->getDefinitions() as $id => $definition) {
|
||||||
|
$this->sourceId = $id;
|
||||||
|
$this->processReferences($definition->getArguments());
|
||||||
|
$this->processReferences($definition->getMethodCalls());
|
||||||
|
$this->processReferences($definition->getProperties());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processReferences(array $arguments)
|
||||||
|
{
|
||||||
|
foreach ($arguments as $argument) {
|
||||||
|
if (is_array($argument)) {
|
||||||
|
$this->processReferences($argument);
|
||||||
|
} else if ($argument instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $argument->getInvalidBehavior()) {
|
||||||
|
$destId = (string) $argument;
|
||||||
|
|
||||||
|
if (!$this->container->has($destId)) {
|
||||||
|
throw new NonExistentServiceException($destId, $this->sourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -66,6 +66,7 @@ class PassConfig
|
|||||||
new AnalyzeServiceReferencesPass(),
|
new AnalyzeServiceReferencesPass(),
|
||||||
new RemoveUnusedDefinitionsPass(),
|
new RemoveUnusedDefinitionsPass(),
|
||||||
)),
|
)),
|
||||||
|
new CheckExceptionOnInvalidReferenceBehaviorPass(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\DependencyInjection;
|
namespace Symfony\Component\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Exception\NonExistentServiceException;
|
||||||
use Symfony\Component\DependencyInjection\Exception\CircularReferenceException;
|
use Symfony\Component\DependencyInjection\Exception\CircularReferenceException;
|
||||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||||
@ -237,7 +238,7 @@ class Container implements ContainerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
|
if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
|
||||||
throw new \InvalidArgumentException(sprintf('The service "%s" does not exist.', $id));
|
throw new NonExistentServiceException($id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Component\DependencyInjection\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when a non-existent service is requested.
|
||||||
|
*
|
||||||
|
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||||
|
*/
|
||||||
|
class NonExistentServiceException extends InvalidArgumentException
|
||||||
|
{
|
||||||
|
private $id;
|
||||||
|
private $sourceId;
|
||||||
|
|
||||||
|
public function __construct($id, $sourceId = null)
|
||||||
|
{
|
||||||
|
if (null === $sourceId) {
|
||||||
|
$msg = sprintf('You have requested a non-existent service "%s".', $id);
|
||||||
|
} else {
|
||||||
|
$msg = sprintf('The service "%s" has a dependency on a non-existent service "%s".', $sourceId, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($msg);
|
||||||
|
|
||||||
|
$this->id = $id;
|
||||||
|
$this->sourceId = $sourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSourceId()
|
||||||
|
{
|
||||||
|
return $this->sourceId;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Tests\Component\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
|
||||||
|
class CheckExceptionOnInvalidReferenceBehaviorPassTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testProcess()
|
||||||
|
{
|
||||||
|
$container = new ContainerBuilder();
|
||||||
|
|
||||||
|
$container
|
||||||
|
->register('a', '\stdClass')
|
||||||
|
->addArgument(new Reference('b'))
|
||||||
|
;
|
||||||
|
$container->register('b', '\stdClass');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Symfony\Component\DependencyInjection\Exception\NonExistentServiceException
|
||||||
|
*/
|
||||||
|
public function testProcessThrowsExceptionOnInvalidReference()
|
||||||
|
{
|
||||||
|
$container = new ContainerBuilder();
|
||||||
|
|
||||||
|
$container
|
||||||
|
->register('a', '\stdClass')
|
||||||
|
->addArgument(new Reference('b'))
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->process($container);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$pass = new CheckExceptionOnInvalidReferenceBehaviorPass();
|
||||||
|
$pass->process($container);
|
||||||
|
}
|
||||||
|
}
|
@ -161,8 +161,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$sc->get('');
|
$sc->get('');
|
||||||
$this->fail('->get() throws a \InvalidArgumentException exception if the service is empty');
|
$this->fail('->get() throws a \InvalidArgumentException exception if the service is empty');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws a \InvalidArgumentException exception if the service is empty');
|
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\NonExistentServiceException', $e, '->get() throws a NonExistentServiceException exception if the service is empty');
|
||||||
$this->assertEquals('The service "" does not exist.', $e->getMessage(), '->get() throws a \InvalidArgumentException exception if the service is empty');
|
|
||||||
}
|
}
|
||||||
$this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE));
|
$this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user