218 lines
5.4 KiB
PHP
218 lines
5.4 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Joomla! Content Management System
|
|
*
|
|
* @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org>
|
|
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
|
*/
|
|
|
|
namespace Joomla\CMS\Cache\Controller;
|
|
|
|
use Joomla\CMS\Cache\Cache;
|
|
use Joomla\CMS\Cache\CacheController;
|
|
use Joomla\CMS\Factory;
|
|
|
|
// phpcs:disable PSR1.Files.SideEffects
|
|
\defined('_JEXEC') or die;
|
|
// phpcs:enable PSR1.Files.SideEffects
|
|
|
|
/**
|
|
* Joomla! Cache page type object
|
|
*
|
|
* @since 1.7.0
|
|
*/
|
|
class PageController extends CacheController
|
|
{
|
|
/**
|
|
* ID property for the cache page object.
|
|
*
|
|
* @var integer
|
|
* @since 1.7.0
|
|
*/
|
|
protected $_id;
|
|
|
|
/**
|
|
* Cache group
|
|
*
|
|
* @var string
|
|
* @since 1.7.0
|
|
*/
|
|
protected $_group;
|
|
|
|
/**
|
|
* Cache lock test
|
|
*
|
|
* @var \stdClass
|
|
* @since 1.7.0
|
|
*/
|
|
protected $_locktest = null;
|
|
|
|
/**
|
|
* Get the cached page data
|
|
*
|
|
* @param boolean $id The cache data ID
|
|
* @param string $group The cache data group
|
|
*
|
|
* @return mixed Boolean false on no result, cached object otherwise
|
|
*
|
|
* @since 1.7.0
|
|
*/
|
|
public function get($id = false, $group = 'page')
|
|
{
|
|
// If an id is not given, generate it from the request
|
|
if (!$id) {
|
|
$id = $this->_makeId();
|
|
}
|
|
|
|
// If the etag matches the page id ... set a no change header and exit : utilize browser cache
|
|
if (!headers_sent() && isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
|
|
$etag = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
|
|
|
|
if ($etag == $id) {
|
|
$browserCache = $this->options['browsercache'] ?? false;
|
|
|
|
if ($browserCache) {
|
|
$this->_noChange();
|
|
}
|
|
}
|
|
}
|
|
|
|
// We got a cache hit... set the etag header and echo the page data
|
|
$data = $this->cache->get($id, $group);
|
|
|
|
$this->_locktest = (object) ['locked' => null, 'locklooped' => null];
|
|
|
|
if ($data === false) {
|
|
$this->_locktest = $this->cache->lock($id, $group);
|
|
|
|
// If locklooped is true try to get the cached data again; it could exist now.
|
|
if ($this->_locktest->locked === true && $this->_locktest->locklooped === true) {
|
|
$data = $this->cache->get($id, $group);
|
|
}
|
|
}
|
|
|
|
if ($data !== false) {
|
|
if ($this->_locktest->locked === true) {
|
|
$this->cache->unlock($id, $group);
|
|
}
|
|
|
|
$data = unserialize(trim($data));
|
|
$data = Cache::getWorkarounds($data);
|
|
|
|
$this->_setEtag($id);
|
|
|
|
return $data;
|
|
}
|
|
|
|
// Set ID and group placeholders
|
|
$this->_id = $id;
|
|
$this->_group = $group;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Stop the cache buffer and store the cached data
|
|
*
|
|
* @param mixed $data The data to store
|
|
* @param string $id The cache data ID
|
|
* @param string $group The cache data group
|
|
* @param boolean $wrkarounds True to use workarounds
|
|
*
|
|
* @return boolean
|
|
*
|
|
* @since 1.7.0
|
|
*/
|
|
public function store($data, $id, $group = null, $wrkarounds = true)
|
|
{
|
|
if ($this->_locktest->locked === false && $this->_locktest->locklooped === true) {
|
|
// We can not store data because another process is in the middle of saving
|
|
return false;
|
|
}
|
|
|
|
// Get page data from the application object
|
|
if (!$data) {
|
|
$data = Factory::getApplication()->getBody();
|
|
|
|
// Only attempt to store if page data exists.
|
|
if (!$data) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Get id and group and reset the placeholders
|
|
if (!$id) {
|
|
$id = $this->_id;
|
|
}
|
|
|
|
if (!$group) {
|
|
$group = $this->_group;
|
|
}
|
|
|
|
if ($wrkarounds) {
|
|
$data = Cache::setWorkarounds(
|
|
$data,
|
|
[
|
|
'nopathway' => 1,
|
|
'nohead' => 1,
|
|
'nomodules' => 1,
|
|
'headers' => true,
|
|
]
|
|
);
|
|
}
|
|
|
|
$result = $this->cache->store(serialize($data), $id, $group);
|
|
|
|
if ($this->_locktest->locked === true) {
|
|
$this->cache->unlock($id, $group);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Generate a page cache id
|
|
*
|
|
* @return string MD5 Hash
|
|
*
|
|
* @since 1.7.0
|
|
* @todo Discuss whether this should be coupled to a data hash or a request hash ... perhaps hashed with a serialized request
|
|
*/
|
|
protected function _makeId()
|
|
{
|
|
return Cache::makeId();
|
|
}
|
|
|
|
/**
|
|
* There is no change in page data so send an unmodified header and die gracefully
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 1.7.0
|
|
*/
|
|
protected function _noChange()
|
|
{
|
|
$app = Factory::getApplication();
|
|
|
|
// Send not modified header and exit gracefully
|
|
$app->setHeader('Status', 304, true);
|
|
$app->sendHeaders();
|
|
$app->close();
|
|
}
|
|
|
|
/**
|
|
* Set the ETag header in the response
|
|
*
|
|
* @param string $etag The entity tag (etag) to set
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 1.7.0
|
|
*/
|
|
protected function _setEtag($etag)
|
|
{
|
|
Factory::getApplication()->setHeader('ETag', '"' . $etag . '"', true);
|
|
}
|
|
}
|