[转]MemCached的PHP客户端操作类二

来源:互联网 发布:工信部数据分析师证书 编辑:程序博客网 时间:2024/04/28 15:11

 

MemCached的PHP客户端操作类二

<?php
/*
 * MemCached PHP client
 * Copyright (c) 2003
 * Ryan Gilfether <hotrodder@rocketmail.com>
 * http://www.gilfether.com
 *
 * Originally translated from Brad Fitzpatrick's <brad@danga.com> MemCached Perl client
 * See the memcached website:
 * http://www.danga.com/memcached/
 *
 * This module is Copyright (c) 2003 Ryan Gilfether.
 * All rights reserved.
 * You may distribute under the terms of the GNU General Public License
 * This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
 *
 */

/**
 * version string
 */
define("MC_VERSION""1.0.10"
);
/**
 * int, buffer size used for sending and receiving
 * data from sockets
 */
define("MC_BUFFER_SZ"1024
);
/**
 * MemCached error numbers
 */
define("MC_ERR_NOT_ACTIVE"1001);    
// no active servers
define("MC_ERR_SOCKET_WRITE"1002);    
// socket_write() failed
define("MC_ERR_SOCKET_READ"1003);    
// socket_read() failed
define("MC_ERR_SOCKET_CONNECT"1004);    
// failed to connect to host
define("MC_ERR_DELETE"1005);        
// delete() did not recieve DELETED command
define("MC_ERR_HOST_FORMAT"1006);    
// sock_to_host() invalid host format
define("MC_ERR_HOST_DEAD"1007);    
// sock_to_host() host is dead
define("MC_ERR_GET_SOCK"1008);    
// get_sock() failed to find a valid socket
define("MC_ERR_SET"1009);        
// _set() failed to receive the STORED response
define("MC_ERR_GET_KEY"1010);        
// _load_items no values returned for key(s)
define("MC_ERR_LOADITEM_END"1011);    
// _load_items failed to receive END response
define("MC_ERR_LOADITEM_BYTES"1012);    
// _load_items bytes read larger than bytes available


/**
 * MemCached PHP client Class.
 *
 * Communicates with the MemCached server, and executes the MemCached protocol
 * MemCached available at http://www.danga.com/memcached
 *
 * @author Ryan Gilfether <ryan@gilfether.com>
 * @package MemCachedClient
 * @access public
 * @version 1.0.10
 */
