Index: branches/5.2.x/core/kernel/utility/cache.php =================================================================== diff -u -N -r14244 -r14560 --- branches/5.2.x/core/kernel/utility/cache.php (.../cache.php) (revision 14244) +++ branches/5.2.x/core/kernel/utility/cache.php (.../cache.php) (revision 14560) @@ -1,6 +1,6 @@ prepareKeyName($name); - $this->_localStorage[$name] = $value; + // 1. stores current version of serial for given cache key + $this->_setCache($name . '_serials', $this->replaceSerials($name), $expiration); - return $this->_handler->set($name, $value, $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 $expires cache record expiration time in seconds + */ + 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 bool $replace_serials + * @param int $max_rebuild_seconds * @return mixed */ - function getCache($name, $store_locally = true, $replace_serials = true) + function getCache($name, $store_locally = true, $max_rebuild_seconds = 0) { - $name = $this->prepareKeyName($name, $replace_serials); + if ( $this->_getCache($name . '_rebuild') ) { + // cache rebuild requested -> rebuild now + $this->delete($name . '_rebuild'); + return false; + } + + $new_serial = $this->replaceSerials($name); + $old_serial = $this->_getCache($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) { + $cache = $this->_getCache($name, $store_locally); + $rebuilding = $this->_getCache($name . '_rebuilding', false); + + 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 $name + * @param bool $store_locally store data locally after retrieved + * @return mixed + */ + function _getCache($name, $store_locally = true) + { + $name = $this->prepareKeyName($name); + if ($store_locally) { - if (array_key_exists($name, $this->_localStorage)) { - if ($this->displayCacheStatistics) { + if ( array_key_exists($name, $this->_localStorage) ) { + if ( $this->displayCacheStatistics ) { $this->setStatistics($name, $this->_localStorage[$name]); } @@ -172,7 +273,7 @@ $res = $this->_handler->get($name); - if ($replace_serials && $this->debugCache) { + if ($this->debugCache) { // don't display subsequent serial cache retrievals (ones, that are part of keys) if (is_array($res)) { $this->Application->Debugger->appendHTML('Restoring key "' . $name . '". Type: ' . gettype($res) . '.'); @@ -188,10 +289,10 @@ } } - if ($store_locally && ($res !== false)) { + if ( $store_locally && ($res !== false) ) { $this->_localStorage[$name] = $res; - if ($this->displayCacheStatistics) { + if ( $this->displayCacheStatistics ) { $this->setStatistics($name, $res); } } @@ -232,27 +333,37 @@ * Replaces serials and adds unique site prefix to cache variable name * * @param string $name - * @param bool $replace_serials * @return string */ - function prepareKeyName($name, $replace_serials = true) + protected function prepareKeyName($name) { - // replace serials in key name - if ($replace_serials && preg_match_all('/\[%(.*?)%\]/', $name, $regs)) { + 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 foreach ($regs[1] as $serial_name) { - $name = str_replace('[%' . $serial_name . '%]', '[' . $serial_name . '=' . $this->getCache($serial_name, true, false) . ']', $name); + $value = str_replace('[%' . $serial_name . '%]', '[' . $serial_name . '=' . $this->_getCache($serial_name, true) . ']', $value); } } - if ($this->cachingType == CACHING_TYPE_TEMPORARY) { - return $name; - } - - // add site-wide prefix to key - return $this->_cachePrefix() . $name; + return $value; } /**