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

RedisBackend
in package
implements StoreBackend

FinalYes

Redis/Valkey-backed StoreBackend.

Row layout: HASH at {prefix}:{table}:{key} (columns → hash fields). Membership (tracked mode): SET at {prefix}:{table}:__keys__.

Two modes chosen at make():

  • 'tracked' (default) — SET-backed; count() is O(1) via SCARD, iterate() via SSCAN. No TTL (an expired key cannot fire SREM on the membership set, which would drift).
  • 'ttl' — per-key expiry via EXPIRE; count() / iterate() use SCAN MATCH (O(N)) because tracked membership isn't viable. Pick one per table; the trade-off is documented in CLAUDE.md.

Every op goes through RedisConnectionPool::with() so concurrent coroutines never share a socket.

Table of Contents

Interfaces

StoreBackend
Behavioural contract every Store backend implements.

Properties

$codec  : TypeCodec
Encodes/decodes PHP scalars to/from Redis hash field strings.
$pool  : RedisConnectionPool
$prefix  : string
$schemas  : array<string, array<string, array{0: int, 1?: int}>>
Per-table column schemas: table name → column name → [TYPE, size?].
$tableOpts  : array<string, array{mode: string, ttl: int}>
Per-table options: table name → ['mode' => 'tracked'|'ttl', 'ttl' => int].

Methods

__construct()  : mixed
clear()  : void
Delete every row in the named table.
count()  : int
Return the number of rows in the named table.
decr()  : int|float
Atomically decrement a numeric column — delegates to incr() with -$by.
del()  : bool
Delete a row from the named table.
eval()  : mixed
Run a Lua script server-side (atomic). KEYS are raw / absolute (un-prefixed, like sadd/publish). Returns whatever the script returns. Used by WSRouter for the race-free per-room server-set.
exists()  : bool
Returns true when the row for $key exists in the named table.
get()  : mixed
Fetch a row or a single field from the named table.
incr()  : int|float
Atomically increment a numeric column in a row.
iterate()  : Generator<string, array<string, scalar>>
Yield every row in the named table as $key => $row.
iteratePaged()  : array{cursor: string, rows: array>}
Cursor-based paginated iteration of the named table.
make()  : void
Register a table schema on the Redis backend.
mget()  : array<string, array<string, scalar>|null>
Fetch multiple rows in a single pipelined round-trip.
mset()  : bool
Write multiple rows in a single pipelined round-trip.
names()  : array<int, string>
Return the names of all tables registered on this backend via make().
ping()  : bool
Health check via PINGStore::ping() delegates here.
pool()  : RedisConnectionPool
Returns the underlying RedisConnectionPool instance.
prefix()  : string
Returns the key namespace prefix (e.g. 'zealstore').
publish()  : int
Pub/sub publish through the pool. Returns the number of subscribers Redis delivered the message to.
publishReliable()  : string
Append a message to a Redis Stream via XADD. Auto-trims to $maxLen entries when set. The payload is stored under the 'payload' field, matching the consumer-side convention in RedisStreams.
sadd()  : int
Add one or more members to a Redis SET at the given absolute $key.
scard()  : int
Return the cardinality (number of members) of a Redis SET at the given absolute $key. O(1). Returns 0 when the key does not exist.
sdel()  : bool
Delete an absolute Redis SET key via UNLINK (non-blocking reclaim).
set()  : bool
Write a row to the named table. Creates or overwrites the Redis HASH at {prefix}:{name}:{key} and, in 'tracked' mode, adds $key to the membership SET. Applies EXPIRE in 'ttl' mode.
srem()  : int
Remove one or more members from a Redis SET at the given absolute $key.
sscanCursor()  : array{0: string, 1: list}
Cursor-based SSCAN on an absolute SET key — one batch per call.
url()  : string
Returns the Redis connection URL this backend was constructed with.
assertMade()  : void
Assert that $name was registered via make(), throwing StoreException otherwise.
countViaScan()  : int
Count rows in 'ttl' mode via SCAN MATCH — O(N).
rowKey()  : string
Build the Redis HASH key for a specific row: {prefix}:{table}:{key}.
setKey()  : string
Build the Redis SET key for the membership index: {prefix}:{table}:__keys__.

Properties

$schemas

Per-table column schemas: table name → column name → [TYPE, size?].

private array<string, array<string, array{0: int, 1?: int}>> $schemas = []

$tableOpts

Per-table options: table name → ['mode' => 'tracked'|'ttl', 'ttl' => int].

private array<string, array{mode: string, ttl: int}> $tableOpts = []

Methods

__construct()