class 
MemCachedClient
{
    
/**
     * array of servers no long available
     * @var array
     */
    
var $host_dead
;
    
/**
     * array of open sockets
     * @var array
     */
    
var $cache_sock
;
    
/**
     * determine if debugging is either on or off
     * @var bool
     */
    
var $debug
;
    
/**
     * array of servers to attempt to use, "host:port" string format
     * @var array
     */
    
var $servers
;
    
/**
     * count of currently active connections to servers
     * @var int
     */
    
var $active
;
    
/**
     * error code if one is set
     * @var int
     */
    
var $errno
;
    
/**
     * string describing error
     * @var string
     */
    
var $errstr
;
    
/**
     * size of val to force compression; 0 turns off; defaults 1
     * @ var int
     */
    
var $compress 1
;
    
/**
     * temp flag to turn compression on/off; defaults on
     * @ var int
     */
    
var $comp_active 1
;

    
/**
     * array that contains parsed out buckets
     * @ var array
     */
    
var $bucket
;


    
/**
     * Constructor
     *
     * Creates a new MemCachedClient object
     * Takes one parameter, a array of options.  The most important key is
     * $options["servers"], but that can also be set later with the set_servers()
     * method.  The servers must be an array of hosts, each of which is
     * either a scalar of the form <10.0.0.10:11211> or an array of the
     * former and an integer weight value.  (the default weight if
     * unspecified is 1.)  It's recommended that weight values be kept as low
     * as possible, as this module currently allocates memory for bucket
     * distribution proportional to the total host weights.
     * $options["debug"] turns the debugging on if set to true
     *
     * @access public
     * @param array $option an array of servers and debug status
     * @return object MemCachedClient the new MemCachedClient object
     */
    
function MemCachedClient($options 0
)
    {
        if(
is_array($options
))
        {
            
$this->set_servers($options["servers"
]);
            
$this->debug $options["debug"
];
            
$this->compress $options["compress"
];
            
$this->cache_sock 
= array();
        }

        
$this->errno 0
;
        
$this->errstr ""
;
    }


    
/**
     * sets up the list of servers and the ports to connect to
     * takes an array of servers in the same format as in the constructor
     *
     * @access public
     * @param array $servers array of servers in the format described in the constructor
     */
    
function set_servers($servers
)
    {
        
$this->servers $servers
;
        
$this->active count($this->servers
);
    }


    
/**
     * if $do_debug is set to true, will print out
     * debugging info, else debug is turned off
     *
     * @access public
     * @param bool $do_debug set to true to turn debugging on, false to turn off
     */
    
function set_debug($do_debug
)
    {
        
$this->debug $do_debug
;
    }


    
/**
     * remove all cached hosts that are no longer good
     *
     * @access public
     */
    
function forget_dead_hosts
()
    {
        unset(
$this->host_dead
);
    }


    
/**
     * disconnects from all servers
     *
     * @access public
     */
    
function disconnect_all
()
    {
        foreach(
$this->cache_sock as $sock
)
            
socket_close($sock
);

        unset(
$this->cache_sock
);
        
$this->active 0
;
    }


    
/**
     * removes the key from the MemCache
     * $time is the amount of time in seconds (or Unix time) until which
     * the client wishes the server to refuse "add" and "replace" commands
     * with this key. For this amount of item, the item is put into a
     * delete queue, which means that it won't possible to retrieve it by
     * the "get" command, but "add" and "replace" command with this key
     * will also fail (the "set" command will succeed, however). After the
     * time passes, the item is finally deleted from server memory.
     * The parameter $time is optional, and, if absent, defaults to 0
     * (which means that the item will be deleted immediately and further
     * storage commands with this key will succeed).
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *        MC_ERR_DELETE
     *
     * @access public
     * @param string $key the key to delete
     * @param timestamp $time optional, the amount of time server will refuse commands on key
     * @return bool TRUE on success, FALSE if key does not exist
     */
    
function delete($key$time 0
)
    {
        if(!
$this->active
)
        {
            
$this->errno MC_ERR_NOT_ACTIVE
;
            
$this->errstr "No active servers are available"
;

            if(
$this->debug
)
                
$this->_debug("delete(): There are no active servers available."
);

            return 
FALSE
;
        }

        
$sock $this->get_sock($key
);

        if(!
is_resource($sock
))
        {
            
$this->errno MC_ERR_GET_SOCK
;
            
$this->errstr "Unable to retrieve a valid socket."
;

            if(
$this->debug
)
                
$this->_debug("delete(): get_sock() returned an invalid socket."
);

            return 
FALSE
;
        }

        if(
is_array($key
))
            
$key $key[1
];

        
$cmd "delete $key $time/r/n"
;
        
$cmd_len strlen($cmd
);
        
$offset 0
;

        
// now send the command
        
while($offset $cmd_len
)
        {
            
$result socket_write($socksubstr($cmd$offsetMC_BUFFER_SZ), MC_BUFFER_SZ
);

            if(
$result !== FALSE
)
                
$offset += $result
;
            else if(
$offset $cmd_len
)
            {
                
$this->errno MC_ERR_SOCKET_WRITE
;
                
$this->errstr "Failed to write to socket."
;

                if(
$this->debug
)
                {
                    
$sockerr socket_last_error($sock
);
                    
$this->_debug("delete(): socket_write() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr
));
                }

                return 
FALSE
;
            }
        }

        
// now read the server's response
        
if(($retval socket_read($sockMC_BUFFER_SZPHP_NORMAL_READ)) === FALSE
)
        {
            
$this->errno MC_ERR_SOCKET_READ
;
            
$this->errstr "Failed to read from socket."
;

            if(
$this->debug
)
            {
                
$sockerr socket_last_error($sock
);
                
$this->_debug("delete(): socket_read() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr
));
            }

            return 
FALSE
;
        }

        
// remove the /r/n from the end
        
$retval rtrim($retval
);

        
// now read the server's response
        
if($retval == "DELETED"
)
            return 
TRUE
;
        else
        {
            
// something went wrong, create the error
            
$this->errno MC_ERR_DELETE
;
            
$this->errstr "Failed to receive DELETED response from server."
;

            if(
$this->debug
)
                
$this->_debug("delete(): Failed to receive DELETED response from server. Received $retval instead."
);

            return 
FALSE
;
        }
    }


    
/**
     * Like set(), but only stores in memcache if the key doesn't already exist.
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *        MC_ERR_SET
     *
     * @access public
     * @param string $key the key to set
     * @param mixed $val the value of the key
     * @param timestamp $exptime optional, the to to live of the key
     * @return bool TRUE on success, else FALSE
     */
    
function add($key$val$exptime 0
)
    {
        return 
$this->_set("add"$key$val$exptime
);
    }


    
/**
     * Like set(), but only stores in memcache if the key already exists.
     * returns TRUE on success else FALSE
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *        MC_ERR_SET
     *
     * @access public
     * @param string $key the key to set
     * @param mixed $val the value of the key
     * @param timestamp $exptime optional, the to to live of the key
     * @return bool TRUE on success, else FALSE
     */
    
function replace($key$val$exptime 0
)
    {
        return 
$this->_set("replace"$key$val$exptime
);
    }


    
/**
     * Unconditionally sets a key to a given value in the memcache.  Returns true
     * if it was stored successfully.
     * The $key can optionally be an arrayref, with the first element being the
     * hash value, as described above.
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *        MC_ERR_SET
     *
     * @access public
     * @param string $key the key to set
     * @param mixed $val the value of the key
     * @param timestamp $exptime optional, the to to live of the key
     * @return bool TRUE on success, else FALSE
     */
    
function set($key$val$exptime 0
)
    {
        return 
$this->_set("set"$key$val$exptime
);
    }


    
/**
     * Retrieves a key from the memcache.  Returns the value (automatically
     * unserialized, if necessary) or FALSE if it fails.
     * The $key can optionally be an array, with the first element being the
     * hash value, if you want to avoid making this module calculate a hash
     * value.  You may prefer, for example, to keep all of a given user's
     * objects on the same memcache server, so you could use the user's
     * unique id as the hash value.
     * Possible errors set are:
     *        MC_ERR_GET_KEY
     *
     * @access public
     * @param string $key the key to retrieve
     * @return mixed the value of the key, FALSE on error
     */
    
function get($key
)
    {
        
$val =& $this->get_multi($key
);

        if(!
$val
)
        {
            
$this->errno MC_ERR_GET_KEY
;
            
$this->errstr "No value found for key $key"
;

            if(
$this->debug
)
                
$this->_debug("get(): No value found for key $key"
);

            return 
FALSE
;
        }

        return 
$val[$key
];
    }


    
/**
     * just like get(), but takes an array of keys, returns FALSE on error
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *
     * @access public
     * @param array $keys the keys to retrieve
     * @return array the value of each key, FALSE on error
     */
    
function get_multi($keys
)
    {
        
$sock_keys 
= array();
        
$socks 
= array();
        
$val 0
;

        if(!
$this->active
)
        {
            
$this->errno MC_ERR_NOT_ACTIVE
;
            
$this->errstr "No active servers are available"
;

            if(
$this->debug
)
                
$this->_debug("get_multi(): There are no active servers available."
);

            return 
FALSE
;
        }

        if(!
is_array($keys
))
        {
            
$arr[] = $keys
;
            
$keys $arr
;
        }

        foreach(
$keys as $k
)
        {
            
$sock $this->get_sock($k
);

            if(
$sock
)
            {
                
$k is_array($k) ? $k[1] : $k
;

                if(@!
is_array($sock_keys[$sock
]))
                    
$sock_keys[$sock
] = array();

                
// if $sock_keys[$sock] doesn't exist, create it
                
if(!$sock_keys[$sock
])
                    
$socks[] = $sock
;

                
$sock_keys[$sock][] = $k
;
            }
        }

        if(!
is_array($socks
))
        {
            
$arr[] = $socks
;
            
$socks $arr
;
        }

        foreach(
$socks as $s
)
        {
            
$this->_load_items($s$val$sock_keys[$sock
]);
        }

        if(
$this->debug
)
        {
            while(list(
$k$v) = @each($val
))
                
$this->_debug("MemCache: got $k = $v/n"
);
        }

        return 
$val
;
    }


    
/**
     * Sends a command to the server to atomically increment the value for
     * $key by $value, or by 1 if $value is undefined.  Returns FALSE if $key
     * doesn't exist on server, otherwise it returns the new value after
     * incrementing.  Value should be zero or greater.  Overflow on server
     * is not checked.  Be aware of values approaching 2**32.  See decr.
     * ONLY WORKS WITH NUMERIC VALUES
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *
     * @access public
     * @param string $key the keys to increment
     * @param int $value the amount to increment the key bye
     * @return int the new value of the key, else FALSE
     */
    
function incr($key$value 1
)
    {
        return 
$this->_incrdecr("incr"$key$value
);
    }


    
/**
     * Like incr, but decrements.  Unlike incr, underflow is checked and new
     * values are capped at 0.  If server value is 1, a decrement of 2
     * returns 0, not -1.
     * ONLY WORKS WITH NUMERIC VALUES
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *
     * @access public
     * @param string $key the keys to increment
     * @param int $value the amount to increment the key bye
     * @return int the new value of the key, else FALSE
     */
    
function decr($key$value 1
)
    {
        return 
$this->_incrdecr("decr"$key$value
);
    }


    
/**
     * When a function returns FALSE, an error code is set.
     * This funtion will return the error code.
     * See error_string()
     *
     * @access public
     * @return int the value of the last error code
     */
    
function error
()
    {
        return 
$this->errno
;
    }


    
/**
     * Returns a string describing the error set in error()
     * See error()
     *
     * @access public
     * @return int a string describing the error code given
     */
    
function error_string
()
    {
        return 
$this->errstr
;
    }


    
/**
     * Resets the error number and error string
     *
     * @access public
     */
    
function error_clear
()
    {
        
// reset to no error
        
$this->errno 0
;
        
$this->errstr ""
;
    }


    
/**
     *    temporarily sets compression on or off
     *    turning it off, and then back on will result in the compression threshold going
     *    back to the original setting from $options
     *    @param int $setting setting of compression (0=off|1=on)
     */

     
function set_compression($setting=1
) {
         if (
$setting != 0
) {
             
$this->comp_active 1
;
         } else {
             
$this->comp_active 0
;
         }
     }



    
/*
     * PRIVATE FUNCTIONS
     */


    /**
     * connects to a server
     * The $host may either a string int the form of host:port or an array of the
     * former and an integer weight value.  (the default weight if
     * unspecified is 1.) See the constructor for details
     * Possible errors set are:
     *        MC_ERR_HOST_FORMAT
     *        MC_ERR_HOST_DEAD
     *        MC_ERR_SOCKET_CONNECT
     *
     * @access private
     * @param mixed $host either an array or a string
     * @return resource the socket of the new connection, else FALSE
     */
    
function sock_to_host($host
)
    {
        if(
is_array($host
))
            
$host array_shift($host
);

        
$now time
();

        
// seperate the ip from the port, index 0 = ip, index 1 = port
        
$conn explode(":"$host
);
        if(
count($conn) != 2
)
        {
            
$this->errno MC_ERR_HOST_FORMAT
;
            
$this->errstr "Host address was not in the format of host:port"
;

            if(
$this->debug
)
                
$this->_debug("sock_to_host(): Host address was not in the format of host:port"
);

            return 
FALSE
;
        }

        if(@(
$this->host_dead[$host] && $this->host_dead[$host] > $now
) ||
        @(
$this->host_dead[$conn[0]] && $this->host_dead[$conn[0]] > $now
))
        {
            
$this->errno MC_ERR_HOST_DEAD
;
            
$this->errstr "Host $host is not available."
;

            if(
$this->debug
)
                
$this->_debug("sock_to_host(): Host $host is not available."
);

            return 
FALSE
;
        }

        
// connect to the server, if it fails, add it to the host_dead below
        
$sock socket_create (AF_INETSOCK_STREAMgetprotobyname("TCP"
));

        
// we need surpress the error message if a connection fails
        
if(!@socket_connect($sock$conn[0], $conn[1
]))
        {
            
$this->host_dead[$host]=$this->host_dead[$conn[0]]=$now+60+intval(rand(010
));

            
$this->errno MC_ERR_SOCKET_CONNECT
;
            
$this->errstr "Failed to connect to ".$conn[0].":".$conn[1
];

            if(
$this->debug
)
                
$this->_debug("sock_to_host(): Failed to connect to ".$conn[0].":".$conn[1
]);

            return 
FALSE
;
        }

        
// success, add to the list of sockets
        
$cache_sock[$host] = $sock
;

        return 
$sock
;
    }


    
/**
     * retrieves the socket associated with a key
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *
     * @access private
     * @param string $key the key to retrieve the socket from
     * @return resource the socket of the connection, else FALSE
     */
    
