mirror of
https://github.com/manualdousuario/marreta.git
synced 2025-09-02 02:30:20 +00:00
novo sistema de cache por disco ou s3
This commit is contained in:
parent
8124288ccc
commit
ca6b2413e2
4 changed files with 264 additions and 36 deletions
|
@ -1,32 +1,45 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Inc\Cache\CacheStorageInterface;
|
||||||
|
use Inc\Cache\DiskStorage;
|
||||||
|
use Inc\Cache\S3Storage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classe responsável pelo gerenciamento de cache do sistema
|
* Classe responsável pelo gerenciamento de cache do sistema
|
||||||
*
|
*
|
||||||
* Esta classe implementa funcionalidades para armazenar e recuperar
|
* Esta classe implementa funcionalidades para armazenar e recuperar
|
||||||
* conteúdo em cache, utilizando o sistema de arquivos como storage.
|
* conteúdo em cache, suportando múltiplos backends de armazenamento (disco ou S3).
|
||||||
* O cache é organizado por URLs convertidas em IDs únicos usando SHA-256.
|
* O cache é organizado por URLs convertidas em IDs únicos usando SHA-256.
|
||||||
* O conteúdo é comprimido usando gzip para economizar espaço em disco.
|
* O conteúdo é comprimido usando gzip para economizar espaço.
|
||||||
*
|
|
||||||
* Quando o modo DEBUG está ativo, todas as operações de cache são desativadas.
|
|
||||||
*/
|
*/
|
||||||
class Cache
|
class Cache
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string Diretório onde os arquivos de cache serão armazenados
|
* @var CacheStorageInterface Implementação de storage para o cache
|
||||||
*/
|
*/
|
||||||
private $cacheDir;
|
private $storage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construtor da classe
|
* Construtor da classe
|
||||||
*
|
*
|
||||||
* Inicializa o diretório de cache e cria-o se não existir
|
* Inicializa o storage apropriado baseado na configuração
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->cacheDir = CACHE_DIR;
|
// Se S3 está configurado e ativo, usa S3Storage
|
||||||
if (!file_exists($this->cacheDir)) {
|
if (defined('S3_CACHE_ENABLED') && S3_CACHE_ENABLED === true) {
|
||||||
mkdir($this->cacheDir, 0777, true);
|
$this->storage = new S3Storage([
|
||||||
|
'key' => S3_ACCESS_KEY,
|
||||||
|
'secret' => S3_SECRET_KEY,
|
||||||
|
'bucket' => S3_BUCKET,
|
||||||
|
'region' => S3_REGION ?? 'us-east-1',
|
||||||
|
'prefix' => S3_PREFIX ?? 'cache/',
|
||||||
|
'acl' => S3_ACL ?? 'private',
|
||||||
|
'endpoint' => defined('S3_ENDPOINT') ? S3_ENDPOINT : null
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// Caso contrário, usa o storage em disco
|
||||||
|
$this->storage = new DiskStorage(CACHE_DIR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +70,7 @@ class Cache
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $this->generateId($url);
|
return $this->storage->exists($this->generateId($url));
|
||||||
$cachePath = $this->cacheDir . '/' . $id . '.gz';
|
|
||||||
return file_exists($cachePath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,19 +86,7 @@ class Cache
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->exists($url)) {
|
return $this->storage->get($this->generateId($url));
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$id = $this->generateId($url);
|
|
||||||
$cachePath = $this->cacheDir . '/' . $id . '.gz';
|
|
||||||
|
|
||||||
// Lê e descomprime o conteúdo
|
|
||||||
$compressedContent = file_get_contents($cachePath);
|
|
||||||
if ($compressedContent === false) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gzdecode($compressedContent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,15 +103,6 @@ class Cache
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $this->generateId($url);
|
return $this->storage->set($this->generateId($url), $content);
|
||||||
$cachePath = $this->cacheDir . '/' . $id . '.gz';
|
|
||||||
|
|
||||||
// Comprime o conteúdo usando gzip
|
|
||||||
$compressedContent = gzencode($content, 3);
|
|
||||||
if ($compressedContent === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return file_put_contents($cachePath, $compressedContent) !== false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
31
app/inc/Cache/CacheStorageInterface.php
Normal file
31
app/inc/Cache/CacheStorageInterface.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Inc\Cache;
|
||||||
|
|
||||||
|
interface CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Verifica se existe cache para um determinado ID
|
||||||
|
*
|
||||||
|
* @param string $id ID do cache
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function exists(string $id): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recupera o conteúdo em cache
|
||||||
|
*
|
||||||
|
* @param string $id ID do cache
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function get(string $id): ?string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Armazena conteúdo em cache
|
||||||
|
*
|
||||||
|
* @param string $id ID do cache
|
||||||
|
* @param string $content Conteúdo a ser armazenado
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function set(string $id, string $content): bool;
|
||||||
|
}
|
67
app/inc/Cache/DiskStorage.php
Normal file
67
app/inc/Cache/DiskStorage.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Inc\Cache;
|
||||||
|
|
||||||
|
class DiskStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string Diretório onde os arquivos de cache serão armazenados
|
||||||
|
*/
|
||||||
|
private $cacheDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor da classe
|
||||||
|
*
|
||||||
|
* @param string $cacheDir Diretório base para armazenamento do cache
|
||||||
|
*/
|
||||||
|
public function __construct(string $cacheDir)
|
||||||
|
{
|
||||||
|
$this->cacheDir = $cacheDir;
|
||||||
|
if (!file_exists($this->cacheDir)) {
|
||||||
|
mkdir($this->cacheDir, 0777, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function exists(string $id): bool
|
||||||
|
{
|
||||||
|
$cachePath = $this->cacheDir . '/' . $id . '.gz';
|
||||||
|
return file_exists($cachePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get(string $id): ?string
|
||||||
|
{
|
||||||
|
if (!$this->exists($id)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cachePath = $this->cacheDir . '/' . $id . '.gz';
|
||||||
|
$compressedContent = file_get_contents($cachePath);
|
||||||
|
|
||||||
|
if ($compressedContent === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gzdecode($compressedContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function set(string $id, string $content): bool
|
||||||
|
{
|
||||||
|
$cachePath = $this->cacheDir . '/' . $id . '.gz';
|
||||||
|
$compressedContent = gzencode($content, 3);
|
||||||
|
|
||||||
|
if ($compressedContent === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_put_contents($cachePath, $compressedContent) !== false;
|
||||||
|
}
|
||||||
|
}
|
140
app/inc/Cache/S3Storage.php
Normal file
140
app/inc/Cache/S3Storage.php
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Inc\Cache;
|
||||||
|
|
||||||
|
use Aws\S3\S3Client;
|
||||||
|
use Aws\Exception\AwsException;
|
||||||
|
|
||||||
|
class S3Storage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var S3Client Cliente AWS S3
|
||||||
|
*/
|
||||||
|
private $s3Client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string Nome do bucket S3
|
||||||
|
*/
|
||||||
|
private $bucket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string Prefixo para os objetos no bucket (opcional)
|
||||||
|
*/
|
||||||
|
private $prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string ACL para os objetos no S3
|
||||||
|
*/
|
||||||
|
private $acl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor da classe
|
||||||
|
*
|
||||||
|
* @param array $config Configuração do AWS S3
|
||||||
|
*/
|
||||||
|
public function __construct(array $config)
|
||||||
|
{
|
||||||
|
$clientConfig = [
|
||||||
|
'version' => 'latest',
|
||||||
|
'region' => $config['region'] ?? 'us-east-1',
|
||||||
|
'credentials' => [
|
||||||
|
'key' => $config['key'],
|
||||||
|
'secret' => $config['secret'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Adiciona endpoint personalizado se fornecido
|
||||||
|
if (!empty($config['endpoint'])) {
|
||||||
|
$clientConfig['endpoint'] = $config['endpoint'];
|
||||||
|
// Use path-style endpoints quando um endpoint personalizado é fornecido
|
||||||
|
$clientConfig['use_path_style_endpoint'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->s3Client = new S3Client($clientConfig);
|
||||||
|
|
||||||
|
$this->bucket = $config['bucket'];
|
||||||
|
$this->prefix = $config['prefix'] ?? 'cache/';
|
||||||
|
$this->acl = $config['acl'] ?? 'private';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gera a chave completa do objeto no S3
|
||||||
|
*
|
||||||
|
* @param string $id ID do cache
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getObjectKey(string $id): string
|
||||||
|
{
|
||||||
|
return $this->prefix . $id . '.gz';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function exists(string $id): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->s3Client->doesObjectExist(
|
||||||
|
$this->bucket,
|
||||||
|
$this->getObjectKey($id)
|
||||||
|
);
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
// Log error if needed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get(string $id): ?string
|
||||||
|
{
|
||||||
|
if (!$this->exists($id)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $this->s3Client->getObject([
|
||||||
|
'Bucket' => $this->bucket,
|
||||||
|
'Key' => $this->getObjectKey($id)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$compressedContent = $result['Body']->getContents();
|
||||||
|
|
||||||
|
if ($compressedContent === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $compressedContent;
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function set(string $id, string $content): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$compressedContent = gzencode($content, 3);
|
||||||
|
if ($compressedContent === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->s3Client->putObject([
|
||||||
|
'Bucket' => $this->bucket,
|
||||||
|
'Key' => $this->getObjectKey($id),
|
||||||
|
'Body' => $compressedContent,
|
||||||
|
'ACL' => $this->acl,
|
||||||
|
'ContentEncoding' => 'gzip',
|
||||||
|
'CacheControl' => 'max-age=31536000' // 1 year
|
||||||
|
]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
// Log error if needed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue