SafeScan
final class SafeScan
Safely scan the Redis keyspace for keys matching a pattern.
This class provides a memory-efficient iterator over keys using the SCAN command, correctly handling the complexity of Redis OPT_PREFIX configuration and Redis Cluster.
The OPT_PREFIX Problem
phpredis has an OPT_PREFIX option that automatically prepends a prefix to keys for most commands (GET, SET, DEL, etc.). However, this creates complexity:
-
SCAN does NOT auto-prefix the pattern - You must manually include OPT_PREFIX in your SCAN pattern to match keys that were stored with auto-prefixing.
-
SCAN returns full keys - Keys returned include the OPT_PREFIX as stored in Redis.
-
DEL DOES auto-prefix - If you pass a SCAN result directly to DEL, phpredis adds OPT_PREFIX again, causing double-prefixing and failed deletions.
Example of the Bug This Class Prevents
OPT_PREFIX = "myapp:"
Stored key in Redis = "myapp:cache:user:1"
// WRONG approach (what broken code does):
$keys = $redis->scan($iter, "myapp:cache:*"); // Returns ["myapp:cache:user:1"]
$redis->del($keys[0]); // Tries to delete "myapp:myapp:cache:user:1" - FAILS!
// CORRECT approach (what SafeScan does):
$keys = $redis->scan($iter, "myapp:cache:*"); // Returns ["myapp:cache:user:1"]
$strippedKey = substr($keys[0], strlen("myapp:")); // "cache:user:1"
$redis->del($strippedKey); // phpredis adds prefix -> deletes "myapp:cache:user:1" - SUCCESS!
Redis Cluster Support
Redis Cluster requires scanning each master node separately because keys are distributed across slots on different nodes. This class handles this automatically:
- For standard Redis: Uses
scan($iter, $pattern, $count) - For RedisCluster: Iterates
_masters()and usesscan($iter, $node, $pattern, $count)
Usage
This class is designed to be used within a connection pool callback:
$context->withConnection(
function (RedisConnection $connection) {
$optPrefix = (string) $connection->getOption(Redis::OPT_PREFIX);
$safeScan = new SafeScan($connection, $optPrefix);
foreach ($safeScan->execute('cache:users:*') as $key) {
// $key is stripped of OPT_PREFIX, safe to use with del(), get(), etc.
}
},
transform: false,
); Methods
Details
at line 81
__construct(RedisConnection $connection, string $optPrefix)
Create a new safe scan instance.
at line 101
Generator
execute(string $pattern, int $count = 1000)
Execute the scan operation.