[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>
|
||||
|
||||
* 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
|
||||
------------
|
||||
|
||||
|
@ -123,10 +123,10 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
|
||||
/**
|
||||
* Returns information about the listener
|
||||
*
|
||||
*
|
||||
* @param object $listener The listener
|
||||
* @param string $eventName The event name
|
||||
*
|
||||
*
|
||||
* @return array Informations about the listener
|
||||
*/
|
||||
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 RemoveUnusedDefinitionsPass(),
|
||||
)),
|
||||
new CheckExceptionOnInvalidReferenceBehaviorPass(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Exception\NonExistentServiceException;
|
||||
use Symfony\Component\DependencyInjection\Exception\CircularReferenceException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
@ -237,7 +238,7 @@ class Container implements ContainerInterface
|
||||
}
|
||||
|
||||
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('');
|
||||
$this->fail('->get() throws a \InvalidArgumentException exception if the service is empty');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws a \InvalidArgumentException 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->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\NonExistentServiceException', $e, '->get() throws a NonExistentServiceException exception if the service is empty');
|
||||
}
|
||||
$this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE));
|
||||
}
|
||||
|
Reference in New Issue
Block a user