vendor/friendsofsymfony/http-cache-bundle/src/EventListener/TagListener.php line 88

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\HttpCacheBundle\CacheManager;
  12. use FOS\HttpCacheBundle\Configuration\Tag;
  13. use FOS\HttpCacheBundle\Http\RuleMatcherInterface;
  14. use FOS\HttpCacheBundle\Http\SymfonyResponseTagger;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  17. use Symfony\Component\HttpFoundation\Request;
  18. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  19. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  20. use Symfony\Component\HttpKernel\Kernel;
  21. use Symfony\Component\HttpKernel\KernelEvents;
  22. if (Kernel::MAJOR_VERSION >= 5) {
  23.     class_alias(ResponseEvent::class, 'FOS\HttpCacheBundle\EventListener\TagResponseEvent');
  24. } else {
  25.     class_alias(FilterResponseEvent::class, 'FOS\HttpCacheBundle\EventListener\TagResponseEvent');
  26. }
  27. /**
  28.  * Event handler for the cache tagging tags.
  29.  *
  30.  * @author David de Boer <david@driebit.nl>
  31.  */
  32. class TagListener extends AbstractRuleListener implements EventSubscriberInterface
  33. {
  34.     /**
  35.      * @var CacheManager
  36.      */
  37.     private $cacheManager;
  38.     /**
  39.      * @var SymfonyResponseTagger
  40.      */
  41.     private $symfonyResponseTagger;
  42.     /**
  43.      * @var ExpressionLanguage|null
  44.      */
  45.     private $expressionLanguage;
  46.     /**
  47.      * @var RuleMatcherInterface
  48.      */
  49.     private $mustInvalidateRule;
  50.     /**
  51.      * @var RuleMatcherInterface
  52.      */
  53.     private $cacheableRule;
  54.     /**
  55.      * Constructor.
  56.      */
  57.     public function __construct(
  58.         CacheManager $cacheManager,
  59.         SymfonyResponseTagger $tagHandler,
  60.         RuleMatcherInterface $cacheableRule,
  61.         RuleMatcherInterface $mustInvalidateRule,
  62.         ExpressionLanguage $expressionLanguage null
  63.     ) {
  64.         $this->cacheManager $cacheManager;
  65.         $this->symfonyResponseTagger $tagHandler;
  66.         $this->cacheableRule $cacheableRule;
  67.         $this->mustInvalidateRule $mustInvalidateRule;
  68.         $this->expressionLanguage $expressionLanguage;
  69.     }
  70.     /**
  71.      * Process the _tags request attribute, which is set when using the Tag
  72.      * annotation.
  73.      *
  74.      * - For a safe (GET or HEAD) request, the tags are set on the response.
  75.      * - For a non-safe request, the tags will be invalidated.
  76.      */
  77.     public function onKernelResponse(TagResponseEvent $event)
  78.     {
  79.         $request $event->getRequest();
  80.         $response $event->getResponse();
  81.         if (!$this->cacheableRule->matches($request$response)
  82.             && !$this->mustInvalidateRule->matches($request$response)
  83.         ) {
  84.             return;
  85.         }
  86.         $tags $this->getAnnotationTags($request);
  87.         $configuredTags $this->matchRule($request);
  88.         if ($configuredTags) {
  89.             $tags array_merge($tags$configuredTags['tags']);
  90.             foreach ($configuredTags['expressions'] as $expression) {
  91.                 $tags[] = $this->evaluateTag($expression$request);
  92.             }
  93.         }
  94.         if ($this->cacheableRule->matches($request$response)) {
  95.             // For safe requests (GET and HEAD), set cache tags on response
  96.             $this->symfonyResponseTagger->addTags($tags);
  97.             // BC for symfony < 5.3
  98.             if (method_exists($event'isMainRequest') ? $event->isMainRequest() : $event->isMasterRequest()) {
  99.                 $this->symfonyResponseTagger->tagSymfonyResponse($response);
  100.             }
  101.         } elseif (count($tags)
  102.             && $this->mustInvalidateRule->matches($request$response)
  103.         ) {
  104.             $this->cacheManager->invalidateTags($tags);
  105.         }
  106.     }
  107.     /**
  108.      * {@inheritdoc}
  109.      */
  110.     public static function getSubscribedEvents()
  111.     {
  112.         return [
  113.             KernelEvents::RESPONSE => 'onKernelResponse',
  114.         ];
  115.     }
  116.     /**
  117.      * Get the tags from the annotations on the controller that was used in the
  118.      * request.
  119.      *
  120.      * @return array List of tags affected by the request
  121.      */
  122.     private function getAnnotationTags(Request $request)
  123.     {
  124.         // Check for _tag request attribute that is set when using @Tag
  125.         // annotation
  126.         /** @var $tagConfigurations Tag[] */
  127.         if (!$tagConfigurations $request->attributes->get('_tag')) {
  128.             return [];
  129.         }
  130.         $tags = [];
  131.         foreach ($tagConfigurations as $tagConfiguration) {
  132.             if (null !== $tagConfiguration->getExpression()) {
  133.                 $tags[] = $this->evaluateTag(
  134.                     $tagConfiguration->getExpression(),
  135.                     $request
  136.                 );
  137.             } else {
  138.                 $tags array_merge($tags$tagConfiguration->getTags());
  139.             }
  140.         }
  141.         return $tags;
  142.     }
  143.     /**
  144.      * Evaluate a tag that contains expressions.
  145.      *
  146.      * @param string $expression
  147.      *
  148.      * @return string Evaluated tag
  149.      */
  150.     private function evaluateTag($expressionRequest $request)
  151.     {
  152.         $values $request->attributes->all();
  153.         // if there is an attribute called "request", it needs to be accessed through the request.
  154.         $values['request'] = $request;
  155.         return $this->getExpressionLanguage()->evaluate($expression$values);
  156.     }
  157.     private function getExpressionLanguage(): ExpressionLanguage
  158.     {
  159.         if (!$this->expressionLanguage) {
  160.             // the expression comes from controller annotations, we can't detect whether they use expressions while building the configuration
  161.             if (!class_exists(ExpressionLanguage::class)) {
  162.                 throw new \RuntimeException('Using the tag annotation requires the '.ExpressionLanguage::class.' to be available.');
  163.             }
  164.             $this->expressionLanguage = new ExpressionLanguage();
  165.         }
  166.         return $this->expressionLanguage;
  167.     }
  168. }