* Redis PHP Bindings - http://code.google.com/p/redis/
*
* Copyright 2009 Ludovico Magnocavallo
+ * Copyright 2009 Salvatore Sanfilippo (ported it to PHP5, fixed some bug)
* Released under the same license as Redis.
*
* Version: 0.1
class Redis {
-
- var $server;
- var $port;
- var $_sock;
-
- function Redis($host, $port=6379) {
+ public $server;
+ public $port;
+ private $_sock;
+
+ public function __construct($host='localhost', $port=6379) {
$this->host = $host;
$this->port = $port;
}
- function connect() {
- if ($this->_sock)
- return;
+ public function connect() {
+ if ($this->_sock) return;
if ($sock = fsockopen($this->host, $this->port, $errno, $errstr)) {
$this->_sock = $sock;
return;
}
$msg = "Cannot open socket to {$this->host}:{$this->port}";
if ($errno || $errmsg)
- $msg .= "," . ($errno ? " error $errno" : "") . ($errmsg ? " $errmsg" : "");
+ $msg .= "," . ($errno ? " error $errno" : "") .
+ ($errmsg ? " $errmsg" : "");
trigger_error("$msg.", E_USER_ERROR);
}
- function disconnect() {
- if ($this->_sock)
- @fclose($this->_sock);
+ public function disconnect() {
+ if ($this->_sock) @fclose($this->_sock);
$this->_sock = null;
}
- function &ping() {
+ public function ping() {
$this->connect();
- $this->_write("PING\r\n");
- return $this->_simple_response();
+ $this->write("PING\r\n");
+ return $this->get_response();
}
- function &do_echo($s) {
+ public function do_echo($s) {
$this->connect();
- $this->_write("ECHO " . strlen($s) . "\r\n$s\r\n");
- return $this->_get_value();
+ $this->write("ECHO " . strlen($s) . "\r\n$s\r\n");
+ return $this->get_response();
}
- function &set($name, $value, $preserve=false) {
+ public function set($name, $value, $preserve=false) {
$this->connect();
- $this->_write(
+ $this->write(
($preserve ? 'SETNX' : 'SET') .
" $name " . strlen($value) . "\r\n$value\r\n"
);
- return $preserve ? $this->_numeric_response() : $this->_simple_response();
+ return $this->get_response();
}
- function &get($name) {
+ public function get($name) {
$this->connect();
- $this->_write("GET $name\r\n");
- return $this->_get_value();
+ $this->write("GET $name\r\n");
+ return $this->get_response();
}
- function &incr($name, $amount=1) {
+ public function incr($name, $amount=1) {
$this->connect();
if ($amount == 1)
- $this->_write("INCR $name\r\n");
+ $this->write("INCR $name\r\n");
else
- $this->_write("INCRBY $name $amount\r\n");
- return $this->_numeric_response();
+ $this->write("INCRBY $name $amount\r\n");
+ return $this->get_response();
}
- function &decr($name, $amount=1) {
+ public function decr($name, $amount=1) {
$this->connect();
if ($amount == 1)
- $this->_write("DECR $name\r\n");
+ $this->write("DECR $name\r\n");
else
- $this->_write("DECRBY $name $amount\r\n");
- return $this->_numeric_response();
+ $this->write("DECRBY $name $amount\r\n");
+ return $this->get_response();
}
- function &exists($name) {
+ public function exists($name) {
$this->connect();
- $this->_write("EXISTS $name\r\n");
- return $this->_numeric_response();
+ $this->write("EXISTS $name\r\n");
+ return $this->get_response();
}
- function &delete($name) {
+ public function delete($name) {
$this->connect();
- $this->_write("DEL $name\r\n");
- return $this->_numeric_response();
+ $this->write("DEL $name\r\n");
+ return $this->get_response();
}
- function &keys($pattern) {
+ public function keys($pattern) {
$this->connect();
- $this->_write("KEYS $pattern\r\n");
- return explode(' ', $this->_get_value());
+ $this->write("KEYS $pattern\r\n");
+ return explode(' ', $this->get_response());
}
- function &randomkey() {
+ public function randomkey() {
$this->connect();
- $this->_write("RANDOMKEY\r\n");
- $s =& trim($this->_read());
- $this->_check_for_error($s);
- return $s;
+ $this->write("RANDOMKEY\r\n");
+ return $this->get_response();
}
- function &rename($src, $dst, $preserve=False) {
+ public function rename($src, $dst) {
$this->connect();
- if ($preserve) {
- $this->_write("RENAMENX $src $dst\r\n");
- return $this->_numeric_response();
- }
- $this->_write("RENAME $src $dst\r\n");
- return trim($this->_simple_response());
+ $this->write("RENAME $src $dst\r\n");
+ return $this->get_response();
+ }
+
+ public function renamenx($src, $dst) {
+ $this->connect();
+ $this->write("RENAMENX $src $dst\r\n");
+ return $this->get_response();
+ }
+
+ public function expire($name, $time) {
+ $this->connect();
+ $this->write("EXPIRE $name $time\r\n");
+ return $this->get_response();
}
- function &push($name, $value, $tail=true) {
+ public function push($name, $value, $tail=true) {
// default is to append the element to the list
$this->connect();
- $this->_write(
+ $this->write(
($tail ? 'RPUSH' : 'LPUSH') .
" $name " . strlen($value) . "\r\n$value\r\n"
);
- return $this->_simple_response();
+ return $this->get_response();
}
-
- function <rim($name, $start, $end) {
+
+ public function lpush($name, $value) {
+ return $this->push($name, $value, false);
+ }
+
+ public function rpush($name, $value) {
+ return $this->push($name, $value, true);
+ }
+
+ public function ltrim($name, $start, $end) {
$this->connect();
- $this->_write("LTRIM $name $start $end\r\n");
- return $this->_simple_response();
+ $this->write("LTRIM $name $start $end\r\n");
+ return $this->get_response();
}
- function &lindex($name, $index) {
+ public function lindex($name, $index) {
$this->connect();
- $this->_write("LINDEX $name $index\r\n");
- return $this->_get_value();
+ $this->write("LINDEX $name $index\r\n");
+ return $this->get_response();
}
- function &pop($name, $tail=true) {
+ public function pop($name, $tail=true) {
$this->connect();
- $this->_write(
+ $this->write(
($tail ? 'RPOP' : 'LPOP') .
" $name\r\n"
);
- return $this->_get_value();
+ return $this->get_response();
+ }
+
+ public function lpop($name, $value) {
+ return $this->pop($name, $value, false);
+ }
+
+ public function rpop($name, $value) {
+ return $this->pop($name, $value, true);
}
- function &llen($name) {
+ public function llen($name) {
$this->connect();
- $this->_write("LLEN $name\r\n");
- return $this->_numeric_response();
+ $this->write("LLEN $name\r\n");
+ return $this->get_response();
}
- function &lrange($name, $start, $end) {
+ public function lrange($name, $start, $end) {
$this->connect();
- $this->_write("LRANGE $name $start $end\r\n");
- return $this->_get_multi();
+ $this->write("LRANGE $name $start $end\r\n");
+ return $this->get_response();
}
- function &sort($name, $query=false) {
+ public function sort($name, $query=false) {
$this->connect();
- if ($query === false) {
- $this->_write("SORT $name\r\n");
- } else {
- $this->_write("SORT $name $query\r\n");
- }
- return $this->_get_multi();
+ $this->write($query == false ? "SORT $name\r\n" : "SORT $name $query\r\n");
+ return $this->get_response();
}
- function &lset($name, $value, $index) {
+ public function lset($name, $value, $index) {
$this->connect();
- $this->_write("LSET $name $index " . strlen($value) . "\r\n$value\r\n");
- return $this->_simple_response();
+ $this->write("LSET $name $index " . strlen($value) . "\r\n$value\r\n");
+ return $this->get_response();
}
- function &sadd($name, $value) {
+ public function sadd($name, $value) {
$this->connect();
- $this->_write("SADD $name " . strlen($value) . "\r\n$value\r\n");
- return $this->_numeric_response();
+ $this->write("SADD $name " . strlen($value) . "\r\n$value\r\n");
+ return $this->get_response();
}
- function &srem($name, $value) {
+ public function srem($name, $value) {
$this->connect();
- $this->_write("SREM $name " . strlen($value) . "\r\n$value\r\n");
- return $this->_numeric_response();
+ $this->write("SREM $name " . strlen($value) . "\r\n$value\r\n");
+ return $this->get_response();
}
- function &sismember($name, $value) {
+ public function sismember($name, $value) {
$this->connect();
- $this->_write("SISMEMBER $name " . strlen($value) . "\r\n$value\r\n");
- return $this->_numeric_response();
+ $this->write("SISMEMBER $name " . strlen($value) . "\r\n$value\r\n");
+ return $this->get_response();
}
- function &sinter($sets) {
+ public function sinter($sets) {
$this->connect();
- $this->_write('SINTER ' . implode(' ', $sets) . "\r\n");
- return $this->_get_multi();
+ $this->write('SINTER ' . implode(' ', $sets) . "\r\n");
+ return $this->get_response();
}
- function &smembers($name) {
+ public function smembers($name) {
$this->connect();
- $this->_write("SMEMBERS $name\r\n");
- return $this->_get_multi();
+ $this->write("SMEMBERS $name\r\n");
+ return $this->get_response();
}
- function &scard($name) {
+ public function scard($name) {
+ $this->connect();
+ $this->write("SCARD $name\r\n");
+ return $this->get_response();
+ }
+
+ public function select_db($name) {
+ $this->connect();
+ $this->write("SELECT $name\r\n");
+ return $this->get_response();
+ }
+
+ public function move($name, $db) {
$this->connect();
- $this->_write("SCARD $name\r\n");
- return $this->_numeric_response();
+ $this->write("MOVE $name $db\r\n");
+ return $this->get_response();
}
- function &select_db($name) {
+ public function save($background=false) {
$this->connect();
- $this->_write("SELECT $name\r\n");
- return $this->_simple_response();
+ $this->write(($background ? "BGSAVE\r\n" : "SAVE\r\n"));
+ return $this->get_response();
}
- function &move($name, $db) {
+ public function bgsave($background=false) {
+ return $this->save(true);
+ }
+
+ public function lastsave() {
$this->connect();
- $this->_write("MOVE $name $db\r\n");
- return $this->_numeric_response();
+ $this->write("LASTSAVE\r\n");
+ return $this->get_response();
}
- function &save($background=false) {
+ public function flushdb($all=false) {
$this->connect();
- $this->_write(($background ? "BGSAVE\r\n" : "SAVE\r\n"));
- return $this->_simple_response();
+ $this->write($all ? "FLUSHALL\r\n" : "FLUSHDB\r\n");
+ return $this->get_response();
+ }
+
+ public function flushall() {
+ return $this->flush(true);
}
- function &lastsave() {
+ public function info() {
$this->connect();
- $this->_write("LASTSAVE\r\n");
- return $this->_numeric_response();
+ $this->write("INFO\r\n");
+ $info = array();
+ $data =& $this->get_response();
+ foreach (explode("\r\n", $data) as $l) {
+ if (!$l)
+ continue;
+ list($k, $v) = explode(':', $l, 2);
+ $_v = strpos($v, '.') !== false ? (float)$v : (int)$v;
+ $info[$k] = (string)$_v == $v ? $_v : $v;
+ }
+ return $info;
}
- function &_write($s) {
+ private function write($s) {
while ($s) {
$i = fwrite($this->_sock, $s);
- if ($i == 0)
+ if ($i == 0) // || $i == strlen($s))
break;
$s = substr($s, $i);
}
}
- function &_read($len=1024) {
+ private function read($len=1024) {
if ($s = fgets($this->_sock))
return $s;
$this->disconnect();
trigger_error("Cannot read from socket.", E_USER_ERROR);
}
- function _check_for_error(&$s) {
- if (!$s || $s[0] != '-')
- return;
- if (substr($s, 0, 4) == '-ERR')
- trigger_error("Redis error: " . trim(substr($s, 4)), E_USER_ERROR);
- trigger_error("Redis error: " . substr(trim($this->_read()), 5), E_USER_ERROR);
- }
-
- function &_simple_response() {
- $s =& trim($this->_read());
- if ($s[0] == '+')
- return substr($s, 1);
- if ($err =& $this->_check_for_error($s))
- return $err;
- trigger_error("Cannot parse first line '$s' for a simple response", E_USER_ERROR);
- }
-
- function &_numeric_response($allow_negative=True) {
- $s =& trim($this->_read());
- $i = (int)$s;
- if ($i . '' == $s) {
- if (!$allow_negative && $i < 0)
- $this->_check_for_error($s);
- return $i;
+ private function get_response() {
+ $data = trim($this->read());
+ $c = $data[0];
+ $data = substr($data, 1);
+ switch ($c) {
+ case '-':
+ trigger_error($data, E_USER_ERROR);
+ break;
+ case '+':
+ return $data;
+ case ':':
+ $i = strpos($data, '.') !== false ? (int)$data : (float)$data;
+ if ((string)$i != $data)
+ trigger_error("Cannot convert data '$c$data' to integer", E_USER_ERROR);
+ return $i;
+ case '$':
+ return $this->get_bulk_reply($c . $data);
+ case '*':
+ $num = (int)$data;
+ if ((string)$num != $data)
+ trigger_error("Cannot convert multi-response header '$data' to integer", E_USER_ERROR);
+ $result = array();
+ for ($i=0; $i<$num; $i++)
+ $result[] =& $this->get_response();
+ return $result;
+ default:
+ trigger_error("Invalid reply type byte: '$c'");
}
- if ($s == 'nil')
+ }
+
+ private function get_bulk_reply($data=null) {
+ if ($data === null)
+ $data = trim($this->read());
+ if ($data == '$-1')
return null;
- trigger_error("Cannot parse '$s' as numeric response.");
- }
-
- function &_get_value() {
- $s =& trim($this->_read());
- if ($s == 'nil')
- return '';
- else if ($s[0] == '-')
- $this->_check_for_error($s);
- $i = (int)$s;
- if ($i . '' != $s)
- trigger_error("Cannot parse '$s' as data length.");
+ $c = $data[0];
+ $data = substr($data, 1);
+ $bulklen = (int)$data;
+ if ((string)$bulklen != $data)
+ trigger_error("Cannot convert bulk read header '$c$data' to integer", E_USER_ERROR);
+ if ($c != '$')
+ trigger_error("Unkown response prefix for '$c$data'", E_USER_ERROR);
$buffer = '';
- while ($i > 0) {
- $s = $this->_read();
- $l = strlen($s);
- $i -= $l;
- if ($l > $i) // ending crlf
- $s = rtrim($s);
- $buffer .= $s;
+ while ($bulklen) {
+ $data = fread($this->_sock,$bulklen);
+ $bulklen -= strlen($data);
+ $buffer .= $data;
}
- if ($i == 0) // let's restore the trailing crlf
- $buffer .= $this->_read();
+ $crlf = fread($this->_sock,2);
return $buffer;
}
-
- function &_get_multi() {
- $results = array();
- $num =& $this->_numeric_response(false);
- if ($num === false)
- return $results;
- while ($num) {
- $results[] =& $this->_get_value();
- $num -= 1;
- }
- return $results;
- }
-
-}
+}
+$r = new Redis();
+var_dump($r->get("foo"));
+var_dump($r->info());
?>