Newwebshop/app/Core/Cache.php

471 lines
12 KiB
PHP

<?php
/**
* Copyright seit 2024 Webshop System
*
* Cache-Core-Klasse für das Webshop-System
*
* @author Webshop System
* @license GPL v3
*/
namespace App\Core;
use Redis;
use Exception;
class Cache
{
private $redis;
private $fileCachePath;
private $defaultTtl = 3600; // 1 Stunde
private $cacheEnabled = true;
public function __construct()
{
$this->fileCachePath = __DIR__ . '/../../cache/';
$this->initRedis();
}
/**
* Redis-Verbindung initialisieren
*/
private function initRedis()
{
try {
$this->redis = new Redis();
$this->redis->connect(
getenv('REDIS_HOST') ?: 'redis',
getenv('REDIS_PORT') ?: 6379
);
if (getenv('REDIS_PASSWORD')) {
$this->redis->auth(getenv('REDIS_PASSWORD'));
}
$this->redis->select(getenv('REDIS_DB') ?: 0);
} catch (Exception $e) {
// Redis nicht verfügbar, verwende File-Cache
$this->redis = null;
}
}
/**
* Cache-Schlüssel generieren
*/
private function generateKey($key, $prefix = 'ws')
{
return $prefix . ':' . md5($key);
}
/**
* Wert im Cache speichern
*/
public function set($key, $value, $ttl = null)
{
if (!$this->cacheEnabled) {
return false;
}
$ttl = $ttl ?: $this->defaultTtl;
$cacheKey = $this->generateKey($key);
try {
if ($this->redis) {
// Redis-Cache
$serializedValue = serialize($value);
return $this->redis->setex($cacheKey, $ttl, $serializedValue);
} else {
// File-Cache
return $this->setFileCache($cacheKey, $value, $ttl);
}
} catch (Exception $e) {
// Fallback zu File-Cache
return $this->setFileCache($cacheKey, $value, $ttl);
}
}
/**
* Wert aus dem Cache abrufen
*/
public function get($key)
{
if (!$this->cacheEnabled) {
return false;
}
$cacheKey = $this->generateKey($key);
try {
if ($this->redis) {
// Redis-Cache
$value = $this->redis->get($cacheKey);
return $value ? unserialize($value) : false;
} else {
// File-Cache
return $this->getFileCache($cacheKey);
}
} catch (Exception $e) {
// Fallback zu File-Cache
return $this->getFileCache($cacheKey);
}
}
/**
* Wert aus dem Cache löschen
*/
public function delete($key)
{
$cacheKey = $this->generateKey($key);
try {
if ($this->redis) {
// Redis-Cache
return $this->redis->del($cacheKey);
} else {
// File-Cache
return $this->deleteFileCache($cacheKey);
}
} catch (Exception $e) {
// Fallback zu File-Cache
return $this->deleteFileCache($cacheKey);
}
}
/**
* Cache komplett leeren
*/
public function clear($pattern = null)
{
try {
if ($this->redis) {
// Redis-Cache
if ($pattern) {
$keys = $this->redis->keys($this->generateKey($pattern, 'ws*'));
if (!empty($keys)) {
return $this->redis->del($keys);
}
} else {
return $this->redis->flushDB();
}
} else {
// File-Cache
return $this->clearFileCache($pattern);
}
} catch (Exception $e) {
return $this->clearFileCache($pattern);
}
}
/**
* Cache-Status prüfen
*/
public function has($key)
{
$cacheKey = $this->generateKey($key);
try {
if ($this->redis) {
// Redis-Cache
return $this->redis->exists($cacheKey);
} else {
// File-Cache
return $this->hasFileCache($cacheKey);
}
} catch (Exception $e) {
return $this->hasFileCache($cacheKey);
}
}
/**
* File-Cache: Wert speichern
*/
private function setFileCache($key, $value, $ttl)
{
$filename = $this->fileCachePath . $key . '.cache';
$data = [
'value' => $value,
'expires' => time() + $ttl
];
if (!is_dir($this->fileCachePath)) {
mkdir($this->fileCachePath, 0755, true);
}
return file_put_contents($filename, serialize($data)) !== false;
}
/**
* File-Cache: Wert abrufen
*/
private function getFileCache($key)
{
$filename = $this->fileCachePath . $key . '.cache';
if (!file_exists($filename)) {
return false;
}
$data = unserialize(file_get_contents($filename));
if (!$data || !isset($data['expires']) || $data['expires'] < time()) {
// Cache abgelaufen
unlink($filename);
return false;
}
return $data['value'];
}
/**
* File-Cache: Wert löschen
*/
private function deleteFileCache($key)
{
$filename = $this->fileCachePath . $key . '.cache';
if (file_exists($filename)) {
return unlink($filename);
}
return true;
}
/**
* File-Cache: Status prüfen
*/
private function hasFileCache($key)
{
$filename = $this->fileCachePath . $key . '.cache';
if (!file_exists($filename)) {
return false;
}
$data = unserialize(file_get_contents($filename));
if (!$data || !isset($data['expires']) || $data['expires'] < time()) {
// Cache abgelaufen
unlink($filename);
return false;
}
return true;
}
/**
* File-Cache: Komplett leeren
*/
private function clearFileCache($pattern = null)
{
if (!is_dir($this->fileCachePath)) {
return true;
}
$files = glob($this->fileCachePath . '*.cache');
$deleted = 0;
foreach ($files as $file) {
if ($pattern) {
// Nur Dateien löschen, die dem Pattern entsprechen
if (strpos(basename($file), md5($pattern)) !== false) {
unlink($file);
$deleted++;
}
} else {
// Alle Cache-Dateien löschen
unlink($file);
$deleted++;
}
}
return $deleted;
}
/**
* Cache-Statistiken abrufen
*/
public function getStats()
{
$stats = [
'enabled' => $this->cacheEnabled,
'driver' => $this->redis ? 'redis' : 'file',
'file_cache_path' => $this->fileCachePath,
'default_ttl' => $this->defaultTtl
];
if ($this->redis) {
try {
$stats['redis_info'] = $this->redis->info();
$stats['redis_keys'] = $this->redis->dbSize();
} catch (Exception $e) {
$stats['redis_error'] = $e->getMessage();
}
} else {
$stats['file_cache_files'] = count(glob($this->fileCachePath . '*.cache'));
}
return $stats;
}
/**
* Cache aktivieren/deaktivieren
*/
public function setEnabled($enabled)
{
$this->cacheEnabled = $enabled;
}
/**
* Cache-Schlüssel mit Tags
*/
public function setWithTags($key, $value, $tags = [], $ttl = null)
{
$result = $this->set($key, $value, $ttl);
if ($result && !empty($tags)) {
foreach ($tags as $tag) {
$tagKey = 'tag:' . $tag;
$taggedKeys = $this->get($tagKey) ?: [];
$taggedKeys[] = $key;
$this->set($tagKey, $taggedKeys, $ttl);
}
}
return $result;
}
/**
* Cache mit Tags löschen
*/
public function deleteByTags($tags)
{
$deleted = 0;
foreach ($tags as $tag) {
$tagKey = 'tag:' . $tag;
$taggedKeys = $this->get($tagKey) ?: [];
foreach ($taggedKeys as $key) {
if ($this->delete($key)) {
$deleted++;
}
}
$this->delete($tagKey);
}
return $deleted;
}
/**
* Cache-Warmup für häufig verwendete Daten
*/
public function warmup()
{
// Kategorien cachen
$this->cacheCategories();
// Konfiguration cachen
$this->cacheConfiguration();
// Sprachen cachen
$this->cacheLanguages();
}
/**
* Kategorien cachen
*/
private function cacheCategories()
{
try {
$connectionParams = [
'dbname' => getenv('DB_DATABASE') ?: 'freeshop',
'user' => getenv('DB_USERNAME') ?: 'freeshop_user',
'password' => getenv('DB_PASSWORD') ?: 'freeshop_password',
'host' => getenv('DB_HOST') ?: 'db',
'driver' => 'pdo_mysql',
'port' => getenv('DB_PORT') ?: 3306,
'charset' => 'utf8mb4',
];
$conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams);
$stmt = $conn->prepare('SELECT * FROM ws_category WHERE active = 1 ORDER BY sort_order ASC');
$stmt->execute();
$categories = $stmt->fetchAllAssociative();
$this->set('categories:all', $categories, 3600);
// Einzelne Kategorien cachen
foreach ($categories as $category) {
$this->set('category:' . $category['id'], $category, 3600);
}
} catch (Exception $e) {
// Ignore cache warmup errors
}
}
/**
* Konfiguration cachen
*/
private function cacheConfiguration()
{
try {
$connectionParams = [
'dbname' => getenv('DB_DATABASE') ?: 'freeshop',
'user' => getenv('DB_USERNAME') ?: 'freeshop_user',
'password' => getenv('DB_PASSWORD') ?: 'freeshop_password',
'host' => getenv('DB_HOST') ?: 'db',
'driver' => 'pdo_mysql',
'port' => getenv('DB_PORT') ?: 3306,
'charset' => 'utf8mb4',
];
$conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams);
$stmt = $conn->prepare('SELECT name, value FROM ws_configuration');
$stmt->execute();
$config = $stmt->fetchAllAssociative();
$configArray = [];
foreach ($config as $item) {
$configArray[$item['name']] = $item['value'];
}
$this->set('config:all', $configArray, 7200);
} catch (Exception $e) {
// Ignore cache warmup errors
}
}
/**
* Sprachen cachen
*/
private function cacheLanguages()
{
try {
$connectionParams = [
'dbname' => getenv('DB_DATABASE') ?: 'freeshop',
'user' => getenv('DB_USERNAME') ?: 'freeshop_user',
'password' => getenv('DB_PASSWORD') ?: 'freeshop_password',
'host' => getenv('DB_HOST') ?: 'db',
'driver' => 'pdo_mysql',
'port' => getenv('DB_PORT') ?: 3306,
'charset' => 'utf8mb4',
];
$conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams);
$stmt = $conn->prepare('SELECT * FROM ws_language WHERE active = 1');
$stmt->execute();
$languages = $stmt->fetchAllAssociative();
$this->set('languages:all', $languages, 7200);
} catch (Exception $e) {
// Ignore cache warmup errors
}
}
}