public __construct(RedisConnectionPool $pool[, string $prefix = 'zealstore' ]) : mixed
Parameters
$pool : RedisConnectionPool

Coroutine-safe connection pool.

$prefix : string = 'zealstore'

Key namespace prefix (default 'zealstore').

clear()

Delete every row in the named table.

public clear(string $name) : void

Uses UNLINK (non-blocking key reclaim, Redis 4.0+) in batches of 100 to avoid blocking the Redis event loop on large tables. In 'tracked' mode also deletes the membership SET key.

Parameters
$name : string

count()

Return the number of rows in the named table.

public count(string $name) : int

In 'tracked' mode: O(1) via SCARD on the membership SET. In 'ttl' mode: O(N) via SCAN MATCH (see countViaScan()).

Parameters
$name : string
Return values
int

decr()

Atomically decrement a numeric column — delegates to incr() with -$by.

public decr(string $name, string $key, string $col[, int|float $by = 1 ]) : int|float
Parameters
$name : string
$key : string
$col : string
$by : int|float = 1
Return values
int|float

The new value after decrementing.

del()

Delete a row from the named table.

public del(string $name, string $key) : bool

In 'tracked' mode also removes $key from the membership SET. Returns true when the key existed and was deleted, false otherwise.

Parameters
$name : string
$key : string
Return values
bool

eval()

Run a Lua script server-side (atomic). KEYS are raw / absolute (un-prefixed, like sadd/publish). Returns whatever the script returns. Used by WSRouter for the race-free per-room server-set.

public eval(string $script[, array<int, string> $keys = [] ][, array<int, string> $args = [] ]) : mixed
Parameters
$script : string
$keys : array<int, string> = []
$args : array<int, string> = []

exists()

Returns true when the row for $key exists in the named table.

public exists(string $name, string $key) : bool
Parameters
$name : string
$key : string
Return values
bool

get()

Fetch a row or a single field from the named table.

public get(string $name, string $key[, string|null $field = null ]) : mixed

When $field is null, returns the full decoded row as array<string, mixed> or null when the key does not exist. When $field is provided, returns only that field's decoded value (or null on miss).

Parameters
$name : string
$key : string
$field : string|null = null

incr()

Atomically increment a numeric column in a row.

public incr(string $name, string $key, string $col[, int|float $by = 1 ]) : int|float

Uses HINCRBY for TYPE_INT columns and HINCRBYFLOAT for TYPE_FLOAT. Creates the row if it does not exist. In 'tracked' mode, adds the key to the membership SET on first creation. Applies EXPIRE in 'ttl' mode.

Parameters
$name : string
$key : string
$col : string
$by : int|float = 1
Return values
int|float

The new value after incrementing.

iterate()

Yield every row in the named table as $key => $row.

public iterate(string $name) : Generator<string, array<string, scalar>>

In 'tracked' mode, iterates the membership SET via SSCAN then fetches each row with HGETALL. In 'ttl' mode, uses SCAN MATCH across the keyspace. Acquires a dedicated connection from the pool for the duration of iteration — do not hold the generator open longer than necessary.

Parameters
$name : string
Return values
Generator<string, array<string, scalar>>

iteratePaged()

Cursor-based paginated iteration of the named table.

public iteratePaged(string $name[, string $cursor = '0' ][, int $count = 100 ]) : array{cursor: string, rows: array>}

Returns one Redis SCAN/SSCAN batch at a time. Each call costs 2 round-trips: one SCAN/SSCAN + one pipelined HGETALL batch via mhgetall(). Pass the returned 'cursor' value as the next call's $cursor; iteration is complete when the returned cursor is '0'.

