2011-01-26 23:14:31 +00:00
|
|
|
<?php
|
|
|
|
|
2011-05-31 09:57:06 +01:00
|
|
|
/*
|
2012-03-31 22:00:32 +01:00
|
|
|
* This file is part of the Symfony package.
|
2011-05-31 09:57:06 +01:00
|
|
|
*
|
|
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
|
|
|
*
|
2012-03-31 22:00:32 +01:00
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
2011-05-31 09:57:06 +01:00
|
|
|
*/
|
|
|
|
|
2011-01-26 23:14:31 +00:00
|
|
|
namespace Symfony\Component\DependencyInjection\Compiler;
|
|
|
|
|
|
|
|
use Symfony\Component\DependencyInjection\Definition;
|
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
|
|
|
use Symfony\Component\DependencyInjection\Reference;
|
|
|
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
2011-12-04 23:51:22 +00:00
|
|
|
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
|
|
|
use Symfony\Component\DependencyInjection\Exception\ScopeCrossingInjectionException;
|
|
|
|
use Symfony\Component\DependencyInjection\Exception\ScopeWideningInjectionException;
|
2011-01-26 23:14:31 +00:00
|
|
|
|
|
|
|
/**
|
2014-12-21 17:00:50 +00:00
|
|
|
* Checks the validity of references.
|
2011-01-26 23:14:31 +00:00
|
|
|
*
|
|
|
|
* The following checks are performed by this pass:
|
|
|
|
* - target definitions are not abstract
|
|
|
|
* - target definitions are of equal or wider scope
|
|
|
|
* - target definitions are in the same scope hierarchy
|
|
|
|
*
|
|
|
|
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
|
|
|
*/
|
|
|
|
class CheckReferenceValidityPass implements CompilerPassInterface
|
|
|
|
{
|
2011-03-11 13:50:46 +00:00
|
|
|
private $container;
|
|
|
|
private $currentId;
|
|
|
|
private $currentScope;
|
|
|
|
private $currentScopeAncestors;
|
|
|
|
private $currentScopeChildren;
|
2011-01-26 23:14:31 +00:00
|
|
|
|
2011-02-13 18:06:41 +00:00
|
|
|
/**
|
|
|
|
* Processes the ContainerBuilder to validate References.
|
|
|
|
*
|
2011-03-04 14:26:00 +00:00
|
|
|
* @param ContainerBuilder $container
|
2011-02-13 18:06:41 +00:00
|
|
|
*/
|
2011-01-26 23:14:31 +00:00
|
|
|
public function process(ContainerBuilder $container)
|
|
|
|
{
|
|
|
|
$this->container = $container;
|
|
|
|
|
2015-06-15 03:41:59 +01:00
|
|
|
$children = $this->container->getScopeChildren(false);
|
2011-01-26 23:14:31 +00:00
|
|
|
$ancestors = array();
|
|
|
|
|
2015-06-15 03:41:59 +01:00
|
|
|
$scopes = $this->container->getScopes(false);
|
2011-01-26 23:14:31 +00:00
|
|
|
foreach ($scopes as $name => $parent) {
|
|
|
|
$ancestors[$name] = array($parent);
|
|
|
|
|
|
|
|
while (isset($scopes[$parent])) {
|
|
|
|
$ancestors[$name][] = $parent = $scopes[$parent];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($container->getDefinitions() as $id => $definition) {
|
|
|
|
if ($definition->isSynthetic() || $definition->isAbstract()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->currentId = $id;
|
2015-06-15 03:41:59 +01:00
|
|
|
$this->currentScope = $scope = $definition->getScope(false);
|
2011-01-26 23:14:31 +00:00
|
|
|
|
|
|
|
if (ContainerInterface::SCOPE_CONTAINER === $scope) {
|
|
|
|
$this->currentScopeChildren = array_keys($scopes);
|
|
|
|
$this->currentScopeAncestors = array();
|
2011-12-18 13:42:59 +00:00
|
|
|
} elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
|
2013-03-23 10:39:42 +00:00
|
|
|
$this->currentScopeChildren = isset($children[$scope]) ? $children[$scope] : array();
|
|
|
|
$this->currentScopeAncestors = isset($ancestors[$scope]) ? $ancestors[$scope] : array();
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->validateReferences($definition->getArguments());
|
|
|
|
$this->validateReferences($definition->getMethodCalls());
|
2011-03-04 14:26:00 +00:00
|
|
|
$this->validateReferences($definition->getProperties());
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-13 18:06:41 +00:00
|
|
|
/**
|
|
|
|
* Validates an array of References.
|
|
|
|
*
|
|
|
|
* @param array $arguments An array of Reference objects
|
2011-12-13 07:50:54 +00:00
|
|
|
*
|
2011-12-04 23:51:22 +00:00
|
|
|
* @throws RuntimeException when there is a reference to an abstract definition.
|
2011-02-13 18:06:41 +00:00
|
|
|
*/
|
2011-03-11 13:50:46 +00:00
|
|
|
private function validateReferences(array $arguments)
|
2011-01-26 23:14:31 +00:00
|
|
|
{
|
|
|
|
foreach ($arguments as $argument) {
|
|
|
|
if (is_array($argument)) {
|
|
|
|
$this->validateReferences($argument);
|
|
|
|
} elseif ($argument instanceof Reference) {
|
|
|
|
$targetDefinition = $this->getDefinition((string) $argument);
|
|
|
|
|
|
|
|
if (null !== $targetDefinition && $targetDefinition->isAbstract()) {
|
2011-12-04 23:51:22 +00:00
|
|
|
throw new RuntimeException(sprintf(
|
2011-01-26 23:14:31 +00:00
|
|
|
'The definition "%s" has a reference to an abstract definition "%s". '
|
|
|
|
.'Abstract definitions cannot be the target of references.',
|
|
|
|
$this->currentId,
|
|
|
|
$argument
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->validateScope($argument, $targetDefinition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-13 18:06:41 +00:00
|
|
|
/**
|
|
|
|
* Validates the scope of a single Reference.
|
|
|
|
*
|
2011-04-23 16:05:44 +01:00
|
|
|
* @param Reference $reference
|
2011-03-04 14:26:00 +00:00
|
|
|
* @param Definition $definition
|
2011-12-13 07:50:54 +00:00
|
|
|
*
|
2011-12-04 23:57:59 +00:00
|
|
|
* @throws ScopeWideningInjectionException when the definition references a service of a narrower scope
|
|
|
|
* @throws ScopeCrossingInjectionException when the definition references a service of another scope hierarchy
|
2011-02-13 18:06:41 +00:00
|
|
|
*/
|
2011-03-11 13:50:46 +00:00
|
|
|
private function validateScope(Reference $reference, Definition $definition = null)
|
2011-01-26 23:14:31 +00:00
|
|
|
{
|
|
|
|
if (ContainerInterface::SCOPE_PROTOTYPE === $this->currentScope) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-15 03:41:59 +01:00
|
|
|
if (!$reference->isStrict(false)) {
|
2011-01-26 23:14:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (null === $definition) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-15 03:41:59 +01:00
|
|
|
if ($this->currentScope === $scope = $definition->getScope(false)) {
|
2011-01-26 23:14:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$id = (string) $reference;
|
|
|
|
|
|
|
|
if (in_array($scope, $this->currentScopeChildren, true)) {
|
2011-03-14 20:12:42 +00:00
|
|
|
throw new ScopeWideningInjectionException($this->currentId, $this->currentScope, $id, $scope);
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!in_array($scope, $this->currentScopeAncestors, true)) {
|
2011-03-14 20:12:42 +00:00
|
|
|
throw new ScopeCrossingInjectionException($this->currentId, $this->currentScope, $id, $scope);
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-13 18:06:41 +00:00
|
|
|
/**
|
|
|
|
* Returns the Definition given an id.
|
|
|
|
*
|
|
|
|
* @param string $id Definition identifier
|
2011-12-13 07:50:54 +00:00
|
|
|
*
|
2011-02-13 18:06:41 +00:00
|
|
|
* @return Definition
|
|
|
|
*/
|
2011-03-11 13:50:46 +00:00
|
|
|
private function getDefinition($id)
|
2011-01-26 23:14:31 +00:00
|
|
|
{
|
|
|
|
if (!$this->container->hasDefinition($id)) {
|
2014-04-16 08:15:58 +01:00
|
|
|
return;
|
2011-01-26 23:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->container->getDefinition($id);
|
|
|
|
}
|
|
|
|
}
|