You are here

class CheckReferenceValidityPass in Service Container 7

Same name and namespace in other branches
  1. 7.2 modules/providers/service_container_symfony/lib/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php \Symfony\Component\DependencyInjection\Compiler\CheckReferenceValidityPass

Checks the validity of references.

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>

Hierarchy

Expanded class hierarchy of CheckReferenceValidityPass

1 file declares its use of CheckReferenceValidityPass
CheckReferenceValidityPassTest.php in modules/providers/service_container_symfony/lib/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php

File

modules/providers/service_container_symfony/lib/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php, line 32

Namespace

Symfony\Component\DependencyInjection\Compiler
View source
class CheckReferenceValidityPass implements CompilerPassInterface {
  private $container;
  private $currentId;
  private $currentScope;
  private $currentScopeAncestors;
  private $currentScopeChildren;

  /**
   * Processes the ContainerBuilder to validate References.
   *
   * @param ContainerBuilder $container
   */
  public function process(ContainerBuilder $container) {
    $this->container = $container;
    $children = $this->container
      ->getScopeChildren();
    $ancestors = array();
    $scopes = $this->container
      ->getScopes();
    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;
      $this->currentDefinition = $definition;
      $this->currentScope = $scope = $definition
        ->getScope();
      if (ContainerInterface::SCOPE_CONTAINER === $scope) {
        $this->currentScopeChildren = array_keys($scopes);
        $this->currentScopeAncestors = array();
      }
      elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
        $this->currentScopeChildren = isset($children[$scope]) ? $children[$scope] : array();
        $this->currentScopeAncestors = isset($ancestors[$scope]) ? $ancestors[$scope] : array();
      }
      $this
        ->validateReferences($definition
        ->getArguments());
      $this
        ->validateReferences($definition
        ->getMethodCalls());
      $this
        ->validateReferences($definition
        ->getProperties());
    }
  }

  /**
   * Validates an array of References.
   *
   * @param array $arguments An array of Reference objects
   *
   * @throws RuntimeException when there is a reference to an abstract definition.
   */
  private function validateReferences(array $arguments) {
    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()) {
          throw new RuntimeException(sprintf('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);
      }
    }
  }

  /**
   * Validates the scope of a single Reference.
   *
   * @param Reference  $reference
   * @param Definition $definition
   *
   * @throws ScopeWideningInjectionException when the definition references a service of a narrower scope
   * @throws ScopeCrossingInjectionException when the definition references a service of another scope hierarchy
   */
  private function validateScope(Reference $reference, Definition $definition = null) {
    if (ContainerInterface::SCOPE_PROTOTYPE === $this->currentScope) {
      return;
    }
    if (!$reference
      ->isStrict()) {
      return;
    }
    if (null === $definition) {
      return;
    }
    if ($this->currentScope === ($scope = $definition
      ->getScope())) {
      return;
    }
    $id = (string) $reference;
    if (in_array($scope, $this->currentScopeChildren, true)) {
      throw new ScopeWideningInjectionException($this->currentId, $this->currentScope, $id, $scope);
    }
    if (!in_array($scope, $this->currentScopeAncestors, true)) {
      throw new ScopeCrossingInjectionException($this->currentId, $this->currentScope, $id, $scope);
    }
  }

  /**
   * Returns the Definition given an id.
   *
   * @param string $id Definition identifier
   *
   * @return Definition
   */
  private function getDefinition($id) {
    if (!$this->container
      ->hasDefinition($id)) {
      return;
    }
    return $this->container
      ->getDefinition($id);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CheckReferenceValidityPass::$container private property
CheckReferenceValidityPass::$currentId private property
CheckReferenceValidityPass::$currentScope private property
CheckReferenceValidityPass::$currentScopeAncestors private property
CheckReferenceValidityPass::$currentScopeChildren private property
CheckReferenceValidityPass::getDefinition private function Returns the Definition given an id.
CheckReferenceValidityPass::process public function Processes the ContainerBuilder to validate References. Overrides CompilerPassInterface::process
CheckReferenceValidityPass::validateReferences private function Validates an array of References.
CheckReferenceValidityPass::validateScope private function Validates the scope of a single Reference.