Files
2026-06-06 18:43:00 +08:00

128 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\Helpers;
use Hyperf\Coroutine\Coroutine;
use Hypervel\Support\Facades\Cache;
use Hyperf\Database\Model\Builder as ModelBuilder;
use Hyperf\Database\Query\Builder as QueryBuilder;
use Hypervel\Database\Eloquent\Model;
class CacheHelper
{
/**
* Get data from cache.
*
* @param mixed $query Can be SQL string or Builder object
* @param mixed $params Parameters to hash (array, object, or model)
* @return mixed|null
*/
public static function get_cache($query, $params = [])
{
try {
$key = self::generateKey($query, $params);
return Cache::get($key);
} catch (\Throwable $e) {
return null;
}
}
/**
* Set data in cache using a background coroutine (fire-and-forget).
*
* @param mixed $query Can be SQL string or Builder object
* @param mixed $params Parameters to hash
* @param mixed $data Data to cache
* @param int $ttl Time to live in seconds (default: 1 day)
*/
public static function set_cache($query, $data, $params = [], int $ttl = 86400)
{
$key = self::generateKey($query, $params);
// Fire-and-forget coroutine: we don't wait for completion
Coroutine::create(function () use ($key, $data, $ttl) {
try {
Cache::put($key, $data, $ttl);
} catch (\Throwable $e) {
// Silently fail as cache operations are non-critical
}
});
}
/**
* Erase a specific cache entry.
*
* @param mixed $query Can be SQL string or Builder object
* @param mixed $params Parameters to hash
*/
public static function erase_cache($query, $params = [])
{
try {
$key = self::generateKey($query, $params);
Cache::forget($key);
} catch (\Throwable $e) {
// Silently fail
}
}
/**
* Generate the cache key using the specific format:
* querycache:[queryhash]++++[parameterhash]
*/
public static function generateKey($query, $params): string
{
$sql = '';
$parameterData = $params;
// Automatically handle Builder objects
if ($query instanceof ModelBuilder || $query instanceof QueryBuilder) {
$sql = $query->toSql();
// If explicit params were not provided or empty, use the builder's bindings
if (empty($params)) {
$parameterData = $query->getBindings();
}
} else {
$sql = (string) $query;
}
$queryHash = md5($sql);
$paramHash = md5(self::serializeParams($parameterData));
return "querycache:{$queryHash}++++{$paramHash}";
}
/**
* Deep serialization of parameters for hashing.
* Supports arrays, objects, and models.
*/
private static function serializeParams($params): string
{
if (is_array($params)) {
$processed = [];
foreach ($params as $key => $value) {
$processed[$key] = self::serializeParams($value);
}
// Sort keys to ensure consistent hashing for the same associative array
ksort($processed);
return json_encode($processed);
}
if (is_object($params)) {
// Handle models specifically
if ($params instanceof Model) {
return get_class($params) . ':' . ($params->hashkey ?? $params->id ?? 'new');
}
if (method_exists($params, 'toArray')) {
return json_encode($params->toArray());
}
return get_class($params) . ':' . spl_object_hash($params);
}
return (string) $params;
}
}