PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/EventListener/PageCacheEnablerListener.php

https://bitbucket.org/joeherold/pagecacheenabler-bundle
PHP | 236 lines | 110 code | 53 blank | 73 comment | 43 complexity | 010d5b84dff8d05b595d377d3d4a04be MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of Pliigo.
  4. *
  5. * Copyright (c) 2018 Johannes Pichler
  6. *
  7. * @license LGPL-3.0+
  8. */
  9. namespace Pliigo\PliigoPageCacheEnabler\EventListener;
  10. use Contao\CoreBundle\Controller\FrontendController;
  11. use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
  12. use Contao\Input;
  13. use Symfony\Component\DependencyInjection\ContainerInterface;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Symfony\Component\HttpFoundation\Response;
  16. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  17. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  18. /**
  19. * Adds the calendar feeds to the page header.
  20. *
  21. * @author Andreas Schempp <https://github.com/aschempp>
  22. * @author Leo Feyer <https://github.com/leofeyer>
  23. */
  24. class PageCacheEnablerListener
  25. {
  26. /**
  27. * @var ContaoFrameworkInterface
  28. */
  29. private $framework;
  30. private $container;
  31. /**
  32. * Constructor.
  33. *
  34. * @param ContaoFrameworkInterface $framework
  35. */
  36. public function __construct(ContaoFrameworkInterface $framework, ContainerInterface $container)
  37. {
  38. $this->framework = $framework;
  39. $this->container = $container;
  40. }
  41. /**
  42. * Placeholder in case we need the request object as well in the future
  43. * @param GetResponseEvent $event
  44. */
  45. public function onKernelRequest(GetResponseEvent $event)
  46. {
  47. // do nothing, just a placeholder
  48. }
  49. /**
  50. * Adds the Contao headers to the Symfony response again after Session destroyed it.
  51. *
  52. * @description added new onKernelResponseListener, to fix issue 1246 with broken cache according to
  53. * symfony/symfony 3.4.4
  54. *
  55. * @param FilterResponseEvent $event
  56. */
  57. public function onKernelResponse(FilterResponseEvent $event)
  58. {
  59. // added new onKernelResponseListener, to fix issue 1246
  60. if (!$this->framework->isInitialized()) {
  61. return;
  62. }
  63. $request = $event->getRequest();
  64. $response = $event->getResponse();
  65. $controllerAttribute = $request->get("_controller");
  66. $insertTagAttribute = $request->get("insertTag");
  67. $formatAttribute = $request->get("_format");
  68. // fist check, if we have a file-download
  69. if(Input::get('file', true)) {
  70. // in case we have a file download request, never cache the file!
  71. return $response->setPrivate();
  72. }
  73. // we distinguish between rendering of insert tags (sub requests) and page rendering (master request)
  74. if (
  75. // AND checks, if request is for an insert tag
  76. $controllerAttribute == "contao.controller.insert_tags:renderAction"
  77. // AND checks, if an insert tag attribute is given in the request
  78. && $insertTagAttribute
  79. ) {
  80. // FIST CASE
  81. // check for format html, because we only want to cache html content
  82. if ($formatAttribute !== "html") {
  83. return;
  84. }
  85. $withoutBraces = str_replace(['{{', '}}'], '',$insertTagAttribute );
  86. $flags = explode('|', $withoutBraces);
  87. $tag = array_shift($flags);
  88. $elements = explode('::', $tag);
  89. // a request for rendering insert tags
  90. // exclude certain elements from being cached
  91. if (
  92. $elements[0] == 'date'
  93. || $elements[0] == 'ua'
  94. || $elements[0] == 'post'
  95. || $elements[0] == 'file'
  96. || $elements[1] == 'back'
  97. || $elements[1] == 'referer'
  98. || $elements[0] == 'request_token'
  99. || $elements[0] == 'toggle_view'
  100. || strncmp($elements[0], 'cache_', 6) === 0
  101. || in_array('uncached', $flags)
  102. || in_array('refresh', $flags)
  103. ) {
  104. $response->setPrivate();
  105. } else {
  106. $this->setCacheHeaders($request, $response);
  107. }
  108. } else if (
  109. // SECOND CASE
  110. // checks, if request is a master request
  111. $event->isMasterRequest()
  112. // AND if the controller ist the FrontendController with its static index action
  113. // what should be "Contao\CoreBundle\Controller\FrontendController::indexAction"
  114. && $controllerAttribute == FrontendController::class . "::indexAction"
  115. ) {
  116. // second case, a master request for rendering a page
  117. $this->setCacheHeaders($request, $response);
  118. }
  119. }
  120. /**
  121. * Set the cache headers according to the page settings.
  122. *
  123. * @param Request $request The request object
  124. * @param Response $response The response object
  125. * @return Response The response object
  126. */
  127. private function setCacheHeaders(Request $request, Response $response)
  128. {
  129. /** @var $objPage \PageModel */
  130. global $objPage;
  131. if (!$objPage) {
  132. return $response;
  133. }
  134. if (($objPage->cache === false || $objPage->cache < 1) && ($objPage->clientCache === false || $objPage->clientCache < 1)) {
  135. return $response->setPrivate();
  136. }
  137. // Do not cache the response if a user is logged in or the page is protected
  138. // TODO: Add support for proxies so they can vary on member context
  139. if (FE_USER_LOGGED_IN === true || BE_USER_LOGGED_IN === true || $objPage->protected || $this->hasAuthenticatedBackendUser($request)) {
  140. return $response->setPrivate();
  141. }
  142. if ($objPage->clientCache > 0) {
  143. $response->setMaxAge($objPage->clientCache);
  144. }
  145. if ($objPage->cache > 0) {
  146. $response->setSharedMaxAge($objPage->cache);
  147. }
  148. $response->isNotModified($request);
  149. return $response;
  150. }
  151. /**
  152. * Checks if there is an authenticated back end user.
  153. *
  154. * @param Request $request
  155. *
  156. * @return bool
  157. */
  158. private function hasAuthenticatedBackendUser(Request $request)
  159. {
  160. if (!$request->cookies->has('BE_USER_AUTH')) {
  161. return false;
  162. }
  163. $sessionHash = $this->getSessionHash('BE_USER_AUTH');
  164. return $request->cookies->get('BE_USER_AUTH') === $sessionHash;
  165. }
  166. /**
  167. * Return the session hash
  168. *
  169. * @param string $strCookie The cookie name
  170. *
  171. * @return string The session hash
  172. */
  173. public function getSessionHash($strCookie)
  174. {
  175. $container = $this->container;
  176. $strHash = $container->get('session')->getId();
  177. if (!$container->getParameter('contao.security.disable_ip_check')) {
  178. $strHash .= \Environment::get('ip');
  179. }
  180. $strHash .= $strCookie;
  181. return sha1($strHash);
  182. }
  183. }