From e4aee0c87dd41b42ddabe11c663162262cbaff11 Mon Sep 17 00:00:00 2001 From: liugang Date: Thu, 7 Nov 2024 17:35:51 +0800 Subject: [PATCH] + [refac] add cache module to manage configuration of cache. --- module/cache/config/form.php | 7 ++ module/cache/control.php | 107 ++++++++++++++++++ module/cache/js/setting.ui.js | 16 +++ module/cache/lang/de.php | 38 +++++++ module/cache/lang/en.php | 38 +++++++ module/cache/lang/fr.php | 38 +++++++ module/cache/lang/zh-cn.php | 38 +++++++ module/cache/model.php | 29 +++++ module/cache/ui/setting.html.php | 182 +++++++++++++++++++++++++++++++ 9 files changed, 493 insertions(+) create mode 100644 module/cache/config/form.php create mode 100644 module/cache/control.php create mode 100644 module/cache/js/setting.ui.js create mode 100644 module/cache/lang/de.php create mode 100644 module/cache/lang/en.php create mode 100644 module/cache/lang/fr.php create mode 100644 module/cache/lang/zh-cn.php create mode 100644 module/cache/model.php create mode 100644 module/cache/ui/setting.html.php diff --git a/module/cache/config/form.php b/module/cache/config/form.php new file mode 100644 index 0000000000..90d0502b68 --- /dev/null +++ b/module/cache/config/form.php @@ -0,0 +1,7 @@ +cache->form = new stdClass(); +$config->cache->form->setting = array(); +$config->cache->form->setting['enable'] = array('type' => 'int', 'default' => 0); +$config->cache->form->setting['driver'] = array('type' => 'string', 'default' => ''); +$config->cache->form->setting['namespace'] = array('type' => 'string', 'default' => ''); +$config->cache->form->setting['redis'] = array('type' => 'array', 'default' => array('host' => '', 'port' => 0, 'username' => '', 'password' => '', 'serializer' => '')); diff --git a/module/cache/control.php b/module/cache/control.php new file mode 100644 index 0000000000..f4bb2b39ee --- /dev/null +++ b/module/cache/control.php @@ -0,0 +1,107 @@ + + * @package cache + * @link https://www.zentao.net + */ +class cache extends control +{ + /** + * 设置是否启用缓存。 + * Set cache enable. + * + * @access public + * @return void + */ + public function setting() + { + if($_POST) + { + $redis = null; + $cache = form::data()->get(); + if(isset($cache->redis)) + { + $redis = (object)$cache->redis; + unset($cache->redis); + } + if(!isset($redis->host)) $redis->host = ''; + if(!isset($redis->port)) $redis->port = ''; + if(!isset($redis->username)) $redis->username = ''; + if(!isset($redis->password)) $redis->password = ''; + if(!isset($redis->serializer)) $redis->serializer = ''; + + if($cache->enable) + { + if($cache->driver == 'apcu') + { + /* 检查是否加载了 APCu 扩展。Check if the APCu extension is loaded. */ + if(!extension_loaded('apcu')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->apcu->notLoaded)); + if(!ini_get('apc.enabled')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->apcu->notEnabled)); + } + if($cache->driver == 'redis') + { + /* 检查是否加载了 Redis 扩展。Check if the Redis extension is loaded. */ + if(!extension_loaded('redis')) return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->redis->notLoaded)); + if(!extension_loaded('igbinary') && $redis->serializer == 'igbinary') return $this->send(array('result' => 'fail', 'message' => $this->lang->cache->redis->igbinaryNotLoaded)); + + /* 检查 Redis 配置是否正确。Check if the Redis configuration is correct. */ + $errors = []; + if(empty($redis->host)) $errors['redis[host]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->host); + if(empty($redis->port)) $errors['redis[port]'] = sprintf($this->lang->error->notempty, $this->lang->cache->redis->port); + if($errors) return $this->send(array('result' => 'fail', 'message' => $errors)); + + /* 检查 Redis 连接是否正常。Check if the Redis connection is normal. */ + try + { + helper::connectRedis($redis); + } + catch(Exception $e) + { + return $this->send(array('result' => 'fail', 'message' => $e->getMessage())); + } + } + } + + /* 如果缓存配置发生变化,清空缓存。If the cache configuration changes, clear the cache. */ + if($cache->enable != $this->config->cache->enable + || $cache->driver != $this->config->cache->driver + || $cache->namespace != $this->config->cache->namespace + || $redis->serializer != $this->config->redis->serializer) + { + $this->mao->clear(); + } + + $this->loadModel('setting')->setItems('system.common.cache', $cache); + if($cache->driver == 'redis') $this->setting->setItems('system.common.redis', $redis); + + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true)); + } + + if($this->config->cache->enable) + { + $this->view->rate = $this->mao->memory('rate'); + $this->view->used = $this->mao->memory('used'); + $this->view->total = $this->mao->memory('total'); + } + + $this->view->title = $this->lang->cache->common; + $this->display(); + } + + /** + * 清除数据缓存。 + * Clear data cache. + * + * @access public + * @return void + */ + public function ajaxClear() + { + $this->cache->clear(); + return $this->send(array('result' => 'success', 'message' => $this->lang->cache->clearSuccess, 'load' => true)); + } +} diff --git a/module/cache/js/setting.ui.js b/module/cache/js/setting.ui.js new file mode 100644 index 0000000000..c5ae216b0b --- /dev/null +++ b/module/cache/js/setting.ui.js @@ -0,0 +1,16 @@ +function toggleCache() +{ + $('.cache').toggleClass('hidden', $(this).val() != '1'); +} + +function toggleDriver() +{ + $('.apcu').toggleClass('hidden', $(this).val() != 'apcu'); + $('.redis').toggleClass('hidden', $(this).val() != 'redis'); +} + +function toggleSerializer() +{ + $('.serialize').toggleClass('hidden', $(this).val() != 'serialize'); + $('.igbinary').toggleClass('hidden', $(this).val() != 'igbinary'); +} diff --git a/module/cache/lang/de.php b/module/cache/lang/de.php new file mode 100644 index 0000000000..eda04d2ccc --- /dev/null +++ b/module/cache/lang/de.php @@ -0,0 +1,38 @@ +cache->clear = 'Clear Cache'; +$lang->cache->clearSuccess = 'Cache cleared successfully.'; +$lang->cache->type = 'Cache Type'; +$lang->cache->namespace = 'Namespace'; +$lang->cache->memory = 'Memory'; +$lang->cache->usedMemory = 'Total %s, used %s'; + +$lang->cache->statusList[1] = 'On'; +$lang->cache->statusList[0] = 'Off'; + +$lang->cache->driverList['apcu'] = 'APCu'; +$lang->cache->driverList['redis'] = 'Redis'; + +$lang->cache->apcu = new stdClass(); +$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; +$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; +$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.'; + +$lang->cache->redis = new stdClass(); +$lang->cache->redis->host = 'Redis Host'; +$lang->cache->redis->port = 'Redis Port'; +$lang->cache->redis->username = 'Redis User'; +$lang->cache->redis->password = 'Redis Password'; +$lang->cache->redis->serializer = 'Redis Serializer'; +$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.'; +$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; +$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; + +$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; + +$lang->cache->redis->tips = new stdClass(); +$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; + +$lang->cache->tips = new stdClass(); +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; diff --git a/module/cache/lang/en.php b/module/cache/lang/en.php new file mode 100644 index 0000000000..eda04d2ccc --- /dev/null +++ b/module/cache/lang/en.php @@ -0,0 +1,38 @@ +cache->clear = 'Clear Cache'; +$lang->cache->clearSuccess = 'Cache cleared successfully.'; +$lang->cache->type = 'Cache Type'; +$lang->cache->namespace = 'Namespace'; +$lang->cache->memory = 'Memory'; +$lang->cache->usedMemory = 'Total %s, used %s'; + +$lang->cache->statusList[1] = 'On'; +$lang->cache->statusList[0] = 'Off'; + +$lang->cache->driverList['apcu'] = 'APCu'; +$lang->cache->driverList['redis'] = 'Redis'; + +$lang->cache->apcu = new stdClass(); +$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; +$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; +$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.'; + +$lang->cache->redis = new stdClass(); +$lang->cache->redis->host = 'Redis Host'; +$lang->cache->redis->port = 'Redis Port'; +$lang->cache->redis->username = 'Redis User'; +$lang->cache->redis->password = 'Redis Password'; +$lang->cache->redis->serializer = 'Redis Serializer'; +$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.'; +$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; +$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; + +$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; + +$lang->cache->redis->tips = new stdClass(); +$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; + +$lang->cache->tips = new stdClass(); +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; diff --git a/module/cache/lang/fr.php b/module/cache/lang/fr.php new file mode 100644 index 0000000000..eda04d2ccc --- /dev/null +++ b/module/cache/lang/fr.php @@ -0,0 +1,38 @@ +cache->clear = 'Clear Cache'; +$lang->cache->clearSuccess = 'Cache cleared successfully.'; +$lang->cache->type = 'Cache Type'; +$lang->cache->namespace = 'Namespace'; +$lang->cache->memory = 'Memory'; +$lang->cache->usedMemory = 'Total %s, used %s'; + +$lang->cache->statusList[1] = 'On'; +$lang->cache->statusList[0] = 'Off'; + +$lang->cache->driverList['apcu'] = 'APCu'; +$lang->cache->driverList['redis'] = 'Redis'; + +$lang->cache->apcu = new stdClass(); +$lang->cache->apcu->notice = 'To use APCu cache, you need to load the APCu extension first.'; +$lang->cache->apcu->notLoaded = 'Please load the APCu extension before enabling cache.'; +$lang->cache->apcu->notEnabled = 'Please enable the apc.enabled option before enabling cach.'; + +$lang->cache->redis = new stdClass(); +$lang->cache->redis->host = 'Redis Host'; +$lang->cache->redis->port = 'Redis Port'; +$lang->cache->redis->username = 'Redis User'; +$lang->cache->redis->password = 'Redis Password'; +$lang->cache->redis->serializer = 'Redis Serializer'; +$lang->cache->redis->notice = 'To use Redis cache, you need to load the Redis extension first.'; +$lang->cache->redis->notLoaded = 'Please load the Redis extension before enabling cach.'; +$lang->cache->redis->igbinaryNotLoaded = 'Please load the igbinary extension before enabling cach.'; + +$lang->cache->redis->serializerList['serialize'] = 'PHP Serialize'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; + +$lang->cache->redis->tips = new stdClass(); +$lang->cache->redis->tips->host = 'Fill in the domain name or IP address, and do not need to fill in the protocol and port number.'; +$lang->cache->redis->tips->serializer = 'Data needs to be serialized and cached. Changing the serializer will clear the cached data.'; + +$lang->cache->tips = new stdClass(); +$lang->cache->tips->namespace = 'Namespaces are used to prevent cache data conflicts between different applications. Changing the namespace will clear the cache data.'; diff --git a/module/cache/lang/zh-cn.php b/module/cache/lang/zh-cn.php new file mode 100644 index 0000000000..643adcdaa9 --- /dev/null +++ b/module/cache/lang/zh-cn.php @@ -0,0 +1,38 @@ +cache->clear = '清除缓存'; +$lang->cache->clearSuccess = '清除成功'; +$lang->cache->type = '缓存类型'; +$lang->cache->namespace = '缓存命名空间'; +$lang->cache->memory = '内存使用'; +$lang->cache->usedMemory = '总计 %s,已使用 %s'; + +$lang->cache->statusList[1] = '开启'; +$lang->cache->statusList[0] = '关闭'; + +$lang->cache->driverList['apcu'] = 'APCu'; +$lang->cache->driverList['redis'] = 'Redis'; + +$lang->cache->apcu = new stdClass(); +$lang->cache->apcu->notice = '使用 APCu 缓存需要先加载 APCu 扩展。'; +$lang->cache->apcu->notLoaded = '请加载 APCu 扩展后再开启数据缓存'; +$lang->cache->apcu->notEnabled = '请启用 apc.enabled 选项后再开启数据缓存'; + +$lang->cache->redis = new stdClass(); +$lang->cache->redis->host = 'Redis 主机'; +$lang->cache->redis->port = 'Redis 端口'; +$lang->cache->redis->username = 'Redis 用户名'; +$lang->cache->redis->password = 'Redis 密码'; +$lang->cache->redis->serializer = 'Redis 序列化器'; +$lang->cache->redis->notice = '使用 Redis 缓存需要先加载 Redis 扩展。'; +$lang->cache->redis->notLoaded = '请加载 Redis 扩展后再开启数据缓存。'; +$lang->cache->redis->igbinaryNotLoaded = '请加载 igbinary 扩展后再开启数据缓存。'; + +$lang->cache->redis->serializerList['serialize'] = 'PHP 内置序列化器'; +$lang->cache->redis->serializerList['igbinary'] = 'igbinary'; + +$lang->cache->redis->tips = new stdClass(); +$lang->cache->redis->tips->host = '填写域名或 IP 地址,无需填写协议和端口号。'; +$lang->cache->redis->tips->serializer = '数据需要序列化后缓存。更改序列化器会清空缓存数据。'; + +$lang->cache->tips = new stdClass(); +$lang->cache->tips->namespace = '命名空间用来防止不同应用间缓存数据冲突。更改命名空间会清空缓存数据。'; diff --git a/module/cache/model.php b/module/cache/model.php new file mode 100644 index 0000000000..692d25b7ef --- /dev/null +++ b/module/cache/model.php @@ -0,0 +1,29 @@ + + * @package cache + * @link https://www.zentao.net + */ +class cacheModel extends model +{ + /** + * 清空缓存。 + * Clear the cache. + * + * @access public + * @return void + */ + public function clear() + { + /* Redis 采用遍历删除的方式,所以需要先关闭缓存,清空之后再打开。Redis uses the method of traversing deletion, so you need to turn off the cache first, clear it, and then turn it on. */ + $needStop = $this->config->cache->driver == 'redis'; + if($needStop) $this->loadModel('setting')->setItem('system.common.cache.enable', 0); + $this->mao->clear(); + if($needStop) $this->setting->setItem('system.common.cache.enable', 1); + } +} diff --git a/module/cache/ui/setting.html.php b/module/cache/ui/setting.html.php new file mode 100644 index 0000000000..6601c8839d --- /dev/null +++ b/module/cache/ui/setting.html.php @@ -0,0 +1,182 @@ + + * @package cache + * @link https://www.zentao.net + */ +namespace zin; + +$driver = !empty($config->cache->driver) ? $config->cache->driver : ''; +$namespace = !empty($config->cache->namespace) ? $config->cache->namespace : $config->db->name; +$serializer = !empty($config->redis->serializer) ? $config->redis->serializer : 'serialize'; +$hiddenCache = $config->cache->enable ? '' : ' hidden'; +$hiddenApcu = $driver == 'apcu' ? '' : ' hidden'; +$hiddenRedis = $driver == 'redis' ? '' : ' hidden'; + +formPanel +( + set::actions + ([ + 'submit', + $config->cache->enable ? ['text' => $lang->cache->clear, 'url' => inlink('ajaxClear'), 'class' => 'secondary ajax-submit'] : null, + 'cancel' + ]), + on::change('input[name=enable]', 'toggleCache'), + on::change('input[name=driver]', 'toggleDriver'), + on::change('input[name=redis\\\[serializer\\\]]', 'toggleSerializer'), + formGroup + ( + set::label($lang->cache->common), + radioList + ( + set::name('enable'), + set::items($lang->cache->statusList), + set::value($config->cache->enable), + set::inline(true) + ), + set::required() + ), + formGroup + ( + setClass('cache' . $hiddenCache), + set::label($lang->cache->type), + radioList + ( + setClass('w-1/3'), + set::name('driver'), + set::items($lang->cache->driverList), + set::value($driver), + set::inline(true) + ), + span + ( + setClass('apcu ml-4 mt-1.5' . $hiddenApcu), + icon('info text-warning mr-2'), + $lang->cache->apcu->notice + ), + span + ( + setClass('redis ml-4 mt-1.5' . $hiddenRedis), + icon('info text-warning mr-2'), + $lang->cache->redis->notice + ), + set::required() + ), + formGroup + ( + setClass('cache' . $hiddenCache), + set::label($lang->cache->namespace), + set::required(), + input + ( + setClass('w-1/3'), + set::name('namespace'), + set::value($namespace) + ), + span + ( + setClass('ml-4 mt-1.5'), + icon('info text-warning mr-2'), + $lang->cache->tips->namespace + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->host), + set::required(), + input + ( + setClass('w-1/3'), + set::name('redis[host]'), + set::value($config->redis->host) + ), + span + ( + setClass('ml-4 mt-1.5'), + icon('info text-warning mr-2'), + $lang->cache->redis->tips->host + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->port), + set::required(), + input + ( + setClass('w-1/3'), + set::name('redis[port]'), + set::value($config->redis->port) + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->username), + input + ( + setClass('w-1/3'), + set::name('redis[username]'), + set::value($config->redis->username) + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->password), + input + ( + setClass('w-1/3'), + set::name('redis[password]'), + set::type('password'), + set::value($config->redis->password) + ) + ), + formGroup + ( + setClass('redis' . $hiddenRedis), + set::label($lang->cache->redis->serializer), + radioList + ( + setClass('w-1/3'), + set::name('redis[serializer]'), + set::items($lang->cache->redis->serializerList), + set::value($serializer), + set::inline(true) + ), + span + ( + setClass('ml-4 mt-1.5'), + icon('info text-warning mr-2'), + $lang->cache->redis->tips->serializer + ) + ), + $config->cache->enable ? formGroup + ( + setStyle(array('align-items' => 'center')), + set::label($lang->cache->memory), + div + ( + setClass('w-1/3'), + progressBar + ( + set::percent($rate), + set::width('100%'), + set::color('rgb(var(--color-' . ($rate <= 50 ? 'success' : ($rate <= 80 ? 'warning' : 'danger')) . '-500-rgb))') + ) + ), + div + ( + setClass('flex ml-4 gap-4'), + span($rate . '%'), + span(sprintf($lang->cache->usedMemory, $total, $used)) + ) + ) : null +); + +render();