vendor/friendsofsymfony/http-cache-bundle/src/EventListener/InvalidationListener.php line 132

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the FOSHttpCacheBundle package.
  4.  *
  5.  * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace FOS\HttpCacheBundle\EventListener;
  11. use FOS\HttpCache\Exception\ExceptionCollection;
  12. use FOS\HttpCacheBundle\CacheManager;
  13. use FOS\HttpCacheBundle\Configuration\InvalidatePath;
  14. use FOS\HttpCacheBundle\Configuration\InvalidateRoute;
  15. use FOS\HttpCacheBundle\Http\RuleMatcherInterface;
  16. use Symfony\Component\Console\ConsoleEvents;
  17. use Symfony\Component\Console\Event\ConsoleEvent;
  18. use Symfony\Component\Console\Output\OutputInterface;
  19. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  20. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\HttpFoundation\Response;
  23. use Symfony\Component\HttpKernel\Event\PostResponseEvent;
  24. use Symfony\Component\HttpKernel\Event\TerminateEvent;
  25. use Symfony\Component\HttpKernel\Kernel;
  26. use Symfony\Component\HttpKernel\KernelEvents;
  27. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  28. if (Kernel::MAJOR_VERSION >= 5) {
  29.     class_alias(TerminateEvent::class, 'FOS\HttpCacheBundle\EventListener\InvalidationTerminateEvent');
  30. } else {
  31.     class_alias(PostResponseEvent::class, 'FOS\HttpCacheBundle\EventListener\InvalidationTerminateEvent');
  32. }
  33. /**
  34.  * On kernel.terminate event, this event handler invalidates routes for the
  35.  * current request and flushes the CacheManager.
  36.  *
  37.  * @author David de Boer <david@driebit.nl>
  38.  */
  39. class InvalidationListener extends AbstractRuleListener implements EventSubscriberInterface
  40. {
  41.     /**
  42.      * Cache manager.
  43.      *
  44.      * @var CacheManager
  45.      */
  46.     private $cacheManager;
  47.     /**
  48.      * Router.
  49.      *
  50.      * @var UrlGeneratorInterface
  51.      */
  52.     private $urlGenerator;
  53.     /**
  54.      * Router.
  55.      *
  56.      * @var ExpressionLanguage|null
  57.      */
  58.     private $expressionLanguage;
  59.     /**
  60.      * @var RuleMatcherInterface
  61.      */
  62.     private $mustInvalidateRule;
  63.     /**
  64.      * Constructor.
  65.      */
  66.     public function __construct(
  67.         CacheManager $cacheManager,
  68.         UrlGeneratorInterface $urlGenerator,
  69.         RuleMatcherInterface $mustInvalidateRule,
  70.         ExpressionLanguage $expressionLanguage null
  71.     ) {
  72.         $this->cacheManager $cacheManager;
  73.         $this->urlGenerator $urlGenerator;
  74.         $this->expressionLanguage $expressionLanguage;
  75.         $this->mustInvalidateRule $mustInvalidateRule;
  76.     }
  77.     /**
  78.      * Apply invalidators and flush cache manager.
  79.      *
  80.      * On kernel.terminate:
  81.      * - see if any invalidators apply to the current request and, if so, add
  82.      *   their routes to the cache manager;
  83.      * - flush the cache manager in order to send invalidation requests to the
  84.      *   HTTP cache.
  85.      */
  86.     public function onKernelTerminate(InvalidationTerminateEvent $event)
  87.     {
  88.         $request $event->getRequest();
  89.         $response $event->getResponse();
  90.         // Don't invalidate any caches if the request was unsuccessful
  91.         if ($this->mustInvalidateRule->matches($request$response)) {
  92.             $this->handleInvalidation($request$response);
  93.         }
  94.         try {
  95.             $this->cacheManager->flush();
  96.         } catch (ExceptionCollection $e) {
  97.             // swallow exception
  98.             // there is the fos_http_cache.event_listener.log to log them
  99.         }
  100.     }
  101.     /**
  102.      * Flush cache manager when kernel exception occurs.
  103.      */
  104.     public function onKernelException()
  105.     {
  106.         try {
  107.             $this->cacheManager->flush();
  108.         } catch (ExceptionCollection $e) {
  109.             // swallow exception
  110.             // there is the fos_http_cache.event_listener.log to log them
  111.         }
  112.     }
  113.     /**
  114.      * Flush cache manager when console terminates or errors.
  115.      *
  116.      * @throws ExceptionCollection If an exception occurs during flush
  117.      */
  118.     public function onConsoleTerminate(ConsoleEvent $event)
  119.     {
  120.         $num $this->cacheManager->flush();
  121.         if ($num && OutputInterface::VERBOSITY_VERBOSE <= $event->getOutput()->getVerbosity()) {
  122.             $event->getOutput()->writeln(sprintf('Sent %d invalidation request(s)'$num));
  123.         }
  124.     }
  125.     /**
  126.      * {@inheritdoc}
  127.      */
  128.     public static function getSubscribedEvents()
  129.     {
  130.         return [
  131.             KernelEvents::TERMINATE => 'onKernelTerminate',
  132.             KernelEvents::EXCEPTION => 'onKernelException',
  133.             ConsoleEvents::TERMINATE => 'onConsoleTerminate',
  134.         ];
  135.     }
  136.     /**
  137.      * Handle the invalidation annotations and configured invalidators.
  138.      */
  139.     private function handleInvalidation(Request $requestResponse $response)
  140.     {
  141.         // Check controller annotations
  142.         if ($paths $request->attributes->get('_invalidate_path')) {
  143.             $this->invalidatePaths($paths);
  144.         }
  145.         if ($routes $request->attributes->get('_invalidate_route')) {
  146.             $this->invalidateRoutes($routes$request);
  147.         }
  148.         // Check configured invalidators
  149.         if (!$invalidatorConfigs $this->matchRule($request$response)) {
  150.             return;
  151.         }
  152.         $requestParams $request->attributes->get('_route_params');
  153.         foreach ($invalidatorConfigs as $route => $config) {
  154.             $path $this->urlGenerator->generate($route$requestParams);
  155.             // If extra route parameters should be ignored, strip the query
  156.             // string generated by the Symfony router from the path
  157.             if (isset($config['ignore_extra_params'])
  158.                 && $config['ignore_extra_params']
  159.                 && $pos strpos($path'?')
  160.             ) {
  161.                 $path substr($path0$pos);
  162.             }
  163.             $this->cacheManager->invalidatePath($path);
  164.         }
  165.     }
  166.     /**
  167.      * Invalidate paths from annotations.
  168.      *
  169.      * @param array|InvalidatePath[] $pathConfigurations
  170.      */
  171.     private function invalidatePaths(array $pathConfigurations)
  172.     {
  173.         foreach ($pathConfigurations as $pathConfiguration) {
  174.             foreach ($pathConfiguration->getPaths() as $path) {
  175.                 $this->cacheManager->invalidatePath($path);
  176.             }
  177.         }
  178.     }
  179.     /**
  180.      * Invalidate routes from annotations.
  181.      *
  182.      * @param array|InvalidateRoute[] $routes
  183.      */
  184.     private function invalidateRoutes(array $routesRequest $request)
  185.     {
  186.         $values $request->attributes->all();
  187.         // if there is an attribute called "request", it needs to be accessed through the request.
  188.         $values['request'] = $request;
  189.         foreach ($routes as $route) {
  190.             $params = [];
  191.             if (null !== $route->getParams()) {
  192.                 // Iterate over route params and try to evaluate their values
  193.                 foreach ($route->getParams() as $key => $value) {
  194.                     if (is_array($value)) {
  195.                         $value $this->getExpressionLanguage()->evaluate($value['expression'], $values);
  196.                     }
  197.                     $params[$key] = $value;
  198.                 }
  199.             }
  200.             $this->cacheManager->invalidateRoute($route->getName(), $params);
  201.         }
  202.     }
  203.     private function getExpressionLanguage(): ExpressionLanguage
  204.     {
  205.         if (!$this->expressionLanguage) {
  206.             // the expression comes from controller annotations, we can't detect whether they use expressions while building the configuration
  207.             if (!class_exists(ExpressionLanguage::class)) {
  208.                 throw new \RuntimeException('Invalidation rules with expressions require '.ExpressionLanguage::class.' to be available.');
  209.             }
  210.             $this->expressionLanguage = new ExpressionLanguage();
  211.         }
  212.         return $this->expressionLanguage;
  213.     }
  214. }