missing comma
[lhc/web/wiklou.git] / includes / MemCachedClient.inc.php
1 <?php
2 /*
3 * MemCached PHP client
4 * Copyright (c) 2003
5 * Ryan Gilfether <hotrodder@rocketmail.com>
6 * http://www.gilfether.com
7 *
8 * Originally translated from Brad Fitzpatrick's <brad@danga.com> MemCached Perl client
9 * See the memcached website:
10 * http://www.danga.com/memcached/
11 *
12 * This module is Copyright (c) 2003 Ryan Gilfether.
13 * All rights reserved.
14 * You may distribute under the terms of the GNU General Public License
15 * This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
16 *
17 */
18
19 /**
20 * version string
21 */
22 define("MC_VERSION", "1.0.10");
23 /**
24 * int, buffer size used for sending and receiving
25 * data from sockets
26 */
27 define("MC_BUFFER_SZ", 1024);
28 /**
29 * MemCached error numbers
30 */
31 define("MC_ERR_NOT_ACTIVE", 1001); // no active servers
32 define("MC_ERR_SOCKET_WRITE", 1002); // socket_write() failed
33 define("MC_ERR_SOCKET_READ", 1003); // socket_read() failed
34 define("MC_ERR_SOCKET_CONNECT", 1004); // failed to connect to host
35 define("MC_ERR_DELETE", 1005); // delete() did not recieve DELETED command
36 define("MC_ERR_HOST_FORMAT", 1006); // sock_to_host() invalid host format
37 define("MC_ERR_HOST_DEAD", 1007); // sock_to_host() host is dead
38 define("MC_ERR_GET_SOCK", 1008); // get_sock() failed to find a valid socket
39 define("MC_ERR_SET", 1009); // _set() failed to receive the STORED response
40 define("MC_ERR_GET_KEY", 1010); // _load_items no values returned for key(s)
41 define("MC_ERR_LOADITEM_END", 1011); // _load_items failed to receive END response
42 define("MC_ERR_LOADITEM_BYTES", 1012); // _load_items bytes read larger than bytes available
43
44
45 /**
46 * MemCached PHP client Class.
47 *
48 * Communicates with the MemCached server, and executes the MemCached protocol
49 * MemCached available at http://www.danga.com/memcached
50 *
51 * @author Ryan Gilfether <ryan@gilfether.com>
52 * @package MemCachedClient
53 * @access public
54 * @version 1.0.10
55 */
56 class MemCachedClient
57 {
58 /**
59 * array of servers no long available
60 * @var array
61 */
62 var $host_dead;
63 /**
64 * array of open sockets
65 * @var array
66 */
67 var $cache_sock;
68 /**
69 * determine if debugging is either on or off
70 * @var bool
71 */
72 var $debug;
73 /**
74 * array of servers to attempt to use, "host:port" string format
75 * @var array
76 */
77 var $servers;
78 /**
79 * count of currently active connections to servers
80 * @var int
81 */
82 var $active;
83 /**
84 * error code if one is set
85 * @var int
86 */
87 var $errno;
88 /**
89 * string describing error
90 * @var string
91 */
92 var $errstr;
93 /**
94 * size of val to force compression; 0 turns off; defaults 1
95 * @ var int
96 */
97 var $compress = 1;
98 /**
99 * temp flag to turn compression on/off; defaults on
100 * @ var int
101 */
102 var $comp_active = 1;
103
104 /**
105 * array that contains parsed out buckets
106 * @ var array
107 */
108 var $bucket;
109
110
111 /**
112 * Constructor
113 *
114 * Creates a new MemCachedClient object
115 * Takes one parameter, a array of options. The most important key is
116 * $options["servers"], but that can also be set later with the set_servers()
117 * method. The servers must be an array of hosts, each of which is
118 * either a scalar of the form <10.0.0.10:11211> or an array of the
119 * former and an integer weight value. (the default weight if
120 * unspecified is 1.) It's recommended that weight values be kept as low
121 * as possible, as this module currently allocates memory for bucket
122 * distribution proportional to the total host weights.
123 * $options["debug"] turns the debugging on if set to true
124 *
125 * @access public
126 * @param array $option an array of servers and debug status
127 * @return object MemCachedClient the new MemCachedClient object
128 */
129 function MemCachedClient($options = 0)
130 {
131 if(is_array($options))
132 {
133 $this->set_servers($options["servers"]);
134 $this->debug = $options["debug"];
135 $this->compress = $options["compress"];
136 $this->cache_sock = array();
137 }
138
139 $this->errno = 0;
140 $this->errstr = "";
141 }
142
143
144 /**
145 * sets up the list of servers and the ports to connect to
146 * takes an array of servers in the same format as in the constructor
147 *
148 * @access public
149 * @param array $servers array of servers in the format described in the constructor
150 */
151 function set_servers($servers)
152 {
153 $this->servers = $servers;
154 $this->active = count($this->servers);
155 }
156
157
158 /**
159 * if $do_debug is set to true, will print out
160 * debugging info, else debug is turned off
161 *
162 * @access public
163 * @param bool $do_debug set to true to turn debugging on, false to turn off
164 */
165 function set_debug($do_debug)
166 {
167 $this->debug = $do_debug;
168 }
169
170
171 /**
172 * remove all cached hosts that are no longer good
173 *
174 * @access public
175 */
176 function forget_dead_hosts()
177 {
178 unset($this->host_dead);
179 }
180
181
182 /**
183 * disconnects from all servers
184 *
185 * @access public
186 */
187 function disconnect_all()
188 {
189 foreach($this->cache_sock as $sock)
190 socket_close($sock);
191
192 unset($this->cache_sock);
193 $this->active = 0;
194 }
195
196
197 /**
198 * removes the key from the MemCache
199 * $time is the amount of time in seconds (or Unix time) until which
200 * the client wishes the server to refuse "add" and "replace" commands
201 * with this key. For this amount of item, the item is put into a
202 * delete queue, which means that it won't possible to retrieve it by
203 * the "get" command, but "add" and "replace" command with this key
204 * will also fail (the "set" command will succeed, however). After the
205 * time passes, the item is finally deleted from server memory.
206 * The parameter $time is optional, and, if absent, defaults to 0
207 * (which means that the item will be deleted immediately and further
208 * storage commands with this key will succeed).
209 * Possible errors set are:
210 * MC_ERR_NOT_ACTIVE
211 * MC_ERR_GET_SOCK
212 * MC_ERR_SOCKET_WRITE
213 * MC_ERR_SOCKET_READ
214 * MC_ERR_DELETE
215 *
216 * @access public
217 * @param string $key the key to delete
218 * @param timestamp $time optional, the amount of time server will refuse commands on key
219 * @return bool TRUE on success, FALSE if key does not exist
220 */
221 function delete($key, $time = 0)
222 {
223 if(!$this->active)
224 {
225 $this->errno = MC_ERR_NOT_ACTIVE;
226 $this->errstr = "No active servers are available";
227
228 if($this->debug)
229 $this->_debug("delete(): There are no active servers available.");
230
231 return FALSE;
232 }
233
234 $sock = $this->get_sock($key);
235
236 if(!is_resource($sock))
237 {
238 $this->errno = MC_ERR_GET_SOCK;
239 $this->errstr = "Unable to retrieve a valid socket.";
240
241 if($this->debug)
242 $this->_debug("delete(): get_sock() returned an invalid socket.");
243
244 return FALSE;
245 }
246
247 if(is_array($key))
248 $key = $key[1];
249
250 $cmd = "delete $key $time\r\n";
251 $cmd_len = strlen($cmd);
252 $offset = 0;
253
254 // now send the command
255 while($offset < $cmd_len)
256 {
257 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
258
259 if($result !== FALSE)
260 $offset += $result;
261 else if($offset < $cmd_len)
262 {
263 $this->errno = MC_ERR_SOCKET_WRITE;
264 $this->errstr = "Failed to write to socket.";
265
266 if($this->debug)
267 {
268 $sockerr = socket_last_error($sock);
269 $this->_debug("delete(): socket_write() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr));
270 }
271
272 return FALSE;
273 }
274 }
275
276 // now read the server's response
277 if(($retval = socket_read($sock, MC_BUFFER_SZ, PHP_NORMAL_READ)) === FALSE)
278 {
279 $this->errno = MC_ERR_SOCKET_READ;
280 $this->errstr = "Failed to read from socket.";
281
282 if($this->debug)
283 {
284 $sockerr = socket_last_error($sock);
285 $this->_debug("delete(): socket_read() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr));
286 }
287
288 return FALSE;
289 }
290
291 // remove the \r\n from the end
292 $retval = rtrim($retval);
293
294 // now read the server's response
295 if($retval == "DELETED")
296 return TRUE;
297 else
298 {
299 // something went wrong, create the error
300 $this->errno = MC_ERR_DELETE;
301 $this->errstr = "Failed to receive DELETED response from server.";
302
303 if($this->debug)
304 $this->_debug("delete(): Failed to receive DELETED response from server. Received $retval instead.");
305
306 return FALSE;
307 }
308 }
309
310
311 /**
312 * Like set(), but only stores in memcache if the key doesn't already exist.
313 * Possible errors set are:
314 * MC_ERR_NOT_ACTIVE
315 * MC_ERR_GET_SOCK
316 * MC_ERR_SOCKET_WRITE
317 * MC_ERR_SOCKET_READ
318 * MC_ERR_SET
319 *
320 * @access public
321 * @param string $key the key to set
322 * @param mixed $val the value of the key
323 * @param timestamp $exptime optional, the to to live of the key
324 * @return bool TRUE on success, else FALSE
325 */
326 function add($key, $val, $exptime = 0)
327 {
328 return $this->_set("add", $key, $val, $exptime);
329 }
330
331
332 /**
333 * Like set(), but only stores in memcache if the key already exists.
334 * returns TRUE on success else FALSE
335 * Possible errors set are:
336 * MC_ERR_NOT_ACTIVE
337 * MC_ERR_GET_SOCK
338 * MC_ERR_SOCKET_WRITE
339 * MC_ERR_SOCKET_READ
340 * MC_ERR_SET
341 *
342 * @access public
343 * @param string $key the key to set
344 * @param mixed $val the value of the key
345 * @param timestamp $exptime optional, the to to live of the key
346 * @return bool TRUE on success, else FALSE
347 */
348 function replace($key, $val, $exptime = 0)
349 {
350 return $this->_set("replace", $key, $val, $exptime);
351 }
352
353
354 /**
355 * Unconditionally sets a key to a given value in the memcache. Returns true
356 * if it was stored successfully.
357 * The $key can optionally be an arrayref, with the first element being the
358 * hash value, as described above.
359 * Possible errors set are:
360 * MC_ERR_NOT_ACTIVE
361 * MC_ERR_GET_SOCK
362 * MC_ERR_SOCKET_WRITE
363 * MC_ERR_SOCKET_READ
364 * MC_ERR_SET
365 *
366 * @access public
367 * @param string $key the key to set
368 * @param mixed $val the value of the key
369 * @param timestamp $exptime optional, the to to live of the key
370 * @return bool TRUE on success, else FALSE
371 */
372 function set($key, $val, $exptime = 0)
373 {
374 return $this->_set("set", $key, $val, $exptime);
375 }
376
377
378 /**
379 * Retrieves a key from the memcache. Returns the value (automatically
380 * unserialized, if necessary) or FALSE if it fails.
381 * The $key can optionally be an array, with the first element being the
382 * hash value, if you want to avoid making this module calculate a hash
383 * value. You may prefer, for example, to keep all of a given user's
384 * objects on the same memcache server, so you could use the user's
385 * unique id as the hash value.
386 * Possible errors set are:
387 * MC_ERR_GET_KEY
388 *
389 * @access public
390 * @param string $key the key to retrieve
391 * @return mixed the value of the key, FALSE on error
392 */
393 function get($key)
394 {
395 $val =& $this->get_multi($key);
396
397 if(!$val)
398 {
399 $this->errno = MC_ERR_GET_KEY;
400 $this->errstr = "No value found for key $key";
401
402 if($this->debug)
403 $this->_debug("get(): No value found for key $key");
404
405 return FALSE;
406 }
407
408 return $val[$key];
409 }
410
411
412 /**
413 * just like get(), but takes an array of keys, returns FALSE on error
414 * Possible errors set are:
415 * MC_ERR_NOT_ACTIVE
416 *
417 * @access public
418 * @param array $keys the keys to retrieve
419 * @return array the value of each key, FALSE on error
420 */
421 function get_multi($keys)
422 {
423 $sock_keys = array();
424 $socks = array();
425 $val = 0;
426
427 if(!$this->active)
428 {
429 $this->errno = MC_ERR_NOT_ACTIVE;
430 $this->errstr = "No active servers are available";
431
432 if($this->debug)
433 $this->_debug("get_multi(): There are no active servers available.");
434
435 return FALSE;
436 }
437
438 if(!is_array($keys))
439 {
440 $arr[] = $keys;
441 $keys = $arr;
442 }
443
444 foreach($keys as $k)
445 {
446 $sock = $this->get_sock($k);
447
448 if($sock)
449 {
450 $k = is_array($k) ? $k[1] : $k;
451
452 if(@!is_array($sock_keys[$sock]))
453 $sock_keys[$sock] = array();
454
455 // if $sock_keys[$sock] doesn't exist, create it
456 if(!$sock_keys[$sock])
457 $socks[] = $sock;
458
459 $sock_keys[$sock][] = $k;
460 }
461 }
462
463 if(!is_array($socks))
464 {
465 $arr[] = $socks;
466 $socks = $arr;
467 }
468
469 foreach($socks as $s)
470 {
471 $this->_load_items($s, $val, $sock_keys[$sock]);
472 }
473
474 if($this->debug)
475 {
476 while(list($k, $v) = @each($val))
477 $this->_debug("MemCache: got $k = $v\n");
478 }
479
480 return $val;
481 }
482
483
484 /**
485 * Sends a command to the server to atomically increment the value for
486 * $key by $value, or by 1 if $value is undefined. Returns FALSE if $key
487 * doesn't exist on server, otherwise it returns the new value after
488 * incrementing. Value should be zero or greater. Overflow on server
489 * is not checked. Be aware of values approaching 2**32. See decr.
490 * ONLY WORKS WITH NUMERIC VALUES
491 * Possible errors set are:
492 * MC_ERR_NOT_ACTIVE
493 * MC_ERR_GET_SOCK
494 * MC_ERR_SOCKET_WRITE
495 * MC_ERR_SOCKET_READ
496 *
497 * @access public
498 * @param string $key the keys to increment
499 * @param int $value the amount to increment the key bye
500 * @return int the new value of the key, else FALSE
501 */
502 function incr($key, $value = 1)
503 {
504 return $this->_incrdecr("incr", $key, $value);
505 }
506
507
508 /**
509 * Like incr, but decrements. Unlike incr, underflow is checked and new
510 * values are capped at 0. If server value is 1, a decrement of 2
511 * returns 0, not -1.
512 * ONLY WORKS WITH NUMERIC VALUES
513 * Possible errors set are:
514 * MC_ERR_NOT_ACTIVE
515 * MC_ERR_GET_SOCK
516 * MC_ERR_SOCKET_WRITE
517 * MC_ERR_SOCKET_READ
518 *
519 * @access public
520 * @param string $key the keys to increment
521 * @param int $value the amount to increment the key bye
522 * @return int the new value of the key, else FALSE
523 */
524 function decr($key, $value = 1)
525 {
526 return $this->_incrdecr("decr", $key, $value);
527 }
528
529
530 /**
531 * When a function returns FALSE, an error code is set.
532 * This funtion will return the error code.
533 * See error_string()
534 *
535 * @access public
536 * @return int the value of the last error code
537 */
538 function error()
539 {
540 return $this->errno;
541 }
542
543
544 /**
545 * Returns a string describing the error set in error()
546 * See error()
547 *
548 * @access public
549 * @return int a string describing the error code given
550 */
551 function error_string()
552 {
553 return $this->errstr;
554 }
555
556
557 /**
558 * Resets the error number and error string
559 *
560 * @access public
561 */
562 function error_clear()
563 {
564 // reset to no error
565 $this->errno = 0;
566 $this->errstr = "";
567 }
568
569
570 /**
571 * temporarily sets compression on or off
572 * turning it off, and then back on will result in the compression threshold going
573 * back to the original setting from $options
574 * @param int $setting setting of compression (0=off|1=on)
575 */
576
577 function set_compression($setting=1) {
578 if ($setting != 0) {
579 $this->comp_active = 1;
580 } else {
581 $this->comp_active = 0;
582 }
583 }
584
585
586
587 /*
588 * PRIVATE FUNCTIONS
589 */
590
591
592 /**
593 * connects to a server
594 * The $host may either a string int the form of host:port or an array of the
595 * former and an integer weight value. (the default weight if
596 * unspecified is 1.) See the constructor for details
597 * Possible errors set are:
598 * MC_ERR_HOST_FORMAT
599 * MC_ERR_HOST_DEAD
600 * MC_ERR_SOCKET_CONNECT
601 *
602 * @access private
603 * @param mixed $host either an array or a string
604 * @return resource the socket of the new connection, else FALSE
605 */
606 function sock_to_host($host)
607 {
608 if(is_array($host))
609 $host = array_shift($host);
610
611 $now = time();
612
613 // seperate the ip from the port, index 0 = ip, index 1 = port
614 $conn = explode(":", $host);
615 if(count($conn) != 2)
616 {
617 $this->errno = MC_ERR_HOST_FORMAT;
618 $this->errstr = "Host address was not in the format of host:port";
619
620 if($this->debug)
621 $this->_debug("sock_to_host(): Host address was not in the format of host:port");
622
623 return FALSE;
624 }
625
626 if(@($this->host_dead[$host] && $this->host_dead[$host] > $now) ||
627 @($this->host_dead[$conn[0]] && $this->host_dead[$conn[0]] > $now))
628 {
629 $this->errno = MC_ERR_HOST_DEAD;
630 $this->errstr = "Host $host is not available.";
631
632 if($this->debug)
633 $this->_debug("sock_to_host(): Host $host is not available.");
634
635 return FALSE;
636 }
637
638 // connect to the server, if it fails, add it to the host_dead below
639 $sock = socket_create (AF_INET, SOCK_STREAM, getprotobyname("TCP"));
640
641 // we need surpress the error message if a connection fails
642 if(!@socket_connect($sock, $conn[0], $conn[1]))
643 {
644 $this->host_dead[$host]=$this->host_dead[$conn[0]]=$now+60+intval(rand(0, 10));
645
646 $this->errno = MC_ERR_SOCKET_CONNECT;
647 $this->errstr = "Failed to connect to ".$conn[0].":".$conn[1];
648
649 if($this->debug)
650 $this->_debug("sock_to_host(): Failed to connect to ".$conn[0].":".$conn[1]);
651
652 return FALSE;
653 }
654
655 // success, add to the list of sockets
656 $cache_sock[$host] = $sock;
657
658 return $sock;
659 }
660
661
662 /**
663 * retrieves the socket associated with a key
664 * Possible errors set are:
665 * MC_ERR_NOT_ACTIVE
666 * MC_ERR_GET_SOCK
667 *
668 * @access private
669 * @param string $key the key to retrieve the socket from
670 * @return resource the socket of the connection, else FALSE
671 */
672 function get_sock($key)
673 {
674 if(!$this->active)
675 {
676 $this->errno = MC_ERR_NOT_ACTIVE;
677 $this->errstr = "No active servers are available";
678
679 if($this->debug)
680 $this->_debug("get_sock(): There are no active servers available.");
681
682 return FALSE;
683 }
684
685 $hv = is_array($key) ? intval($key[0]) : $this->_hashfunc($key);
686
687 if(!$this->buckets)
688 {
689 $bu = $this->buckets = array();
690
691 foreach($this->servers as $v)
692 {
693 if(is_array($v))
694 {
695 for($i = 1; $i <= $v[1]; ++$i)
696 $bu[] = $v[0];
697 }
698 else
699 $bu[] = $v;
700 }
701
702 $this->buckets = $bu;
703 }
704
705 $real_key = is_array($key) ? $key[1] : $key;
706 $tries = 0;
707 while($tries < 20)
708 {
709 $host = @$this->buckets[$hv % count($this->buckets)];
710 $sock = $this->sock_to_host($host);
711
712 if(is_resource($sock))
713 return $sock;
714
715 $hv += $this->_hashfunc($tries.$real_key);
716 ++$tries;
717 }
718
719 $this->errno = MC_ERR_GET_SOCK;
720 $this->errstr = "Unable to retrieve a valid socket.";
721
722 if($this->debug)
723 $this->_debug("get_sock(): Unable to retrieve a valid socket.");
724
725 return FALSE;
726 }
727
728
729 /**
730 * increments or decrements a numerical value in memcached. this function is
731 * called from incr() and decr()
732 * ONLY WORKS WITH NUMERIC VALUES
733 * Possible errors set are:
734 * MC_ERR_NOT_ACTIVE
735 * MC_ERR_GET_SOCK
736 * MC_ERR_SOCKET_WRITE
737 * MC_ERR_SOCKET_READ
738 *
739 * @access private
740 * @param string $cmdname the command to send, either incr or decr
741 * @param string $key the key to perform the command on
742 * @param mixed $value the value to incr or decr the key value by
743 * @return int the new value of the key, FALSE if something went wrong
744 */
745 function _incrdecr($cmdname, $key, $value)
746 {
747 if(!$this->active)
748 {
749 $this->errno = MC_ERR_NOT_ACTIVE;
750 $this->errstr = "No active servers are available";
751
752 if($this->debug)
753 $this->_debug("_incrdecr(): There are no active servers available.");
754
755 return FALSE;
756 }
757
758 $sock = $this->get_sock($key);
759 if(!is_resource($sock))
760 {
761 $this->errno = MC_ERR_GET_SOCK;
762 $this->errstr = "Unable to retrieve a valid socket.";
763
764 if($this->debug)
765 $this->_debug("_incrdecr(): Invalid socket returned by get_sock().");
766
767 return FALSE;
768 }
769
770 if($value == "")
771 $value = 1;
772
773 $cmd = "$cmdname $key $value\r\n";
774 $cmd_len = strlen($cmd);
775 $offset = 0;
776
777 // write the command to the server
778 while($offset < $cmd_len)
779 {
780 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
781
782 if($result !== FALSE)
783 $offset += $result;
784 else if($offset < $cmd_len)
785 {
786 $this->errno = MC_ERR_SOCKET_WRITE;
787 $this->errstr = "Failed to write to socket.";
788
789 if($this->debug)
790 {
791 $sockerr = socket_last_error($sock);
792 $this->_debug("_incrdecr(): socket_write() returned FALSE. Error $errno: ".socket_strerror($sockerr));
793 }
794
795 return FALSE;
796 }
797 }
798
799 // now read the server's response
800 if(($retval = socket_read($sock, MC_BUFFER_SZ, PHP_NORMAL_READ)) === FALSE)
801 {
802 $this->errno = MC_ERR_SOCKET_READ;
803 $this->errstr = "Failed to read from socket.";
804
805 if($this->debug)
806 {
807 $sockerr = socket_last_error($sock);
808 $this->_debug("_incrdecr(): socket_read() returned FALSE. Socket Error $errno: ".socket_strerror($sockerr));
809 }
810
811 return FALSE;
812 }
813
814 // strip the /r/n from the end and return value
815 return trim($retval);
816 }
817
818 /**
819 * sends the command to the server
820 * Possible errors set are:
821 * MC_ERR_NOT_ACTIVE
822 * MC_ERR_GET_SOCK
823 * MC_ERR_SOCKET_WRITE
824 * MC_ERR_SOCKET_READ
825 * MC_ERR_SET
826 *
827 * @access private
828 * @param string $cmdname the command to send, either incr or decr
829 * @param string $key the key to perform the command on
830 * @param mixed $value the value to set the key to
831 * @param timestamp $exptime expiration time of the key
832 * @return bool TRUE on success, else FALSE
833 */
834 function _set($cmdname, $key, $val, $exptime = 0)
835 {
836 if(!$this->active)
837 {
838 $this->errno = MC_ERR_NOT_ACTIVE;
839 $this->errstr = "No active servers are available";
840
841 if($this->debug)
842 $this->_debug("_set(): No active servers are available.");
843
844 return FALSE;
845 }
846
847 $sock = $this->get_sock($key);
848 if(!is_resource($sock))
849 {
850 $this->errno = MC_ERR_GET_SOCK;
851 $this->errstr = "Unable to retrieve a valid socket.";
852
853 if($this->debug)
854 $this->_debug("_set(): Invalid socket returned by get_sock().");
855
856 return FALSE;
857 }
858
859 $flags = 0;
860 $key = is_array($key) ? $key[1] : $key;
861
862 $raw_val = $val;
863
864 // if the value is not scalar, we need to serialize it
865 if(!is_scalar($val))
866 {
867 $val = serialize($val);
868 $flags |= 1;
869 }
870
871 if (($this->compress_active) && ($this->compress > 0) && (strlen($val) > $this->compress)) {
872 $this->_debug("_set(): compressing data. size in:".strlen($val));
873 $cval=gzcompress($val);
874 $this->_debug("_set(): done compressing data. size out:".strlen($cval));
875 if ((strlen($cval) < strlen($val)) && (strlen($val) - strlen($cval) > 2048)){
876 $flags |= 2;
877 $val=$cval;
878 }
879 unset($cval);
880 }
881
882 $len = strlen($val);
883 if (!is_int($exptime))
884 $exptime = 0;
885
886 // send off the request
887 $cmd = "$cmdname $key $flags $exptime $len\r\n$val\r\n";
888 $cmd_len = strlen($cmd);
889 $offset = 0;
890
891 // write the command to the server
892 while($offset < $cmd_len)
893 {
894 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
895
896 if($result !== FALSE)
897 $offset += $result;
898 else if($offset < $cmd_len)
899 {
900 $this->errno = MC_ERR_SOCKET_WRITE;
901 $this->errstr = "Failed to write to socket.";
902
903 if($this->debug)
904 {
905 $errno = socket_last_error($sock);
906 $this->_debug("_set(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno));
907 }
908
909 return FALSE;
910 }
911 }
912
913 // now read the server's response
914 if(($l_szResponse = socket_read($sock, 6, PHP_NORMAL_READ)) === FALSE)
915 {
916 $this->errno = MC_ERR_SOCKET_READ;
917 $this->errstr = "Failed to read from socket.";
918
919 if($this->debug)
920 {
921 $errno = socket_last_error($sock);
922 $this->_debug("_set(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno));
923 }
924
925 return FALSE;
926 }
927
928 if($l_szResponse == "STORED")
929 {
930 if($this->debug)
931 $this->_debug("MemCache: $cmdname $key = $raw_val");
932
933 return TRUE;
934 }
935
936 $this->errno = MC_ERR_SET;
937 $this->errstr = "Failed to receive the STORED response from the server.";
938
939 if($this->debug)
940 $this->_debug("_set(): Did not receive STORED as the server response! Received $l_szResponse instead.");
941
942 return FALSE;
943 }
944
945
946 /**
947 * retrieves the value, and returns it unserialized
948 * Possible errors set are:
949 * MC_ERR_SOCKET_WRITE
950 * MC_ERR_SOCKET_READ
951 * MC_ERR_GET_KEY
952 * MC_ERR_LOADITEM_END
953 * MC_ERR_LOADITEM_BYTES
954 *
955 * @access private
956 * @param resource $sock the socket to connection we are retriving from
957 * @param array $val reference to the values retrieved
958 * @param mixed $sock_keys either a string or an array of keys to retrieve
959 * @return array TRUE on success, else FALSE
960 */
961 function _load_items($sock, &$val, $sock_keys)
962 {
963 $val = array();
964 $cmd = "get ";
965
966 if(!is_array($sock_keys))
967 {
968 $arr[] = $sock_keys;
969 $sock_keys = $arr;
970 }
971
972 foreach($sock_keys as $sk)
973 $cmd .= $sk." ";
974
975 $cmd .="\r\n";
976 $cmd_len = strlen($cmd);
977 $offset = 0;
978
979 // write the command to the server
980 while($offset < $cmd_len)
981 {
982 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
983
984 if($result !== FALSE)
985 $offset += $result;
986 else if($offset < $cmd_len)
987 {
988 $this->errno = MC_ERR_SOCKET_WRITE;
989 $this->errstr = "Failed to write to socket.";
990
991 if($this->debug)
992 {
993 $errno = socket_last_error($sock);
994 $this->_debug("_load_items(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno));
995 }
996
997 return FALSE;
998 }
999 }
1000
1001 $len = 0;
1002 $buf = "";
1003 $flags_array = array();
1004
1005 // now read the response from the server
1006 while($line = socket_read($sock, MC_BUFFER_SZ, PHP_BINARY_READ))
1007 {
1008 // check for a socket_read error
1009 if($line === FALSE)
1010 {
1011 $this->errno = MC_ERR_SOCKET_READ;
1012 $this->errstr = "Failed to read from socket.";
1013
1014 if($this->debug)
1015 {
1016 $errno = socket_last_error($sock);
1017 $this->_debug("_load_items(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno));
1018 }
1019
1020 return FALSE;
1021 }
1022
1023 if($len == 0)
1024 {
1025 $header = substr($line, 0, strpos($line, "\r\n"));
1026 $matches = explode(" ", $header);
1027
1028 if(is_string($matches[1]) && is_numeric($matches[2]) && is_numeric($matches[3]))
1029 {
1030 $rk = $matches[1];
1031 $flags = $matches[2];
1032 $len = $matches[3];
1033
1034 if($flags)
1035 $flags_array[$rk] = $flags;
1036
1037 $len_array[$rk] = $len;
1038 $bytes_read = 0;
1039
1040 // get the left over data after the header is read
1041 $line = substr($line, strpos($line, "\r\n")+2, strlen($line));
1042 }
1043 else
1044 {
1045 $this->errno = MC_ERR_GET_KEY;
1046 $this->errstr = "Requested key(s) returned no values.";
1047
1048 // something went wrong, we never recieved the header
1049 if($this->debug)
1050 $this->_debug("_load_items(): Requested key(s) returned no values.");
1051
1052 return FALSE;
1053 }
1054 }
1055
1056 // skip over the extra return or newline
1057 if($line == "\r" || $line == "\n")
1058 continue;
1059
1060 $bytes_read += strlen($line);
1061 $buf .= $line;
1062
1063 // we read the all of the data, take in account
1064 // for the /r/nEND/r/n
1065 if($bytes_read == ($len + 7))
1066 {
1067 $end = substr($buf, $len+2, 3);
1068 if($end == "END")
1069 {
1070 $val[$rk] = substr($buf, 0, $len);
1071
1072 foreach($sock_keys as $sk)
1073 {
1074 if(!isset($val[$sk]))
1075 continue;
1076
1077 if(strlen($val[$sk]) != $len_array[$sk])
1078 continue;
1079 if(@$flags_array[$sk] & 2)
1080 $val[$sk] = gzuncompress($val[$sk]);
1081
1082 if(@$flags_array[$sk] & 1)
1083 $val[$sk] = unserialize($val[$sk]);
1084 }
1085
1086 return TRUE;
1087 }
1088 else
1089 {
1090 $this->errno = MC_ERR_LOADITEM_END;
1091 $this->errstr = "Failed to receive END response from server.";
1092
1093 if($this->debug)
1094 $this->_debug("_load_items(): Failed to receive END. Received $end instead.");
1095
1096 return FALSE;
1097 }
1098 }
1099
1100 // take in consideration for the "\r\nEND\r\n"
1101 if($bytes_read > ($len + 7))
1102 {
1103 $this->errno = MC_ERR_LOADITEM_BYTES;
1104 $this->errstr = "Bytes read from server greater than size of data.";
1105
1106 if($this->debug)
1107 $this->_debug("_load_items(): Bytes read is greater than requested data size.");
1108
1109 return FALSE;
1110 }
1111
1112 }
1113 }
1114
1115
1116 /**
1117 * creates our hash
1118 *
1119 * @access private
1120 * @param int $num
1121 * @return hash
1122 */
1123 function _hashfunc($num)
1124 {
1125 $hash = sprintf("%u",crc32($num));
1126
1127 return $hash;
1128 }
1129
1130 /**
1131 * function that can be overridden to handle debug output
1132 * by default debug info is print to the screen
1133 *
1134 * @access private
1135 * @param $text string to output debug info
1136 */
1137 function _debug($text)
1138 {
1139 print $text . "\r\n";
1140 }
1141 }
1142
1143 ?>