Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1097263
in-portal
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Aug 12, 4:33 PM
Size
18 KB
Mime Type
text/x-diff
Expires
Thu, Aug 14, 4:33 PM (7 h, 21 m)
Engine
blob
Format
Raw Data
Handle
710666
Attached To
rINP In-Portal
in-portal
View Options
Index: branches/5.2.x/core/kernel/utility/cache.php
===================================================================
--- branches/5.2.x/core/kernel/utility/cache.php (revision 15095)
+++ branches/5.2.x/core/kernel/utility/cache.php (revision 15096)
@@ -1,787 +1,789 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* Manager of all implemented caching handlers
*
*/
class kCache extends kBase {
/**
* Rebuild cache now
*
*/
const REBUILD_NOW = 1;
/**
* Rebuild cache later
*
*/
const REBUILD_LATER = 2;
/**
* Cache waiting step (in seconds)
*
*/
const WAIT_STEP = 2;
/**
* Object of cache handler
*
* @var FakeCacheHandler
*/
var $_handler = null;
/**
* Part of what we retrieve will be stored locally (per script run) not to bother memcache a lot
*
* @var Array
*/
var $_localStorage = Array ();
/**
* What type of caching is being used
*
* @var int
*/
var $cachingType = CACHING_TYPE_NONE;
/**
* Cache usage statistics (per script run)
*
* @var Array
*/
var $statistics = Array ();
/**
* Debug cache usage
*
* @var bool
*/
var $debugCache = false;
/**
* Displays cache usage statistics
*
* @var bool
*/
var $displayCacheStatistics = false;
/**
* Site key name
* Prepended to each cached key name
*
* @var string
*/
var $siteKeyName = '';
/**
* Site key value
* Prepended to each cached key name
*
* @var string
*/
var $siteKeyValue = null;
/**
* Creates cache manager
*
* @access public
*/
public function __construct()
{
parent::__construct();
$vars = kUtil::getConfigVars();
$this->siteKeyName = 'site_serial:' . crc32(SQL_TYPE . '://' . SQL_USER . ':' . SQL_PASS . '@' . SQL_SERVER . ':' . TABLE_PREFIX . ':' . SQL_DB);
// get cache handler class to use
$handler_class = (isset($vars['CacheHandler']) ? $vars['CacheHandler'] : '') . 'CacheHandler';
// defined cache handler doesn't exist -> use default
if ( !class_exists($handler_class) ) {
$handler_class = 'FakeCacheHandler';
}
$handler = new $handler_class();
if ( !$handler->isWorking() ) {
// defined cache handler is not working -> use default
trigger_error('Failed to initialize "<strong>' . $handler_class . '</strong>" caching handler.', E_USER_WARNING);
$handler = new FakeCacheHandler();
}
elseif ( $this->Application->isDebugMode() && ($handler->cachingType == CACHING_TYPE_MEMORY) ) {
$this->Application->Debugger->appendHTML('Memory Caching: "<strong>' . $handler_class . '</strong>"');
}
$this->_handler =& $handler;
$this->cachingType = $handler->cachingType;
$this->debugCache = $handler->cachingType == CACHING_TYPE_MEMORY && $this->Application->isDebugMode();
$this->displayCacheStatistics = defined('DBG_CACHE') && DBG_CACHE && $this->Application->isDebugMode();
}
/**
* Returns caching type of current storage engine
*
* @return int
*/
function getCachingType()
{
return $this->cachingType;
}
/**
* Stores value to cache
*
* @param string $name
* @param mixed $value
* @param int $expiration cache record expiration time in seconds
* @return bool
*/
function setCache($name, $value, $expiration)
{
// 1. stores current version of serial for given cache key
$this->_setCache($name . '_serials', $this->replaceSerials($name), $expiration);
// 2. remove rebuilding mark
$this->delete($name . '_rebuilding');
return $this->_setCache($name, $value, $expiration);
}
/**
* Stores value to cache
*
* @param string $name
* @param mixed $value
* @param int $expiration cache record expiration time in seconds
* @return bool
*/
function _setCache($name, $value, $expiration)
{
$prepared_name = $this->prepareKeyName($name);
$this->_localStorage[$prepared_name] = $value;
return $this->_handler->set($prepared_name, $value, $expiration);
}
/**
* Sets rebuilding mode for given cache
*
* @param string $name
* @param int $mode
* @param int $max_rebuilding_time
*/
function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
{
if ( !isset($mode) || $mode == self::REBUILD_NOW ) {
$this->_setCache($name . '_rebuilding', 1, $max_rebuilding_time);
$this->delete($name . '_rebuild');
}
elseif ( $mode == self::REBUILD_LATER ) {
$this->_setCache($name . '_rebuild', 1, 0);
$this->delete($name . '_rebuilding');
}
}
/**
* Returns value from cache
*
* @param string $name
* @param bool $store_locally store data locally after retrieved
* @param int $max_rebuild_seconds
* @return mixed
*/
function getCache($name, $store_locally = true, $max_rebuild_seconds = 0)
{
$cached_data = $this->_getCache(Array ($name . '_rebuild', $name . '_serials'), Array (true, true));
if ( $cached_data[$name . '_rebuild'] ) {
// cache rebuild requested -> rebuild now
$this->delete($name . '_rebuild');
return false;
}
$new_serial = $this->replaceSerials($name);
$old_serial = $cached_data[$name . '_serials'];
if ( $name == $new_serial || $new_serial != $old_serial ) {
// no serials in cache key OR cache is outdated
$wait_seconds = $max_rebuild_seconds;
while (true) {
$cached_data = $this->_getCache(Array ($name, $name . '_rebuilding'), Array ($store_locally, false));
$cache = $cached_data[$name];
$rebuilding = $cached_data[$name . '_rebuilding'];
if ( ($cache === false) && (!$rebuilding || $wait_seconds == 0) ) {
// cache missing and nobody rebuilding it -> rebuild; enough waiting for cache to be ready
return false;
}
elseif ( $cache !== false ) {
// cache present (if other user is rebuilding it, then it's outdated cache) -> return it
return $rebuilding || $new_serial == $old_serial ? $cache : false;
}
$wait_seconds -= self::WAIT_STEP;
sleep(self::WAIT_STEP);
}
return $cache;
}
return $this->_getCache($name, $store_locally);
}
/**
* Returns value from cache
*
* @param string|Array $names
* @param bool|Array $store_locally store data locally after retrieved
* @return mixed
*/
function _getCache($names, $store_locally = true)
{
static $request_number = 1;
$res = Array ();
$names = (array)$names;
$store_locally = (array)$store_locally;
$to_get = $prepared_names = array_map(Array (&$this, 'prepareKeyName'), $names);
foreach ($prepared_names as $index => $prepared_name) {
$name = $names[$index];
if ( $store_locally[$index] && array_key_exists($prepared_name, $this->_localStorage) ) {
if ( $this->displayCacheStatistics ) {
$this->setStatistics($prepared_name, $this->_localStorage[$prepared_name]);
}
$res[$name] = $this->_localStorage[$prepared_name];
unset($to_get[$index]);
}
}
- $multi_res = $this->_handler->get($to_get);
+ if ( $to_get ) {
+ $multi_res = $this->_handler->get($to_get);
- foreach ($to_get as $index => $prepared_name) {
- $name = $names[$index];
+ foreach ($to_get as $index => $prepared_name) {
+ $name = $names[$index];
- if ( array_key_exists($prepared_name, $multi_res) ) {
- $res[$name] =& $multi_res[$prepared_name];
- }
- else {
- $res[$name] = false;
+ if ( array_key_exists($prepared_name, $multi_res) ) {
+ $res[$name] =& $multi_res[$prepared_name];
+ }
+ else {
+ $res[$name] = false;
+ }
+
+ $this->_postProcessGetCache($prepared_name, $res[$name], $store_locally[$index], $request_number);
}
- $this->_postProcessGetCache($prepared_name, $res[$name], $store_locally[$index], $request_number);
+ $request_number++;
}
- $request_number++;
-
return count($res) == 1 ? array_pop($res) : $res;
}
/**
* Stores variable in local cache & collects debug info about cache
*
* @param string $name
* @param mixed $res
* @param bool $store_locally
* @param int $request_number
* @return void
* @access protected
*/
protected function _postProcessGetCache($name, &$res, $store_locally = true, $request_number)
{
if ( $this->debugCache ) {
// don't display subsequent serial cache retrievals (ones, that are part of keys)
if ( is_array($res) ) {
$this->Application->Debugger->appendHTML('r' . $request_number . ': Restoring key "' . $name . '". Type: ' . gettype($res) . '.');
}
else {
$res_display = strip_tags($res);
if ( strlen($res_display) > 200 ) {
$res_display = substr($res_display, 0, 50) . ' ...';
}
$this->Application->Debugger->appendHTML('r' . $request_number . ': Restoring key "' . $name . '" resulted [' . $res_display . ']');
}
}
- if ( $store_locally && ($res !== false) ) {
+ if ( $store_locally /*&& ($res !== false)*/ ) {
$this->_localStorage[$name] = $res;
if ( $this->displayCacheStatistics ) {
$this->setStatistics($name, $res);
}
}
}
/**
* Deletes value from cache
*
* @param string $name
* @return mixed
*/
function delete($name)
{
$name = $this->prepareKeyName($name);
unset($this->_localStorage[$name]);
return $this->_handler->delete($name);
}
/**
* Reset's all memory cache at once
*/
function reset()
{
// don't check for enabled, because we maybe need to reset cache anyway
if ($this->cachingType == CACHING_TYPE_TEMPORARY) {
return ;
}
$site_key = $this->_cachePrefix(true);
$this->_handler->set($site_key, $this->_handler->get($site_key) + 1);
}
/**
* Replaces serials and adds unique site prefix to cache variable name
*
* @param string $name
* @return string
*/
protected function prepareKeyName($name)
{
if ( $this->cachingType == CACHING_TYPE_TEMPORARY ) {
return $name;
}
// add site-wide prefix to key
return $this->_cachePrefix() . $name;
}
/**
* Replaces serials within given string
*
* @param string $value
* @return string
* @access protected
*/
protected function replaceSerials($value)
{
if ( preg_match_all('/\[%(.*?)%\]/', $value, $regs) ) {
// [%LangSerial%] - prefix-wide serial in case of any change in "lang" prefix
// [%LangIDSerial:5%] - one id-wide serial in case of data, associated with given id was changed
// [%CiIDSerial:ItemResourceId:5%] - foreign key-based serial in case of data, associated with given foreign key was changed
$serial_names = $regs[1];
$serial_count = count($serial_names);
$store_locally = Array ();
for ($i = 0; $i < $serial_count; $i++) {
$store_locally[] = true;
}
$serial_values = $this->_getCache($serial_names, $store_locally);
if ( !is_array($serial_values) ) {
- $serial_values[ current($serial_names) ] = $serial_values;
+ $serial_values = Array (current($serial_names) => $serial_values);
}
foreach ($serial_names as $serial_name) {
$value = str_replace('[%' . $serial_name . '%]', '[' . $serial_name . '=' . $serial_values[$serial_name] . ']', $value);
}
}
return $value;
}
/**
* Returns site-wide caching prefix
*
* @param bool $only_site_key_name
* @return string
*/
function _cachePrefix($only_site_key_name = false)
{
if ($only_site_key_name) {
return $this->siteKeyName;
}
if ( !isset($this->siteKeyValue) ) {
$this->siteKeyValue = $this->_handler->get($this->siteKeyName);
if (!$this->siteKeyValue) {
$this->siteKeyValue = 1;
$this->_handler->set($this->siteKeyName, $this->siteKeyValue);
}
}
return "{$this->siteKeyName}:{$this->siteKeyValue}:";
}
function setStatistics($name, $found)
{
if (strpos($name, ']:') !== false) {
list ($cache_name, $name) = explode(']:', $name, 2);
}
else {
$cache_name = '-';
}
if (!array_key_exists($cache_name, $this->statistics)) {
$this->statistics[$cache_name] = Array ();
}
if (!array_key_exists($name, $this->statistics[$cache_name])) {
$this->statistics[$cache_name][$name] = Array ();
}
$status_key = $found ? 'found' : 'not_found';
if (!isset($this->statistics[$cache_name][$name][$status_key])) {
$this->statistics[$cache_name][$name][$status_key] = 0;
}
$this->statistics[$cache_name][$name][$status_key]++;
}
/**
* Returns storage size in bytes
*
* @return int
*/
function getStorageSize()
{
return strlen( serialize($this->_localStorage) );
}
function printStatistics()
{
$cache_size = $this->getStorageSize();
$this->Application->Debugger->appendHTML('<strong>Cache Size:</strong> ' . kUtil::formatSize($cache_size) . ' (' . $cache_size . ')');
foreach ($this->statistics as $cache_name => $cache_data) {
foreach ($cache_data as $key => $value) {
if (!array_key_exists('found', $value) || $value['found'] == 1) {
// remove cached records, that were used only 1 or 2 times
unset($this->statistics[$cache_name][$key]);
}
}
}
kUtil::print_r($this->statistics, 'Cache Statistics:');
}
}
class FakeCacheHandler {
var $cachingType = CACHING_TYPE_TEMPORARY;
function FakeCacheHandler()
{
}
/**
* Retrieves value from cache
*
* @param string $names
* @return mixed
*/
function get($names)
{
if ( is_array($names) ) {
$res = Array ();
foreach ($names as $name) {
$res[$name] = false;
}
return $res;
}
return false;
}
/**
* Stores value in cache
*
* @param string $name
* @param mixed $value
* @param int $expiration
* @return bool
*/
function set($name, $value, $expiration = 0)
{
return true;
}
/**
* Deletes key from cach
*
* @param string $name
* @return bool
*/
function delete($name)
{
return true;
}
/**
* Determines, that cache storage is working fine
*
* @return bool
*/
function isWorking()
{
return true;
}
}
class MemcacheCacheHandler {
var $_enabled = false;
/**
* Memcache connection
*
* @var Memcache
*/
var $_handler = null;
var $cachingType = CACHING_TYPE_MEMORY;
function MemcacheCacheHandler($default_servers = '')
{
$vars = kUtil::getConfigVars();
$memcached_servers = isset($vars['MemcacheServers']) ? $vars['MemcacheServers'] : $default_servers;
if ( $memcached_servers && class_exists('Memcache') ) {
$this->_enabled = true;
$this->_handler = new Memcache();
$servers = explode(';', $memcached_servers);
foreach ($servers as $server) {
if ( preg_match('/(.*):([\d]+)$/', $server, $regs) ) {
// "hostname:port" OR "unix:///path/to/socket:0"
$server = $regs[1];
$port = $regs[2];
}
else {
$port = 11211;
}
$this->_handler->addServer($server, $port);
}
// verify, that memcache server is working
if ( !$this->_handler->set('test', 1) ) {
$this->_enabled = false;
}
}
}
/**
* Retrieves value from cache
*
* @param string $name
* @return mixed
*/
function get($name)
{
return $this->_handler->get($name);
}
/**
* Stores value in cache
*
* @param string $name
* @param mixed $value
* @param int $expiration
* @return bool
*/
function set($name, $value, $expiration = 0)
{
// 0 - don't use compression
return $this->_handler->set($name, $value, 0, $expiration);
}
/**
* Deletes key from cache
*
* @param string $name
* @return bool
*/
function delete($name)
{
return $this->_handler->delete($name, 0);
}
/**
* Determines, that cache storage is working fine
*
* @return bool
*/
function isWorking()
{
return $this->_enabled;
}
}
class ApcCacheHandler {
var $_enabled = false;
var $cachingType = CACHING_TYPE_MEMORY;
function ApcCacheHandler()
{
$this->_enabled = function_exists('apc_fetch');
// verify, that apc is working
if ($this->_enabled && !$this->set('test', 1)) {
$this->_enabled = false;
}
}
/**
* Retrieves value from cache
*
* @param string $name
* @return mixed
*/
function get($name)
{
return apc_fetch($name);
}
/**
* Stores value in cache
*
* @param string $name
* @param mixed $value
* @param int $expiration
* @return bool
*/
function set($name, $value, $expiration = 0)
{
return apc_store($name, $value, $expiration);
}
/**
* Deletes key from cache
*
* @param string $name
* @return bool
*/
function delete($name)
{
return apc_delete($name);
}
/**
* Determines, that cache storage is working fine
*
* @return bool
*/
function isWorking()
{
return $this->_enabled;
}
}
class XCacheCacheHandler {
var $_enabled = false;
var $cachingType = CACHING_TYPE_MEMORY;
function XCacheCacheHandler()
{
$this->_enabled = function_exists('xcache_get');
// verify, that xcache is working
if ($this->_enabled && !$this->set('test', 1)) {
$this->_enabled = false;
}
}
/**
* Retrieves value from cache
*
* @param string|Array $names
* @return mixed
*/
function get($names)
{
if ( is_array($names) ) {
$res = Array ();
foreach ($names as $name) {
$res[$name] = $this->get($name);
}
return $res;
}
return xcache_isset($names) ? xcache_get($names) : false;
}
/**
* Stores value in cache
*
* @param string $name
* @param mixed $value
* @param int $expiration
* @return bool
*/
function set($name, $value, $expiration = 0)
{
return xcache_set($name, $value, $expiration);
}
/**
* Deletes key from cache
*
* @param string $name
* @return bool
*/
function delete($name)
{
return xcache_unset($name);
}
/**
* Determines, that cache storage is working fine
*
* @return bool
*/
function isWorking()
{
return $this->_enabled;
}
}
\ No newline at end of file
Event Timeline
Log In to Comment