Alpha ZealPHP is early-stage and under active development. APIs may change between minor versions until v1.0. Feedback and bug reports welcome on GitHub.
API Index — Namespaces, Packages, Reports, Indices

Cache
in package

Cache — Tiered key-value cache (memory + file)

General-purpose cache with a dead-simple API. Two tiers: Tier 1: In-memory via Store (OpenSwoole\Table) — fast, cross-worker, volatile Tier 2: File-based (.cache/ directory) — persistent, survives restarts

Every set() writes through to both tiers. get() checks memory first, falls back to file. TTL-based expiry with lazy cleanup + periodic GC timer.

Usage:

// Before $app->run():
Cache::init();

// Anywhere (any worker):
Cache::set('user:42', $profile, ttl: 300);
$profile = Cache::get('user:42');
Cache::has('user:42');
Cache::del('user:42');

LIMITATIONS — when to use Redis/Valkey instead:

  • Multi-server: Cache is per-server. Redis shares state across machines.
  • Large datasets: Memory tier caps at maxRows (default 4096), 8KB per value.
  • Pub/Sub: No built-in publish/subscribe between workers or servers.
  • Data structures: No sorted sets, streams, Lua scripting. Flat KV only.
  • Persistence: File tier is best-effort. Redis AOF/RDB is crash-safe.
  • Eviction: No LRU/LFU. Full memory tier spills to file-only.
  • Transactions: No MULTI/EXEC. Store has per-row spinlocks only.

Table of Contents

Constants

MAX_MEM_SIZE  : mixed = 8192
STAMPEDE_LOCK_SLOTS  : mixed = 256
Fixed pool size for getOrCompute() stampede locks — bounds the backend's counter map instead of growing one lock per distinct key forever.
TABLE  : mixed = '__cache'

Properties

$dir  : string
$fileRotations  : Counter|null
C-2: count of file-tier evictions due to maxFiles cap (oldest-first).
$hitsFile  : Counter|null
$hitsMem  : Counter|null
$initialized  : bool
$maxFiles  : int
C-2: cap on file-tier entries — oldest-first eviction beyond this. 0 = unlimited.
$misses  : Counter|null
$spillsFile  : Counter|null
$spillsFull  : Counter|null
$stampedeBlocked  : Counter|null
C-1: stampede gate — count of getOrCompute losers that waited then served from cache.
$tagInvalidations  : Counter|null
C-3: count of tag invalidations performed.

Methods

clear()  : bool
Alias for flush() — PSR-16 naming convention. Returns true.
count()  : int
Number of entries in memory tier (may include expired).
del()  : bool
Delete from both tiers.
delete()  : bool
Alias for del() — PSR-16 naming convention.
flush()  : void
Clear all cache entries from both tiers.
get()  : mixed
Retrieve a value. Memory tier checked first, file tier as fallback.
getOrCompute()  : T
Read-through helper: if $key exists, return it; otherwise call $compute(), store the result, and return it.
has()  : bool
Check existence without deserializing. Respects TTL.
init()  : void
Initialize the cache. Must be called before $app->run().
invalidateTag()  : int
C-3 — invalidate every key that was set() with the given tag.
mget()  : array<string, mixed>
Batch get — fetch multiple keys in one call.
mset()  : int
Batch set — store multiple key-value pairs in one call.
set()  : bool
Store a value. Writes to both memory and file tiers.
stats()  : array{memory_entries: int, hits_memory: int, hits_file: int, misses: int, spills_oversize: int, spills_full: int, stampede_blocked: int, file_rotations: int, tag_invalidations: int, hit_rate: float}
Cache performance stats. All counters are cross-worker (atomic).
filePath()  : string
readFile()  : mixed
registerGc()  : void
writeFile()  : bool

Constants

MAX_MEM_SIZE

private mixed MAX_MEM_SIZE = 8192

STAMPEDE_LOCK_SLOTS

Fixed pool size for getOrCompute() stampede locks — bounds the backend's counter map instead of growing one lock per distinct key forever.

private mixed STAMPEDE_LOCK_SLOTS = 256

TABLE

private mixed TABLE = '__cache'

Properties

$dir

private static string $dir = ''

$fileRotations

C-2: count of file-tier evictions due to maxFiles cap (oldest-first).

private static Counter|null $fileRotations = null

$initialized

private static bool $initialized = false

$maxFiles

C-2: cap on file-tier entries — oldest-first eviction beyond this. 0 = unlimited.

private static int $maxFiles = 0

$stampedeBlocked

C-1: stampede gate — count of getOrCompute losers that waited then served from cache.

private static Counter|null $stampedeBlocked = null

$tagInvalidations

C-3: count of tag invalidations performed.

private static Counter|null $tagInvalidations = null

Methods

clear()

Alias for flush() — PSR-16 naming convention. Returns true.

public static clear() : bool
Return values
bool

count()

Number of entries in memory tier (may include expired).

public static count() : int
Return values
int

del()

Delete from both tiers.

public static del(string $key) : bool
Parameters
$key : string
Return values
bool

delete()

Alias for del() — PSR-16 naming convention.

public static delete(string $key) : bool
Parameters
$key : string
Return values
bool

flush()

Clear all cache entries from both tiers.

public static flush() : void

get()

Retrieve a value. Memory tier checked first, file tier as fallback.

public static get(string $key[, mixed $default = null ]) : mixed
Parameters
$key : string
$default : mixed = null

