/ APCu Cache

APCu Cache

APCu (Alternative PHP Cache - User) provides fast in-memory caching for PHP applications.


APCu Cache Methods

Method Description
get($key)Get cached value
put($key, $value, $ttl)Store value with TTL
forget($key)Remove cached value
remember($key, $callback, $ttl)Get or compute and cache
has($key)Check if key exists
flush()Clear all cache
increment($key)Increment numeric value
decrement($key)Decrement numeric value

Basic Usage

use Miko\Cache\ApcuCache;

$cache = new ApcuCache('myapp_');  // Prefix for keys

// Store value (TTL in seconds)
$cache->put('user:1', $userData, 3600);  // 1 hour

// Get value
$user = $cache->get('user:1');

// Get with default
$user = $cache->get('user:1', null);

// Check existence
if ($cache->has('user:1')) {
    // Key exists
}

// Remove
$cache->forget('user:1');

// Clear all
$cache->flush();

Remember Pattern

Get from cache or compute and store:

// If cached, return cached value
// If not, execute callback, cache result, and return
$user = $cache->remember('user:1', function() {
    return User::find(1);
}, 3600);

// With complex computation
$stats = $cache->remember('dashboard:stats', function() {
    return [
        'users' => User::count(),
        'orders' => Order::count(),
        'revenue' => Order::sum('TotalAmount')
    ];
}, 300);  // 5 minutes

Increment / Decrement

// Initialize counter
$cache->put('visitors', 0, 86400);

// Increment
$cache->increment('visitors');
$cache->increment('visitors', 5);  // Increment by 5

// Decrement
$cache->decrement('stock:123');
$cache->decrement('stock:123', 2);  // Decrement by 2

// Get current value
$count = $cache->get('visitors');

Tags Support

Group related cache entries:

// Store with tags
$cache->putWithTags('user:1', $userData, 3600, ['users', 'user_1']);
$cache->putWithTags('user:1:orders', $orders, 3600, ['users', 'user_1', 'orders']);

// Invalidate by tag
$cache->forgetByTag('user_1');  // Removes all user_1 related cache

// Invalidate all users
$cache->forgetByTag('users');

Cache Statistics

$stats = $cache->getStats();

// Returns:
// [
//     'hits' => 15000,
//     'misses' => 500,
//     'hit_rate' => 96.77,
//     'memory_used' => 52428800,
//     'memory_available' => 134217728,
//     'entries' => 1250
// ]

echo "Hit rate: " . $stats['hit_rate'] . "%";
echo "Memory: " . round($stats['memory_used'] / 1024 / 1024, 2) . " MB";

Practical Examples

User Session Cache

class SessionCache
{
    private ApcuCache $cache;
    
    public function __construct()
    {
        $this->cache = new ApcuCache('session_');
    }
    
    public function getUser(int $userId): ?User
    {
        return $this->cache->remember("user:$userId", function() use ($userId) {
            return User::with('roles', 'permissions')->find($userId);
        }, 1800);  // 30 minutes
    }
    
    public function invalidateUser(int $userId): void
    {
        $this->cache->forget("user:$userId");
    }
}

API Rate Limiting

class RateLimiter
{
    private ApcuCache $cache;
    
    public function __construct()
    {
        $this->cache = new ApcuCache('rate_');
    }
    
    public function attempt(string $key, int $maxAttempts, int $decaySeconds): bool
    {
        $attempts = $this->cache->get($key, 0);
        
        if ($attempts >= $maxAttempts) {
            return false;
        }
        
        if ($attempts === 0) {
            $this->cache->put($key, 1, $decaySeconds);
        } else {
            $this->cache->increment($key);
        }
        
        return true;
    }
    
    public function remaining(string $key, int $maxAttempts): int
    {
        return max(0, $maxAttempts - $this->cache->get($key, 0));
    }
}

// Usage
$limiter = new RateLimiter();
$ip = $_SERVER['REMOTE_ADDR'];

if (!$limiter->attempt("api:$ip", 100, 60)) {
    http_response_code(429);
    die('Rate limit exceeded');
}

Configuration Cache

class ConfigCache
{
    private ApcuCache $cache;
    
    public function __construct()
    {
        $this->cache = new ApcuCache('config_');
    }
    
    public function get(string $key, $default = null)
    {
        return $this->cache->remember("config:$key", function() use ($key, $default) {
            $setting = Setting::where('Key', $key)->first();
            return $setting ? $setting->Value : $default;
        }, 3600);
    }
    
    public function set(string $key, $value): void
    {
        Setting::updateOrCreate(['Key' => $key], ['Value' => $value]);
        $this->cache->forget("config:$key");
    }
    
    public function clearAll(): void
    {
        $this->cache->flush();
    }
}

Requirements

APCu must be installed and enabled:

; php.ini
extension=apcu
apc.enabled=1
apc.shm_size=128M
apc.ttl=7200

Check if available:

if (!extension_loaded('apcu') || !apcu_enabled()) {
    throw new Exception('APCu is not available');
}

Best Practices

Practice Description
Use prefixesAvoid key collisions between apps
Set appropriate TTLBalance freshness vs performance
Use tagsEasier cache invalidation
Monitor memoryDon't exceed available memory
Handle missesAlways have fallback for cache misses