function get_sock($key
)
    {
        if(!
$this->active
)
        {
            
$this->errno MC_ERR_NOT_ACTIVE
;
            
$this->errstr "No active servers are available"
;

            if(
$this->debug
)
                
$this->_debug("get_sock(): There are no active servers available."
);

            return 
FALSE
;
        }

        
$hv is_array($key) ? intval($key[0]) : $this->_hashfunc($key
);

        if(!
$this->buckets
)
        {
            
$bu $this->buckets 
= array();

            foreach(
$this->servers as $v
)
            {
                if(
is_array($v
))
                {
                    for(
$i 1;  $i <= $v[1]; ++$i
)
                        
$bu[] =  $v[0
];
                }
                else
                    
$bu[] = $v
;
            }

            
$this->buckets $bu
;
        }

        
$real_key is_array($key) ? $key[1] : $key
;
        
$tries 0
;
        while(
$tries 20
)
        {
            
$host = @$this->buckets[$hv count($this->buckets
)];
            
$sock $this->sock_to_host($host
);

            if(
is_resource($sock
))
                return 
$sock
;

            
$hv += $this->_hashfunc($tries.$real_key
);
            ++
$tries
;
        }

        
$this->errno MC_ERR_GET_SOCK
;
        
$this->errstr "Unable to retrieve a valid socket."
;

        if(
$this->debug
)
            
$this->_debug("get_sock(): Unable to retrieve a valid socket."
);

        return 
FALSE
;
    }


    
/**
     * increments or decrements a numerical value in memcached. this function is
     * called from incr() and decr()
     * ONLY WORKS WITH NUMERIC VALUES
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *
     * @access private
     * @param string $cmdname the command to send, either incr or decr
     * @param string $key the key to perform the command on
     * @param mixed $value the value to incr or decr the key value by
     * @return int the new value of the key, FALSE if something went wrong
     */
    
function _incrdecr($cmdname$key$value
)
    {
        if(!
$this->active
)
        {
            
$this->errno MC_ERR_NOT_ACTIVE
;
            
$this->errstr "No active servers are available"
;

            if(
$this->debug
)
                
$this->_debug("_incrdecr(): There are no active servers available."
);

            return 
FALSE
;
        }

        
$sock $this->get_sock($key
);
        if(!
is_resource($sock
))
        {
            
$this->errno MC_ERR_GET_SOCK
;
            
$this->errstr "Unable to retrieve a valid socket."
;

            if(
$this->debug
)
                
$this->_debug("_incrdecr(): Invalid socket returned by get_sock()."
);

            return 
FALSE
;
        }

        if(
$value == ""
)
            
$value 1
;

        
$cmd "$cmdname $key $value/r/n"
;
        
$cmd_len strlen($cmd
);
        
$offset 0
;

        
// write the command to the server
        
while($offset $cmd_len
)
        {
            
$result socket_write($socksubstr($cmd$offsetMC_BUFFER_SZ), MC_BUFFER_SZ
);

            if(
$result !== FALSE
)
                
$offset += $result
;
            else if(
$offset $cmd_len
)
            {
                
$this->errno MC_ERR_SOCKET_WRITE
;
                
$this->errstr "Failed to write to socket."
;

                if(
$this->debug
)
                {
                    
$sockerr socket_last_error($sock
);
                    
$this->_debug("_incrdecr(): socket_write() returned FALSE. Error $errno: ".socket_strerror($sockerr
));
                }

                return 
FALSE
;
            }
        }

        
// now read the server's response
        
if(($retval socket_read($sockMC_BUFFER_SZPHP_NORMAL_READ)) === FALSE
)
        {
            
$this->errno MC_ERR_SOCKET_READ
;
            
$this->errstr "Failed to read from socket."
;

            if(
$this->debug
)
            {
                
$sockerr socket_last_error($sock
);
                
$this->_debug("_incrdecr(): socket_read() returned FALSE. Socket Error $errno: ".socket_strerror($sockerr
));
            }

            return 
FALSE
;
        }

        
// strip the /r/n from the end and return value
        
return trim($retval
);
    }

    
/**
     * sends the command to the server
     * Possible errors set are:
     *        MC_ERR_NOT_ACTIVE
     *        MC_ERR_GET_SOCK
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *        MC_ERR_SET
     *
     * @access private
     * @param string $cmdname the command to send, either incr or decr
     * @param string $key the key to perform the command on
     * @param mixed $value the value to set the key to
     * @param timestamp $exptime expiration time of the key
     * @return bool TRUE on success, else FALSE
     */
    
