Add the apcu caching ability for performance (#7276)
* add the apcu caching ability for performance When the PHP extension APCu is loaded, the settings class and the auto_loader will cache their results across requests in RAM. For more information about the APCu extension visit the PHP page: https://www.php.net/apcu * use global instead of default terminology
This commit is contained in:
parent
305f585b17
commit
d919a3cc1b
|
|
@ -331,4 +331,3 @@
|
||||||
require_once "resources/footer.php";
|
require_once "resources/footer.php";
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ $search = $_REQUEST['search'] ?? '';
|
||||||
$domain_uuid = $_GET['id'] ?? null;
|
$domain_uuid = $_GET['id'] ?? null;
|
||||||
|
|
||||||
//reload default settings
|
//reload default settings
|
||||||
require "resources/classes/domains.php";
|
settings::clear_cache();
|
||||||
$domain = new domains();
|
$domain = new domains();
|
||||||
$domain->set();
|
$domain->set();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//clear domain apcu cache due to changes
|
||||||
|
settings::clear_cache('domain');
|
||||||
|
|
||||||
//redirect the browser
|
//redirect the browser
|
||||||
if ($action == "update") {
|
if ($action == "update") {
|
||||||
message::add($text['message-update']);
|
message::add($text['message-update']);
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,9 @@ if (!empty($_POST) && empty($_POST["persistformvar"])) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//clear the user settings cache
|
||||||
|
settings::clear_cache('user');
|
||||||
|
|
||||||
//redirect the browser
|
//redirect the browser
|
||||||
if ($action == "update") {
|
if ($action == "update") {
|
||||||
message::add($text['message-update']);
|
message::add($text['message-update']);
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,21 @@
|
||||||
class auto_loader {
|
class auto_loader {
|
||||||
|
|
||||||
const FILE = 'autoloader_cache.php';
|
const FILE = 'autoloader_cache.php';
|
||||||
|
const CACHE_KEY = 'autoloader_classes';
|
||||||
|
|
||||||
private $classes;
|
private $classes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks the APCu extension for caching to RAM drive across requests
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $apcu_enabled;
|
||||||
|
|
||||||
public function __construct($project_path = '') {
|
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
|
//classes must be loaded before this object is registered
|
||||||
if (!$this->load_cache()) {
|
if (!$this->load_cache()) {
|
||||||
//cache miss so load them
|
//cache miss so load them
|
||||||
|
|
@ -43,34 +54,49 @@ class auto_loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update_cache(string $file = ''): bool {
|
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
|
//ensure we have somewhere to put the file
|
||||||
if (empty($file)) {
|
if (empty($file)) {
|
||||||
$file = sys_get_temp_dir() . '/' . self::FILE;
|
$file = sys_get_temp_dir() . '/' . self::FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//guard against writing an empty file
|
//export the classes array using PHP engine
|
||||||
if (!empty($this->classes)) {
|
$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
|
//put the array in a form that it can be loaded directly to an array
|
||||||
$result = file_put_contents($file, "<?php\n return " . $data . ";\n");
|
$result = file_put_contents($file, "<?php\n return " . $data . ";\n");
|
||||||
if ($result !== false) {
|
if ($result !== false) {
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
$error_array = error_get_last();
|
|
||||||
//send to syslog when debugging
|
|
||||||
if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') {
|
|
||||||
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
|
|
||||||
syslog(LOG_WARNING, "[php][auto_loader] " . $error_array['message']);
|
|
||||||
closelog();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$error_array = error_get_last();
|
||||||
|
//send to syslog when debugging
|
||||||
|
if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') {
|
||||||
|
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
|
||||||
|
syslog(LOG_WARNING, "[php][auto_loader] " . $error_array['message']);
|
||||||
|
closelog();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load_cache(string $file = ''): bool {
|
public function load_cache(string $file = ''): bool {
|
||||||
$this->classes = [];
|
$this->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
|
//use a standard file
|
||||||
if (empty($file)) {
|
if (empty($file)) {
|
||||||
$file = sys_get_temp_dir() . '/'. self::FILE;
|
$file = sys_get_temp_dir() . '/'. self::FILE;
|
||||||
|
|
@ -81,6 +107,10 @@ class auto_loader {
|
||||||
}
|
}
|
||||||
//assign to an array
|
//assign to an array
|
||||||
if (!empty($this->classes)) {
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -125,6 +155,11 @@ class auto_loader {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Smarty has it's own autoloader so reject the request
|
||||||
|
if ($class_name === 'Smarty_Autoloader') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//cache miss
|
//cache miss
|
||||||
if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') {
|
if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') {
|
||||||
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
|
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
|
||||||
|
|
@ -186,4 +221,10 @@ class auto_loader {
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function clear_cache() {
|
||||||
|
if (function_exists('apcu_enabled') && apcu_enabled()) {
|
||||||
|
apcu_delete(self::CACHE_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,17 @@ class cache {
|
||||||
closelog();
|
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
|
//cache method memcache
|
||||||
if ($this->method === "memcache") {
|
if ($this->method === "memcache") {
|
||||||
// connect to event socket
|
// connect to event socket
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,12 @@ class settings {
|
||||||
*/
|
*/
|
||||||
private $database;
|
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.
|
* Create a settings object using key/value pairs in the $setting_array.
|
||||||
*
|
*
|
||||||
|
|
@ -63,6 +69,16 @@ class settings {
|
||||||
*/
|
*/
|
||||||
public function __construct($setting_array = []) {
|
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
|
//open a database connection
|
||||||
if (empty($setting_array['database'])) {
|
if (empty($setting_array['database'])) {
|
||||||
$this->database = database::new();
|
$this->database = database::new();
|
||||||
|
|
@ -155,7 +171,7 @@ class settings {
|
||||||
* @param bool $enabled (optional) True or False. Default is True.
|
* @param bool $enabled (optional) True or False. Default is True.
|
||||||
* @param string $description (optional) Description. Default is empty string.
|
* @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
|
//set the table name
|
||||||
$table_name = $table_prefix.'_settings';
|
$table_name = $table_prefix.'_settings';
|
||||||
|
|
@ -207,6 +223,15 @@ class settings {
|
||||||
*/
|
*/
|
||||||
private function default_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
|
//get the default settings
|
||||||
$sql = "select * from v_default_settings ";
|
$sql = "select * from v_default_settings ";
|
||||||
$sql .= "where default_setting_enabled = 'true' ";
|
$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
|
* @access private
|
||||||
*/
|
*/
|
||||||
private function domain_settings() {
|
private function domain_settings() {
|
||||||
|
$key = 'settings_domain_'.$this->domain_uuid;
|
||||||
$sql = "select * from v_domain_settings ";
|
$result = '';
|
||||||
$sql .= "where domain_uuid = :domain_uuid ";
|
//if the apcu extension is loaded get the cached database result
|
||||||
$sql .= "and domain_setting_enabled = 'true' ";
|
if ($this->apcu_enabled && apcu_exists($key)) {
|
||||||
$parameters['domain_uuid'] = $this->domain_uuid;
|
$result = apcu_fetch($key);
|
||||||
$result = $this->database->select($sql, $parameters, 'all');
|
} else {
|
||||||
unset($sql, $parameters);
|
$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)) {
|
if (!empty($result)) {
|
||||||
//domain setting array types override the default settings set as type array
|
//domain setting array types override the default settings set as type array
|
||||||
foreach ($result as $row) {
|
foreach ($result as $row) {
|
||||||
|
|
@ -305,7 +343,6 @@ class settings {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unset($result, $row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -314,15 +351,25 @@ class settings {
|
||||||
* @depends $this->domain_uuid
|
* @depends $this->domain_uuid
|
||||||
*/
|
*/
|
||||||
private function user_settings() {
|
private function user_settings() {
|
||||||
|
$key = 'settings_user_'.$this->user_uuid;
|
||||||
$sql = "select * from v_user_settings ";
|
$result = '';
|
||||||
$sql .= "where domain_uuid = :domain_uuid ";
|
//if the apcu extension is loaded get the cached database result
|
||||||
$sql .= "and user_uuid = :user_uuid ";
|
if ($this->apcu_enabled && apcu_exists($key)) {
|
||||||
$sql .= " order by user_setting_order asc ";
|
$result = apcu_fetch($key);
|
||||||
$parameters['domain_uuid'] = $this->domain_uuid;
|
} else {
|
||||||
$parameters['user_uuid'] = $this->user_uuid;
|
$sql = "select * from v_user_settings ";
|
||||||
$result = $this->database->select($sql, $parameters, 'all');
|
$sql .= "where domain_uuid = :domain_uuid ";
|
||||||
if (is_array($result)) {
|
$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) {
|
foreach ($result as $row) {
|
||||||
if ($row['user_setting_enabled'] == 'true') {
|
if ($row['user_setting_enabled'] == 'true') {
|
||||||
$name = $row['user_setting_name'];
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue