ArrayApcuDriver.php

PHP

Path: src/Mini/ApcuDrivers/ArrayApcuDriver.php

<?php
namespace mini\Mini\ApcuDrivers;

/**
 * ArrayApcuDriver - Simple in-memory APCu polyfill using static array
 *
 * Provides APCu-compatible caching when neither APCu extension nor Swoole is available.
 * Data is stored in a static array and persists only for the lifetime of the PHP process.
 *
 * Limitations:
 *   - Not shared between PHP-FPM workers or processes
 *   - Cleared on each request in PHP-FPM (but persists in CLI/long-running processes)
 *   - No memory limits (can grow unbounded)
 *   - TTL cleanup only happens on access (lazy expiration)
 *
 * Best used as a fallback or for testing when real APCu is unavailable.
 */
class ArrayApcuDriver implements ApcuDriverInterface
{
    use ApcuDriverTrait;

    /**
     * In-memory storage: key => payload
     * Static so it persists across multiple driver instances in the same process.
     *
     * @var array<string, string>
     */
    private static array $data = [];

    /* --------------------------------------------------------------------
     * LOW-LEVEL BACKEND PRIMITIVES FOR ApcuDriverTrait
     * ------------------------------------------------------------------ */

    /**
     * Fetch raw payload from static array.
     */
    protected function _fetch(string $key, bool &$found = null): ?string
    {
        $found = array_key_exists($key, self::$data);
        return $found ? self::$data[$key] : null;
    }

    /**
     * Atomic "add if not exists" (SETNX) - single-threaded, so no actual locking needed.
     */
    protected function _add(string $key, string $payload, int $ttl): bool
    {
        if (array_key_exists($key, self::$data)) {
            return false; // Already exists
        }

        self::$data[$key] = $payload;
        return true;
    }

    /**
     * Unconditional overwrite (SET).
     */
    protected function _store(string $key, string $payload, int $ttl): bool
    {
        self::$data[$key] = $payload;
        return true;
    }

    /**
     * Delete a key.
     */
    protected function _delete(string $key): bool
    {
        if (array_key_exists($key, self::$data)) {
            unset(self::$data[$key]);
            return true;
        }
        return false;
    }

    /* --------------------------------------------------------------------
     * REQUIRED ApcuDriverInterface METHODS NOT PROVIDED BY THE TRAIT
     * ------------------------------------------------------------------ */

    /**
     * apcu_cache_info(): return basic stats about the array cache.
     */
    public function info(bool $limited = false): array|false
    {
        $numEntries = count(self::$data);
        $memoryUsage = 0;

        // Estimate memory usage (rough approximation)
        foreach (self::$data as $key => $payload) {
            $memoryUsage += strlen($key) + strlen($payload);
        }

        return [
            'num_entries'  => $numEntries,
            'memory_usage' => $memoryUsage,
            'limited'      => $limited,
            'driver'       => 'array',
        ];
    }

    /**
     * apcu_sma_info(): no real allocator, return stub.
     */
    public function sma_info(bool $limited = false): array|false
    {
        return [
            'available_memory' => null,
            'used_memory'      => $this->info()['memory_usage'] ?? 0,
            'num_seg'          => 1,
            'seg_size'         => null,
            'limited'          => $limited,
            'driver'           => 'array',
        ];
    }

    /**
     * apcu_clear_cache(): wipe the static array.
     */
    public function clear_cache(): bool
    {
        self::$data = [];
        return true;
    }

    /**
     * apcu_enabled(): this driver is always enabled (no dependencies).
     */
    public function enabled(): bool
    {
        return true;
    }
}