$cursor = '0';
do {
    ['cursor' => $cursor, 'rows' => $rows] =
        Store::iteratePaged('my_table', $cursor, 50);
    foreach ($rows as $key => $row) { // ... }
} while ($cursor !== '0');
Parameters
$name : string
$cursor : string = '0'
$count : int = 100
Return values
array{cursor: string, rows: array>}

make()

Register a table schema on the Redis backend.

public make(string $name, int $maxRows, array<string, array{0: int, 1?: int}> $columns[, array<string, mixed> $opts = [] ]) : void

Must be called before any set/get/del/incr/count/iterate operations on the named table. Accepts the same $columns shape as TableBackend::make() (column => [TYPE, size?]). $maxRows is advisory only — Redis has no hard cap; use maxmemory + maxmemory-policy server-side instead.

$opts accepts:

  • 'mode''tracked' (default) or 'ttl'. See class docblock.
  • 'ttl' — TTL in seconds; required and >= 1 when mode = 'ttl'.
Parameters
$name : string
$maxRows : int
$columns : array<string, array{0: int, 1?: int}>

Column definitions.

$opts : array<string, mixed> = []

Table options.

Tags
throws
StoreException

When mode is unknown, 'tracked' + ttl > 0, or 'ttl' mode with ttl < 1.

mget()

Fetch multiple rows in a single pipelined round-trip.

public mget(string $name, array<int, string> $keys) : array<string, array<string, scalar>|null>

Returns a map of key → decoded row for every key in $keys. Missing keys map to null. Uses mhgetall() (pipelined HGETALL) internally — O(1) round-trips regardless of batch size.

Parameters
$name : string
$keys : array<int, string>
Return values
array<string, array<string, scalar>|null>

mset()

Write multiple rows in a single pipelined round-trip.

public mset(string $name, array<string, array<string, bool|float|int|string>> $rows) : bool

Uses mhsetWithMembership() internally — one pipeline for all HMSET + optional EXPIRE + a single SADD for tracked-mode membership. Idempotent on existing keys (tracked SET ignores duplicate members). Returns true on success.

Parameters
$name : string
$rows : array<string, array<string, bool|float|int|string>>

Key → column-value map.

Return values
bool

names()

Return the names of all tables registered on this backend via make().

public names() : array<int, string>
Return values
array<int, string>

ping()

Health check via PINGStore::ping() delegates here.

public ping() : bool
Return values
bool

prefix()

Returns the key namespace prefix (e.g. 'zealstore').

public prefix() : string
Return values
string

publish()

Pub/sub publish through the pool. Returns the number of subscribers Redis delivered the message to.

public publish(string $channel, string $payload) : int
Parameters
$channel : string
$payload : string
Return values
int

publishReliable()

Append a message to a Redis Stream via XADD. Auto-trims to $maxLen entries when set. The payload is stored under the 'payload' field, matching the consumer-side convention in RedisStreams.

public publishReliable(string $stream, string $payload[, int|null $maxLen = null ]) : string
Parameters
$stream : string
$payload : string
$maxLen : int|null = null
Return values
string

The Redis-generated message id (e.g. '1626300000000-0').

sadd()

Add one or more members to a Redis SET at the given absolute $key.

public sadd(string $key, string ...$members) : int

The key is NOT prefixed — callers own the namespace (used by Room). Returns the number of NEW members added (existing members are ignored).

Parameters
$key : string
$members : string
Return values
int

scard()

Return the cardinality (number of members) of a Redis SET at the given absolute $key. O(1). Returns 0 when the key does not exist.

public scard(string $key) : int
Parameters
$key : string
Return values
int

sdel()

Delete an absolute Redis SET key via UNLINK (non-blocking reclaim).

public sdel(string $key) : bool

Returns true when the key was present and unlinked.

Parameters
$key : string
Return values
bool

set()

Write a row to the named table. Creates or overwrites the Redis HASH at {prefix}:{name}:{key} and, in 'tracked' mode, adds $key to the membership SET. Applies EXPIRE in 'ttl' mode.

public set(string $name, string $key, array<string, bool|float|int|string> $row) : bool
Parameters
$name : string
$key : string
$row : array<string, bool|float|int|string>

Column values to store.

Return values
bool

srem()

Remove one or more members from a Redis SET at the given absolute $key.

public srem(string $key, string ...$members) : int

Returns the number of members that were actually removed.

Parameters
$key : string
$members : string
Return values
int

sscanCursor()

Cursor-based SSCAN on an absolute SET key — one batch per call.

public sscanCursor(string $key, string $cursor, int $count) : array{0: string, 1: list}
Parameters
$key : string
$cursor : string
$count : int
Return values
array{0: string, 1: list}

[nextCursor, members]

url()

Returns the Redis connection URL this backend was constructed with.

public url() : string
Return values
string

assertMade()

Assert that $name was registered via make(), throwing StoreException otherwise.

private assertMade(string $name) : void
Parameters
$name : string
Tags
throws
StoreException

When the table has not been registered.

countViaScan()

Count rows in 'ttl' mode via SCAN MATCH — O(N).

private countViaScan(string $name) : int

Only called from count() when the table mode is 'ttl'.

Parameters
$name : string
Return values
int

rowKey()

Build the Redis HASH key for a specific row: {prefix}:{table}:{key}.

private rowKey(string $table, string $key) : string
Parameters
$table : string
$key : string
Return values
string

setKey()

Build the Redis SET key for the membership index: {prefix}:{table}:__keys__.

private setKey(string $table) : string
Parameters
$table : string
Return values
string
On this page