container = $container; $this->dependencyOrderingService = $dependencyOrderingService; $this->cache = $cache; $this->baseCacheIdentifier = '_' . $baseCacheIdentifier; } /** * Returns the middleware stack registered in all packages within Configuration/RequestMiddlewares.php * which are sorted by given dependency requirements * * @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException * @throws \TYPO3\CMS\Core\Exception */ public function resolve(string $stackName): \ArrayObject { return new \ArrayObject($this->getFromCache($stackName) ?? $this->computeMiddlewareStack($stackName)); } protected function getFromCache(string $stackName): ?array { $cacheIdentifier = $this->getCacheIdentifier($stackName); if (!$this->cache->has($cacheIdentifier)) { return null; } $result = $this->cache->require($cacheIdentifier); if ($result === false) { // Cache entry has been removed in the meantime return null; } if (!is_array($result)) { // An invalid result is to be ignored (cache will be recreated) return null; } return $result; } protected function computeMiddlewareStack(string $stackName): array { $allMiddlewares = $this->loadConfiguration(); $middlewares = $this->sanitizeMiddlewares($allMiddlewares); // Ensure that we create a cache for $stackName, even if the stack is empty if (!isset($middlewares[$stackName])) { $middlewares[$stackName] = []; } foreach ($middlewares as $stack => $middlewaresOfStack) { $this->cache->set($this->getCacheIdentifier($stack), 'return ' . var_export($middlewaresOfStack, true) . ';'); } return $middlewares[$stackName]; } /** * Lazy load configuration from the container */ protected function loadConfiguration(): \ArrayObject { return $this->container->get('middlewares'); } /** * Order each stack and sanitize to a plain array */ protected function sanitizeMiddlewares(\ArrayObject $allMiddlewares): array { $middlewares = []; foreach ($allMiddlewares as $stack => $middlewaresOfStack) { $middlewaresOfStack = $this->dependencyOrderingService->orderByDependencies($middlewaresOfStack); $sanitizedMiddlewares = []; foreach ($middlewaresOfStack as $name => $middleware) { if (isset($middleware['disabled']) && $middleware['disabled'] === true) { // Skip this middleware if disabled by configuration continue; } $sanitizedMiddlewares[$name] = $middleware['target']; } // Order reverse, MiddlewareDispatcher executes the last middleware in the array first (last in, first out). $middlewares[$stack] = array_reverse($sanitizedMiddlewares); } return $middlewares; } protected function getCacheIdentifier(string $stackName): string { return 'middlewares_' . $stackName . $this->baseCacheIdentifier; } #[AsEventListener('typo3-core/middlewares')] public function warmupCaches(CacheWarmupEvent $event): void { if ($event->hasGroup('system')) { $allMiddlewares = $this->loadConfiguration(); $middlewares = $this->sanitizeMiddlewares($allMiddlewares); foreach ($middlewares as $stack => $middlewaresOfStack) { $this->cache->set($this->getCacheIdentifier($stack), 'return ' . var_export($middlewaresOfStack, true) . ';'); } } } }