From c54c581e74355b24e06134a15e360cce69112658 Mon Sep 17 00:00:00 2001 From: Kamil Kokot Date: Wed, 13 Feb 2019 00:38:07 +0100 Subject: [PATCH] Refactor our environment handler to decorate the original one --- phpstan.neon | 2 +- .../ContextServiceEnvironmentHandler.php | 190 +++++++----------- ...nitialisedSymfonyExtensionEnvironment.php} | 2 +- ...nt.php => SymfonyExtensionEnvironment.php} | 2 +- ...UninitialisedContextServiceEnvironment.php | 63 ------ ...initialisedSymfonyExtensionEnvironment.php | 64 ++++++ src/ServiceContainer/SymfonyExtension.php | 13 +- 7 files changed, 145 insertions(+), 191 deletions(-) rename src/Context/Environment/{InitialisedContextServiceEnvironment.php => InitialisedSymfonyExtensionEnvironment.php} (95%) rename src/Context/Environment/{ContextServiceEnvironment.php => SymfonyExtensionEnvironment.php} (89%) delete mode 100644 src/Context/Environment/UninitialisedContextServiceEnvironment.php create mode 100644 src/Context/Environment/UninitialisedSymfonyExtensionEnvironment.php diff --git a/phpstan.neon b/phpstan.neon index 5894523..e1d4afb 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,5 +5,5 @@ parameters: - '/Cannot access offset 0 on callable/' - '/Cannot access offset 1 on callable/' - '/Cannot call method [a-zA-Z0-9]+\(\) on Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface|null\./' - - '/Method FriendsOfBehat\\SymfonyExtension\\Context\\Environment\\InitialisedContextServiceEnvironment::bindCallee\(\) should return callable/' + - '/Method FriendsOfBehat\\SymfonyExtension\\Context\\Environment\\InitialisedSymfonyExtensionEnvironment::bindCallee\(\) should return callable/' - '/Strict comparison using === between 0\|1 and 2 will always evaluate to false\./' diff --git a/src/Context/Environment/Handler/ContextServiceEnvironmentHandler.php b/src/Context/Environment/Handler/ContextServiceEnvironmentHandler.php index d64c1f6..887d264 100644 --- a/src/Context/Environment/Handler/ContextServiceEnvironmentHandler.php +++ b/src/Context/Environment/Handler/ContextServiceEnvironmentHandler.php @@ -13,21 +13,18 @@ declare(strict_types=1); namespace FriendsOfBehat\SymfonyExtension\Context\Environment\Handler; -use Behat\Behat\Context\Argument\ArgumentResolverFactory; -use Behat\Behat\Context\Argument\NullFactory; -use Behat\Behat\Context\Argument\SuiteScopedResolverFactory; -use Behat\Behat\Context\Argument\SuiteScopedResolverFactoryAdapter; use Behat\Behat\Context\Context; -use Behat\Behat\Context\ContextClass\ClassResolver; -use Behat\Behat\Context\ContextFactory; +use Behat\Behat\Context\Environment\ContextEnvironment; +use Behat\Behat\Context\Environment\InitializedContextEnvironment; use Behat\Testwork\Environment\Environment; use Behat\Testwork\Environment\Exception\EnvironmentIsolationException; use Behat\Testwork\Environment\Handler\EnvironmentHandler; use Behat\Testwork\Suite\Exception\SuiteConfigurationException; +use Behat\Testwork\Suite\GenericSuite; use Behat\Testwork\Suite\Suite; use FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle; -use FriendsOfBehat\SymfonyExtension\Context\Environment\InitialisedContextServiceEnvironment; -use FriendsOfBehat\SymfonyExtension\Context\Environment\UninitialisedContextServiceEnvironment; +use FriendsOfBehat\SymfonyExtension\Context\Environment\InitialisedSymfonyExtensionEnvironment; +use FriendsOfBehat\SymfonyExtension\Context\Environment\UninitialisedSymfonyExtensionEnvironment; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\KernelInterface; @@ -36,29 +33,13 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler /** @var KernelInterface */ private $symfonyKernel; - /** @var ClassResolver[] */ - private $classResolvers = []; + /** @var EnvironmentHandler */ + private $decoratedEnvironmentHandler; - /** @var ContextFactory */ - private $contextFactory; - - /** @var ArgumentResolverFactory */ - private $resolverFactory; - - /** - * @param ArgumentResolverFactory|SuiteScopedResolverFactory $resolverFactory - */ - public function __construct(KernelInterface $symfonyKernel, ContextFactory $factory, $resolverFactory = null) + public function __construct(KernelInterface $symfonyKernel, EnvironmentHandler $decoratedEnvironmentHandler) { $this->symfonyKernel = $symfonyKernel; - - $this->contextFactory = $factory; - - if ($resolverFactory && !$resolverFactory instanceof ArgumentResolverFactory) { - $resolverFactory = new SuiteScopedResolverFactoryAdapter($resolverFactory); - } - - $this->resolverFactory = $resolverFactory ?: new NullFactory(); + $this->decoratedEnvironmentHandler = $decoratedEnvironmentHandler; } public function supportsSuite(Suite $suite): bool @@ -68,21 +49,34 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler public function buildEnvironment(Suite $suite): Environment { - $environment = new UninitialisedContextServiceEnvironment($suite); - foreach ($this->getSuiteContextsServices($suite) as [$contextId, $contextArguments]) { - $environment->registerContextService($contextId, $this->getContextClass($contextId), $contextArguments); + $symfonyContexts = []; + + foreach ($this->getSuiteContextsServices($suite) as $serviceId) { + if (!$this->getContainer()->has($serviceId)) { + continue; + } + + $symfonyContexts[$serviceId] = get_class($this->getContainer()->get($serviceId)); } - return $environment; + $delegatedSuite = $this->cloneSuiteWithoutContexts($suite, array_keys($symfonyContexts)); + + $delegatedEnvironment = $this->decoratedEnvironmentHandler->buildEnvironment($delegatedSuite); + + if (!$delegatedEnvironment instanceof ContextEnvironment) { + throw new \Exception(); + } + + return new UninitialisedSymfonyExtensionEnvironment($suite, $symfonyContexts, $delegatedEnvironment); } public function supportsEnvironmentAndSubject(Environment $environment, $testSubject = null): bool { - return $environment instanceof UninitialisedContextServiceEnvironment; + return $environment instanceof UninitialisedSymfonyExtensionEnvironment; } /** - * @param UninitialisedContextServiceEnvironment $uninitializedEnvironment + * @param UninitialisedSymfonyExtensionEnvironment $uninitializedEnvironment * * @throws EnvironmentIsolationException */ @@ -90,54 +84,78 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler { $this->assertEnvironmentCanBeIsolated($uninitializedEnvironment, $testSubject); - $environment = new InitialisedContextServiceEnvironment($uninitializedEnvironment->getSuite()); - $resolvers = $this->resolverFactory->createArgumentResolvers($environment); + $environment = new InitialisedSymfonyExtensionEnvironment($uninitializedEnvironment->getSuite()); - foreach ($uninitializedEnvironment->getContextServicesWithArguments() as $contextId => $arguments) { + foreach ($uninitializedEnvironment->getServices() as $serviceId) { /** @var Context $context */ - $context = $this->getContext($contextId, $arguments, $resolvers); + $context = $this->getContainer()->get($serviceId); + + $environment->registerContext($context); + } + + $delegatedEnvironment = $this->decoratedEnvironmentHandler->isolateEnvironment($uninitializedEnvironment->getDelegatedEnvironment()); + + if (!$delegatedEnvironment instanceof InitializedContextEnvironment) { + throw new \Exception(); + } + + foreach ($delegatedEnvironment->getContexts() as $context) { $environment->registerContext($context); } return $environment; } - public function registerClassResolver(ClassResolver $classResolver): void - { - $this->classResolvers[] = $classResolver; - } - /** - * @return array[] + * @return string[] * * @throws SuiteConfigurationException If "contexts" setting is not an array */ private function getSuiteContextsServices(Suite $suite): array { - $contextsServices = $suite->getSetting('contexts'); + $contexts = $suite->getSetting('contexts'); - if (!is_array($contextsServices)) { + if (!is_array($contexts)) { throw new SuiteConfigurationException(sprintf( '"contexts" setting of the "%s" suite is expected to be an array, %s given.', $suite->getName(), - gettype($contextsServices) + gettype($contexts) ), $suite->getName()); } - return array_map( - function ($context): array { - $class = $context; - $arguments = []; + return array_map([$this, 'normaliseContext'], $contexts); + } - if (is_array($context)) { - $class = current(array_keys($context)); - $arguments = $context[$class]; - } + private function cloneSuiteWithoutContexts(Suite $suite, array $contextsToRemove): Suite + { + $contexts = $suite->getSetting('contexts'); - return [$class, $arguments]; - }, - $contextsServices - ); + if (!is_array($contexts)) { + throw new SuiteConfigurationException(sprintf( + '"contexts" setting of the "%s" suite is expected to be an array, %s given.', + $suite->getName(), + gettype($contexts) + ), $suite->getName()); + } + + $contexts = array_filter($contexts, function ($context) use ($contextsToRemove): bool { + return !in_array($this->normaliseContext($context), $contextsToRemove, true); + }); + + return new GenericSuite($suite->getName(), array_merge($suite->getSettings(), ['contexts' => $contexts])); + } + + private function normaliseContext($context): string + { + if (is_array($context)) { + return current(array_keys($context)); + } + + if (is_string($context)) { + return $context; + } + + throw new \Exception(); } /** @@ -154,60 +172,6 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler } } - private function resolveContextId(string $contextId): string - { - foreach ($this->classResolvers as $resolver) { - if ($resolver->supportsClass($contextId)) { - return $resolver->resolveClass($contextId); - } - } - - return $contextId; - } - - private function getContextClass(string $contextId): string - { - $contextId = $this->resolveContextId($contextId); - - if ($this->getContainer()->has($contextId)) { - return get_class($this->getContainer()->get($contextId)); - } - - $class = '\\' . ltrim($contextId, '\\'); - - if (class_exists($class)) { - return $class; - } - - throw new \DomainException(sprintf('There is no service or class "%s".', $contextId)); - } - - private function getContext(string $contextId, array $arguments = [], array $resolvers = []): Context - { - $contextId = $this->resolveContextId($contextId); - - $class = '\\' . ltrim($contextId, '\\'); - - if ($this->getContainer()->has($contextId)) { - $context = $this->getContainer()->get($contextId); - } elseif (class_exists($class)) { - $context = $this->contextFactory->createContext($class, $arguments, $resolvers); - } else { - throw new \DomainException(sprintf('There is no service or class "%s".', $contextId)); - } - - if (!$context instanceof Context) { - throw new \DomainException(sprintf( - 'Context "%s" referenced as "%s" needs to implement "%s".', - get_class($context), - $contextId, - Context::class - )); - } - - return $context; - } - private function getContainer(): ContainerInterface { try { diff --git a/src/Context/Environment/InitialisedContextServiceEnvironment.php b/src/Context/Environment/InitialisedSymfonyExtensionEnvironment.php similarity index 95% rename from src/Context/Environment/InitialisedContextServiceEnvironment.php rename to src/Context/Environment/InitialisedSymfonyExtensionEnvironment.php index d8c1904..609bbe4 100644 --- a/src/Context/Environment/InitialisedContextServiceEnvironment.php +++ b/src/Context/Environment/InitialisedSymfonyExtensionEnvironment.php @@ -22,7 +22,7 @@ use FriendsOfBehat\SymfonyExtension\Context\Environment\Handler\ContextServiceEn /** * @see ContextServiceEnvironmentHandler */ -final class InitialisedContextServiceEnvironment implements ContextServiceEnvironment +final class InitialisedSymfonyExtensionEnvironment implements SymfonyExtensionEnvironment { /** @var Suite */ private $suite; diff --git a/src/Context/Environment/ContextServiceEnvironment.php b/src/Context/Environment/SymfonyExtensionEnvironment.php similarity index 89% rename from src/Context/Environment/ContextServiceEnvironment.php rename to src/Context/Environment/SymfonyExtensionEnvironment.php index ebf7b95..b75b232 100644 --- a/src/Context/Environment/ContextServiceEnvironment.php +++ b/src/Context/Environment/SymfonyExtensionEnvironment.php @@ -19,6 +19,6 @@ use FriendsOfBehat\SymfonyExtension\Context\Environment\Handler\ContextServiceEn /** * @see ContextServiceEnvironmentHandler */ -interface ContextServiceEnvironment extends ContextEnvironment +interface SymfonyExtensionEnvironment extends ContextEnvironment { } diff --git a/src/Context/Environment/UninitialisedContextServiceEnvironment.php b/src/Context/Environment/UninitialisedContextServiceEnvironment.php deleted file mode 100644 index abc8a4a..0000000 --- a/src/Context/Environment/UninitialisedContextServiceEnvironment.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FriendsOfBehat\SymfonyExtension\Context\Environment; - -use Behat\Testwork\Environment\StaticEnvironment; -use FriendsOfBehat\SymfonyExtension\Context\Environment\Handler\ContextServiceEnvironmentHandler; - -/** - * @see ContextServiceEnvironmentHandler - */ -final class UninitialisedContextServiceEnvironment extends StaticEnvironment implements ContextServiceEnvironment -{ - /** @var string[] */ - private $contextServices = []; - - public function registerContextService(string $serviceId, string $serviceClass, array $arguments = []): void - { - $this->contextServices[$serviceId] = [ - 'class' => $serviceClass, - 'arguments' => $arguments, - ]; - } - - public function getContextServices(): array - { - return array_keys($this->contextServices); - } - - public function hasContexts(): bool - { - return count($this->contextServices) > 0; - } - - public function getContextClasses(): array - { - return array_map(function (array $contextDetails): string { - return $contextDetails['class']; - }, $this->contextServices); - } - - public function hasContextClass($class): bool - { - return in_array($class, $this->getContextClasses(), true); - } - - public function getContextServicesWithArguments(): iterable - { - foreach ($this->contextServices as $contextDetails) { - yield $contextDetails['class'] => $contextDetails['arguments']; - } - } -} diff --git a/src/Context/Environment/UninitialisedSymfonyExtensionEnvironment.php b/src/Context/Environment/UninitialisedSymfonyExtensionEnvironment.php new file mode 100644 index 0000000..dbdebb5 --- /dev/null +++ b/src/Context/Environment/UninitialisedSymfonyExtensionEnvironment.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FriendsOfBehat\SymfonyExtension\Context\Environment; + +use Behat\Behat\Context\Environment\ContextEnvironment; +use Behat\Testwork\Environment\StaticEnvironment; +use Behat\Testwork\Suite\Suite; +use FriendsOfBehat\SymfonyExtension\Context\Environment\Handler\ContextServiceEnvironmentHandler; + +/** + * @see ContextServiceEnvironmentHandler + */ +final class UninitialisedSymfonyExtensionEnvironment extends StaticEnvironment implements SymfonyExtensionEnvironment +{ + /** @var string[] */ + private $contexts; + + /** @var ContextEnvironment|null */ + private $delegatedEnvironment; + + public function __construct(Suite $suite, array $contexts, ContextEnvironment $delegatedEnvironment) + { + parent::__construct($suite); + + $this->contexts = $contexts; + $this->delegatedEnvironment = $delegatedEnvironment; + } + + public function getServices(): array + { + return array_keys($this->contexts); + } + + public function hasContexts(): bool + { + return count($this->contexts) > 0 || $this->delegatedEnvironment->hasContexts(); + } + + public function getContextClasses(): array + { + return array_merge(array_values($this->contexts), $this->delegatedEnvironment->getContextClasses()); + } + + public function hasContextClass($class): bool + { + return in_array($class, $this->contexts, true) || $this->delegatedEnvironment->hasContextClass($class); + } + + public function getDelegatedEnvironment(): ContextEnvironment + { + return $this->delegatedEnvironment; + } +} diff --git a/src/ServiceContainer/SymfonyExtension.php b/src/ServiceContainer/SymfonyExtension.php index bf99aa7..fdcafd5 100644 --- a/src/ServiceContainer/SymfonyExtension.php +++ b/src/ServiceContainer/SymfonyExtension.php @@ -88,7 +88,6 @@ final class SymfonyExtension implements Extension public function process(ContainerBuilder $container): void { - $this->processEnvironmentHandler($container); } private function registerMinkDriver(ExtensionManager $extensionManager): void @@ -137,8 +136,7 @@ final class SymfonyExtension implements Extension { $definition = new Definition(ContextServiceEnvironmentHandler::class, [ new Reference(self::KERNEL_ID), - new Reference(ContextExtension::FACTORY_ID), - new Reference(ContextExtension::AGGREGATE_RESOLVER_FACTORY_ID) + new Reference('environment.handler.context'), ]); $definition->addTag(EnvironmentExtension::HANDLER_TAG, ['priority' => 128]); @@ -247,13 +245,4 @@ final class SymfonyExtension implements Extension return is_string($bootstrap) ? $bootstrap : null; } - - private function processEnvironmentHandler(ContainerBuilder $container): void - { - $definition = $container->findDefinition('fob_symfony.environment_handler.context_service'); - - foreach ($container->findTaggedServiceIds(ContextExtension::CLASS_RESOLVER_TAG) as $serviceId => $tags) { - $definition->addMethodCall('registerClassResolver', [$container->getDefinition($serviceId)]); - } - } }