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

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\HttpKernelInterface;
  21. use Symfony\Component\HttpKernel\Kernel;
  22. use Symfony\Component\HttpKernel\KernelEvents;
  23. if (Kernel::MAJOR_VERSION >= 5) {
  24.     class_alias(ResponseEvent::class, 'FOS\HttpCacheBundle\EventListener\TagResponseEvent');
  25. } else {
  26.     class_alias(FilterResponseEvent::class, 'FOS\HttpCacheBundle\EventListener\TagResponseEvent');
  27. }
  28. /**
  29.  * Event handler for the cache tagging tags.
  30.  *
  31.  * @author David de Boer <david@driebit.nl>
  32.  */
  33. class TagListener extends AbstractRuleListener implements EventSubscriberInterface
  34. {
  35.     /**
  36.      * @var CacheManager
  37.      */
  38.     private $cacheManager;
  39.     /**
  40.      * @var SymfonyResponseTagger
  41.      */
  42.     private $symfonyResponseTagger;
  43.     /**
  44.      * @var ExpressionLanguage|null
  45.      */
  46.     private $expressionLanguage;
  47.     /**
  48.      * @var RuleMatcherInterface
  49.      */
  50.     private $mustInvalidateRule;
  51.     /**
  52.      * @var RuleMatcherInterface
  53.      */
  54.     private $cacheableRule;
  55.     /**
  56.      * Constructor.
  57.      */
  58.     public function __construct(
  59.         CacheManager $cacheManager,
  60.         SymfonyResponseTagger $tagHandler,
  61.         RuleMatcherInterface $cacheableRule,
  62.         RuleMatcherInterface $mustInvalidateRule,
  63.         ExpressionLanguage $expressionLanguage null
  64.     ) {
  65.         $this->cacheManager $cacheManager;
  66.         $this->symfonyResponseTagger $tagHandler;
  67.         $this->cacheableRule $cacheableRule;
  68.         $this->mustInvalidateRule $mustInvalidateRule;
  69.         $this->expressionLanguage $expressionLanguage;
  70.     }
  71.     /**
  72.      * Process the _tags request attribute, which is set when using the Tag
  73.      * annotation.
  74.      *
  75.      * - For a safe (GET or HEAD) request, the tags are set on the response.
  76.      * - For a non-safe request, the tags will be invalidated.
  77.      */
  78.     public function onKernelResponse(TagResponseEvent $event)
  79.     {
  80.         $request $event->getRequest();
  81.         $response $event->getResponse();
  82.         if (!$this->cacheableRule->matches($request$response)
  83.             && !$this->mustInvalidateRule->matches($request$response)
  84.         ) {
  85.             return;
  86.         }
  87.         $tags $this->getAnnotationTags($request);
  88.         $configuredTags $this->matchRule($request);
  89.         if ($configuredTags) {
  90.             $tags array_merge($tags$configuredTags['tags']);
  91.             foreach ($configuredTags['expressions'] as $expression) {
  92.                 $tags[] = $this->evaluateTag($expression$request);
  93.             }
  94.         }
  95.         if ($this->cacheableRule->matches($request$response)) {
  96.             // For safe requests (GET and HEAD), set cache tags on response
  97.             $this->symfonyResponseTagger->addTags($tags);
  98.             if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
  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. }