KV from PHP
ePHPm’s built-in KV store is reachable two ways from PHP:
- SAPI functions —
ephpm_kv_*calls into the embedded store directly. ~100 ns per op, zero serialization. - RESP protocol — any Redis client (Predis, phpredis) connects to the embedded RESP listener. ~10–100 µs per op.
The store is the same in both cases. Use SAPI for hot paths; use RESP when you need portability or Redis-style commands.
SAPI functions
No external dependency. Available as native PHP functions whenever PHP is running inside ePHPm.
ephpm_kv_set("greeting", "hello");
$greeting = ephpm_kv_get("greeting"); // "hello"
$missing = ephpm_kv_get("nope"); // null
ephpm_kv_exists("greeting"); // 1
ephpm_kv_del("greeting"); // 1
ephpm_kv_exists("greeting"); // 0
// Counters
ephpm_kv_incr("page:views", 1); // 1
ephpm_kv_incr("page:views", 5); // 6
// TTL (in milliseconds)
ephpm_kv_set("session:abc", "data");
ephpm_kv_expire("session:abc", 60_000); // 60 seconds
ephpm_kv_pttl("session:abc"); // ~60000
// returns -1 if no expiry, -2 if missing
When to use SAPI
- High-frequency operations (logging, hit counters, rate limit hot path)
- Simple key/value patterns
- You don’t care about portability to standalone Redis
RESP protocol (Predis / phpredis)
Enable the listener in ephpm.toml:
[kv.redis_compat]
enabled = true
listen = "127.0.0.1:6379" # default
# password = "..." # optional AUTHThen connect like any Redis server:
$redis = new Predis\Client('tcp://127.0.0.1:6379');
$redis->set('greeting', 'hello');
$redis->get('greeting');
$redis->setex('session:abc', 60, json_encode($data));
$count = $redis->incr('page:views');Supported commands
| Group | Commands |
|---|---|
| Strings | GET, SET, SETEX, MGET, MSET, SETNX, INCR, DECR, INCRBY, DECRBY, APPEND, STRLEN, GETSET |
| Keys | DEL, EXISTS, EXPIRE, PEXPIRE, PERSIST, TTL, PTTL, TYPE, KEYS, DBSIZE, FLUSHDB, FLUSHALL, RENAME |
| Connection | PING, ECHO, SELECT, QUIT, COMMAND, INFO, AUTH |
Not implemented: hashes, lists, sets, transactions, SCAN, pub/sub. ePHPm targets the cache + counter + session use case — if you need full Redis, run actual Redis.
Multi-tenant note
The RESP listener exposes the whole store with no per-vhost filtering. In multi-tenant deployments ([server] sites_dir = ...) keep [kv.redis_compat] enabled = false and use the SAPI functions, which are automatically namespaced per virtual host. ePHPm derives a per-site password from the master [kv] secret and injects it into PHP’s $_ENV as EPHPM_REDIS_PASSWORD.
Common patterns
Cache-aside
$key = "cache:user:{$id}";
$cached = ephpm_kv_get($key);
if ($cached === null) {
$cached = expensive_lookup($id);
ephpm_kv_set($key, json_encode($cached));
ephpm_kv_expire($key, 5 * 60 * 1000); // 5 minutes
}
return json_decode($cached, true);Token-bucket rate limit
$key = "ratelimit:{$ip}";
$count = ephpm_kv_incr($key, 1);
if ($count === 1) {
ephpm_kv_expire($key, 60_000); // first request opens a 60s window
}
return $count <= $max_per_minute;Session storage
ephpm_kv_set("session:{$id}", json_encode($data));
ephpm_kv_expire("session:{$id}", 3600 * 1000); // 1 hour
Configuration
[kv]
memory_limit = "256MB"
eviction_policy = "allkeys-lru" # or noeviction / volatile-lru / allkeys-random
compression = "none" # or gzip / brotli / zstd
compression_level = 6
compression_min_size = 1024 # bytes — values below this are stored raw
[kv.redis_compat]
enabled = false # turn on the RESP listener
listen = "127.0.0.1:6379"
# password = "..." # AUTH required when setSee Configuration reference for every key.
See also
ephpm kvCLI — debug the live store- KV store architecture — how it works under the hood
- Examples in the repo:
examples/kv-sapi-basic.php,examples/kv-redis-predis.php