MediaWiki REL1_37
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.
69use Psr\Log\LoggerInterface;
70use Psr\Log\NullLogger;
71use Wikimedia\IPUtils;
72
73// {{{ class MemcachedClient
81 // {{{ properties
82 // {{{ public
83
84 // {{{ constants
85 // {{{ flags
86
90 const SERIALIZED = 1;
91
95 const COMPRESSED = 2;
96
100 const INTVAL = 4;
101
102 // }}}
103
108
109 // }}}
110
117 public $stats;
118
119 // }}}
120 // {{{ private
121
129
136 public $_debug;
137
145
153
161
169
177
185
192 public $_servers;
193
200 public $_buckets;
201
209
216 public $_active;
217
225
233
238
243
247 private $_logger;
248
249 // }}}
250 // }}}
251 // {{{ methods
252 // {{{ public functions
253 // {{{ memcached()
254
260 public function __construct( $args ) {
261 $this->set_servers( $args['servers'] ?? array() );
262 $this->_debug = $args['debug'] ?? false;
263 $this->stats = array();
264 $this->_compress_threshold = $args['compress_threshold'] ?? 0;
265 $this->_persistent = $args['persistent'] ?? false;
266 $this->_compress_enable = true;
267 $this->_have_zlib = function_exists( 'gzcompress' );
268
269 $this->_cache_sock = array();
270 $this->_host_dead = array();
271
272 $this->_timeout_seconds = 0;
273 $this->_timeout_microseconds = $args['timeout'] ?? 500000;
274
275 $this->_connect_timeout = $args['connect_timeout'] ?? 0.1;
276 $this->_connect_attempts = 2;
277
278 $this->_logger = $args['logger'] ?? new NullLogger();
279 }
280
281 // }}}
282
287 public function serialize( $value ) {
288 return serialize( $value );
289 }
290
295 public function unserialize( $value ) {
296 return unserialize( $value );
297 }
298
299 // {{{ add()
300
315 public function add( $key, $val, $exp = 0 ) {
316 return $this->_set( 'add', $key, $val, $exp );
317 }
318
319 // }}}
320 // {{{ decr()
321
330 public function decr( $key, $amt = 1 ) {
331 return $this->_incrdecr( 'decr', $key, $amt );
332 }
333
334 // }}}
335 // {{{ delete()
336
345 public function delete( $key, $time = 0 ) {
346 if ( !$this->_active ) {
347 return false;
348 }
349
350 $sock = $this->get_sock( $key );
351 if ( !$sock ) {
352 return false;
353 }
354
355 $key = is_array( $key ) ? $key[1] : $key;
356
357 if ( isset( $this->stats['delete'] ) ) {
358 $this->stats['delete']++;
359 } else {
360 $this->stats['delete'] = 1;
361 }
362 $cmd = "delete $key $time\r\n";
363 if ( !$this->_fwrite( $sock, $cmd ) ) {
364 return false;
365 }
366 $res = $this->_fgets( $sock );
367
368 if ( $this->_debug ) {
369 $this->_debugprint( sprintf( "MemCache: delete %s (%s)", $key, $res ) );
370 }
371
372 if ( $res == "DELETED" || $res == "NOT_FOUND" ) {
373 return true;
374 }
375
376 return false;
377 }
378
387 public function touch( $key, $time = 0 ) {
388 if ( !$this->_active ) {
389 return false;
390 }
391
392 $sock = $this->get_sock( $key );
393 if ( !$sock ) {
394 return false;
395 }
396
397 $key = is_array( $key ) ? $key[1] : $key;
398
399 if ( isset( $this->stats['touch'] ) ) {
400 $this->stats['touch']++;
401 } else {
402 $this->stats['touch'] = 1;
403 }
404 $cmd = "touch $key $time\r\n";
405 if ( !$this->_fwrite( $sock, $cmd ) ) {
406 return false;
407 }
408 $res = $this->_fgets( $sock );
409
410 if ( $this->_debug ) {
411 $this->_debugprint( sprintf( "MemCache: touch %s (%s)", $key, $res ) );
412 }
413
414 if ( $res == "TOUCHED" ) {
415 return true;
416 }
417
418 return false;
419 }
420
421 // }}}
422 // {{{ disconnect_all()
423
427 public function disconnect_all() {
428 foreach ( $this->_cache_sock as $sock ) {
429 fclose( $sock );
430 }
431
432 $this->_cache_sock = array();
433 }
434
435 // }}}
436 // {{{ enable_compress()
437
443 public function enable_compress( $enable ) {
444 $this->_compress_enable = $enable;
445 }
446
447 // }}}
448 // {{{ forget_dead_hosts()
449
453 public function forget_dead_hosts() {
454 $this->_host_dead = array();
455 }
456
457 // }}}
458 // {{{ get()
459
468 public function get( $key, &$casToken = null ) {
469 $getToken = ( func_num_args() >= 2 );
470
471 if ( $this->_debug ) {
472 $this->_debugprint( "get($key)" );
473 }
474
475 if ( !is_array( $key ) && strval( $key ) === '' ) {
476 $this->_debugprint( "Skipping key which equals to an empty string" );
477 return false;
478 }
479
480 if ( !$this->_active ) {
481 return false;
482 }
483
484 $sock = $this->get_sock( $key );
485
486 if ( !$sock ) {
487 return false;
488 }
489
490 $key = is_array( $key ) ? $key[1] : $key;
491 if ( isset( $this->stats['get'] ) ) {
492 $this->stats['get']++;
493 } else {
494 $this->stats['get'] = 1;
495 }
496
497 $cmd = $getToken ? "gets" : "get";
498 $cmd .= " $key\r\n";
499 if ( !$this->_fwrite( $sock, $cmd ) ) {
500 return false;
501 }
502
503 $val = array();
504 $this->_load_items( $sock, $val, $casToken );
505
506 if ( $this->_debug ) {
507 foreach ( $val as $k => $v ) {
508 $this->_debugprint(
509 sprintf( "MemCache: sock %s got %s", $this->serialize( $sock ), $k ) );
510 }
511 }
512
513 $value = false;
514 if ( isset( $val[$key] ) ) {
515 $value = $val[$key];
516 }
517 return $value;
518 }
519
520 // }}}
521 // {{{ get_multi()
522
530 public function get_multi( $keys ) {
531 if ( !$this->_active ) {
532 return array();
533 }
534
535 if ( isset( $this->stats['get_multi'] ) ) {
536 $this->stats['get_multi']++;
537 } else {
538 $this->stats['get_multi'] = 1;
539 }
540 $sock_keys = array();
541 $socks = array();
542 foreach ( $keys as $key ) {
543 $sock = $this->get_sock( $key );
544 if ( !$sock ) {
545 continue;
546 }
547 $key = is_array( $key ) ? $key[1] : $key;
548 $sockValue = intval( $sock );
549
550 if ( !isset( $sock_keys[$sockValue] ) ) {
551 $sock_keys[$sockValue] = array();
552 $socks[] = $sock;
553 }
554 $sock_keys[$sockValue][] = $key;
555 }
556
557 $gather = array();
558 // Send out the requests
559 foreach ( $socks as $sock ) {
560 $cmd = 'get';
561 foreach ( $sock_keys[intval( $sock )] as $key ) {
562 $cmd .= ' ' . $key;
563 }
564 $cmd .= "\r\n";
565
566 if ( $this->_fwrite( $sock, $cmd ) ) {
567 $gather[] = $sock;
568 }
569 }
570
571 // Parse responses
572 $val = array();
573 foreach ( $gather as $sock ) {
574 $this->_load_items( $sock, $val );
575 }
576
577 if ( $this->_debug ) {
578 foreach ( $val as $k => $v ) {
579 $this->_debugprint( sprintf( "MemCache: got %s", $k ) );
580 }
581 }
582
583 return $val;
584 }
585
586 // }}}
587 // {{{ incr()
588
599 public function incr( $key, $amt = 1 ) {
600 return $this->_incrdecr( 'incr', $key, $amt );
601 }
602
603 // }}}
604 // {{{ replace()
605
619 public function replace( $key, $value, $exp = 0 ) {
620 return $this->_set( 'replace', $key, $value, $exp );
621 }
622
623 // }}}
624 // {{{ run_command()
625
635 public function run_command( $sock, $cmd ) {
636 if ( !$sock ) {
637 return array();
638 }
639
640 if ( !$this->_fwrite( $sock, $cmd ) ) {
641 return array();
642 }
643
644 $ret = array();
645 while ( true ) {
646 $res = $this->_fgets( $sock );
647 $ret[] = $res;
648 if ( preg_match( '/^END/', $res ) ) {
649 break;
650 }
651 if ( strlen( $res ) == 0 ) {
652 break;
653 }
654 }
655 return $ret;
656 }
657
658 // }}}
659 // {{{ set()
660
675 public function set( $key, $value, $exp = 0 ) {
676 return $this->_set( 'set', $key, $value, $exp );
677 }
678
679 // }}}
680 // {{{ cas()
681
697 public function cas( $casToken, $key, $value, $exp = 0 ) {
698 return $this->_set( 'cas', $key, $value, $exp, $casToken );
699 }
700
701 // }}}
702 // {{{ set_compress_threshold()
703
709 public function set_compress_threshold( $thresh ) {
710 $this->_compress_threshold = $thresh;
711 }
712
713 // }}}
714 // {{{ set_debug()
715
722 public function set_debug( $dbg ) {
723 $this->_debug = $dbg;
724 }
725
726 // }}}
727 // {{{ set_servers()
728
735 public function set_servers( $list ) {
736 $this->_servers = $list;
737 $this->_active = count( $list );
738 $this->_buckets = null;
739 $this->_bucketcount = 0;
740
741 $this->_single_sock = null;
742 if ( $this->_active == 1 ) {
743 $this->_single_sock = $this->_servers[0];
744 }
745 }
746
753 public function set_timeout( $seconds, $microseconds ) {
754 $this->_timeout_seconds = $seconds;
755 $this->_timeout_microseconds = $microseconds;
756 }
757
758 // }}}
759 // }}}
760 // {{{ private methods
761 // {{{ _close_sock()
762
770 function _close_sock( $sock ) {
771 $host = array_search( $sock, $this->_cache_sock );
772 fclose( $this->_cache_sock[$host] );
773 unset( $this->_cache_sock[$host] );
774 }
775
776 // }}}
777 // {{{ _connect_sock()
778
788 function _connect_sock( &$sock, $host ) {
789 $port = null;
790 $hostAndPort = IPUtils::splitHostAndPort( $host );
791 if ( $hostAndPort ) {
792 $ip = $hostAndPort[0];
793 if ( $hostAndPort[1] ) {
794 $port = $hostAndPort[1];
795 }
796 } else {
797 $ip = $host;
798 }
799 $sock = false;
800 $timeout = $this->_connect_timeout;
801 $errno = $errstr = null;
802 for ( $i = 0; !$sock && $i < $this->_connect_attempts; $i++ ) {
803 Wikimedia\suppressWarnings();
804 if ( $this->_persistent == 1 ) {
805 $sock = pfsockopen( $ip, $port, $errno, $errstr, $timeout );
806 } else {
807 $sock = fsockopen( $ip, $port, $errno, $errstr, $timeout );
808 }
809 Wikimedia\restoreWarnings();
810 }
811 if ( !$sock ) {
812 $this->_error_log( "Error connecting to $host: $errstr" );
813 $this->_dead_host( $host );
814 return false;
815 }
816
817 // Initialise timeout
818 stream_set_timeout( $sock, $this->_timeout_seconds, $this->_timeout_microseconds );
819
820 // If the connection was persistent, flush the read buffer in case there
821 // was a previous incomplete request on this connection
822 if ( $this->_persistent ) {
823 $this->_flush_read_buffer( $sock );
824 }
825 return true;
826 }
827
828 // }}}
829 // {{{ _dead_sock()
830
838 function _dead_sock( $sock ) {
839 $host = array_search( $sock, $this->_cache_sock );
840 $this->_dead_host( $host );
841 }
842
846 function _dead_host( $host ) {
847 $hostAndPort = IPUtils::splitHostAndPort( $host );
848 if ( $hostAndPort ) {
849 $ip = $hostAndPort[0];
850 } else {
851 $ip = $host;
852 }
853 $this->_host_dead[$ip] = time() + 30 + intval( rand( 0, 10 ) );
854 $this->_host_dead[$host] = $this->_host_dead[$ip];
855 unset( $this->_cache_sock[$host] );
856 }
857
858 // }}}
859 // {{{ get_sock()
860
869 function get_sock( $key ) {
870 if ( !$this->_active ) {
871 return false;
872 }
873
874 if ( $this->_single_sock !== null ) {
875 return $this->sock_to_host( $this->_single_sock );
876 }
877
878 $hv = is_array( $key ) ? intval( $key[0] ) : $this->_hashfunc( $key );
879 if ( $this->_buckets === null ) {
880 $bu = array();
881 foreach ( $this->_servers as $v ) {
882 if ( is_array( $v ) ) {
883 for ( $i = 0; $i < $v[1]; $i++ ) {
884 $bu[] = $v[0];
885 }
886 } else {
887 $bu[] = $v;
888 }
889 }
890 $this->_buckets = $bu;
891 $this->_bucketcount = count( $bu );
892 }
893
894 $realkey = is_array( $key ) ? $key[1] : $key;
895 for ( $tries = 0; $tries < 20; $tries++ ) {
896 $host = $this->_buckets[$hv % $this->_bucketcount];
897 $sock = $this->sock_to_host( $host );
898 if ( $sock ) {
899 return $sock;
900 }
901 $hv = $this->_hashfunc( $hv . $realkey );
902 }
903
904 return false;
905 }
906
907 // }}}
908 // {{{ _hashfunc()
909
918 function _hashfunc( $key ) {
919 # Hash function must be in [0,0x7ffffff]
920 # We take the first 31 bits of the MD5 hash, which unlike the hash
921 # function used in a previous version of this client, works
922 return hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
923 }
924
925 // }}}
926 // {{{ _incrdecr()
927
938 function _incrdecr( $cmd, $key, $amt = 1 ) {
939 if ( !$this->_active ) {
940 return null;
941 }
942
943 $sock = $this->get_sock( $key );
944 if ( !$sock ) {
945 return null;
946 }
947
948 $key = is_array( $key ) ? $key[1] : $key;
949 if ( isset( $this->stats[$cmd] ) ) {
950 $this->stats[$cmd]++;
951 } else {
952 $this->stats[$cmd] = 1;
953 }
954 if ( !$this->_fwrite( $sock, "$cmd $key $amt\r\n" ) ) {
955 return null;
956 }
957
958 $line = $this->_fgets( $sock );
959 $match = array();
960 if ( !preg_match( '/^(\d+)/', $line, $match ) ) {
961 return null;
962 }
963 return $match[1];
964 }
965
966 // }}}
967 // {{{ _load_items()
968
979 function _load_items( $sock, &$ret, &$casToken = null ) {
980 $results = array();
981
982 while ( 1 ) {
983 $decl = $this->_fgets( $sock );
984
985 if ( $decl === false ) {
986 /*
987 * If nothing can be read, something is wrong because we know exactly when
988 * to stop reading (right after "END") and we return right after that.
989 */
990 return false;
991 } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+)(?: (\d+))?$/', $decl, $match ) ) {
992 /*
993 * Read all data returned. This can be either one or multiple values.
994 * Save all that data (in an array) to be processed later: we'll first
995 * want to continue reading until "END" before doing anything else,
996 * to make sure that we don't leave our client in a state where it's
997 * output is not yet fully read.
998 */
999 $results[] = array(
1000 $match[1], // rkey
1001 $match[2], // flags
1002 $match[3], // len
1003 $match[4] ?? null, // casToken (appears with "gets" but not "get")
1004 $this->_fread( $sock, $match[3] + 2 ), // data
1005 );
1006 } elseif ( $decl == "END" ) {
1007 if ( count( $results ) == 0 ) {
1008 return false;
1009 }
1010
1015 foreach ( $results as [ $rkey, $flags, /* length */, $casToken, $data ] ) {
1016 if ( $data === false || substr( $data, -2 ) !== "\r\n" ) {
1017 $this->_handle_error( $sock,
1018 'line ending missing from data block from $1' );
1019 return false;
1020 }
1021 $data = substr( $data, 0, -2 );
1022 $ret[$rkey] = $data;
1023
1024 if ( $this->_have_zlib && $flags & self::COMPRESSED ) {
1025 $ret[$rkey] = gzuncompress( $ret[$rkey] );
1026 }
1027
1028 /*
1029 * This unserialize is the exact reason that we only want to
1030 * process data after having read until "END" (instead of doing
1031 * this right away): "unserialize" can trigger outside code:
1032 * in the event that $ret[$rkey] is a serialized object,
1033 * unserializing it will trigger __wakeup() if present. If that
1034 * function attempted to read from memcached (while we did not
1035 * yet read "END"), these 2 calls would collide.
1036 */
1037 if ( $flags & self::SERIALIZED ) {
1038 $ret[$rkey] = $this->unserialize( $ret[$rkey] );
1039 } elseif ( $flags & self::INTVAL ) {
1040 $ret[$rkey] = intval( $ret[$rkey] );
1041 }
1042 }
1043
1044 return true;
1045 } else {
1046 $this->_handle_error( $sock, 'Error parsing response from $1' );
1047 return false;
1048 }
1049 }
1050 }
1051
1052 // }}}
1053 // {{{ _set()
1054
1071 function _set( $cmd, $key, $val, $exp, $casToken = null ) {
1072 if ( !$this->_active ) {
1073 return false;
1074 }
1075
1076 $sock = $this->get_sock( $key );
1077 if ( !$sock ) {
1078 return false;
1079 }
1080
1081 if ( isset( $this->stats[$cmd] ) ) {
1082 $this->stats[$cmd]++;
1083 } else {
1084 $this->stats[$cmd] = 1;
1085 }
1086
1087 $flags = 0;
1088
1089 if ( is_int( $val ) ) {
1090 $flags |= self::INTVAL;
1091 } elseif ( !is_scalar( $val ) ) {
1092 $val = $this->serialize( $val );
1093 $flags |= self::SERIALIZED;
1094 if ( $this->_debug ) {
1095 $this->_debugprint( sprintf( "client: serializing data as it is not scalar" ) );
1096 }
1097 }
1098
1099 $len = strlen( $val );
1100
1101 if ( $this->_have_zlib && $this->_compress_enable
1102 && $this->_compress_threshold && $len >= $this->_compress_threshold
1103 ) {
1104 $c_val = gzcompress( $val, 9 );
1105 $c_len = strlen( $c_val );
1106
1107 if ( $c_len < $len * ( 1 - self::COMPRESSION_SAVINGS ) ) {
1108 if ( $this->_debug ) {
1109 $this->_debugprint( sprintf( "client: compressing data; was %d bytes is now %d bytes", $len, $c_len ) );
1110 }
1111 $val = $c_val;
1112 $len = $c_len;
1113 $flags |= self::COMPRESSED;
1114 }
1115 }
1116
1117 $command = "$cmd $key $flags $exp $len";
1118 if ( $casToken ) {
1119 $command .= " $casToken";
1120 }
1121
1122 if ( !$this->_fwrite( $sock, "$command\r\n$val\r\n" ) ) {
1123 return false;
1124 }
1125
1126 $line = $this->_fgets( $sock );
1127
1128 if ( $this->_debug ) {
1129 $this->_debugprint( sprintf( "%s %s (%s)", $cmd, $key, $line ) );
1130 }
1131 if ( $line === "STORED" ) {
1132 return true;
1133 } elseif ( $line === "NOT_STORED" && $cmd === "set" ) {
1134 // "Not stored" is always used as the mcrouter response with AllAsyncRoute
1135 return true;
1136 }
1137
1138 return false;
1139 }
1140
1141 // }}}
1142 // {{{ sock_to_host()
1143
1152 function sock_to_host( $host ) {
1153 if ( isset( $this->_cache_sock[$host] ) ) {
1154 return $this->_cache_sock[$host];
1155 }
1156
1157 $sock = null;
1158 $now = time();
1159 $hostAndPort = IPUtils::splitHostAndPort( $host );
1160 if ( $hostAndPort ) {
1161 $ip = $hostAndPort[0];
1162 } else {
1163 $ip = $host;
1164 }
1165 if ( isset( $this->_host_dead[$host] ) && $this->_host_dead[$host] > $now ||
1166 isset( $this->_host_dead[$ip] ) && $this->_host_dead[$ip] > $now
1167 ) {
1168 return null;
1169 }
1170
1171 if ( !$this->_connect_sock( $sock, $host ) ) {
1172 return null;
1173 }
1174
1175 // Do not buffer writes
1176 stream_set_write_buffer( $sock, 0 );
1177
1178 $this->_cache_sock[$host] = $sock;
1179
1180 return $this->_cache_sock[$host];
1181 }
1182
1186 function _debugprint( $text ) {
1187 $this->_logger->debug( $text );
1188 }
1189
1193 function _error_log( $text ) {
1194 $this->_logger->error( "Memcached error: $text" );
1195 }
1196
1204 function _fwrite( $sock, $buf ) {
1205 $bytesWritten = 0;
1206 $bufSize = strlen( $buf );
1207 while ( $bytesWritten < $bufSize ) {
1208 $result = fwrite( $sock, $buf );
1209 $data = stream_get_meta_data( $sock );
1210 if ( $data['timed_out'] ) {
1211 $this->_handle_error( $sock, 'timeout writing to $1' );
1212 return false;
1213 }
1214 // Contrary to the documentation, fwrite() returns zero on error in PHP 5.3.
1215 if ( $result === false || $result === 0 ) {
1216 $this->_handle_error( $sock, 'error writing to $1' );
1217 return false;
1218 }
1219 $bytesWritten += $result;
1220 }
1221
1222 return true;
1223 }
1224
1231 function _handle_error( $sock, $msg ) {
1232 $peer = stream_socket_get_name( $sock, true );
1233 if ( strval( $peer ) === '' ) {
1234 $peer = array_search( $sock, $this->_cache_sock );
1235 if ( $peer === false ) {
1236 $peer = '[unknown host]';
1237 }
1238 }
1239 $msg = str_replace( '$1', $peer, $msg );
1240 $this->_error_log( "$msg" );
1241 $this->_dead_sock( $sock );
1242 }
1243
1252 function _fread( $sock, $len ) {
1253 $buf = '';
1254 while ( $len > 0 ) {
1255 $result = fread( $sock, $len );
1256 $data = stream_get_meta_data( $sock );
1257 if ( $data['timed_out'] ) {
1258 $this->_handle_error( $sock, 'timeout reading from $1' );
1259 return false;
1260 }
1261 if ( $result === false ) {
1262 $this->_handle_error( $sock, 'error reading buffer from $1' );
1263 return false;
1264 }
1265 if ( $result === '' ) {
1266 // This will happen if the remote end of the socket is shut down
1267 $this->_handle_error( $sock, 'unexpected end of file reading from $1' );
1268 return false;
1269 }
1270 $len -= strlen( $result );
1271 $buf .= $result;
1272 }
1273 return $buf;
1274 }
1275
1283 function _fgets( $sock ) {
1284 $result = fgets( $sock );
1285 // fgets() may return a partial line if there is a select timeout after
1286 // a successful recv(), so we have to check for a timeout even if we
1287 // got a string response.
1288 $data = stream_get_meta_data( $sock );
1289 if ( $data['timed_out'] ) {
1290 $this->_handle_error( $sock, 'timeout reading line from $1' );
1291 return false;
1292 }
1293 if ( $result === false ) {
1294 $this->_handle_error( $sock, 'error reading line from $1' );
1295 return false;
1296 }
1297 if ( substr( $result, -2 ) === "\r\n" ) {
1298 $result = substr( $result, 0, -2 );
1299 } elseif ( substr( $result, -1 ) === "\n" ) {
1300 $result = substr( $result, 0, -1 );
1301 } else {
1302 $this->_handle_error( $sock, 'line ending missing in response from $1' );
1303 return false;
1304 }
1305 return $result;
1306 }
1307
1312 function _flush_read_buffer( $f ) {
1313 if ( !$f ) {
1314 return;
1315 }
1316 $r = array( $f );
1317 $w = null;
1318 $e = null;
1319 $n = stream_select( $r, $w, $e, 0, 0 );
1320 while ( $n == 1 && !feof( $f ) ) {
1321 fread( $f, 1024 );
1322 $r = array( $f );
1323 $w = null;
1324 $e = null;
1325 $n = stream_select( $r, $w, $e, 0, 0 );
1326 }
1327 }
1328
1329 // }}}
1330 // }}}
1331 // }}}
1332}
1333
1334// }}}
serialize()
memcached client class implemented using (p)fsockopen()
set_debug( $dbg)
Set the debug flag.
array $_cache_sock
Cached Sockets that are connected.
const SERIALIZED
Flag: indicates data is serialized.
const COMPRESSION_SAVINGS
Minimum savings to store data compressed.
_hashfunc( $key)
Creates a hash integer based on the $key.
set_compress_threshold( $thresh)
Set the compression threshold.
_fread( $sock, $len)
Read the specified number of bytes from a stream.
array $_servers
Array containing ip:port or array(ip:port, weight)
bool $_compress_enable
Do we want to use compression?
decr( $key, $amt=1)
Decrease a value stored on the memcache server.
const INTVAL
Flag: indicates data is an integer.
int $_compress_threshold
At how many bytes should we compress?
int $_timeout_seconds
Stream timeout in seconds.
disconnect_all()
Disconnects all connected sockets.
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,...
array $_buckets
Our bit buckets.
set_timeout( $seconds, $microseconds)
Sets the timeout for new connections.
_fgets( $sock)
Read a line from a stream.
incr( $key, $amt=1)
Increments $key (optionally) by $amt.
forget_dead_hosts()
Forget about all of the dead hosts.
_incrdecr( $cmd, $key, $amt=1)
Perform increment/decriment on $key.
$_connect_attempts
Number of connection attempts for each server.
add( $key, $val, $exp=0)
Adds a key/value to the memcache server if one isn't already set with that key.
_handle_error( $sock, $msg)
Handle an I/O error.
sock_to_host( $host)
Returns the socket for the host.
$_connect_timeout
Connect timeout in seconds.
touch( $key, $time=0)
Changes the TTL on a key from the server to $time.
bool $_have_zlib
Is compression available?
int $_bucketcount
Total # of bit buckets we have.
_fwrite( $sock, $buf)
Write to a stream.
bool $_debug
Current debug status; 0 - none to 9 - profiling.
replace( $key, $value, $exp=0)
Overwrites an existing value for key; only works if key is already set.
set_servers( $list)
Set the server list to distribute key gets and puts between.
_load_items( $sock, &$ret, &$casToken=null)
Load items into $ret from $sock.
string $_single_sock
If only using one server; contains ip:port to connect to.
get_multi( $keys)
Get multiple keys from the server(s)
_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...
_set( $cmd, $key, $val, $exp, $casToken=null)
Performs the requested storage operation to the memcache server.
LoggerInterface $_logger
int $_timeout_microseconds
Stream timeout in microseconds.
__construct( $args)
Memcache initializer.
_flush_read_buffer( $f)
Flush the read buffer of a stream.
enable_compress( $enable)
Enable / Disable compression.
array $_host_dead
Dead hosts, assoc array, 'host'=>'unixtime when ok to check again'.
const COMPRESSED
Flag: indicates data is compressed.
get_sock( $key)
get_sock
_dead_sock( $sock)
Marks a host as dead until 30-40 seconds in the future.
_connect_sock(&$sock, $host)
Connects $sock to $host, timing out after $timeout.
array $stats
Command statistics.
bool $_persistent
Are we using persistent links?
$line
Definition mcc.php:119
$command
Definition mcc.php:125
if( $line===false) $args
Definition mcc.php:124