位运算符允许对整型数中指定的位进行求值和操作。
例子 | 名称 | 结果 |
$a & $b | And(按位与) | 将把 $a 和 $b 中都为 1 的位设为 1。 |
$a | $b | Or(按位或) | 将把 $a 和 $b 中任何一个为 1 的位设为 1。 |
$a ^ $b | Xor(按位异或) | 将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。 |
~ $a | Not(按位取反) | 将 $a 中为 0 的位设为 1,反之亦然。 |
$a << $b | Shift left(左移) | 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。 |
$a >> $b | Shift right(右移) | 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。 |
PHP位运算实例(原创)
当我们需要将多项用户权限、状态合并记录到一个数据表字段中时,可以先用二进制数字表示,然后再转换为十进制存储到数据表中。而当需要修改这个字段时,就需要用到位运算(相当于取出十进制数转换成二进制,然后进行相应替换后,转换回十进制存储到数据库中)。
例如:
用户有12项权限(或状态)需要记录,我们可以用一个12位的二进制数字表示,每一位代表一种权限(供其他程序读取使用),1表示具有该权限,0表示不具备该权限。
#B 0000 0000 0001 //权限A (十进制数1)
#B 0000 0000 0010 //权限B (十进制数2)
#B 0000 0000 0100 //权限C (十进制数4)
……(自行记录每一位为1时的十进制数,作为后面增减该权限时需要用到的参与位运算的数值*)
********************
*附:PHP自带的进制转换函数 base_convert
base_convert('100000000000',2,10);
其中2代表数值当前进制,10为需要转换的进制。两者都不能超过36(10个数字和26个数字组合),如果需要更高进制的转换,可参考 PHP 10进制与62进制互转
********************
当我们首次赋予用户某项权限时,例如赋予权限B(0000 0000 0010),对应的十进制数是2,将其记录到数据表中。
以后当我们需要增减某项权限时,例如权限C(0000 0000 0100),对应的十进制数是4,我们可以取出数据表中原有权限2,将其与要增减的4进行位运算:
$qx = 2; //数据库中取出的原有权限
$bg = 4; //要增减的权限(上文下划线处记录的值)
$qx | $bg = 6; //位运算后得到的结果(代表具有权限B和权限C),记录到数据表中
其中用到了运算符 | (按位或),表示把 $qx 和 $bg 中任何一个为 1 的位设为 1,实际运算过程是:
#B 0000 0000 0010 (2的二进制)
#B 0000 0000 0100 (4的二进制)
| 按位或:任何一个为1的位设为1,得到
#B 0000 0000 0110 (转换为十进制即为6)
下面来进行逆操作,当前用户具有权限B和权限C,数据库中记录为6,二进制数为 0000 0110 ,我们要将权限B(二进制0000 0010/十进制2)去掉,即 0000 0100 :
$qx = 6; //数据库中取出的原有权限
$bg = 2; //要增减的权限(上文下划线处记录的值)
在Discuz! X3中,我们留意到它使用了 ^ 运算符(按位异或)来去掉某种状态(或权限),详见……
我们先来试试这个运算符:
$qx ^ $bg = 4;
运算过程是:
#B 0000 0000 0110 (6的二进制)
#B 0000 0000 0010 (2的二进制)
^ 按位异或,将两者中一个为 1 另一个为 0 (两者相同的位数上数值不同)的位设为 1,否则为0
#B 0000 0000 0100 (转换为十进制即为4)
这是一个正确结果。那使用这个运算符就没有问题吗?在一种情况下是不行的!
例如,用户本就不具有某项权限,我们还错误地去执行去掉这项权限时,就会出错!
假设用户本不具有权限A(二进制0000 0000 0001/十进制1),我们看看使用 ^ 去掉这项权限会有什么结果:
$qx = 6; //数据库中取出的原有权限
$bg = 1; //要“去掉”的本就不存在权限
$qx ^ $bg = 7; //这是我们需要的正确结果吗?看看!
#B 0000 0000 0110 (6的二进制)
#B 0000 0000 0001 (1的二进制)
^ 按位异或,将两者中一个为 1 另一个为 0 (两者相同的位数上数值不同)的位设为 1,否则为0
#B 0000 0000 0111 (转换为十进制为7)
本要“去掉”权限A,现在反倒增加上去了!很显然这不是我们要的结果!
怎么解决呢?我们可以试试先对要“去掉”的权限用 ~ 取反,然后用 & 按位与:
$qx & ~ $bg = 6;
实际计算过程:
#B 0000 0000 0110 (6的二进制)
#B 0000 0000 0001 (1的二进制)
先用 ~ 对要去掉的权限取反,得到:
#B 1111 1111 1110
再用 & 按位与(将两者中都为 1 的位设为 1)
#B 0000 0000 0110 (6的二进制)
我们可以看到结果没变,因为我们是错误“去掉”本就不具有的权限,所以最后正常结果应该是不产生变化。
再来验证 &~ 能否正常去掉存在的某项权限,还是以刚才 ^ 执行成功的情况为例:
$qx = 6; //数据库中取出的原有权限
$bg = 2; //要增减的权限(上文下划线处记录的值)
$qx ^ $bg = 4; //前面 ^ 的运算结果
$qx & ~ $bg = 4; //用 &~ 的运算结果
运算过程是:
#B 0000 0000 0110 (6的二进制)
#B 0000 0000 0010 (2的二进制)
先用 ~ 对要去掉的权限取反,得到:
#B 1111 1111 1101
再用 & 按位与(将两者中都为 1 的位设为 1)
#B 0000 0000 0100 (转换为十进制即为4)
更多参考:http://php.net/manual/zh/language.operators.bitwise.php
若您喜欢这篇文章,欢迎订阅老张小站以获得最新内容。 / 欢迎交流探讨,请发电子邮件至 mail[at]vdazhang.com 。
欢迎谈谈你的看法(无须登录) *正文中请勿包含"http://"否则将被拦截