¿Por qué debería usar bitwise/bitmask en PHP?
bit-manipulation (8)
Estoy trabajando en un sistema de permisos / roles de usuario en PHP para un script.
A continuación se muestra un código que utiliza un método de máscara de bits para los permisos que encontré en phpbuilder.com.
Debajo de esa parte hay una versión mucho más simple que podría hacer básicamente lo mismo sin la parte de bits.
Muchas personas han recomendado el uso de operadores de bits y otros para la configuración y otras cosas en PHP, aunque nunca he entendido por qué. En el código a continuación, ¿hay algún beneficio de usar el primer código en lugar del segundo?
<?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.'';
}
?>
versión sin bits
<?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.'';
}
?>
¿Por qué no simplemente hacer esto ...
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.'';
}
También puede crear muchas combinaciones arbitrarias de permisos si usa bits ...
$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
El primero permite a las personas tener muchos permisos, por ejemplo, leer / agregar / actualizar. El segundo ejemplo, el usuario solo tiene PERMISSION_UPDATE
.
Las pruebas de bitwise funcionan al probar bits para valores de verdad.
Por ejemplo, la secuencia binaria 10010
identificaría un usuario con PERMISSION_DELETE
y PERMISSION_READ
(el bit que identifica a PERMISSION_READ
es la columna para 2, el bit que identifica a PERMISSION_DELETE
es la columna para 16), 10010
en binario es 18 en decimal (16 + 2 = 18) ). Tu segunda muestra de código no te permite hacer ese tipo de prueba. Podría realizar verificaciones de estilo superiores, pero eso supone que todas las personas con PERMISSION_DELETE
también deberían tener PERMISSION_UPDATE
, lo que puede no ser una suposición válida.
El problema de esto es si PERMISSION_READ es una máscara en sí misma.
if($ARR_permission & PERMISSION_READ) {
echo ''Access granted.'';
}else {
echo ''Access denied.'';
luego para 0101 - $ rightWeHave 0011 - $ rightWeRequire
Es un acceso otorgado, que probablemente no queremos, por lo que debería ser
if (($rightWeHave & $rightWeRequire) == $rightWeRequire) {
echo ''access granted'';
}
así que ahora para
0101 0011
el resultado es
0001 por lo que el acceso no se concede porque no es igual a 0011
pero para
1101 0101
está bien ya que el resultado es 0101
Intente usar lo que está en bit.class.php en http://code.google.com/p/samstyle-php-framework/source/browse/trunk/class/bit.class.php
Comprobando contra un bit específico:
<?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
}
?>
Y para encender y apagar:
<?php
$permissions = 8;
bit::toggle(&$permissions,PERMISSION_DENIED);
var_dump($permissions); // outputs int(9)
?>
Supongo que el primer ejemplo le da más control de exactamente qué permisos tiene un usuario. En el segundo solo tienes un ''nivel'' de usuario; los niveles supuestamente superiores heredan todos los permisos otorgados a un usuario de "nivel" inferior, por lo que no tiene un control tan preciso.
Además, si he entendido correctamente, la línea
if($user_permission_level === 4)
significa que solo los usuarios con exactamente el nivel de permiso 4 tienen acceso a la acción; seguramente querrá comprobar que los usuarios tengan al menos ese nivel.
Tal vez es solo porque no uso las máscaras de bits muy a menudo, pero me parece que en un lenguaje como PHP donde la productividad del desarrollador y la legibilidad del código son más importantes que la velocidad o el uso de la memoria (obviamente, dentro de los límites), no hay ninguna razón real para usar la máscara de bits. .
¿Por qué no crear una clase que haga un seguimiento de cosas como permisos, usuarios registrados, etc.? Llamémoslo Auth. Luego, si desea verificar que un usuario tenga un permiso, puede crear un método HasPermission. p.ej,
if(Auth::logged_in() && Auth::currentUser()->hasPermission(''read''))
//user can read
Entonces, si quieres comprobar si tienen alguna combinación de permisos:
if(Auth::logged_in() && Auth::currentUser()->hasAllPermissions(''read'', ''write''))
//user can read, and write
o si desea verificar si tienen alguno de un determinado grupo de permisos:
if(Auth::logged_in() && Auth::currentUser()->hasAnyPermissions(''read'', ''write''))
//user can read, or write
Por supuesto, puede que no sea una mala idea definir constantes, como PERMISSION_READ, que puede definir como la cadena ''leer'', y así sucesivamente.
Considero que este enfoque es más fácil de leer que las máscaras de bits porque los nombres de los métodos te dicen exactamente qué es lo que estás buscando.
Edición : al volver a leer la pregunta, parece que los permisos del usuario regresan de su base de datos en un campo de bits. Si ese es el caso, vas a tener que usar operadores bitwise. Un usuario cuyo permiso en la base de datos es 5
tiene PERMISSION_READ
y PERMISSION_DENIED
porque (PERMISSION_READ & 5) != 0
, y (PERMISSION_DENIED & 5) != 0
. No tendría PERMISSION_ADD
, porque (PERMISSION_ADD & 5) == 0
¿Tiene sentido? Todo lo complejo en su ejemplo bitwise parece innecesario.
Si no entiende completamente las operaciones de bitwise, entonces no las use. Sólo llevará a muchos dolores de cabeza. Si te sientes cómodo con ellos, entonces úsalos donde creas que son apropiados. Usted (o quien haya escrito el código bitwise) no parece comprender completamente las operaciones bitwise. Existen varios problemas, como el hecho de que se utiliza la función pow()
, que anularía cualquier tipo de beneficio de rendimiento. (En lugar de pow(2, $n)
, debe usar el bitwise 1 << $n
, por ejemplo).
Dicho esto, las dos piezas de código no parecen hacer las mismas cosas.
El script comprueba qué máscara se ha establecido en decimal. Tal vez alguien lo necesite:
<?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";
Salida (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