aliases = isset($container_definition['aliases']) ? $container_definition['aliases'] : []; $this->parameters = isset($container_definition['parameters']) ? $container_definition['parameters'] : []; $this->serviceDefinitions = isset($container_definition['services']) ? $container_definition['services'] : []; $this->frozen = isset($container_definition['frozen']) ? $container_definition['frozen'] : FALSE; // Register the service_container with itself. $this->services['service_container'] = $this; } /** * {@inheritdoc} */ protected function createService(array $definition, $id) { // This method is a verbatim copy of // \Drupal\Component\DependencyInjection\Container::createService // except for the following difference: // - There are no instanceof checks on \stdClass, which are used in the // parent class to avoid resolving services and parameters when it is // known from dumping that there is nothing to resolve. if (isset($definition['synthetic']) && $definition['synthetic'] === TRUE) { throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The service container does not know how to construct this service. The service will need to be set before it is first used.', $id)); } $arguments = []; if (isset($definition['arguments'])) { $arguments = $this->resolveServicesAndParameters($definition['arguments']); } if (isset($definition['file'])) { $file = $this->frozen ? $definition['file'] : current($this->resolveServicesAndParameters([$definition['file']])); require_once $file; } if (isset($definition['factory'])) { $factory = $definition['factory']; if (is_array($factory)) { $factory = $this->resolveServicesAndParameters([$factory[0], $factory[1]]); } elseif (!is_string($factory)) { throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); } $service = call_user_func_array($factory, $arguments); } else { $class = $this->frozen ? $definition['class'] : current($this->resolveServicesAndParameters([$definition['class']])); $length = isset($definition['arguments_count']) ? $definition['arguments_count'] : count($arguments); // Optimize class instantiation for services with up to 10 parameters as // reflection is noticeably slow. switch ($length) { case 0: $service = new $class(); break; case 1: $service = new $class($arguments[0]); break; case 2: $service = new $class($arguments[0], $arguments[1]); break; case 3: $service = new $class($arguments[0], $arguments[1], $arguments[2]); break; case 4: $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; case 6: $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); break; case 7: $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6]); break; case 8: $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7]); break; case 9: $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7], $arguments[8]); break; case 10: $service = new $class($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6], $arguments[7], $arguments[8], $arguments[9]); break; default: $r = new \ReflectionClass($class); $service = $r->newInstanceArgs($arguments); break; } } if (!isset($definition['shared']) || $definition['shared'] !== FALSE) { $this->services[$id] = $service; } if (isset($definition['calls'])) { foreach ($definition['calls'] as $call) { $method = $call[0]; $arguments = []; if (!empty($call[1])) { $arguments = $call[1]; $arguments = $this->resolveServicesAndParameters($arguments); } call_user_func_array([$service, $method], $arguments); } } if (isset($definition['properties'])) { $definition['properties'] = $this->resolveServicesAndParameters($definition['properties']); foreach ($definition['properties'] as $key => $value) { $service->{$key} = $value; } } if (isset($definition['configurator'])) { $callable = $definition['configurator']; if (is_array($callable)) { $callable = $this->resolveServicesAndParameters($callable); } if (!is_callable($callable)) { throw new InvalidArgumentException(sprintf('The configurator for class "%s" is not a callable.', get_class($service))); } call_user_func($callable, $service); } return $service; } /** * {@inheritdoc} */ protected function resolveServicesAndParameters($arguments) { // This method is different from the parent method only for the following // cases: // - A service is denoted by '@service' and not by a \stdClass object. // - A parameter is denoted by '%parameter%' and not by a \stdClass object. // - The depth of the tree representing the arguments is not known in // advance, so it needs to be fully traversed recursively. foreach ($arguments as $key => $argument) { if ($argument instanceof \stdClass) { $type = $argument->type; // Private services are a special flavor: In case a private service is // only used by one other service, the ContainerBuilder uses a // Definition object as an argument, which does not have an ID set. // Therefore the format uses a \stdClass object to store the definition // and to be able to create the service on the fly. // // Note: When constructing a private service by hand, 'id' must be set. // // The PhpArrayDumper just uses the hash of the private service // definition to generate a unique ID. // // @see \Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper::getPrivateServiceCall if ($type == 'private_service') { $id = $argument->id; // Check if the private service already exists - in case it is shared. if (!empty($argument->shared) && isset($this->privateServices[$id])) { $arguments[$key] = $this->privateServices[$id]; continue; } // Create a private service from a service definition. $arguments[$key] = $this->createService($argument->value, $id); if (!empty($argument->shared)) { $this->privateServices[$id] = $arguments[$key]; } continue; } if ($type !== NULL) { throw new InvalidArgumentException("Undefined type '$type' while resolving parameters and services."); } } if (is_array($argument)) { $arguments[$key] = $this->resolveServicesAndParameters($argument); continue; } if (!is_string($argument)) { continue; } // Resolve parameters. if ($argument[0] === '%') { $name = substr($argument, 1, -1); if (!isset($this->parameters[$name])) { $arguments[$key] = $this->getParameter($name); // This can never be reached as getParameter() throws an Exception, // because we already checked that the parameter is not set above. } $argument = $this->parameters[$name]; $arguments[$key] = $argument; } // Resolve services. if ($argument[0] === '@') { $id = substr($argument, 1); $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; if ($id[0] === '?') { $id = substr($id, 1); $invalid_behavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; } if (isset($this->services[$id])) { $arguments[$key] = $this->services[$id]; } else { $arguments[$key] = $this->get($id, $invalid_behavior); } } } return $arguments; } }