VariableFrontend::class, 'backend' => Typo3DatabaseBackend::class, 'options' => [], 'groups' => ['all'], ]; public function __construct( protected bool $disableCaching = false ) {} /** * Sets configurations for caches. The key of each entry specifies the * cache identifier and the value is an array of configuration options. * Possible options are: * * frontend * backend * backendOptions * * If one of the options is not specified, the default value is assumed. * Existing cache configurations are preserved. * * @param array $cacheConfigurations The cache configurations to set * @throws \InvalidArgumentException If $cacheConfigurations is not an array */ public function setCacheConfigurations(array $cacheConfigurations): void { $newConfiguration = []; foreach ($cacheConfigurations as $identifier => $configuration) { if (empty($identifier)) { throw new \InvalidArgumentException('A cache identifier was not set.', 1596980032); } if (!is_array($configuration)) { throw new \InvalidArgumentException('The cache configuration for cache "' . $identifier . '" was not an array as expected.', 1231259656); } $newConfiguration[$identifier] = $configuration; } $this->cacheConfigurations = $newConfiguration; } /** * Registers a cache so it can be retrieved at a later point. * * @param array $groups Cache groups to be associated to the cache * @throws DuplicateIdentifierException if a cache with the given identifier has already been registered. */ public function registerCache(FrontendInterface $cache, array $groups = []): void { $identifier = $cache->getIdentifier(); if (isset($this->caches[$identifier])) { throw new DuplicateIdentifierException('A cache with identifier "' . $identifier . '" has already been registered.', 1203698223); } $this->caches[$identifier] = $cache; foreach ($groups as $groupIdentifier) { $this->cacheGroups[$groupIdentifier][] = $identifier; } } /** * Returns the cache specified by $identifier * * @throws NoSuchCacheException */ public function getCache(string $identifier): FrontendInterface { if ($this->hasCache($identifier) === false) { throw new NoSuchCacheException('A cache with identifier "' . $identifier . '" does not exist.', 1203699034); } if (!isset($this->caches[$identifier])) { $this->createCache($identifier); } return $this->caches[$identifier]; } /** * Checks if the specified cache has been registered. */ public function hasCache(string $identifier): bool { return isset($this->caches[$identifier]) || isset($this->cacheConfigurations[$identifier]); } /** * Flushes all registered caches */ public function flushCaches(): void { $this->createAllCaches(); foreach ($this->caches as $cache) { $cache->flush(); } } /** * Flushes all registered caches of a specific group * * @throws NoSuchCacheGroupException */ public function flushCachesInGroup(string $groupIdentifier): void { $this->createAllCaches(); if (!isset($this->cacheGroups[$groupIdentifier])) { throw new NoSuchCacheGroupException('No cache in the specified group \'' . $groupIdentifier . '\'', 1390334120); } foreach ($this->cacheGroups[$groupIdentifier] as $cacheIdentifier) { if (isset($this->caches[$cacheIdentifier])) { $this->caches[$cacheIdentifier]->flush(); } } } /** * Flushes entries tagged by the specified tag of all registered * caches of a specific group. * * @throws NoSuchCacheGroupException */ public function flushCachesInGroupByTag(string $groupIdentifier, string $tag): void { if (empty($tag)) { return; } $this->createAllCaches(); if (!isset($this->cacheGroups[$groupIdentifier])) { throw new NoSuchCacheGroupException('No cache in the specified group \'' . $groupIdentifier . '\'', 1390337129); } foreach ($this->cacheGroups[$groupIdentifier] as $cacheIdentifier) { if (isset($this->caches[$cacheIdentifier])) { $this->caches[$cacheIdentifier]->flushByTag($tag); } } } /** * Flushes entries tagged by any of the specified tags in all registered * caches of a specific group. * * @throws NoSuchCacheGroupException */ public function flushCachesInGroupByTags(string $groupIdentifier, array $tags): void { if (empty($tags)) { return; } $this->createAllCaches(); if (!isset($this->cacheGroups[$groupIdentifier])) { throw new NoSuchCacheGroupException('No cache in the specified group \'' . $groupIdentifier . '\'', 1390337130); } foreach ($this->cacheGroups[$groupIdentifier] as $cacheIdentifier) { if (isset($this->caches[$cacheIdentifier])) { $this->caches[$cacheIdentifier]->flushByTags($tags); } } } /** * Flushes entries tagged by the specified tag of all registered caches. */ public function flushCachesByTag(string $tag): void { $this->createAllCaches(); foreach ($this->caches as $cache) { $cache->flushByTag($tag); } } /** * Flushes entries tagged by any of the specified tags in all registered caches. */ public function flushCachesByTags(array $tags): void { $this->createAllCaches(); foreach ($this->caches as $cache) { $cache->flushByTags($tags); } } /** * @return string[] * @internal */ public function getCacheGroups(): array { $groups = array_keys($this->cacheGroups); foreach ($this->cacheConfigurations as $config) { foreach ($config['groups'] ?? [] as $group) { if (!in_array($group, $groups, true)) { $groups[] = $group; } } } return $groups; } public function handleCacheFlushEvent(CacheFlushEvent $event): void { foreach ($event->getGroups() as $group) { $this->flushCachesInGroup($group); } } protected function createAllCaches(): void { foreach ($this->cacheConfigurations as $identifier => $_) { if (!isset($this->caches[$identifier])) { $this->createCache($identifier); } } } /** * Instantiates the cache for $identifier. * * @throws DuplicateIdentifierException * @throws InvalidBackendException * @throws InvalidCacheException */ protected function createCache(string $identifier): void { if (isset($this->cacheConfigurations[$identifier]['frontend'])) { $frontend = $this->cacheConfigurations[$identifier]['frontend']; } else { $frontend = $this->defaultCacheConfiguration['frontend']; } if (isset($this->cacheConfigurations[$identifier]['backend'])) { $backend = $this->cacheConfigurations[$identifier]['backend']; } else { $backend = $this->defaultCacheConfiguration['backend']; } if (isset($this->cacheConfigurations[$identifier]['options'])) { $backendOptions = $this->cacheConfigurations[$identifier]['options']; } else { $backendOptions = $this->defaultCacheConfiguration['options']; } // Normalize legacy non-bool 'compression' values for strictly typed backend setters. if (isset($backendOptions['compression']) && !is_bool($backendOptions['compression'])) { $backendOptions['compression'] = (bool)$backendOptions['compression']; } if ($this->disableCaching && $backend !== TransientMemoryBackend::class) { $backend = NullBackend::class; $backendOptions = []; } // Add the cache identifier to the groups that it should be attached to, or use the default ones. if (isset($this->cacheConfigurations[$identifier]['groups']) && is_array($this->cacheConfigurations[$identifier]['groups'])) { $assignedGroups = $this->cacheConfigurations[$identifier]['groups']; } else { $assignedGroups = $this->defaultCacheConfiguration['groups']; } foreach ($assignedGroups as $groupIdentifier) { if (!isset($this->cacheGroups[$groupIdentifier])) { $this->cacheGroups[$groupIdentifier] = []; } $this->cacheGroups[$groupIdentifier][] = $identifier; } // New operator used on purpose: This class is required early during // bootstrap before makeInstance() is properly set up $backend = '\\' . ltrim($backend, '\\'); $backendInstance = new $backend($backendOptions); if (!$backendInstance instanceof BackendInterface) { throw new InvalidBackendException('"' . $backend . '" is not a valid cache backend object.', 1464550977); } if (is_callable([$backendInstance, 'initializeObject'])) { $backendInstance->initializeObject(); } // New used on purpose, see comment above $frontendInstance = new $frontend($identifier, $backendInstance); if (!$frontendInstance instanceof FrontendInterface) { throw new InvalidCacheException('"' . $frontend . '" is not a valid cache frontend object.', 1464550984); } if (is_callable([$frontendInstance, 'initializeObject'])) { $frontendInstance->initializeObject(); } $this->registerCache($frontendInstance); } }