2011年02月 存档

foreach、array_map和array_reduce的执行效率

2011年02月27日,星期天

<?php

define(”ELEMENTS”, 10000);

function microtime_float() {

list($usec, $sec) = explode(” “, microtime());

return ((float)$usec + (float)$sec);

}

$a = array();

for($i=0; $i<ELEMENTS; $i++) {

$a[] = array(’id’ => $i);

}

$iStart = microtime_float();

function mapfun($i) {

return $i['id'];

}

$r2 = array_map(”mapfun”, $a);

$iFinish = microtime_float();

$aCosts[] = $iFinish - $iStart;

$iStart = microtime_float();

$r1 = array();

foreach($a as $i) {

$r1[] = $i['id'];

}

$iFinish = microtime_float();

$aCosts[] = $iFinish - $iStart;

$iStart = microtime_float();

function reducefun($r, $i) {

$r[] = $i['id'];

return $r;

}

$r3 = array_reduce($a, “reducefun”);

$iFinish = microtime_float();

$aCosts[] = $iFinish - $iStart;

var_dump($aCosts);

?>

SimCassandra: MemcacheDB是否支持二进制key

2011年02月24日,星期四

<?php

$m = new Memcache();

$m->addServer(”127.0.0.1″, 21201);

$mid = pack(”H*”, “620062″);

$k1 = “/woshao/$mid/k1″;

$k2 = “/woshao/$mid/k2″;

$m->set($k1, “v1″);

$m->set($k2, “v2″);

var_dump($k1);

var_dump($k2);

var_dump($m->get($k1));

var_dump($m->get($k2));

?>

用户登录的进化史

2011年02月23日,星期三

关于用户登录、验证的常见做法,是在验证用户的用户名和密码之后将用户的ID保存在Cookie中:

user_id=123456

验证的时候,将user_id从Cookie中取出,然后从数据库中获取对应ID的用户:

GET /users/123456

如果获取失败,就重定向到登录界面,否则算验证登录成功。这时候,一般也会看看该用户是否已经被封禁,如果被封的话,同样也会重定向到登录界面。

为了防止通过篡改Cookie的user_id值实现更换用户(也许这个用户本身不属于篡改者),还会保存一个将用户ID和密钥字符串放在一起摘要的摘要字符串(摘要算法可以使用MD5):

user_id=123456;login_sum=abcdabcdabcdabcdabcdabcdabcdabcd

这样可以在一定程度上防止冒名顶替的事情发生。

但是现有的网站一般都提供了“记住我”选项,而且一般勾选该选项都会导致用户的登录信息在Cookie中保存一个月甚至更长的时间。

如果在此期间,用户访问了一些不安全的网站,暴露了自己保存在Cookie中的用户信息,由于登录信息的摘要字符串在Cookie消失之前是不会改变的,这就有可能使不安全网站的拥有者使用用户本人的用户信息登录进网站。

为了解决这个问题,我们可以引入login_sum_expires参数,定义该摘要字符串的过期时间(Unix时间戳记):

user_id=123456;login_sum=abcdabcdabcdabcdabcdabcdabcdabcd;login_sum_expires=1234567890

同时,login_sum的计算公式中也加入该时间,保证随着过期时间的不同,摘要字符串也不一样。

之后再设置一个比较合适的过期时间(一般是20min),超过这个时间之后,即宣告该摘要字符串过期,同时为其设置一个新的过期时间并随之产生新的摘要字符串。

这样,即使用户被不安全网站窃取了Cookie中的用户信息,20min(甚至更短)之后由于其持有的用户信息已失效,就没有办法冒名顶替了。

真的是这样吗?

事实上,不安全网站拥有者持有该信息进入网站后,网站无法识别其是否是真正的用户。网站会按照一般流程,自动为摘要字符串续期!

为了能够识别用户的合法性,我们可以引入login_ip参数,记录该用户登录时的IP地址:

user_id=123456;login_sum=abcdabcdabcdabcdabcdabcdabcdabcd;login_sum_expires=1234567890;login_ip=222.222.222.222

同时,login_sum的计算公式中也加入该IP地址,保证随着登录IP的不同,摘要字符串也不一样。

这时候,即使用户被不安全网站窃取了Cookie中的用户信息,由于IP地址不同,网站也会视该登录信息失效,从而解决冒名顶替问题。

