我为什么要在PHP中使用按位/位掩码?

时间:2009-09-04 15:45:40

标签: php bit-manipulation bitmask

我正在使用PHP中的用户角色/权限系统来处理脚本。

下面是我在phpbuilder.com上找到的使用位掩码方法获取权限的代码。

在那部分之下是一个更简单的版本,如果没有位部分,它可以做基本相同的事情。

许多人建议在PHP中使用位操作符等设置和其他东西,但我从未理解为什么。在下面的代码中使用第一个代码而不是第二个代码任何好处

<?php
/**
 * Correct the variables stored in array.
 * @param    integer    $mask Integer of the bit
 * @return    array
 */
function bitMask($mask = 0) {
    $return = array();
    while ($mask > 0) {
        for($i = 0, $n = 0; $i <= $mask; $i = 1 * pow(2, $n), $n++) {
            $end = $i;
        }
        $return[] = $end;
        $mask = $mask - $end;
    }
    sort($return);
    return $return;
}


define('PERMISSION_DENIED', 0);
define('PERMISSION_READ', 1);
define('PERMISSION_ADD',  2);
define('PERMISSION_UPDATE', 4);
define('PERMISSION_DELETE', 8);

//run function
// this value would be pulled from a user's setting mysql table
$_ARR_permission = bitMask('5');

if(in_array(PERMISSION_READ, $_ARR_permission)) {
    echo 'Access granted.';
}else {
    echo 'Access denied.';
}
?>

非位版本

<?PHP
/*
   NON bitwise method
*/

// this value would be pulled from a user's setting mysql table
$user_permission_level = 4;

if($user_permission_level === 4) {
    echo 'Access granted.';
}else {
    echo 'Access denied.';
}

?>

8 个答案:

答案 0 :(得分:41)

为什么不这样做......

define('PERMISSION_DENIED', 0);
define('PERMISSION_READ', 1);
define('PERMISSION_ADD',  2);
define('PERMISSION_UPDATE', 4);
define('PERMISSION_DELETE', 8);

//run function
// this value would be pulled from a user's setting mysql table
$_ARR_permission = 5;

if($_ARR_permission & PERMISSION_READ) {
    echo 'Access granted.';
}else {
    echo 'Access denied.';
}

如果您使用位...

,您还可以创建许多任意权限组合
$read_only = PERMISSION_READ;
$read_delete = PERMISSION_READ | PERMISSION_DELETE;
$full_rights = PERMISSION_DENIED | PERMISSION_READ | PERMISSION_ADD | PERMISSION_UPDATE | PERMISSION_DELETE;

//manipulating permissions is easy...
$myrights = PERMISSION_READ;
$myrights |= PERMISSION_UPDATE;    // add Update permission to my rights

答案 1 :(得分:10)

第一个允许人们拥有许多权限 - 例如,读取/添加/更新。第二个例子,用户只有PERMISSION_UPDATE

按位测试的工作原理是测试真值的位。

例如,二进制序列10010将标识具有PERMISSION_DELETEPERMISSION_READ的用户(标识为PERMISSION_READ的位是2的列,标识为{{的位1}}是16)的列,二进制的PERMISSION_DELETE是十进制的18(16 + 2 = 18)。您的第二个代码示例不允许您进行此类测试。您可以执行大于样式检查,但假设10010的所有人都应该PERMISSION_DELETE,这可能不是一个有效的假设。

答案 2 :(得分:10)

也许这只是因为我不经常使用bitmasks,但我发现在像PHP这样的语言中,开发人员的工作效率和代码可读性比速度或内存使用更重要(显然在极限范围内),没有真正的原因使用bitmasking。

为什么不创建一个跟踪权限,登录用户等内容的类?我们称之为Auth。然后,如果要检查用户是否具有权限,则可以创建方法HasPermission。 如,

if(Auth::logged_in() && Auth::currentUser()->hasPermission('read'))
    //user can read

然后,如果你想检查他们是否有一些权限组合:

if(Auth::logged_in() && Auth::currentUser()->hasAllPermissions('read', 'write'))
    //user can read, and write

