diff --git a/core/core.services.yml b/core/core.services.yml index ecd8743..415d3ce 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -497,6 +497,7 @@ services: entity.manager: class: Drupal\Core\Entity\EntityManager parent: container.trait + deprecated: %service_id%-no-warning # @todo Remove this tag in https://www.drupal.org/node/2549143. tags: - { name: plugin_manager_cache_clear } diff --git a/core/lib/Drupal/Component/DependencyInjection/Container.php b/core/lib/Drupal/Component/DependencyInjection/Container.php index df25ae8..201b3d1 100644 --- a/core/lib/Drupal/Component/DependencyInjection/Container.php +++ b/core/lib/Drupal/Component/DependencyInjection/Container.php @@ -9,6 +9,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\IntrospectableContainerInterface; +use Symfony\Component\DependencyInjection\ResettableContainerInterface; use Symfony\Component\DependencyInjection\ScopeInterface; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -54,7 +55,7 @@ * * @ingroup container */ -class Container implements IntrospectableContainerInterface { +class Container implements IntrospectableContainerInterface, ResettableContainerInterface { /** * The parameters of the container. @@ -182,11 +183,7 @@ public function get($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_IN } catch (\Exception $e) { unset($this->loading[$id]); - - // Remove a potentially shared service that was constructed incompletely. - if (array_key_exists($id, $this->services)) { - unset($this->services[$id]); - } + unset($this->services[$id]); if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalid_behavior) { return; @@ -201,6 +198,17 @@ public function get($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_IN } /** + * {@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 @@ -359,6 +367,10 @@ protected function createService(array $definition, $id) { * {@inheritdoc} */ public function set($id, $service, $scope = ContainerInterface::SCOPE_CONTAINER) { + if (!in_array($scope, array('container', 'request')) || ('request' === $scope && 'request' !== $id)) { + @trigger_error('The concept of container scopes is deprecated since version 2.8 and will be removed in 3.0. Omit the third parameter.', E_USER_DEPRECATED); + } + $this->services[$id] = $service; } @@ -585,6 +597,10 @@ protected function getParameterAlternatives($name) { * {@inheritdoc} */ public function enterScope($name) { + if ('request' !== $name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + throw new \BadMethodCallException(sprintf("'%s' is not supported by Drupal 8.", __FUNCTION__)); } @@ -592,6 +608,10 @@ public function enterScope($name) { * {@inheritdoc} */ public function leaveScope($name) { + if ('request' !== $name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + throw new \BadMethodCallException(sprintf("'%s' is not supported by Drupal 8.", __FUNCTION__)); } @@ -599,6 +619,11 @@ public function leaveScope($name) { * {@inheritdoc} */ public function addScope(ScopeInterface $scope) { + + $name = $scope->getName(); + if ('request' !== $name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } throw new \BadMethodCallException(sprintf("'%s' is not supported by Drupal 8.", __FUNCTION__)); } @@ -606,6 +631,10 @@ public function addScope(ScopeInterface $scope) { * {@inheritdoc} */ public function hasScope($name) { + if ('request' !== $name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + throw new \BadMethodCallException(sprintf("'%s' is not supported by Drupal 8.", __FUNCTION__)); } @@ -613,6 +642,8 @@ public function hasScope($name) { * {@inheritdoc} */ public function isScopeActive($name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + throw new \BadMethodCallException(sprintf("'%s' is not supported by Drupal 8.", __FUNCTION__)); } @@ -626,4 +657,11 @@ public function getServiceIds() { return array_keys($this->serviceDefinitions + $this->services); } + /** + * Ensure that cloning doesn't work. + */ + private function __clone() + { + } + } diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php index 1edaec3..ff72be4 100644 --- a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php +++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php @@ -9,8 +9,12 @@ use Symfony\Component\DependencyInjection\ContainerBuilder as SymfonyContainerBuilder; use Symfony\Component\DependencyInjection\Container as SymfonyContainer; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\InactiveScopeException; /** * Drupal's dependency injection container builder. @@ -30,6 +34,146 @@ public function __construct(ParameterBagInterface $parameterBag = NULL) { } /** + * Creates a service for a service definition. + * + * Overrides the parent implementation, but just changes one line about + * deprecations, see below. + * + * @param \Symfony\Component\DependencyInjection\Definition $definition + * @param string $id + * @param bool|true $tryProxy + * + * @return mixed|object + */ + public function createService(Definition $definition, $id, $tryProxy = true) + { + if ($definition->isSynthetic()) { + throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); + } + + if ($definition->isDeprecated()) { + // @this line is changed. + if ($definition->getDeprecationMessage($id) != ($id . '-no-warning')) { + @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED); + } + } + + if ($tryProxy && $definition->isLazy()) { + $container = $this; + + $proxy = $this + ->getProxyInstantiator() + ->instantiateProxy( + $container, + $definition, + $id, function () use ($definition, $id, $container) { + return $container->createService($definition, $id, false); + } + ); + $this->shareService($definition, $proxy, $id); + + return $proxy; + } + + $parameterBag = $this->getParameterBag(); + + if (null !== $definition->getFile()) { + require_once $parameterBag->resolveValue($definition->getFile()); + } + + $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments()))); + + if (null !== $factory = $definition->getFactory()) { + if (is_array($factory)) { + $factory = array($this->resolveServices($parameterBag->resolveValue($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); + + if (!$definition->isDeprecated() && is_array($factory) && is_string($factory[0])) { + $r = new \ReflectionClass($factory[0]); + + if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) { + @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED); + } + } + } elseif (null !== $definition->getFactoryMethod(false)) { + if (null !== $definition->getFactoryClass(false)) { + $factory = $parameterBag->resolveValue($definition->getFactoryClass(false)); + } elseif (null !== $definition->getFactoryService(false)) { + $factory = $this->get($parameterBag->resolveValue($definition->getFactoryService(false))); + } else { + throw new RuntimeException(sprintf('Cannot create service "%s" from factory method without a factory service or factory class.', $id)); + } + + $service = call_user_func_array(array($factory, $definition->getFactoryMethod(false)), $arguments); + } else { + $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass())); + + $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); + + if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) { + // Skip deprecation notices for deprecations which opt out. + @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED); + } + } + + if ($tryProxy || !$definition->isLazy()) { + // share only if proxying failed, or if not a proxy + $this->shareService($definition, $service, $id); + } + + foreach ($definition->getMethodCalls() as $call) { + $this->callMethod($service, $call); + } + + $properties = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties()))); + foreach ($properties as $name => $value) { + $service->$name = $value; + } + + if ($callable = $definition->getConfigurator()) { + if (is_array($callable)) { + $callable[0] = $parameterBag->resolveValue($callable[0]); + + if ($callable[0] instanceof Reference) { + $callable[0] = $this->get((string) $callable[0], $callable[0]->getInvalidBehavior()); + } elseif ($callable[0] instanceof Definition) { + $callable[0] = $this->createService($callable[0], null); + } + } + + if (!is_callable($callable)) { + throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service))); + } + + call_user_func($callable, $service); + } + + return $service; + } + + /** + * Direct copy of the parent function. + */ + protected function shareService(Definition $definition, $service, $id) + { + if ($definition->isShared() && self::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) { + if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) { + throw new InactiveScopeException($id, $scope); + } + + $this->services[$lowerId = strtolower($id)] = $service; + + if (self::SCOPE_CONTAINER !== $scope) { + $this->scopedServices[$scope][$lowerId] = $service; + } + } + } + + /** * Overrides Symfony\Component\DependencyInjection\ContainerBuilder::set(). * * Drupal's container builder can be used at runtime after compilation, so we diff --git a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php index 4c7fea9..1c2bf81 100644 --- a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php +++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php @@ -159,8 +159,15 @@ private function parseDefinition($id, $service, $file) $definition->setClass($service['class']); } + if (isset($service['shared'])) { + $definition->setShared($service['shared']); + } + if (isset($service['scope'])) { - $definition->setScope($service['scope']); + if ('request' !== $id) { + @trigger_error(sprintf('The "scope" key of service "%s" in file "%s" is deprecated since version 2.8 and will be removed in 3.0.', $id, $file), E_USER_DEPRECATED); + } + $definition->setScope($service['scope'], false); } if (isset($service['synthetic'])) { @@ -183,6 +190,10 @@ private function parseDefinition($id, $service, $file) $definition->setAbstract($service['abstract']); } + if (array_key_exists('deprecated', $service)) { + $definition->setDeprecated(true, $service['deprecated']); + } + if (isset($service['factory'])) { if (is_string($service['factory'])) { if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) { @@ -275,7 +286,30 @@ private function parseDefinition($id, $service, $file) if (isset($service['decorates'])) { $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null; - $definition->setDecoratedService($service['decorates'], $renameId); + $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0; + $definition->setDecoratedService($service['decorates'], $renameId, $priority); + } + + if (isset($service['autowire'])) { + $definition->setAutowired($service['autowire']); + } + + if (isset($service['autowiring_types'])) { + if (is_string($service['autowiring_types'])) { + $definition->addAutowiringType($service['autowiring_types']); + } else { + if (!is_array($service['autowiring_types'])) { + throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + foreach ($service['autowiring_types'] as $autowiringType) { + if (!is_string($autowiringType)) { + throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + $definition->addAutowiringType($autowiringType); + } + } } $this->container->setDefinition($id, $definition);