| Current Path : /home/digilove/www/41423/ |
| Current File : /home/digilove/www/41423/Cache.tar |
Controller/CallbackController.php 0000644 00000013405 15234436765 0013162 0 ustar 00 <?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;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\Cache;
use Joomla\CMS\Cache\CacheController;
/**
* Joomla! Cache callback type object
*
* @since 1.7.0
*/
class CallbackController extends CacheController
{
/**
* Executes a cacheable callback if not found in cache else returns cached output and result
*
* Since arguments to this function are read with func_get_args you can pass any number of arguments to this method
* as long as the first argument passed is the callback definition.
*
* The callback definition can be in several forms:
* - Standard PHP Callback array see <https://www.php.net/callback> [recommended]
* - Function name as a string eg. 'foo' for function foo()
* - Static method name as a string eg. 'MyClass::myMethod' for method myMethod() of class MyClass
*
* @return mixed Result of the callback
*
* @since 1.7.0
* @deprecated 4.0
*/
public function call()
{
// Get callback and arguments
$args = func_get_args();
$callback = array_shift($args);
return $this->get($callback, $args);
}
/**
* Executes a cacheable callback if not found in cache else returns cached output and result
*
* @param mixed $callback Callback or string shorthand for a callback
* @param array $args Callback arguments
* @param mixed $id Cache ID
* @param boolean $wrkarounds True to use workarounds
* @param array $woptions Workaround options
*
* @return mixed Result of the callback
*
* @since 1.7.0
*/
public function get($callback, $args = array(), $id = false, $wrkarounds = false, $woptions = array())
{
// Normalize callback
if (is_array($callback) || is_callable($callback))
{
// We have a standard php callback array -- do nothing
}
elseif (strstr($callback, '::'))
{
// This is shorthand for a static method callback classname::methodname
list ($class, $method) = explode('::', $callback);
$callback = array(trim($class), trim($method));
}
elseif (strstr($callback, '->'))
{
/*
* This is a really not so smart way of doing this... we provide this for backward compatibility but this
* WILL! disappear in a future version. If you are using this syntax change your code to use the standard
* PHP callback array syntax: <https://www.php.net/callback>
*
* We have to use some silly global notation to pull it off and this is very unreliable
*/
list ($object_123456789, $method) = explode('->', $callback);
global $$object_123456789;
$callback = array($$object_123456789, $method);
}
if (!$id)
{
// Generate an ID
$id = $this->_makeId($callback, $args);
}
$data = $this->cache->get($id);
$locktest = (object) array('locked' => null, 'locklooped' => null);
if ($data === false)
{
$locktest = $this->cache->lock($id);
// If locklooped is true try to get the cached data again; it could exist now.
if ($locktest->locked === true && $locktest->locklooped === true)
{
$data = $this->cache->get($id);
}
}
if ($data !== false)
{
if ($locktest->locked === true)
{
$this->cache->unlock($id);
}
$data = unserialize(trim($data));
if ($wrkarounds)
{
echo Cache::getWorkarounds(
$data['output'],
array('mergehead' => isset($woptions['mergehead']) ? $woptions['mergehead'] : 0)
);
}
else
{
echo $data['output'];
}
return $data['result'];
}
if (!is_array($args))
{
$referenceArgs = !empty($args) ? array(&$args) : array();
}
else
{
$referenceArgs = &$args;
}
if ($locktest->locked === false && $locktest->locklooped === true)
{
// We can not store data because another process is in the middle of saving
return call_user_func_array($callback, $referenceArgs);
}
$coptions = array();
if (isset($woptions['modulemode']) && $woptions['modulemode'] == 1)
{
$document = \JFactory::getDocument();
if (method_exists($document, 'getHeadData'))
{
$coptions['headerbefore'] = $document->getHeadData();
}
$coptions['modulemode'] = 1;
}
else
{
$coptions['modulemode'] = 0;
}
$coptions['nopathway'] = isset($woptions['nopathway']) ? $woptions['nopathway'] : 1;
$coptions['nohead'] = isset($woptions['nohead']) ? $woptions['nohead'] : 1;
$coptions['nomodules'] = isset($woptions['nomodules']) ? $woptions['nomodules'] : 1;
ob_start();
ob_implicit_flush(false);
$result = call_user_func_array($callback, $referenceArgs);
$output = ob_get_clean();
$data = array('result' => $result);
if ($wrkarounds)
{
$data['output'] = Cache::setWorkarounds($output, $coptions);
}
else
{
$data['output'] = $output;
}
// Store the cache data
$this->cache->store(serialize($data), $id);
if ($locktest->locked === true)
{
$this->cache->unlock($id);
}
echo $output;
return $result;
}
/**
* Generate a callback cache ID
*
* @param callback $callback Callback to cache
* @param array $args Arguments to the callback method to cache
*
* @return string MD5 Hash
*
* @since 1.7.0
*/
protected function _makeId($callback, $args)
{
if (is_array($callback) && is_object($callback[0]))
{
$vars = get_object_vars($callback[0]);
$vars[] = strtolower(get_class($callback[0]));
$callback[0] = $vars;
}
// A Closure can't be serialized, so to generate the ID we'll need to get its hash
if (is_a($callback, 'closure'))
{
$hash = spl_object_hash($callback);
return md5($hash . serialize(array($args)));
}
return md5(serialize(array($callback, $args)));
}
}
Controller/OutputController.php 0000644 00000010675 15234436765 0012774 0 ustar 00 <?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;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\CacheController;
use Joomla\CMS\Log\Log;
/**
* Joomla Cache output type object
*
* @since 1.7.0
*/
class OutputController extends CacheController
{
/**
* Cache data ID
*
* @var string
* @since 1.7.0
*/
protected $_id;
/**
* Cache data group
*
* @var string
* @since 1.7.0
*/
protected $_group;
/**
* Object to test locked state
*
* @var \stdClass
* @since 1.7.0
* @deprecated 4.0
*/
protected $_locktest = null;
/**
* Start the cache
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
* @deprecated 4.0
*/
public function start($id, $group = null)
{
Log::add(
__METHOD__ . '() is deprecated.',
Log::WARNING,
'deprecated'
);
// If we have data in cache use that.
$data = $this->cache->get($id, $group);
$this->_locktest = new \stdClass;
$this->_locktest->locked = null;
$this->_locktest->locklooped = null;
if ($data === false)
{
$this->_locktest = $this->cache->lock($id, $group);
if ($this->_locktest->locked == true && $this->_locktest->locklooped == true)
{
$data = $this->cache->get($id, $group);
}
}
if ($data !== false)
{
$data = unserialize(trim($data));
echo $data;
if ($this->_locktest->locked == true)
{
$this->cache->unlock($id, $group);
}
return true;
}
// Nothing in cache... let's start the output buffer and start collecting data for next time.
if ($this->_locktest->locked == false)
{
$this->_locktest = $this->cache->lock($id, $group);
}
ob_start();
ob_implicit_flush(false);
// Set id and group placeholders
$this->_id = $id;
$this->_group = $group;
return false;
}
/**
* Stop the cache buffer and store the cached data
*
* @return boolean True if the cache data was stored
*
* @since 1.7.0
* @deprecated 4.0
*/
public function end()
{
Log::add(
__METHOD__ . '() is deprecated.',
Log::WARNING,
'deprecated'
);
// Get data from output buffer and echo it
$data = ob_get_clean();
echo $data;
// Get the ID and group and reset the placeholders
$id = $this->_id;
$group = $this->_group;
$this->_id = null;
$this->_group = null;
// Get the storage handler and store the cached data
$ret = $this->cache->store(serialize($data), $id, $group);
if ($this->_locktest->locked == true)
{
$this->cache->unlock($id, $group);
}
return $ret;
}
/**
* Get stored cached data by ID and group
*
* @param string $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, $group = null)
{
$data = $this->cache->get($id, $group);
if ($data === false)
{
$locktest = $this->cache->lock($id, $group);
// If locklooped is true try to get the cached data again; it could exist now.
if ($locktest->locked === true && $locktest->locklooped === true)
{
$data = $this->cache->get($id, $group);
}
if ($locktest->locked === true)
{
$this->cache->unlock($id, $group);
}
}
// Check again because we might get it from second attempt
if ($data !== false)
{
// Trim to fix unserialize errors
$data = unserialize(trim($data));
}
return $data;
}
/**
* Store data to cache by ID and group
*
* @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 wrkarounds
*
* @return boolean True if cache stored
*
* @since 1.7.0
*/
public function store($data, $id, $group = null, $wrkarounds = true)
{
$locktest = $this->cache->lock($id, $group);
if ($locktest->locked === false && $locktest->locklooped === true)
{
// We can not store data because another process is in the middle of saving
return false;
}
$result = $this->cache->store(serialize($data), $id, $group);
if ($locktest->locked === true)
{
$this->cache->unlock($id, $group);
}
return $result;
}
}
Controller/PageController.php 0000644 00000011067 15234436765 0012344 0 ustar 00 <?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;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\Cache;
use Joomla\CMS\Cache\CacheController;
/**
* 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 = isset($this->options['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) array('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 = \JFactory::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,
array(
'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 = \JFactory::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)
{
\JFactory::getApplication()->setHeader('ETag', '"' . $etag . '"', true);
}
}
Controller/ViewController.php 0000644 00000006414 15234436765 0012402 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2010 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;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\Cache;
use Joomla\CMS\Cache\CacheController;
/**
* Joomla! Cache view type object
*
* @since 1.7.0
*/
class ViewController extends CacheController
{
/**
* Get the cached view data
*
* @param object $view The view object to cache output for
* @param string $method The method name of the view method to cache output for
* @param mixed $id The cache data ID
* @param boolean $wrkarounds True to enable workarounds.
*
* @return boolean True if the cache is hit (false else)
*
* @since 1.7.0
*/
public function get($view, $method = 'display', $id = false, $wrkarounds = true)
{
// If an id is not given generate it from the request
if (!$id)
{
$id = $this->_makeId($view, $method);
}
$data = $this->cache->get($id);
$locktest = (object) array('locked' => null, 'locklooped' => null);
if ($data === false)
{
$locktest = $this->cache->lock($id);
/*
* If the loop is completed and returned true it means the lock has been set.
* If looped is true try to get the cached data again; it could exist now.
*/
if ($locktest->locked === true && $locktest->locklooped === true)
{
$data = $this->cache->get($id);
}
// False means that locking is either turned off or maxtime has been exceeded. Execute the view.
}
if ($data !== false)
{
if ($locktest->locked === true)
{
$this->cache->unlock($id);
}
$data = unserialize(trim($data));
if ($wrkarounds)
{
echo Cache::getWorkarounds($data);
}
else
{
// No workarounds, so all data is stored in one piece
echo $data;
}
return true;
}
// No hit so we have to execute the view
if (!method_exists($view, $method))
{
return false;
}
if ($locktest->locked === false && $locktest->locklooped === true)
{
// We can not store data because another process is in the middle of saving
$view->$method();
return false;
}
// Capture and echo output
ob_start();
ob_implicit_flush(false);
$view->$method();
$data = ob_get_clean();
echo $data;
/*
* For a view we have a special case. We need to cache not only the output from the view, but the state
* of the document head after the view has been rendered. This will allow us to properly cache any attached
* scripts or stylesheets or links or any other modifications that the view has made to the document object
*/
if ($wrkarounds)
{
$data = Cache::setWorkarounds($data);
}
// Store the cache data
$this->cache->store(serialize($data), $id);
if ($locktest->locked === true)
{
$this->cache->unlock($id);
}
return false;
}
/**
* Generate a view cache ID.
*
* @param object $view The view object to cache output for
* @param string $method The method name to cache for the view object
*
* @return string MD5 Hash
*
* @since 1.7.0
*/
protected function _makeId($view, $method)
{
return md5(serialize(array(Cache::makeId(), get_class($view), $method)));
}
}
Exception/CacheConnectingException.php 0000644 00000000742 15234436765 0014127 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Exception;
defined('JPATH_PLATFORM') or die;
/**
* Exception class defining an error connecting to the cache storage engine
*
* @since 3.6.3
*/
class CacheConnectingException extends \RuntimeException implements CacheExceptionInterface
{
}
Exception/CacheExceptionInterface.php 0000644 00000000622 15234436765 0013735 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Exception;
defined('JPATH_PLATFORM') or die;
/**
* Exception interface defining a cache storage error
*
* @since 3.7.0
*/
interface CacheExceptionInterface
{
}
Exception/UnsupportedCacheException.php 0000644 00000000727 15234436765 0014373 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Exception;
defined('JPATH_PLATFORM') or die;
/**
* Exception class defining an unsupported cache storage object
*
* @since 3.6.3
*/
class UnsupportedCacheException extends \RuntimeException implements CacheExceptionInterface
{
}
Storage/ApcStorage.php 0000644 00000015400 15234436765 0010730 0 ustar 00 <?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\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\CacheStorage;
/**
* APC cache storage handler
*
* @link https://www.php.net/manual/en/book.apc.php
* @since 1.7.0
* @deprecated 4.0 Use the APCu handler instead
*/
class ApcStorage extends CacheStorage
{
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
return apc_exists($this->_getCacheId($id, $group));
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function get($id, $group, $checkTime = true)
{
return apc_fetch($this->_getCacheId($id, $group));
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function getAll()
{
$allinfo = apc_cache_info('user');
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
$data = array();
foreach ($keys as $key)
{
if (isset($key['info']))
{
// If APCu is being used for this adapter, the internal key name changed with APCu 4.0.7 from key to info
$name = $key['info'];
}
elseif (isset($key['entry_name']))
{
// Some APC modules changed the internal key name from key to entry_name, HHVM is one such case
$name = $key['entry_name'];
}
else
{
// A fall back for the old internal key name
$name = $key['key'];
}
$namearr = explode('-', $name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new CacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key['mem_size']);
$data[$group] = $item;
}
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 1.7.0
*/
public function store($id, $group, $data)
{
return apc_store($this->_getCacheId($id, $group), $data, $this->_lifetime);
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function remove($id, $group)
{
return apc_delete($this->_getCacheId($id, $group));
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 1.7.0
*/
public function clean($group, $mode = null)
{
$allinfo = apc_cache_info('user');
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (isset($key['info']))
{
// If APCu is being used for this adapter, the internal key name changed with APCu 4.0.7 from key to info
$internalKey = $key['info'];
}
elseif (isset($key['entry_name']))
{
// Some APC modules changed the internal key name from key to entry_name, HHVM is one such case
$internalKey = $key['entry_name'];
}
else
{
// A fall back for the old internal key name
$internalKey = $key['key'];
}
if (strpos($internalKey, $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
apc_delete($internalKey);
}
}
return true;
}
/**
* Garbage collect expired cache data
*
* @return boolean
*
* @since 1.7.0
*/
public function gc()
{
$allinfo = apc_cache_info('user');
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (isset($key['info']))
{
// If APCu is being used for this adapter, the internal key name changed with APCu 4.0.7 from key to info
$internalKey = $key['info'];
}
elseif (isset($key['entry_name']))
{
// Some APC modules changed the internal key name from key to entry_name, HHVM is one such case
$internalKey = $key['entry_name'];
}
else
{
// A fall back for the old internal key name
$internalKey = $key['key'];
}
if (strpos($internalKey, $secret . '-cache-'))
{
apc_fetch($internalKey);
}
}
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.0.0
*/
public static function isSupported()
{
$supported = extension_loaded('apc') && ini_get('apc.enabled');
// If on the CLI interface, the `apc.enable_cli` option must also be enabled
if ($supported && php_sapi_name() === 'cli')
{
$supported = ini_get('apc.enable_cli');
}
return (bool) $supported;
}
/**
* Lock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return mixed Boolean false if locking failed or an object containing properties lock and locklooped
*
* @since 1.7.0
*/
public function lock($id, $group, $locktime)
{
$returning = new \stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$cache_id = $this->_getCacheId($id, $group) . '_lock';
$data_lock = apc_add($cache_id, 1, $locktime);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released. That implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
$returning->locked = false;
$returning->locklooped = true;
break;
}
usleep(100);
$data_lock = apc_add($cache_id, 1, $locktime);
$lock_counter++;
}
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function unlock($id, $group = null)
{
return apc_delete($this->_getCacheId($id, $group) . '_lock');
}
}
Storage/ApcuStorage.php 0000644 00000015445 15234436765 0011126 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\CacheStorage;
/**
* APCu cache storage handler
*
* @link https://www.php.net/manual/en/ref.apcu.php
* @since 3.5
*/
class ApcuStorage extends CacheStorage
{
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
return apcu_exists($this->_getCacheId($id, $group));
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 3.5
*/
public function get($id, $group, $checkTime = true)
{
return apcu_fetch($this->_getCacheId($id, $group));
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 3.5
*/
public function getAll()
{
$allinfo = apcu_cache_info();
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
$data = array();
foreach ($keys as $key)
{
if (isset($key['info']))
{
// The internal key name changed with APCu 4.0.7 from key to info
$name = $key['info'];
}
elseif (isset($key['entry_name']))
{
// Some APCu modules changed the internal key name from key to entry_name
$name = $key['entry_name'];
}
else
{
// A fall back for the old internal key name
$name = $key['key'];
}
$namearr = explode('-', $name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new CacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key['mem_size']);
$data[$group] = $item;
}
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 3.5
*/
public function store($id, $group, $data)
{
return apcu_store($this->_getCacheId($id, $group), $data, $this->_lifetime);
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.5
*/
public function remove($id, $group)
{
$cache_id = $this->_getCacheId($id, $group);
// The apcu_delete function returns false if the ID does not exist
if (apcu_exists($cache_id))
{
return apcu_delete($cache_id);
}
return true;
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 3.5
*/
public function clean($group, $mode = null)
{
$allinfo = apcu_cache_info();
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (isset($key['info']))
{
// The internal key name changed with APCu 4.0.7 from key to info
$internalKey = $key['info'];
}
elseif (isset($key['entry_name']))
{
// Some APCu modules changed the internal key name from key to entry_name
$internalKey = $key['entry_name'];
}
else
{
// A fall back for the old internal key name
$internalKey = $key['key'];
}
if (strpos($internalKey, $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
apcu_delete($internalKey);
}
}
return true;
}
/**
* Garbage collect expired cache data
*
* @return boolean
*
* @since 3.5
*/
public function gc()
{
$allinfo = apcu_cache_info();
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (isset($key['info']))
{
// The internal key name changed with APCu 4.0.7 from key to info
$internalKey = $key['info'];
}
elseif (isset($key['entry_name']))
{
// Some APCu modules changed the internal key name from key to entry_name
$internalKey = $key['entry_name'];
}
else
{
// A fall back for the old internal key name
$internalKey = $key['key'];
}
if (strpos($internalKey, $secret . '-cache-'))
{
apcu_fetch($internalKey);
}
}
return true;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.5
*/
public static function isSupported()
{
$supported = extension_loaded('apcu') && ini_get('apc.enabled');
// If on the CLI interface, the `apc.enable_cli` option must also be enabled
if ($supported && php_sapi_name() === 'cli')
{
$supported = ini_get('apc.enable_cli');
}
return (bool) $supported;
}
/**
* Lock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return mixed Boolean false if locking failed or an object containing properties lock and locklooped
*
* @since 3.5
*/
public function lock($id, $group, $locktime)
{
$returning = new \stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$cache_id = $this->_getCacheId($id, $group) . '_lock';
$data_lock = apcu_add($cache_id, 1, $locktime);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
$returning->locked = false;
$returning->locklooped = true;
break;
}
usleep(100);
$data_lock = apcu_add($cache_id, 1, $locktime);
$lock_counter++;
}
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.5
*/
public function unlock($id, $group = null)
{
$cache_id = $this->_getCacheId($id, $group) . '_lock';
// The apcu_delete function returns false if the ID does not exist
if (apcu_exists($cache_id))
{
return apcu_delete($cache_id);
}
return true;
}
}
Storage/CacheStorageHelper.php 0000644 00000002012 15234436765 0012363 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Storage;
defined('JPATH_PLATFORM') or die;
/**
* Cache storage helper functions.
*
* @since 1.7.0
*/
class CacheStorageHelper
{
/**
* Cache data group
*
* @var string
* @since 1.7.0
*/
public $group = '';
/**
* Cached item size
*
* @var string
* @since 1.7.0
*/
public $size = 0;
/**
* Counter
*
* @var integer
* @since 1.7.0
*/
public $count = 0;
/**
* Constructor
*
* @param string $group The cache data group
*
* @since 1.7.0
*/
public function __construct($group)
{
$this->group = $group;
}
/**
* Increase cache items count.
*
* @param string $size Cached item size
*
* @return void
*
* @since 1.7.0
*/
public function updateSize($size)
{
$this->size += $size;
$this->count++;
}
}
Storage/CacheliteStorage.php 0000644 00000017144 15234436765 0012115 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\CacheStorage;
/**
* Cache lite storage handler
*
* @link http://pear.php.net/package/Cache_Lite/
* @since 1.7.0
* @deprecated 4.0 Deprecated without replacement
*/
class CacheliteStorage extends CacheStorage
{
/**
* Singleton Cache_Lite instance
*
* @var \Cache_Lite
* @since 1.7.0
*/
protected static $CacheLiteInstance = null;
/**
* Root path
*
* @var string
* @since 1.7.0
*/
protected $_root;
/**
* Constructor
*
* @param array $options Optional parameters.
*
* @since 1.7.0
*/
public function __construct($options = array())
{
parent::__construct($options);
$this->_root = $options['cachebase'];
$cloptions = array(
'cacheDir' => $this->_root . '/',
'lifeTime' => $this->_lifetime,
'fileLocking' => $this->_locking,
'automaticCleaningFactor' => isset($options['autoclean']) ? $options['autoclean'] : 200,
'fileNameProtection' => false,
'hashedDirectoryLevel' => 0,
'caching' => $options['caching'],
);
if (static::$CacheLiteInstance === null)
{
$this->initCache($cloptions);
}
}
/**
* Instantiates the Cache_Lite object. Only initializes the engine if it does not already exist.
*
* @param array $cloptions optional parameters
*
* @return \Cache_Lite
*
* @since 1.7.0
*/
protected function initCache($cloptions)
{
if (!class_exists('\\Cache_Lite'))
{
require_once 'Cache/Lite.php';
}
static::$CacheLiteInstance = new \Cache_Lite($cloptions);
return static::$CacheLiteInstance;
}
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
return $this->get($id, $group) !== false;
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function get($id, $group, $checkTime = true)
{
static::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
// This call is needed to ensure $this->rawname is set
$this->_getCacheId($id, $group);
return static::$CacheLiteInstance->get($this->rawname, $group);
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function getAll()
{
$path = $this->_root;
$folders = new \DirectoryIterator($path);
$data = array();
foreach ($folders as $folder)
{
if (!$folder->isDir() || $folder->isDot())
{
continue;
}
$foldername = $folder->getFilename();
$files = new \DirectoryIterator($path . '/' . $foldername);
$item = new CacheStorageHelper($foldername);
foreach ($files as $file)
{
if (!$file->isFile())
{
continue;
}
$filename = $file->getFilename();
$item->updateSize(filesize($path . '/' . $foldername . '/' . $filename));
}
$data[$foldername] = $item;
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 1.7.0
*/
public function store($id, $group, $data)
{
$dir = $this->_root . '/' . $group;
// If the folder doesn't exist try to create it
if (!is_dir($dir))
{
// Make sure the index file is there
$indexFile = $dir . '/index.html';
@mkdir($dir) && file_put_contents($indexFile, '<!DOCTYPE html><title></title>');
}
// Make sure the folder exists
if (!is_dir($dir))
{
return false;
}
static::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
// This call is needed to ensure $this->rawname is set
$this->_getCacheId($id, $group);
return static::$CacheLiteInstance->save($data, $this->rawname, $group);
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function remove($id, $group)
{
static::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
// This call is needed to ensure $this->rawname is set
$this->_getCacheId($id, $group);
return static::$CacheLiteInstance->remove($this->rawname, $group);
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 1.7.0
*/
public function clean($group, $mode = null)
{
\JLoader::import('joomla.filesystem.folder');
\JLoader::import('joomla.filesystem.file');
switch ($mode)
{
case 'notgroup':
$clmode = 'notingroup';
$success = static::$CacheLiteInstance->clean($group, $clmode);
break;
case 'group':
if (is_dir($this->_root . '/' . $group))
{
$clmode = $group;
static::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
$success = static::$CacheLiteInstance->clean($group, $clmode);
// Remove sub-folders of folder; disable all filtering
$folders = \JFolder::folders($this->_root . '/' . $group, '.', false, true, array(), array());
foreach ($folders as $folder)
{
if (is_link($folder))
{
if (\JFile::delete($folder) !== true)
{
return false;
}
}
elseif (\JFolder::delete($folder) !== true)
{
return false;
}
}
}
else
{
$success = true;
}
break;
default:
if (is_dir($this->_root . '/' . $group))
{
$clmode = $group;
static::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
$success = static::$CacheLiteInstance->clean($group, $clmode);
}
else
{
$success = true;
}
break;
}
return $success;
}
/**
* Garbage collect expired cache data
*
* @return boolean
*
* @since 1.7.0
*/
public function gc()
{
$result = true;
static::$CacheLiteInstance->setOption('automaticCleaningFactor', 1);
static::$CacheLiteInstance->setOption('hashedDirectoryLevel', 1);
$success1 = static::$CacheLiteInstance->_cleanDir($this->_root . '/', false, 'old');
if (!($dh = opendir($this->_root . '/')))
{
return false;
}
while ($file = readdir($dh))
{
if (($file != '.') && ($file != '..') && ($file != '.svn'))
{
$file2 = $this->_root . '/' . $file;
if (is_dir($file2))
{
$result = ($result && (static::$CacheLiteInstance->_cleanDir($file2 . '/', false, 'old')));
}
}
}
$success = ($success1 && $result);
return $success;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.0.0
*/
public static function isSupported()
{
@include_once 'Cache/Lite.php';
return class_exists('\Cache_Lite');
}
}
Storage/FileStorage.php 0000644 00000042023 15234436765 0011105 0 ustar 00 <?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\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\CacheStorage;
use Joomla\CMS\Log\Log;
/**
* File cache storage handler
*
* @since 1.7.0
* @note For performance reasons this class does not use the Filesystem package's API
*/
class FileStorage extends CacheStorage
{
/**
* Root path
*
* @var string
* @since 1.7.0
*/
protected $_root;
/**
* Locked resources
*
* @var array
* @since 3.7.0
*
*/
protected $_locked_files = array();
/**
* Constructor
*
* @param array $options Optional parameters
*
* @since 1.7.0
*/
public function __construct($options = array())
{
parent::__construct($options);
$this->_root = $options['cachebase'];
// Workaround for php 5.3
$locked_files = &$this->_locked_files;
// Remove empty locked files at script shutdown.
$clearAtShutdown = function () use (&$locked_files)
{
foreach ($locked_files as $path => $handle)
{
if (is_resource($handle))
{
@flock($handle, LOCK_UN);
@fclose($handle);
}
// Delete only the existing file if it is empty.
if (@filesize($path) === 0)
{
@unlink($path);
}
unset($locked_files[$path]);
}
};
register_shutdown_function($clearAtShutdown);
}
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
return $this->_checkExpire($id, $group);
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function get($id, $group, $checkTime = true)
{
$path = $this->_getFilePath($id, $group);
$close = false;
if ($checkTime == false || ($checkTime == true && $this->_checkExpire($id, $group) === true))
{
if (file_exists($path))
{
if (isset($this->_locked_files[$path]))
{
$_fileopen = $this->_locked_files[$path];
}
else
{
$_fileopen = @fopen($path, 'rb');
// There is no lock, we have to close file after store data
$close = true;
}
if ($_fileopen)
{
// On Windows system we can not use file_get_contents on the file locked by yourself
$data = stream_get_contents($_fileopen);
if ($close)
{
@fclose($_fileopen);
}
if ($data !== false)
{
// Remove the initial die() statement
return str_replace('<?php die("Access Denied"); ?>#x#', '', $data);
}
}
}
}
return false;
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function getAll()
{
$path = $this->_root;
$folders = $this->_folders($path);
$data = array();
foreach ($folders as $folder)
{
$files = $this->_filesInFolder($path . '/' . $folder);
$item = new CacheStorageHelper($folder);
foreach ($files as $file)
{
$item->updateSize(filesize($path . '/' . $folder . '/' . $file));
}
$data[$folder] = $item;
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 1.7.0
*/
public function store($id, $group, $data)
{
$path = $this->_getFilePath($id, $group);
$close = false;
// Prepend a die string
$data = '<?php die("Access Denied"); ?>#x#' . $data;
if (isset($this->_locked_files[$path]))
{
$_fileopen = $this->_locked_files[$path];
// Because lock method uses flag c+b we have to truncate it manually
@ftruncate($_fileopen, 0);
}
else
{
$_fileopen = @fopen($path, 'wb');
// There is no lock, we have to close file after store data
$close = true;
}
if ($_fileopen)
{
$length = strlen($data);
$result = @fwrite($_fileopen, $data, $length);
if ($close)
{
@fclose($_fileopen);
}
return $result === $length;
}
return false;
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function remove($id, $group)
{
$path = $this->_getFilePath($id, $group);
if (!@unlink($path))
{
return false;
}
return true;
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 1.7.0
*/
public function clean($group, $mode = null)
{
$return = true;
$folder = $group;
if (trim($folder) == '')
{
$mode = 'notgroup';
}
switch ($mode)
{
case 'notgroup' :
$folders = $this->_folders($this->_root);
for ($i = 0, $n = count($folders); $i < $n; $i++)
{
if ($folders[$i] != $folder)
{
$return |= $this->_deleteFolder($this->_root . '/' . $folders[$i]);
}
}
break;
case 'group' :
default :
if (is_dir($this->_root . '/' . $folder))
{
$return = $this->_deleteFolder($this->_root . '/' . $folder);
}
break;
}
return (bool) $return;
}
/**
* Garbage collect expired cache data
*
* @return boolean
*
* @since 1.7.0
*/
public function gc()
{
$result = true;
// Files older than lifeTime get deleted from cache
$files = $this->_filesInFolder($this->_root, '', true, true, array('.svn', 'CVS', '.DS_Store', '__MACOSX', 'index.html'));
foreach ($files as $file)
{
$time = @filemtime($file);
if (($time + $this->_lifetime) < $this->_now || empty($time))
{
$result |= @unlink($file);
}
}
return (bool) $result;
}
/**
* Lock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return mixed Boolean false if locking failed or an object containing properties lock and locklooped
*
* @since 1.7.0
*/
public function lock($id, $group, $locktime)
{
$returning = new \stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$path = $this->_getFilePath($id, $group);
$_fileopen = @fopen($path, 'c+b');
if (!$_fileopen)
{
$returning->locked = false;
return $returning;
}
$data_lock = (bool) @flock($_fileopen, LOCK_EX|LOCK_NB);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
break;
}
usleep(100);
$data_lock = (bool) @flock($_fileopen, LOCK_EX|LOCK_NB);
$lock_counter++;
}
$returning->locklooped = true;
}
if ($data_lock === true)
{
// Remember resource, flock release lock if you unset/close resource
$this->_locked_files[$path] = $_fileopen;
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function unlock($id, $group = null)
{
$path = $this->_getFilePath($id, $group);
if (isset($this->_locked_files[$path]))
{
$ret = (bool) @flock($this->_locked_files[$path], LOCK_UN);
@fclose($this->_locked_files[$path]);
unset($this->_locked_files[$path]);
return $ret;
}
return true;
}
/**
* Check if a cache object has expired
*
* Using @ error suppressor here because between if we did a file_exists() and then filemsize() there will
* be a little time space when another process can delete the file and then you get PHP Warning
*
* @param string $id Cache ID to check
* @param string $group The cache data group
*
* @return boolean True if the cache ID is valid
*
* @since 1.7.0
*/
protected function _checkExpire($id, $group)
{
$path = $this->_getFilePath($id, $group);
// Check prune period
if (file_exists($path))
{
$time = @filemtime($path);
if (($time + $this->_lifetime) < $this->_now || empty($time))
{
@unlink($path);
return false;
}
// If, right now, the file does not exist then return false
if (@filesize($path) == 0)
{
return false;
}
return true;
}
return false;
}
/**
* Get a cache file path from an ID/group pair
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean|string The path to the data object or boolean false if the cache directory does not exist
*
* @since 1.7.0
*/
protected function _getFilePath($id, $group)
{
$name = $this->_getCacheId($id, $group);
$dir = $this->_root . '/' . $group;
// If the folder doesn't exist try to create it
if (!is_dir($dir))
{
// Make sure the index file is there
$indexFile = $dir . '/index.html';
@mkdir($dir) && file_put_contents($indexFile, '<!DOCTYPE html><title></title>');
}
// Make sure the folder exists
if (!is_dir($dir))
{
return false;
}
return $dir . '/' . $name . '.php';
}
/**
* Quickly delete a folder of files
*
* @param string $path The path to the folder to delete.
*
* @return boolean
*
* @since 1.7.0
*/
protected function _deleteFolder($path)
{
// Sanity check
if (!$path || !is_dir($path) || empty($this->_root))
{
// Bad programmer! Bad, bad programmer!
Log::add(__METHOD__ . ' ' . \JText::_('JLIB_FILESYSTEM_ERROR_DELETE_BASE_DIRECTORY'), Log::WARNING, 'jerror');
return false;
}
$path = $this->_cleanPath($path);
// Check to make sure path is inside cache folder, we do not want to delete Joomla root!
$pos = strpos($path, $this->_cleanPath($this->_root));
if ($pos === false || $pos > 0)
{
Log::add(__METHOD__ . ' ' . \JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path), Log::WARNING, 'jerror');
return false;
}
// Remove all the files in folder if they exist; disable all filtering
$files = $this->_filesInFolder($path, '.', false, true, array(), array());
if (!empty($files) && !is_array($files))
{
if (@unlink($files) !== true)
{
return false;
}
}
elseif (!empty($files) && is_array($files))
{
foreach ($files as $file)
{
$file = $this->_cleanPath($file);
// In case of restricted permissions we zap it one way or the other as long as the owner is either the webserver or the ftp
if (@unlink($file) !== true)
{
Log::add(__METHOD__ . ' ' . \JText::sprintf('JLIB_FILESYSTEM_DELETE_FAILED', basename($file)), Log::WARNING, 'jerror');
return false;
}
}
}
// Remove sub-folders of folder; disable all filtering
$folders = $this->_folders($path, '.', false, true, array(), array());
foreach ($folders as $folder)
{
if (is_link($folder))
{
// Don't descend into linked directories, just delete the link.
if (@unlink($folder) !== true)
{
return false;
}
}
elseif ($this->_deleteFolder($folder) !== true)
{
return false;
}
}
// In case of restricted permissions we zap it one way or the other as long as the owner is either the webserver or the ftp
if (@rmdir($path))
{
return true;
}
Log::add(__METHOD__ . ' ' . \JText::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_DELETE', $path), Log::WARNING, 'jerror');
return false;
}
/**
* Function to strip additional / or \ in a path name
*
* @param string $path The path to clean
* @param string $ds Directory separator (optional)
*
* @return string The cleaned path
*
* @since 1.7.0
*/
protected function _cleanPath($path, $ds = DIRECTORY_SEPARATOR)
{
$path = trim($path);
if (empty($path))
{
return $this->_root;
}
// Remove double slashes and backslashes and convert all slashes and backslashes to DIRECTORY_SEPARATOR
$path = preg_replace('#[/\\\\]+#', $ds, $path);
return $path;
}
/**
* Utility function to quickly read the files in a folder.
*
* @param string $path The path of the folder to read.
* @param string $filter A filter for file names.
* @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth.
* @param boolean $fullpath True to return the full path to the file.
* @param array $exclude Array with names of files which should not be shown in the result.
* @param array $excludefilter Array of folder names to exclude
*
* @return array Files in the given folder.
*
* @since 1.7.0
*/
protected function _filesInFolder($path, $filter = '.', $recurse = false, $fullpath = false,
$exclude = array('.svn', 'CVS', '.DS_Store', '__MACOSX'), $excludefilter = array('^\..*', '.*~'))
{
$arr = array();
// Check to make sure the path valid and clean
$path = $this->_cleanPath($path);
// Is the path a folder?
if (!is_dir($path))
{
Log::add(__METHOD__ . ' ' . \JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path), Log::WARNING, 'jerror');
return false;
}
// Read the source directory.
if (!($handle = @opendir($path)))
{
return $arr;
}
if (count($excludefilter))
{
$excludefilter = '/(' . implode('|', $excludefilter) . ')/';
}
else
{
$excludefilter = '';
}
while (($file = readdir($handle)) !== false)
{
if (($file != '.') && ($file != '..') && (!in_array($file, $exclude)) && (!$excludefilter || !preg_match($excludefilter, $file)))
{
$dir = $path . '/' . $file;
$isDir = is_dir($dir);
if ($isDir)
{
if ($recurse)
{
if (is_int($recurse))
{
$arr2 = $this->_filesInFolder($dir, $filter, $recurse - 1, $fullpath);
}
else
{
$arr2 = $this->_filesInFolder($dir, $filter, $recurse, $fullpath);
}
$arr = array_merge($arr, $arr2);
}
}
else
{
if (preg_match("/$filter/", $file))
{
if ($fullpath)
{
$arr[] = $path . '/' . $file;
}
else
{
$arr[] = $file;
}
}
}
}
}
closedir($handle);
return $arr;
}
/**
* Utility function to read the folders in a folder.
*
* @param string $path The path of the folder to read.
* @param string $filter A filter for folder names.
* @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth.
* @param boolean $fullpath True to return the full path to the folders.
* @param array $exclude Array with names of folders which should not be shown in the result.
* @param array $excludefilter Array with regular expressions matching folders which should not be shown in the result.
*
* @return array Folders in the given folder.
*
* @since 1.7.0
*/
protected function _folders($path, $filter = '.', $recurse = false, $fullpath = false, $exclude = array('.svn', 'CVS', '.DS_Store', '__MACOSX'),
$excludefilter = array('^\..*'))
{
$arr = array();
// Check to make sure the path valid and clean
$path = $this->_cleanPath($path);
// Is the path a folder?
if (!is_dir($path))
{
Log::add(__METHOD__ . ' ' . \JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path), Log::WARNING, 'jerror');
return false;
}
// Read the source directory
if (!($handle = @opendir($path)))
{
return $arr;
}
if (count($excludefilter))
{
$excludefilter_string = '/(' . implode('|', $excludefilter) . ')/';
}
else
{
$excludefilter_string = '';
}
while (($file = readdir($handle)) !== false)
{
if (($file != '.') && ($file != '..')
&& (!in_array($file, $exclude))
&& (empty($excludefilter_string) || !preg_match($excludefilter_string, $file)))
{
$dir = $path . '/' . $file;
$isDir = is_dir($dir);
if ($isDir)
{
// Removes filtered directories
if (preg_match("/$filter/", $file))
{
if ($fullpath)
{
$arr[] = $dir;
}
else
{
$arr[] = $file;
}
}
if ($recurse)
{
if (is_int($recurse))
{
$arr2 = $this->_folders($dir, $filter, $recurse - 1, $fullpath, $exclude, $excludefilter);
}
else
{
$arr2 = $this->_folders($dir, $filter, $recurse, $fullpath, $exclude, $excludefilter);
}
$arr = array_merge($arr, $arr2);
}
}
}
}
closedir($handle);
return $arr;
}
}
Storage/MemcacheStorage.php 0000644 00000022140 15234436765 0011726 0 ustar 00 <?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\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\Cache;
use Joomla\CMS\Cache\CacheStorage;
use Joomla\CMS\Cache\Exception\CacheConnectingException;
/**
* Memcache cache storage handler
*
* @link https://www.php.net/manual/en/book.memcache.php
* @since 1.7.0
* @deprecated 4.0 Use the Memcached handler instead
*/
class MemcacheStorage extends CacheStorage
{
/**
* Memcache connection object
*
* @var \Memcache
* @since 1.7.0
*/
protected static $_db = null;
/**
* Payload compression level
*
* @var integer
* @since 1.7.0
*/
protected $_compress = 0;
/**
* Constructor
*
* @param array $options Optional parameters.
*
* @since 1.7.0
*/
public function __construct($options = array())
{
parent::__construct($options);
$this->_compress = \JFactory::getConfig()->get('memcache_compress', false) ? MEMCACHE_COMPRESSED : 0;
if (static::$_db === null)
{
$this->getConnection();
}
}
/**
* Create the Memcache connection
*
* @return void
*
* @since 1.7.0
* @throws \RuntimeException
*/
protected function getConnection()
{
if (!static::isSupported())
{
throw new \RuntimeException('Memcache Extension is not available');
}
$config = \JFactory::getConfig();
$host = $config->get('memcache_server_host', 'localhost');
$port = $config->get('memcache_server_port', 11211);
// Create the memcache connection
static::$_db = new \Memcache;
if ($config->get('memcache_persist', true))
{
$result = @static::$_db->pconnect($host, $port);
}
else
{
$result = @static::$_db->connect($host, $port);
}
if (!$result)
{
// Null out the connection to inform the constructor it will need to attempt to connect if this class is instantiated again
static::$_db = null;
throw new CacheConnectingException('Could not connect to memcache server');
}
}
/**
* Get a cache_id string from an id/group pair
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return string The cache_id string
*
* @since 1.7.0
*/
protected function _getCacheId($id, $group)
{
$prefix = Cache::getPlatformPrefix();
$length = strlen($prefix);
$cache_id = parent::_getCacheId($id, $group);
if ($length)
{
// Memcache use suffix instead of prefix
$cache_id = substr($cache_id, $length) . strrev($prefix);
}
return $cache_id;
}
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
return $this->get($id, $group) !== false;
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function get($id, $group, $checkTime = true)
{
return static::$_db->get($this->_getCacheId($id, $group));
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function getAll()
{
$keys = static::$_db->get($this->_hash . '-index');
$secret = $this->_hash;
$data = array();
if (is_array($keys))
{
foreach ($keys as $key)
{
if (empty($key))
{
continue;
}
$namearr = explode('-', $key->name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new CacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key->size);
$data[$group] = $item;
}
}
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 1.7.0
*/
public function store($id, $group, $data)
{
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = static::$_db->get($this->_hash . '-index');
if (!is_array($index))
{
$index = array();
}
$tmparr = new \stdClass;
$tmparr->name = $cache_id;
$tmparr->size = strlen($data);
$index[] = $tmparr;
static::$_db->set($this->_hash . '-index', $index, 0, 0);
$this->unlockindex();
static::$_db->set($cache_id, $data, $this->_compress, $this->_lifetime);
return true;
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function remove($id, $group)
{
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = static::$_db->get($this->_hash . '-index');
if (is_array($index))
{
foreach ($index as $key => $value)
{
if ($value->name == $cache_id)
{
unset($index[$key]);
static::$_db->set($this->_hash . '-index', $index, 0, 0);
break;
}
}
}
$this->unlockindex();
return static::$_db->delete($cache_id);
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 1.7.0
*/
public function clean($group, $mode = null)
{
if (!$this->lockindex())
{
return false;
}
$index = static::$_db->get($this->_hash . '-index');
if (is_array($index))
{
$prefix = $this->_hash . '-cache-' . $group . '-';
foreach ($index as $key => $value)
{
if (strpos($value->name, $prefix) === 0 xor $mode != 'group')
{
static::$_db->delete($value->name);
unset($index[$key]);
}
}
static::$_db->set($this->_hash . '-index', $index, 0, 0);
}
$this->unlockindex();
return true;
}
/**
* Flush all existing items in storage.
*
* @return boolean
*
* @since 3.6.3
*/
public function flush()
{
if (!$this->lockindex())
{
return false;
}
return static::$_db->flush();
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.0.0
*/
public static function isSupported()
{
return extension_loaded('memcache') && class_exists('\\Memcache');
}
/**
* Lock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return mixed Boolean false if locking failed or an object containing properties lock and locklooped
*
* @since 1.7.0
*/
public function lock($id, $group, $locktime)
{
$returning = new \stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$cache_id = $this->_getCacheId($id, $group);
$data_lock = static::$_db->add($cache_id . '_lock', 1, 0, $locktime);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished.
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
break;
}
usleep(100);
$data_lock = static::$_db->add($cache_id . '_lock', 1, 0, $locktime);
$lock_counter++;
}
$returning->locklooped = true;
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function unlock($id, $group = null)
{
$cache_id = $this->_getCacheId($id, $group) . '_lock';
return static::$_db->delete($cache_id);
}
/**
* Lock cache index
*
* @return boolean
*
* @since 1.7.0
*/
protected function lockindex()
{
$looptime = 300;
$data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 0, 30);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released. that implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
return false;
}
usleep(100);
$data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 0, 30);
$lock_counter++;
}
}
return true;
}
/**
* Unlock cache index
*
* @return boolean
*
* @since 1.7.0
*/
protected function unlockindex()
{
return static::$_db->delete($this->_hash . '-index_lock');
}
}
Storage/MemcachedStorage.php 0000644 00000023256 15234436765 0012103 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\Cache;
use Joomla\CMS\Cache\CacheStorage;
use Joomla\CMS\Cache\Exception\CacheConnectingException;
/**
* Memcached cache storage handler
*
* @link https://www.php.net/manual/en/book.memcached.php
* @since 3.0.0
*/
class MemcachedStorage extends CacheStorage
{
/**
* Memcached connection object
*
* @var \Memcached
* @since 3.0.0
*/
protected static $_db = null;
/**
* Payload compression level
*
* @var integer
* @since 3.0.0
*/
protected $_compress = 0;
/**
* Constructor
*
* @param array $options Optional parameters.
*
* @since 3.0.0
*/
public function __construct($options = array())
{
parent::__construct($options);
$this->_compress = \JFactory::getConfig()->get('memcached_compress', false) ? \Memcached::OPT_COMPRESSION : 0;
if (static::$_db === null)
{
$this->getConnection();
}
}
/**
* Create the Memcached connection
*
* @return void
*
* @since 3.0.0
* @throws \RuntimeException
*/
protected function getConnection()
{
if (!static::isSupported())
{
throw new \RuntimeException('Memcached Extension is not available');
}
$config = \JFactory::getConfig();
$host = $config->get('memcached_server_host', 'localhost');
$port = $config->get('memcached_server_port', 11211);
// Create the memcached connection
if ($config->get('memcached_persist', true))
{
static::$_db = new \Memcached($this->_hash);
$servers = static::$_db->getServerList();
if ($servers && ($servers[0]['host'] != $host || $servers[0]['port'] != $port))
{
static::$_db->resetServerList();
$servers = array();
}
if (!$servers)
{
static::$_db->addServer($host, $port);
}
}
else
{
static::$_db = new \Memcached;
static::$_db->addServer($host, $port);
}
static::$_db->setOption(\Memcached::OPT_COMPRESSION, $this->_compress);
$stats = static::$_db->getStats();
$result = !empty($stats["$host:$port"]) && $stats["$host:$port"]['pid'] > 0;
if (!$result)
{
// Null out the connection to inform the constructor it will need to attempt to connect if this class is instantiated again
static::$_db = null;
throw new CacheConnectingException('Could not connect to memcached server');
}
}
/**
* Get a cache_id string from an id/group pair
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return string The cache_id string
*
* @since 1.7.0
*/
protected function _getCacheId($id, $group)
{
$prefix = Cache::getPlatformPrefix();
$length = strlen($prefix);
$cache_id = parent::_getCacheId($id, $group);
if ($length)
{
// Memcached use suffix instead of prefix
$cache_id = substr($cache_id, $length) . strrev($prefix);
}
return $cache_id;
}
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
static::$_db->get($this->_getCacheId($id, $group));
return static::$_db->getResultCode() !== \Memcached::RES_NOTFOUND;
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 3.0.0
*/
public function get($id, $group, $checkTime = true)
{
return static::$_db->get($this->_getCacheId($id, $group));
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 3.0.0
*/
public function getAll()
{
$keys = static::$_db->get($this->_hash . '-index');
$secret = $this->_hash;
$data = array();
if (is_array($keys))
{
foreach ($keys as $key)
{
if (empty($key))
{
continue;
}
$namearr = explode('-', $key->name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new CacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key->size);
$data[$group] = $item;
}
}
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 3.0.0
*/
public function store($id, $group, $data)
{
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = static::$_db->get($this->_hash . '-index');
if (!is_array($index))
{
$index = array();
}
$tmparr = new \stdClass;
$tmparr->name = $cache_id;
$tmparr->size = strlen($data);
$index[] = $tmparr;
static::$_db->set($this->_hash . '-index', $index, 0);
$this->unlockindex();
static::$_db->set($cache_id, $data, $this->_lifetime);
return true;
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.0.0
*/
public function remove($id, $group)
{
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = static::$_db->get($this->_hash . '-index');
if (is_array($index))
{
foreach ($index as $key => $value)
{
if ($value->name == $cache_id)
{
unset($index[$key]);
static::$_db->set($this->_hash . '-index', $index, 0);
break;
}
}
}
$this->unlockindex();
return static::$_db->delete($cache_id);
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 3.0.0
*/
public function clean($group, $mode = null)
{
if (!$this->lockindex())
{
return false;
}
$index = static::$_db->get($this->_hash . '-index');
if (is_array($index))
{
$prefix = $this->_hash . '-cache-' . $group . '-';
foreach ($index as $key => $value)
{
if (strpos($value->name, $prefix) === 0 xor $mode != 'group')
{
static::$_db->delete($value->name);
unset($index[$key]);
}
}
static::$_db->set($this->_hash . '-index', $index, 0);
}
$this->unlockindex();
return true;
}
/**
* Flush all existing items in storage.
*
* @return boolean
*
* @since 3.6.3
*/
public function flush()
{
if (!$this->lockindex())
{
return false;
}
return static::$_db->flush();
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.0.0
*/
public static function isSupported()
{
/*
* GAE and HHVM have both had instances where Memcached the class was defined but no extension was loaded.
* If the class is there, we can assume support.
*/
return class_exists('Memcached');
}
/**
* Lock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return mixed Boolean false if locking failed or an object containing properties lock and locklooped
*
* @since 3.0.0
*/
public function lock($id, $group, $locktime)
{
$returning = new \stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$cache_id = $this->_getCacheId($id, $group);
$data_lock = static::$_db->add($cache_id . '_lock', 1, $locktime);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished.
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
break;
}
usleep(100);
$data_lock = static::$_db->add($cache_id . '_lock', 1, $locktime);
$lock_counter++;
}
$returning->locklooped = true;
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.0.0
*/
public function unlock($id, $group = null)
{
$cache_id = $this->_getCacheId($id, $group) . '_lock';
return static::$_db->delete($cache_id);
}
/**
* Lock cache index
*
* @return boolean
*
* @since 3.0.0
*/
protected function lockindex()
{
$looptime = 300;
$data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 30);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released. that implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
return false;
}
usleep(100);
$data_lock = static::$_db->add($this->_hash . '-index_lock', 1, 30);
$lock_counter++;
}
}
return true;
}
/**
* Unlock cache index
*
* @return boolean
*
* @since 3.0.0
*/
protected function unlockindex()
{
return static::$_db->delete($this->_hash . '-index_lock');
}
}
Storage/RedisStorage.php 0000644 00000016673 15234436765 0011310 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\CacheStorage;
use Joomla\CMS\Log\Log;
/**
* Redis cache storage handler for PECL
*
* @since 3.4
*/
class RedisStorage extends CacheStorage
{
/**
* Redis connection object
*
* @var \Redis
* @since 3.4
*/
protected static $_redis = null;
/**
* Persistent session flag
*
* @var boolean
* @since 3.4
*/
protected $_persistent = false;
/**
* Constructor
*
* @param array $options Optional parameters.
*
* @since 3.4
*/
public function __construct($options = array())
{
parent::__construct($options);
if (static::$_redis === null)
{
$this->getConnection();
}
}
/**
* Create the Redis connection
*
* @return \Redis|boolean Redis connection object on success, boolean on failure
*
* @since 3.4
* @note As of 4.0 this method will throw a JCacheExceptionConnecting object on connection failure
*/
protected function getConnection()
{
if (static::isSupported() == false)
{
return false;
}
$config = \JFactory::getConfig();
$this->_persistent = $config->get('redis_persist', true);
$server = array(
'host' => $config->get('redis_server_host', 'localhost'),
'port' => $config->get('redis_server_port', 6379),
'auth' => $config->get('redis_server_auth', null),
'db' => (int) $config->get('redis_server_db', null),
);
// If you are trying to connect to a socket file, ignore the supplied port
if ($server['host'][0] === '/')
{
$server['port'] = 0;
}
static::$_redis = new \Redis;
try
{
if ($this->_persistent)
{
$connection = static::$_redis->pconnect($server['host'], $server['port']);
}
else
{
$connection = static::$_redis->connect($server['host'], $server['port']);
}
}
catch (\RedisException $e)
{
Log::add($e->getMessage(), Log::DEBUG);
}
if ($connection == false)
{
static::$_redis = null;
// Because the application instance may not be available on cli script, use it only if needed
if (\JFactory::getApplication()->isClient('administrator'))
{
\JError::raiseWarning(500, 'Redis connection failed');
}
return false;
}
try
{
$auth = $server['auth'] ? static::$_redis->auth($server['auth']) : true;
}
catch (\RedisException $e)
{
$auth = false;
Log::add($e->getMessage(), Log::DEBUG);
}
if ($auth === false)
{
static::$_redis = null;
// Because the application instance may not be available on cli script, use it only if needed
if (\JFactory::getApplication()->isClient('administrator'))
{
\JError::raiseWarning(500, 'Redis authentication failed');
}
return false;
}
$select = static::$_redis->select($server['db']);
if ($select == false)
{
static::$_redis = null;
// Because the application instance may not be available on cli script, use it only if needed
if (\JFactory::getApplication()->isClient('administrator'))
{
\JError::raiseWarning(500, 'Redis failed to select database');
}
return false;
}
try
{
static::$_redis->ping();
}
catch (\RedisException $e)
{
static::$_redis = null;
// Because the application instance may not be available on cli script, use it only if needed
if (\JFactory::getApplication()->isClient('administrator'))
{
\JError::raiseWarning(500, 'Redis ping failed');
}
return false;
}
return static::$_redis;
}
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
if (static::isConnected() == false)
{
return false;
}
// Redis exists returns integer values lets convert that to boolean see: https://redis.io/commands/exists
return (bool) static::$_redis->exists($this->_getCacheId($id, $group));
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 3.4
*/
public function get($id, $group, $checkTime = true)
{
if (static::isConnected() == false)
{
return false;
}
return static::$_redis->get($this->_getCacheId($id, $group));
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 3.4
*/
public function getAll()
{
if (static::isConnected() == false)
{
return false;
}
$allKeys = static::$_redis->keys('*');
$data = array();
$secret = $this->_hash;
if (!empty($allKeys))
{
foreach ($allKeys as $key)
{
$namearr = explode('-', $key);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new CacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize(strlen($key)*8);
$data[$group] = $item;
}
}
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 3.4
*/
public function store($id, $group, $data)
{
if (static::isConnected() == false)
{
return false;
}
static::$_redis->setex($this->_getCacheId($id, $group), $this->_lifetime, $data);
return true;
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.4
*/
public function remove($id, $group)
{
if (static::isConnected() == false)
{
return false;
}
return (bool) static::$_redis->del($this->_getCacheId($id, $group));
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 3.4
*/
public function clean($group, $mode = null)
{
if (static::isConnected() == false)
{
return false;
}
$allKeys = static::$_redis->keys('*');
if ($allKeys === false)
{
$allKeys = array();
}
$secret = $this->_hash;
foreach ($allKeys as $key)
{
if (strpos($key, $secret . '-cache-' . $group . '-') === 0 && $mode == 'group')
{
static::$_redis->del($key);
}
if (strpos($key, $secret . '-cache-' . $group . '-') !== 0 && $mode != 'group')
{
static::$_redis->del($key);
}
}
return true;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.4
*/
public static function isSupported()
{
return class_exists('\\Redis');
}
/**
* Test to see if the Redis connection is available.
*
* @return boolean
*
* @since 3.4
*/
public static function isConnected()
{
return static::$_redis instanceof \Redis;
}
}
Storage/WincacheStorage.php 0000644 00000010204 15234436765 0011743 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\CacheStorage;
/**
* WinCache cache storage handler
*
* @link https://www.php.net/manual/en/book.wincache.php
* @since 1.7.0
*/
class WincacheStorage extends CacheStorage
{
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
return wincache_ucache_exists($this->_getCacheId($id, $group));
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function get($id, $group, $checkTime = true)
{
return wincache_ucache_get($this->_getCacheId($id, $group));
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function getAll()
{
$allinfo = wincache_ucache_info();
$keys = $allinfo['ucache_entries'];
$secret = $this->_hash;
$data = array();
foreach ($keys as $key)
{
$name = $key['key_name'];
$namearr = explode('-', $name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new CacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
if (isset($key['value_size']))
{
$item->updateSize($key['value_size']);
}
else
{
// Dummy, WINCACHE version is too low.
$item->updateSize(1);
}
$data[$group] = $item;
}
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 1.7.0
*/
public function store($id, $group, $data)
{
return wincache_ucache_set($this->_getCacheId($id, $group), $data, $this->_lifetime);
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function remove($id, $group)
{
return wincache_ucache_delete($this->_getCacheId($id, $group));
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 1.7.0
*/
public function clean($group, $mode = null)
{
$allinfo = wincache_ucache_info();
$keys = $allinfo['ucache_entries'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (strpos($key['key_name'], $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
wincache_ucache_delete($key['key_name']);
}
}
return true;
}
/**
* Garbage collect expired cache data
*
* @return boolean
*
* @since 1.7.0
*/
public function gc()
{
$allinfo = wincache_ucache_info();
$keys = $allinfo['ucache_entries'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (strpos($key['key_name'], $secret . '-cache-'))
{
wincache_ucache_get($key['key_name']);
}
}
return true;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.0.0
*/
public static function isSupported()
{
return extension_loaded('wincache') && function_exists('wincache_ucache_get') && !strcmp(ini_get('wincache.ucenabled'), '1');
}
}
Storage/XcacheStorage.php 0000644 00000011730 15234436765 0011422 0 ustar 00 <?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\Storage;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\CacheStorage;
/**
* XCache cache storage handler
*
* @link https://xcache.lighttpd.net/
* @since 1.7.0
* @deprecated 4.0 The XCache PHP extension is not compatible with PHP 7
*/
class XcacheStorage extends CacheStorage
{
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
return xcache_isset($this->_getCacheId($id, $group));
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function get($id, $group, $checkTime = true)
{
// Make sure XCache is configured properly
if (static::isSupported() == false)
{
return false;
}
$cache_id = $this->_getCacheId($id, $group);
$cache_content = xcache_get($cache_id);
if ($cache_content === null)
{
return false;
}
return $cache_content;
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
* @note This requires the php.ini setting xcache.admin.enable_auth = Off.
*/
public function getAll()
{
// Make sure XCache is configured properly
if (static::isSupported() == false)
{
return array();
}
$allinfo = xcache_list(XC_TYPE_VAR, 0);
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
$data = array();
foreach ($keys as $key)
{
$namearr = explode('-', $key['name']);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new CacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key['size']);
$data[$group] = $item;
}
}
return $data;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 1.7.0
*/
public function store($id, $group, $data)
{
// Make sure XCache is configured properly
if (static::isSupported() == false)
{
return false;
}
return xcache_set($this->_getCacheId($id, $group), $data, $this->_lifetime);
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function remove($id, $group)
{
// Make sure XCache is configured properly
if (static::isSupported() == false)
{
return false;
}
$cache_id = $this->_getCacheId($id, $group);
if (!xcache_isset($cache_id))
{
return true;
}
return xcache_unset($cache_id);
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 1.7.0
*/
public function clean($group, $mode = null)
{
// Make sure XCache is configured properly
if (static::isSupported() == false)
{
return true;
}
$allinfo = xcache_list(XC_TYPE_VAR, 0);
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (strpos($key['name'], $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
xcache_unset($key['name']);
}
}
return true;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.0.0
*/
public static function isSupported()
{
if (extension_loaded('xcache'))
{
// XCache Admin must be disabled for Joomla to use XCache
$xcache_admin_enable_auth = ini_get('xcache.admin.enable_auth');
// Some extensions ini variables are reported as strings
if ($xcache_admin_enable_auth == 'Off')
{
return true;
}
// We require a string with contents 0, not a null value because it is not set since that then defaults to On/True
if ($xcache_admin_enable_auth === '0')
{
return true;
}
// In some enviorments empty is equivalent to Off; See JC: #34044 && Github: #4083
if ($xcache_admin_enable_auth === '')
{
return true;
}
}
// If the settings are not correct, give up
return false;
}
}
Cache.php 0000644 00000044470 15234436765 0006310 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache;
defined('JPATH_PLATFORM') or die;
use Joomla\Application\Web\WebClient;
use Joomla\CMS\Cache\Exception\CacheExceptionInterface;
use Joomla\String\StringHelper;
/**
* Joomla! Cache base object
*
* @since 1.7.0
*/
class Cache
{
/**
* Storage handler
*
* @var CacheStorage[]
* @since 1.7.0
*/
public static $_handler = array();
/**
* Cache options
*
* @var array
* @since 1.7.0
*/
public $_options;
/**
* Constructor
*
* @param array $options Cache options
*
* @since 1.7.0
*/
public function __construct($options)
{
$conf = \JFactory::getConfig();
$this->_options = array(
'cachebase' => $conf->get('cache_path', JPATH_CACHE),
'lifetime' => (int) $conf->get('cachetime'),
'language' => $conf->get('language', 'en-GB'),
'storage' => $conf->get('cache_handler', ''),
'defaultgroup' => 'default',
'locking' => true,
'locktime' => 15,
'checkTime' => true,
'caching' => ($conf->get('caching') >= 1) ? true : false,
);
// Overwrite default options with given options
foreach ($options as $option => $value)
{
if (isset($options[$option]) && $options[$option] !== '')
{
$this->_options[$option] = $options[$option];
}
}
if (empty($this->_options['storage']))
{
$this->setCaching(false);
}
}
/**
* Returns a reference to a cache adapter object, always creating it
*
* @param string $type The cache object type to instantiate
* @param array $options The array of options
*
* @return CacheController
*
* @since 1.7.0
*/
public static function getInstance($type = 'output', $options = array())
{
return CacheController::getInstance($type, $options);
}
/**
* Get the storage handlers
*
* @return array
*
* @since 1.7.0
*/
public static function getStores()
{
$handlers = array();
// Get an iterator and loop trough the driver classes.
$iterator = new \DirectoryIterator(__DIR__ . '/Storage');
/** @type $file \DirectoryIterator */
foreach ($iterator as $file)
{
$fileName = $file->getFilename();
// Only load for php files.
if (!$file->isFile() || $file->getExtension() != 'php' || $fileName == 'CacheStorageHelper.php')
{
continue;
}
// Derive the class name from the type.
$class = str_ireplace('.php', '', __NAMESPACE__ . '\\Storage\\' . ucfirst(trim($fileName)));
// If the class doesn't exist we have nothing left to do but look at the next type. We did our best.
if (!class_exists($class))
{
continue;
}
// Sweet! Our class exists, so now we just need to know if it passes its test method.
if ($class::isSupported())
{
// Connector names should not have file extensions.
$handler = str_ireplace('Storage.php', '', $fileName);
$handler = str_ireplace('.php', '', $handler);
$handlers[] = strtolower($handler);
}
}
return $handlers;
}
/**
* Set caching enabled state
*
* @param boolean $enabled True to enable caching
*
* @return void
*
* @since 1.7.0
*/
public function setCaching($enabled)
{
$this->_options['caching'] = $enabled;
}
/**
* Get caching state
*
* @return boolean
*
* @since 1.7.0
*/
public function getCaching()
{
return $this->_options['caching'];
}
/**
* Set cache lifetime
*
* @param integer $lt Cache lifetime
*
* @return void
*
* @since 1.7.0
*/
public function setLifeTime($lt)
{
$this->_options['lifetime'] = $lt;
}
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group = null)
{
if (!$this->getCaching())
{
return false;
}
// Get the default group
$group = $group ?: $this->_options['defaultgroup'];
return $this->_getStorage()->contains($id, $group);
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function get($id, $group = null)
{
if (!$this->getCaching())
{
return false;
}
// Get the default group
$group = $group ?: $this->_options['defaultgroup'];
return $this->_getStorage()->get($id, $group, $this->_options['checkTime']);
}
/**
* Get a list of all cached data
*
* @return mixed Boolean false on failure or an object with a list of cache groups and data
*
* @since 1.7.0
*/
public function getAll()
{
if (!$this->getCaching())
{
return false;
}
return $this->_getStorage()->getAll();
}
/**
* Store the cached data by ID and group
*
* @param mixed $data The data to store
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function store($data, $id, $group = null)
{
if (!$this->getCaching())
{
return false;
}
// Get the default group
$group = $group ?: $this->_options['defaultgroup'];
// Get the storage and store the cached data
return $this->_getStorage()->store($id, $group, $data);
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function remove($id, $group = null)
{
// Get the default group
$group = $group ?: $this->_options['defaultgroup'];
try
{
return $this->_getStorage()->remove($id, $group);
}
catch (CacheExceptionInterface $e)
{
if (!$this->getCaching())
{
return false;
}
throw $e;
}
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean True on success, false otherwise
*
* @since 1.7.0
*/
public function clean($group = null, $mode = 'group')
{
// Get the default group
$group = $group ?: $this->_options['defaultgroup'];
try
{
return $this->_getStorage()->clean($group, $mode);
}
catch (CacheExceptionInterface $e)
{
if (!$this->getCaching())
{
return false;
}
throw $e;
}
}
/**
* Garbage collect expired cache data
*
* @return boolean
*
* @since 1.7.0
*/
public function gc()
{
try
{
return $this->_getStorage()->gc();
}
catch (CacheExceptionInterface $e)
{
if (!$this->getCaching())
{
return false;
}
throw $e;
}
}
/**
* Set lock flag on cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $locktime The default locktime for locking the cache.
*
* @return \stdClass Object with properties of lock and locklooped
*
* @since 1.7.0
*/
public function lock($id, $group = null, $locktime = null)
{
$returning = new \stdClass;
$returning->locklooped = false;
if (!$this->getCaching())
{
$returning->locked = false;
return $returning;
}
// Get the default group
$group = $group ?: $this->_options['defaultgroup'];
// Get the default locktime
$locktime = $locktime ?: $this->_options['locktime'];
/*
* Allow storage handlers to perform locking on their own
* NOTE drivers with lock need also unlock or unlocking will fail because of false $id
*/
$handler = $this->_getStorage();
if ($this->_options['locking'] == true)
{
$locked = $handler->lock($id, $group, $locktime);
if ($locked !== false)
{
return $locked;
}
}
// Fallback
$curentlifetime = $this->_options['lifetime'];
// Set lifetime to locktime for storing in children
$this->_options['lifetime'] = $locktime;
$looptime = $locktime * 10;
$id2 = $id . '_lock';
if ($this->_options['locking'] == true)
{
$data_lock = $handler->get($id2, $group, $this->_options['checkTime']);
}
else
{
$data_lock = false;
$returning->locked = false;
}
if ($data_lock !== false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released. That implies that data get from other thread has finished
while ($data_lock !== false)
{
if ($lock_counter > $looptime)
{
$returning->locked = false;
$returning->locklooped = true;
break;
}
usleep(100);
$data_lock = $handler->get($id2, $group, $this->_options['checkTime']);
$lock_counter++;
}
}
if ($this->_options['locking'] == true)
{
$returning->locked = $handler->store($id2, $group, 1);
}
// Revert lifetime to previous one
$this->_options['lifetime'] = $curentlifetime;
return $returning;
}
/**
* Unset lock flag on cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function unlock($id, $group = null)
{
if (!$this->getCaching())
{
return false;
}
// Get the default group
$group = $group ?: $this->_options['defaultgroup'];
// Allow handlers to perform unlocking on their own
$handler = $this->_getStorage();
$unlocked = $handler->unlock($id, $group);
if ($unlocked !== false)
{
return $unlocked;
}
// Fallback
return $handler->remove($id . '_lock', $group);
}
/**
* Get the cache storage handler
*
* @return CacheStorage
*
* @since 1.7.0
*/
public function &_getStorage()
{
$hash = md5(serialize($this->_options));
if (isset(self::$_handler[$hash]))
{
return self::$_handler[$hash];
}
self::$_handler[$hash] = CacheStorage::getInstance($this->_options['storage'], $this->_options);
return self::$_handler[$hash];
}
/**
* Perform workarounds on retrieved cached data
*
* @param string $data Cached data
* @param array $options Array of options
*
* @return string Body of cached data
*
* @since 1.7.0
*/
public static function getWorkarounds($data, $options = array())
{
$app = \JFactory::getApplication();
$document = \JFactory::getDocument();
$body = null;
// Get the document head out of the cache.
if (isset($options['mergehead']) && $options['mergehead'] == 1 && isset($data['head']) && !empty($data['head'])
&& method_exists($document, 'mergeHeadData'))
{
$document->mergeHeadData($data['head']);
}
elseif (isset($data['head']) && method_exists($document, 'setHeadData'))
{
$document->setHeadData($data['head']);
}
// Get the document MIME encoding out of the cache
if (isset($data['mime_encoding']))
{
$document->setMimeEncoding($data['mime_encoding'], true);
}
// If the pathway buffer is set in the cache data, get it.
if (isset($data['pathway']) && is_array($data['pathway']))
{
// Push the pathway data into the pathway object.
$app->getPathway()->setPathway($data['pathway']);
}
// @todo check if the following is needed, seems like it should be in page cache
// If a module buffer is set in the cache data, get it.
if (isset($data['module']) && is_array($data['module']))
{
// Iterate through the module positions and push them into the document buffer.
foreach ($data['module'] as $name => $contents)
{
$document->setBuffer($contents, 'module', $name);
}
}
// Set cached headers.
if (isset($data['headers']) && $data['headers'])
{
foreach ($data['headers'] as $header)
{
$app->setHeader($header['name'], $header['value']);
}
}
// The following code searches for a token in the cached page and replaces it with the proper token.
if (isset($data['body']))
{
$token = \JSession::getFormToken();
$search = '#<input type="hidden" name="[0-9a-f]{32}" value="1" />#';
$replacement = '<input type="hidden" name="' . $token . '" value="1" />';
$data['body'] = preg_replace($search, $replacement, $data['body']);
$body = $data['body'];
}
// Get the document body out of the cache.
return $body;
}
/**
* Create workarounds for data to be cached
*
* @param string $data Cached data
* @param array $options Array of options
*
* @return string Data to be cached
*
* @since 1.7.0
*/
public static function setWorkarounds($data, $options = array())
{
$loptions = array(
'nopathway' => 0,
'nohead' => 0,
'nomodules' => 0,
'modulemode' => 0,
);
if (isset($options['nopathway']))
{
$loptions['nopathway'] = $options['nopathway'];
}
if (isset($options['nohead']))
{
$loptions['nohead'] = $options['nohead'];
}
if (isset($options['nomodules']))
{
$loptions['nomodules'] = $options['nomodules'];
}
if (isset($options['modulemode']))
{
$loptions['modulemode'] = $options['modulemode'];
}
$app = \JFactory::getApplication();
$document = \JFactory::getDocument();
if ($loptions['nomodules'] != 1)
{
// Get the modules buffer before component execution.
$buffer1 = $document->getBuffer();
if (!is_array($buffer1))
{
$buffer1 = array();
}
// Make sure the module buffer is an array.
if (!isset($buffer1['module']) || !is_array($buffer1['module']))
{
$buffer1['module'] = array();
}
}
// View body data
$cached['body'] = $data;
// Document head data
if ($loptions['nohead'] != 1 && method_exists($document, 'getHeadData'))
{
if ($loptions['modulemode'] == 1)
{
$headnow = $document->getHeadData();
$unset = array('title', 'description', 'link', 'links', 'metaTags');
foreach ($unset as $un)
{
unset($headnow[$un]);
unset($options['headerbefore'][$un]);
}
$cached['head'] = array();
// Only store what this module has added
foreach ($headnow as $now => $value)
{
if (isset($options['headerbefore'][$now]))
{
// We have to serialize the content of the arrays because the may contain other arrays which is a notice in PHP 5.4 and newer
$nowvalue = array_map('serialize', $headnow[$now]);
$beforevalue = array_map('serialize', $options['headerbefore'][$now]);
$newvalue = array_diff_assoc($nowvalue, $beforevalue);
$newvalue = array_map('unserialize', $newvalue);
// Special treatment for script and style declarations.
if (($now == 'script' || $now == 'style') && is_array($newvalue) && is_array($options['headerbefore'][$now]))
{
foreach ($newvalue as $type => $currentScriptStr)
{
if (isset($options['headerbefore'][$now][strtolower($type)]))
{
$oldScriptStr = $options['headerbefore'][$now][strtolower($type)];
if ($oldScriptStr != $currentScriptStr)
{
// Save only the appended declaration.
$newvalue[strtolower($type)] = StringHelper::substr($currentScriptStr, StringHelper::strlen($oldScriptStr));
}
}
}
}
}
else
{
$newvalue = $headnow[$now];
}
if (!empty($newvalue))
{
$cached['head'][$now] = $newvalue;
}
}
}
else
{
$cached['head'] = $document->getHeadData();
}
}
// Document MIME encoding
$cached['mime_encoding'] = $document->getMimeEncoding();
// Pathway data
if ($app->isClient('site') && $loptions['nopathway'] != 1)
{
$cached['pathway'] = is_array($data) && isset($data['pathway']) ? $data['pathway'] : $app->getPathway()->getPathway();
}
if ($loptions['nomodules'] != 1)
{
// @todo Check if the following is needed, seems like it should be in page cache
// Get the module buffer after component execution.
$buffer2 = $document->getBuffer();
if (!is_array($buffer2))
{
$buffer2 = array();
}
// Make sure the module buffer is an array.
if (!isset($buffer2['module']) || !is_array($buffer2['module']))
{
$buffer2['module'] = array();
}
// Compare the second module buffer against the first buffer.
$cached['module'] = array_diff_assoc($buffer2['module'], $buffer1['module']);
}
// Headers data
if (isset($options['headers']) && $options['headers'])
{
$cached['headers'] = $app->getHeaders();
}
return $cached;
}
/**
* Create a safe ID for cached data from URL parameters
*
* @return string MD5 encoded cache ID
*
* @since 1.7.0
*/
public static function makeId()
{
$app = \JFactory::getApplication();
$registeredurlparams = new \stdClass;
// Get url parameters set by plugins
if (!empty($app->registeredurlparams))
{
$registeredurlparams = $app->registeredurlparams;
}
// Platform defaults
$defaulturlparams = array(
'format' => 'WORD',
'option' => 'WORD',
'view' => 'WORD',
'layout' => 'WORD',
'tpl' => 'CMD',
'id' => 'INT',
);
// Use platform defaults if parameter doesn't already exist.
foreach ($defaulturlparams as $param => $type)
{
if (!property_exists($registeredurlparams, $param))
{
$registeredurlparams->$param = $type;
}
}
$safeuriaddon = new \stdClass;
foreach ($registeredurlparams as $key => $value)
{
$safeuriaddon->$key = $app->input->get($key, null, $value);
}
return md5(serialize($safeuriaddon));
}
/**
* Set a prefix cache key if device calls for separate caching
*
* @return string
*
* @since 3.5
*/
public static function getPlatformPrefix()
{
// No prefix when Global Config is set to no platform specific prefix
if (!\JFactory::getConfig()->get('cache_platformprefix', '0'))
{
return '';
}
$webclient = new WebClient;
if ($webclient->mobile)
{
return 'M-';
}
return '';
}
/**
* Add a directory where Cache should search for handlers. You may either pass a string or an array of directories.
*
* @param array|string $path A path to search.
*
* @return array An array with directory elements
*
* @since 1.7.0
*/
public static function addIncludePath($path = '')
{
static $paths;
if (!isset($paths))
{
$paths = array();
}
if (!empty($path) && !in_array($path, $paths))
{
\JLoader::import('joomla.filesystem.path');
array_unshift($paths, \JPath::clean($path));
}
return $paths;
}
}
CacheController.php 0000644 00000011445 15234436765 0010350 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Cache;
defined('JPATH_PLATFORM') or die;
/**
* Public cache handler
*
* @since 1.7.0
* @note As of 4.0 this class will be abstract
*/
class CacheController
{
/**
* Cache object
*
* @var Cache
* @since 1.7.0
*/
public $cache;
/**
* Array of options
*
* @var array
* @since 1.7.0
*/
public $options;
/**
* Constructor
*
* @param array $options Array of options
*
* @since 1.7.0
*/
public function __construct($options)
{
$this->cache = new Cache($options);
$this->options = & $this->cache->_options;
// Overwrite default options with given options
foreach ($options as $option => $value)
{
if (isset($options[$option]))
{
$this->options[$option] = $options[$option];
}
}
}
/**
* Magic method to proxy CacheController method calls to Cache
*
* @param string $name Name of the function
* @param array $arguments Array of arguments for the function
*
* @return mixed
*
* @since 1.7.0
*/
public function __call($name, $arguments)
{
return call_user_func_array(array($this->cache, $name), $arguments);
}
/**
* Returns a reference to a cache adapter object, always creating it
*
* @param string $type The cache object type to instantiate; default is output.
* @param array $options Array of options
*
* @return CacheController
*
* @since 1.7.0
* @throws \RuntimeException
*/
public static function getInstance($type = 'output', $options = array())
{
self::addIncludePath(__DIR__ . '/Controller');
$type = strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $type));
$class = __NAMESPACE__ . '\\Controller\\' . ucfirst($type) . 'Controller';
if (!class_exists($class))
{
$class = 'JCacheController' . ucfirst($type);
}
if (!class_exists($class))
{
// Search for the class file in the Cache include paths.
\JLoader::import('joomla.filesystem.path');
$path = \JPath::find(self::addIncludePath(), strtolower($type) . '.php');
if ($path !== false)
{
\JLoader::register($class, $path);
}
// The class should now be loaded
if (!class_exists($class))
{
throw new \RuntimeException('Unable to load Cache Controller: ' . $type, 500);
}
}
return new $class($options);
}
/**
* Add a directory where Cache should search for controllers. You may either pass a string or an array of directories.
*
* @param array|string $path A path to search.
*
* @return array An array with directory elements
*
* @since 1.7.0
*/
public static function addIncludePath($path = '')
{
static $paths;
if (!isset($paths))
{
$paths = array();
}
if (!empty($path) && !in_array($path, $paths))
{
\JLoader::import('joomla.filesystem.path');
array_unshift($paths, \JPath::clean($path));
}
return $paths;
}
/**
* Get stored cached data by ID and group
*
* @param string $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
* @deprecated 4.0 Implement own method in subclass
*/
public function get($id, $group = null)
{
$data = $this->cache->get($id, $group);
if ($data === false)
{
$locktest = $this->cache->lock($id, $group);
// If locklooped is true try to get the cached data again; it could exist now.
if ($locktest->locked === true && $locktest->locklooped === true)
{
$data = $this->cache->get($id, $group);
}
if ($locktest->locked === true)
{
$this->cache->unlock($id, $group);
}
}
// Check again because we might get it from second attempt
if ($data !== false)
{
// Trim to fix unserialize errors
$data = unserialize(trim($data));
}
return $data;
}
/**
* Store data to cache by ID and group
*
* @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 wrkarounds
*
* @return boolean True if cache stored
*
* @since 1.7.0
* @deprecated 4.0 Implement own method in subclass
*/
public function store($data, $id, $group = null, $wrkarounds = true)
{
$locktest = $this->cache->lock($id, $group);
if ($locktest->locked === false && $locktest->locklooped === true)
{
// We can not store data because another process is in the middle of saving
return false;
}
$result = $this->cache->store(serialize($data), $id, $group);
if ($locktest->locked === true)
{
$this->cache->unlock($id, $group);
}
return $result;
}
}
CacheStorage.php 0000644 00000020612 15234436765 0007625 0 ustar 00 <?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;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Cache\Exception\UnsupportedCacheException;
use Joomla\CMS\Log\Log;
/**
* Abstract cache storage handler
*
* @since 1.7.0
* @note As of 4.0 this class will be abstract
*/
class CacheStorage
{
/**
* The raw object name
*
* @var string
* @since 1.7.0
*/
protected $rawname;
/**
* Time that the cache storage handler was instantiated
*
* @var integer
* @since 1.7.0
*/
public $_now;
/**
* Cache lifetime
*
* @var integer
* @since 1.7.0
*/
public $_lifetime;
/**
* Flag if locking is enabled
*
* @var boolean
* @since 1.7.0
*/
public $_locking;
/**
* Language code
*
* @var string
* @since 1.7.0
*/
public $_language;
/**
* Application name
*
* @var string
* @since 1.7.0
*/
public $_application;
/**
* Object hash
*
* @var string
* @since 1.7.0
*/
public $_hash;
/**
* Constructor
*
* @param array $options Optional parameters
*
* @since 1.7.0
*/
public function __construct($options = array())
{
$config = \JFactory::getConfig();
$this->_hash = md5($config->get('secret'));
$this->_application = (isset($options['application'])) ? $options['application'] : md5(JPATH_CONFIGURATION);
$this->_language = (isset($options['language'])) ? $options['language'] : 'en-GB';
$this->_locking = (isset($options['locking'])) ? $options['locking'] : true;
$this->_lifetime = (isset($options['lifetime'])) ? $options['lifetime'] * 60 : $config->get('cachetime') * 60;
$this->_now = (isset($options['now'])) ? $options['now'] : time();
// Set time threshold value. If the lifetime is not set, default to 60 (0 is BAD)
// _threshold is now available ONLY as a legacy (it's deprecated). It's no longer used in the core.
if (empty($this->_lifetime))
{
$this->_threshold = $this->_now - 60;
$this->_lifetime = 60;
}
else
{
$this->_threshold = $this->_now - $this->_lifetime;
}
}
/**
* Returns a cache storage handler object.
*
* @param string $handler The cache storage handler to instantiate
* @param array $options Array of handler options
*
* @return CacheStorage
*
* @since 1.7.0
* @throws \UnexpectedValueException
* @throws UnsupportedCacheException
*/
public static function getInstance($handler = null, $options = array())
{
static $now = null;
// @deprecated 4.0 This class path is autoloaded, manual inclusion is no longer necessary
self::addIncludePath(__DIR__ . '/Storage');
if (!isset($handler))
{
$handler = \JFactory::getConfig()->get('cache_handler');
if (empty($handler))
{
throw new \UnexpectedValueException('Cache Storage Handler not set.');
}
}
if (is_null($now))
{
$now = time();
}
$options['now'] = $now;
// We can't cache this since options may change...
$handler = strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $handler));
/** @var CacheStorage $class */
$class = __NAMESPACE__ . '\\Storage\\' . ucfirst($handler) . 'Storage';
if (!class_exists($class))
{
$class = 'JCacheStorage' . ucfirst($handler);
}
if (!class_exists($class))
{
// Search for the class file in the JCacheStorage include paths.
\JLoader::import('joomla.filesystem.path');
$path = \JPath::find(self::addIncludePath(), strtolower($handler) . '.php');
if ($path === false)
{
throw new UnsupportedCacheException(sprintf('Unable to load Cache Storage: %s', $handler));
}
\JLoader::register($class, $path);
// The class should now be loaded
if (!class_exists($class))
{
throw new UnsupportedCacheException(sprintf('Unable to load Cache Storage: %s', $handler));
}
}
// Validate the cache storage is supported on this platform
if (!$class::isSupported())
{
throw new UnsupportedCacheException(sprintf('The %s Cache Storage is not supported on this platform.', $handler));
}
return new $class($options);
}
/**
* Check if the cache contains data stored by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 3.7.0
*/
public function contains($id, $group)
{
return false;
}
/**
* Get cached data by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function get($id, $group, $checkTime = true)
{
return false;
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 1.7.0
*/
public function getAll()
{
return false;
}
/**
* Store the data to cache by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean
*
* @since 1.7.0
*/
public function store($id, $group, $data)
{
return true;
}
/**
* Remove a cached data entry by ID and group
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function remove($id, $group)
{
return true;
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean
*
* @since 1.7.0
*/
public function clean($group, $mode = null)
{
return true;
}
/**
* Flush all existing items in storage.
*
* @return boolean
*
* @since 3.6.3
*/
public function flush()
{
return true;
}
/**
* Garbage collect expired cache data
*
* @return boolean
*
* @since 1.7.0
*/
public function gc()
{
return true;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 3.0.0
*/
public static function isSupported()
{
return true;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean
*
* @since 1.7.0
* @deprecated 4.0
*/
public static function test()
{
Log::add(__METHOD__ . '() is deprecated. Use CacheStorage::isSupported() instead.', Log::WARNING, 'deprecated');
return static::isSupported();
}
/**
* Lock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return mixed Boolean false if locking failed or an object containing properties lock and locklooped
*
* @since 1.7.0
*/
public function lock($id, $group, $locktime)
{
return false;
}
/**
* Unlock cached item
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return boolean
*
* @since 1.7.0
*/
public function unlock($id, $group = null)
{
return false;
}
/**
* Get a cache ID string from an ID/group pair
*
* @param string $id The cache data ID
* @param string $group The cache data group
*
* @return string
*
* @since 1.7.0
*/
protected function _getCacheId($id, $group)
{
$name = md5($this->_application . '-' . $id . '-' . $this->_language);
$this->rawname = $this->_hash . '-' . $name;
return Cache::getPlatformPrefix() . $this->_hash . '-cache-' . $group . '-' . $name;
}
/**
* Add a directory where CacheStorage should search for handlers. You may either pass a string or an array of directories.
*
* @param array|string $path A path to search.
*
* @return array An array with directory elements
*
* @since 1.7.0
*/
public static function addIncludePath($path = '')
{
static $paths;
if (!isset($paths))
{
$paths = array();
}
if (!empty($path) && !in_array($path, $paths))
{
\JLoader::import('joomla.filesystem.path');
array_unshift($paths, \JPath::clean($path));
}
return $paths;
}
}