或者如果您想检查他们是否具有任何一组权限:

if(Auth::logged_in() && Auth::currentUser()->hasAnyPermissions('read', 'write'))
    //user can read, or write

当然,定义常量可能不是一个坏主意,例如PERMISSION_READ,您可以将其定义为字符串'read',依此类推。

我觉得这种方法比bitmasks更容易阅读,因为方法名称可以准确地告诉你它正在寻找什么。

答案 3 :(得分:1)

编辑:重读问题,看起来用户的权限是从位域中的数据库返回的。如果是这种情况,您将不得不使用按位运算符。在数据库中5拥有PERMISSION_READPERMISSION_DENIED的权限的用户是(PERMISSION_READ & 5) != 0(PERMISSION_DENIED & 5) != 0。他不会PERMISSION_ADD,因为(PERMISSION_ADD & 5) == 0

这有意义吗?您的按位示例中的所有复杂内容看起来都是不必要的。


如果您不完全理解按位操作,请不要使用它们。这只会导致很多麻烦。如果您对它们感到满意,那么在您认为合适的地方使用它们。您(或编写按位代码的人)似乎并不完全掌握按位操作。它有几个问题,比如使用pow()函数的事实,它会否定任何类型的性能优势。 (例如,您应该使用按位pow(2, $n)而不是1 << $n。)

也就是说,这两段代码似乎没有做同样的事情。

答案 4 :(得分:1)

尝试使用http://code.google.com/p/samstyle-php-framework/source/browse/trunk/class/bit.class.php

中bit.class.php中的内容

检查特定位:

<?php

define('PERMISSION_DENIED', 1);
define('PERMISSION_READ', 2);
define('PERMISSION_ADD',  3);
define('PERMISSION_UPDATE', 4);
define('PERMISSION_DELETE', 5);


if(bit::query($permission,PERMISSION_DENIED)){
echo 'Your permission is denied';
exit();
}else{
// so on
}

?>

打开和关闭:

<?php

$permissions = 8;
bit::toggle(&$permissions,PERMISSION_DENIED);

var_dump($permissions); // outputs int(9)

?>

答案 5 :(得分:1)

问题是如果PERMISSION_READ本身就是一个掩码

if($ARR_permission & PERMISSION_READ) {
    echo 'Access granted.';
}else {
    echo 'Access denied.';

然后 0101 - $ rightWeHave 0011 - $ rightWeRequire

它是被授予的访问权限,我们可能不希望它应该是

if (($rightWeHave & $rightWeRequire) == $rightWeRequire) {
echo 'access granted';
}

现在是

0101 0011

结果是

0001因此不授予访问权限,因为它不等于0011

但是

1101 0101

没关系,结果是0101

答案 6 :(得分:1)

脚本检查哪个蒙版已设置为十进制。也许有人需要它:

<?php

$max = 1073741824;
$series = array(0);
$x = 1;
$input = $argv[1]; # from command line eg.'12345': php script.php 12345
$sum = 0;

# generates all bitmasks (with $max)
while ($x <= $max) {
    $series[] = $x;
    $x = $x * 2;
}

# show what bitmask has been set in '$argv[1]'
foreach ($series as $value) {
    if ($value & $input) {
        $sum += $value;
        echo "$value - SET,\n";
    } else {
        echo "$value\n";
    }
}

# sum of set masks
echo "\nSum of set masks: $sum\n\n";

输出(php maskChecker.php 123):

0
1 - SET,
2 - SET,
4
8 - SET,
16 - SET,
32 - SET,
64 - SET,
128
256
512
1024
2048
4096
8192
(...)

Sum of set mask: 123

答案 7 :(得分:0)

我想第一个例子可以让您更准确地控制用户拥有的权限。在第二个你只有一个用户'级别';可能更高的级别继承了授予较低“级别”用户的所有权限,因此您没有这样的精细控制。

另外,如果我理解正确,行

if($user_permission_level === 4)

表示只有具有完全权限级别4的用户才能访问该操作 - 当然您需要检查用户是否至少该级别?