getOrCompute()

Read-through helper: if $key exists, return it; otherwise call $compute(), store the result, and return it.

public static getOrCompute(string $key, callable(): T $compute[, int $ttl = 0 ]) : T

The fall-through-on-miss-then-compute pattern is the canonical way to cache expensive lookups (DB queries, API calls, derived values). Without this helper users write the same 3 lines:

$v = Cache::get($k);
if ($v === null) { $v = expensiveLookup($k); Cache::set($k, $v, $ttl); }
return $v;

getOrCompute() collapses that to one call:

$v = Cache::getOrCompute($k, fn() => expensiveLookup($k), $ttl);

Storage semantics are identical to set()+get() — the value goes to BOTH the memory tier (Store table, capped at MAX_MEM_SIZE) AND the file tier (large values + overflow). Subsequent calls within $ttl short-circuit to the cached read.

Parameters
$key : string
$compute : callable(): T
$ttl : int = 0
Tags
template
Return values
T

has()

Check existence without deserializing. Respects TTL.

public static has(string $key) : bool
Parameters
$key : string
Return values
bool

init()

Initialize the cache. Must be called before $app->run().

public static init([int $maxRows = 4096 ][, string|null $cacheDir = null ][, int $gcIntervalMs = 60000 ][, int|null $ttlSeconds = null ][, int $maxFiles = 0 ]) : void
Parameters
$maxRows : int = 4096

Max entries in memory tier (default 4096). HARD CAP on the Table backend (OpenSwoole\Table allocates a fixed-size shared-memory segment). NOT ENFORCED on the Redis backend — Redis is a global key-value store with no per-table size cap. Pair with $ttlSeconds OR configure Redis-server maxmemory + maxmemory-policy for bounded growth there. See the warning emitted at init() when this combo is misused.

$cacheDir : string|null = null

File tier directory (default: .cache/ in project root)

$gcIntervalMs : int = 60000

GC sweep interval in ms (default 60000)

$ttlSeconds : int|null = null

Per-key TTL hint (default null). On the Redis backend, setting this flips the underlying Store table to mode='ttl' so keys auto-expire server-side (Cache::set's per-key $ttl still wins as a per-call override; this is the DEFAULT TTL for keys whose set() doesn't pass one).

$maxFiles : int = 0

invalidateTag()

C-3 — invalidate every key that was set() with the given tag.

public static invalidateTag(string $tag) : int

Drops the entries from both memory + file tiers in a single sweep. Returns the count of keys invalidated.

Requires Redis or Tiered backend (the tag→keys SET lives in Redis). On Table backend this is a no-op + warns to error_log; for single-node cache groups, prefer keying with a versioned prefix (e.g. Cache::set("user:42:v$ver", ...) + bump $ver to invalidate).

Parameters
$tag : string
Return values
int

mget()

Batch get — fetch multiple keys in one call.

public static mget(array<int, string> $keys) : array<string, mixed>

Returns an associative array of key => value for keys that exist. Missing keys are omitted from the result.

Parameters
$keys : array<int, string>
Return values
array<string, mixed>

mset()

Batch set — store multiple key-value pairs in one call.

public static mset(array<string, mixed> $items[, int $ttl = 0 ]) : int

Returns the number of keys successfully stored.

Parameters
$items : array<string, mixed>

key => value

$ttl : int = 0
Return values
int

set()

Store a value. Writes to both memory and file tiers.

public static set(string $key, mixed $value[, int $ttl = 0 ][, array<int, string> $tags = [] ]) : bool

Values larger than 8KB are stored in file tier only.

Parameters
$key : string
$value : mixed
$ttl : int = 0
$tags : array<int, string> = []

C-3: optional tags for bulk invalidation via Cache::invalidateTag($tag). Tag index requires Redis-capable backend (Redis / Tiered); ignored otherwise.

Return values
bool

stats()

Cache performance stats. All counters are cross-worker (atomic).

public static stats() : array{memory_entries: int, hits_memory: int, hits_file: int, misses: int, spills_oversize: int, spills_full: int, stampede_blocked: int, file_rotations: int, tag_invalidations: int, hit_rate: float}

Returns: [ 'memory_entries' => int, // current rows in memory tier 'hits_memory' => int, // get() served from memory 'hits_file' => int, // get() served from file (memory miss) 'misses' => int, // get() found nothing 'spills_oversize' => int, // set() skipped memory (value > 8KB) 'spills_full' => int, // set() skipped memory (table full) 'stampede_blocked' => int, // C-1 — getOrCompute losers that // waited then served cached 'file_rotations' => int, // C-2 — file evictions by maxFiles cap 'tag_invalidations'=> int, // C-3 — Cache::invalidateTag calls 'hit_rate' => float, // hits / (hits + misses), 0.0–1.0 ]

Return values
array{memory_entries: int, hits_memory: int, hits_file: int, misses: int, spills_oversize: int, spills_full: int, stampede_blocked: int, file_rotations: int, tag_invalidations: int, hit_rate: float}

filePath()

private static filePath(string $hash) : string
Parameters
$hash : string
Return values
string

readFile()

private static readFile(string $hash) : mixed
Parameters
$hash : string

registerGc()

private static registerGc(int $intervalMs) : void
Parameters
$intervalMs : int

writeFile()

private static writeFile(string $hash, string $serialized, int $expires) : bool
Parameters
$hash : string
$serialized : string
$expires : int
Return values
bool
On this page