位运算适合处理二进制标记,例如把“读取、编辑、删除”几项权限存放在一个整数里。它也能表达某些乘除关系,但不应该把“更底层”直接理解为“在 PHP 中必然更快”。
本文示例都使用整数;PHP 的某些位运算在两个操作数都是字符串时会按字节处理,不要与下面的整数逻辑混用。
一、左移与右移
var_dump(3 << 4); // int(48) |
对非负整数,在结果不溢出、移位数合法的范围内,左移 n 位相当于乘以 2 的 n 次方。非负整数右移 n 位相当于除以 2 的 n 次方并向下取整。
这不适用于所有除法语义:
var_dump(5 / 2); // float(2.5) |
PHP 的整数宽度依赖平台,可以查看 PHP_INT_SIZE 和 PHP_INT_MAX。1 << 32 不能无条件等同于 pow(2, 32):常见 64 位平台能表示这个移位结果,32 位平台则不能;移位还可能丢弃高位、改变符号,而 pow() 有自己的数值及类型规则。
位移不能直接求平方根。用移位表达整数乘除也不保证性能更好,实际耗时还取决于 PHP 版本、JIT、类型转换和上下文。业务代码优先写清楚运算含义,有性能瓶颈时再测量。语义和平台限制见 PHP 位运算文档。
二、与、或、异或、取反
| 运算符 | 含义 | 对整数位的作用 | |
|---|---|---|---|
& |
按位与 | 两边都是 1,该位才是 1 | |
| `\ | ` | 按位或 | 任一边是 1,该位就是 1 |
^ |
按位异或 | 两边不同,该位是 1 | |
~ |
按位取反 | 翻转每一位,包含整数符号相关的位 |
判断奇偶
$number = 7; |
最低位为 1 表示奇数,为 0 表示偶数。这里显式加括号,避免把按位运算和比较运算的优先级记混。按位或 | 与逻辑 or、|| 不是同一种操作。
设置与清除最低位
var_dump(6 | 1); // int(7),最低位设为 1 |
对非负整数,清除最低位得到不大于原数的相邻偶数,不能笼统称为“最接近的偶数”:例如 7 到 6 和 8 的距离相同。
三、用不同的位记录权限
每一项独立标记占用一个不同的二进制位:
const READ = 1 << 0; // 1 |
检查“全部具备”和“任意具备”的条件不同。异或是切换,不是保证添加;重复执行两次又会恢复原状态。添加标记用或、清除标记用与配合取反,意图更明确。
位掩码只是权限数据的表示形式,不能替代服务端鉴权。真正允许用户操作某条记录之前,还需要核对身份、角色、资源归属等业务规则。位数也受整数宽度限制,不要无限增加标记。