[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |