MediaWiki  master
MemcachedClient.php
Go to the documentation of this file.
1 <?php
2 // phpcs:ignoreFile -- It's an external lib and it isn't. Let's not bother.
71 
72 // {{{ class MemcachedClient
80  // {{{ properties
81  // {{{ public
82 
83  // {{{ constants
84  // {{{ flags
85 
89  const SERIALIZED = 1;
90 
94  const COMPRESSED = 2;
95 
99  const INTVAL = 4;
100 
101  // }}}
102 
106  const COMPRESSION_SAVINGS = 0.20;
107 
108  // }}}
109 
116  public $stats;
117 
118  // }}}
119  // {{{ private
120 
127  public $_cache_sock;
128 
135  public $_debug;
136 
143  public $_host_dead;
144 
151  public $_have_zlib;
152 
160 
168 
175  public $_persistent;
176 
184 
191  public $_servers;
192 
199  public $_buckets;
200 
208 
215  public $_active;
216 
224 
232 
237 
242 
246  private $_logger;
247 
248  // }}}
249  // }}}
250  // {{{ methods
251  // {{{ public functions
252  // {{{ memcached()
253 
259  public function __construct( $args ) {
260  $this->set_servers( $args['servers'] ?? array() );
261  $this->_debug = $args['debug'] ?? false;
262  $this->stats = array();
263  $this->_compress_threshold = $args['compress_threshold'] ?? 0;
264  $this->_persistent = $args['persistent'] ?? false;
265  $this->_compress_enable = true;
266  $this->_have_zlib = function_exists( 'gzcompress' );
267 
268  $this->_cache_sock = array();
269  $this->_host_dead = array();
270 
271  $this->_timeout_seconds = 0;
272  $this->_timeout_microseconds = $args['timeout'] ?? 500000;
273 
274  $this->_connect_timeout = $args['connect_timeout'] ?? 0.1;
275  $this->_connect_attempts = 2;
276 
277  $this->_logger = $args['logger'] ?? new NullLogger();
278  }
279 
280  // }}}
281 
286  public function serialize( $value ) {
287  return serialize( $value );
288  }
289 
294  public function unserialize( $value ) {
295  return unserialize( $value );
296  }
297 
298  // {{{ add()
299 
314  public function add( $key, $val, $exp = 0 ) {
315  return $this->_set( 'add', $key, $val, $exp );
316  }
317 
318  // }}}
319  // {{{ decr()
320 
329  public function decr( $key, $amt = 1 ) {
330  return $this->_incrdecr( 'decr', $key, $amt );
331  }
332 
333  // }}}
334  // {{{ delete()
335 
344  public function delete( $key, $time = 0 ) {
345  if ( !$this->_active ) {
346  return false;
347  }
348 
349  $sock = $this->get_sock( $key );
350  if ( !is_resource( $sock ) ) {
351  return false;
352  }
353 
354  $key = is_array( $key ) ? $key[1] : $key;
355 
356  if ( isset( $this->stats['delete'] ) ) {
357  $this->stats['delete']++;
358  } else {
359  $this->stats['delete'] = 1;
360  }
361  $cmd = "delete $key $time\r\n";
362  if ( !$this->_fwrite( $sock, $cmd ) ) {
363  return false;
364  }
365  $res = $this->_fgets( $sock );
366 
367  if ( $this->_debug ) {
368  $this->_debugprint( sprintf( "MemCache: delete %s (%s)", $key, $res ) );
369  }
370 
371  if ( $res == "DELETED" || $res == "NOT_FOUND" ) {
372  return true;
373  }
374 
375  return false;
376  }
377 
386  public function touch( $key, $time = 0 ) {
387  if ( !$this->_active ) {
388  return false;
389  }
390 
391  $sock = $this->get_sock( $key );
392  if ( !is_resource( $sock ) ) {
393  return false;
394  }
395 
396  $key = is_array( $key ) ? $key[1] : $key;
397 
398  if ( isset( $this->stats['touch'] ) ) {
399  $this->stats['touch']++;
400  } else {
401  $this->stats['touch'] = 1;
402  }
403  $cmd = "touch $key $time\r\n";
404  if ( !$this->_fwrite( $sock, $cmd ) ) {
405  return false;
406  }
407  $res = $this->_fgets( $sock );
408 
409  if ( $this->_debug ) {
410  $this->_debugprint( sprintf( "MemCache: touch %s (%s)", $key, $res ) );
411  }
412 
413  if ( $res == "TOUCHED" ) {
414  return true;
415  }
416 
417  return false;
418  }
419 
420  // }}}
421  // {{{ disconnect_all()
422 
426  public function disconnect_all() {
427  foreach ( $this->_cache_sock as $sock ) {
428  fclose( $sock );
429  }
430 
431  $this->_cache_sock = array();
432  }
433 
434  // }}}
435  // {{{ enable_compress()
436 
442  public function enable_compress( $enable ) {
443  $this->_compress_enable = $enable;
444  }
445 
446  // }}}
447  // {{{ forget_dead_hosts()
448 
452  public function forget_dead_hosts() {
453  $this->_host_dead = array();
454  }
455 
456  // }}}
457  // {{{ get()
458 
467  public function get( $key, &$casToken = null ) {
468  if ( $this->_debug ) {
469  $this->_debugprint( "get($key)" );
470  }
471 
472  if ( !is_array( $key ) && strval( $key ) === '' ) {
473  $this->_debugprint( "Skipping key which equals to an empty string" );
474  return false;
475  }
476 
477  if ( !$this->_active ) {
478  return false;
479  }
480 
481  $sock = $this->get_sock( $key );
482 
483  if ( !is_resource( $sock ) ) {
484  return false;
485  }
486 
487  $key = is_array( $key ) ? $key[1] : $key;
488  if ( isset( $this->stats['get'] ) ) {
489  $this->stats['get']++;
490  } else {
491  $this->stats['get'] = 1;
492  }
493 
494  $cmd = "gets $key\r\n";
495  if ( !$this->_fwrite( $sock, $cmd ) ) {
496  return false;
497  }
498 
499  $val = array();
500  $this->_load_items( $sock, $val, $casToken );
501 
502  if ( $this->_debug ) {
503  foreach ( $val as $k => $v ) {
504  $this->_debugprint(
505  sprintf( "MemCache: sock %s got %s", $this->serialize( $sock ), $k ) );
506  }
507  }
508 
509  $value = false;
510  if ( isset( $val[$key] ) ) {
511  $value = $val[$key];
512  }
513  return $value;
514  }
515 
516  // }}}
517  // {{{ get_multi()
518 
526  public function get_multi( $keys ) {
527  if ( !$this->_active ) {
528  return array();
529  }
530 
531  if ( isset( $this->stats['get_multi'] ) ) {
532  $this->stats['get_multi']++;
533  } else {
534  $this->stats['get_multi'] = 1;
535  }
536  $sock_keys = array();
537  $socks = array();
538  foreach ( $keys as $key ) {
539  $sock = $this->get_sock( $key );
540  if ( !is_resource( $sock ) ) {
541  continue;
542  }
543  $key = is_array( $key ) ? $key[1] : $key;
544  if ( !isset( $sock_keys[$sock] ) ) {
545  $sock_keys[intval( $sock )] = array();
546  $socks[] = $sock;
547  }
548  $sock_keys[intval( $sock )][] = $key;
549  }
550 
551  $gather = array();
552  // Send out the requests
553  foreach ( $socks as $sock ) {
554  $cmd = 'gets';
555  foreach ( $sock_keys[intval( $sock )] as $key ) {
556  $cmd .= ' ' . $key;
557  }
558  $cmd .= "\r\n";
559 
560  if ( $this->_fwrite( $sock, $cmd ) ) {
561  $gather[] = $sock;
562  }
563  }
564 
565  // Parse responses
566  $val = array();
567  foreach ( $gather as $sock ) {
568  $this->_load_items( $sock, $val, $casToken );
569  }
570 
571  if ( $this->_debug ) {
572  foreach ( $val as $k => $v ) {
573  $this->_debugprint( sprintf( "MemCache: got %s", $k ) );
574  }
575  }
576 
577  return $val;
578  }
579 
580  // }}}
581  // {{{ incr()
582 
593  public function incr( $key, $amt = 1 ) {
594  return $this->_incrdecr( 'incr', $key, $amt );
595  }
596 
597  // }}}
598  // {{{ replace()
599 
613  public function replace( $key, $value, $exp = 0 ) {
614  return $this->_set( 'replace', $key, $value, $exp );
615  }
616 
617  // }}}
618  // {{{ run_command()
619 
629  public function run_command( $sock, $cmd ) {
630  if ( !is_resource( $sock ) ) {
631  return array();
632  }
633 
634  if ( !$this->_fwrite( $sock, $cmd ) ) {
635  return array();
636  }
637 
638  $ret = array();
639  while ( true ) {
640  $res = $this->_fgets( $sock );
641  $ret[] = $res;
642  if ( preg_match( '/^END/', $res ) ) {
643  break;
644  }
645  if ( strlen( $res ) == 0 ) {
646  break;
647  }
648  }
649  return $ret;
650  }
651 
652  // }}}
653  // {{{ set()
654 
669  public function set( $key, $value, $exp = 0 ) {
670  return $this->_set( 'set', $key, $value, $exp );
671  }
672 
673  // }}}
674  // {{{ cas()
675 
691  public function cas( $casToken, $key, $value, $exp = 0 ) {
692  return $this->_set( 'cas', $key, $value, $exp, $casToken );
693  }
694 
695  // }}}
696  // {{{ set_compress_threshold()
697 
703  public function set_compress_threshold( $thresh ) {
704  $this->_compress_threshold = $thresh;
705  }
706 
707  // }}}
708  // {{{ set_debug()
709 
716  public function set_debug( $dbg ) {
717  $this->_debug = $dbg;
718  }
719 
720  // }}}
721  // {{{ set_servers()
722 
729  public function set_servers( $list ) {
730  $this->_servers = $list;
731  $this->_active = count( $list );
732  $this->_buckets = null;
733  $this->_bucketcount = 0;
734 
735  $this->_single_sock = null;
736  if ( $this->_active == 1 ) {
737  $this->_single_sock = $this->_servers[0];
738  }
739  }
740 
747  public function set_timeout( $seconds, $microseconds ) {
748  $this->_timeout_seconds = $seconds;
749  $this->_timeout_microseconds = $microseconds;
750  }
751 
752  // }}}
753  // }}}
754  // {{{ private methods
755  // {{{ _close_sock()
756 
764  function _close_sock( $sock ) {
765  $host = array_search( $sock, $this->_cache_sock );
766  fclose( $this->_cache_sock[$host] );
767  unset( $this->_cache_sock[$host] );
768  }
769 
770  // }}}
771  // {{{ _connect_sock()
772 
782  function _connect_sock( &$sock, $host ) {
783  list( $ip, $port ) = preg_split( '/:(?=\d)/', $host );
784  $sock = false;
785  $timeout = $this->_connect_timeout;
786  $errno = $errstr = null;
787  for ( $i = 0; !$sock && $i < $this->_connect_attempts; $i++ ) {
788  Wikimedia\suppressWarnings();
789  if ( $this->_persistent == 1 ) {
790  $sock = pfsockopen( $ip, $port, $errno, $errstr, $timeout );
791  } else {
792  $sock = fsockopen( $ip, $port, $errno, $errstr, $timeout );
793  }
794  Wikimedia\restoreWarnings();
795  }
796  if ( !$sock ) {
797  $this->_error_log( "Error connecting to $host: $errstr" );
798  $this->_dead_host( $host );
799  return false;
800  }
801 
802  // Initialise timeout
803  stream_set_timeout( $sock, $this->_timeout_seconds, $this->_timeout_microseconds );
804 
805  // If the connection was persistent, flush the read buffer in case there
806  // was a previous incomplete request on this connection
807  if ( $this->_persistent ) {
808  $this->_flush_read_buffer( $sock );
809  }
810  return true;
811  }
812 
813  // }}}
814  // {{{ _dead_sock()
815 
823  function _dead_sock( $sock ) {
824  $host = array_search( $sock, $this->_cache_sock );
825  $this->_dead_host( $host );
826  }
827 
831  function _dead_host( $host ) {
832  $ip = explode( ':', $host )[0];
833  $this->_host_dead[$ip] = time() + 30 + intval( rand( 0, 10 ) );
834  $this->_host_dead[$host] = $this->_host_dead[$ip];
835  unset( $this->_cache_sock[$host] );
836  }
837 
838  // }}}
839  // {{{ get_sock()
840 
849  function get_sock( $key ) {
850  if ( !$this->_active ) {
851  return false;
852  }
853 
854  if ( $this->_single_sock !== null ) {
855  return $this->sock_to_host( $this->_single_sock );
856  }
857 
858  $hv = is_array( $key ) ? intval( $key[0] ) : $this->_hashfunc( $key );
859  if ( $this->_buckets === null ) {
860  $bu = array();
861  foreach ( $this->_servers as $v ) {
862  if ( is_array( $v ) ) {
863  for ( $i = 0; $i < $v[1]; $i++ ) {
864  $bu[] = $v[0];
865  }
866  } else {
867  $bu[] = $v;
868  }
869  }
870  $this->_buckets = $bu;
871  $this->_bucketcount = count( $bu );
872  }
873 
874  $realkey = is_array( $key ) ? $key[1] : $key;
875  for ( $tries = 0; $tries < 20; $tries++ ) {
876  $host = $this->_buckets[$hv % $this->_bucketcount];
877  $sock = $this->sock_to_host( $host );
878  if ( is_resource( $sock ) ) {
879  return $sock;
880  }
881  $hv = $this->_hashfunc( $hv . $realkey );
882  }
883 
884  return false;
885  }
886 
887  // }}}
888  // {{{ _hashfunc()
889 
898  function _hashfunc( $key ) {
899  # Hash function must be in [0,0x7ffffff]
900  # We take the first 31 bits of the MD5 hash, which unlike the hash
901  # function used in a previous version of this client, works
902  return hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
903  }
904 
905  // }}}
906  // {{{ _incrdecr()
907 
918  function _incrdecr( $cmd, $key, $amt = 1 ) {
919  if ( !$this->_active ) {
920  return null;
921  }
922 
923  $sock = $this->get_sock( $key );
924  if ( !is_resource( $sock ) ) {
925  return null;
926  }
927 
928  $key = is_array( $key ) ? $key[1] : $key;
929  if ( isset( $this->stats[$cmd] ) ) {
930  $this->stats[$cmd]++;
931  } else {
932  $this->stats[$cmd] = 1;
933  }
934  if ( !$this->_fwrite( $sock, "$cmd $key $amt\r\n" ) ) {
935  return null;
936  }
937 
938  $line = $this->_fgets( $sock );
939  $match = array();
940  if ( !preg_match( '/^(\d+)/', $line, $match ) ) {
941  return null;
942  }
943  return $match[1];
944  }
945 
946  // }}}
947  // {{{ _load_items()
948 
959  function _load_items( $sock, &$ret, &$casToken = null ) {
960  $results = array();
961 
962  while ( 1 ) {
963  $decl = $this->_fgets( $sock );
964 
965  if ( $decl === false ) {
966  /*
967  * If nothing can be read, something is wrong because we know exactly when
968  * to stop reading (right after "END") and we return right after that.
969  */
970  return false;
971  } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+) (\d+)$/', $decl, $match ) ) {
972  /*
973  * Read all data returned. This can be either one or multiple values.
974  * Save all that data (in an array) to be processed later: we'll first
975  * want to continue reading until "END" before doing anything else,
976  * to make sure that we don't leave our client in a state where it's
977  * output is not yet fully read.
978  */
979  $results[] = array(
980  $match[1], // rkey
981  $match[2], // flags
982  $match[3], // len
983  $match[4], // casToken
984  $this->_fread( $sock, $match[3] + 2 ), // data
985  );
986  } elseif ( $decl == "END" ) {
987  if ( count( $results ) == 0 ) {
988  return false;
989  }
990 
995  foreach ( $results as $vars ) {
996  list( $rkey, $flags, $len, $casToken, $data ) = $vars;
997 
998  if ( $data === false || substr( $data, -2 ) !== "\r\n" ) {
999  $this->_handle_error( $sock,
1000  'line ending missing from data block from $1' );
1001  return false;
1002  }
1003  $data = substr( $data, 0, -2 );
1004  $ret[$rkey] = $data;
1005 
1006  if ( $this->_have_zlib && $flags & self::COMPRESSED ) {
1007  $ret[$rkey] = gzuncompress( $ret[$rkey] );
1008  }
1009 
1010  /*
1011  * This unserialize is the exact reason that we only want to
1012  * process data after having read until "END" (instead of doing
1013  * this right away): "unserialize" can trigger outside code:
1014  * in the event that $ret[$rkey] is a serialized object,
1015  * unserializing it will trigger __wakeup() if present. If that
1016  * function attempted to read from memcached (while we did not
1017  * yet read "END"), these 2 calls would collide.
1018  */
1019  if ( $flags & self::SERIALIZED ) {
1020  $ret[$rkey] = $this->unserialize( $ret[$rkey] );
1021  } elseif ( $flags & self::INTVAL ) {
1022  $ret[$rkey] = intval( $ret[$rkey] );
1023  }
1024  }
1025 
1026  return true;
1027  } else {
1028  $this->_handle_error( $sock, 'Error parsing response from $1' );
1029  return false;
1030  }
1031  }
1032  }
1033 
1034  // }}}
1035  // {{{ _set()
1036 
1053  function _set( $cmd, $key, $val, $exp, $casToken = null ) {
1054  if ( !$this->_active ) {
1055  return false;
1056  }
1057 
1058  $sock = $this->get_sock( $key );
1059  if ( !is_resource( $sock ) ) {
1060  return false;
1061  }
1062 
1063  if ( isset( $this->stats[$cmd] ) ) {
1064  $this->stats[$cmd]++;
1065  } else {
1066  $this->stats[$cmd] = 1;
1067  }
1068 
1069  $flags = 0;
1070 
1071  if ( is_int( $val ) ) {
1072  $flags |= self::INTVAL;
1073  } elseif ( !is_scalar( $val ) ) {
1074  $val = $this->serialize( $val );
1075  $flags |= self::SERIALIZED;
1076  if ( $this->_debug ) {
1077  $this->_debugprint( sprintf( "client: serializing data as it is not scalar" ) );
1078  }
1079  }
1080 
1081  $len = strlen( $val );
1082 
1083  if ( $this->_have_zlib && $this->_compress_enable
1084  && $this->_compress_threshold && $len >= $this->_compress_threshold
1085  ) {
1086  $c_val = gzcompress( $val, 9 );
1087  $c_len = strlen( $c_val );
1088 
1089  if ( $c_len < $len * ( 1 - self::COMPRESSION_SAVINGS ) ) {
1090  if ( $this->_debug ) {
1091  $this->_debugprint( sprintf( "client: compressing data; was %d bytes is now %d bytes", $len, $c_len ) );
1092  }
1093  $val = $c_val;
1094  $len = $c_len;
1095  $flags |= self::COMPRESSED;
1096  }
1097  }
1098 
1099  $command = "$cmd $key $flags $exp $len";
1100  if ( $casToken ) {
1101  $command .= " $casToken";
1102  }
1103 
1104  if ( !$this->_fwrite( $sock, "$command\r\n$val\r\n" ) ) {
1105  return false;
1106  }
1107 
1108  $line = $this->_fgets( $sock );
1109 
1110  if ( $this->_debug ) {
1111  $this->_debugprint( sprintf( "%s %s (%s)", $cmd, $key, $line ) );
1112  }
1113  if ( $line === "STORED" ) {
1114  return true;
1115  } elseif ( $line === "NOT_STORED" && $cmd === "set" ) {
1116  // "Not stored" is always used as the mcrouter response with AllAsyncRoute
1117  return true;
1118  }
1119 
1120  return false;
1121  }
1122 
1123  // }}}
1124  // {{{ sock_to_host()
1125 
1134  function sock_to_host( $host ) {
1135  if ( isset( $this->_cache_sock[$host] ) ) {
1136  return $this->_cache_sock[$host];
1137  }
1138 
1139  $sock = null;
1140  $now = time();
1141  list( $ip, /* $port */) = explode( ':', $host );
1142  if ( isset( $this->_host_dead[$host] ) && $this->_host_dead[$host] > $now ||
1143  isset( $this->_host_dead[$ip] ) && $this->_host_dead[$ip] > $now
1144  ) {
1145  return null;
1146  }
1147 
1148  if ( !$this->_connect_sock( $sock, $host ) ) {
1149  return null;
1150  }
1151 
1152  // Do not buffer writes
1153  stream_set_write_buffer( $sock, 0 );
1154 
1155  $this->_cache_sock[$host] = $sock;
1156 
1157  return $this->_cache_sock[$host];
1158  }
1159 
1163  function _debugprint( $text ) {
1164  $this->_logger->debug( $text );
1165  }
1166 
1170  function _error_log( $text ) {
1171  $this->_logger->error( "Memcached error: $text" );
1172  }
1173 
1181  function _fwrite( $sock, $buf ) {
1182  $bytesWritten = 0;
1183  $bufSize = strlen( $buf );
1184  while ( $bytesWritten < $bufSize ) {
1185  $result = fwrite( $sock, $buf );
1186  $data = stream_get_meta_data( $sock );
1187  if ( $data['timed_out'] ) {
1188  $this->_handle_error( $sock, 'timeout writing to $1' );
1189  return false;
1190  }
1191  // Contrary to the documentation, fwrite() returns zero on error in PHP 5.3.
1192  if ( $result === false || $result === 0 ) {
1193  $this->_handle_error( $sock, 'error writing to $1' );
1194  return false;
1195  }
1196  $bytesWritten += $result;
1197  }
1198 
1199  return true;
1200  }
1201 
1208  function _handle_error( $sock, $msg ) {
1209  $peer = stream_socket_get_name( $sock, true );
1210  if ( strval( $peer ) === '' ) {
1211  $peer = array_search( $sock, $this->_cache_sock );
1212  if ( $peer === false ) {
1213  $peer = '[unknown host]';
1214  }
1215  }
1216  $msg = str_replace( '$1', $peer, $msg );
1217  $this->_error_log( "$msg" );
1218  $this->_dead_sock( $sock );
1219  }
1220 
1229  function _fread( $sock, $len ) {
1230  $buf = '';
1231  while ( $len > 0 ) {
1232  $result = fread( $sock, $len );
1233  $data = stream_get_meta_data( $sock );
1234  if ( $data['timed_out'] ) {
1235  $this->_handle_error( $sock, 'timeout reading from $1' );
1236  return false;
1237  }
1238  if ( $result === false ) {
1239  $this->_handle_error( $sock, 'error reading buffer from $1' );
1240  return false;
1241  }
1242  if ( $result === '' ) {
1243  // This will happen if the remote end of the socket is shut down
1244  $this->_handle_error( $sock, 'unexpected end of file reading from $1' );
1245  return false;
1246  }
1247  $len -= strlen( $result );
1248  $buf .= $result;
1249  }
1250  return $buf;
1251  }
1252 
1260  function _fgets( $sock ) {
1261  $result = fgets( $sock );
1262  // fgets() may return a partial line if there is a select timeout after
1263  // a successful recv(), so we have to check for a timeout even if we
1264  // got a string response.
1265  $data = stream_get_meta_data( $sock );
1266  if ( $data['timed_out'] ) {
1267  $this->_handle_error( $sock, 'timeout reading line from $1' );
1268  return false;
1269  }
1270  if ( $result === false ) {
1271  $this->_handle_error( $sock, 'error reading line from $1' );
1272  return false;
1273  }
1274  if ( substr( $result, -2 ) === "\r\n" ) {
1275  $result = substr( $result, 0, -2 );
1276  } elseif ( substr( $result, -1 ) === "\n" ) {
1277  $result = substr( $result, 0, -1 );
1278  } else {
1279  $this->_handle_error( $sock, 'line ending missing in response from $1' );
1280  return false;
1281  }
1282  return $result;
1283  }
1284 
1289  function _flush_read_buffer( $f ) {
1290  if ( !is_resource( $f ) ) {
1291  return;
1292  }
1293  $r = array( $f );
1294  $w = null;
1295  $e = null;
1296  $n = stream_select( $r, $w, $e, 0, 0 );
1297  while ( $n == 1 && !feof( $f ) ) {
1298  fread( $f, 1024 );
1299  $r = array( $f );
1300  $w = null;
1301  $e = null;
1302  $n = stream_select( $r, $w, $e, 0, 0 );
1303  }
1304  }
1305 
1306  // }}}
1307  // }}}
1308  // }}}
1309 }
1310 
1311 // }}}
sock_to_host( $host)
Returns the socket for the host.
array $stats
Command statistics.
memcached client class implemented using (p)fsockopen()
array $_host_dead
Dead hosts, assoc array, &#39;host&#39;=>&#39;unixtime when ok to check again&#39;.
_handle_error( $sock, $msg)
Handle an I/O error.
_flush_read_buffer( $f)
Flush the read buffer of a stream.
$command
Definition: cdb.php:65
const INTVAL
Flag: indicates data is an integer.
_close_sock( $sock)
Close the specified socket.
run_command( $sock, $cmd)
Passes through $cmd to the memcache server connected by $sock; returns output as an array (null array...
forget_dead_hosts()
Forget about all of the dead hosts.
$_connect_timeout
Connect timeout in seconds.
_load_items( $sock, &$ret, &$casToken=null)
Load items into $ret from $sock.
get_multi( $keys)
Get multiple keys from the server(s)
disconnect_all()
Disconnects all connected sockets.
decr( $key, $amt=1)
Decrease a value stored on the memcache server.
int $_timeout_microseconds
Stream timeout in microseconds.
__construct( $args)
Memcache initializer.
bool $_have_zlib
Is compression available?
get_sock( $key)
get_sock
_connect_sock(&$sock, $host)
Connects $sock to $host, timing out after $timeout.
if( $line===false) $args
Definition: cdb.php:64
int $_timeout_seconds
Stream timeout in seconds.
set_compress_threshold( $thresh)
Set the compression threshold.
replace( $key, $value, $exp=0)
Overwrites an existing value for key; only works if key is already set.
int $_compress_threshold
At how many bytes should we compress?
bool $_persistent
Are we using persistent links?
incr( $key, $amt=1)
Increments $key (optionally) by $amt.
const SERIALIZED
Flag: indicates data is serialized.
set_timeout( $seconds, $microseconds)
Sets the timeout for new connections.
array $_servers
Array containing ip:port or array(ip:port, weight)
set_debug( $dbg)
Set the debug flag.
_hashfunc( $key)
Creates a hash integer based on the $key.
_fread( $sock, $len)
Read the specified number of bytes from a stream.
bool $_debug
Current debug status; 0 - none to 9 - profiling.
_fwrite( $sock, $buf)
Write to a stream.
array $_buckets
Our bit buckets.
_dead_sock( $sock)
Marks a host as dead until 30-40 seconds in the future.
_incrdecr( $cmd, $key, $amt=1)
Perform increment/decriment on $key.
string $_single_sock
If only using one server; contains ip:port to connect to.
int $_active
of total servers we have
cas( $casToken, $key, $value, $exp=0)
Sets a key to a given value in the memcache if the current value still corresponds to a known...
bool $_compress_enable
Do we want to use compression?
enable_compress( $enable)
Enable / Disable compression.
$line
Definition: cdb.php:59
_set( $cmd, $key, $val, $exp, $casToken=null)
Performs the requested storage operation to the memcache server.
array $_cache_sock
Cached Sockets that are connected.
const COMPRESSED
Flag: indicates data is compressed.
set_servers( $list)
Set the server list to distribute key gets and puts between.
const COMPRESSION_SAVINGS
Minimum savings to store data compressed.
_fgets( $sock)
Read a line from a stream.
add( $key, $val, $exp=0)
Adds a key/value to the memcache server if one isn&#39;t already set with that key.
int $_bucketcount
Total # of bit buckets we have.
touch( $key, $time=0)
Changes the TTL on a key from the server to $time.
$_connect_attempts
Number of connection attempts for each server.
LoggerInterface $_logger