MemCached的PHP客户端操作类二
2007/01/25 16:40 ArthurXF
<?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($sock, substr($cmd, $offset, MC_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($sock, MC_BUFFER_SZ, PHP_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_INET, SOCK_STREAM, getprotobyname("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(0, 10));
$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($sock, substr($cmd, $offset, MC_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($sock, MC_BUFFER_SZ, PHP_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($sock, substr($cmd, $offset, MC_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($sock, 6, PHP_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($sock, substr($cmd, $offset, MC_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($sock, MC_BUFFER_SZ, PHP_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($line, 0, strpos($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($line, strpos($line, "\r\n")+2, strlen($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+2, 3);
if($end == "END")
{
$val[$rk] = substr($buf, 0, $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";
}
}
?>
/*
* 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($sock, substr($cmd, $offset, MC_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($sock, MC_BUFFER_SZ, PHP_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_INET, SOCK_STREAM, getprotobyname("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(0, 10));
$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($sock, substr($cmd, $offset, MC_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($sock, MC_BUFFER_SZ, PHP_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($sock, substr($cmd, $offset, MC_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($sock, 6, PHP_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($sock, substr($cmd, $offset, MC_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($sock, MC_BUFFER_SZ, PHP_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($line, 0, strpos($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($line, strpos($line, "\r\n")+2, strlen($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+2, 3);
if($end == "END")
{
$val[$rk] = substr($buf, 0, $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";
}
}
?>
MemCached的PHP客户端操作类一
2007/01/25 16:38 ArthurXF
<?php
//
// +---------------------------------------------------------------------------+
// | memcached client, PHP |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2003 Ryan T. Dean <rtdean@cytherianage.net> |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | 1. Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | 2. Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// +---------------------------------------------------------------------------+
// | Author: Ryan T. Dean <rtdean@cytherianage.net> |
// | Heavily influenced by the Perl memcached client by Brad Fitzpatrick. |
// | Permission granted by Brad Fitzpatrick for relicense of ported Perl |
// | client logic under 2-clause BSD license. |
// +---------------------------------------------------------------------------+
//
// $TCAnet$
//
/**
* This is the PHP client for memcached - a distributed memory cache daemon.
* More information is available at http://www.danga.com/memcached/
*
* Usage example:
*
* require_once 'memcached.php';
*
* $mc = new memcached(array(
* 'servers' => array('127.0.0.1:10000',
* array('192.0.0.1:10010', 2),
* '127.0.0.1:10020'),
* 'debug' => false,
* 'compress_threshold' => 10240,
* 'persistant' => true));
*
* $mc->add('key', array('some', 'array'));
* $mc->replace('key', 'some random string');
* $val = $mc->get('key');
*
* @author Ryan T. Dean <rtdean@cytherianage.net>
* @package memcached-client
* @version 0.1.2
*/
// {{{ requirements
// }}}
// {{{ constants
// {{{ flags
/**
* Flag: indicates data is serialized
*/
define("MEMCACHE_SERIALIZED", 1<<0);
/**
* Flag: indicates data is compressed
*/
define("MEMCACHE_COMPRESSED", 1<<1);
// }}}
/**
* Minimum savings to store data compressed
*/
define("COMPRESSION_SAVINGS", 0.20);
// }}}
// {{{ class memcached
/**
* memcached client class implemented using (p)fsockopen()
*
* @author Ryan T. Dean <rtdean@cytherianage.net>
* @package memcached-client
*/
class memcached
{
// {{{ properties
// {{{ public
/**
* Command statistics
*
* @var array
* @access public
*/
var $stats;
// }}}
// {{{ private
/**
* Cached Sockets that are connected
*
* @var array
* @access private
*/
var $_cache_sock;
/**
* Current debug status; 0 - none to 9 - profiling
*
* @var boolean
* @access private
*/
var $_debug;
/**
* Dead hosts, assoc array, 'host'=>'unixtime when ok to check again'
*
* @var array
* @access private
*/
var $_host_dead;
/**
* Is compression available?
*
* @var boolean
* @access private
*/
var $_have_zlib;
/**
* Do we want to use compression?
*
* @var boolean
* @access private
*/
var $_compress_enable;
/**
* At how many bytes should we compress?
*
* @var interger
* @access private
*/
var $_compress_threshold;
/**
* Are we using persistant links?
*
* @var boolean
* @access private
*/
var $_persistant;
/**
* If only using one server; contains ip:port to connect to
*
* @var string
* @access private
*/
var $_single_sock;
/**
* Array containing ip:port or array(ip:port, weight)
*
* @var array
* @access private
*/
var $_servers;
/**
* Our bit buckets
*
* @var array
* @access private
*/
var $_buckets;
/**
* Total # of bit buckets we have
*
* @var interger
* @access private
*/
var $_bucketcount;
/**
* # of total servers we have
*
* @var interger
* @access private
*/
var $_active;
// }}}
// }}}
// {{{ methods
// {{{ public functions
// {{{ memcached()
/**
* Memcache initializer
*
* @param array $args Associative array of settings
*
* @return mixed
* @access public
*/
function memcached ($args)
{
$this->set_servers($args['servers']);
$this->_debug = $args['debug'];
$this->stats = array();
$this->_compress_threshold = $args['compress_threshold'];
$this->_persistant = isset($args['persistant']) ? $args['persistant'] : false;
$this->_compress_enable = true;
$this->_have_zlib = function_exists("gzcompress");
$this->_cache_sock = array();
$this->_host_dead = array();
}
// }}}
// {{{ add()
/**
* Adds a key/value to the memcache server if one isn't already set with
* that key
*
* @param string $key Key to set with data
* @param mixed $val Value to store
* @param interger $exp (optional) Time to expire data at
*
* @return boolean
* @access public
*/
function add ($key, $val, $exp = 0)
{
return $this->_set('add', $key, $val, $exp);
}
// }}}
// {{{ decr()
/**
* Decriment a value stored on the memcache server
*
* @param string $key Key to decriment
* @param interger $amt (optional) Amount to decriment
*
* @return mixed FALSE on failure, value on success
* @access public
*/
function decr ($key, $amt=1)
{
return $this->_incrdecr('decr', $key, $amt);
}
// }}}
// {{{ delete()
/**
* Deletes a key from the server, optionally after $time
*
* @param string $key Key to delete
* @param interger $time (optional) How long to wait before deleting
*
* @return boolean TRUE on success, FALSE on failure
* @access public
*/
function delete ($key, $time = 0)
{
if (!$this->_active)
return false;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$key = is_array($key) ? $key[1] : $key;
$this->stats['delete']++;
$cmd = "delete $key $time\r\n";
if(!fwrite($sock, $cmd, strlen($cmd)))
{
$this->_dead_sock($sock);
return false;
}
$res = trim(fgets($sock));
if ($this->_debug)
printf("MemCache: delete %s (%s)\n", $key, $res);
if ($res == "DELETED")
return true;
return false;
}
// }}}
// {{{ disconnect_all()
/**
* Disconnects all connected sockets
*
* @access public
*/
function disconnect_all ()
{
foreach ($this->_cache_sock as $sock)
fclose($sock);
$this->_cache_sock = array();
}
// }}}
// {{{ enable_compress()
/**
* Enable / Disable compression
*
* @param boolean $enable TRUE to enable, FALSE to disable
*
* @access public
*/
function enable_compress ($enable)
{
$this->_compress_enable = $enable;
}
// }}}
// {{{ forget_dead_hosts()
/**
* Forget about all of the dead hosts
*
* @access public
*/
function forget_dead_hosts ()
{
$this->_host_dead = array();
}
// }}}
// {{{ get()
/**
* Retrieves the value associated with the key from the memcache server
*
* @param string $key Key to retrieve
*
* @return mixed
* @access public
*/
function get ($key)
{
if (!$this->_active)
return false;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$this->stats['get']++;
$cmd = "get $key\r\n";
if (!fwrite($sock, $cmd, strlen($cmd)))
{
$this->_dead_sock($sock);
return false;
}
$val = array();
$this->_load_items($sock, $val);
if ($this->_debug)
foreach ($val as $k => $v)
printf("MemCache: sock %s got %s => %s\r\n", $sock, $k, $v);
return $val[$key];
}
// }}}
// {{{ get_multi()
/**
* Get multiple keys from the server(s)
*
* @param array $keys Keys to retrieve
*
* @return array
* @access public
*/
function get_multi ($keys)
{
if (!$this->_active)
return false;
$this->stats['get_multi']++;
foreach ($keys as $key)
{
$sock = $this->get_sock($key);
if (!is_resource($sock)) continue;
$key = is_array($key) ? $key[1] : $key;
if (!isset($sock_keys[$sock]))
{
$sock_keys[$sock] = array();
$socks[] = $sock;
}
$sock_keys[$sock][] = $key;
}
// Send out the requests
foreach ($socks as $sock)
{
$cmd = "get";
foreach ($sock_keys[$sock] as $key)
{
$cmd .= " ". $key;
}
$cmd .= "\r\n";
if (fwrite($sock, $cmd, strlen($cmd)))
{
$gather[] = $sock;
} else
{
$this->_dead_sock($sock);
}
}
// Parse responses
$val = array();
foreach ($gather as $sock)
{
$this->_load_items($sock, $val);
}
if ($this->_debug)
foreach ($val as $k => $v)
printf("MemCache: got %s => %s\r\n", $k, $v);
return $val;
}
// }}}
// {{{ incr()
/**
* Increments $key (optionally) by $amt
*
* @param string $key Key to increment
* @param interger $amt (optional) amount to increment
*
* @return interger New key value?
* @access public
*/
function incr ($key, $amt=1)
{
return $this->_incrdecr('incr', $key, $amt);
}
// }}}
// {{{ replace()
/**
* Overwrites an existing value for key; only works if key is already set
*
* @param string $key Key to set value as
* @param mixed $value Value to store
* @param interger $exp (optional) Experiation time
*
* @return boolean
* @access public
*/
function replace ($key, $value, $exp=0)
{
return $this->_set('replace', $key, $value, $exp);
}
// }}}
// {{{ run_command()
/**
* Passes through $cmd to the memcache server connected by $sock; returns
* output as an array (null array if no output)
*
* NOTE: due to a possible bug in how PHP reads while using fgets(), each
* line may not be terminated by a \r\n. More specifically, my testing
* has shown that, on FreeBSD at least, each line is terminated only
* with a \n. This is with the PHP flag auto_detect_line_endings set
* to falase (the default).
*
* @param resource $sock Socket to send command on
* @param string $cmd Command to run
*
* @return array Output array
* @access public
*/
function run_command ($sock, $cmd)
{
if (!is_resource($sock))
return array();
if (!fwrite($sock, $cmd, strlen($cmd)))
return array();
while (true)
{
$res = fgets($sock);
$ret[] = $res;
if (preg_match('/^END/', $res))
break;
if (strlen($res) == 0)
break;
}
return $ret;
}
// }}}
// {{{ set()
/**
* Unconditionally sets a key to a given value in the memcache. Returns true
* if set successfully.
*
* @param string $key Key to set value as
* @param mixed $value Value to set
* @param interger $exp (optional) Experiation time
*
* @return boolean TRUE on success
* @access public
*/
function set ($key, $value, $exp=0)
{
return $this->_set('set', $key, $value, $exp);
}
// }}}
// {{{ set_compress_threshold()
/**
* Sets the compression threshold
*
* @param interger $thresh Threshold to compress if larger than
*
* @access public
*/
function set_compress_threshold ($thresh)
{
$this->_compress_threshold = $thresh;
}
// }}}
// {{{ set_debug()
/**
* Sets the debug flag
*
* @param boolean $dbg TRUE for debugging, FALSE otherwise
*
* @access public
*
* @see memcahced::memcached
*/
function set_debug ($dbg)
{
$this->_debug = $dbg;
}
// }}}
// {{{ set_servers()
/**
* Sets the server list to distribute key gets and puts between
*
* @param array $list Array of servers to connect to
*
* @access public
*
* @see memcached::memcached()
*/
function set_servers ($list)
{
$this->_servers = $list;
$this->_active = count($list);
$this->_buckets = null;
$this->_bucketcount = 0;
$this->_single_sock = null;
if ($this->_active == 1)
$this->_single_sock = $this->_servers[0];
}
// }}}
// }}}
// {{{ private methods
// {{{ _close_sock()
/**
* Close the specified socket
*
* @param string $sock Socket to close
*
* @access private
*/
function _close_sock ($sock)
{
$host = array_search($sock, $this->_cache_sock);
fclose($this->_cache_sock[$host]);
unset($this->_cache_sock[$host]);
}
// }}}
// {{{ _connect_sock()
/**
* Connects $sock to $host, timing out after $timeout
*
* @param interger $sock Socket to connect
* @param string $host Host:IP to connect to
* @param float $timeout (optional) Timeout value, defaults to 0.25s
*
* @return boolean
* @access private
*/
function _connect_sock (&$sock, $host, $timeout = 0.25)
{
list ($ip, $port) = explode(":", $host);
if ($this->_persistant == 1)
{
$sock = @pfsockopen($ip, $port, $errno, $errstr, $timeout);
} else
{
$sock = @fsockopen($ip, $port, $errno, $errstr, $timeout);
}
if (!$sock)
return false;
return true;
}
// }}}
// {{{ _dead_sock()
/**
* Marks a host as dead until 30-40 seconds in the future
*
* @param string $sock Socket to mark as dead
*
* @access private
*/
function _dead_sock ($sock)
{
$host = array_search($sock, $this->_cache_sock);
list ($ip, $port) = explode(":", $host);
$this->_host_dead[$ip] = time() + 30 + intval(rand(0, 10));
$this->_host_dead[$host] = $this->_host_dead[$ip];
unset($this->_cache_sock[$host]);
}
// }}}
// {{{ get_sock()
/**
* get_sock
*
* @param string $key Key to retrieve value for;
*
* @return mixed resource on success, false on failure
* @access private
*/
function get_sock ($key)
{
if (!$this->_active)
return false;
if ($this->_single_sock !== null)
return $this->sock_to_host($this->_single_sock);
$hv = is_array($key) ? intval($key[0]) : $this->_hashfunc($key);
if ($this->_buckets === null)
{
foreach ($this->_servers as $v)
{
if (is_array($v))
{
for ($i=0; $i<$v[1]; $i++)
$bu[] = $v[0];
} else
{
$bu[] = $v;
}
}
$this->_buckets = $bu;
$this->_bucketcount = count($bu);
}
$realkey = is_array($key) ? $key[1] : $key;
for ($tries = 0; $tries<20; $tries++)
{
$host = $this->_buckets[$hv % $this->_bucketcount];
$sock = $this->sock_to_host($host);
if (is_resource($sock))
return $sock;
$hv += $this->_hashfunc($tries . $realkey);
}
return false;
}
// }}}
// {{{ _hashfunc()
/**
* Creates a hash interger based on the $key
*
* @param string $key Key to hash
*
* @return interger Hash value
* @access private
*/
function _hashfunc ($key)
{
$hash = 0;
for ($i=0; $i<strlen($key); $i++)
{
$hash = $hash*33 + ord($key[$i]);
}
return $hash;
}
// }}}
// {{{ _incrdecr()
/**
* Perform increment/decriment on $key
*
* @param string $cmd Command to perform
* @param string $key Key to perform it on
* @param interger $amt Amount to adjust
*
* @return interger New value of $key
* @access private
*/
function _incrdecr ($cmd, $key, $amt=1)
{
if (!$this->_active)
return null;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return null;
$key = is_array($key) ? $key[1] : $key;
$this->stats[$cmd]++;
if (!fwrite($sock, "$cmd $key $amt\r\n"))
return $this->_dead_sock($sock);
stream_set_timeout($sock, 1, 0);
$line = fgets($sock);
if (!preg_match('/^(\d+)/', $line, $match))
return null;
return $match[1];
}
// }}}
// {{{ _load_items()
/**
* Load items into $ret from $sock
*
* @param resource $sock Socket to read from
* @param array $ret Returned values
*
* @access private
*/
function _load_items ($sock, &$ret)
{
while (1)
{
$decl = fgets($sock);
if ($decl == "END\r\n")
{
return true;
} elseif (preg_match('/^VALUE (\S+) (\d+) (\d+)\r\n$/', $decl, $match))
{
list($rkey, $flags, $len) = array($match[1], $match[2], $match[3]);
$bneed = $len+2;
$offset = 0;
while ($bneed > 0)
{
$data = fread($sock, $bneed);
$n = strlen($data);
if ($n == 0)
break;
$offset += $n;
$bneed -= $n;
$ret[$rkey] .= $data;
}
if ($offset != $len+2)
{
// Something is borked!
if ($this->_debug)
printf("Something is borked! key %s expecting %d got %d length\n", $rkey, $len+2, $offset);
unset($ret[$rkey]);
$this->_close_sock($sock);
return false;
}
$ret[$rkey] = rtrim($ret[$rkey]);
if ($this->_have_zlib && $flags & MEMCACHE_COMPRESSED)
$ret[$rkey] = gzuncompress($ret[$rkey]);
if ($flags & MEMCACHE_SERIALIZED)
$ret[$rkey] = unserialize($ret[$rkey]);
} else
{
if ($this->_debug)
print("Error parsing memcached response\n");
return 0;
}
}
}
// }}}
// {{{ _set()
/**
* Performs the requested storage operation to the memcache server
*
* @param string $cmd Command to perform
* @param string $key Key to act on
* @param mixed $val What we need to store
* @param interger $exp When it should expire
*
* @return boolean
* @access private
*/
function _set ($cmd, $key, $val, $exp)
{
if (!$this->_active)
return false;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$this->stats[$cmd]++;
$flags = 0;
if (!is_scalar($val))
{
$val = serialize($val);
$flags |= MEMCACHE_SERIALIZED;
if ($this->_debug)
printf("client: serializing data as it is not scalar\n");
}
$len = strlen($val);
if ($this->_have_zlib && $this->_compress_enable &&
$this->_compress_threshold && $len >= $this->_compress_threshold)
{
$c_val = gzcompress($val, 9);
$c_len = strlen($c_val);
if ($c_len < $len*(1 - COMPRESS_SAVINGS))
{
if ($this->_debug)
printf("client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len);
$val = $c_val;
$len = $c_len;
$flags |= MEMCACHE_COMPRESSED;
}
}
if (!fwrite($sock, "$cmd $key $flags $exp $len\r\n$val\r\n"))
return $this->_dead_sock($sock);
$line = trim(fgets($sock));
if ($this->_debug)
{
if ($flags & MEMCACHE_COMPRESSED)
$val = 'compressed data';
printf("MemCache: %s %s => %s (%s)\n", $cmd, $key, $val, $line);
}
if ($line == "STORED")
return true;
return false;
}
// }}}
// {{{ sock_to_host()
/**
* Returns the socket for the host
*
* @param string $host Host:IP to get socket for
*
* @return mixed IO Stream or false
* @access private
*/
function sock_to_host ($host)
{
if (isset($this->_cache_sock[$host]))
return $this->_cache_sock[$host];
$now = time();
list ($ip, $port) = explode (":", $host);
if (isset($this->_host_dead[$host]) && $this->_host_dead[$host] > $now ||
isset($this->_host_dead[$ip]) && $this->_host_dead[$ip] > $now)
return null;
if (!$this->_connect_sock($sock, $host))
return $this->_dead_sock($host);
// Do not buffer writes
stream_set_write_buffer($sock, 0);
$this->_cache_sock[$host] = $sock;
return $this->_cache_sock[$host];
}
// }}}
// }}}
// }}}
}
// }}}
?>
//
// +---------------------------------------------------------------------------+
// | memcached client, PHP |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2003 Ryan T. Dean <rtdean@cytherianage.net> |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | 1. Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | 2. Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// +---------------------------------------------------------------------------+
// | Author: Ryan T. Dean <rtdean@cytherianage.net> |
// | Heavily influenced by the Perl memcached client by Brad Fitzpatrick. |
// | Permission granted by Brad Fitzpatrick for relicense of ported Perl |
// | client logic under 2-clause BSD license. |
// +---------------------------------------------------------------------------+
//
// $TCAnet$
//
/**
* This is the PHP client for memcached - a distributed memory cache daemon.
* More information is available at http://www.danga.com/memcached/
*
* Usage example:
*
* require_once 'memcached.php';
*
* $mc = new memcached(array(
* 'servers' => array('127.0.0.1:10000',
* array('192.0.0.1:10010', 2),
* '127.0.0.1:10020'),
* 'debug' => false,
* 'compress_threshold' => 10240,
* 'persistant' => true));
*
* $mc->add('key', array('some', 'array'));
* $mc->replace('key', 'some random string');
* $val = $mc->get('key');
*
* @author Ryan T. Dean <rtdean@cytherianage.net>
* @package memcached-client
* @version 0.1.2
*/
// {{{ requirements
// }}}
// {{{ constants
// {{{ flags
/**
* Flag: indicates data is serialized
*/
define("MEMCACHE_SERIALIZED", 1<<0);
/**
* Flag: indicates data is compressed
*/
define("MEMCACHE_COMPRESSED", 1<<1);
// }}}
/**
* Minimum savings to store data compressed
*/
define("COMPRESSION_SAVINGS", 0.20);
// }}}
// {{{ class memcached
/**
* memcached client class implemented using (p)fsockopen()
*
* @author Ryan T. Dean <rtdean@cytherianage.net>
* @package memcached-client
*/
class memcached
{
// {{{ properties
// {{{ public
/**
* Command statistics
*
* @var array
* @access public
*/
var $stats;
// }}}
// {{{ private
/**
* Cached Sockets that are connected
*
* @var array
* @access private
*/
var $_cache_sock;
/**
* Current debug status; 0 - none to 9 - profiling
*
* @var boolean
* @access private
*/
var $_debug;
/**
* Dead hosts, assoc array, 'host'=>'unixtime when ok to check again'
*
* @var array
* @access private
*/
var $_host_dead;
/**
* Is compression available?
*
* @var boolean
* @access private
*/
var $_have_zlib;
/**
* Do we want to use compression?
*
* @var boolean
* @access private
*/
var $_compress_enable;
/**
* At how many bytes should we compress?
*
* @var interger
* @access private
*/
var $_compress_threshold;
/**
* Are we using persistant links?
*
* @var boolean
* @access private
*/
var $_persistant;
/**
* If only using one server; contains ip:port to connect to
*
* @var string
* @access private
*/
var $_single_sock;
/**
* Array containing ip:port or array(ip:port, weight)
*
* @var array
* @access private
*/
var $_servers;
/**
* Our bit buckets
*
* @var array
* @access private
*/
var $_buckets;
/**
* Total # of bit buckets we have
*
* @var interger
* @access private
*/
var $_bucketcount;
/**
* # of total servers we have
*
* @var interger
* @access private
*/
var $_active;
// }}}
// }}}
// {{{ methods
// {{{ public functions
// {{{ memcached()
/**
* Memcache initializer
*
* @param array $args Associative array of settings
*
* @return mixed
* @access public
*/
function memcached ($args)
{
$this->set_servers($args['servers']);
$this->_debug = $args['debug'];
$this->stats = array();
$this->_compress_threshold = $args['compress_threshold'];
$this->_persistant = isset($args['persistant']) ? $args['persistant'] : false;
$this->_compress_enable = true;
$this->_have_zlib = function_exists("gzcompress");
$this->_cache_sock = array();
$this->_host_dead = array();
}
// }}}
// {{{ add()
/**
* Adds a key/value to the memcache server if one isn't already set with
* that key
*
* @param string $key Key to set with data
* @param mixed $val Value to store
* @param interger $exp (optional) Time to expire data at
*
* @return boolean
* @access public
*/
function add ($key, $val, $exp = 0)
{
return $this->_set('add', $key, $val, $exp);
}
// }}}
// {{{ decr()
/**
* Decriment a value stored on the memcache server
*
* @param string $key Key to decriment
* @param interger $amt (optional) Amount to decriment
*
* @return mixed FALSE on failure, value on success
* @access public
*/
function decr ($key, $amt=1)
{
return $this->_incrdecr('decr', $key, $amt);
}
// }}}
// {{{ delete()
/**
* Deletes a key from the server, optionally after $time
*
* @param string $key Key to delete
* @param interger $time (optional) How long to wait before deleting
*
* @return boolean TRUE on success, FALSE on failure
* @access public
*/
function delete ($key, $time = 0)
{
if (!$this->_active)
return false;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$key = is_array($key) ? $key[1] : $key;
$this->stats['delete']++;
$cmd = "delete $key $time\r\n";
if(!fwrite($sock, $cmd, strlen($cmd)))
{
$this->_dead_sock($sock);
return false;
}
$res = trim(fgets($sock));
if ($this->_debug)
printf("MemCache: delete %s (%s)\n", $key, $res);
if ($res == "DELETED")
return true;
return false;
}
// }}}
// {{{ disconnect_all()
/**
* Disconnects all connected sockets
*
* @access public
*/
function disconnect_all ()
{
foreach ($this->_cache_sock as $sock)
fclose($sock);
$this->_cache_sock = array();
}
// }}}
// {{{ enable_compress()
/**
* Enable / Disable compression
*
* @param boolean $enable TRUE to enable, FALSE to disable
*
* @access public
*/
function enable_compress ($enable)
{
$this->_compress_enable = $enable;
}
// }}}
// {{{ forget_dead_hosts()
/**
* Forget about all of the dead hosts
*
* @access public
*/
function forget_dead_hosts ()
{
$this->_host_dead = array();
}
// }}}
// {{{ get()
/**
* Retrieves the value associated with the key from the memcache server
*
* @param string $key Key to retrieve
*
* @return mixed
* @access public
*/
function get ($key)
{
if (!$this->_active)
return false;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$this->stats['get']++;
$cmd = "get $key\r\n";
if (!fwrite($sock, $cmd, strlen($cmd)))
{
$this->_dead_sock($sock);
return false;
}
$val = array();
$this->_load_items($sock, $val);
if ($this->_debug)
foreach ($val as $k => $v)
printf("MemCache: sock %s got %s => %s\r\n", $sock, $k, $v);
return $val[$key];
}
// }}}
// {{{ get_multi()
/**
* Get multiple keys from the server(s)
*
* @param array $keys Keys to retrieve
*
* @return array
* @access public
*/
function get_multi ($keys)
{
if (!$this->_active)
return false;
$this->stats['get_multi']++;
foreach ($keys as $key)
{
$sock = $this->get_sock($key);
if (!is_resource($sock)) continue;
$key = is_array($key) ? $key[1] : $key;
if (!isset($sock_keys[$sock]))
{
$sock_keys[$sock] = array();
$socks[] = $sock;
}
$sock_keys[$sock][] = $key;
}
// Send out the requests
foreach ($socks as $sock)
{
$cmd = "get";
foreach ($sock_keys[$sock] as $key)
{
$cmd .= " ". $key;
}
$cmd .= "\r\n";
if (fwrite($sock, $cmd, strlen($cmd)))
{
$gather[] = $sock;
} else
{
$this->_dead_sock($sock);
}
}
// Parse responses
$val = array();
foreach ($gather as $sock)
{
$this->_load_items($sock, $val);
}
if ($this->_debug)
foreach ($val as $k => $v)
printf("MemCache: got %s => %s\r\n", $k, $v);
return $val;
}
// }}}
// {{{ incr()
/**
* Increments $key (optionally) by $amt
*
* @param string $key Key to increment
* @param interger $amt (optional) amount to increment
*
* @return interger New key value?
* @access public
*/
function incr ($key, $amt=1)
{
return $this->_incrdecr('incr', $key, $amt);
}
// }}}
// {{{ replace()
/**
* Overwrites an existing value for key; only works if key is already set
*
* @param string $key Key to set value as
* @param mixed $value Value to store
* @param interger $exp (optional) Experiation time
*
* @return boolean
* @access public
*/
function replace ($key, $value, $exp=0)
{
return $this->_set('replace', $key, $value, $exp);
}
// }}}
// {{{ run_command()
/**
* Passes through $cmd to the memcache server connected by $sock; returns
* output as an array (null array if no output)
*
* NOTE: due to a possible bug in how PHP reads while using fgets(), each
* line may not be terminated by a \r\n. More specifically, my testing
* has shown that, on FreeBSD at least, each line is terminated only
* with a \n. This is with the PHP flag auto_detect_line_endings set
* to falase (the default).
*
* @param resource $sock Socket to send command on
* @param string $cmd Command to run
*
* @return array Output array
* @access public
*/
function run_command ($sock, $cmd)
{
if (!is_resource($sock))
return array();
if (!fwrite($sock, $cmd, strlen($cmd)))
return array();
while (true)
{
$res = fgets($sock);
$ret[] = $res;
if (preg_match('/^END/', $res))
break;
if (strlen($res) == 0)
break;
}
return $ret;
}
// }}}
// {{{ set()
/**
* Unconditionally sets a key to a given value in the memcache. Returns true
* if set successfully.
*
* @param string $key Key to set value as
* @param mixed $value Value to set
* @param interger $exp (optional) Experiation time
*
* @return boolean TRUE on success
* @access public
*/
function set ($key, $value, $exp=0)
{
return $this->_set('set', $key, $value, $exp);
}
// }}}
// {{{ set_compress_threshold()
/**
* Sets the compression threshold
*
* @param interger $thresh Threshold to compress if larger than
*
* @access public
*/
function set_compress_threshold ($thresh)
{
$this->_compress_threshold = $thresh;
}
// }}}
// {{{ set_debug()
/**
* Sets the debug flag
*
* @param boolean $dbg TRUE for debugging, FALSE otherwise
*
* @access public
*
* @see memcahced::memcached
*/
function set_debug ($dbg)
{
$this->_debug = $dbg;
}
// }}}
// {{{ set_servers()
/**
* Sets the server list to distribute key gets and puts between
*
* @param array $list Array of servers to connect to
*
* @access public
*
* @see memcached::memcached()
*/
function set_servers ($list)
{
$this->_servers = $list;
$this->_active = count($list);
$this->_buckets = null;
$this->_bucketcount = 0;
$this->_single_sock = null;
if ($this->_active == 1)
$this->_single_sock = $this->_servers[0];
}
// }}}
// }}}
// {{{ private methods
// {{{ _close_sock()
/**
* Close the specified socket
*
* @param string $sock Socket to close
*
* @access private
*/
function _close_sock ($sock)
{
$host = array_search($sock, $this->_cache_sock);
fclose($this->_cache_sock[$host]);
unset($this->_cache_sock[$host]);
}
// }}}
// {{{ _connect_sock()
/**
* Connects $sock to $host, timing out after $timeout
*
* @param interger $sock Socket to connect
* @param string $host Host:IP to connect to
* @param float $timeout (optional) Timeout value, defaults to 0.25s
*
* @return boolean
* @access private
*/
function _connect_sock (&$sock, $host, $timeout = 0.25)
{
list ($ip, $port) = explode(":", $host);
if ($this->_persistant == 1)
{
$sock = @pfsockopen($ip, $port, $errno, $errstr, $timeout);
} else
{
$sock = @fsockopen($ip, $port, $errno, $errstr, $timeout);
}
if (!$sock)
return false;
return true;
}
// }}}
// {{{ _dead_sock()
/**
* Marks a host as dead until 30-40 seconds in the future
*
* @param string $sock Socket to mark as dead
*
* @access private
*/
function _dead_sock ($sock)
{
$host = array_search($sock, $this->_cache_sock);
list ($ip, $port) = explode(":", $host);
$this->_host_dead[$ip] = time() + 30 + intval(rand(0, 10));
$this->_host_dead[$host] = $this->_host_dead[$ip];
unset($this->_cache_sock[$host]);
}
// }}}
// {{{ get_sock()
/**
* get_sock
*
* @param string $key Key to retrieve value for;
*
* @return mixed resource on success, false on failure
* @access private
*/
function get_sock ($key)
{
if (!$this->_active)
return false;
if ($this->_single_sock !== null)
return $this->sock_to_host($this->_single_sock);
$hv = is_array($key) ? intval($key[0]) : $this->_hashfunc($key);
if ($this->_buckets === null)
{
foreach ($this->_servers as $v)
{
if (is_array($v))
{
for ($i=0; $i<$v[1]; $i++)
$bu[] = $v[0];
} else
{
$bu[] = $v;
}
}
$this->_buckets = $bu;
$this->_bucketcount = count($bu);
}
$realkey = is_array($key) ? $key[1] : $key;
for ($tries = 0; $tries<20; $tries++)
{
$host = $this->_buckets[$hv % $this->_bucketcount];
$sock = $this->sock_to_host($host);
if (is_resource($sock))
return $sock;
$hv += $this->_hashfunc($tries . $realkey);
}
return false;
}
// }}}
// {{{ _hashfunc()
/**
* Creates a hash interger based on the $key
*
* @param string $key Key to hash
*
* @return interger Hash value
* @access private
*/
function _hashfunc ($key)
{
$hash = 0;
for ($i=0; $i<strlen($key); $i++)
{
$hash = $hash*33 + ord($key[$i]);
}
return $hash;
}
// }}}
// {{{ _incrdecr()
/**
* Perform increment/decriment on $key
*
* @param string $cmd Command to perform
* @param string $key Key to perform it on
* @param interger $amt Amount to adjust
*
* @return interger New value of $key
* @access private
*/
function _incrdecr ($cmd, $key, $amt=1)
{
if (!$this->_active)
return null;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return null;
$key = is_array($key) ? $key[1] : $key;
$this->stats[$cmd]++;
if (!fwrite($sock, "$cmd $key $amt\r\n"))
return $this->_dead_sock($sock);
stream_set_timeout($sock, 1, 0);
$line = fgets($sock);
if (!preg_match('/^(\d+)/', $line, $match))
return null;
return $match[1];
}
// }}}
// {{{ _load_items()
/**
* Load items into $ret from $sock
*
* @param resource $sock Socket to read from
* @param array $ret Returned values
*
* @access private
*/
function _load_items ($sock, &$ret)
{
while (1)
{
$decl = fgets($sock);
if ($decl == "END\r\n")
{
return true;
} elseif (preg_match('/^VALUE (\S+) (\d+) (\d+)\r\n$/', $decl, $match))
{
list($rkey, $flags, $len) = array($match[1], $match[2], $match[3]);
$bneed = $len+2;
$offset = 0;
while ($bneed > 0)
{
$data = fread($sock, $bneed);
$n = strlen($data);
if ($n == 0)
break;
$offset += $n;
$bneed -= $n;
$ret[$rkey] .= $data;
}
if ($offset != $len+2)
{
// Something is borked!
if ($this->_debug)
printf("Something is borked! key %s expecting %d got %d length\n", $rkey, $len+2, $offset);
unset($ret[$rkey]);
$this->_close_sock($sock);
return false;
}
$ret[$rkey] = rtrim($ret[$rkey]);
if ($this->_have_zlib && $flags & MEMCACHE_COMPRESSED)
$ret[$rkey] = gzuncompress($ret[$rkey]);
if ($flags & MEMCACHE_SERIALIZED)
$ret[$rkey] = unserialize($ret[$rkey]);
} else
{
if ($this->_debug)
print("Error parsing memcached response\n");
return 0;
}
}
}
// }}}
// {{{ _set()
/**
* Performs the requested storage operation to the memcache server
*
* @param string $cmd Command to perform
* @param string $key Key to act on
* @param mixed $val What we need to store
* @param interger $exp When it should expire
*
* @return boolean
* @access private
*/
function _set ($cmd, $key, $val, $exp)
{
if (!$this->_active)
return false;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$this->stats[$cmd]++;
$flags = 0;
if (!is_scalar($val))
{
$val = serialize($val);
$flags |= MEMCACHE_SERIALIZED;
if ($this->_debug)
printf("client: serializing data as it is not scalar\n");
}
$len = strlen($val);
if ($this->_have_zlib && $this->_compress_enable &&
$this->_compress_threshold && $len >= $this->_compress_threshold)
{
$c_val = gzcompress($val, 9);
$c_len = strlen($c_val);
if ($c_len < $len*(1 - COMPRESS_SAVINGS))
{
if ($this->_debug)
printf("client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len);
$val = $c_val;
$len = $c_len;
$flags |= MEMCACHE_COMPRESSED;
}
}
if (!fwrite($sock, "$cmd $key $flags $exp $len\r\n$val\r\n"))
return $this->_dead_sock($sock);
$line = trim(fgets($sock));
if ($this->_debug)
{
if ($flags & MEMCACHE_COMPRESSED)
$val = 'compressed data';
printf("MemCache: %s %s => %s (%s)\n", $cmd, $key, $val, $line);
}
if ($line == "STORED")
return true;
return false;
}
// }}}
// {{{ sock_to_host()
/**
* Returns the socket for the host
*
* @param string $host Host:IP to get socket for
*
* @return mixed IO Stream or false
* @access private
*/
function sock_to_host ($host)
{
if (isset($this->_cache_sock[$host]))
return $this->_cache_sock[$host];
$now = time();
list ($ip, $port) = explode (":", $host);
if (isset($this->_host_dead[$host]) && $this->_host_dead[$host] > $now ||
isset($this->_host_dead[$ip]) && $this->_host_dead[$ip] > $now)
return null;
if (!$this->_connect_sock($sock, $host))
return $this->_dead_sock($host);
// Do not buffer writes
stream_set_write_buffer($sock, 0);
$this->_cache_sock[$host] = $sock;
return $this->_cache_sock[$host];
}
// }}}
// }}}
// }}}
}
// }}}
?>
跟着ArthurXF学FreeBSD15——从ports安装eAccelerator
2007/01/25 14:44 ArthurXF
什么是 eAccelerator ?
eAccelerator 是一个开源并且免费的 PHP 加速器,优化器,编码器,同时也能够为 PHP 提供动态内容缓存。它能够将 PHP 脚本缓存为已编译状态以达到提升 PHP 脚本运行性能的目的,因此传统的预编译几乎被消除。eAccelerator 也能够优化 PHP 脚本以提升 PHP 脚本的执行速度。eAccelerator 可有效降低服务器负载并且提高 PHP 程序速度达 1-10 倍。
TurckMMCache 是 eAccelerator 的前身。
( http://sourceforge.net/project/turckmm-cache/ by Dmitry Stogov )
eAccelerator 包含一个 PHP 编码器和加载器。您可以使用编码器对 .php 脚本进行编码,从而能够以非源代码方式发布您的 PHP 程序。经过编码的 PHP 程序可以运行在任何安装有PHP 解析环境和 eAccelerator 的站点上,由于编码后的 PHP 程序存储为已编译代码,并且已编译版本中不包含程序的源代码,因此,经过 eAccelerator 编码的 PHP 程序是不能被还原恢复的。当然,一些内部脚本可以被某些不同的反编译引擎工具(如 disassemblers, debuggers等)进行还原恢复,但这并非是微不足道的。
eAccelerator 与 Zend Optimizer 加载器兼容。在 php.ini 中,Zend Optimizer 必须在eAccelerator 之后加载。如果您的站点不运行任何经由 Zend 编码器编码的 PHP 脚本,那么我们并不推荐您在安装 eAccelerator 的服务器上安装 Zend Optimizer。
eAccelerator 不能运行于 CGI 模式下,但它可以运行于像 lighttpd 类似的 Fast-CGI模式。
以下是一些与 eAccelerator 具有相同功能的产品:
- Zend Performance Suite (http://www.zend.com)
- Alternative PHP Cache (http://pecl.php.net/package/APC)
eAccelerator已经是很常用的PHP平台预编译加速的手段了,安装后php执行速度大幅度提升,所以今天我们来学习从ports安装eAccelerator,并且配置好它。
作为Zend扩展安装配置文件
作为独立模块安装配置文件
这个是ArthurXF,session全部放进内存中的配置,倾情奉献给大家:
如果上面两个配置加上去了,都没能启动eAccelerator,那么执行下面的命令:
vi /usr/local/etc/php/extensions.ini
在最后加入下面的语句即可
extension=eaccelerator.so
安装好了,重起apache,看看phpinfo,是不是多了下面的内容啊?
with eAccelerator v0.9.5, Copyright (c) 2004-2006 eAccelerator, by eAccelerator
这样就说明安装好了。
下面看看eAccelerator配置选项
eaccelerator.shm_size
指定 eAccelerator 能够使用的共享内存数量,单位:MB。
"0" 代表操作系统默认。默认值为 "0"。
eaccelerator.cache_dir
用户磁盘缓存的目录。eAccelerator 在该目录中存储预编译代码、session 数据、内容等。
相同的数据也可以存储于共享内存中(以获得更快的存取速度)。默认值为 "/tmp/eaccelerator"。
eaccelerator.enable
开启或关闭 eAccelerator。"1" 为开启,"0" 为关闭。默认值为 "1"。
eaccelerator.optimizer
开启或关闭内部优化器,可以提升代码执行速度。"1" 为开启,"0" 为关闭。默认值为 "1"。
eaccelerator.debug
开启或关闭调试日志记录。"1" 为开启,"0" 为关闭。默认值为 "0"。
eaccelerator.check_mtime
开启或关闭 PHP 文件改动检查。"1" 为开启,"0" 为关闭。如果您想要在修改后重新编译 PHP
程序则需要设置为 "1"。默认值为 "1"。
eaccelerator.filter
判断哪些 PHP 文件必须缓存。您可以指定缓存和不缓存的文件类型(如 "*.php *.phtml"等)
如果参数以 "!" 开头,则匹配这些参数的文件被忽略缓存。默认值为 "",即,所有 PHP 文件
都将被缓存。
eaccelerator.shm_max
当使用 " eaccelerator_put() " 函数时禁止其向共享内存中存储过大的文件。该参数指定允许
存储的最大值,单位:字节 (10240, 10K, 1M)。"0" 为不限制。默认值为 "0"。
eaccelerator.shm_ttl
当 eAccelerator 获取新脚本的共享内存大小失败时,它将从共享内存中删除所有在
最后 "shm_ttl" 秒内无法存取的脚本缓存。默认值为 "0",即:不从共享内春中删除
任何缓存文件。
eaccelerator.shm_prune_period
当 eAccelerator 获取新脚本的共享内存大小失败时,他将试图从共享内存中删除早于
"shm_prune_period" 秒的缓存脚本。默认值为 "0",即:不从共享内春中删除
任何缓存文件。
eaccelerator.shm_only
允许或禁止将已编译脚本缓存在磁盘上。该选项对 session 数据和内容缓存无效。默认
值为 "0",即:使用磁盘和共享内存进行缓存。
eaccelerator.compress
允许或禁止压缩内容缓存。默认值为 "1",即:允许压缩。
eaccelerator.compress_level
指定内容缓存的压缩等级。默认值为 "9",为最高等级。
eaccelerator.name_sapce
一个所有键(keys)的前缀字符串。如果设置该前缀字符串则允许 .htaccess 或者 主配置
文件在相同主机上运行两个相同的键名。
eaccelerator.keys
eaccelerator.sessions
eaccelerator.content
判断哪些键(keys)、session 数据和内容将被缓存。可用参数值为:
"shm_and_disk" - 同时在共享内存和磁盘中缓存数据(默认值);
"shm" - 如果共享内存用尽或者数据容量大于 "eaccelerator.shm_max"
则在共享内存或磁盘中缓存数据;
"shm_only" - 仅在共享内存中缓存数据;
"disk_only" - 仅在磁盘中缓存数据;
"none" - 禁止缓存数据。
eAccelerator 应用程序接口(API)
eaccelerator_put($key, $value, $ttl=0)
将 $value 存储在共享内存中,并存储 $tll 秒。
eaccelerator_get($key)
从共享内存中返回 eaccelerator_put() 函数所存储的缓存数值,如果不存在或者已经
过期,则返回 null。
eaccelerator_rm($key)
从共享内存中删除 $key。
eaccelerator_gc()
删除所有过期的键(keys)
eaccelerator_lock($lock)
创建一个指定名称的锁(lock)。该锁可以通过 eaccelerator_unlock() 函数解除,在请求
结束时也会自动解锁。例如:
eaccelerator_unlock($lock)
解除指定名称的锁(lock)。
eaccelerator_set_session_handlers()
安装 eAccelerator session 句柄。
从 PHP 4.2.0 以后,您可以通过设置 php.ini 中的 "session.save_handler=eaacelerator"
安装 eAccelerator 句柄。
eaccelerator_cache_output($key, $eval_code, $ttl=0)
在共享内存中缓存 $eval_code 的输出,缓存 $ttl 秒。
可以调用 mmcach_rm() 函数删除相同 $key 的输出。例如:
eaccelerator_cache_result($key, $eval_code, $ttl=0)
在共享内存中缓存 $eval_code 的结果,缓存 $ttl 秒。
可以调用 mmcach_rm() 函数删除相同 $key 的结果。例如:
eaccelerator_cache_page($key, $ttl=0)
缓存整个页面,且缓存 $ttl 秒。例如:
eaccelerator_rm_page($key)
从缓存中删除由 eaccelerator_cache_page() 函数创建的相同 $key 的页。
eaccelerator_encode($filename)
返回 $filename 文件经过编译后的编码。
eaccelerator_load($code)
加载被 eaccelerator_encode() 函数编码过的脚本。
现在ArthurXF本人正在搞PHP等技术培训,如果想学习的人可以跟我联系。另外培训的招生简章在这个网址,想了解的可以去看看。加我QQ:29011218交流也可。
PHP培训招生简章
eAccelerator 是一个开源并且免费的 PHP 加速器,优化器,编码器,同时也能够为 PHP 提供动态内容缓存。它能够将 PHP 脚本缓存为已编译状态以达到提升 PHP 脚本运行性能的目的,因此传统的预编译几乎被消除。eAccelerator 也能够优化 PHP 脚本以提升 PHP 脚本的执行速度。eAccelerator 可有效降低服务器负载并且提高 PHP 程序速度达 1-10 倍。
TurckMMCache 是 eAccelerator 的前身。
( http://sourceforge.net/project/turckmm-cache/ by Dmitry Stogov )
eAccelerator 包含一个 PHP 编码器和加载器。您可以使用编码器对 .php 脚本进行编码,从而能够以非源代码方式发布您的 PHP 程序。经过编码的 PHP 程序可以运行在任何安装有PHP 解析环境和 eAccelerator 的站点上,由于编码后的 PHP 程序存储为已编译代码,并且已编译版本中不包含程序的源代码,因此,经过 eAccelerator 编码的 PHP 程序是不能被还原恢复的。当然,一些内部脚本可以被某些不同的反编译引擎工具(如 disassemblers, debuggers等)进行还原恢复,但这并非是微不足道的。
eAccelerator 与 Zend Optimizer 加载器兼容。在 php.ini 中,Zend Optimizer 必须在eAccelerator 之后加载。如果您的站点不运行任何经由 Zend 编码器编码的 PHP 脚本,那么我们并不推荐您在安装 eAccelerator 的服务器上安装 Zend Optimizer。
eAccelerator 不能运行于 CGI 模式下,但它可以运行于像 lighttpd 类似的 Fast-CGI模式。
以下是一些与 eAccelerator 具有相同功能的产品:
- Zend Performance Suite (http://www.zend.com)
- Alternative PHP Cache (http://pecl.php.net/package/APC)
eAccelerator已经是很常用的PHP平台预编译加速的手段了,安装后php执行速度大幅度提升,所以今天我们来学习从ports安装eAccelerator,并且配置好它。
引用
# cd /usr/ports/www/eaccelerator
make
make install clean
mkdir /tmp/eaccelerator
chown -R www /tmp/eaccelerator
chmod 0700 /tmp/eaccelerator
vi /usr/local/etc/php.ini
make
make install clean
mkdir /tmp/eaccelerator
chown -R www /tmp/eaccelerator
chmod 0700 /tmp/eaccelerator
vi /usr/local/etc/php.ini
作为Zend扩展安装配置文件
引用
zend_extension="/usr/local/lib/php/20060613/eaccelerator.so"
[eaccelerator]
eaccelerator.shm_size="16"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
[eaccelerator]
eaccelerator.shm_size="16"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
作为独立模块安装配置文件
引用
[eaccelerator]
extension=”eaccelerator.so”
eaccelerator.shm_size=”0″
eaccelerator.cache_dir=”/tmp/eaccelerator”
eaccelerator.enable=”1″
eaccelerator.optimizer=”1″
eaccelerator.check_mtime=”1″
eaccelerator.debug=”0″
eaccelerator.filter=”"
eaccelerator.shm_max=”0″
eaccelerator.shm_ttl=”0″
eaccelerator.shm_prune_period=”0″
eaccelerator.shm_only=”0″
eaccelerator.compress=”1″
eaccelerator.compress_level=”9″
extension=”eaccelerator.so”
eaccelerator.shm_size=”0″
eaccelerator.cache_dir=”/tmp/eaccelerator”
eaccelerator.enable=”1″
eaccelerator.optimizer=”1″
eaccelerator.check_mtime=”1″
eaccelerator.debug=”0″
eaccelerator.filter=”"
eaccelerator.shm_max=”0″
eaccelerator.shm_ttl=”0″
eaccelerator.shm_prune_period=”0″
eaccelerator.shm_only=”0″
eaccelerator.compress=”1″
eaccelerator.compress_level=”9″
这个是ArthurXF,session全部放进内存中的配置,倾情奉献给大家:
引用
[eaccelerator]
eaccelerator.shm_size="32"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="900"
eaccelerator.shm_prune_period="1800"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
eaccelerator.sessions="shm"
eaccelerator.keys="shm"
eaccelerator.content="shm"
eaccelerator.shm_size="32"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="900"
eaccelerator.shm_prune_period="1800"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
eaccelerator.sessions="shm"
eaccelerator.keys="shm"
eaccelerator.content="shm"
如果上面两个配置加上去了,都没能启动eAccelerator,那么执行下面的命令:
vi /usr/local/etc/php/extensions.ini
在最后加入下面的语句即可
extension=eaccelerator.so
安装好了,重起apache,看看phpinfo,是不是多了下面的内容啊?
with eAccelerator v0.9.5, Copyright (c) 2004-2006 eAccelerator, by eAccelerator
这样就说明安装好了。
下面看看eAccelerator配置选项
eaccelerator.shm_size
指定 eAccelerator 能够使用的共享内存数量,单位:MB。
"0" 代表操作系统默认。默认值为 "0"。
eaccelerator.cache_dir
用户磁盘缓存的目录。eAccelerator 在该目录中存储预编译代码、session 数据、内容等。
相同的数据也可以存储于共享内存中(以获得更快的存取速度)。默认值为 "/tmp/eaccelerator"。
eaccelerator.enable
开启或关闭 eAccelerator。"1" 为开启,"0" 为关闭。默认值为 "1"。
eaccelerator.optimizer
开启或关闭内部优化器,可以提升代码执行速度。"1" 为开启,"0" 为关闭。默认值为 "1"。
eaccelerator.debug
开启或关闭调试日志记录。"1" 为开启,"0" 为关闭。默认值为 "0"。
eaccelerator.check_mtime
开启或关闭 PHP 文件改动检查。"1" 为开启,"0" 为关闭。如果您想要在修改后重新编译 PHP
程序则需要设置为 "1"。默认值为 "1"。
eaccelerator.filter
判断哪些 PHP 文件必须缓存。您可以指定缓存和不缓存的文件类型(如 "*.php *.phtml"等)
如果参数以 "!" 开头,则匹配这些参数的文件被忽略缓存。默认值为 "",即,所有 PHP 文件
都将被缓存。
eaccelerator.shm_max
当使用 " eaccelerator_put() " 函数时禁止其向共享内存中存储过大的文件。该参数指定允许
存储的最大值,单位:字节 (10240, 10K, 1M)。"0" 为不限制。默认值为 "0"。
eaccelerator.shm_ttl
当 eAccelerator 获取新脚本的共享内存大小失败时,它将从共享内存中删除所有在
最后 "shm_ttl" 秒内无法存取的脚本缓存。默认值为 "0",即:不从共享内春中删除
任何缓存文件。
eaccelerator.shm_prune_period
当 eAccelerator 获取新脚本的共享内存大小失败时,他将试图从共享内存中删除早于
"shm_prune_period" 秒的缓存脚本。默认值为 "0",即:不从共享内春中删除
任何缓存文件。
eaccelerator.shm_only
允许或禁止将已编译脚本缓存在磁盘上。该选项对 session 数据和内容缓存无效。默认
值为 "0",即:使用磁盘和共享内存进行缓存。
eaccelerator.compress
允许或禁止压缩内容缓存。默认值为 "1",即:允许压缩。
eaccelerator.compress_level
指定内容缓存的压缩等级。默认值为 "9",为最高等级。
eaccelerator.name_sapce
一个所有键(keys)的前缀字符串。如果设置该前缀字符串则允许 .htaccess 或者 主配置
文件在相同主机上运行两个相同的键名。
eaccelerator.keys
eaccelerator.sessions
eaccelerator.content
判断哪些键(keys)、session 数据和内容将被缓存。可用参数值为:
"shm_and_disk" - 同时在共享内存和磁盘中缓存数据(默认值);
"shm" - 如果共享内存用尽或者数据容量大于 "eaccelerator.shm_max"
则在共享内存或磁盘中缓存数据;
"shm_only" - 仅在共享内存中缓存数据;
"disk_only" - 仅在磁盘中缓存数据;
"none" - 禁止缓存数据。
eAccelerator 应用程序接口(API)
eaccelerator_put($key, $value, $ttl=0)
将 $value 存储在共享内存中,并存储 $tll 秒。
eaccelerator_get($key)
从共享内存中返回 eaccelerator_put() 函数所存储的缓存数值,如果不存在或者已经
过期,则返回 null。
eaccelerator_rm($key)
从共享内存中删除 $key。
eaccelerator_gc()
删除所有过期的键(keys)
eaccelerator_lock($lock)
创建一个指定名称的锁(lock)。该锁可以通过 eaccelerator_unlock() 函数解除,在请求
结束时也会自动解锁。例如:
<?php
eaccelerator_lock("count");
eaccelerator_put("count",eaccelerator_get("count")+1));
?>
eaccelerator_lock("count");
eaccelerator_put("count",eaccelerator_get("count")+1));
?>
eaccelerator_unlock($lock)
解除指定名称的锁(lock)。
eaccelerator_set_session_handlers()
安装 eAccelerator session 句柄。
从 PHP 4.2.0 以后,您可以通过设置 php.ini 中的 "session.save_handler=eaacelerator"
安装 eAccelerator 句柄。
eaccelerator_cache_output($key, $eval_code, $ttl=0)
在共享内存中缓存 $eval_code 的输出,缓存 $ttl 秒。
可以调用 mmcach_rm() 函数删除相同 $key 的输出。例如:
<?php eaccelerator_cache_output('test', 'echo time(); phpinfo();', 30); ?>
eaccelerator_cache_result($key, $eval_code, $ttl=0)
在共享内存中缓存 $eval_code 的结果,缓存 $ttl 秒。
可以调用 mmcach_rm() 函数删除相同 $key 的结果。例如:
<?php eaccelerator_cache_output('test', 'time()." Hello";', 30); ?>
eaccelerator_cache_page($key, $ttl=0)
缓存整个页面,且缓存 $ttl 秒。例如:
<?php
eaccelerator_cache_page($_SERVER['PHP_SELF'].'?GET='.serialize($_GET),30);
echo time();
phpinfo();
?>
eaccelerator_cache_page($_SERVER['PHP_SELF'].'?GET='.serialize($_GET),30);
echo time();
phpinfo();
?>
eaccelerator_rm_page($key)
从缓存中删除由 eaccelerator_cache_page() 函数创建的相同 $key 的页。
eaccelerator_encode($filename)
返回 $filename 文件经过编译后的编码。
eaccelerator_load($code)
加载被 eaccelerator_encode() 函数编码过的脚本。
现在ArthurXF本人正在搞PHP等技术培训,如果想学习的人可以跟我联系。另外培训的招生简章在这个网址,想了解的可以去看看。加我QQ:29011218交流也可。
PHP培训招生简章