权限值必须是2的幂(1、2、4、8…),以确保每个权限独占一个二进制位,避免位运算时误判;如用3(0011)会导致read和write无法独立识别。

直接说结论:用 & 判断权限、| 合并权限、& ~ 清除权限,整数字段存数据库即可,但必须确保每个权限值是 2 的幂(1、2、4、8…),否则位运算失效。
为什么权限值必须是 2 的幂(如 1、2、4、8)
因为只有这样,每个权限才独占一个二进制位,互不干扰。比如:
– READ = 1 → 二进制 0001
– WRITE = 2 → 二进制 0010
– DELETE = 4 → 二进制 0100
– ADMIN = 8 → 二进制 1000
若误用 3 或 5 作权限值,其二进制含多个 1(如 3 是 0011),会导致 & 检查时“误判为有权限”——比如用户只有 READ | WRITE = 3,但 3 & 3 成立,3 & 1 也成立,可 3 & 2 同样成立,无法区分到底是哪个权限在起作用。
推荐写法:
– define('PERM_READ', 1 <br>– <code>define('PERM_WRITE', 1 <br>– <code>define('PERM_DELETE', 1 <br>– 这样既清晰,又避免手算出错。
如何安全判断用户是否具备某项权限
常见错误是只写 if ($userPerm & $perm),这在权限值非零时恒为真(比如 $userPerm = 3,$perm = 1 时成立,但 $perm = 3 也成立,语义错乱)。
正确做法是严格比对结果是否等于目标权限值:
– if (($userPerm & $perm) === $perm)
– 必须用 ===,防止类型隐式转换(如 0 == false 但 0 === false 不成立)
封装成函数更稳妥:
function hasPermission(int $userPerm, int $needPerm): bool<br>{<br> return ($userPerm & $needPerm) === $needPerm;<br>}
使用示例:
– 用户权限 $userPerm = PERM_READ | PERM_WRITE;(即 3)
– hasPermission($userPerm, PERM_READ) → true
– hasPermission($userPerm, PERM_DELETE) → false
添加、移除权限时的典型误操作
添加权限看似简单,但容易忽略“重复添加”导致逻辑冗余;移除权限则常错用 ^(异或),它在权限已不存在时反而会“加回来”。
- ✅ 正确添加:
$userPerm |= $newPerm;(幂等,多次执行无副作用) - ❌ 错误移除(用异或):
$userPerm ^= $perm;—— 若用户本无该权限,$userPerm ^= $perm会把它“加回去” - ✅ 正确移除:
$userPerm &= ~$perm;(无论原来有没有,结果都确保清除)
举例:
– 初始 $userPerm = 3(READ|WRITE)
– 执行 $userPerm ^= PERM_WRITE; → 变成 1(正确)
– 再执行一次 → 变成 3(错误!权限被意外恢复)
– 改用 $userPerm &= ~PERM_WRITE; → 第一次变 1,第二次仍是 1,行为确定
INT 字段能支持多少权限?溢出风险在哪
PHP 的 int 在 32 位系统上最大为 231−1(约 21 亿),对应最多 31 个独立权限位;64 位系统上限更高,但数据库字段类型才是瓶颈。
MySQL 的 INT 默认是带符号 32 位,最大正整数为 2147483647(231−1),所以最多支持 31 种权限(从 1 到 <code>1 )。若定义第 32 个权限 <code>1 ,在有符号 INT 下会变成负数,导致 <code>& 判断失效(符号位干扰)。
规避方式:
– 明确限制权限总数 ≤ 31
– 或改用 UNSIGNED INT(支持到 232−1,即 32 位)
– 更彻底的方案:用 BIGINT UNSIGNED,支持 64 种权限(不过绝大多数业务远用不到)
真正容易被忽略的是:权限常量一旦上线,就不能随意调整值。比如把 PERM_EXPORT = 16 改成 32,所有已有用户的权限数据就全乱了——位含义偏移,检查必然失败。所以权限编号应视为不可变契约。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











