PHP控制路由器自动换IP
2007/01/29 12:40 ArthurXF
我们经常会碰到需要自动换IP的需求,比方模拟点击投票,数据采集被封IP,Alexa作弊等等,也就是需要经常换IP的,我们都可以通过PHP控制路由器来换IP,这样就不需要用按键精灵搞得那么累了。呵呵。
有的路由器是提供通过WEB方式管理路由器设置的,中间有一个功能就是断线和重新连接,我们就利用这个功能,呵呵,下面看代码:
使用方法:url("/userRpm/StatusRpm.htm?Disconnect=断%20线");
断线后可以不连接,一般路由器会自动连接的。当然你也可以控制连接,就是把断线改成连接,不过每个路由器的连接可能会有些不同,请自行查找路由器正确的连接URL。
有的路由器是提供通过WEB方式管理路由器设置的,中间有一个功能就是断线和重新连接,我们就利用这个功能,呵呵,下面看代码:
<?php
function url($target='')
{
$server = '192.168.1.1'; // IP address
$host = '192.168.1.1'; // Domain name
$port = 80;
$referer = 'http://'.$host.$target; // Referer
$username = "admin";#ADSL的路由的用户名
$password = "admin";#ADSL的路由的管理密码
$authorization = base64_encode($username.":".$password);
$File = fsockopen($server, $port, $errno, $errstr, 30);
if ($File)
{
$out = "GET $target HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Referer: $referer\r\n";
$out .= "Authorization: Basic $authorization\r\n";
$out .= "Connection: Close\r\n\r\n";
fputs($File, $out);
$makeFile = $buffer = "";
while ($buffer = fread($File,4096))
{
$makeFile = $makeFile.$buffer;
}
fclose($File);
}
}
?>
function url($target='')
{
$server = '192.168.1.1'; // IP address
$host = '192.168.1.1'; // Domain name
$port = 80;
$referer = 'http://'.$host.$target; // Referer
$username = "admin";#ADSL的路由的用户名
$password = "admin";#ADSL的路由的管理密码
$authorization = base64_encode($username.":".$password);
$File = fsockopen($server, $port, $errno, $errstr, 30);
if ($File)
{
$out = "GET $target HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Referer: $referer\r\n";
$out .= "Authorization: Basic $authorization\r\n";
$out .= "Connection: Close\r\n\r\n";
fputs($File, $out);
$makeFile = $buffer = "";
while ($buffer = fread($File,4096))
{
$makeFile = $makeFile.$buffer;
}
fclose($File);
}
}
?>
使用方法:url("/userRpm/StatusRpm.htm?Disconnect=断%20线");
断线后可以不连接,一般路由器会自动连接的。当然你也可以控制连接,就是把断线改成连接,不过每个路由器的连接可能会有些不同,请自行查找路由器正确的连接URL。
会自动整理和修复HTML页面代码的php函数tidy
2007/01/29 12:16 ArthurXF
我们程序员和页面美工是经常配合的,当我们碰到一个有经验的,制作页面代码整洁的页面制作人员呢,那真是我们程序员的福气,写代码的效率都提高了,如果碰到一个刚入行的,那就痛苦了,光看他们做的页面代码就头大了,大部分时间要花在给他们做的代码找错上面。现在随着HTMLArea、 FCKeditor 等免费又功能强大的 WYSWYG在线编辑器的流行,我们就更加需要修正由用户造成的HTML代码错误。
不过现在好了,我们可以直接用php函数来修复页面代码,HTML Tidy Library包已经被整合到了PHP5,所以我们在PHP5下面就可以直接使用tidy函数了。
tidy函数库有下面这些函数:
我们只看几个最常用函数,其他的自己研究哦,下面看看tidy_repair_string例子:
程序代码:
输出结果:
看看错误被修正了,呵呵,所以我们对于用户通过在线编辑器输入的内容都应该用tidy修复。
不过现在好了,我们可以直接用php函数来修复页面代码,HTML Tidy Library包已经被整合到了PHP5,所以我们在PHP5下面就可以直接使用tidy函数了。
tidy函数库有下面这些函数:
引用
ob_ tidyhandler
tidy_ access_ count
tidy_ clean_ repair
tidy_ config_ count
tidy:: _ _ construct
tidy_ diagnose
tidy_ error_ count
tidy_ get_ body
tidy_ get_ config
tidy_ get_ error_ buffer
tidy_ get_ head
tidy_ get_ html_ ver
tidy_ get_ html
tidy_ get_ opt_ doc
tidy_ get_ output
tidy_ get_ release
tidy_ get_ root
tidy_ get_ status
tidy_ getopt
tidy_ is_ xhtml
tidy_ is_ xml
tidy_ load_ config
tidy_ node-> get_ attr
tidy_ node-> get_ nodes
tidy_ node-> next
tidy_ node-> prev
tidy_ parse_ file
tidy_ parse_ string
tidy_ repair_ file
tidy_ repair_ string
tidy_ reset_ config
tidy_ save_ config
tidy_ set_ encoding
tidy_ setopt
tidy_ warning_ count
tidyNode-> hasChildren
tidyNode-> hasSiblings
tidyNode-> isAsp
tidyNode-> isComment
tidyNode-> isHtml
tidyNode-> isJste
tidyNode-> isPhp
tidyNode-> isText
tidy_ access_ count
tidy_ clean_ repair
tidy_ config_ count
tidy:: _ _ construct
tidy_ diagnose
tidy_ error_ count
tidy_ get_ body
tidy_ get_ config
tidy_ get_ error_ buffer
tidy_ get_ head
tidy_ get_ html_ ver
tidy_ get_ html
tidy_ get_ opt_ doc
tidy_ get_ output
tidy_ get_ release
tidy_ get_ root
tidy_ get_ status
tidy_ getopt
tidy_ is_ xhtml
tidy_ is_ xml
tidy_ load_ config
tidy_ node-> get_ attr
tidy_ node-> get_ nodes
tidy_ node-> next
tidy_ node-> prev
tidy_ parse_ file
tidy_ parse_ string
tidy_ repair_ file
tidy_ repair_ string
tidy_ reset_ config
tidy_ save_ config
tidy_ set_ encoding
tidy_ setopt
tidy_ warning_ count
tidyNode-> hasChildren
tidyNode-> hasSiblings
tidyNode-> isAsp
tidyNode-> isComment
tidyNode-> isHtml
tidyNode-> isJste
tidyNode-> isPhp
tidyNode-> isText
我们只看几个最常用函数,其他的自己研究哦,下面看看tidy_repair_string例子:
程序代码:
<?php
ob_start();
?>
<html>
<head>
<title>test</title>
</head>
<body>
<p>error</i>
</body>
</html>
<?php
$buffer = ob_get_clean();
$tidy = tidy_repair_string($buffer);
echo $tidy;
?>
ob_start();
?>
<html>
<head>
<title>test</title>
</head>
<body>
<p>error</i>
</body>
</html>
<?php
$buffer = ob_get_clean();
$tidy = tidy_repair_string($buffer);
echo $tidy;
?>
输出结果:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<title>test</title>
</head>
<body>
<p>error</p>
</body>
</html>
<html>
<head>
<title>test</title>
</head>
<body>
<p>error</p>
</body>
</html>
看看错误被修正了,呵呵,所以我们对于用户通过在线编辑器输入的内容都应该用tidy修复。
将php的session保存到内存中
2007/01/26 15:57 ArthurXF
今天一直在研究如何将session保存到内存中去,我们先说一下为什么要把session保存到内存中去,有什么好处呢?
因为如果我们的php程序中设置了session_start(),那么每个访问此文件的人都会产生一个session文件存在系统缓存中/tmp目录下,我们经常写的购物车或者会员系统经常要使用到session,所以如果一个流量很大的网站或者session保存时间设置很长的网站,就会在/tmp目录下建立大量session文件,虽然有时候这些文件大小仅仅为0,可是它确实还是占用了一个文件句柄,我有一台服务器因为设置的保存时间太长了,结果产生了120多万个session,占用了硬盘所有的文件句柄,导致再也不能建立新文件了,我用了35个小时来删除这120多万个session,只要你设置了session_start(),服务器会根据用户本地的cookie查找session的值,如果有保存session文件那么取回session的值,如果没有那么新建立一个session文件。可想而知,在120多万个文件中查找你要的文件,是多么恐怖的事情啊,我用ll命令等几分钟都没反应呢,所以太多的session文件势必造成系统运行缓慢,所以我想把session保存到内存中去,这样就可以大大加快session的读写速度,还可以减少硬盘的损耗,两全其美啊。哈哈
我找到了很多文章说使用MM一个session使用共享内存的模块可以做这个工作,刚好ports里面也有,我就安装了一下,而且还按照说明重新编译了php,并且把php.ini的session.save_handler = files改成了session.save_handler = mm,结果我不管怎么调用还是抱错说找不到save_handler mm,这可愁死我了,搞了一个早上,也没找出问题所在。因为MM测试未成功,我这里就不提供MM的安装步骤了。
结果我在察看phpinfo的时候,偶尔在session那段发现了Registered save handlers = files user eaccelerator ,呵呵,原来是我安装了eaccelerator,可能它和mm不能共存吧。我把session.save_handler = eaccelerator,重起apache后,运行正常。去/tmp下察看,确实不在生成新的session文件了,哈哈!
另外需要提醒大家一点的就是,虽然session保存到内存中有种种好处,但是如果使用apachectl restart,那么内存中的session会丢失哦,所以不要动不动就重起apache。呵呵!
不知道eaccelerator和不会安装eaccelerator的,请到这里来看我写的关于eaccelerator的安装方法!http://www.bizeway.net/read.php/29.htm
因为如果我们的php程序中设置了session_start(),那么每个访问此文件的人都会产生一个session文件存在系统缓存中/tmp目录下,我们经常写的购物车或者会员系统经常要使用到session,所以如果一个流量很大的网站或者session保存时间设置很长的网站,就会在/tmp目录下建立大量session文件,虽然有时候这些文件大小仅仅为0,可是它确实还是占用了一个文件句柄,我有一台服务器因为设置的保存时间太长了,结果产生了120多万个session,占用了硬盘所有的文件句柄,导致再也不能建立新文件了,我用了35个小时来删除这120多万个session,只要你设置了session_start(),服务器会根据用户本地的cookie查找session的值,如果有保存session文件那么取回session的值,如果没有那么新建立一个session文件。可想而知,在120多万个文件中查找你要的文件,是多么恐怖的事情啊,我用ll命令等几分钟都没反应呢,所以太多的session文件势必造成系统运行缓慢,所以我想把session保存到内存中去,这样就可以大大加快session的读写速度,还可以减少硬盘的损耗,两全其美啊。哈哈
我找到了很多文章说使用MM一个session使用共享内存的模块可以做这个工作,刚好ports里面也有,我就安装了一下,而且还按照说明重新编译了php,并且把php.ini的session.save_handler = files改成了session.save_handler = mm,结果我不管怎么调用还是抱错说找不到save_handler mm,这可愁死我了,搞了一个早上,也没找出问题所在。因为MM测试未成功,我这里就不提供MM的安装步骤了。
结果我在察看phpinfo的时候,偶尔在session那段发现了Registered save handlers = files user eaccelerator ,呵呵,原来是我安装了eaccelerator,可能它和mm不能共存吧。我把session.save_handler = eaccelerator,重起apache后,运行正常。去/tmp下察看,确实不在生成新的session文件了,哈哈!
另外需要提醒大家一点的就是,虽然session保存到内存中有种种好处,但是如果使用apachectl restart,那么内存中的session会丢失哦,所以不要动不动就重起apache。呵呵!
不知道eaccelerator和不会安装eaccelerator的,请到这里来看我写的关于eaccelerator的安装方法!http://www.bizeway.net/read.php/29.htm
跟着ArthurXF学FreeBSD16——ports中的make可带有的参数
2007/01/26 13:39 ArthurXF
我们经常使用ports来安装程序,ports中的make命令还可以有很多的功能,今天我将它可以带有的参数列出来,供大家参考使用。
现在ArthurXF本人正在搞PHP等技术培训,如果想学习的人可以跟我联系。另外培训的招生简章在这个网址,想了解的可以去看看。加我QQ:29011218交流也可。
PHP培训招生简章
引用
fetch - Retrieves ${DISTFILES} (and ${PATCHFILES} if defined) into ${DISTDIR} as necessary.
fetch-list - Show list of files that would be retrieved by fetch.
fetch-recursive - Retrieves ${DISTFILES} (and ${PATCHFILES} if defined), for port and dependencies into ${DISTDIR} as necessary.
fetch-recursive-list - Show list of files that would be retrieved by fetch-recursive.
fetch-required - Retrieves ${DISTFILES} (and ${PATCHFILES} if defined), for port and dependencies that are not already installed into ${DISTDIR}.
all-depends-list - Show all directories which are dependencies for this port.
build-depends-list - Show all directories which are build-dependencies for this port.
package-depends-list - Show all directories which are package-dependencies for this port.
run-depends-list - Show all directories which are run-dependencies for this port.
extract - Unpacks ${DISTFILES} into ${WRKDIR}.
patch - Apply any provided patches to the source.
configure - Runs either GNU configure, one or more local configure scripts or nothing, depending on what's available.
build - Actually compile the sources.
install - 安装编译结果.
reinstall - 安装编译结果,忽略“已经安装”错误.
deinstall - 卸载这个安装.
deinstall-all - Remove all installations with the same PKGORIGIN.
package - Create a package from an _installed_ port.
package-recursive - Create a package for a port and _all_ of its dependancies.
describe - Try to generate a one-line description for each port for use in INDEX files and the like.
checkpatch - Do a "patch -C" instead of a "patch". Note that it may give incorrect results if multiple patches deal with the same file.
checksum - Use distinfo to ensure that your distfiles are valid.
checksum-recursive - Run checksum in this port and all dependencies.
makesum - Generate distinfo (only do this for your own ports!).
clean - Remove ${WRKDIR} and other temporary files used for building.
clean-depends - Do a "make clean" for all dependencies.
config - Configure options for this port (using ${DIALOG}). Automatically run prior to extract, patch, configure, build, install, and package.
showconfig - 显示这个port的config选项
rmconfig - 从这个port移除config选项
fetch-list - Show list of files that would be retrieved by fetch.
fetch-recursive - Retrieves ${DISTFILES} (and ${PATCHFILES} if defined), for port and dependencies into ${DISTDIR} as necessary.
fetch-recursive-list - Show list of files that would be retrieved by fetch-recursive.
fetch-required - Retrieves ${DISTFILES} (and ${PATCHFILES} if defined), for port and dependencies that are not already installed into ${DISTDIR}.
all-depends-list - Show all directories which are dependencies for this port.
build-depends-list - Show all directories which are build-dependencies for this port.
package-depends-list - Show all directories which are package-dependencies for this port.
run-depends-list - Show all directories which are run-dependencies for this port.
extract - Unpacks ${DISTFILES} into ${WRKDIR}.
patch - Apply any provided patches to the source.
configure - Runs either GNU configure, one or more local configure scripts or nothing, depending on what's available.
build - Actually compile the sources.
install - 安装编译结果.
reinstall - 安装编译结果,忽略“已经安装”错误.
deinstall - 卸载这个安装.
deinstall-all - Remove all installations with the same PKGORIGIN.
package - Create a package from an _installed_ port.
package-recursive - Create a package for a port and _all_ of its dependancies.
describe - Try to generate a one-line description for each port for use in INDEX files and the like.
checkpatch - Do a "patch -C" instead of a "patch". Note that it may give incorrect results if multiple patches deal with the same file.
checksum - Use distinfo to ensure that your distfiles are valid.
checksum-recursive - Run checksum in this port and all dependencies.
makesum - Generate distinfo (only do this for your own ports!).
clean - Remove ${WRKDIR} and other temporary files used for building.
clean-depends - Do a "make clean" for all dependencies.
config - Configure options for this port (using ${DIALOG}). Automatically run prior to extract, patch, configure, build, install, and package.
showconfig - 显示这个port的config选项
rmconfig - 从这个port移除config选项
现在ArthurXF本人正在搞PHP等技术培训,如果想学习的人可以跟我联系。另外培训的招生简章在这个网址,想了解的可以去看看。加我QQ:29011218交流也可。
PHP培训招生简章
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";
}
}
?>