Skip to content

Instantly share code, notes, and snippets.

@nrk
Created April 4, 2011 21:54
Show Gist options
  • Save nrk/902540 to your computer and use it in GitHub Desktop.
Save nrk/902540 to your computer and use it in GitHub Desktop.
Namespace Redis keys transparently using Predis v0.6.x
<?php
// The Predis\NamespacingProfile class was originally conceived for Predis v0.7-dev but it
// has been backported to the current stable branch (Predis v0.6.x). Things will be handled
// differently with Predis v0.7, see this gist http://gist.github.com/903421 for details.
require 'Predis.php';
require 'NamespacingProfile.php';
$profile = new Predis\NamespacingProfile('nrk:', '2.2');
$redis = new Predis\Client('tcp://127.0.0.1', $profile);
$redis->mset(array('foo' => 'bar', 'lol' => 'wut'));
var_dump($redis->mget('foo', 'lol'));
/*
array(2) {
[0]=> string(3) "bar"
[1]=> string(3) "wut"
}
*/
var_dump($redis->keys('*'));
/*
array(2) {
[0]=> string(7) "nrk:foo"
[1]=> string(7) "nrk:lol"
}
*/
<?php
namespace Predis;
class NamespacingProfile extends RedisServerProfile {
private $_prefix;
private $_profile;
private $_strategies;
public function __construct($prefix, $profile = 'default') {
$this->_prefix = $prefix;
$this->_profile = $this->createProfile($profile);
$this->_strategies = $this->getPrefixStrategies();
}
protected function getSupportedCommands() {
// NOOP
}
private function createProfile($profile) {
if (!$profile instanceof RedisServerProfile) {
$profile = RedisServerProfile::get($profile);
}
$this->checkProfile($profile->getVersion());
return $profile;
}
protected function checkProfile($profile) {
if (!in_array($profile, $this->getSupportedProfiles())) {
throw new ClientException("Unsupported profile: $profile");
}
}
protected function getSupportedProfiles() {
return array('1.2', '2.0', '2.2');
}
protected function getPrefixStrategies() {
$singleKey = function(&$arguments, $prefix) {
$arguments[0] = "$prefix{$arguments[0]}";
};
$multiKeys = function(&$arguments, $prefix) {
if (count($arguments) === 1 && is_array($arguments[0])) {
$arguments = &$arguments[0];
}
foreach ($arguments as &$key) {
$key = "$prefix$key";
}
};
$skipLast = function(&$arguments, $prefix) {
$length = count($arguments);
for ($i = 0; $i < $length - 1; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
};
$interleavedKeys = function(&$arguments, $prefix) {
$length = count($arguments);
if ($length === 1 && is_array($arguments[0])) {
$oldKvs = &$arguments[0];
$newKvs = array();
foreach ($oldKvs as $key => $value) {
$newKvs["$prefix$key"] = $value;
unset($oldKvs[$key]);
}
$arguments[0] = $newKvs;
}
else {
for ($i = 0; $i < $length; $i += 2) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
}
};
$zunionstore = function(&$arguments, $prefix) {
$arguments[0] = "$prefix{$arguments[0]}";
if (is_array($arguments[1])) {
foreach ($arguments[1] as &$destinationKey) {
$destinationKey = "$prefix$destinationKey";
}
$args = &$arguments[1];
$length = count($args);
for ($i = 0; $i < $length; $i++) {
$arguments[1][$i] = "$prefix{$args[$i]}";
}
}
else {
$length = (int)$arguments[1];
for ($i = 2; $i < $length; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
}
};
$sort = function(&$arguments, $prefix) {
$arguments[0] = "$prefix{$arguments[0]}";
if (count($arguments) === 1) {
return;
}
foreach ($arguments[1] as $modifier => &$value) {
switch (strtoupper($modifier)) {
case 'BY':
case 'STORE':
$value = "$prefix$value";
break;
case 'GET':
if (is_array($value)) {
foreach ($value as &$getItem) {
if ($getItem !== '#') {
$getItem = "$prefix$getItem";
}
}
}
else {
if ($value !== '#') {
$value = "$prefix$value";
}
}
break;
}
}
};
$debug = function(&$arguments, $prefix) {
if (count($arguments) === 3 && strtoupper($arguments[1]) == 'OBJECT') {
$arguments[2] = "$prefix{$arguments[2]}";
}
};
$cmdSingleKey = array(
'type', 'exists', 'move', 'expire', 'persist', 'sort', 'expireat', 'ttl', 'append',
'getrange', 'setnx', 'decr', 'getset', 'setrange', 'decrby', 'incr', 'set', 'strlen',
'get', 'incrby', 'setbit', 'getbit', 'setex', 'hdel', 'hgetall', 'hlen', 'hset',
'hexists', 'hincrby', 'hmget', 'hsetnx', 'hget', 'hkeys', 'hmset', 'hvals', 'lindex',
'linsert', 'llen', 'lpop', 'lpush', 'lpushx', 'rpushx', 'lrange', 'lrem', 'lset',
'ltrim', 'rpop', 'rpush', 'rpushx', 'sadd', 'scard', 'sismember', 'smembers', 'spop',
'srandmember', 'srem', 'zadd', 'zcard', 'zcount', 'zincrby', 'zrange', 'zrangebyscore',
'zrank', 'zrem', 'zremrangebyrank', 'zremrangebyscore', 'zrevrange', 'zrevrangebyscore',
'zrevrank', 'zscore', 'publish', 'keys',
);
$cmdMultiKeys = array(
'del', 'rename', 'renamenx', 'mget', 'rpoplpush', 'sdiff', 'sdiffstore', 'sinter',
'sinterstore', 'sunion', 'sunionstore', 'subscribe', 'punsubscribe', 'subscribe',
'unsubscribe', 'watch',
);
return array_merge(
array_fill_keys($cmdSingleKey, $singleKey),
array_fill_keys($cmdMultiKeys, $multiKeys),
array(
'blpop' => $skipLast, 'blpop' => $skipLast, 'brpoplpush' => $skipLast, 'smove' => $skipLast,
'mset' => $interleavedKeys, 'msetnx' => $interleavedKeys,
'zinterstore' => $zunionstore, 'zunionstore' => $zunionstore,
'sort' => $sort, 'debug' => $debug
)
);
}
public function getProfile() {
return $this->_profile;
}
public function setPrefixStrategy($command, $callable) {
$this->_strategies[$command] = $callable;
}
public function getPrefixStrategy($command) {
if (isset($this->_strategies[$command])) {
return $this->_strategies[$command];
}
}
public function createCommand($method, $arguments = array()) {
if (isset($this->_strategies[$method])) {
$this->_strategies[$method]($arguments, $this->_prefix);
}
return $this->_profile->createCommand($method, $arguments);
}
public function getVersion() {
return $this->_profile->getVersion();
}
public function supportsCommand($command) {
return $this->_profile->supportsCommand($command);
}
public function supportsCommands(Array $commands) {
return $this->_profile->supportsCommands($commands);
}
}
@mihroot
Copy link

mihroot commented May 7, 2012

On line:112 additional check needed coz SORT command can take 'GET #' param, that don't need to be namespaced.
if($getItem != "#")
$getItem = "$prefix$getItem";

@nrk
Copy link
Author

nrk commented May 7, 2012

@mihroot your are definitely right, I didn't notice this error back when I created this gist for an old version of Predis. Luckily the current stable version of Predis ships with a correct prefix strategy for SORT and there's no need for this gist since it was mostly an hack to get some support for key prefixing back when v0.7 was still considered the development version of Predis. I'll fix this gist anyway, thanks for pointing out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment