Try with this /lib/Cache/BaseCache.php:
Code: Select all
<?php
namespace FormaLms\lib\Cache;
abstract class BaseCache
{
public const FORMAT_PHP = 'php';
public const FORMAT_JSON = 'json';
public const FORMAT_MSGPACK = 'msgpack';
public const FORMAT_IGBINARY = 'igbinary';
protected string $cacheDir;
protected string $namespace;
protected array $memoryCache = [];
protected string $cacheFormat;
protected int $cacheTTL;
protected static $instances = [];
protected function __construct(
string $subDir,
?string $format = self::FORMAT_JSON,
?int $ttl = null,
?string $namespace = null
) {
$this->cacheDir = _files_ . '/cache/' . trim($subDir, '/') . '/';
$this->namespace = $namespace ?? static::class;
$this->cacheTTL = $ttl ?? 3600;
$this->cacheFormat = $this->determineFormat($format);
$this->ensureStructure();
}
public function __clone() {}
public function __wakeup() {}
public static function getInstance(
?string $format = 'php',
?int $ttl = null,
?string $namespace = null
) {
$class = static::class;
$key = $class . '_' . ($format ?? 'default') . '_' . ($ttl ?? 'default');
if (!isset(self::$instances[$key])) {
self::$instances[$key] = new static(
static::getSubDir(),
$format,
$ttl,
$namespace
);
}
return self::$instances[$key];
}
public static function resetInstances(): void
{
self::$instances = [];
}
abstract protected static function getSubDir(): string;
protected function ensureStructure(): void
{
if (!is_dir($this->cacheDir) && !mkdir($this->cacheDir, 0755, true) && !is_dir($this->cacheDir)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $this->cacheDir));
}
}
protected function determineFormat(?string $format = null): string
{
$selectedFormat = strtolower(\FormaLms\lib\Get::cfg('cache_format', $format));
switch ($selectedFormat) {
case 'msgpack':
return extension_loaded('msgpack') ? self::FORMAT_MSGPACK : self::FORMAT_JSON;
case 'igbinary':
return extension_loaded('igbinary') ? self::FORMAT_IGBINARY : self::FORMAT_JSON;
case 'php':
return self::FORMAT_PHP;
case 'json':
default:
return self::FORMAT_JSON;
}
}
public function getCacheFormat(): string
{
return $this->cacheFormat;
}
public function getTTL(): int
{
return $this->cacheTTL;
}
public function setTTL(int $ttl): void
{
$this->cacheTTL = $ttl;
}
public function get($key, $subKey, $type = 'default')
{
$cacheKey = $this->buildMemoryCacheKey($key, $subKey, $type);
if (isset($this->memoryCache[$cacheKey])) {
return $this->memoryCache[$cacheKey];
}
$file = $this->getCacheFile($key, $subKey, $type);
if (!file_exists($file)) {
return null;
}
try {
$content = file_get_contents($file);
if ($content === false) {
return null;
}
$data = $this->unserialize($content);
if (!is_array($data) || !isset($data['timestamp']) || !isset($data['data'])) {
return null;
}
if ((time() - $data['timestamp']) > $this->cacheTTL) {
@unlink($file);
return null;
}
$this->memoryCache[$cacheKey] = $data['data'];
return $data['data'];
} catch (\Throwable $e) {
return null;
}
}
public function set($key, $subKey, $data, $type = 'default'): bool
{
try {
$file = $this->getCacheFile($key, $subKey, $type);
$cacheKey = $this->buildMemoryCacheKey($key, $subKey, $type);
$this->memoryCache[$cacheKey] = $data;
$dir = dirname($file);
if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir));
}
$cacheData = [
'timestamp' => time(),
'data' => $data
];
$content = $this->serialize($cacheData);
if (file_put_contents($file, $content) === false) {
return false;
}
if (function_exists('opcache_invalidate')) {
opcache_invalidate($file, true);
if ($this->cacheFormat === self::FORMAT_PHP) {
opcache_compile_file($file);
}
}
return true;
} catch (\Throwable $e) {
return false;
}
}
protected function serialize($data): string
{
switch ($this->cacheFormat) {
case self::FORMAT_PHP:
return sprintf("<?php\nreturn %s;", var_export($data, true));
case self::FORMAT_JSON:
return json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
case self::FORMAT_MSGPACK:
return msgpack_pack($data);
case self::FORMAT_IGBINARY:
return igbinary_serialize($data);
default:
throw new \RuntimeException("Unsupported cache format: {$this->cacheFormat}");
}
}
protected function unserialize($content)
{
switch ($this->cacheFormat) {
case self::FORMAT_PHP:
return include $content;
case self::FORMAT_JSON:
return json_decode($content, true, 512, JSON_THROW_ON_ERROR);
case self::FORMAT_MSGPACK:
return msgpack_unpack($content);
case self::FORMAT_IGBINARY:
return igbinary_unserialize($content);
default:
throw new \RuntimeException("Unsupported cache format: {$this->cacheFormat}");
}
}
protected function getCacheExtension(): string
{
switch ($this->cacheFormat) {
case self::FORMAT_PHP:
return '.php';
case self::FORMAT_JSON:
return '.json';
case self::FORMAT_MSGPACK:
return '.msg';
case self::FORMAT_IGBINARY:
return '.igb';
default:
return '.cache';
}
}
protected function buildMemoryCacheKey($key, $subKey, $type): string
{
return sprintf('%s_%s_%s', $type, $key, $subKey);
}
protected function getCacheFile($key, $subKey, $type): string
{
return $this->cacheDir . $type . '/' . $key . '/' . $subKey . $this->getCacheExtension();
}
public function clear($key = null, $subKey = null, $type = null): void
{
if ($key === null && $subKey === null && $type === null) {
$this->memoryCache = [];
} else {
$pattern = sprintf(
'%s%s%s',
$type === null ? '' : $type . '_',
$key === null ? '' : $key . '_',
$subKey ?? ''
);
foreach ($this->memoryCache as $cacheKey => $value) {
if (strpos($cacheKey, $pattern) === 0) {
unset($this->memoryCache[$cacheKey]);
}
}
}
if ($key === null && $subKey === null && $type === null) {
$this->clearDirectory($this->cacheDir);
$this->ensureStructure();
return;
}
if ($type !== null) {
$typeDir = $this->cacheDir . $type;
if ($key === null) {
$this->clearDirectory($typeDir);
return;
}
$keyDir = $typeDir . '/' . $key;
if ($subKey === null) {
$this->clearDirectory($keyDir);
return;
}
$file = $this->getCacheFile($key, $subKey, $type);
if (file_exists($file)) {
unlink($file);
if (function_exists('opcache_invalidate')) {
opcache_invalidate($file, true);
}
}
}
}
protected function clearDirectory(string $dir): void
{
if (!is_dir($dir)) return;
$files = glob($dir . '/*');
foreach ($files as $file) {
if (is_dir($file)) {
$this->clearDirectory($file);
@rmdir($file);
} else {
@unlink($file);
if (function_exists('opcache_invalidate')) {
opcache_invalidate($file, true);
}
}
}
}
public function getStats(): array
{
return [
'format' => $this->cacheFormat,
'ttl' => $this->cacheTTL,
'memory_cache_size' => count($this->memoryCache),
'directory' => $this->cacheDir,
'extensions' => [
'msgpack' => extension_loaded('msgpack'),
'igbinary' => extension_loaded('igbinary'),
'opcache' => function_exists('opcache_invalidate')
]
];
}
}
And with this /lib/Cache/Lang/LangCache.php:
Code: Select all
<?php
namespace FormaLms\lib\Cache\Lang;
use FormaLms\lib\Cache\BaseCache;
class LangCache extends BaseCache
{
private const CACHE_DIR = 'languages';
private const CACHE_TTL = 86400;
// Compatibile con PHP 7.4: rimosso `: static`
public static function getInstance(?string $format = self::FORMAT_JSON, ?int $ttl = self::CACHE_TTL, ?string $namespace = null)
{
return parent::getInstance($format, $ttl, $namespace);
}
protected static function getSubDir(): string
{
return self::CACHE_DIR;
}
/**
* Override ensureStructure to add specific directories for language cache
*/
protected function ensureStructure(): void
{
$dirs = [
$this->cacheDir . '/lists',
];
foreach ($dirs as $dir) {
if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir));
}
}
}
/**
* Override getCacheFile for language-specific path structure
*/
protected function getCacheFile($lang_code, $key, $type): string
{
switch ($type) {
case 'translation':
return $this->cacheDir . '/' . $lang_code . '/translations/' . $key . $this->getCacheExtension();
case 'language':
return $this->cacheDir . '/' . $lang_code . '/info' . $this->getCacheExtension();
case 'language_list':
return $this->cacheDir . '/lists/available' . $this->getCacheExtension();
case 'module_list':
return $this->cacheDir . '/lists/modules' . $this->getCacheExtension();
default:
return $this->cacheDir . '/' . $type . '/' . $lang_code . '/' . $key . $this->getCacheExtension();
}
}
}