+Enable Atom support for that script that isn't migrated to Querypage.php class yet.
[mediawiki.git] / includes / MemCachedClient.inc.php
blobb21c0e70cca4250f9e299abbd5722f1518dbe4e5
1 <?php
2 /*
3 * MemCached PHP client
4 * Copyright (c) 2003
5 * Ryan Gilfether <hotrodder@rocketmail.com>
6 * http://www.gilfether.com
8 * Originally translated from Brad Fitzpatrick's <brad@danga.com> MemCached Perl client
9 * See the memcached website:
10 * http://www.danga.com/memcached/
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.
19 /**
20 * version string
22 define("MC_VERSION", "1.0.10");
23 /**
24 * int, buffer size used for sending and receiving
25 * data from sockets
27 define("MC_BUFFER_SZ", 1024);
28 /**
29 * MemCached error numbers
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
45 /**
46 * MemCached PHP client Class.
48 * Communicates with the MemCached server, and executes the MemCached protocol
49 * MemCached available at http://www.danga.com/memcached
51 * @author Ryan Gilfether <ryan@gilfether.com>
52 * @package MemCachedClient
53 * @access public
54 * @version 1.0.10
56 class MemCachedClient
58 /**
59 * array of servers no long available
60 * @var array
62 var $host_dead;
63 /**
64 * array of open sockets
65 * @var array
67 var $cache_sock;
68 /**
69 * determine if debugging is either on or off
70 * @var bool
72 var $debug;
73 /**
74 * array of servers to attempt to use, "host:port" string format
75 * @var array
77 var $servers;
78 /**
79 * count of currently active connections to servers
80 * @var int
82 var $active;
83 /**
84 * error code if one is set
85 * @var int
87 var $errno;
88 /**
89 * string describing error
90 * @var string
92 var $errstr;
93 /**
94 * size of val to force compression; 0 turns off; defaults 1
95 * @ var int
97 var $compress = 1;
98 /**
99 * temp flag to turn compression on/off; defaults on
100 * @ var int
102 var $comp_active = 1;
105 * array that contains parsed out buckets
106 * @ var array
108 var $bucket;
112 * Constructor
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
125 * @access public
126 * @param array $option an array of servers and debug status
127 * @return object MemCachedClient the new MemCachedClient object
129 function MemCachedClient($options = 0)
131 if(is_array($options))
133 $this->set_servers($options["servers"]);
134 $this->debug = $options["debug"];
135 $this->compress = $options["compress"];
136 $this->cache_sock = array();
139 $this->errno = 0;
140 $this->errstr = "";
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
148 * @access public
149 * @param array $servers array of servers in the format described in the constructor
151 function set_servers($servers)
153 $this->servers = $servers;
154 $this->active = count($this->servers);
159 * if $do_debug is set to true, will print out
160 * debugging info, else debug is turned off
162 * @access public
163 * @param bool $do_debug set to true to turn debugging on, false to turn off
165 function set_debug($do_debug)
167 $this->debug = $do_debug;
172 * remove all cached hosts that are no longer good
174 * @access public
176 function forget_dead_hosts()
178 unset($this->host_dead);
183 * disconnects from all servers
185 * @access public
187 function disconnect_all()
189 foreach($this->cache_sock as $sock)
190 socket_close($sock);
192 unset($this->cache_sock);
193 $this->active = 0;
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
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
221 function delete($key, $time = 0)
223 if(!$this->active)
225 $this->errno = MC_ERR_NOT_ACTIVE;
226 $this->errstr = "No active servers are available";
228 if($this->debug)
229 $this->_debug("delete(): There are no active servers available.");
231 return FALSE;
234 $sock = $this->get_sock($key);
236 if(!is_resource($sock))
238 $this->errno = MC_ERR_GET_SOCK;
239 $this->errstr = "Unable to retrieve a valid socket.";
241 if($this->debug)
242 $this->_debug("delete(): get_sock() returned an invalid socket.");
244 return FALSE;
247 if(is_array($key))
248 $key = $key[1];
250 $cmd = "delete $key $time\r\n";
251 $cmd_len = strlen($cmd);
252 $offset = 0;
254 // now send the command
255 while($offset < $cmd_len)
257 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
259 if($result !== FALSE)
260 $offset += $result;
261 else if($offset < $cmd_len)
263 $this->errno = MC_ERR_SOCKET_WRITE;
264 $this->errstr = "Failed to write to socket.";
266 if($this->debug)
268 $sockerr = socket_last_error($sock);
269 $this->_debug("delete(): socket_write() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr));
272 return FALSE;
276 // now read the server's response
277 if(($retval = socket_read($sock, MC_BUFFER_SZ, PHP_NORMAL_READ)) === FALSE)
279 $this->errno = MC_ERR_SOCKET_READ;
280 $this->errstr = "Failed to read from socket.";
282 if($this->debug)
284 $sockerr = socket_last_error($sock);
285 $this->_debug("delete(): socket_read() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr));
288 return FALSE;
291 // remove the \r\n from the end
292 $retval = rtrim($retval);
294 // now read the server's response
295 if($retval == "DELETED")
296 return TRUE;
297 else
299 // something went wrong, create the error
300 $this->errno = MC_ERR_DELETE;
301 $this->errstr = "Failed to receive DELETED response from server.";
303 if($this->debug)
304 $this->_debug("delete(): Failed to receive DELETED response from server. Received $retval instead.");
306 return FALSE;
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
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
326 function add($key, $val, $exptime = 0)
328 return $this->_set("add", $key, $val, $exptime);
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
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
348 function replace($key, $val, $exptime = 0)
350 return $this->_set("replace", $key, $val, $exptime);
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
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
372 function set($key, $val, $exptime = 0)
374 return $this->_set("set", $key, $val, $exptime);
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
389 * @access public
390 * @param string $key the key to retrieve
391 * @return mixed the value of the key, FALSE on error
393 function get($key)
395 $val =& $this->get_multi($key);
397 if(!$val)
399 $this->errno = MC_ERR_GET_KEY;
400 $this->errstr = "No value found for key $key";
402 if($this->debug)
403 $this->_debug("get(): No value found for key $key");
405 return FALSE;
408 return $val[$key];
413 * just like get(), but takes an array of keys, returns FALSE on error
414 * Possible errors set are:
415 * MC_ERR_NOT_ACTIVE
417 * @access public
418 * @param array $keys the keys to retrieve
419 * @return array the value of each key, FALSE on error
421 function get_multi($keys)
423 $sock_keys = array();
424 $socks = array();
425 $val = 0;
427 if(!$this->active)
429 $this->errno = MC_ERR_NOT_ACTIVE;
430 $this->errstr = "No active servers are available";
432 if($this->debug)
433 $this->_debug("get_multi(): There are no active servers available.");
435 return FALSE;
438 if(!is_array($keys))
440 $arr[] = $keys;
441 $keys = $arr;
444 foreach($keys as $k)
446 $sock = $this->get_sock($k);
448 if($sock)
450 $k = is_array($k) ? $k[1] : $k;
452 if(@!is_array($sock_keys[$sock]))
453 $sock_keys[$sock] = array();
455 // if $sock_keys[$sock] doesn't exist, create it
456 if(!$sock_keys[$sock])
457 $socks[] = $sock;
459 $sock_keys[$sock][] = $k;
463 if(!is_array($socks))
465 $arr[] = $socks;
466 $socks = $arr;
469 foreach($socks as $s)
471 $this->_load_items($s, $val, $sock_keys[$sock]);
474 if($this->debug)
476 while(list($k, $v) = @each($val))
477 $this->_debug("MemCache: got $k = $v\n");
480 return $val;
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
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
502 function incr($key, $value = 1)
504 return $this->_incrdecr("incr", $key, $value);
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
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
524 function decr($key, $value = 1)
526 return $this->_incrdecr("decr", $key, $value);
531 * When a function returns FALSE, an error code is set.
532 * This funtion will return the error code.
533 * See error_string()
535 * @access public
536 * @return int the value of the last error code
538 function error()
540 return $this->errno;
545 * Returns a string describing the error set in error()
546 * See error()
548 * @access public
549 * @return int a string describing the error code given
551 function error_string()
553 return $this->errstr;
558 * Resets the error number and error string
560 * @access public
562 function error_clear()
564 // reset to no error
565 $this->errno = 0;
566 $this->errstr = "";
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)
577 function set_compression($setting=1) {
578 if ($setting != 0) {
579 $this->comp_active = 1;
580 } else {
581 $this->comp_active = 0;
588 * PRIVATE FUNCTIONS
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
602 * @access private
603 * @param mixed $host either an array or a string
604 * @return resource the socket of the new connection, else FALSE
606 function sock_to_host($host)
608 if(is_array($host))
609 $host = array_shift($host);
611 $now = time();
613 // seperate the ip from the port, index 0 = ip, index 1 = port
614 $conn = explode(":", $host);
615 if(count($conn) != 2)
617 $this->errno = MC_ERR_HOST_FORMAT;
618 $this->errstr = "Host address was not in the format of host:port";
620 if($this->debug)
621 $this->_debug("sock_to_host(): Host address was not in the format of host:port");
623 return FALSE;
626 if(@($this->host_dead[$host] && $this->host_dead[$host] > $now) ||
627 @($this->host_dead[$conn[0]] && $this->host_dead[$conn[0]] > $now))
629 $this->errno = MC_ERR_HOST_DEAD;
630 $this->errstr = "Host $host is not available.";
632 if($this->debug)
633 $this->_debug("sock_to_host(): Host $host is not available.");
635 return FALSE;
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"));
641 // we need surpress the error message if a connection fails
642 if(!@socket_connect($sock, $conn[0], $conn[1]))
644 $this->host_dead[$host]=$this->host_dead[$conn[0]]=$now+60+intval(rand(0, 10));
646 $this->errno = MC_ERR_SOCKET_CONNECT;
647 $this->errstr = "Failed to connect to ".$conn[0].":".$conn[1];
649 if($this->debug)
650 $this->_debug("sock_to_host(): Failed to connect to ".$conn[0].":".$conn[1]);
652 return FALSE;
655 // success, add to the list of sockets
656 $cache_sock[$host] = $sock;
658 return $sock;
663 * retrieves the socket associated with a key
664 * Possible errors set are:
665 * MC_ERR_NOT_ACTIVE
666 * MC_ERR_GET_SOCK
668 * @access private
669 * @param string $key the key to retrieve the socket from
670 * @return resource the socket of the connection, else FALSE
672 function get_sock($key)
674 if(!$this->active)
676 $this->errno = MC_ERR_NOT_ACTIVE;
677 $this->errstr = "No active servers are available";
679 if($this->debug)
680 $this->_debug("get_sock(): There are no active servers available.");
682 return FALSE;
685 $hv = is_array($key) ? intval($key[0]) : $this->_hashfunc($key);
687 if(!$this->buckets)
689 $bu = $this->buckets = array();
691 foreach($this->servers as $v)
693 if(is_array($v))
695 for($i = 1; $i <= $v[1]; ++$i)
696 $bu[] = $v[0];
698 else
699 $bu[] = $v;
702 $this->buckets = $bu;
705 $real_key = is_array($key) ? $key[1] : $key;
706 $tries = 0;
707 while($tries < 20)
709 $host = @$this->buckets[$hv % count($this->buckets)];
710 $sock = $this->sock_to_host($host);
712 if(is_resource($sock))
713 return $sock;
715 $hv += $this->_hashfunc($tries.$real_key);
716 ++$tries;
719 $this->errno = MC_ERR_GET_SOCK;
720 $this->errstr = "Unable to retrieve a valid socket.";
722 if($this->debug)
723 $this->_debug("get_sock(): Unable to retrieve a valid socket.");
725 return FALSE;
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
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
745 function _incrdecr($cmdname, $key, $value)
747 if(!$this->active)
749 $this->errno = MC_ERR_NOT_ACTIVE;
750 $this->errstr = "No active servers are available";
752 if($this->debug)
753 $this->_debug("_incrdecr(): There are no active servers available.");
755 return FALSE;
758 $sock = $this->get_sock($key);
759 if(!is_resource($sock))
761 $this->errno = MC_ERR_GET_SOCK;
762 $this->errstr = "Unable to retrieve a valid socket.";
764 if($this->debug)
765 $this->_debug("_incrdecr(): Invalid socket returned by get_sock().");
767 return FALSE;
770 if($value == "")
771 $value = 1;
773 $cmd = "$cmdname $key $value\r\n";
774 $cmd_len = strlen($cmd);
775 $offset = 0;
777 // write the command to the server
778 while($offset < $cmd_len)
780 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
782 if($result !== FALSE)
783 $offset += $result;
784 else if($offset < $cmd_len)
786 $this->errno = MC_ERR_SOCKET_WRITE;
787 $this->errstr = "Failed to write to socket.";
789 if($this->debug)
791 $sockerr = socket_last_error($sock);
792 $this->_debug("_incrdecr(): socket_write() returned FALSE. Error $errno: ".socket_strerror($sockerr));
795 return FALSE;
799 // now read the server's response
800 if(($retval = socket_read($sock, MC_BUFFER_SZ, PHP_NORMAL_READ)) === FALSE)
802 $this->errno = MC_ERR_SOCKET_READ;
803 $this->errstr = "Failed to read from socket.";
805 if($this->debug)
807 $sockerr = socket_last_error($sock);
808 $this->_debug("_incrdecr(): socket_read() returned FALSE. Socket Error $errno: ".socket_strerror($sockerr));
811 return FALSE;
814 // strip the /r/n from the end and return value
815 return trim($retval);
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
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
834 function _set($cmdname, $key, $val, $exptime = 0)
836 if(!$this->active)
838 $this->errno = MC_ERR_NOT_ACTIVE;
839 $this->errstr = "No active servers are available";
841 if($this->debug)
842 $this->_debug("_set(): No active servers are available.");
844 return FALSE;
847 $sock = $this->get_sock($key);
848 if(!is_resource($sock))
850 $this->errno = MC_ERR_GET_SOCK;
851 $this->errstr = "Unable to retrieve a valid socket.";
853 if($this->debug)
854 $this->_debug("_set(): Invalid socket returned by get_sock().");
856 return FALSE;
859 $flags = 0;
860 $key = is_array($key) ? $key[1] : $key;
862 $raw_val = $val;
864 // if the value is not scalar, we need to serialize it
865 if(!is_scalar($val))
867 $val = serialize($val);
868 $flags |= 1;
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;
879 unset($cval);
882 $len = strlen($val);
883 if (!is_int($exptime))
884 $exptime = 0;
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;
891 // write the command to the server
892 while($offset < $cmd_len)
894 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
896 if($result !== FALSE)
897 $offset += $result;
898 else if($offset < $cmd_len)
900 $this->errno = MC_ERR_SOCKET_WRITE;
901 $this->errstr = "Failed to write to socket.";
903 if($this->debug)
905 $errno = socket_last_error($sock);
906 $this->_debug("_set(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno));
909 return FALSE;
913 // now read the server's response
914 if(($l_szResponse = socket_read($sock, 6, PHP_NORMAL_READ)) === FALSE)
916 $this->errno = MC_ERR_SOCKET_READ;
917 $this->errstr = "Failed to read from socket.";
919 if($this->debug)
921 $errno = socket_last_error($sock);
922 $this->_debug("_set(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno));
925 return FALSE;
928 if($l_szResponse == "STORED")
930 if($this->debug)
931 $this->_debug("MemCache: $cmdname $key = $raw_val");
933 return TRUE;
936 $this->errno = MC_ERR_SET;
937 $this->errstr = "Failed to receive the STORED response from the server.";
939 if($this->debug)
940 $this->_debug("_set(): Did not receive STORED as the server response! Received $l_szResponse instead.");
942 return FALSE;
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
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
961 function _load_items($sock, &$val, $sock_keys)
963 $val = array();
964 $cmd = "get ";
966 if(!is_array($sock_keys))
968 $arr[] = $sock_keys;
969 $sock_keys = $arr;
972 foreach($sock_keys as $sk)
973 $cmd .= $sk." ";
975 $cmd .="\r\n";
976 $cmd_len = strlen($cmd);
977 $offset = 0;
979 // write the command to the server
980 while($offset < $cmd_len)
982 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
984 if($result !== FALSE)
985 $offset += $result;
986 else if($offset < $cmd_len)
988 $this->errno = MC_ERR_SOCKET_WRITE;
989 $this->errstr = "Failed to write to socket.";
991 if($this->debug)
993 $errno = socket_last_error($sock);
994 $this->_debug("_load_items(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno));
997 return FALSE;
1001 $len = 0;
1002 $buf = "";
1003 $flags_array = array();
1005 // now read the response from the server
1006 while($line = socket_read($sock, MC_BUFFER_SZ, PHP_BINARY_READ))
1008 // check for a socket_read error
1009 if($line === FALSE)
1011 $this->errno = MC_ERR_SOCKET_READ;
1012 $this->errstr = "Failed to read from socket.";
1014 if($this->debug)
1016 $errno = socket_last_error($sock);
1017 $this->_debug("_load_items(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno));
1020 return FALSE;
1023 if($len == 0)
1025 $header = substr($line, 0, strpos($line, "\r\n"));
1026 $matches = explode(" ", $header);
1028 if(is_string($matches[1]) && is_numeric($matches[2]) && is_numeric($matches[3]))
1030 $rk = $matches[1];
1031 $flags = $matches[2];
1032 $len = $matches[3];
1034 if($flags)
1035 $flags_array[$rk] = $flags;
1037 $len_array[$rk] = $len;
1038 $bytes_read = 0;
1040 // get the left over data after the header is read
1041 $line = substr($line, strpos($line, "\r\n")+2, strlen($line));
1043 else
1045 $this->errno = MC_ERR_GET_KEY;
1046 $this->errstr = "Requested key(s) returned no values.";
1048 // something went wrong, we never recieved the header
1049 if($this->debug)
1050 $this->_debug("_load_items(): Requested key(s) returned no values.");
1052 return FALSE;
1056 // skip over the extra return or newline
1057 if($line == "\r" || $line == "\n")
1058 continue;
1060 $bytes_read += strlen($line);
1061 $buf .= $line;
1063 // if we're almost at the end, read the rest, so
1064 // that we don't corrupt the \r\nEND\r\n
1065 if ($bytes_read >= $len && $bytes_read < ($len +7))
1067 $lastbit = socket_read($sock, $len - $bytes_read + 7, PHP_BINARY_READ);
1068 $line .= $lastbit;
1069 $buf .= $lastbit;
1070 $bytes_read += strlen($lastbit);
1073 // we read the all of the data, take in account
1074 // for the /r/nEND/r/n
1075 if($bytes_read == ($len + 7))
1077 $end = substr($buf, $len+2, 3);
1078 if($end == "END")
1080 $val[$rk] = substr($buf, 0, $len);
1082 foreach($sock_keys as $sk)
1084 if(!isset($val[$sk]))
1085 continue;
1087 if(strlen($val[$sk]) != $len_array[$sk])
1088 continue;
1089 if(@$flags_array[$sk] & 2)
1090 $val[$sk] = gzuncompress($val[$sk]);
1092 if(@$flags_array[$sk] & 1)
1093 $val[$sk] = unserialize($val[$sk]);
1096 return TRUE;
1098 else
1100 $this->errno = MC_ERR_LOADITEM_END;
1101 $this->errstr = "Failed to receive END response from server.";
1103 if($this->debug)
1104 $this->_debug("_load_items(): Failed to receive END. Received $end instead.");
1106 return FALSE;
1110 // take in consideration for the "\r\nEND\r\n"
1111 if($bytes_read > ($len + 7))
1113 $this->errno = MC_ERR_LOADITEM_BYTES;
1114 $this->errstr = "Bytes read from server greater than size of data.";
1116 if($this->debug)
1117 $this->_debug("_load_items(): Bytes read is greater than requested data size.");
1119 return FALSE;
1127 * creates our hash
1129 * @access private
1130 * @param int $num
1131 * @return hash
1133 function _hashfunc($num)
1135 $hash = sprintf("%u",crc32($num));
1137 return $hash;
1141 * function that can be overridden to handle debug output
1142 * by default debug info is print to the screen
1144 * @access private
1145 * @param $text string to output debug info
1147 function _debug($text)
1149 print $text . "\r\n";