diff --git a/docs/book/v3/application-integration/usage-in-a-laminas-mvc-application.md b/docs/book/v3/application-integration/usage-in-a-laminas-mvc-application.md index 68712387..0652f774 100644 --- a/docs/book/v3/application-integration/usage-in-a-laminas-mvc-application.md +++ b/docs/book/v3/application-integration/usage-in-a-laminas-mvc-application.md @@ -37,7 +37,11 @@ return [ ]; ``` -The factory `Laminas\Cache\Service\StorageCacheAbstractServiceFactory` uses the configuration, searches for the configuration key `caches` and creates the storage adapters using the discovered configuration. +The factory `Laminas\Cache\Service\StorageCacheAbstractServiceFactory` uses the configuration, searches for the configuration key `caches` and creates the storage adapters using the discovered configuration. + +WARNING: **Cache Named `config` Is Not Possible** +A cache named `config` is not possible due to internal service conflicts with MVC configuration. +The service named `config` is reserved for project configuration and thus cannot be used with the `caches` configuration. ## Create Controller diff --git a/src/Command/DeprecatedStorageFactoryConfigurationCheckCommandFactory.php b/src/Command/DeprecatedStorageFactoryConfigurationCheckCommandFactory.php index 73bdef3e..e605eed4 100644 --- a/src/Command/DeprecatedStorageFactoryConfigurationCheckCommandFactory.php +++ b/src/Command/DeprecatedStorageFactoryConfigurationCheckCommandFactory.php @@ -19,6 +19,21 @@ final class DeprecatedStorageFactoryConfigurationCheckCommandFactory { public function __invoke(ContainerInterface $container): DeprecatedStorageFactoryConfigurationCheckCommand { + $config = $this->detectConfigFromContainer($container); + + $schemaDetector = new DeprecatedSchemaDetector(); + return new DeprecatedStorageFactoryConfigurationCheckCommand( + $config, + $schemaDetector + ); + } + + private function detectConfigFromContainer(ContainerInterface $container): ArrayAccess + { + if (! $container->has('config')) { + return new ArrayObject([]); + } + $config = $container->get('config'); if (is_array($config)) { $config = new ArrayObject($config); @@ -28,10 +43,6 @@ public function __invoke(ContainerInterface $container): DeprecatedStorageFactor throw new RuntimeException('Configuration from container must be either `ArrayAccess` or an array.'); } - $schemaDetector = new DeprecatedSchemaDetector(); - return new DeprecatedStorageFactoryConfigurationCheckCommand( - $config, - $schemaDetector - ); + return $config; } } diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 19fddb36..ad4a4a50 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -10,10 +10,14 @@ use Laminas\Cache\Service\StoragePluginFactory; use Laminas\Cache\Service\StoragePluginFactoryFactory; use Laminas\Cache\Service\StoragePluginFactoryInterface; +use Laminas\ServiceManager\ServiceManager; use Symfony\Component\Console\Command\Command; use function class_exists; +/** + * @psalm-import-type ServiceManagerConfiguration from ServiceManager + */ class ConfigProvider { public const ADAPTER_PLUGIN_MANAGER_CONFIGURATION_KEY = 'storage_adapters'; @@ -34,7 +38,7 @@ public function __invoke() /** * Return default service mappings for laminas-cache. * - * @return array + * @return ServiceManagerConfiguration */ public function getDependencyConfig() { diff --git a/src/Service/StorageCacheAbstractServiceFactory.php b/src/Service/StorageCacheAbstractServiceFactory.php index 7cbc8ef2..a1b8e24a 100644 --- a/src/Service/StorageCacheAbstractServiceFactory.php +++ b/src/Service/StorageCacheAbstractServiceFactory.php @@ -14,7 +14,8 @@ */ class StorageCacheAbstractServiceFactory implements AbstractFactoryInterface { - public const CACHES_CONFIGURATION_KEY = 'caches'; + public const CACHES_CONFIGURATION_KEY = 'caches'; + private const RESERVED_CONFIG_SERVICE_NAME = 'config'; /** @var array|null */ protected $config; @@ -32,6 +33,10 @@ class StorageCacheAbstractServiceFactory implements AbstractFactoryInterface */ public function canCreate(ContainerInterface $container, $requestedName) { + if ($requestedName === self::RESERVED_CONFIG_SERVICE_NAME) { + return false; + } + $config = $this->getConfig($container); if (empty($config)) { return false; diff --git a/test/Service/ConfigProviderIntegrationTest.php b/test/Service/ConfigProviderIntegrationTest.php new file mode 100644 index 00000000..518a04c0 --- /dev/null +++ b/test/Service/ConfigProviderIntegrationTest.php @@ -0,0 +1,92 @@ +container = $this->createContainer(); + } + + private function createContainer(): ContainerInterface + { + return new ServiceManager((new ConfigProvider())->getDependencyConfig()); + } + + /** + * @dataProvider servicesProvidedByConfigProvider + */ + public function testContainerCanProvideRegisteredServices(string $serviceName): void + { + $instance = $this->container->get($serviceName); + self::assertIsObject($instance); + } + + /** + * @return Generator + */ + public function servicesProvidedByConfigProvider(): Generator + { + $provider = new ConfigProvider(); + $dependencies = $provider->getDependencyConfig(); + + $factories = $dependencies['factories'] ?? []; + self::assertArrayIsMappedWithStrings($factories); + $invokables = $dependencies['invokables'] ?? []; + self::assertArrayIsMappedWithStrings($invokables); + $services = $dependencies['services'] ?? []; + self::assertArrayIsMappedWithStrings($services); + $aliases = $dependencies['aliases'] ?? []; + self::assertArrayIsMappedWithStrings($aliases); + + $serviceNames = array_unique( + array_merge( + array_keys($factories), + array_keys($invokables), + array_keys($services), + array_keys($aliases), + ), + ); + + foreach ($serviceNames as $serviceName) { + yield $serviceName => [$serviceName]; + } + } + + /** + * @psalm-assert array $array + */ + private static function assertArrayIsMappedWithStrings(mixed $array): void + { + if (! is_array($array)) { + throw new InvalidArgumentException('Expecting value to be an array.'); + } + + foreach (array_keys($array) as $value) { + if (is_string($value)) { + continue; + } + + throw new InvalidArgumentException('Expecting all values to are mapped with a string.'); + } + } +} diff --git a/test/Service/StorageCacheAbstractServiceFactoryTest.php b/test/Service/StorageCacheAbstractServiceFactoryTest.php index daad0607..f0542de3 100644 --- a/test/Service/StorageCacheAbstractServiceFactoryTest.php +++ b/test/Service/StorageCacheAbstractServiceFactoryTest.php @@ -94,6 +94,14 @@ public function testWillPassInvalidArgumentExceptionFromConfigurationValidityAss ($this->factory)($this->container, 'Foo'); } + public function testNeverCallsContainerWhenConfigServiceIsCheckedForCreation(): void + { + $container = $this->createMock(ContainerInterface::class); + $container->expects(self::never())->method(self::anything()); + + self::assertFalse($this->factory->canCreate($container, 'config')); + } + public function testInvalidCacheServiceNameWillBeIgnored(): void { self::assertFalse(