]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | /******************************************************************************* | |
3 | * Redis PHP Bindings - http://code.google.com/p/redis/ | |
4 | * | |
5 | * Copyright 2009 Ludovico Magnocavallo | |
6 | * Copyright 2009 Salvatore Sanfilippo (ported it to PHP5, fixed some bug) | |
7 | * Released under the same license as Redis. | |
8 | * | |
9 | * Version: 0.1 | |
10 | * | |
11 | * $Revision: 139 $ | |
12 | * $Date: 2009-03-15 22:59:40 +0100 (Dom, 15 Mar 2009) $ | |
13 | * | |
14 | ******************************************************************************/ | |
15 | ||
16 | ||
17 | class Redis { | |
18 | public $server; | |
19 | public $port; | |
20 | private $_sock; | |
21 | ||
22 | public function __construct($host='localhost', $port=6379) { | |
23 | $this->host = $host; | |
24 | $this->port = $port; | |
25 | } | |
26 | ||
27 | public function connect() { | |
28 | if ($this->_sock) return; | |
29 | if ($sock = fsockopen($this->host, $this->port, $errno, $errstr)) { | |
30 | $this->_sock = $sock; | |
31 | return; | |
32 | } | |
33 | $msg = "Cannot open socket to {$this->host}:{$this->port}"; | |
34 | if ($errno || $errmsg) | |
35 | $msg .= "," . ($errno ? " error $errno" : "") . | |
36 | ($errmsg ? " $errmsg" : ""); | |
37 | trigger_error("$msg.", E_USER_ERROR); | |
38 | } | |
39 | ||
40 | public function disconnect() { | |
41 | if ($this->_sock) @fclose($this->_sock); | |
42 | $this->_sock = null; | |
43 | } | |
44 | ||
45 | public function ping() { | |
46 | $this->connect(); | |
47 | $this->write("PING\r\n"); | |
48 | return $this->get_response(); | |
49 | } | |
50 | ||
51 | public function do_echo($s) { | |
52 | $this->connect(); | |
53 | $this->write("ECHO " . strlen($s) . "\r\n$s\r\n"); | |
54 | return $this->get_response(); | |
55 | } | |
56 | ||
57 | public function set($name, $value, $preserve=false) { | |
58 | $this->connect(); | |
59 | $this->write( | |
60 | ($preserve ? 'SETNX' : 'SET') . | |
61 | " $name " . strlen($value) . "\r\n$value\r\n" | |
62 | ); | |
63 | return $this->get_response(); | |
64 | } | |
65 | ||
66 | public function get($name) { | |
67 | $this->connect(); | |
68 | $this->write("GET $name\r\n"); | |
69 | return $this->get_response(); | |
70 | } | |
71 | ||
72 | public function incr($name, $amount=1) { | |
73 | $this->connect(); | |
74 | if ($amount == 1) | |
75 | $this->write("INCR $name\r\n"); | |
76 | else | |
77 | $this->write("INCRBY $name $amount\r\n"); | |
78 | return $this->get_response(); | |
79 | } | |
80 | ||
81 | public function decr($name, $amount=1) { | |
82 | $this->connect(); | |
83 | if ($amount == 1) | |
84 | $this->write("DECR $name\r\n"); | |
85 | else | |
86 | $this->write("DECRBY $name $amount\r\n"); | |
87 | return $this->get_response(); | |
88 | } | |
89 | ||
90 | public function exists($name) { | |
91 | $this->connect(); | |
92 | $this->write("EXISTS $name\r\n"); | |
93 | return $this->get_response(); | |
94 | } | |
95 | ||
96 | public function delete($name) { | |
97 | $this->connect(); | |
98 | $this->write("DEL $name\r\n"); | |
99 | return $this->get_response(); | |
100 | } | |
101 | ||
102 | public function keys($pattern) { | |
103 | $this->connect(); | |
104 | $this->write("KEYS $pattern\r\n"); | |
105 | return explode(' ', $this->get_response()); | |
106 | } | |
107 | ||
108 | public function randomkey() { | |
109 | $this->connect(); | |
110 | $this->write("RANDOMKEY\r\n"); | |
111 | return $this->get_response(); | |
112 | } | |
113 | ||
114 | public function rename($src, $dst) { | |
115 | $this->connect(); | |
116 | $this->write("RENAME $src $dst\r\n"); | |
117 | return $this->get_response(); | |
118 | } | |
119 | ||
120 | public function renamenx($src, $dst) { | |
121 | $this->connect(); | |
122 | $this->write("RENAMENX $src $dst\r\n"); | |
123 | return $this->get_response(); | |
124 | } | |
125 | ||
126 | public function expire($name, $time) { | |
127 | $this->connect(); | |
128 | $this->write("EXPIRE $name $time\r\n"); | |
129 | return $this->get_response(); | |
130 | } | |
131 | ||
132 | public function push($name, $value, $tail=true) { | |
133 | // default is to append the element to the list | |
134 | $this->connect(); | |
135 | $this->write( | |
136 | ($tail ? 'RPUSH' : 'LPUSH') . | |
137 | " $name " . strlen($value) . "\r\n$value\r\n" | |
138 | ); | |
139 | return $this->get_response(); | |
140 | } | |
141 | ||
142 | public function lpush($name, $value) { | |
143 | return $this->push($name, $value, false); | |
144 | } | |
145 | ||
146 | public function rpush($name, $value) { | |
147 | return $this->push($name, $value, true); | |
148 | } | |
149 | ||
150 | public function ltrim($name, $start, $end) { | |
151 | $this->connect(); | |
152 | $this->write("LTRIM $name $start $end\r\n"); | |
153 | return $this->get_response(); | |
154 | } | |
155 | ||
156 | public function lindex($name, $index) { | |
157 | $this->connect(); | |
158 | $this->write("LINDEX $name $index\r\n"); | |
159 | return $this->get_response(); | |
160 | } | |
161 | ||
162 | public function pop($name, $tail=true) { | |
163 | $this->connect(); | |
164 | $this->write( | |
165 | ($tail ? 'RPOP' : 'LPOP') . | |
166 | " $name\r\n" | |
167 | ); | |
168 | return $this->get_response(); | |
169 | } | |
170 | ||
171 | public function lpop($name, $value) { | |
172 | return $this->pop($name, $value, false); | |
173 | } | |
174 | ||
175 | public function rpop($name, $value) { | |
176 | return $this->pop($name, $value, true); | |
177 | } | |
178 | ||
179 | public function llen($name) { | |
180 | $this->connect(); | |
181 | $this->write("LLEN $name\r\n"); | |
182 | return $this->get_response(); | |
183 | } | |
184 | ||
185 | public function lrange($name, $start, $end) { | |
186 | $this->connect(); | |
187 | $this->write("LRANGE $name $start $end\r\n"); | |
188 | return $this->get_response(); | |
189 | } | |
190 | ||
191 | public function sort($name, $query=false) { | |
192 | $this->connect(); | |
193 | $this->write($query == false ? "SORT $name\r\n" : "SORT $name $query\r\n"); | |
194 | return $this->get_response(); | |
195 | } | |
196 | ||
197 | public function lset($name, $value, $index) { | |
198 | $this->connect(); | |
199 | $this->write("LSET $name $index " . strlen($value) . "\r\n$value\r\n"); | |
200 | return $this->get_response(); | |
201 | } | |
202 | ||
203 | public function sadd($name, $value) { | |
204 | $this->connect(); | |
205 | $this->write("SADD $name " . strlen($value) . "\r\n$value\r\n"); | |
206 | return $this->get_response(); | |
207 | } | |
208 | ||
209 | public function srem($name, $value) { | |
210 | $this->connect(); | |
211 | $this->write("SREM $name " . strlen($value) . "\r\n$value\r\n"); | |
212 | return $this->get_response(); | |
213 | } | |
214 | ||
215 | public function sismember($name, $value) { | |
216 | $this->connect(); | |
217 | $this->write("SISMEMBER $name " . strlen($value) . "\r\n$value\r\n"); | |
218 | return $this->get_response(); | |
219 | } | |
220 | ||
221 | public function sinter($sets) { | |
222 | $this->connect(); | |
223 | $this->write('SINTER ' . implode(' ', $sets) . "\r\n"); | |
224 | return $this->get_response(); | |
225 | } | |
226 | ||
227 | public function smembers($name) { | |
228 | $this->connect(); | |
229 | $this->write("SMEMBERS $name\r\n"); | |
230 | return $this->get_response(); | |
231 | } | |
232 | ||
233 | public function scard($name) { | |
234 | $this->connect(); | |
235 | $this->write("SCARD $name\r\n"); | |
236 | return $this->get_response(); | |
237 | } | |
238 | ||
239 | public function select_db($name) { | |
240 | $this->connect(); | |
241 | $this->write("SELECT $name\r\n"); | |
242 | return $this->get_response(); | |
243 | } | |
244 | ||
245 | public function move($name, $db) { | |
246 | $this->connect(); | |
247 | $this->write("MOVE $name $db\r\n"); | |
248 | return $this->get_response(); | |
249 | } | |
250 | ||
251 | public function save($background=false) { | |
252 | $this->connect(); | |
253 | $this->write(($background ? "BGSAVE\r\n" : "SAVE\r\n")); | |
254 | return $this->get_response(); | |
255 | } | |
256 | ||
257 | public function bgsave($background=false) { | |
258 | return $this->save(true); | |
259 | } | |
260 | ||
261 | public function lastsave() { | |
262 | $this->connect(); | |
263 | $this->write("LASTSAVE\r\n"); | |
264 | return $this->get_response(); | |
265 | } | |
266 | ||
267 | public function flushdb($all=false) { | |
268 | $this->connect(); | |
269 | $this->write($all ? "FLUSHALL\r\n" : "FLUSHDB\r\n"); | |
270 | return $this->get_response(); | |
271 | } | |
272 | ||
273 | public function flushall() { | |
274 | return $this->flush(true); | |
275 | } | |
276 | ||
277 | public function info() { | |
278 | $this->connect(); | |
279 | $this->write("INFO\r\n"); | |
280 | $info = array(); | |
281 | $data =& $this->get_response(); | |
282 | foreach (explode("\r\n", $data) as $l) { | |
283 | if (!$l) | |
284 | continue; | |
285 | list($k, $v) = explode(':', $l, 2); | |
286 | $_v = strpos($v, '.') !== false ? (float)$v : (int)$v; | |
287 | $info[$k] = (string)$_v == $v ? $_v : $v; | |
288 | } | |
289 | return $info; | |
290 | } | |
291 | ||
292 | private function write($s) { | |
293 | while ($s) { | |
294 | $i = fwrite($this->_sock, $s); | |
295 | if ($i == 0) // || $i == strlen($s)) | |
296 | break; | |
297 | $s = substr($s, $i); | |
298 | } | |
299 | } | |
300 | ||
301 | private function read($len=1024) { | |
302 | if ($s = fgets($this->_sock)) | |
303 | return $s; | |
304 | $this->disconnect(); | |
305 | trigger_error("Cannot read from socket.", E_USER_ERROR); | |
306 | } | |
307 | ||
308 | private function get_response() { | |
309 | $data = trim($this->read()); | |
310 | $c = $data[0]; | |
311 | $data = substr($data, 1); | |
312 | switch ($c) { | |
313 | case '-': | |
314 | trigger_error($data, E_USER_ERROR); | |
315 | break; | |
316 | case '+': | |
317 | return $data; | |
318 | case ':': | |
319 | $i = strpos($data, '.') !== false ? (int)$data : (float)$data; | |
320 | if ((string)$i != $data) | |
321 | trigger_error("Cannot convert data '$c$data' to integer", E_USER_ERROR); | |
322 | return $i; | |
323 | case '$': | |
324 | return $this->get_bulk_reply($c . $data); | |
325 | case '*': | |
326 | $num = (int)$data; | |
327 | if ((string)$num != $data) | |
328 | trigger_error("Cannot convert multi-response header '$data' to integer", E_USER_ERROR); | |
329 | $result = array(); | |
330 | for ($i=0; $i<$num; $i++) | |
331 | $result[] =& $this->get_response(); | |
332 | return $result; | |
333 | default: | |
334 | trigger_error("Invalid reply type byte: '$c'"); | |
335 | } | |
336 | } | |
337 | ||
338 | private function get_bulk_reply($data=null) { | |
339 | if ($data === null) | |
340 | $data = trim($this->read()); | |
341 | if ($data == '$-1') | |
342 | return null; | |
343 | $c = $data[0]; | |
344 | $data = substr($data, 1); | |
345 | $bulklen = (int)$data; | |
346 | if ((string)$bulklen != $data) | |
347 | trigger_error("Cannot convert bulk read header '$c$data' to integer", E_USER_ERROR); | |
348 | if ($c != '$') | |
349 | trigger_error("Unkown response prefix for '$c$data'", E_USER_ERROR); | |
350 | $buffer = ''; | |
351 | while ($bulklen) { | |
352 | $data = fread($this->_sock,$bulklen); | |
353 | $bulklen -= strlen($data); | |
354 | $buffer .= $data; | |
355 | } | |
356 | $crlf = fread($this->_sock,2); | |
357 | return $buffer; | |
358 | } | |
359 | } | |
360 | ||
361 | /* | |
362 | $r = new Redis(); | |
363 | var_dump($r->set("foo","bar")); | |
364 | var_dump($r->get("foo")); | |
365 | var_dump($r->info()); | |
366 | */ | |
367 | ||
368 | ?> |