first commit
This commit is contained in:
59
plugins/system/cache/cache.xml
vendored
Normal file
59
plugins/system/cache/cache.xml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="system" method="upgrade">
|
||||
<name>plg_system_cache</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2007-02</creationDate>
|
||||
<copyright>(C) 2007 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.0.0</version>
|
||||
<description>PLG_CACHE_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\System\Cache</namespace>
|
||||
<files>
|
||||
<folder plugin="cache">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_system_cache.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_system_cache.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="browsercache"
|
||||
type="radio"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
label="PLG_CACHE_FIELD_BROWSERCACHE_LABEL"
|
||||
default="0"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="exclude_menu_items"
|
||||
type="menuitem"
|
||||
label="PLG_CACHE_FIELD_EXCLUDE_MENU_ITEMS_LABEL"
|
||||
multiple="multiple"
|
||||
filter="intarray"
|
||||
layout="joomla.form.field.groupedlist-fancy-select"
|
||||
/>
|
||||
|
||||
</fieldset>
|
||||
<fieldset name="advanced">
|
||||
<field
|
||||
name="exclude"
|
||||
type="textarea"
|
||||
label="PLG_CACHE_FIELD_EXCLUDE_LABEL"
|
||||
description="PLG_CACHE_FIELD_EXCLUDE_DESC"
|
||||
rows="15"
|
||||
filter="raw"
|
||||
/>
|
||||
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
52
plugins/system/cache/services/provider.php
vendored
Normal file
52
plugins/system/cache/services/provider.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.cache
|
||||
*
|
||||
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
\defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Cache\CacheControllerFactoryInterface;
|
||||
use Joomla\CMS\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\CMS\Profiler\Profiler;
|
||||
use Joomla\CMS\Router\SiteRouter;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\System\Cache\Extension\Cache;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
* @since 4.2.0
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = PluginHelper::getPlugin('system', 'cache');
|
||||
$dispatcher = $container->get(DispatcherInterface::class);
|
||||
$documentFactory = $container->get('document.factory');
|
||||
$cacheControllerFactory = $container->get(CacheControllerFactoryInterface::class);
|
||||
$profiler = (\defined('JDEBUG') && JDEBUG) ? Profiler::getInstance('Application') : null;
|
||||
$router = $container->has(SiteRouter::class) ? $container->get(SiteRouter::class) : null;
|
||||
|
||||
$plugin = new Cache($dispatcher, (array) $plugin, $documentFactory, $cacheControllerFactory, $profiler, $router);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
382
plugins/system/cache/src/Extension/Cache.php
vendored
Normal file
382
plugins/system/cache/src/Extension/Cache.php
vendored
Normal file
@ -0,0 +1,382 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.cache
|
||||
*
|
||||
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\System\Cache\Extension;
|
||||
|
||||
use Joomla\CMS\Cache\CacheController;
|
||||
use Joomla\CMS\Cache\CacheControllerFactoryInterface;
|
||||
use Joomla\CMS\Document\FactoryInterface as DocumentFactoryInterface;
|
||||
use Joomla\CMS\Event\AbstractEvent;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\CMS\Profiler\Profiler;
|
||||
use Joomla\CMS\Router\SiteRouter;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Event\Event;
|
||||
use Joomla\Event\Priority;
|
||||
use Joomla\Event\SubscriberInterface;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Joomla! Page Cache Plugin.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
final class Cache extends CMSPlugin implements SubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Cache instance.
|
||||
*
|
||||
* @var CacheController
|
||||
* @since 1.5
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* The application's document factory interface
|
||||
*
|
||||
* @var DocumentFactoryInterface
|
||||
* @since 4.2.0
|
||||
*/
|
||||
private $documentFactory;
|
||||
|
||||
/**
|
||||
* Cache controller factory interface
|
||||
*
|
||||
* @var CacheControllerFactoryInterface
|
||||
* @since 4.2.0
|
||||
*/
|
||||
private $cacheControllerFactory;
|
||||
|
||||
/**
|
||||
* The application profiler, used when Debug Site is set to Yes in Global Configuration.
|
||||
*
|
||||
* @var Profiler|null
|
||||
* @since 4.2.0
|
||||
*/
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* The frontend router, injected by the service provider.
|
||||
*
|
||||
* @var SiteRouter|null
|
||||
* @since 4.2.0
|
||||
*/
|
||||
private $router;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DispatcherInterface $dispatcher The object to observe
|
||||
* @param array $config An optional associative
|
||||
* array of configuration
|
||||
* settings. Recognized key
|
||||
* values include 'name',
|
||||
* 'group', 'params',
|
||||
* 'language'
|
||||
* (this list is not meant
|
||||
* to be comprehensive).
|
||||
* @param DocumentFactoryInterface $documentFactory The application's
|
||||
* document factory
|
||||
* @param CacheControllerFactoryInterface $cacheControllerFactory Cache controller factory
|
||||
* @param Profiler|null $profiler The application profiler
|
||||
* @param SiteRouter|null $router The frontend router
|
||||
*
|
||||
* @since 4.2.0
|
||||
*/
|
||||
public function __construct(
|
||||
DispatcherInterface $dispatcher,
|
||||
array $config,
|
||||
DocumentFactoryInterface $documentFactory,
|
||||
CacheControllerFactoryInterface $cacheControllerFactory,
|
||||
?Profiler $profiler,
|
||||
?SiteRouter $router
|
||||
) {
|
||||
parent::__construct($dispatcher, $config);
|
||||
|
||||
$this->documentFactory = $documentFactory;
|
||||
$this->cacheControllerFactory = $cacheControllerFactory;
|
||||
$this->profiler = $profiler;
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of CMS events this plugin will listen to and the respective handlers.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 4.2.0
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
/**
|
||||
* Note that onAfterRender and onAfterRespond must be the last handlers to run for this
|
||||
* plugin to operate as expected. These handlers put pages into cache. We must make sure
|
||||
* that a. the page SHOULD be cached and b. we are caching the complete page, as it's
|
||||
* output to the browser.
|
||||
*/
|
||||
return [
|
||||
'onAfterRoute' => 'onAfterRoute',
|
||||
'onAfterRender' => ['onAfterRender', Priority::LOW],
|
||||
'onAfterRespond' => ['onAfterRespond', Priority::LOW],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cached page if the current URL exists in the cache.
|
||||
*
|
||||
* @param Event $event The Joomla event being handled
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function onAfterRoute(Event $event)
|
||||
{
|
||||
if (!$this->appStateSupportsCaching()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If any `pagecache` plugins return false for onPageCacheSetCaching, do not use the cache.
|
||||
PluginHelper::importPlugin('pagecache');
|
||||
|
||||
$results = $this->getApplication()->triggerEvent('onPageCacheSetCaching');
|
||||
|
||||
$this->getCacheController()->setCaching(!\in_array(false, $results, true));
|
||||
|
||||
$data = $this->getCacheController()->get($this->getCacheKey());
|
||||
|
||||
if ($data === false) {
|
||||
// No cached data.
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the page content from the cache and output it to the browser.
|
||||
$this->getApplication()->setBody($data);
|
||||
|
||||
echo $this->getApplication()->toString((bool) $this->getApplication()->get('gzip'));
|
||||
|
||||
// Mark afterCache in debug and run debug onAfterRespond events, e.g. show Joomla Debug Console if debug is active.
|
||||
if (JDEBUG) {
|
||||
// Create a document instance and load it into the application.
|
||||
$document = $this->documentFactory
|
||||
->createDocument($this->getApplication()->getInput()->get('format', 'html'));
|
||||
$this->getApplication()->loadDocument($document);
|
||||
|
||||
if ($this->profiler) {
|
||||
$this->profiler->mark('afterCache');
|
||||
}
|
||||
|
||||
$this->getDispatcher()->dispatch('onAfterRespond', AbstractEvent::create(
|
||||
'onAfterRespond',
|
||||
[
|
||||
'subject' => $this->getApplication(),
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
// Closes the application.
|
||||
$this->getApplication()->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the current application state allow for caching?
|
||||
*
|
||||
* The following conditions must be met:
|
||||
* * This is the frontend application. This plugin does not apply to other applications.
|
||||
* * This is a GET request. This plugin does not apply to POST, PUT etc.
|
||||
* * There is no currently logged in user (pages might have user–specific content).
|
||||
* * The message queue is empty.
|
||||
*
|
||||
* The first two tests are cached to make early returns possible; these conditions cannot change
|
||||
* throughout the lifetime of the request.
|
||||
*
|
||||
* The other two tests MUST NOT be cached because auto–login plugins may fire anytime within
|
||||
* the application lifetime logging in a user and messages can be generated anytime within the
|
||||
* application's lifetime.
|
||||
*
|
||||
* @return boolean
|
||||
* @since 4.2.0
|
||||
*/
|
||||
private function appStateSupportsCaching(): bool
|
||||
{
|
||||
static $isSite = null;
|
||||
static $isGET = null;
|
||||
|
||||
if ($isSite === null) {
|
||||
$isSite = $this->getApplication()->isClient('site');
|
||||
$isGET = $this->getApplication()->getInput()->getMethod() === 'GET';
|
||||
}
|
||||
|
||||
// Boolean short–circuit evaluation means this returns fast false when $isSite is false.
|
||||
return $isSite
|
||||
&& $isGET
|
||||
&& $this->getApplication()->getIdentity()->guest
|
||||
&& empty($this->getApplication()->getMessageQueue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache controller
|
||||
*
|
||||
* @return CacheController
|
||||
* @since 4.2.0
|
||||
*/
|
||||
private function getCacheController(): CacheController
|
||||
{
|
||||
if (!empty($this->cache)) {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
// Set the cache options.
|
||||
$options = [
|
||||
'defaultgroup' => 'page',
|
||||
'browsercache' => $this->params->get('browsercache', 0),
|
||||
'caching' => false,
|
||||
];
|
||||
|
||||
// Instantiate cache with previous options.
|
||||
$this->cache = $this->cacheControllerFactory->createCacheController('page', $options);
|
||||
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cache key for the current page based on the url and possible other factors.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
private function getCacheKey(): string
|
||||
{
|
||||
static $key;
|
||||
|
||||
if (!$key) {
|
||||
PluginHelper::importPlugin('pagecache');
|
||||
|
||||
$parts = $this->getApplication()->triggerEvent('onPageCacheGetKey');
|
||||
$parts[] = Uri::getInstance()->toString();
|
||||
|
||||
$key = md5(serialize($parts));
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* After Render Event. Check whether the current page is excluded from cache.
|
||||
*
|
||||
* @param Event $event The CMS event we are handling.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.12
|
||||
*/
|
||||
public function onAfterRender(Event $event)
|
||||
{
|
||||
if (!$this->appStateSupportsCaching() || $this->getCacheController()->getCaching() === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isExcluded() === true) {
|
||||
$this->getCacheController()->setCaching(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable compression before caching the page.
|
||||
$this->getApplication()->set('gzip', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the page is excluded from the cache or not.
|
||||
*
|
||||
* @return boolean True if the page is excluded else false
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function isExcluded(): bool
|
||||
{
|
||||
// Check if menu items have been excluded.
|
||||
$excludedMenuItems = $this->params->get('exclude_menu_items', []);
|
||||
|
||||
if ($excludedMenuItems) {
|
||||
// Get the current menu item.
|
||||
$active = $this->getApplication()->getMenu()->getActive();
|
||||
|
||||
if ($active && $active->id && \in_array((int) $active->id, (array) $excludedMenuItems)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if regular expressions are being used.
|
||||
$exclusions = $this->params->get('exclude', '');
|
||||
|
||||
if ($exclusions) {
|
||||
// Convert the exclusions into a normalised array
|
||||
$exclusions = str_replace(["\r\n", "\r"], "\n", $exclusions);
|
||||
$exclusions = explode("\n", $exclusions);
|
||||
$filterExpression = function ($x) {
|
||||
return $x !== '';
|
||||
};
|
||||
$exclusions = array_filter($exclusions, $filterExpression);
|
||||
|
||||
// Gets the internal (non-SEF) and the external (possibly SEF) URIs.
|
||||
$internalUrl = '/index.php?'
|
||||
. Uri::getInstance()->buildQuery($this->router->getVars());
|
||||
$externalUrl = Uri::getInstance()->toString();
|
||||
|
||||
$reduceCallback
|
||||
= function (bool $carry, string $exclusion) use ($internalUrl, $externalUrl) {
|
||||
// Test both external and internal URIs
|
||||
return $carry && preg_match(
|
||||
'#' . $exclusion . '#i',
|
||||
$externalUrl . ' ' . $internalUrl,
|
||||
$match
|
||||
);
|
||||
};
|
||||
$excluded = array_reduce($exclusions, $reduceCallback, false);
|
||||
|
||||
if ($excluded) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If any pagecache plugins return true for onPageCacheIsExcluded, exclude.
|
||||
PluginHelper::importPlugin('pagecache');
|
||||
|
||||
$results = $this->getApplication()->triggerEvent('onPageCacheIsExcluded');
|
||||
|
||||
return \in_array(true, $results, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* After Respond Event. Stores page in cache.
|
||||
*
|
||||
* @param Event $event The application event we are handling.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
public function onAfterRespond(Event $event)
|
||||
{
|
||||
if (!$this->appStateSupportsCaching() || $this->getCacheController()->getCaching() === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Saves current page in cache.
|
||||
$this->getCacheController()->store($this->getApplication()->getBody(), $this->getCacheKey());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user