Main Page | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages

MemCachedClient.inc.php

Go to the documentation of this file.
00001 <?php 00002 /* 00003 * MemCached PHP client 00004 * Copyright (c) 2003 00005 * Ryan Gilfether <hotrodder@rocketmail.com> 00006 * http://www.gilfether.com 00007 * 00008 * Originally translated from Brad Fitzpatrick's <brad@danga.com> MemCached Perl client 00009 * See the memcached website: 00010 * http://www.danga.com/memcached/ 00011 * 00012 * This module is Copyright (c) 2003 Ryan Gilfether. 00013 * All rights reserved. 00014 * You may distribute under the terms of the GNU General Public License 00015 * This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND. 00016 * 00017 */ 00018 00022 define("MC_VERSION", "1.0.10"); 00027 define("MC_BUFFER_SZ", 1024); 00031 define("MC_ERR_NOT_ACTIVE", 1001); // no active servers 00032 define("MC_ERR_SOCKET_WRITE", 1002); // socket_write() failed 00033 define("MC_ERR_SOCKET_READ", 1003); // socket_read() failed 00034 define("MC_ERR_SOCKET_CONNECT", 1004); // failed to connect to host 00035 define("MC_ERR_DELETE", 1005); // delete() did not recieve DELETED command 00036 define("MC_ERR_HOST_FORMAT", 1006); // sock_to_host() invalid host format 00037 define("MC_ERR_HOST_DEAD", 1007); // sock_to_host() host is dead 00038 define("MC_ERR_GET_SOCK", 1008); // get_sock() failed to find a valid socket 00039 define("MC_ERR_SET", 1009); // _set() failed to receive the STORED response 00040 define("MC_ERR_GET_KEY", 1010); // _load_items no values returned for key(s) 00041 define("MC_ERR_LOADITEM_END", 1011); // _load_items failed to receive END response 00042 define("MC_ERR_LOADITEM_BYTES", 1012); // _load_items bytes read larger than bytes available 00043 00044 00056 class MemCachedClient 00057 { 00062 var $host_dead; 00067 var $cache_sock; 00072 var $debug; 00077 var $servers; 00082 var $active; 00087 var $errno; 00092 var $errstr; 00097 var $compress = 1; 00102 var $comp_active = 1; 00103 00108 var $bucket; 00109 00110 00129 function MemCachedClient($options = 0) 00130 { 00131 if(is_array($options)) 00132 { 00133 $this->set_servers($options["servers"]); 00134 $this->debug = $options["debug"]; 00135 $this->compress = $options["compress"]; 00136 $this->cache_sock = array(); 00137 } 00138 00139 $this->errno = 0; 00140 $this->errstr = ""; 00141 } 00142 00143 00151 function set_servers($servers) 00152 { 00153 $this->servers = $servers; 00154 $this->active = count($this->servers); 00155 } 00156 00157 00165 function set_debug($do_debug) 00166 { 00167 $this->debug = $do_debug; 00168 } 00169 00170 00176 function forget_dead_hosts() 00177 { 00178 unset($this->host_dead); 00179 } 00180 00181 00187 function disconnect_all() 00188 { 00189 foreach($this->cache_sock as $sock) 00190 socket_close($sock); 00191 00192 unset($this->cache_sock); 00193 $this->active = 0; 00194 } 00195 00196 00221 function delete($key, $time = 0) 00222 { 00223 if(!$this->active) 00224 { 00225 $this->errno = MC_ERR_NOT_ACTIVE; 00226 $this->errstr = "No active servers are available"; 00227 00228 if($this->debug) 00229 $this->_debug("delete(): There are no active servers available."); 00230 00231 return FALSE; 00232 } 00233 00234 $sock = $this->get_sock($key); 00235 00236 if(!is_resource($sock)) 00237 { 00238 $this->errno = MC_ERR_GET_SOCK; 00239 $this->errstr = "Unable to retrieve a valid socket."; 00240 00241 if($this->debug) 00242 $this->_debug("delete(): get_sock() returned an invalid socket."); 00243 00244 return FALSE; 00245 } 00246 00247 if(is_array($key)) 00248 $key = $key[1]; 00249 00250 $cmd = "delete $key $time\r\n"; 00251 $cmd_len = strlen($cmd); 00252 $offset = 0; 00253 00254 // now send the command 00255 while($offset < $cmd_len) 00256 { 00257 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ); 00258 00259 if($result !== FALSE) 00260 $offset += $result; 00261 else if($offset < $cmd_len) 00262 { 00263 $this->errno = MC_ERR_SOCKET_WRITE; 00264 $this->errstr = "Failed to write to socket."; 00265 00266 if($this->debug) 00267 { 00268 $sockerr = socket_last_error($sock); 00269 $this->_debug("delete(): socket_write() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr)); 00270 } 00271 00272 return FALSE; 00273 } 00274 } 00275 00276 // now read the server's response 00277 if(($retval = socket_read($sock, MC_BUFFER_SZ, PHP_NORMAL_READ)) === FALSE) 00278 { 00279 $this->errno = MC_ERR_SOCKET_READ; 00280 $this->errstr = "Failed to read from socket."; 00281 00282 if($this->debug) 00283 { 00284 $sockerr = socket_last_error($sock); 00285 $this->_debug("delete(): socket_read() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr)); 00286 } 00287 00288 return FALSE; 00289 } 00290 00291 // remove the \r\n from the end 00292 $retval = rtrim($retval); 00293 00294 // now read the server's response 00295 if($retval == "DELETED") 00296 return TRUE; 00297 else 00298 { 00299 // something went wrong, create the error 00300 $this->errno = MC_ERR_DELETE; 00301 $this->errstr = "Failed to receive DELETED response from server."; 00302 00303 if($this->debug) 00304 $this->_debug("delete(): Failed to receive DELETED response from server. Received $retval instead."); 00305 00306 return FALSE; 00307 } 00308 } 00309 00310 00326 function add($key, $val, $exptime = 0) 00327 { 00328 return $this->_set("add", $key, $val, $exptime); 00329 } 00330 00331 00348 function replace($key, $val, $exptime = 0) 00349 { 00350 return $this->_set("replace", $key, $val, $exptime); 00351 } 00352 00353 00372 function set($key, $val, $exptime = 0) 00373 { 00374 return $this->_set("set", $key, $val, $exptime); 00375 } 00376 00377 00393 function get($key) 00394 { 00395 $val =& $this->get_multi($key); 00396 00397 if(!$val) 00398 { 00399 $this->errno = MC_ERR_GET_KEY; 00400 $this->errstr = "No value found for key $key"; 00401 00402 if($this->debug) 00403 $this->_debug("get(): No value found for key $key"); 00404 00405 return FALSE; 00406 } 00407 00408 return $val[$key]; 00409 } 00410 00411 00421 function get_multi($keys) 00422 { 00423 $sock_keys = array(); 00424 $socks = array(); 00425 $val = 0; 00426 00427 if(!$this->active) 00428 { 00429 $this->errno = MC_ERR_NOT_ACTIVE; 00430 $this->errstr = "No active servers are available"; 00431 00432 if($this->debug) 00433 $this->_debug("get_multi(): There are no active servers available."); 00434 00435 return FALSE; 00436 } 00437 00438 if(!is_array($keys)) 00439 { 00440 $arr[] = $keys; 00441 $keys = $arr; 00442 } 00443 00444 foreach($keys as $k) 00445 { 00446 $sock = $this->get_sock($k); 00447 00448 if($sock) 00449 { 00450 $k = is_array($k) ? $k[1] : $k; 00451 00452 if(@!is_array($sock_keys[$sock])) 00453 $sock_keys[$sock] = array(); 00454 00455 // if $sock_keys[$sock] doesn't exist, create it 00456 if(!$sock_keys[$sock]) 00457 $socks[] = $sock; 00458 00459 $sock_keys[$sock][] = $k; 00460 } 00461 } 00462 00463 if(!is_array($socks)) 00464 { 00465 $arr[] = $socks; 00466 $socks = $arr; 00467 } 00468 00469 foreach($socks as $s) 00470 { 00471 $this->_load_items($s, $val, $sock_keys[$sock]); 00472 } 00473 00474 if($this->debug) 00475 { 00476 while(list($k, $v) = @each($val)) 00477 $this->_debug("MemCache: got $k = $v\n"); 00478 } 00479 00480 return $val; 00481 } 00482 00483 00502 function incr($key, $value = 1) 00503 { 00504 return $this->_incrdecr("incr", $key, $value); 00505 } 00506 00507 00524 function decr($key, $value = 1) 00525 { 00526 return $this->_incrdecr("decr", $key, $value); 00527 } 00528 00529 00538 function error() 00539 { 00540 return $this->errno; 00541 } 00542 00543 00551 function error_string() 00552 { 00553 return $this->errstr; 00554 } 00555 00556 00562 function error_clear() 00563 { 00564 // reset to no error 00565 $this->errno = 0; 00566 $this->errstr = ""; 00567 } 00568 00569 00577 function set_compression($setting=1) { 00578 if ($setting != 0) { 00579 $this->comp_active = 1; 00580 } else { 00581 $this->comp_active = 0; 00582 } 00583 } 00584 00585 00586 00587 /* 00588 * PRIVATE FUNCTIONS 00589 */ 00590 00591 00606 function sock_to_host($host) 00607 { 00608 if(is_array($host)) 00609 $host = array_shift($host); 00610 00611 $now = time(); 00612 00613 // seperate the ip from the port, index 0 = ip, index 1 = port 00614 $conn = explode(":", $host); 00615 if(count($conn) != 2) 00616 { 00617 $this->errno = MC_ERR_HOST_FORMAT; 00618 $this->errstr = "Host address was not in the format of host:port"; 00619 00620 if($this->debug) 00621 $this->_debug("sock_to_host(): Host address was not in the format of host:port"); 00622 00623 return FALSE; 00624 } 00625 00626 if(@($this->host_dead[$host] && $this->host_dead[$host] > $now) || 00627 @($this->host_dead[$conn[0]] && $this->host_dead[$conn[0]] > $now)) 00628 { 00629 $this->errno = MC_ERR_HOST_DEAD; 00630 $this->errstr = "Host $host is not available."; 00631 00632 if($this->debug) 00633 $this->_debug("sock_to_host(): Host $host is not available."); 00634 00635 return FALSE; 00636 } 00637 00638 // connect to the server, if it fails, add it to the host_dead below 00639 $sock = socket_create (AF_INET, SOCK_STREAM, getprotobyname("TCP")); 00640 00641 // we need surpress the error message if a connection fails 00642 if(!@socket_connect($sock, $conn[0], $conn[1])) 00643 { 00644 $this->host_dead[$host]=$this->host_dead[$conn[0]]=$now+60+intval(rand(0, 10)); 00645 00646 $this->errno = MC_ERR_SOCKET_CONNECT; 00647 $this->errstr = "Failed to connect to ".$conn[0].":".$conn[1]; 00648 00649 if($this->debug) 00650 $this->_debug("sock_to_host(): Failed to connect to ".$conn[0].":".$conn[1]); 00651 00652 return FALSE; 00653 } 00654 00655 // success, add to the list of sockets 00656 $cache_sock[$host] = $sock; 00657 00658 return $sock; 00659 } 00660 00661 00672 function get_sock($key) 00673 { 00674 if(!$this->active) 00675 { 00676 $this->errno = MC_ERR_NOT_ACTIVE; 00677 $this->errstr = "No active servers are available"; 00678 00679 if($this->debug) 00680 $this->_debug("get_sock(): There are no active servers available."); 00681 00682 return FALSE; 00683 } 00684 00685 $hv = is_array($key) ? intval($key[0]) : $this->_hashfunc($key); 00686 00687 if(!$this->buckets) 00688 { 00689 $bu = $this->buckets = array(); 00690 00691 foreach($this->servers as $v) 00692 { 00693 if(is_array($v)) 00694 { 00695 for($i = 1; $i <= $v[1]; ++$i) 00696 $bu[] = $v[0]; 00697 } 00698 else 00699 $bu[] = $v; 00700 } 00701 00702 $this->buckets = $bu; 00703 } 00704 00705 $real_key = is_array($key) ? $key[1] : $key; 00706 $tries = 0; 00707 while($tries < 20) 00708 { 00709 $host = @$this->buckets[$hv % count($this->buckets)]; 00710 $sock = $this->sock_to_host($host); 00711 00712 if(is_resource($sock)) 00713 return $sock; 00714 00715 $hv += $this->_hashfunc($tries.$real_key); 00716 ++$tries; 00717 } 00718 00719 $this->errno = MC_ERR_GET_SOCK; 00720 $this->errstr = "Unable to retrieve a valid socket."; 00721 00722 if($this->debug) 00723 $this->_debug("get_sock(): Unable to retrieve a valid socket."); 00724 00725 return FALSE; 00726 } 00727 00728 00745 function _incrdecr($cmdname, $key, $value) 00746 { 00747 if(!$this->active) 00748 { 00749 $this->errno = MC_ERR_NOT_ACTIVE; 00750 $this->errstr = "No active servers are available"; 00751 00752 if($this->debug) 00753 $this->_debug("_incrdecr(): There are no active servers available."); 00754 00755 return FALSE; 00756 } 00757 00758 $sock = $this->get_sock($key); 00759 if(!is_resource($sock)) 00760 { 00761 $this->errno = MC_ERR_GET_SOCK; 00762 $this->errstr = "Unable to retrieve a valid socket."; 00763 00764 if($this->debug) 00765 $this->_debug("_incrdecr(): Invalid socket returned by get_sock()."); 00766 00767 return FALSE; 00768 } 00769 00770 if($value == "") 00771 $value = 1; 00772 00773 $cmd = "$cmdname $key $value\r\n"; 00774 $cmd_len = strlen($cmd); 00775 $offset = 0; 00776 00777 // write the command to the server 00778 while($offset < $cmd_len) 00779 { 00780 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ); 00781 00782 if($result !== FALSE) 00783 $offset += $result; 00784 else if($offset < $cmd_len) 00785 { 00786 $this->errno = MC_ERR_SOCKET_WRITE; 00787 $this->errstr = "Failed to write to socket."; 00788 00789 if($this->debug) 00790 { 00791 $sockerr = socket_last_error($sock); 00792 $this->_debug("_incrdecr(): socket_write() returned FALSE. Error $errno: ".socket_strerror($sockerr)); 00793 } 00794 00795 return FALSE; 00796 } 00797 } 00798 00799 // now read the server's response 00800 if(($retval = socket_read($sock, MC_BUFFER_SZ, PHP_NORMAL_READ)) === FALSE) 00801 { 00802 $this->errno = MC_ERR_SOCKET_READ; 00803 $this->errstr = "Failed to read from socket."; 00804 00805 if($this->debug) 00806 { 00807 $sockerr = socket_last_error($sock); 00808 $this->_debug("_incrdecr(): socket_read() returned FALSE. Socket Error $errno: ".socket_strerror($sockerr)); 00809 } 00810 00811 return FALSE; 00812 } 00813 00814 // strip the /r/n from the end and return value 00815 return trim($retval); 00816 } 00817 00834 function _set($cmdname, $key, $val, $exptime = 0) 00835 { 00836 if(!$this->active) 00837 { 00838 $this->errno = MC_ERR_NOT_ACTIVE; 00839 $this->errstr = "No active servers are available"; 00840 00841 if($this->debug) 00842 $this->_debug("_set(): No active servers are available."); 00843 00844 return FALSE; 00845 } 00846 00847 $sock = $this->get_sock($key); 00848 if(!is_resource($sock)) 00849 { 00850 $this->errno = MC_ERR_GET_SOCK; 00851 $this->errstr = "Unable to retrieve a valid socket."; 00852 00853 if($this->debug) 00854 $this->_debug("_set(): Invalid socket returned by get_sock()."); 00855 00856 return FALSE; 00857 } 00858 00859 $flags = 0; 00860 $key = is_array($key) ? $key[1] : $key; 00861 00862 $raw_val = $val; 00863 00864 // if the value is not scalar, we need to serialize it 00865 if(!is_scalar($val)) 00866 { 00867 $val = serialize($val); 00868 $flags |= 1; 00869 } 00870 00871 if (($this->compress_active) && ($this->compress > 0) && (strlen($val) > $this->compress)) { 00872 $this->_debug("_set(): compressing data. size in:".strlen($val)); 00873 $cval=gzcompress($val); 00874 $this->_debug("_set(): done compressing data. size out:".strlen($cval)); 00875 if ((strlen($cval) < strlen($val)) && (strlen($val) - strlen($cval) > 2048)){ 00876 $flags |= 2; 00877 $val=$cval; 00878 } 00879 unset($cval); 00880 } 00881 00882 $len = strlen($val); 00883 if (!is_int($exptime)) 00884 $exptime = 0; 00885 00886 // send off the request 00887 $cmd = "$cmdname $key $flags $exptime $len\r\n$val\r\n"; 00888 $cmd_len = strlen($cmd); 00889 $offset = 0; 00890 00891 // write the command to the server 00892 while($offset < $cmd_len) 00893 { 00894 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ); 00895 00896 if($result !== FALSE) 00897 $offset += $result; 00898 else if($offset < $cmd_len) 00899 { 00900 $this->errno = MC_ERR_SOCKET_WRITE; 00901 $this->errstr = "Failed to write to socket."; 00902 00903 if($this->debug) 00904 { 00905 $errno = socket_last_error($sock); 00906 $this->_debug("_set(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno)); 00907 } 00908 00909 return FALSE; 00910 } 00911 } 00912 00913 // now read the server's response 00914 if(($l_szResponse = socket_read($sock, 6, PHP_NORMAL_READ)) === FALSE) 00915 { 00916 $this->errno = MC_ERR_SOCKET_READ; 00917 $this->errstr = "Failed to read from socket."; 00918 00919 if($this->debug) 00920 { 00921 $errno = socket_last_error($sock); 00922 $this->_debug("_set(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno)); 00923 } 00924 00925 return FALSE; 00926 } 00927 00928 if($l_szResponse == "STORED") 00929 { 00930 if($this->debug) 00931 $this->_debug("MemCache: $cmdname $key = $raw_val"); 00932 00933 return TRUE; 00934 } 00935 00936 $this->errno = MC_ERR_SET; 00937 $this->errstr = "Failed to receive the STORED response from the server."; 00938 00939 if($this->debug) 00940 $this->_debug("_set(): Did not receive STORED as the server response! Received $l_szResponse instead."); 00941 00942 return FALSE; 00943 } 00944 00945 00961 function _load_items($sock, &$val, $sock_keys) 00962 { 00963 $val = array(); 00964 $cmd = "get "; 00965 00966 if(!is_array($sock_keys)) 00967 { 00968 $arr[] = $sock_keys; 00969 $sock_keys = $arr; 00970 } 00971 00972 foreach($sock_keys as $sk) 00973 $cmd .= $sk." "; 00974 00975 $cmd .="\r\n"; 00976 $cmd_len = strlen($cmd); 00977 $offset = 0; 00978 00979 // write the command to the server 00980 while($offset < $cmd_len) 00981 { 00982 $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ); 00983 00984 if($result !== FALSE) 00985 $offset += $result; 00986 else if($offset < $cmd_len) 00987 { 00988 $this->errno = MC_ERR_SOCKET_WRITE; 00989 $this->errstr = "Failed to write to socket."; 00990 00991 if($this->debug) 00992 { 00993 $errno = socket_last_error($sock); 00994 $this->_debug("_load_items(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno)); 00995 } 00996 00997 return FALSE; 00998 } 00999 } 01000 01001 $len = 0; 01002 $buf = ""; 01003 $flags_array = array(); 01004 01005 // now read the response from the server 01006 while($line = socket_read($sock, MC_BUFFER_SZ, PHP_BINARY_READ)) 01007 { 01008 // check for a socket_read error 01009 if($line === FALSE) 01010 { 01011 $this->errno = MC_ERR_SOCKET_READ; 01012 $this->errstr = "Failed to read from socket."; 01013 01014 if($this->debug) 01015 { 01016 $errno = socket_last_error($sock); 01017 $this->_debug("_load_items(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno)); 01018 } 01019 01020 return FALSE; 01021 } 01022 01023 if($len == 0) 01024 { 01025 $header = substr($line, 0, strpos($line, "\r\n")); 01026 $matches = explode(" ", $header); 01027 01028 if(is_string($matches[1]) && is_numeric($matches[2]) && is_numeric($matches[3])) 01029 { 01030 $rk = $matches[1]; 01031 $flags = $matches[2]; 01032 $len = $matches[3]; 01033 01034 if($flags) 01035 $flags_array[$rk] = $flags; 01036 01037 $len_array[$rk] = $len; 01038 $bytes_read = 0; 01039 01040 // get the left over data after the header is read 01041 $line = substr($line, strpos($line, "\r\n")+2, strlen($line)); 01042 } 01043 else 01044 { 01045 $this->errno = MC_ERR_GET_KEY; 01046 $this->errstr = "Requested key(s) returned no values."; 01047 01048 // something went wrong, we never recieved the header 01049 if($this->debug) 01050 $this->_debug("_load_items(): Requested key(s) returned no values."); 01051 01052 return FALSE; 01053 } 01054 } 01055 01056 // skip over the extra return or newline 01057 if($line == "\r" || $line == "\n") 01058 continue; 01059 01060 $bytes_read += strlen($line); 01061 $buf .= $line; 01062 01063 // if we're almost at the end, read the rest, so 01064 // that we don't corrupt the \r\nEND\r\n 01065 if ($bytes_read >= $len && $bytes_read < ($len +7)) 01066 { 01067 $lastbit = socket_read($sock, $len - $bytes_read + 7, PHP_BINARY_READ); 01068 $line .= $lastbit; 01069 $buf .= $lastbit; 01070 $bytes_read += strlen($lastbit); 01071 } 01072 01073 // we read the all of the data, take in account 01074 // for the /r/nEND/r/n 01075 if($bytes_read == ($len + 7)) 01076 { 01077 $end = substr($buf, $len+2, 3); 01078 if($end == "END") 01079 { 01080 $val[$rk] = substr($buf, 0, $len); 01081 01082 foreach($sock_keys as $sk) 01083 { 01084 if(!isset($val[$sk])) 01085 continue; 01086 01087 if(strlen($val[$sk]) != $len_array[$sk]) 01088 continue; 01089 if(@$flags_array[$sk] & 2) 01090 $val[$sk] = gzuncompress($val[$sk]); 01091 01092 if(@$flags_array[$sk] & 1) 01093 $val[$sk] = unserialize($val[$sk]); 01094 } 01095 01096 return TRUE; 01097 } 01098 else 01099 { 01100 $this->errno = MC_ERR_LOADITEM_END; 01101 $this->errstr = "Failed to receive END response from server."; 01102 01103 if($this->debug) 01104 $this->_debug("_load_items(): Failed to receive END. Received $end instead."); 01105 01106 return FALSE; 01107 } 01108 } 01109 01110 // take in consideration for the "\r\nEND\r\n" 01111 if($bytes_read > ($len + 7)) 01112 { 01113 $this->errno = MC_ERR_LOADITEM_BYTES; 01114 $this->errstr = "Bytes read from server greater than size of data."; 01115 01116 if($this->debug) 01117 $this->_debug("_load_items(): Bytes read is greater than requested data size."); 01118 01119 return FALSE; 01120 } 01121 01122 } 01123 } 01124 01125 01133 function _hashfunc($num) 01134 { 01135 $hash = sprintf("%u",crc32($num)); 01136 01137 return $hash; 01138 } 01139 01147 function _debug($text) 01148 { 01149 print $text . "\r\n"; 01150 } 01151 } 01152 01153 ?>

Generated on Tue Jun 29 23:40:05 2004 for Mediawiki by doxygen 1.3.7