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