256 lines
7.5 KiB
PHP
256 lines
7.5 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* Redis Cache для ускорения CRM
|
|||
|
|
*
|
|||
|
|
* Кеширует:
|
|||
|
|
* - Метаданные модулей (табиды, поля)
|
|||
|
|
* - Права доступа пользователей
|
|||
|
|
* - Списки picklist значений
|
|||
|
|
* - Настройки модулей
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
class RedisCache {
|
|||
|
|
private $redis;
|
|||
|
|
private $enabled = false;
|
|||
|
|
private $prefix = 'crm:cache:';
|
|||
|
|
private $defaultTTL = 3600; // 1 час
|
|||
|
|
|
|||
|
|
public function __construct() {
|
|||
|
|
try {
|
|||
|
|
if (class_exists('Redis')) {
|
|||
|
|
// Используем расширение Redis
|
|||
|
|
$this->redis = new Redis();
|
|||
|
|
$this->redis->connect('127.0.0.1', 6379);
|
|||
|
|
$this->redis->auth('CRM_Redis_Pass_2025_Secure!');
|
|||
|
|
$this->enabled = true;
|
|||
|
|
} else {
|
|||
|
|
// Используем Predis
|
|||
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|||
|
|
$this->redis = new Predis\Client([
|
|||
|
|
'scheme' => 'tcp',
|
|||
|
|
'host' => '127.0.0.1',
|
|||
|
|
'port' => 6379,
|
|||
|
|
'password' => 'CRM_Redis_Pass_2025_Secure!',
|
|||
|
|
]);
|
|||
|
|
$this->enabled = true;
|
|||
|
|
}
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
error_log("Redis cache disabled: " . $e->getMessage());
|
|||
|
|
$this->enabled = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Получить значение из кеша
|
|||
|
|
*/
|
|||
|
|
public function get($key) {
|
|||
|
|
if (!$this->enabled) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$value = $this->redis->get($this->prefix . $key);
|
|||
|
|
if ($value === false || $value === null) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
return json_decode($value, true);
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
error_log("Redis get error: " . $e->getMessage());
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Сохранить значение в кеш
|
|||
|
|
*/
|
|||
|
|
public function set($key, $value, $ttl = null) {
|
|||
|
|
if (!$this->enabled) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$ttl = $ttl ?? $this->defaultTTL;
|
|||
|
|
$this->redis->setex(
|
|||
|
|
$this->prefix . $key,
|
|||
|
|
$ttl,
|
|||
|
|
json_encode($value)
|
|||
|
|
);
|
|||
|
|
return true;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
error_log("Redis set error: " . $e->getMessage());
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Удалить значение из кеша
|
|||
|
|
*/
|
|||
|
|
public function delete($key) {
|
|||
|
|
if (!$this->enabled) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$this->redis->del($this->prefix . $key);
|
|||
|
|
return true;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
error_log("Redis delete error: " . $e->getMessage());
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Очистить весь кеш
|
|||
|
|
*/
|
|||
|
|
public function flush() {
|
|||
|
|
if (!$this->enabled) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// Удаляем все ключи с нашим префиксом
|
|||
|
|
$keys = $this->redis->keys($this->prefix . '*');
|
|||
|
|
if (!empty($keys)) {
|
|||
|
|
$this->redis->del($keys);
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
error_log("Redis flush error: " . $e->getMessage());
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Получить или установить значение (если не существует)
|
|||
|
|
*/
|
|||
|
|
public function remember($key, $callback, $ttl = null) {
|
|||
|
|
$value = $this->get($key);
|
|||
|
|
|
|||
|
|
if ($value !== null) {
|
|||
|
|
return $value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Вызываем callback для получения значения
|
|||
|
|
$value = $callback();
|
|||
|
|
$this->set($key, $value, $ttl);
|
|||
|
|
|
|||
|
|
return $value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Кешировать результат SQL запроса
|
|||
|
|
*/
|
|||
|
|
public function cacheQuery($key, $query, $params = [], $ttl = null) {
|
|||
|
|
return $this->remember($key, function() use ($query, $params) {
|
|||
|
|
global $adb;
|
|||
|
|
$result = $adb->pquery($query, $params);
|
|||
|
|
|
|||
|
|
$data = [];
|
|||
|
|
while ($row = $adb->fetch_array($result)) {
|
|||
|
|
$data[] = $row;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $data;
|
|||
|
|
}, $ttl);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Кешировать tabid модуля
|
|||
|
|
*/
|
|||
|
|
public function getTabId($moduleName) {
|
|||
|
|
return $this->remember("tabid:{$moduleName}", function() use ($moduleName) {
|
|||
|
|
global $adb;
|
|||
|
|
$result = $adb->pquery("SELECT tabid FROM vtiger_tab WHERE name=?", [$moduleName]);
|
|||
|
|
return $adb->query_result($result, 0, 'tabid');
|
|||
|
|
}, 86400); // 24 часа
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Кешировать поля модуля
|
|||
|
|
*/
|
|||
|
|
public function getModuleFields($moduleName) {
|
|||
|
|
return $this->remember("fields:{$moduleName}", function() use ($moduleName) {
|
|||
|
|
global $adb;
|
|||
|
|
$tabid = getTabid($moduleName);
|
|||
|
|
|
|||
|
|
$query = "SELECT fieldname, fieldlabel, uitype, columnname, tablename, typeofdata
|
|||
|
|
FROM vtiger_field
|
|||
|
|
WHERE tabid=? AND presence IN (0,2)
|
|||
|
|
ORDER BY sequence";
|
|||
|
|
|
|||
|
|
$result = $adb->pquery($query, [$tabid]);
|
|||
|
|
|
|||
|
|
$fields = [];
|
|||
|
|
while ($row = $adb->fetch_array($result)) {
|
|||
|
|
$fields[] = $row;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $fields;
|
|||
|
|
}, 3600); // 1 час
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Кешировать picklist значения
|
|||
|
|
*/
|
|||
|
|
public function getPicklistValues($fieldName) {
|
|||
|
|
return $this->remember("picklist:{$fieldName}", function() use ($fieldName) {
|
|||
|
|
global $adb;
|
|||
|
|
|
|||
|
|
$query = "SELECT DISTINCT vtiger_$fieldName.*
|
|||
|
|
FROM vtiger_$fieldName
|
|||
|
|
ORDER BY sortorderid";
|
|||
|
|
|
|||
|
|
$result = $adb->query($query);
|
|||
|
|
|
|||
|
|
$values = [];
|
|||
|
|
while ($row = $adb->fetch_array($result)) {
|
|||
|
|
$values[] = $row;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $values;
|
|||
|
|
}, 3600); // 1 час
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Кешировать права доступа пользователя
|
|||
|
|
*/
|
|||
|
|
public function getUserPrivileges($userId) {
|
|||
|
|
return $this->remember("privileges:user:{$userId}", function() use ($userId) {
|
|||
|
|
require_once('include/utils/UserInfoUtil.php');
|
|||
|
|
$privileges = getAllUserPrivileges($userId);
|
|||
|
|
return $privileges;
|
|||
|
|
}, 1800); // 30 минут
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Проверить включен ли кеш
|
|||
|
|
*/
|
|||
|
|
public function isEnabled() {
|
|||
|
|
return $this->enabled;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Получить статистику кеша
|
|||
|
|
*/
|
|||
|
|
public function getStats() {
|
|||
|
|
if (!$this->enabled) {
|
|||
|
|
return ['enabled' => false];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$info = $this->redis->info();
|
|||
|
|
return [
|
|||
|
|
'enabled' => true,
|
|||
|
|
'keys' => $this->redis->dbsize(),
|
|||
|
|
'memory' => $info['used_memory_human'] ?? 'unknown',
|
|||
|
|
'hits' => $info['keyspace_hits'] ?? 0,
|
|||
|
|
'misses' => $info['keyspace_misses'] ?? 0,
|
|||
|
|
];
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
return ['enabled' => false, 'error' => $e->getMessage()];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|