login_sum_expires参数已无用处!

(未完待续……)

SimCassandra: 比较long型整数

2011年02月22日,星期二

Cassandra的一大特性就是各Column的顺序在插入的时候就已经确定了,这表明Cassandra本身可能使用了插入排序的算法。

虽然之前研究了如何将Unix时间戳记转化为长整型数,但是如果不能进行比较的话就无法完成插入排序。

函数首先将两个长整型数unpack到两个数组中,每个数组4个元素,从1到4,分别代表从低到高的各个字。

$a1 = unpack(”v*”, $l1);

$a2 = unpack(”v*”, $l2);

然后从高位依次进行比较,如果相同就比较低16位,不同就直接返回,直到全部比较完毕。

注意:这里还是不能直接unpack成32位,PHP不能支持32位无符号整型数。

function compareLong($l1, $l2) {

$a1 = unpack(”v*”, $l1);

$a2 = unpack(”v*”, $l2);

for($i=4; $i>=1; $i–) {

$r = (int)$a1[$i] - (int)$a2[$i];

if($r != 0) {

break;

}

}

return $r;

}

$l1 = pack(”H*”, “58ba06b3de9c0400″);

$l2 = pack(”H*”, “58ba06b3de9c0400″);

var_dump(compareLong($l1, $l2));

BytesType的比较可能和LongType类似,只不过比较方向相反,从第一个字节开始。

SimCassandra: 实现timestamp到long的转换

2011年02月21日,星期一

为了实现Cassandra的LongType,必须编写代码实现将Unix时间戳记转化为64位长整型,并以低位在先的binary方式保存在变量中。

在网上找了一些解决方案,大体上都是通过第三方扩展,或者通过将低32位和高32位分开保存,再做处理的。有些方案的结果根本就是错误的。

还好,目前应对的问题仅是Unix时间戳记,而且microtime()函数是将小数点前面的部分和后面的部分分开保存的,这一切都有利于实现这个函数。

首先,和一般的算法一样,将高低32位分开。由于microtime返回的字符串实际代表的浮点数小数部分有8位十进制位,而Cassandra常用的只有6位,因此后两位就在算法中忽略掉。将除数的小数点也向前6位,这样保证得到的结果与直接的整型运算相符。注意:这样做的前提是,我们预先知道Unix时间戳记的高32位代表的数字不会溢出。

$iHi = (int)($sec / 4294.967296);

然后,取得剩余的浮点数,并乘以100,为下一步做准备。

$fRemain = (float)(($sec - $iHi * 4294.967296 + $usec) * 100);

考虑到这是个64位长整型,其低32位只能是无符号整数,PHP也没有办法处理。因此,需要暂时提取中间的16位,保证数据正确。本应该除以0.065536,但是之前已经乘以100了,这里就直接除以6.5536。

$iMed = (int)($fRemain / 6.5536);

继续获取剩余的浮点数。

$fRemain = (float)($fRemain - $iMed * 6.5536);

那么低16位就是剩余数字乘以10000了。

$iLow = (int)($fRemain * 10000);

最后,按照低位在前的方式打包所有部分,并串接起来,得到的变量就是我们要的长整型数据了。这一步是力求接近Cassandra的实现代码,目前还没有考虑Memcache协议是否允许key中包含0×00。如果不允许的话,在保存的时候还需要使用bin2hex转化为字符串。

以下为全部代码:

function get_long_timestamp($sMicrotime = ”) {
if(!$sMicrotime) $sMicrotime = microtime();
list($usec, $sec) = explode(” “, $sMicrotime);
$iHi = (int)($sec / 4294.967296);
$fRemain = (float)(($sec - $iHi * 4294.967296 + $usec) * 100);
$iMed = (int)($fRemain / 6.5536);
$fRemain = (float)($fRemain - $iMed * 6.5536);
$iLow = (int)($fRemain * 10000);
return (pack(”v”, $iLow) . pack(”v”, $iMed) . pack(”V”, $iHi));
}

$sMicrotime = microtime();
var_dump($sMicrotime);
var_dump(bin2hex(get_long_timestamp($sMicrotime)));

下面可能会研究Memcache协议的key中是否允许出现0×00。