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} */ public function get($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } // Re-use shared service instance if it exists. if (isset($this->services[$id]) || ($invalid_behavior === ContainerInterface::NULL_ON_INVALID_REFERENCE && array_key_exists($id, $this->services))) { return $this->services[$id]; } if (isset($this->loading[$id])) { throw new ServiceCircularReferenceException($id, array_keys($this->loading)); } $definition = isset($this->serviceDefinitions[$id]) ? $this->serviceDefinitions[$id] : NULL; if (!$definition && $invalid_behavior === ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { if (!$id) { throw new ServiceNotFoundException($id); } throw new ServiceNotFoundException($id, NULL, NULL, $this->getServiceAlternatives($id)); } // In case something else than ContainerInterface::NULL_ON_INVALID_REFERENCE // is used, the actual wanted behavior is to re-try getting the service at a // later point. if (!$definition) { return; } // Definition is a keyed array, so [0] is only defined when it is a // serialized string. if (isset($definition[0])) { $definition = unserialize($definition); } // Now create the service. $this->loading[$id] = TRUE; try { $service = $this->createService($definition, $id); } catch (\Exception $e) { unset($this->loading[$id]); unset($this->services[$id]); if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalid_behavior) { return; } throw $e; } unset($this->loading[$id]); return $service; } /** * {@inheritdoc} */ public function reset() { if (!empty($this->scopedServices)) { throw new LogicException('Resetting the container is not allowed when a scope is active.'); } $this->services = []; } /** * Creates a service from a service definition. * * @param array $definition * The service definition to create a service from. * @param string $id * The service identifier, necessary so it can be shared if its public. * * @return object * The service described by the service definition. * * @throws \Symfony\Component\DependencyInjection\Exception\RuntimeException * Thrown when the service is a synthetic service. * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException * Thrown when the configurator callable in $definition['configurator'] is * not actually a callable. * @throws \ReflectionException * Thrown when the service class takes more than 10 parameters to construct, * and cannot be instantiated. */ protected function createService(array $definition, $id) { 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 = $definition['arguments']; if ($arguments instanceof \stdClass) { $arguments = $this->resolveServicesAndParameters($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 // ReflectionClass 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]; if ($arguments instanceof \stdClass) { $arguments = $this->resolveServicesAndParameters($arguments); } } call_user_func_array([$service, $method], $arguments); } } if (isset($definition['properties'])) { if ($definition['properties'] instanceof \stdClass) { $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} */ public function set($id, $service) { $this->services[$id] = $service; } /** * {@inheritdoc} */ public function has($id) { return isset($this->aliases[$id]) || isset($this->services[$id]) || isset($this->serviceDefinitions[$id]); } /** * {@inheritdoc} */ public function getParameter($name) { if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) { if (!$name) { throw new ParameterNotFoundException($name); } throw new ParameterNotFoundException($name, NULL, NULL, NULL, $this->getParameterAlternatives($name)); } return $this->parameters[$name]; } /** * {@inheritdoc} */ public function hasParameter($name) { return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters); } /** * {@inheritdoc} */ public function setParameter($name, $value) { if ($this->frozen) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } $this->parameters[$name] = $value; } /** * {@inheritdoc} */ public function initialized($id) { if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } return isset($this->services[$id]) || array_key_exists($id, $this->services); } /** * Resolves arguments that represent services or variables to the real values. * * @param array|\stdClass $arguments * The arguments to resolve. * * @return array * The resolved arguments. * * @throws \Symfony\Component\DependencyInjection\Exception\RuntimeException * If a parameter/service could not be resolved. * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException * If an unknown type is met while resolving parameters and services. */ protected function resolveServicesAndParameters($arguments) { // Check if this collection needs to be resolved. if ($arguments instanceof \stdClass) { if ($arguments->type !== 'collection') { throw new InvalidArgumentException(sprintf('Undefined type "%s" while resolving parameters and services.', $arguments->type)); } // In case there is nothing to resolve, we are done here. if (!$arguments->resolve) { return $arguments->value; } $arguments = $arguments->value; } // Process the arguments. foreach ($arguments as $key => $argument) { // For this machine-optimized format, only \stdClass arguments are // processed and resolved. All other values are kept as is. if ($argument instanceof \stdClass) { $type = $argument->type; // Check for parameter. if ($type == 'parameter') { $name = $argument->name; 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. } // Update argument. $argument = $arguments[$key] = $this->parameters[$name]; // In case there is not a machine readable value (e.g. a service) // behind this resolved parameter, continue. if (!($argument instanceof \stdClass)) { continue; } // Fall through. $type = $argument->type; } // Create a service. if ($type == 'service') { $id = $argument->id; // Does the service already exist? if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } if (isset($this->services[$id])) { $arguments[$key] = $this->services[$id]; continue; } // Return the service. $arguments[$key] = $this->get($id, $argument->invalidBehavior); continue; } // Create private service. elseif ($type == 'private_service') { $id = $argument->id; // Does the private service already exist. if (isset($this->privateServices[$id])) { $arguments[$key] = $this->privateServices[$id]; continue; } // Create the private service. $arguments[$key] = $this->createService($argument->value, $id); if ($argument->shared) { $this->privateServices[$id] = $arguments[$key]; } continue; } // Check for collection. elseif ($type == 'collection') { $value = $argument->value; // Does this collection need resolving? if ($argument->resolve) { $arguments[$key] = $this->resolveServicesAndParameters($value); } else { $arguments[$key] = $value; } continue; } if ($type !== NULL) { throw new InvalidArgumentException(sprintf('Undefined type "%s" while resolving parameters and services.', $type)); } } } return $arguments; } /** * Provides alternatives for a given array and key. * * @param string $search_key * The search key to get alternatives for. * @param array $keys * The search space to search for alternatives in. * * @return string[] * An array of strings with suitable alternatives. */ protected function getAlternatives($search_key, array $keys) { $alternatives = []; foreach ($keys as $key) { $lev = levenshtein($search_key, $key); if ($lev <= strlen($search_key) / 3 || strpos($key, $search_key) !== FALSE) { $alternatives[] = $key; } } return $alternatives; } /** * Provides alternatives in case a service was not found. * * @param string $id * The service to get alternatives for. * * @return string[] * An array of strings with suitable alternatives. */ protected function getServiceAlternatives($id) { $all_service_keys = array_unique(array_merge(array_keys($this->services), array_keys($this->serviceDefinitions))); return $this->getAlternatives($id, $all_service_keys); } /** * Provides alternatives in case a parameter was not found. * * @param string $name * The parameter to get alternatives for. * * @return string[] * An array of strings with suitable alternatives. */ protected function getParameterAlternatives($name) { return $this->getAlternatives($name, array_keys($this->parameters)); } /** * Gets all defined service IDs. * * @return array * An array of all defined service IDs. */ public function getServiceIds() { return array_keys($this->serviceDefinitions + $this->services); } /** * Ensure that cloning doesn't work. */ private function __clone() { } }