[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/plugins/system/cache/src/Extension/ -> Cache.php (source)

   1  <?php
   2  /**
   3   * @package     Joomla.Plugin
   4   * @subpackage  System.cache
   5   *
   6   * @copyright   (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license     GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\Plugin\System\Cache\Extension;
  11  
  12  defined('_JEXEC') or die;
  13  
  14  use Joomla\CMS\Application\CMSApplication;
  15  use Joomla\CMS\Application\CMSApplicationInterface;
  16  use Joomla\CMS\Cache\CacheController;
  17  use Joomla\CMS\Cache\CacheControllerFactoryInterface;
  18  use Joomla\CMS\Document\FactoryInterface as DocumentFactoryInterface;
  19  use Joomla\CMS\Plugin\CMSPlugin;
  20  use Joomla\CMS\Plugin\PluginHelper;
  21  use Joomla\CMS\Profiler\Profiler;
  22  use Joomla\CMS\Router\SiteRouter;
  23  use Joomla\CMS\Uri\Uri;
  24  use Joomla\Event\DispatcherInterface;
  25  use Joomla\Event\Event;
  26  use Joomla\Event\Priority;
  27  use Joomla\Event\SubscriberInterface;
  28  
  29  /**
  30   * Joomla! Page Cache Plugin.
  31   *
  32   * @since  1.5
  33   */
  34  final class Cache extends CMSPlugin implements SubscriberInterface
  35  {
  36      /**
  37       * Cache instance.
  38       *
  39       * @var    CacheController
  40       * @since  1.5
  41       */
  42      private $cache;
  43  
  44      /**
  45       * The application's document factory interface
  46       *
  47       * @var   DocumentFactoryInterface
  48       * @since 4.2.0
  49       */
  50      private $documentFactory;
  51  
  52      /**
  53       * Cache controller factory interface
  54       *
  55       * @var    CacheControllerFactoryInterface
  56       * @since  4.2.0
  57       */
  58      private $cacheControllerFactory;
  59  
  60      /**
  61       * The application profiler, used when Debug Site is set to Yes in Global Configuration.
  62       *
  63       * @var    Profiler|null
  64       * @since  4.2.0
  65       */
  66      private $profiler;
  67  
  68      /**
  69       * The frontend router, injected by the service provider.
  70       *
  71       * @var   SiteRouter|null
  72       * @since 4.2.0
  73       */
  74      private $router;
  75  
  76      /**
  77       * Constructor
  78       *
  79       * @param   DispatcherInterface              $subject                 The object to observe
  80       * @param   array                            $config                  An optional associative
  81       *                                                                    array of configuration
  82       *                                                                    settings. Recognized key
  83       *                                                                    values include 'name',
  84       *                                                                    'group', 'params',
  85       *                                                                    'language'
  86       *                                                                    (this list is not meant
  87       *                                                                    to be comprehensive).
  88       * @param   DocumentFactoryInterface         $documentFactory         The application's
  89       *                                                                    document factory
  90       * @param   CacheControllerFactoryInterface  $cacheControllerFactory  Cache controller factory
  91       * @param   Profiler|null                    $profiler                The application profiler
  92       * @param   SiteRouter|null                  $router                  The frontend router
  93       *
  94       * @since   4.2.0
  95       */
  96  	public function __construct(
  97          &$subject,
  98          $config,
  99          DocumentFactoryInterface $documentFactory,
 100          CacheControllerFactoryInterface $cacheControllerFactory,
 101          ?Profiler $profiler,
 102          ?SiteRouter $router
 103      )
 104      {
 105          parent::__construct($subject, $config);
 106  
 107          $this->documentFactory        = $documentFactory;
 108          $this->cacheControllerFactory = $cacheControllerFactory;
 109          $this->profiler               = $profiler;
 110          $this->router                 = $router;
 111      }
 112  
 113      /**
 114       * Returns an array of CMS events this plugin will listen to and the respective handlers.
 115       *
 116       * @return  array
 117       *
 118       * @since   4.2.0
 119       */
 120  	public static function getSubscribedEvents(): array
 121      {
 122          /**
 123           * Note that onAfterRender and onAfterRespond must be the last handlers to run for this
 124           * plugin to operate as expected. These handlers put pages into cache. We must make sure
 125           * that a. the page SHOULD be cached and b. we are caching the complete page, as it's
 126           * output to the browser.
 127           */
 128          return [
 129              'onAfterRoute'   => 'onAfterRoute',
 130              'onAfterRender'  => ['onAfterRender', Priority::LOW],
 131              'onAfterRespond' => ['onAfterRespond', Priority::LOW],
 132          ];
 133      }
 134  
 135      /**
 136       * Returns a cached page if the current URL exists in the cache.
 137       *
 138       * @param   Event  $event  The Joomla event being handled
 139       *
 140       * @return  void
 141       *
 142       * @since   4.0.0
 143       */
 144  	public function onAfterRoute(Event $event)
 145      {
 146          if (!$this->appStateSupportsCaching())
 147          {
 148              return;
 149          }
 150  
 151          // If any `pagecache` plugins return false for onPageCacheSetCaching, do not use the cache.
 152          PluginHelper::importPlugin('pagecache');
 153  
 154          $results = $this->getApplication()->triggerEvent('onPageCacheSetCaching');
 155  
 156          $this->getCacheController()->setCaching(!in_array(false, $results, true));
 157  
 158          $data = $this->getCacheController()->get($this->getCacheKey());
 159  
 160          if ($data === false)
 161          {
 162              // No cached data.
 163              return;
 164          }
 165  
 166          // Set the page content from the cache and output it to the browser.
 167          $this->getApplication()->setBody($data);
 168  
 169          echo $this->getApplication()->toString((bool) $this->getApplication()->get('gzip'));
 170  
 171          // Mark afterCache in debug and run debug onAfterRespond events, e.g. show Joomla Debug Console if debug is active.
 172          if (JDEBUG)
 173          {
 174              // Create a document instance and load it into the application.
 175              $document = $this->documentFactory
 176                  ->createDocument($this->getApplication()->input->get('format', 'html'));
 177              $this->getApplication()->loadDocument($document);
 178  
 179              if ($this->profiler)
 180              {
 181                  $this->profiler->mark('afterCache');
 182              }
 183  
 184              $this->getApplication()->triggerEvent('onAfterRespond');
 185          }
 186  
 187          // Closes the application.
 188          $this->getApplication()->close();
 189      }
 190  
 191      /**
 192       * Does the current application state allow for caching?
 193       *
 194       * The following conditions must be met:
 195       * * This is the frontend application. This plugin does not apply to other applications.
 196       * * This is a GET request. This plugin does not apply to POST, PUT etc.
 197       * * There is no currently logged in user (pages might have user–specific content).
 198       * * The message queue is empty.
 199       *
 200       * The first two tests are cached to make early returns possible; these conditions cannot change
 201       * throughout the lifetime of the request.
 202       *
 203       * The other two tests MUST NOT be cached because auto–login plugins may fire anytime within
 204       * the application lifetime logging in a user and messages can be generated anytime within the
 205       * application's lifetime.
 206       *
 207       * @return  boolean
 208       * @since   4.2.0
 209       */
 210  	private function appStateSupportsCaching(): bool
 211      {
 212          static $isSite = null;
 213          static $isGET = null;
 214  
 215          if ($isSite === null)
 216          {
 217              $isSite = $this->getApplication()->isClient('site');
 218              $isGET  = $this->getApplication()->input->getMethod() === 'GET';
 219          }
 220  
 221          // Boolean short–circuit evaluation means this returns fast false when $isSite is false.
 222          return $isSite
 223              && $isGET
 224              && $this->getApplication()->getIdentity()->guest
 225              && empty($this->getApplication()->getMessageQueue());
 226      }
 227  
 228      /**
 229       * Get the cache controller
 230       *
 231       * @return  CacheController
 232       * @since   4.2.0
 233       */
 234  	private function getCacheController(): CacheController
 235      {
 236          if (!empty($this->cache))
 237          {
 238              return $this->cache;
 239          }
 240  
 241          // Set the cache options.
 242          $options = [
 243              'defaultgroup' => 'page',
 244              'browsercache' => $this->params->get('browsercache', 0),
 245              'caching'      => false,
 246          ];
 247  
 248          // Instantiate cache with previous options.
 249          $this->cache = $this->cacheControllerFactory->createCacheController('page', $options);
 250  
 251          return $this->cache;
 252      }
 253  
 254      /**
 255       * Get a cache key for the current page based on the url and possible other factors.
 256       *
 257       * @return  string
 258       *
 259       * @since   3.7
 260       */
 261  	private function getCacheKey(): string
 262      {
 263          static $key;
 264  
 265          if (!$key)
 266          {
 267              PluginHelper::importPlugin('pagecache');
 268  
 269              $parts   = $this->getApplication()->triggerEvent('onPageCacheGetKey');
 270              $parts[] = Uri::getInstance()->toString();
 271  
 272              $key = md5(serialize($parts));
 273          }
 274  
 275          return $key;
 276      }
 277  
 278      /**
 279       * After Render Event. Check whether the current page is excluded from cache.
 280       *
 281       * @param   Event  $event  The CMS event we are handling.
 282       *
 283       * @return  void
 284       *
 285       * @since   3.9.12
 286       */
 287  	public function onAfterRender(Event $event)
 288      {
 289          if (!$this->appStateSupportsCaching() || $this->getCacheController()->getCaching() === false)
 290          {
 291              return;
 292          }
 293  
 294          if ($this->isExcluded() === true)
 295          {
 296              $this->getCacheController()->setCaching(false);
 297  
 298              return;
 299          }
 300  
 301          // Disable compression before caching the page.
 302          $this->getApplication()->set('gzip', false);
 303      }
 304  
 305      /**
 306       * Check if the page is excluded from the cache or not.
 307       *
 308       * @return   boolean  True if the page is excluded else false
 309       *
 310       * @since    3.5
 311       */
 312  	private function isExcluded(): bool
 313      {
 314          // Check if menu items have been excluded.
 315          $excludedMenuItems = $this->params->get('exclude_menu_items', []);
 316  
 317          if ($excludedMenuItems)
 318          {
 319              // Get the current menu item.
 320              $active = $this->getApplication()->getMenu()->getActive();
 321  
 322              if ($active && $active->id && in_array((int) $active->id, (array) $excludedMenuItems))
 323              {
 324                  return true;
 325              }
 326          }
 327  
 328          // Check if regular expressions are being used.
 329          $exclusions = $this->params->get('exclude', '');
 330  
 331          if ($exclusions)
 332          {
 333              // Convert the exclusions into a normalised array
 334              $exclusions       = str_replace(["\r\n", "\r"], "\n", $exclusions);
 335              $exclusions       = explode("\n", $exclusions);
 336              $filterExpression = function ($x)
 337              {
 338                  return $x !== '';
 339              };
 340              $exclusions       = array_filter($exclusions, $filterExpression);
 341  
 342              // Gets the internal (non-SEF) and the external (possibly SEF) URIs.
 343              $internalUrl = '/index.php?'
 344                  . Uri::getInstance()->buildQuery($this->router->getVars());
 345              $externalUrl = Uri::getInstance()->toString();
 346  
 347              $reduceCallback
 348                  = function (bool $carry, string $exclusion) use ($internalUrl, $externalUrl)
 349                  {
 350                      // Test both external and internal URIs
 351                      return $carry && preg_match(
 352                          '#' . $exclusion . '#i',
 353                          $externalUrl . ' ' . $internalUrl, $match
 354                      );
 355                  };
 356              $excluded       = array_reduce($exclusions, $reduceCallback, false);
 357  
 358              if ($excluded)
 359              {
 360                  return true;
 361              }
 362          }
 363  
 364          // If any pagecache plugins return true for onPageCacheIsExcluded, exclude.
 365          PluginHelper::importPlugin('pagecache');
 366  
 367          $results = $this->getApplication()->triggerEvent('onPageCacheIsExcluded');
 368  
 369          return in_array(true, $results, true);
 370      }
 371  
 372      /**
 373       * After Respond Event. Stores page in cache.
 374       *
 375       * @param   Event  $event  The application event we are handling.
 376       *
 377       * @return  void
 378       *
 379       * @since   1.5
 380       */
 381  	public function onAfterRespond(Event $event)
 382      {
 383          if (!$this->appStateSupportsCaching() || $this->getCacheController()->getCaching() === false)
 384          {
 385              return;
 386          }
 387  
 388          // Saves current page in cache.
 389          $this->getCacheController()->store($this->getApplication()->getBody(), $this->getCacheKey());
 390      }
 391  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer