From 87392ab30dedc51ec4f07e917f80f0bebe7a04f8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 16 Aug 2018 10:54:12 +0200 Subject: [PATCH] [DI] leverage Contracts\Service --- .../FrameworkExtension.php | 2 +- .../FrameworkBundle/Test/KernelTestCase.php | 4 +- .../Resource/ReflectionClassResource.php | 8 ++- .../DependencyInjection/CHANGELOG.md | 2 + .../RegisterServiceSubscribersPass.php | 2 +- .../ResettableContainerInterface.php | 2 + .../DependencyInjection/ServiceLocator.php | 66 +++++++------------ .../ServiceSubscriberInterface.php | 37 ++--------- .../RegisterServiceSubscribersPassTest.php | 2 +- .../Tests/Fixtures/TestServiceSubscriber.php | 2 +- .../Fixtures/TestServiceSubscriberParent.php | 2 +- .../Tests/Fixtures/includes/classes.php | 2 +- .../Tests/ServiceLocatorTest.php | 4 +- .../EventListener/TestSessionListenerTest.php | 10 --- 14 files changed, 48 insertions(+), 97 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index af5e99d7f9f98..9437f567367e0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -50,7 +50,6 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Finder\Finder; @@ -102,6 +101,7 @@ use Symfony\Component\Yaml\Command\LintCommand as BaseYamlLintCommand; use Symfony\Component\Yaml\Yaml; use Symfony\Contracts\Service\ResetInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; /** * FrameworkExtension. diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index 351d27900e83a..f951ba7afd6d9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -13,8 +13,8 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\ResettableContainerInterface; use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Contracts\Service\ResetInterface; /** * KernelTestCase is the base class for tests needing a Kernel. @@ -119,7 +119,7 @@ protected static function ensureKernelShutdown() if (null !== static::$kernel) { $container = static::$kernel->getContainer(); static::$kernel->shutdown(); - if ($container instanceof ResettableContainerInterface) { + if ($container instanceof ResetInterface) { $container->reset(); } } diff --git a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php index 3ca08166d560a..3faf01cdda6dc 100644 --- a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php +++ b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php @@ -11,8 +11,9 @@ namespace Symfony\Component\Config\Resource; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface as LegacyServiceSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; /** * @author Nicolas Grekas @@ -157,7 +158,10 @@ private function generateSignature(\ReflectionClass $class) yield print_r(\call_user_func(array($class->name, 'getSubscribedEvents')), true); } - if (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) { + if (interface_exists(LegacyServiceSubscriberInterface::class, false) && $class->isSubclassOf(LegacyServiceSubscriberInterface::class)) { + yield LegacyServiceSubscriberInterface::class; + yield print_r(\call_user_func(array($class->name, 'getSubscribedServices')), true); + } elseif (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) { yield ServiceSubscriberInterface::class; yield print_r(\call_user_func(array($class->name, 'getSubscribedServices')), true); } diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 8898e512b8864..2cc1a49725930 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -10,6 +10,8 @@ CHANGELOG * added `ServiceLocatorArgument` and `!service_locator` config tag for creating optimized service-locators * added support for autoconfiguring bindings * added `%env(key:...)%` processor to fetch a specific key from an array + * deprecated `ServiceSubscriberInterface`, use the same interface from the `Symfony\Contracts\Service` namespace instead + * deprecated `ResettableContainerInterface`, use `Symfony\Contracts\Service\ResetInterface` instead 4.1.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php index 12675c1b91b9e..a044cb539dd29 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/RegisterServiceSubscribersPass.php @@ -15,8 +15,8 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Contracts\Service\ServiceSubscriberInterface; /** * Compiler pass to register tagged services that require a service locator. diff --git a/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php b/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php index 32cf0e9b045ca..b9714d25098fe 100644 --- a/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php +++ b/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php @@ -19,6 +19,8 @@ * not needed anymore. * * @author Christophe Coevoet + * + * @deprecated since Symfony 4.2, use "Symfony\Contracts\Service\ResetInterface" instead. */ interface ResettableContainerInterface extends ContainerInterface, ResetInterface { diff --git a/src/Symfony/Component/DependencyInjection/ServiceLocator.php b/src/Symfony/Component/DependencyInjection/ServiceLocator.php index 5ec14147d13d9..83c3b829c4cd6 100644 --- a/src/Symfony/Component/DependencyInjection/ServiceLocator.php +++ b/src/Symfony/Component/DependencyInjection/ServiceLocator.php @@ -11,10 +11,14 @@ namespace Symfony\Component\DependencyInjection; +use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface as PsrContainerInterface; +use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Contracts\Service\ServiceLocatorTrait; +use Symfony\Contracts\Service\ServiceSubscriberInterface; /** * @author Robin Chalas @@ -22,51 +26,22 @@ */ class ServiceLocator implements PsrContainerInterface { - private $factories; - private $loading = array(); - private $externalId; - private $container; - - /** - * @param callable[] $factories - */ - public function __construct(array $factories) - { - $this->factories = $factories; + use ServiceLocatorTrait { + get as private doGet; } - /** - * {@inheritdoc} - */ - public function has($id) - { - return isset($this->factories[$id]); - } + private $externalId; + private $container; - /** - * {@inheritdoc} - */ public function get($id) { - if (!isset($this->factories[$id])) { - throw new ServiceNotFoundException($id, end($this->loading) ?: null, null, array(), $this->createServiceNotFoundMessage($id)); - } - - if (isset($this->loading[$id])) { - $ids = array_values($this->loading); - $ids = \array_slice($this->loading, array_search($id, $ids)); - $ids[] = $id; - - throw new ServiceCircularReferenceException($id, $ids); + if (!$this->externalId) { + return $this->doGet($id); } - $this->loading[$id] = $id; try { - return $this->factories[$id](); + return $this->doGet($id); } catch (RuntimeException $e) { - if (!$this->externalId) { - throw $e; - } $what = sprintf('service "%s" required by "%s"', $id, $this->externalId); $message = preg_replace('/service "\.service_locator\.[^"]++"/', $what, $e->getMessage()); @@ -79,8 +54,6 @@ public function get($id) $r->setValue($e, $message); throw $e; - } finally { - unset($this->loading[$id]); } } @@ -101,14 +74,16 @@ public function withContext($externalId, Container $container) return $locator; } - private function createServiceNotFoundMessage($id) + private function createNotFoundException(string $id): NotFoundExceptionInterface { if ($this->loading) { - return sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $this->formatAlternatives()); + $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $this->formatAlternatives()); + + return new ServiceNotFoundException($id, end($this->loading) ?: null, null, array(), $msg); } - $class = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 3); - $class = isset($class[2]['object']) ? \get_class($class[2]['object']) : null; + $class = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 4); + $class = isset($class[3]['object']) ? \get_class($class[3]['object']) : null; $externalId = $this->externalId ?: $class; $msg = sprintf('Service "%s" not found: ', $id); @@ -143,7 +118,12 @@ private function createServiceNotFoundMessage($id) $msg .= 'Try using dependency injection instead.'; } - return $msg; + return new ServiceNotFoundException($id, end($this->loading) ?: null, null, array(), $msg); + } + + private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface + { + return new ServiceCircularReferenceException($id, $path); } private function formatAlternatives(array $alternatives = null, $separator = 'and') diff --git a/src/Symfony/Component/DependencyInjection/ServiceSubscriberInterface.php b/src/Symfony/Component/DependencyInjection/ServiceSubscriberInterface.php index 7024484bd8aee..a3b6ba790718e 100644 --- a/src/Symfony/Component/DependencyInjection/ServiceSubscriberInterface.php +++ b/src/Symfony/Component/DependencyInjection/ServiceSubscriberInterface.php @@ -11,40 +11,13 @@ namespace Symfony\Component\DependencyInjection; +use Symfony\Contracts\Service\ServiceSubscriberInterface as BaseServiceSubscriberInterface; + /** - * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method. - * - * The getSubscribedServices method returns an array of service types required by such instances, - * optionally keyed by the service names used internally. Service types that start with an interrogation - * mark "?" are optional, while the other ones are mandatory service dependencies. - * - * The injected service locators SHOULD NOT allow access to any other services not specified by the method. - * - * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally. - * This interface does not dictate any injection method for these service locators, although constructor - * injection is recommended. + * {@inheritdoc} * - * @author Nicolas Grekas + * @deprecated since Symfony 4.2, use Symfony\Contracts\Service\ServiceSubscriberInterface instead. */ -interface ServiceSubscriberInterface +interface ServiceSubscriberInterface extends BaseServiceSubscriberInterface { - /** - * Returns an array of service types required by such instances, optionally keyed by the service names used internally. - * - * For mandatory dependencies: - * - * * array('logger' => 'Psr\Log\LoggerInterface') means the objects use the "logger" name - * internally to fetch a service which must implement Psr\Log\LoggerInterface. - * * array('Psr\Log\LoggerInterface') is a shortcut for - * * array('Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface') - * - * otherwise: - * - * * array('logger' => '?Psr\Log\LoggerInterface') denotes an optional dependency - * * array('?Psr\Log\LoggerInterface') is a shortcut for - * * array('Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface') - * - * @return array The required service types, optionally keyed by service names - */ - public static function getSubscribedServices(); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php index b24e4cd46f5ee..24b3088ed343a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php @@ -38,7 +38,7 @@ class RegisterServiceSubscribersPassTest extends TestCase { /** * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage Service "foo" must implement interface "Symfony\Component\DependencyInjection\ServiceSubscriberInterface". + * @expectedExceptionMessage Service "foo" must implement interface "Symfony\Contracts\Service\ServiceSubscriberInterface". */ public function testInvalidClass() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php index 875abe9e02e12..98be035c054c9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php @@ -2,7 +2,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; class TestServiceSubscriber implements ServiceSubscriberInterface { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php index 77d28d2ac5112..04a024e4ecd81 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriberParent.php @@ -2,7 +2,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; use Symfony\Contracts\Service\ServiceSubscriberTrait; class TestServiceSubscriberParent implements ServiceSubscriberInterface diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index 33b043fa3f384..29913a8556093 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -2,7 +2,7 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; function sc_configure($instance) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php b/src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php index 8af1b5a0972c1..bf1af4d509ebd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ServiceLocatorTest.php @@ -13,7 +13,7 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ServiceLocator; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; use Symfony\Contracts\Tests\Service\ServiceLocatorTest as BaseServiceLocatorTest; class ServiceLocatorTest extends BaseServiceLocatorTest @@ -74,7 +74,7 @@ public function testInvoke() } } -class SomeServiceSubscriber implements ServiceSubscriberinterface +class SomeServiceSubscriber implements ServiceSubscriberInterface { public $container; diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php index b06dca3c53302..cd52bd56e2d57 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\HttpKernel\Tests\EventListener; use PHPUnit\Framework\TestCase; -use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -144,15 +143,6 @@ public function testUnstartedSessionIsNotSave() $this->filterResponse(new Request()); } - public function testDoesNotImplementServiceSubscriberInterface() - { - $this->assertTrue(interface_exists(ServiceSubscriberInterface::class)); - $this->assertTrue(class_exists(SessionListener::class)); - $this->assertTrue(class_exists(TestSessionListener::class)); - $this->assertFalse(is_subclass_of(SessionListener::class, ServiceSubscriberInterface::class), 'Implementing ServiceSubscriberInterface would create a dep on the DI component, which eg Silex cannot afford'); - $this->assertFalse(is_subclass_of(TestSessionListener::class, ServiceSubscriberInterface::class, 'Implementing ServiceSubscriberInterface would create a dep on the DI component, which eg Silex cannot afford')); - } - public function testDoesNotThrowIfRequestDoesNotHaveASession() { $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();