00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00022 define(
"MC_VERSION",
"1.0.10");
00027 define(
"MC_BUFFER_SZ", 1024);
00031 define(
"MC_ERR_NOT_ACTIVE", 1001);
00032 define(
"MC_ERR_SOCKET_WRITE", 1002);
00033 define(
"MC_ERR_SOCKET_READ", 1003);
00034 define(
"MC_ERR_SOCKET_CONNECT", 1004);
00035 define(
"MC_ERR_DELETE", 1005);
00036 define(
"MC_ERR_HOST_FORMAT", 1006);
00037 define(
"MC_ERR_HOST_DEAD", 1007);
00038 define(
"MC_ERR_GET_SOCK", 1008);
00039 define(
"MC_ERR_SET", 1009);
00040 define(
"MC_ERR_GET_KEY", 1010);
00041 define(
"MC_ERR_LOADITEM_END", 1011);
00042 define(
"MC_ERR_LOADITEM_BYTES", 1012);
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
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
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
00292 $retval = rtrim($retval);
00293
00294
00295
if($retval ==
"DELETED")
00296
return TRUE;
00297
else
00298 {
00299
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
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
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
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
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
00639
$sock = socket_create (AF_INET, SOCK_STREAM, getprotobyname(
"TCP"));
00640
00641
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
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
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
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
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
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
00887 $cmd =
"$cmdname $key $flags $exptime $len\r\n$val\r\n";
00888 $cmd_len = strlen($cmd);
00889 $offset = 0;
00890
00891
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
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
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
01006
while(
$line = socket_read($sock,
MC_BUFFER_SZ, PHP_BINARY_READ))
01007 {
01008
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
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
01049
if($this->debug)
01050 $this->
_debug(
"_load_items(): Requested key(s) returned no values.");
01051
01052
return FALSE;
01053 }
01054 }
01055
01056
01057
if(
$line ==
"\r" ||
$line ==
"\n")
01058
continue;
01059
01060 $bytes_read += strlen($line);
01061 $buf .=
$line;
01062
01063
01064
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
01074
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
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 ?>