250 $this->_debug = isset(
$args[
'debug'] ) ?
$args[
'debug'] :
false;
251 $this->stats =
array();
252 $this->_compress_threshold = isset(
$args[
'compress_threshold'] ) ?
$args[
'compress_threshold'] : 0;
253 $this->_persistent = isset(
$args[
'persistent'] ) ?
$args[
'persistent'] :
false;
254 $this->_compress_enable =
true;
255 $this->_have_zlib = function_exists(
'gzcompress' );
257 $this->_cache_sock =
array();
258 $this->_host_dead =
array();
260 $this->_timeout_seconds = 0;
261 $this->_timeout_microseconds = isset(
$args[
'timeout'] ) ?
$args[
'timeout'] : 500000;
263 $this->_connect_timeout = isset(
$args[
'connect_timeout'] ) ?
$args[
'connect_timeout'] : 0.1;
264 $this->_connect_attempts = 2;
284 public function add( $key, $val, $exp = 0 ) {
285 return $this->
_set(
'add', $key, $val, $exp );
299 public function decr( $key, $amt = 1 ) {
300 return $this->
_incrdecr(
'decr', $key, $amt );
314 public function delete( $key,
$time = 0 ) {
315 if ( !$this->_active ) {
320 if ( !is_resource( $sock ) ) {
324 $key = is_array( $key ) ? $key[1] : $key;
326 if ( isset( $this->stats[
'delete'] ) ) {
327 $this->stats[
'delete']++;
329 $this->stats[
'delete'] = 1;
331 $cmd =
"delete $key $time\r\n";
332 if ( !$this->
_fwrite( $sock, $cmd ) ) {
337 if ( $this->_debug ) {
341 if (
$res ==
"DELETED" ||
$res ==
"NOT_FOUND" ) {
353 public function lock( $key, $timeout = 0 ) {
362 public function unlock( $key ) {
374 foreach ( $this->_cache_sock
as $sock ) {
378 $this->_cache_sock =
array();
390 $this->_compress_enable = $enable;
400 $this->_host_dead =
array();
414 public function get( $key, &$casToken = null ) {
417 if ( $this->_debug ) {
421 if ( !$this->_active ) {
428 if ( !is_resource( $sock ) ) {
433 $key = is_array( $key ) ? $key[1] : $key;
434 if ( isset( $this->stats[
'get'] ) ) {
435 $this->stats[
'get']++;
437 $this->stats[
'get'] = 1;
440 $cmd =
"gets $key\r\n";
441 if ( !$this->
_fwrite( $sock, $cmd ) ) {
449 if ( $this->_debug ) {
450 foreach ( $val
as $k => $v ) {
451 $this->
_debugprint( sprintf(
"MemCache: sock %s got %s\n", serialize( $sock ), $k ) );
456 if ( isset( $val[$key] ) ) {
474 if ( !$this->_active ) {
478 if ( isset( $this->stats[
'get_multi'] ) ) {
479 $this->stats[
'get_multi']++;
481 $this->stats[
'get_multi'] = 1;
483 $sock_keys =
array();
487 if ( !is_resource( $sock ) ) {
490 $key = is_array( $key ) ? $key[1] : $key;
491 if ( !isset( $sock_keys[$sock] ) ) {
492 $sock_keys[intval( $sock )] =
array();
495 $sock_keys[intval( $sock )][] = $key;
500 foreach ( $socks
as $sock ) {
502 foreach ( $sock_keys[intval( $sock )]
as $key ) {
507 if ( $this->
_fwrite( $sock, $cmd ) ) {
514 foreach ( $gather
as $sock ) {
518 if ( $this->_debug ) {
519 foreach ( $val
as $k => $v ) {
520 $this->
_debugprint( sprintf(
"MemCache: got %s\n", $k ) );
540 public function incr( $key, $amt = 1 ) {
541 return $this->
_incrdecr(
'incr', $key, $amt );
577 if ( !is_resource( $sock ) ) {
581 if ( !$this->
_fwrite( $sock, $cmd ) ) {
589 if ( preg_match(
'/^END/',
$res ) ) {
592 if ( strlen(
$res ) == 0 ) {
616 public function set( $key,
$value, $exp = 0 ) {
617 return $this->
_set(
'set', $key,
$value, $exp );
638 public function cas( $casToken, $key,
$value, $exp = 0 ) {
639 return $this->
_set(
'cas', $key,
$value, $exp, $casToken );
651 $this->_compress_threshold = $thresh;
665 $this->_debug = $dbg;
679 $this->_servers = $list;
680 $this->_active = count( $list );
681 $this->_buckets =
null;
682 $this->_bucketcount = 0;
684 $this->_single_sock =
null;
685 if ( $this->_active == 1 ) {
686 $this->_single_sock = $this->_servers[0];
696 public function set_timeout( $seconds, $microseconds ) {
697 $this->_timeout_seconds = $seconds;
698 $this->_timeout_microseconds = $microseconds;
714 $host = array_search( $sock, $this->_cache_sock );
715 fclose( $this->_cache_sock[$host] );
716 unset( $this->_cache_sock[$host] );
732 list( $ip, $port ) = preg_split(
'/:(?=\d)/', $host );
735 $errno = $errstr =
null;
738 if ( $this->_persistent == 1 ) {
739 $sock = pfsockopen( $ip, $port, $errno, $errstr, $timeout );
741 $sock = fsockopen( $ip, $port, $errno, $errstr, $timeout );
746 $this->
_error_log(
"Error connecting to $host: $errstr\n" );
752 stream_set_timeout( $sock, $this->_timeout_seconds, $this->_timeout_microseconds );
756 if ( $this->_persistent ) {
773 $host = array_search( $sock, $this->_cache_sock );
781 $parts = explode(
':', $host );
783 $this->_host_dead[$ip] = time() + 30 + intval( rand( 0, 10 ) );
784 $this->_host_dead[$host] = $this->_host_dead[$ip];
785 unset( $this->_cache_sock[$host] );
800 if ( !$this->_active ) {
804 if ( $this->_single_sock !==
null ) {
808 $hv = is_array( $key ) ? intval( $key[0] ) : $this->
_hashfunc( $key );
809 if ( $this->_buckets ===
null ) {
811 foreach ( $this->_servers
as $v ) {
812 if ( is_array( $v ) ) {
813 for ( $i = 0; $i < $v[1]; $i++ ) {
820 $this->_buckets = $bu;
821 $this->_bucketcount = count( $bu );
824 $realkey = is_array( $key ) ? $key[1] : $key;
825 for ( $tries = 0; $tries < 20; $tries++ ) {
828 if ( is_resource( $sock ) ) {
831 $hv = $this->
_hashfunc( $hv . $realkey );
849 # Hash function must be in [0,0x7ffffff]
850 # We take the first 31 bits of the MD5 hash, which unlike the hash
851 # function used in a previous version of this client, works
852 return hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
868 function _incrdecr( $cmd, $key, $amt = 1 ) {
869 if ( !$this->_active ) {
874 if ( !is_resource( $sock ) ) {
878 $key = is_array( $key ) ? $key[1] : $key;
879 if ( isset( $this->stats[$cmd] ) ) {
880 $this->stats[$cmd]++;
882 $this->stats[$cmd] = 1;
884 if ( !$this->
_fwrite( $sock,
"$cmd $key $amt\r\n" ) ) {
890 if ( !preg_match(
'/^(\d+)/',
$line, $match ) ) {
913 $decl = $this->
_fgets( $sock );
915 if ( $decl ===
false ) {
921 } elseif ( preg_match(
'/^VALUE (\S+) (\d+) (\d+) (\d+)$/', $decl, $match ) ) {
934 $this->
_fread( $sock, $match[3] + 2 ),
936 } elseif ( $decl ==
"END" ) {
937 if ( count( $results ) == 0 ) {
945 foreach ( $results
as $vars ) {
948 if ( $data ===
false || substr( $data, -2 ) !==
"\r\n" ) {
950 'line ending missing from data block from $1' );
953 $data = substr( $data, 0, -2 );
956 if ( $this->_have_zlib &&
$flags & self::COMPRESSED ) {
957 $ret[$rkey] = gzuncompress(
$ret[$rkey] );
969 if (
$flags & self::SERIALIZED ) {
970 $ret[$rkey] = unserialize(
$ret[$rkey] );
976 $this->
_handle_error( $sock,
'Error parsing response from $1' );
1001 function _set( $cmd, $key, $val, $exp, $casToken =
null ) {
1002 if ( !$this->_active ) {
1007 if ( !is_resource( $sock ) ) {
1011 if ( isset( $this->stats[$cmd] ) ) {
1012 $this->stats[$cmd]++;
1014 $this->stats[$cmd] = 1;
1019 if ( !is_scalar( $val ) ) {
1020 $val = serialize( $val );
1022 if ( $this->_debug ) {
1023 $this->
_debugprint( sprintf(
"client: serializing data as it is not scalar\n" ) );
1027 $len = strlen( $val );
1029 if ( $this->_have_zlib && $this->_compress_enable
1030 && $this->_compress_threshold && $len >= $this->_compress_threshold
1032 $c_val = gzcompress( $val, 9 );
1033 $c_len = strlen( $c_val );
1035 if ( $c_len < $len * ( 1 - self::COMPRESSION_SAVINGS ) ) {
1036 if ( $this->_debug ) {
1037 $this->
_debugprint( sprintf(
"client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len ) );
1045 $command =
"$cmd $key $flags $exp $len";
1050 if ( !$this->
_fwrite( $sock,
"$command\r\n$val\r\n" ) ) {
1056 if ( $this->_debug ) {
1059 if (
$line ==
"STORED" ) {
1077 if ( isset( $this->_cache_sock[$host] ) ) {
1078 return $this->_cache_sock[$host];
1083 list( $ip, ) = explode(
':', $host );
1084 if ( isset( $this->_host_dead[$host] ) && $this->_host_dead[$host] > $now ||
1085 isset( $this->_host_dead[$ip] ) && $this->_host_dead[$ip] > $now
1095 stream_set_write_buffer( $sock, 0 );
1097 $this->_cache_sock[$host] = $sock;
1099 return $this->_cache_sock[$host];
1113 wfDebugLog(
'memcached-serious',
"Memcached error: $text" );
1123 function _fwrite( $sock, $buf ) {
1125 $bufSize = strlen( $buf );
1126 while ( $bytesWritten < $bufSize ) {
1127 $result = fwrite( $sock, $buf );
1128 $data = stream_get_meta_data( $sock );
1129 if ( $data[
'timed_out'] ) {
1148 $peer = stream_socket_get_name( $sock,
true );
1149 if ( strval( $peer ) ===
'' ) {
1150 $peer = array_search( $sock, $this->_cache_sock );
1151 if ( $peer ===
false ) {
1152 $peer =
'[unknown host]';
1155 $msg = str_replace(
'$1', $peer, $msg );
1168 function _fread( $sock, $len ) {
1170 while ( $len > 0 ) {
1171 $result = fread( $sock, $len );
1172 $data = stream_get_meta_data( $sock );
1173 if ( $data[
'timed_out'] ) {
1178 $this->
_handle_error( $sock,
'error reading buffer from $1' );
1183 $this->
_handle_error( $sock,
'unexpected end of file reading from $1' );
1199 function _fgets( $sock ) {
1204 $data = stream_get_meta_data( $sock );
1205 if ( $data[
'timed_out'] ) {
1206 $this->
_handle_error( $sock,
'timeout reading line from $1' );
1210 $this->
_handle_error( $sock,
'error reading line from $1' );
1215 } elseif ( substr(
$result, -1 ) ===
"\n" ) {
1218 $this->
_handle_error( $sock,
'line ending missing in response from $1' );
1229 if ( !is_resource(
$f ) ) {
1235 $n = stream_select( $r, $w,
$e, 0, 0 );
1236 while (
$n == 1 && !feof(
$f ) ) {
1241 $n = stream_select( $r, $w,
$e, 0, 0 );