function _set($cmdname$key$val$exptime 0
)
    {
        if(!
$this->active
)
        {
            
$this->errno MC_ERR_NOT_ACTIVE
;
            
$this->errstr "No active servers are available"
;

            if(
$this->debug
)
                
$this->_debug("_set(): No active servers are available."
);

            return 
FALSE
;
        }

        
$sock $this->get_sock($key
);
        if(!
is_resource($sock
))
        {
            
$this->errno MC_ERR_GET_SOCK
;
            
$this->errstr "Unable to retrieve a valid socket."
;

            if(
$this->debug
)
                
$this->_debug("_set(): Invalid socket returned by get_sock()."
);

            return 
FALSE
;
        }

        
$flags 0
;
        
$key is_array($key) ? $key[1] : $key
;

        
$raw_val $val
;

        
// if the value is not scalar, we need to serialize it
        
if(!is_scalar($val
))
        {
            
$val serialize($val
);
            
$flags |= 1
;
        }

        if ((
$this->compress_active) && ($this->compress 0) && (strlen($val) > $this->compress
)) {
            
$this->_debug("_set(): compressing data. size in:".strlen($val
));
            
$cval=gzcompress($val
);
            
$this->_debug("_set(): done compressing data. size out:".strlen($cval
));
            if ((
strlen($cval) < strlen($val)) && (strlen($val) - strlen($cval) > 2048
)){
                
$flags |= 2
;
                
$val=$cval
;
            }
            unset(
$cval
);
        }

        
$len strlen($val
);
        if (!
is_int($exptime
))
            
$exptime 0
;

        
// send off the request
        
$cmd "$cmdname $key $flags $exptime $len/r/n$val/r/n"
;
        
$cmd_len strlen($cmd
);
        
$offset 0
;

        
// write the command to the server
        
while($offset $cmd_len
)
        {
            
$result socket_write($socksubstr($cmd$offsetMC_BUFFER_SZ), MC_BUFFER_SZ
);

            if(
$result !== FALSE
)
                
$offset += $result
;
            else if(
$offset $cmd_len
)
            {
                
$this->errno MC_ERR_SOCKET_WRITE
;
                
$this->errstr "Failed to write to socket."
;

                if(
$this->debug
)
                {
                    
$errno socket_last_error($sock
);
                    
$this->_debug("_set(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno
));
                }

                return 
FALSE
;
            }
        }

        
// now read the server's response
        
if(($l_szResponse socket_read($sock6PHP_NORMAL_READ)) === FALSE
)
        {
            
$this->errno MC_ERR_SOCKET_READ
;
            
$this->errstr "Failed to read from socket."
;

            if(
$this->debug
)
            {
                
$errno socket_last_error($sock
);
                
$this->_debug("_set(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno
));
            }

            return 
FALSE
;
        }

        if(
$l_szResponse == "STORED"
)
        {
            if(
$this->debug
)
                
$this->_debug("MemCache: $cmdname $key = $raw_val"
);

            return 
TRUE
;
        }

        
$this->errno MC_ERR_SET
;
        
$this->errstr "Failed to receive the STORED response from the server."
;

        if(
$this->debug
)
            
$this->_debug("_set(): Did not receive STORED as the server response! Received $l_szResponse instead."
);

        return 
FALSE
;
    }


    
/**
     * retrieves the value, and returns it unserialized
     * Possible errors set are:
     *        MC_ERR_SOCKET_WRITE
     *        MC_ERR_SOCKET_READ
     *        MC_ERR_GET_KEY
     *        MC_ERR_LOADITEM_END
     *        MC_ERR_LOADITEM_BYTES
     *
     * @access private
     * @param resource $sock the socket to connection we are retriving from
     * @param array $val reference to the values retrieved
     * @param mixed $sock_keys either a string or an array of keys to retrieve
     * @return array TRUE on success, else FALSE
     */
    
