举例 dicuz的帖子表的status字段,官方预留了16个标志位(0×0000 – 0xFFFF) 即2^16
目前规划使用了只有8个标志位,如下
#数据库表字段结构
#type:smallint 大小两字节 8*2=16位
`status` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '用户状态:采用二进制位一个标志位代表一种状态',
#二进制-标志位
0000 0000 0000 0001 是否缓存帖子位置信息
0000 0000 0000 0010 是否回帖只对管理人员和发帖者可见
0000 0000 0000 0100 是否抢楼贴
0000 0000 0000 1000 是否倒序查看回帖
0000 0000 0001 0000 是否存在主题图章标志位
0000 0000 0010 0000 回复是否通知作者
0000 0000 0100 0000 是否推送到QQ空间
0000 0000 1000 0000 是否推送到腾讯微博
这8种状态可以使用一个数字来同时表示,节省了字段
discuz对此的二进制位运算实现
/**
* 位运算-获取$status值得第$position位的值
*
* @param int $status
* @param int $position 标志位
* @return int 返回该标志位的值
*/
function getstatus($status, $position) {
$t = $status & pow(2, $position - 1) ? 1 : 0;
return $t;
}
/**
* 位运算-设置$position位的$value值,并返回新状态值
*
* @param int $position 标志位
* @param int $value 设置的标志位的值 0或1
* @param int $baseon 状态值
* @return int 返回新状态值
*/
function setstatus($position, $value, $baseon = null) {
$t = pow(2, $position - 1);
if($value) {
$t = $baseon | $t;
} elseif ($baseon !== null) {
$t = $baseon & ~$t;
} else {
$t = ~$t;
}
return $t & 0xFFFF;
}
#注意 写这段代码的人显然受到了C的影响 其实 $a & ~$b 和 $a ^ $b 是等效的 只不过 ^是PHP的写法 另外 pow(2, $position - 1)换成 1 << ($position -1) 其实更好理解。
如何用一个数字来标识这些权限位呢
以刚才discuz的帖子表达status字段为例,检查帖子回复是否通知作者 就看二进制上第六位是否是置位为1 那么怎么检查呢?就是用上面我们提到的与运算。
与运算是将把 $a 和 $b 中都为 1 的位设为 1。那么假设
$a=36=0b 0010 0100
$b=0b 0010 0000
$a&$b = 0b 0010 0100 & 0b 0010 0000 = 0b 0010 0000 = 32 = 26-1 = 25
因此 检查,某个数代表的第N个权限标志位有没有置位(是1) 只要选择该数与仅该标志位置位的操作数2N-1进行与运算即可,相反要计算某个标志位被置位的数字 只要选择合适的操作数进行或运算即可。