You are here

class UpdateCompilerPass in Drupal 8

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Core/Update/UpdateCompilerPass.php \Drupal\Core\Update\UpdateCompilerPass
  2. 10 core/lib/Drupal/Core/Update/UpdateCompilerPass.php \Drupal\Core\Update\UpdateCompilerPass

Removes services with unmet dependencies.

Updates can install new modules that add services that existing services now depend on. This compiler pass allows the update system to work in such cases.

Hierarchy

  • class \Drupal\Core\Update\UpdateCompilerPass implements \Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface

Expanded class hierarchy of UpdateCompilerPass

File

core/lib/Drupal/Core/Update/UpdateCompilerPass.php, line 16

Namespace

Drupal\Core\Update
View source
class UpdateCompilerPass implements CompilerPassInterface {

  /**
   * {@inheritdoc}
   */
  public function process(ContainerBuilder $container) {
    $process_aliases = FALSE;

    // Loop over the defined services and remove any with unmet dependencies.
    // The kernel cannot be booted if the container has such services. This
    // allows modules to run their update hooks to enable newly added
    // dependencies.
    do {
      $has_changed = FALSE;
      foreach ($container
        ->getDefinitions() as $key => $definition) {

        // Ensure all the definition's arguments are valid.
        foreach ($definition
          ->getArguments() as $argument) {
          if ($this
            ->isArgumentMissingService($argument, $container)) {
            $container
              ->removeDefinition($key);
            $container
              ->log($this, sprintf('Removed service "%s"; reason: depends on non-existent service "%s".', $key, (string) $argument));
            $has_changed = TRUE;
            $process_aliases = TRUE;

            // Process the next definition.
            continue 2;
          }
        }

        // Ensure all the method call arguments are valid.
        foreach ($definition
          ->getMethodCalls() as $call) {
          foreach ($call[1] as $argument) {
            if ($this
              ->isArgumentMissingService($argument, $container)) {
              $container
                ->removeDefinition($key);
              $container
                ->log($this, sprintf('Removed service "%s"; reason: method call "%s" depends on non-existent service "%s".', $key, $call[0], (string) $argument));
              $has_changed = TRUE;
              $process_aliases = TRUE;

              // Process the next definition.
              continue 3;
            }
          }
        }
      }

      // Repeat if services have been removed.
    } while ($has_changed);

    // Remove aliases to services that have been removed. This does not need to
    // be part of the loop above because references to aliases have already been
    // resolved by Symfony's ResolveReferencesToAliasesPass.
    if ($process_aliases) {
      foreach ($container
        ->getAliases() as $key => $alias) {
        $id = (string) $alias;
        if (!$container
          ->has($id)) {
          $container
            ->removeAlias($key);
          $container
            ->log($this, sprintf('Removed alias "%s"; reason: alias to non-existent service "%s".', $key, $id));
        }
      }
    }
  }

  /**
   * Checks if a reference argument is to a missing service.
   *
   * @param mixed $argument
   *   The argument to check.
   * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
   *   The container.
   *
   * @return bool
   *   TRUE if the argument is a reference to a service that is missing from the
   *   container and the reference is required, FALSE if not.
   */
  private function isArgumentMissingService($argument, ContainerBuilder $container) {
    if ($argument instanceof Reference) {
      $argument_id = (string) $argument;
      if (!$container
        ->has($argument_id) && $argument
        ->getInvalidBehavior() === ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
        return TRUE;
      }
    }
    return FALSE;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
UpdateCompilerPass::isArgumentMissingService private function Checks if a reference argument is to a missing service.
UpdateCompilerPass::process public function You can modify the container here before it is dumped to PHP code.