function _load_items($sock, &$val$sock_keys
)
    {
        
$val 
= array();
        
$cmd "get "
;

        if(!
is_array($sock_keys
))
        {
            
$arr[] = $sock_keys
;
            
$sock_keys $arr
;
        }

        foreach(
$sock_keys as $sk
)
            
$cmd .= $sk." "
;

        
$cmd .="/r/n"
;
        
$cmd_len strlen($cmd
);
        
$offset 0
;

        
// write the command to the server
        
while($offset $cmd_len
)
        {
            
$result socket_write($socksubstr($cmd$offsetMC_BUFFER_SZ), MC_BUFFER_SZ
);

            if(
$result !== FALSE
)
                
$offset += $result
;
            else if(
$offset $cmd_len
)
            {
                
$this->errno MC_ERR_SOCKET_WRITE
;
                
$this->errstr "Failed to write to socket."
;

                if(
$this->debug
)
                {
                    
$errno socket_last_error($sock
);
                    
$this->_debug("_load_items(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno
));
                }

                return 
FALSE
;
            }
        }

        
$len 0
;
        
$buf ""
;
        
$flags_array 
= array();

        
// now read the response from the server
        
while($line socket_read($sockMC_BUFFER_SZPHP_BINARY_READ
))
        {
            
// check for a socket_read error
            
if($line === FALSE
)
            {
                
$this->errno MC_ERR_SOCKET_READ
;
                
$this->errstr "Failed to read from socket."
;

                if(
$this->debug
)
                {
                    
$errno socket_last_error($sock
);
                    
$this->_debug("_load_items(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno
));
                }

                return 
FALSE
;
            }

            if(
$len == 0
)
            {
                
$header substr($line0strpos($line"/r/n"
));
                
$matches explode(" "$header
);

                if(
is_string($matches[1]) && is_numeric($matches[2]) && is_numeric($matches[3
]))
                {
                    
$rk $matches[1
];
                    
$flags $matches[2
];
                    
$len $matches[3
];

                    if(
$flags
)
                        
$flags_array[$rk] = $flags
;

                    
$len_array[$rk] = $len
;
                    
$bytes_read 0
;

                    
// get the left over data after the header is read
                    
$line substr($linestrpos($line"/r/n")+2strlen($line
));
                }
                else
                {
                    
$this->errno MC_ERR_GET_KEY
;
                    
$this->errstr "Requested key(s) returned no values."
;

                    
// something went wrong, we never recieved the header
                    
if($this->debug
)
                        
$this->_debug("_load_items(): Requested key(s) returned no values."
);

                    return 
FALSE
;
                }
            }

            
// skip over the extra return or newline
            
if($line == "/r" || $line == "/n"
)
                continue;

            
$bytes_read += strlen($line
);
            
$buf .= $line
;

            
// we read the all of the data, take in account
            // for the /r/nEND/r/n
            
if($bytes_read == ($len 7
))
            {
                
$end substr($buf$len+23
);
                if(
$end == "END"
)
                {
                    
$val[$rk] = substr($buf0$len
);

                    foreach(
$sock_keys as $sk
)
                    {
                        if(!isset(
$val[$sk
]))
                            continue;

                        if(
strlen($val[$sk]) != $len_array[$sk
])
                            continue;
                        if(@
$flags_array[$sk] & 2
)
                            
$val[$sk] = gzuncompress($val[$sk
]);

                        if(@
$flags_array[$sk] & 1
)
                            
$val[$sk] = unserialize($val[$sk
]);
                    }

                    return 
TRUE
;
                }
                else
                {
                    
$this->errno MC_ERR_LOADITEM_END
;
                    
$this->errstr "Failed to receive END response from server."
;

                    if(
$this->debug
)
                        
$this->_debug("_load_items(): Failed to receive END. Received $end instead."
);

                    return 
FALSE
;
                }
            }

            
// take in consideration for the "/r/nEND/r/n"
            
if($bytes_read > ($len 7
))
            {
                
$this->errno MC_ERR_LOADITEM_BYTES
;
                
$this->errstr "Bytes read from server greater than size of data."
;

                if(
$this->debug
)
                    
$this->_debug("_load_items(): Bytes read is greater than requested data size."
);

                return 
FALSE
;
            }

        }
    }


    
/**
     * creates our hash
     *
     * @access private
     * @param int $num
     * @return hash
     */
    
function _hashfunc($num
)
    {
        
$hash sprintf("%u",crc32($num
));

        return 
$hash
;
    }

    
/**
     * function that can be overridden to handle debug output
     * by default debug info is print to the screen
     *
     * @access private
     * @param $text string to output debug info
     */
    
function _debug($text
)
    {
        print 
$text "/r/n"
;
    }
}
?>