From 771dbfe114f5ae2c2b0d79e1d0544d614264f15c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 24 Aug 2025 22:16:39 +0900 Subject: [PATCH] Experimental method to clear APC cache from PHP-CLI #2554 #1943 --- common/framework/Cache.php | 35 +++++++++++++++++ modules/module/conf/module.xml | 13 ++++--- modules/module/module.controller.php | 56 ++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 6 deletions(-) diff --git a/common/framework/Cache.php b/common/framework/Cache.php index f1e2dbe5b..83bfe13b9 100644 --- a/common/framework/Cache.php +++ b/common/framework/Cache.php @@ -239,6 +239,10 @@ class Cache { if (self::$_driver !== null) { + if (self::$_driver instanceof Drivers\Cache\APC && \PHP_SAPI === 'cli') + { + self::sendClearRequest([$key]); + } return self::$_driver->delete(self::getRealKey($key)) ? true : false; } else @@ -323,6 +327,10 @@ class Cache { if (self::$_driver !== null) { + if (self::$_driver instanceof Drivers\Cache\APC && \PHP_SAPI === 'cli') + { + self::sendClearRequest([$group_name . ':*']); + } $success = self::$_driver->incr(self::$_prefix . $group_name . '#version', 1) ? true : false; unset(self::$_group_versions[$group_name]); return $success; @@ -344,6 +352,10 @@ class Cache { if (self::$_driver !== null) { + if (self::$_driver instanceof Drivers\Cache\APC && \PHP_SAPI === 'cli') + { + self::sendClearRequest(['*']); + } return self::$_driver->clear() ? true : false; } else @@ -396,4 +408,27 @@ class Cache return self::$_prefix . $key; } + + /** + * Send a web request to clear the cache. + * + * @param array $keys + * @return void + */ + public static function sendClearRequest(array $keys): void + { + if (!$keys) + { + return; + } + + $keystring = implode('|', $keys); + $signature = Security::createSignature($keystring); + HTTP::post(\Context::getRequestUri(), [ + 'module' => 'module', + 'act' => 'procModuleClearCache', + 'keys' => $keys, + 'signature' => $signature, + ]); + } } diff --git a/modules/module/conf/module.xml b/modules/module/conf/module.xml index 01b315c99..4946fe04c 100644 --- a/modules/module/conf/module.xml +++ b/modules/module/conf/module.xml @@ -7,16 +7,17 @@ - + - + - + + @@ -26,7 +27,7 @@ - + @@ -36,12 +37,12 @@ - + - + diff --git a/modules/module/module.controller.php b/modules/module/module.controller.php index 93e76cfac..c2e9006eb 100644 --- a/modules/module/module.controller.php +++ b/modules/module/module.controller.php @@ -1266,6 +1266,62 @@ class ModuleController extends Module return executeQuery('module.deleteModuleFileBox', $args); } + /** + * API call to clear cache entries. + * + * This can be used to clear the APC cache from CLI scripts, + * such as async tasks run from crontab. + */ + public function procModuleClearCache() + { + // This is a JSON API. + Context::setResponseMethod('JSON'); + if (PHP_SAPI === 'cli') + { + throw new Rhymix\Framework\Exceptions\InvalidRequest; + } + + // Get cache keys to clear. + $keys = Context::get('keys'); + if (!$keys) + { + throw new Rhymix\Framework\Exceptions\InvalidRequest; + } + if (!is_array($keys)) + { + $keys = [$keys]; + } + + // Verify the API signature. + $keystring = implode('|', $keys); + $signature = Context::get('signature'); + if (!$signature) + { + throw new Rhymix\Framework\Exceptions\NotPermitted; + } + if (!Rhymix\Framework\Security::verifySignature($keystring, $signature)) + { + throw new Rhymix\Framework\Exceptions\NotPermitted; + } + + // Clear the requested cache keys. + foreach ($keys as $key) + { + if ($key === '*') + { + Rhymix\Framework\Cache::clearAll(); + } + elseif (preg_match('/^([^:]+):\*$/', $key, $matches)) + { + Rhymix\Framework\Cache::clearGroup($matches[1]); + } + else + { + Rhymix\Framework\Cache::delete($key); + } + } + } + /** * @brief function of locking (timeout is in seconds) */