Por que eu deveria usar bit -mask/bitmask no php?
-
21-09-2019 - |
Pergunta
Estou trabalhando em um sistema de papel / permissão no PHP para um script.
Abaixo está um código usando um método de máscara de bits para permissões que encontrei no phpbuilder.com.
Abaixo dessa parte, há uma versão muito mais simples que o W3Hich poderia fazer básico a mesma coisa sem a parte do bit.
Muitas pessoas recomendaram o uso de operadores de bits e tal para configurações e outras coisas no PHP, nunca entendi por que. No código abaixo está lá Qualquer benefício de usar o primeiro código em vez do 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.';
}
?>
versão não bit
<?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.';
}
?>
Solução
Por que não apenas fazer isso ...
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.';
}
Você também pode criar muitas combinações arbitrárias de permissões se usar 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
Outras dicas
O primeiro permite que as pessoas tenham muitas permissões - Leia/Adicionar/atualizar, por exemplo. O segundo exemplo, o usuário tem apenas PERMISSION_UPDATE
.
Os testes bitweeques funcionam testando bits para valores da verdade.
Por exemplo, a sequência binária 10010
identificaria um usuário com PERMISSION_DELETE
e PERMISSION_READ
(a parte identificando PERMISSION_READ
é a coluna para 2, o bit identificando PERMISSION_DELETE
é a coluna para 16), 10010
em binário é 18 em decimal (16 + 2 = 18). Seu segundo amostra de código não permite que você faça esse tipo de teste. Você pode fazer verificações maiores do que no estilo, mas isso assume todos com PERMISSION_DELETE
também deve ter PERMISSION_UPDATE
, o que pode não ser uma suposição válida.
Talvez seja apenas porque eu não uso máscaras de bits com muita frequência, mas acho que em um idioma como PHP, onde a produtividade e a legibilidade do código dos desenvolvedores são mais importantes que o uso de velocidade ou memória (dentro dos limites, obviamente), não há razão real para usar o Bitmasking .
Por que não criar uma classe que rastreia coisas como permissões e login nos usuários e assim por diante? Vamos chamá -lo de auth. Então, se você deseja verificar se um usuário tem uma permissão, poderá criar um método Haspermission. por exemplo,
if(Auth::logged_in() && Auth::currentUser()->hasPermission('read'))
//user can read
Então, se você quiser verificar se eles têm alguma combinação de permissões:
if(Auth::logged_in() && Auth::currentUser()->hasAllPermissions('read', 'write'))
//user can read, and write
Ou se você quiser verificar se eles têm algum de um determinado grupo de permissões:
if(Auth::logged_in() && Auth::currentUser()->hasAnyPermissions('read', 'write'))
//user can read, or write
Obviamente, pode não ser uma má idéia definir constantes, como permissão_read, que você pode definir como a string 'Read' e assim por diante.
Acho essa abordagem mais fácil de ler do que máscaras de bits, porque os nomes de métodos dizem exatamente o que você está procurando.
Editar: Relendo a pergunta, parece que as permissões do usuário estão voltando do seu banco de dados em um campo de bits. Se for esse o caso, você terá que usar operadores bitwise. Um usuário que tem permissão no banco de dados é 5
tem PERMISSION_READ
e PERMISSION_DENIED
Porque (PERMISSION_READ & 5) != 0
, e (PERMISSION_DENIED & 5) != 0
. Ele não teria PERMISSION_ADD
, Porque (PERMISSION_ADD & 5) == 0
Isso faz sentido? Todas as coisas complexas em seu exemplo bit -bones parecem desnecessárias.
Se você não entende completamente as operações bit newise, não as use. Isso só levará a muitas dores de cabeça. Se você se sentir confortável com eles, use -os onde você sente que são apropriados. Você (ou quem escreveu o código bit) não parece compreender completamente as operações bit -new. Existem vários problemas com isso, como o fato de que o pow()
A função é usada, que negaria qualquer tipo de benefício de desempenho. (Ao invés de pow(2, $n)
, você deve usar o bit a bit 1 << $n
, por exemplo.)
Dito isto, as duas peças de código não parecem fazer as mesmas coisas.
Tente usar o que está no bit.class.php em http://code.google.com/p/samstyle-php-framework/source/browse/trunk/class/bit.class.php
Verificando contra um 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
}
?>
E para ligar e desligar:
<?php
$permissions = 8;
bit::toggle(&$permissions,PERMISSION_DENIED);
var_dump($permissions); // outputs int(9)
?>
O problema disso é se permissão_read for uma máscara
if($ARR_permission & PERMISSION_READ) {
echo 'Access granted.';
}else {
echo 'Access denied.';
Então, para 0101 - $ RightWehave 0011 - $ RightWerequire
é o acesso concedido, o que provavelmente não queremos, então deve ser
if (($rightWeHave & $rightWeRequire) == $rightWeRequire) {
echo 'access granted';
}
Então agora para
0101 0011
resultado é
0001 Portanto, o acesso não é concedido porque não é igual a 0011
mas pelo
1101 0101
Tudo bem, pois o resultado é 0101
Verifica qual máscara foi definida em decimal. Talvez alguém precise:
<?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";
Saída (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
Eu acho que o primeiro exemplo oferece mais controle sobre exatamente quais permissões um usuário tem. No segundo, você apenas tem um 'nível' de usuário; Presumivelmente, os níveis mais altos herdam todas as permissões concedidas a um usuário do nível mais baixo, para que você não tenha um controle tão bom.
Além disso, se eu entendi corretamente, a linha
if($user_permission_level === 4)
significa que apenas usuários com exatamente O nível de permissão 4 tem acesso à ação - certamente você gostaria de verificar se os usuários têm pelo menos esse nível?