diff --git a/app/sip_status/sip_status.php b/app/sip_status/sip_status.php index 5dfb85360e..364023a108 100644 --- a/app/sip_status/sip_status.php +++ b/app/sip_status/sip_status.php @@ -331,4 +331,3 @@ require_once "resources/footer.php"; ?> - diff --git a/core/default_settings/default_settings_reload.php b/core/default_settings/default_settings_reload.php index 1400e7b976..916d51add7 100644 --- a/core/default_settings/default_settings_reload.php +++ b/core/default_settings/default_settings_reload.php @@ -46,7 +46,7 @@ $search = $_REQUEST['search'] ?? ''; $domain_uuid = $_GET['id'] ?? null; //reload default settings -require "resources/classes/domains.php"; +settings::clear_cache(); $domain = new domains(); $domain->set(); diff --git a/core/domain_settings/domain_setting_edit.php b/core/domain_settings/domain_setting_edit.php index 80b8395569..e9294fd58a 100644 --- a/core/domain_settings/domain_setting_edit.php +++ b/core/domain_settings/domain_setting_edit.php @@ -307,6 +307,9 @@ } } + //clear domain apcu cache due to changes + settings::clear_cache('domain'); + //redirect the browser if ($action == "update") { message::add($text['message-update']); diff --git a/core/user_settings/user_setting_edit.php b/core/user_settings/user_setting_edit.php index b480be6f05..3e0ec3fc2f 100644 --- a/core/user_settings/user_setting_edit.php +++ b/core/user_settings/user_setting_edit.php @@ -298,6 +298,9 @@ if (!empty($_POST) && empty($_POST["persistformvar"])) { } } + //clear the user settings cache + settings::clear_cache('user'); + //redirect the browser if ($action == "update") { message::add($text['message-update']); diff --git a/resources/classes/auto_loader.php b/resources/classes/auto_loader.php index 85dbcdb261..63042eb2d7 100644 --- a/resources/classes/auto_loader.php +++ b/resources/classes/auto_loader.php @@ -27,10 +27,21 @@ class auto_loader { const FILE = 'autoloader_cache.php'; + const CACHE_KEY = 'autoloader_classes'; private $classes; + /** + * Tracks the APCu extension for caching to RAM drive across requests + * @var bool + */ + private $apcu_enabled; + public function __construct($project_path = '') { + + //set if we can use RAM cache + $this->apcu_enabled = function_exists('apcu_enabled') && apcu_enabled(); + //classes must be loaded before this object is registered if (!$this->load_cache()) { //cache miss so load them @@ -43,34 +54,49 @@ class auto_loader { } public function update_cache(string $file = ''): bool { + //guard against writing an empty file + if (!empty($this->classes)) { + return false; + } + + //update RAM cache when available + if ($this->apcu_enabled) { + apcu_store(self::CACHE_KEY, $this->classes); + } + //ensure we have somewhere to put the file if (empty($file)) { $file = sys_get_temp_dir() . '/' . self::FILE; } - //guard against writing an empty file - if (!empty($this->classes)) { - //export the classes array using PHP engine - $data = var_export($this->classes, true); + //export the classes array using PHP engine + $data = var_export($this->classes, true); - //put the array in a form that it can be loaded directly to an array - $result = file_put_contents($file, "classes = []; + + //use apcu when available + if ($this->apcu_enabled && apcu_exists(self::CACHE_KEY)) { + $this->classes = apcu_fetch(self::CACHE_KEY, $success); + if ($success) return true; + } + //use a standard file if (empty($file)) { $file = sys_get_temp_dir() . '/'. self::FILE; @@ -81,6 +107,10 @@ class auto_loader { } //assign to an array if (!empty($this->classes)) { + //cache edge case of first time using apcu cache + if ($this->apcu_enabled) { + apcu_store(self::CACHE_KEY, $this->classes); + } return true; } return false; @@ -125,6 +155,11 @@ class auto_loader { return true; } + //Smarty has it's own autoloader so reject the request + if ($class_name === 'Smarty_Autoloader') { + return false; + } + //cache miss if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') { openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0); @@ -186,4 +221,10 @@ class auto_loader { } return ''; } + + public static function clear_cache() { + if (function_exists('apcu_enabled') && apcu_enabled()) { + apcu_delete(self::CACHE_KEY); + } + } } diff --git a/resources/classes/cache.php b/resources/classes/cache.php index 557d2cda63..f0ef9056ee 100644 --- a/resources/classes/cache.php +++ b/resources/classes/cache.php @@ -187,6 +187,17 @@ class cache { closelog(); } + //check for apcu extension + if (function_exists('apcu_enabled') && apcu_enabled()) { + //flush everything + apcu_clear_cache(); + } + + //remove the autoloader file cache + if (file_exists(sys_get_temp_dir() . '/' . auto_loader::FILE)) { + @unlink(sys_get_temp_dir() . '/' . auto_loader::FILE); + } + //cache method memcache if ($this->method === "memcache") { // connect to event socket diff --git a/resources/classes/settings.php b/resources/classes/settings.php index b2a85e024a..3a9a21c452 100644 --- a/resources/classes/settings.php +++ b/resources/classes/settings.php @@ -53,6 +53,12 @@ class settings { */ private $database; + /** + * Tracks if the APCu extension is loaded for database queries + * @var bool + */ + private $apcu_enabled; + /** * Create a settings object using key/value pairs in the $setting_array. * @@ -63,6 +69,16 @@ class settings { */ public function __construct($setting_array = []) { + //try to use RAM cache by default + if (!isset($setting_array['allow_caching'])) { + $setting_array['allow_caching'] = true; + } + + //track ram caching + if ($setting_array['allow_caching']) { + $this->apcu_enabled = function_exists('apcu_enabled') && apcu_enabled(); + } + //open a database connection if (empty($setting_array['database'])) { $this->database = database::new(); @@ -155,7 +171,7 @@ class settings { * @param bool $enabled (optional) True or False. Default is True. * @param string $description (optional) Description. Default is empty string. */ - public function set(string $table_prefix, string $uuid, string $category, string $subcategory, string $type = 'text', string $value = "", bool $enabled = true, string $description = "") { + public function set(string $table_prefix, string $uuid, string $category, string $subcategory, string $value = "", string $type = 'text', bool $enabled = true, string $description = "") { //set the table name $table_name = $table_prefix.'_settings'; @@ -207,6 +223,15 @@ class settings { */ private function default_settings() { + //set the key for global defaults + $key = 'settings_global_' . $this->category; + + //if the apcu extension is loaded get the already parsed array + if ($this->apcu_enabled && apcu_exists($key)) { + $this->settings = apcu_fetch($key); + return; + } + //get the default settings $sql = "select * from v_default_settings "; $sql .= "where default_setting_enabled = 'true' "; @@ -247,7 +272,11 @@ class settings { } } } - unset($sql, $result, $row); + + //if the apcu extension is loaded store the result + if ($this->apcu_enabled) { + apcu_store($key, $this->settings); + } } /** @@ -255,13 +284,22 @@ class settings { * @access private */ private function domain_settings() { - - $sql = "select * from v_domain_settings "; - $sql .= "where domain_uuid = :domain_uuid "; - $sql .= "and domain_setting_enabled = 'true' "; - $parameters['domain_uuid'] = $this->domain_uuid; - $result = $this->database->select($sql, $parameters, 'all'); - unset($sql, $parameters); + $key = 'settings_domain_'.$this->domain_uuid; + $result = ''; + //if the apcu extension is loaded get the cached database result + if ($this->apcu_enabled && apcu_exists($key)) { + $result = apcu_fetch($key); + } else { + $sql = "select * from v_domain_settings "; + $sql .= "where domain_uuid = :domain_uuid "; + $sql .= "and domain_setting_enabled = 'true' "; + $parameters['domain_uuid'] = $this->domain_uuid; + $result = $this->database->select($sql, $parameters, 'all'); + //if the apcu extension is loaded store the result + if ($this->apcu_enabled) { + apcu_store($key, $result); + } + } if (!empty($result)) { //domain setting array types override the default settings set as type array foreach ($result as $row) { @@ -305,7 +343,6 @@ class settings { } } - unset($result, $row); } /** @@ -314,15 +351,25 @@ class settings { * @depends $this->domain_uuid */ private function user_settings() { - - $sql = "select * from v_user_settings "; - $sql .= "where domain_uuid = :domain_uuid "; - $sql .= "and user_uuid = :user_uuid "; - $sql .= " order by user_setting_order asc "; - $parameters['domain_uuid'] = $this->domain_uuid; - $parameters['user_uuid'] = $this->user_uuid; - $result = $this->database->select($sql, $parameters, 'all'); - if (is_array($result)) { + $key = 'settings_user_'.$this->user_uuid; + $result = ''; + //if the apcu extension is loaded get the cached database result + if ($this->apcu_enabled && apcu_exists($key)) { + $result = apcu_fetch($key); + } else { + $sql = "select * from v_user_settings "; + $sql .= "where domain_uuid = :domain_uuid "; + $sql .= "and user_uuid = :user_uuid "; + $sql .= " order by user_setting_order asc "; + $parameters['domain_uuid'] = $this->domain_uuid; + $parameters['user_uuid'] = $this->user_uuid; + $result = $this->database->select($sql, $parameters, 'all'); + //if the apcu extension is loaded store the result + if ($this->apcu_enabled) { + apcu_store($key, $result); + } + } + if (!empty($result)) { foreach ($result as $row) { if ($row['user_setting_enabled'] == 'true') { $name = $row['user_setting_name']; @@ -406,4 +453,34 @@ class settings { } } } + + /** + * Clears the settings cache + * @param string $type Empty clears all settings. Values can be global, domain, or user. + */ + public static function clear_cache(string $type = '') { + if (function_exists('apcu_enabled') && apcu_enabled()) { + switch ($type) { + case 'all': + case '': + default: + $type = ""; + break; + case 'global': + case 'domain': + case 'user': + $type .= "_"; + break; + } + $cache = apcu_cache_info(false); + if (!empty($cache['cache_list'])) { + foreach ($cache['cache_list'] as $entry) { + $key = $entry['info']; + if (str_starts_with($key, 'settings_' . $type)) { + apcu_delete($key); + } + } + } + } + } }