From 9e790a71f633ff19079dca0eb0b6331f10eaeb43 Mon Sep 17 00:00:00 2001 From: Znote Date: Sat, 24 Jul 2021 00:34:54 +0200 Subject: [PATCH] Fix #371 - fast APCu memory caching support --- changelog.php | 1 + config.php | 11 +- engine/function/cache.php | 241 ++++++++++++++++++++++---------------- index.php | 1 + layout/sub/index.php | 1 + 5 files changed, 155 insertions(+), 100 deletions(-) diff --git a/changelog.php b/changelog.php index e1c9133..a44759d 100644 --- a/changelog.php +++ b/changelog.php @@ -54,6 +54,7 @@ if (user_logged_in()) {

Changelog

useMemory(false); if ($updateCache === true) { $changelogs = mysql_select_multi("SELECT `id`, `text`, `time`, `report_id`, `status` FROM `znote_changelog` ORDER BY `id` DESC;"); diff --git a/config.php b/config.php index 147ccac..bc455bf 100644 --- a/config.php +++ b/config.php @@ -658,8 +658,15 @@ 'name' => 'Forgotten' // Must be identical to config.lua (OT config file) server name. ); - // How often do you want highscores to update? - $config['cache_lifespan'] = 5; // 60 * 15; // 15 minutes. + // How often do you want highscores (cache) to update? + $config['cache'] = array( + // If you have two instances installed on same server, make each instance prefix unique + 'prefix' => 'znote_', + // 60 * 15; // 15 minutes. + 'lifespan' => 5, + // Store cache in memory/RAM? Requires PHP extension APCu + 'memory' => true + ); // WARNING! Account names written here will have admin access to web page! $config['page_admin_access'] = array( diff --git a/engine/function/cache.php b/engine/function/cache.php index 7c140bb..7a48603 100644 --- a/engine/function/cache.php +++ b/engine/function/cache.php @@ -1,122 +1,167 @@ _file = $file . self::EXT; - $this->setExpiration(config('cache_lifespan')); + /** + * @param string $file + * @access public + * @return void + **/ + public function __construct($file) { + $cfg = config('cache'); + + $this->setExpiration($cfg['lifespan']); + if (function_exists('apcu_fetch')) { + $this->_canMemory = true; + $this->_memory = $cfg['memory']; } + $this->_file = $file . self::EXT; + + if (!$this->_memory && $cfg['memory']) die(" +

Configuration error! +
Cannot save cache to memory, but it is configured to do so. +
You need to enable PHP extension APCu to enable memory cache. +
Install it or set \$config['cache']['memory'] to false! +
Ubuntu install: sudo apt install php-apcu

+ "); + } - /** - * Sets the cache expiration limit (IMPORTANT NOTE: seconds, NOT ms!). - * - * @param integer $span - * @access public - * @return void - **/ - public function setExpiration($span) { - $this->_lifespan = $span; - } + /** + * Sets the cache expiration limit (IMPORTANT NOTE: seconds, NOT ms!). + * + * @param integer $span + * @access public + * @return void + **/ + public function setExpiration($span) { + $this->_lifespan = $span; + } - /** - * Set the content you'd like to cache. - * - * @param mixed $content - * @access public - * @return void - **/ - public function setContent($content) { - switch (strtolower(gettype($content))) { - case 'array': - $this->_content = json_encode($content); - break; - - default: - $this->_content = $content; - break; - } - } - - - /** - * Validates whether it is time to refresh the cache data or not. - * - * @access public - * @return boolean - **/ - public function hasExpired() { - if (is_file($this->_file) && time() < filemtime($this->_file) + $this->_lifespan) { - return false; - } - + /** + * Enable or disable memory RAM storage. + * + * @param bool $bool + * @access public + * @return bool $status + **/ + public function useMemory($bool) { + if ($bool and $this->_canMemory) { + $this->_memory = true; return true; } + $this->_memory = false; + return false; + } - /** - * Returns remaining time before scoreboard will update itself. - * - * @access public - * @return integer - **/ - public function remainingTime() { - $remaining = 0; - if (!$this->hasExpired()) { - $remaining = (filemtime($this->_file) + $this->_lifespan) - time(); + + /** + * Set the content you'd like to cache. + * + * @param mixed $content + * @access public + * @return void + **/ + public function setContent($content) { + $this->_content = (!$this->_memory && strtolower(gettype($content)) == 'array') ? json_encode($content) : $content; + } + + + /** + * Validates whether it is time to refresh the cache data or not. + * + * @access public + * @return boolean + **/ + public function hasExpired() { + if ($this->_memory) { + return !apcu_exists($this->_file); + } + if (is_file($this->_file) && time() < filemtime($this->_file) + $this->_lifespan) { + return false; + } + return true; + } + + /** + * Returns remaining time before scoreboard will update itself. + * + * @access public + * @return integer + **/ + public function remainingTime() { + $remaining = 0; + if ($this->_memory) { + if (apcu_exists($this->_file)) { + $meta = apc_cache_info('user'); + foreach ($meta['cache_list'] AS $item) { + if ($item['info'] == $this->_file) { + $remaining = ($item['creation_time'] + $item['ttl']) - time(); + return ($remaining > 0) ? $remaining : 0; + } + } } return $remaining; } + if (!$this->hasExpired()) { + $remaining = (filemtime($this->_file) + $this->_lifespan) - time(); + } + return $remaining; + } - /** - * Saves the content into its appropriate cache file. - * - * @access public - * @return void - **/ - public function save() { - $handle = fopen($this->_file, 'w'); - fwrite($handle, $this->_content); - fclose($handle); + /** + * Saves the content into its appropriate cache file. + * + * @access public + * @return void + **/ + public function save() { + if ($this->_memory) { + return apcu_store($this->_file, $this->_content, $this->_lifespan); + } + $handle = fopen($this->_file, 'w'); + fwrite($handle, $this->_content); + fclose($handle); + } + + + /** + * Loads the content from a specified cache file. + * + * @access public + * @return mixed + **/ + public function load() { + if ($this->_memory) { + return apcu_fetch($this->_file); + } + if (!is_file($this->_file)) { + return false; } + ob_start(); + include_once($this->_file); + $content = ob_get_clean(); - /** - * Loads the content from a specified cache file. - * - * @access public - * @return mixed - **/ - public function load() { - if (!is_file($this->_file)) { - return false; - } + if (!isset($content) && strlen($content) == 0) { + return false; + } - ob_start(); - include_once($this->_file); - $content = ob_get_clean(); - - if (!isset($content) && strlen($content) == 0) { - return false; - } - - if ($content = json_decode($content, true)) { - return (array) $content; - } else { - return $content; - } + if ($content = json_decode($content, true)) { + return (array) $content; + } else { + return $content; } } +} diff --git a/index.php b/index.php index c42be58..bb80f59 100644 --- a/index.php +++ b/index.php @@ -15,6 +15,7 @@ require_once 'engine/init.php'; include 'layout/overall/header.php'; // Changelog ticker // // Load from cache $changelogCache = new Cache('engine/cache/changelog'); + $changelogCache->useMemory(false); $changelogs = $changelogCache->load(); if (isset($changelogs) && !empty($changelogs) && $changelogs !== false) { diff --git a/layout/sub/index.php b/layout/sub/index.php index e3482da..da31421 100644 --- a/layout/sub/index.php +++ b/layout/sub/index.php @@ -4,6 +4,7 @@ if ($config['UseChangelogTicker']) { // Changelog ticker // // Load from cache $changelogCache = new Cache('engine/cache/changelog'); + $changelogCache->useMemory(false); $changelogs = $changelogCache->load(); if (isset($changelogs) && !empty($changelogs) && $changelogs !== false) {