Files
tinker_tickets/helpers/CacheHelper.php
Jared Vititoe d2a8c73e2c Add caching layer and database helper
- Add CacheHelper for file-based caching with TTL support
- Add Database helper for centralized connection management
- Update WorkflowModel to cache status transitions (10 min TTL)
- Update UserPreferencesModel to cache user prefs (5 min TTL)
- Update manage_workflows.php to clear cache on changes
- Update get_users.php to use Database helper (example migration)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 10:53:26 -05:00

192 lines
5.7 KiB
PHP

<?php
/**
* Simple File-Based Cache Helper
*
* Provides caching for frequently accessed data that doesn't change often,
* such as workflow rules, user preferences, and configuration data.
*/
class CacheHelper {
private static $cacheDir = null;
private static $memoryCache = [];
/**
* Get the cache directory path
*
* @return string Cache directory path
*/
private static function getCacheDir() {
if (self::$cacheDir === null) {
self::$cacheDir = sys_get_temp_dir() . '/tinker_tickets_cache';
if (!is_dir(self::$cacheDir)) {
mkdir(self::$cacheDir, 0755, true);
}
}
return self::$cacheDir;
}
/**
* Generate a cache key from components
*
* @param string $prefix Cache prefix (e.g., 'workflow', 'user_prefs')
* @param mixed $identifier Unique identifier
* @return string Cache key
*/
private static function makeKey($prefix, $identifier = null) {
$key = $prefix;
if ($identifier !== null) {
$key .= '_' . md5(serialize($identifier));
}
return preg_replace('/[^a-zA-Z0-9_]/', '_', $key);
}
/**
* Get cached data
*
* @param string $prefix Cache prefix
* @param mixed $identifier Unique identifier
* @param int $ttl Time-to-live in seconds (default 300 = 5 minutes)
* @return mixed|null Cached data or null if not found/expired
*/
public static function get($prefix, $identifier = null, $ttl = 300) {
$key = self::makeKey($prefix, $identifier);
// Check memory cache first (fastest)
if (isset(self::$memoryCache[$key])) {
$cached = self::$memoryCache[$key];
if (time() - $cached['time'] < $ttl) {
return $cached['data'];
}
unset(self::$memoryCache[$key]);
}
// Check file cache
$filePath = self::getCacheDir() . '/' . $key . '.json';
if (file_exists($filePath)) {
$content = @file_get_contents($filePath);
if ($content !== false) {
$cached = json_decode($content, true);
if ($cached && isset($cached['time']) && isset($cached['data'])) {
if (time() - $cached['time'] < $ttl) {
// Store in memory cache for faster subsequent access
self::$memoryCache[$key] = $cached;
return $cached['data'];
}
}
}
// Expired - delete file
@unlink($filePath);
}
return null;
}
/**
* Store data in cache
*
* @param string $prefix Cache prefix
* @param mixed $identifier Unique identifier
* @param mixed $data Data to cache
* @return bool Success
*/
public static function set($prefix, $identifier, $data) {
$key = self::makeKey($prefix, $identifier);
$cached = [
'time' => time(),
'data' => $data
];
// Store in memory cache
self::$memoryCache[$key] = $cached;
// Store in file cache
$filePath = self::getCacheDir() . '/' . $key . '.json';
return @file_put_contents($filePath, json_encode($cached), LOCK_EX) !== false;
}
/**
* Delete cached data
*
* @param string $prefix Cache prefix
* @param mixed $identifier Unique identifier (null to delete all with prefix)
* @return bool Success
*/
public static function delete($prefix, $identifier = null) {
if ($identifier !== null) {
$key = self::makeKey($prefix, $identifier);
unset(self::$memoryCache[$key]);
$filePath = self::getCacheDir() . '/' . $key . '.json';
return !file_exists($filePath) || @unlink($filePath);
}
// Delete all files with this prefix
$pattern = self::getCacheDir() . '/' . preg_replace('/[^a-zA-Z0-9_]/', '_', $prefix) . '*.json';
$files = glob($pattern);
foreach ($files as $file) {
@unlink($file);
}
// Clear memory cache entries with this prefix
foreach (array_keys(self::$memoryCache) as $key) {
if (strpos($key, $prefix) === 0) {
unset(self::$memoryCache[$key]);
}
}
return true;
}
/**
* Clear all cache
*
* @return bool Success
*/
public static function clearAll() {
self::$memoryCache = [];
$files = glob(self::getCacheDir() . '/*.json');
foreach ($files as $file) {
@unlink($file);
}
return true;
}
/**
* Get data from cache or fetch it using a callback
*
* @param string $prefix Cache prefix
* @param mixed $identifier Unique identifier
* @param callable $callback Function to call if cache miss
* @param int $ttl Time-to-live in seconds
* @return mixed Cached or freshly fetched data
*/
public static function remember($prefix, $identifier, $callback, $ttl = 300) {
$data = self::get($prefix, $identifier, $ttl);
if ($data === null) {
$data = $callback();
if ($data !== null) {
self::set($prefix, $identifier, $data);
}
}
return $data;
}
/**
* Clean up expired cache files (call periodically)
*
* @param int $maxAge Maximum age in seconds (default 1 hour)
*/
public static function cleanup($maxAge = 3600) {
$files = glob(self::getCacheDir() . '/*.json');
$now = time();
foreach ($files as $file) {
if ($now - filemtime($file) > $maxAge) {
@unlink($file);
}
}
}
}