MediaWiki  REL1_24
MemcachedClient.php
Go to the documentation of this file.
00001 <?php
00002 // @codingStandardsIgnoreFile It's an external lib and it isn't. Let's not bother.
00067 // {{{ requirements
00068 // }}}
00069 
00070 // {{{ class MWMemcached
00077 class MWMemcached {
00078     // {{{ properties
00079     // {{{ public
00080 
00081     // {{{ constants
00082     // {{{ flags
00083 
00087     const SERIALIZED = 1;
00088 
00092     const COMPRESSED = 2;
00093 
00094     // }}}
00095 
00099     const COMPRESSION_SAVINGS = 0.20;
00100 
00101     // }}}
00102 
00109     public $stats;
00110 
00111     // }}}
00112     // {{{ private
00113 
00120     public $_cache_sock;
00121 
00128     public $_debug;
00129 
00136     public $_host_dead;
00137 
00144     public $_have_zlib;
00145 
00152     public $_compress_enable;
00153 
00160     public $_compress_threshold;
00161 
00168     public $_persistent;
00169 
00176     public $_single_sock;
00177 
00184     public $_servers;
00185 
00192     public $_buckets;
00193 
00200     public $_bucketcount;
00201 
00208     public $_active;
00209 
00216     public $_timeout_seconds;
00217 
00224     public $_timeout_microseconds;
00225 
00229     public $_connect_timeout;
00230 
00234     public $_connect_attempts;
00235 
00236     // }}}
00237     // }}}
00238     // {{{ methods
00239     // {{{ public functions
00240     // {{{ memcached()
00241 
00249     public function __construct( $args ) {
00250         $this->set_servers( isset( $args['servers'] ) ? $args['servers'] : array() );
00251         $this->_debug = isset( $args['debug'] ) ? $args['debug'] : false;
00252         $this->stats = array();
00253         $this->_compress_threshold = isset( $args['compress_threshold'] ) ? $args['compress_threshold'] : 0;
00254         $this->_persistent = isset( $args['persistent'] ) ? $args['persistent'] : false;
00255         $this->_compress_enable = true;
00256         $this->_have_zlib = function_exists( 'gzcompress' );
00257 
00258         $this->_cache_sock = array();
00259         $this->_host_dead = array();
00260 
00261         $this->_timeout_seconds = 0;
00262         $this->_timeout_microseconds = isset( $args['timeout'] ) ? $args['timeout'] : 500000;
00263 
00264         $this->_connect_timeout = isset( $args['connect_timeout'] ) ? $args['connect_timeout'] : 0.1;
00265         $this->_connect_attempts = 2;
00266     }
00267 
00268     // }}}
00269     // {{{ add()
00270 
00285     public function add( $key, $val, $exp = 0 ) {
00286         return $this->_set( 'add', $key, $val, $exp );
00287     }
00288 
00289     // }}}
00290     // {{{ decr()
00291 
00300     public function decr( $key, $amt = 1 ) {
00301         return $this->_incrdecr( 'decr', $key, $amt );
00302     }
00303 
00304     // }}}
00305     // {{{ delete()
00306 
00315     public function delete( $key, $time = 0 ) {
00316         if ( !$this->_active ) {
00317             return false;
00318         }
00319 
00320         $sock = $this->get_sock( $key );
00321         if ( !is_resource( $sock ) ) {
00322             return false;
00323         }
00324 
00325         $key = is_array( $key ) ? $key[1] : $key;
00326 
00327         if ( isset( $this->stats['delete'] ) ) {
00328             $this->stats['delete']++;
00329         } else {
00330             $this->stats['delete'] = 1;
00331         }
00332         $cmd = "delete $key $time\r\n";
00333         if ( !$this->_fwrite( $sock, $cmd ) ) {
00334             return false;
00335         }
00336         $res = $this->_fgets( $sock );
00337 
00338         if ( $this->_debug ) {
00339             $this->_debugprint( sprintf( "MemCache: delete %s (%s)\n", $key, $res ) );
00340         }
00341 
00342         if ( $res == "DELETED" || $res == "NOT_FOUND" ) {
00343             return true;
00344         }
00345 
00346         return false;
00347     }
00348 
00354     public function lock( $key, $timeout = 0 ) {
00355         /* stub */
00356         return true;
00357     }
00358 
00363     public function unlock( $key ) {
00364         /* stub */
00365         return true;
00366     }
00367 
00368     // }}}
00369     // {{{ disconnect_all()
00370 
00374     public function disconnect_all() {
00375         foreach ( $this->_cache_sock as $sock ) {
00376             fclose( $sock );
00377         }
00378 
00379         $this->_cache_sock = array();
00380     }
00381 
00382     // }}}
00383     // {{{ enable_compress()
00384 
00390     public function enable_compress( $enable ) {
00391         $this->_compress_enable = $enable;
00392     }
00393 
00394     // }}}
00395     // {{{ forget_dead_hosts()
00396 
00400     public function forget_dead_hosts() {
00401         $this->_host_dead = array();
00402     }
00403 
00404     // }}}
00405     // {{{ get()
00406 
00415     public function get( $key, &$casToken = null ) {
00416         wfProfileIn( __METHOD__ );
00417 
00418         if ( $this->_debug ) {
00419             $this->_debugprint( "get($key)\n" );
00420         }
00421 
00422         if ( !is_array( $key ) && strval( $key ) === '' ) {
00423             $this->_debugprint( "Skipping key which equals to an empty string" );
00424             wfProfileOut( __METHOD__ );
00425             return false;
00426         }
00427 
00428         if ( !$this->_active ) {
00429             wfProfileOut( __METHOD__ );
00430             return false;
00431         }
00432 
00433         $sock = $this->get_sock( $key );
00434 
00435         if ( !is_resource( $sock ) ) {
00436             wfProfileOut( __METHOD__ );
00437             return false;
00438         }
00439 
00440         $key = is_array( $key ) ? $key[1] : $key;
00441         if ( isset( $this->stats['get'] ) ) {
00442             $this->stats['get']++;
00443         } else {
00444             $this->stats['get'] = 1;
00445         }
00446 
00447         $cmd = "gets $key\r\n";
00448         if ( !$this->_fwrite( $sock, $cmd ) ) {
00449             wfProfileOut( __METHOD__ );
00450             return false;
00451         }
00452 
00453         $val = array();
00454         $this->_load_items( $sock, $val, $casToken );
00455 
00456         if ( $this->_debug ) {
00457             foreach ( $val as $k => $v ) {
00458                 $this->_debugprint( sprintf( "MemCache: sock %s got %s\n", serialize( $sock ), $k ) );
00459             }
00460         }
00461 
00462         $value = false;
00463         if ( isset( $val[$key] ) ) {
00464             $value = $val[$key];
00465         }
00466         wfProfileOut( __METHOD__ );
00467         return $value;
00468     }
00469 
00470     // }}}
00471     // {{{ get_multi()
00472 
00480     public function get_multi( $keys ) {
00481         if ( !$this->_active ) {
00482             return false;
00483         }
00484 
00485         if ( isset( $this->stats['get_multi'] ) ) {
00486             $this->stats['get_multi']++;
00487         } else {
00488             $this->stats['get_multi'] = 1;
00489         }
00490         $sock_keys = array();
00491         $socks = array();
00492         foreach ( $keys as $key ) {
00493             $sock = $this->get_sock( $key );
00494             if ( !is_resource( $sock ) ) {
00495                 continue;
00496             }
00497             $key = is_array( $key ) ? $key[1] : $key;
00498             if ( !isset( $sock_keys[$sock] ) ) {
00499                 $sock_keys[intval( $sock )] = array();
00500                 $socks[] = $sock;
00501             }
00502             $sock_keys[intval( $sock )][] = $key;
00503         }
00504 
00505         $gather = array();
00506         // Send out the requests
00507         foreach ( $socks as $sock ) {
00508             $cmd = 'gets';
00509             foreach ( $sock_keys[intval( $sock )] as $key ) {
00510                 $cmd .= ' ' . $key;
00511             }
00512             $cmd .= "\r\n";
00513 
00514             if ( $this->_fwrite( $sock, $cmd ) ) {
00515                 $gather[] = $sock;
00516             }
00517         }
00518 
00519         // Parse responses
00520         $val = array();
00521         foreach ( $gather as $sock ) {
00522             $this->_load_items( $sock, $val, $casToken );
00523         }
00524 
00525         if ( $this->_debug ) {
00526             foreach ( $val as $k => $v ) {
00527                 $this->_debugprint( sprintf( "MemCache: got %s\n", $k ) );
00528             }
00529         }
00530 
00531         return $val;
00532     }
00533 
00534     // }}}
00535     // {{{ incr()
00536 
00547     public function incr( $key, $amt = 1 ) {
00548         return $this->_incrdecr( 'incr', $key, $amt );
00549     }
00550 
00551     // }}}
00552     // {{{ replace()
00553 
00567     public function replace( $key, $value, $exp = 0 ) {
00568         return $this->_set( 'replace', $key, $value, $exp );
00569     }
00570 
00571     // }}}
00572     // {{{ run_command()
00573 
00583     public function run_command( $sock, $cmd ) {
00584         if ( !is_resource( $sock ) ) {
00585             return array();
00586         }
00587 
00588         if ( !$this->_fwrite( $sock, $cmd ) ) {
00589             return array();
00590         }
00591 
00592         $ret = array();
00593         while ( true ) {
00594             $res = $this->_fgets( $sock );
00595             $ret[] = $res;
00596             if ( preg_match( '/^END/', $res ) ) {
00597                 break;
00598             }
00599             if ( strlen( $res ) == 0 ) {
00600                 break;
00601             }
00602         }
00603         return $ret;
00604     }
00605 
00606     // }}}
00607     // {{{ set()
00608 
00623     public function set( $key, $value, $exp = 0 ) {
00624         return $this->_set( 'set', $key, $value, $exp );
00625     }
00626 
00627     // }}}
00628     // {{{ cas()
00629 
00645     public function cas( $casToken, $key, $value, $exp = 0 ) {
00646         return $this->_set( 'cas', $key, $value, $exp, $casToken );
00647     }
00648 
00649     // }}}
00650     // {{{ set_compress_threshold()
00651 
00657     public function set_compress_threshold( $thresh ) {
00658         $this->_compress_threshold = $thresh;
00659     }
00660 
00661     // }}}
00662     // {{{ set_debug()
00663 
00671     public function set_debug( $dbg ) {
00672         $this->_debug = $dbg;
00673     }
00674 
00675     // }}}
00676     // {{{ set_servers()
00677 
00685     public function set_servers( $list ) {
00686         $this->_servers = $list;
00687         $this->_active = count( $list );
00688         $this->_buckets = null;
00689         $this->_bucketcount = 0;
00690 
00691         $this->_single_sock = null;
00692         if ( $this->_active == 1 ) {
00693             $this->_single_sock = $this->_servers[0];
00694         }
00695     }
00696 
00703     public function set_timeout( $seconds, $microseconds ) {
00704         $this->_timeout_seconds = $seconds;
00705         $this->_timeout_microseconds = $microseconds;
00706     }
00707 
00708     // }}}
00709     // }}}
00710     // {{{ private methods
00711     // {{{ _close_sock()
00712 
00720     function _close_sock( $sock ) {
00721         $host = array_search( $sock, $this->_cache_sock );
00722         fclose( $this->_cache_sock[$host] );
00723         unset( $this->_cache_sock[$host] );
00724     }
00725 
00726     // }}}
00727     // {{{ _connect_sock()
00728 
00738     function _connect_sock( &$sock, $host ) {
00739         list( $ip, $port ) = preg_split( '/:(?=\d)/', $host );
00740         $sock = false;
00741         $timeout = $this->_connect_timeout;
00742         $errno = $errstr = null;
00743         for ( $i = 0; !$sock && $i < $this->_connect_attempts; $i++ ) {
00744             wfSuppressWarnings();
00745             if ( $this->_persistent == 1 ) {
00746                 $sock = pfsockopen( $ip, $port, $errno, $errstr, $timeout );
00747             } else {
00748                 $sock = fsockopen( $ip, $port, $errno, $errstr, $timeout );
00749             }
00750             wfRestoreWarnings();
00751         }
00752         if ( !$sock ) {
00753             $this->_error_log( "Error connecting to $host: $errstr\n" );
00754             $this->_dead_host( $host );
00755             return false;
00756         }
00757 
00758         // Initialise timeout
00759         stream_set_timeout( $sock, $this->_timeout_seconds, $this->_timeout_microseconds );
00760 
00761         // If the connection was persistent, flush the read buffer in case there
00762         // was a previous incomplete request on this connection
00763         if ( $this->_persistent ) {
00764             $this->_flush_read_buffer( $sock );
00765         }
00766         return true;
00767     }
00768 
00769     // }}}
00770     // {{{ _dead_sock()
00771 
00779     function _dead_sock( $sock ) {
00780         $host = array_search( $sock, $this->_cache_sock );
00781         $this->_dead_host( $host );
00782     }
00783 
00787     function _dead_host( $host ) {
00788         $parts = explode( ':', $host );
00789         $ip = $parts[0];
00790         $this->_host_dead[$ip] = time() + 30 + intval( rand( 0, 10 ) );
00791         $this->_host_dead[$host] = $this->_host_dead[$ip];
00792         unset( $this->_cache_sock[$host] );
00793     }
00794 
00795     // }}}
00796     // {{{ get_sock()
00797 
00806     function get_sock( $key ) {
00807         if ( !$this->_active ) {
00808             return false;
00809         }
00810 
00811         if ( $this->_single_sock !== null ) {
00812             return $this->sock_to_host( $this->_single_sock );
00813         }
00814 
00815         $hv = is_array( $key ) ? intval( $key[0] ) : $this->_hashfunc( $key );
00816         if ( $this->_buckets === null ) {
00817             $bu = array();
00818             foreach ( $this->_servers as $v ) {
00819                 if ( is_array( $v ) ) {
00820                     for ( $i = 0; $i < $v[1]; $i++ ) {
00821                         $bu[] = $v[0];
00822                     }
00823                 } else {
00824                     $bu[] = $v;
00825                 }
00826             }
00827             $this->_buckets = $bu;
00828             $this->_bucketcount = count( $bu );
00829         }
00830 
00831         $realkey = is_array( $key ) ? $key[1] : $key;
00832         for ( $tries = 0; $tries < 20; $tries++ ) {
00833             $host = $this->_buckets[$hv % $this->_bucketcount];
00834             $sock = $this->sock_to_host( $host );
00835             if ( is_resource( $sock ) ) {
00836                 return $sock;
00837             }
00838             $hv = $this->_hashfunc( $hv . $realkey );
00839         }
00840 
00841         return false;
00842     }
00843 
00844     // }}}
00845     // {{{ _hashfunc()
00846 
00855     function _hashfunc( $key ) {
00856         # Hash function must be in [0,0x7ffffff]
00857         # We take the first 31 bits of the MD5 hash, which unlike the hash
00858         # function used in a previous version of this client, works
00859         return hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
00860     }
00861 
00862     // }}}
00863     // {{{ _incrdecr()
00864 
00875     function _incrdecr( $cmd, $key, $amt = 1 ) {
00876         if ( !$this->_active ) {
00877             return null;
00878         }
00879 
00880         $sock = $this->get_sock( $key );
00881         if ( !is_resource( $sock ) ) {
00882             return null;
00883         }
00884 
00885         $key = is_array( $key ) ? $key[1] : $key;
00886         if ( isset( $this->stats[$cmd] ) ) {
00887             $this->stats[$cmd]++;
00888         } else {
00889             $this->stats[$cmd] = 1;
00890         }
00891         if ( !$this->_fwrite( $sock, "$cmd $key $amt\r\n" ) ) {
00892             return null;
00893         }
00894 
00895         $line = $this->_fgets( $sock );
00896         $match = array();
00897         if ( !preg_match( '/^(\d+)/', $line, $match ) ) {
00898             return null;
00899         }
00900         return $match[1];
00901     }
00902 
00903     // }}}
00904     // {{{ _load_items()
00905 
00916     function _load_items( $sock, &$ret, &$casToken = null ) {
00917         $results = array();
00918 
00919         while ( 1 ) {
00920             $decl = $this->_fgets( $sock );
00921 
00922             if ( $decl === false ) {
00923                 /*
00924                  * If nothing can be read, something is wrong because we know exactly when
00925                  * to stop reading (right after "END") and we return right after that.
00926                  */
00927                 return false;
00928             } elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+) (\d+)$/', $decl, $match ) ) {
00929                 /*
00930                  * Read all data returned. This can be either one or multiple values.
00931                  * Save all that data (in an array) to be processed later: we'll first
00932                  * want to continue reading until "END" before doing anything else,
00933                  * to make sure that we don't leave our client in a state where it's
00934                  * output is not yet fully read.
00935                  */
00936                 $results[] = array(
00937                     $match[1], // rkey
00938                     $match[2], // flags
00939                     $match[3], // len
00940                     $match[4], // casToken
00941                     $this->_fread( $sock, $match[3] + 2 ), // data
00942                 );
00943             } elseif ( $decl == "END" ) {
00944                 if ( count( $results ) == 0 ) {
00945                     return false;
00946                 }
00947 
00952                 foreach ( $results as $vars ) {
00953                     list( $rkey, $flags, $len, $casToken, $data ) = $vars;
00954 
00955                     if ( $data === false || substr( $data, -2 ) !== "\r\n" ) {
00956                         $this->_handle_error( $sock,
00957                             'line ending missing from data block from $1' );
00958                         return false;
00959                     }
00960                     $data = substr( $data, 0, -2 );
00961                     $ret[$rkey] = $data;
00962 
00963                     if ( $this->_have_zlib && $flags & self::COMPRESSED ) {
00964                         $ret[$rkey] = gzuncompress( $ret[$rkey] );
00965                     }
00966 
00967                     /*
00968                      * This unserialize is the exact reason that we only want to
00969                      * process data after having read until "END" (instead of doing
00970                      * this right away): "unserialize" can trigger outside code:
00971                      * in the event that $ret[$rkey] is a serialized object,
00972                      * unserializing it will trigger __wakeup() if present. If that
00973                      * function attempted to read from memcached (while we did not
00974                      * yet read "END"), these 2 calls would collide.
00975                      */
00976                     if ( $flags & self::SERIALIZED ) {
00977                         $ret[$rkey] = unserialize( $ret[$rkey] );
00978                     }
00979                 }
00980 
00981                 return true;
00982             } else {
00983                 $this->_handle_error( $sock, 'Error parsing response from $1' );
00984                 return false;
00985             }
00986         }
00987     }
00988 
00989     // }}}
00990     // {{{ _set()
00991 
01008     function _set( $cmd, $key, $val, $exp, $casToken = null ) {
01009         if ( !$this->_active ) {
01010             return false;
01011         }
01012 
01013         $sock = $this->get_sock( $key );
01014         if ( !is_resource( $sock ) ) {
01015             return false;
01016         }
01017 
01018         if ( isset( $this->stats[$cmd] ) ) {
01019             $this->stats[$cmd]++;
01020         } else {
01021             $this->stats[$cmd] = 1;
01022         }
01023 
01024         $flags = 0;
01025 
01026         if ( !is_scalar( $val ) ) {
01027             $val = serialize( $val );
01028             $flags |= self::SERIALIZED;
01029             if ( $this->_debug ) {
01030                 $this->_debugprint( sprintf( "client: serializing data as it is not scalar\n" ) );
01031             }
01032         }
01033 
01034         $len = strlen( $val );
01035 
01036         if ( $this->_have_zlib && $this->_compress_enable
01037             && $this->_compress_threshold && $len >= $this->_compress_threshold
01038         ) {
01039             $c_val = gzcompress( $val, 9 );
01040             $c_len = strlen( $c_val );
01041 
01042             if ( $c_len < $len * ( 1 - self::COMPRESSION_SAVINGS ) ) {
01043                 if ( $this->_debug ) {
01044                     $this->_debugprint( sprintf( "client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len ) );
01045                 }
01046                 $val = $c_val;
01047                 $len = $c_len;
01048                 $flags |= self::COMPRESSED;
01049             }
01050         }
01051 
01052         $command = "$cmd $key $flags $exp $len";
01053         if ( $casToken ) {
01054             $command .= " $casToken";
01055         }
01056 
01057         if ( !$this->_fwrite( $sock, "$command\r\n$val\r\n" ) ) {
01058             return false;
01059         }
01060 
01061         $line = $this->_fgets( $sock );
01062 
01063         if ( $this->_debug ) {
01064             $this->_debugprint( sprintf( "%s %s (%s)\n", $cmd, $key, $line ) );
01065         }
01066         if ( $line == "STORED" ) {
01067             return true;
01068         }
01069         return false;
01070     }
01071 
01072     // }}}
01073     // {{{ sock_to_host()
01074 
01083     function sock_to_host( $host ) {
01084         if ( isset( $this->_cache_sock[$host] ) ) {
01085             return $this->_cache_sock[$host];
01086         }
01087 
01088         $sock = null;
01089         $now = time();
01090         list( $ip, /* $port */) = explode( ':', $host );
01091         if ( isset( $this->_host_dead[$host] ) && $this->_host_dead[$host] > $now ||
01092             isset( $this->_host_dead[$ip] ) && $this->_host_dead[$ip] > $now
01093         ) {
01094             return null;
01095         }
01096 
01097         if ( !$this->_connect_sock( $sock, $host ) ) {
01098             return null;
01099         }
01100 
01101         // Do not buffer writes
01102         stream_set_write_buffer( $sock, 0 );
01103 
01104         $this->_cache_sock[$host] = $sock;
01105 
01106         return $this->_cache_sock[$host];
01107     }
01108 
01112     function _debugprint( $text ) {
01113         wfDebugLog( 'memcached', $text );
01114     }
01115 
01119     function _error_log( $text ) {
01120         wfDebugLog( 'memcached-serious', "Memcached error: $text" );
01121     }
01122 
01130     function _fwrite( $sock, $buf ) {
01131         $bytesWritten = 0;
01132         $bufSize = strlen( $buf );
01133         while ( $bytesWritten < $bufSize ) {
01134             $result = fwrite( $sock, $buf );
01135             $data = stream_get_meta_data( $sock );
01136             if ( $data['timed_out'] ) {
01137                 $this->_handle_error( $sock, 'timeout writing to $1' );
01138                 return false;
01139             }
01140             // Contrary to the documentation, fwrite() returns zero on error in PHP 5.3.
01141             if ( $result === false || $result === 0 ) {
01142                 $this->_handle_error( $sock, 'error writing to $1' );
01143                 return false;
01144             }
01145             $bytesWritten += $result;
01146         }
01147 
01148         return true;
01149     }
01150 
01157     function _handle_error( $sock, $msg ) {
01158         $peer = stream_socket_get_name( $sock, true  );
01159         if ( strval( $peer ) === '' ) {
01160             $peer = array_search( $sock, $this->_cache_sock );
01161             if ( $peer === false ) {
01162                 $peer = '[unknown host]';
01163             }
01164         }
01165         $msg = str_replace( '$1', $peer, $msg );
01166         $this->_error_log( "$msg\n" );
01167         $this->_dead_sock( $sock );
01168     }
01169 
01178     function _fread( $sock, $len ) {
01179         $buf = '';
01180         while ( $len > 0 ) {
01181             $result = fread( $sock, $len );
01182             $data = stream_get_meta_data( $sock );
01183             if ( $data['timed_out'] ) {
01184                 $this->_handle_error( $sock, 'timeout reading from $1' );
01185                 return false;
01186             }
01187             if ( $result === false ) {
01188                 $this->_handle_error( $sock, 'error reading buffer from $1' );
01189                 return false;
01190             }
01191             if ( $result === '' ) {
01192                 // This will happen if the remote end of the socket is shut down
01193                 $this->_handle_error( $sock, 'unexpected end of file reading from $1' );
01194                 return false;
01195             }
01196             $len -= strlen( $result );
01197             $buf .= $result;
01198         }
01199         return $buf;
01200     }
01201 
01209     function _fgets( $sock ) {
01210         $result = fgets( $sock );
01211         // fgets() may return a partial line if there is a select timeout after
01212         // a successful recv(), so we have to check for a timeout even if we
01213         // got a string response.
01214         $data = stream_get_meta_data( $sock );
01215         if ( $data['timed_out'] ) {
01216             $this->_handle_error( $sock, 'timeout reading line from $1' );
01217             return false;
01218         }
01219         if ( $result === false ) {
01220             $this->_handle_error( $sock, 'error reading line from $1' );
01221             return false;
01222         }
01223         if ( substr( $result, -2 ) === "\r\n" ) {
01224             $result = substr( $result, 0, -2 );
01225         } elseif ( substr( $result, -1 ) === "\n" ) {
01226             $result = substr( $result, 0, -1 );
01227         } else {
01228             $this->_handle_error( $sock, 'line ending missing in response from $1' );
01229             return false;
01230         }
01231         return $result;
01232     }
01233 
01238     function _flush_read_buffer( $f ) {
01239         if ( !is_resource( $f ) ) {
01240             return;
01241         }
01242         $r = array( $f );
01243         $w = null;
01244         $e = null;
01245         $n = stream_select( $r, $w, $e, 0, 0 );
01246         while ( $n == 1 && !feof( $f ) ) {
01247             fread( $f, 1024 );
01248             $r = array( $f );
01249             $w = null;
01250             $e = null;
01251             $n = stream_select( $r, $w, $e, 0, 0 );
01252         }
01253     }
01254 
01255     // }}}
01256     // }}}
01257     // }}}
01258 }
01259 
01260 // }}}
01261 
01262 class MemCachedClientforWiki extends MWMemcached {
01263 }