Bit mask resurrection
Packing Boolean variables for storage and search in the database
Storage of a set of checkboxes in one database field. Bit mask.
A wonderful idea was buried in these topics. Well, try to revive it again ...
I'll start with an example. The user must have access to certain functions of the site. All of these functions, for example, 1000. There are groups to which a certain set of functions is attached. A user can belong to several groups at once. For one request to the site, several functions can be performed at once. Pretty real task, right?
Groups will be stored in the groups table. To store a set of functions for a group, the most obvious solution is to create a linking table: Now, when requesting a function, you need to make a request to the database:
CREATE TABLE `functions_to_group` (
`group_id` int(4) unsigned NOT NULL,
`function_id` int(4) NOT NULL
UNIQUE KEY `f_to_g` (`group_id`,`function_id`)
) "SELECT
`function_id`
FROM
`functions_to_group`
WHERE
`group_id` in (" .implode(",", $user_groups). ") and
`function_id` = " .$current_function_id;But we do not know how many more functions will be called, and each time we have to fulfill this request.
It is possible in one fell swoop, i.e. by request, get all available functions and drag an array of several hundred elements along: Somehow ugly ... All we need is to check if there is a certain value in the data set. Here the bit mask is very useful. But storing the mask as a whole is no longer an option. After all, we have 1000 functions. Nafik numbers, we need binary data, well, so there is such a function pack (), just it will do for us. We write a simple class: Now we pack the entire set of functions for the group in one line, for example, like this:
"SELECT
`group_id`,
`function_id`
FROM
`functions_to_group`
WHERE
`group_id` in (" .implode(",", $user_groups). ")"class BitMask
{
function __construct(){}
function GetMask($num)
{
// если 0, то возвращаем нулевой байт
if (!$num)
{
return "\0";
}
// само число определяет позицию единичного бита в бинарной строке
$num--;
//определяем кол-во нулевых байт, предшествующих значащему биту
$null_bytes = floor($num / 8);
//выставляем единичный бит в значащем байте...
$number = pow(2, $num % 8);
//и пакуем всё это дело
return pack('@' .$null_bytes. 'C', $number);
}
function InMask($needle, $findIn)
{
//проверяем, есть ли в строке искомое значение,
//т.е. побитово умножаем и отрубаем нулевые биты
return (bool)strlen(trim($needle & $findIn, "\0"));
}
}$x = new BitMask();
$group_access = $x->GetMask(0);
while (list($k, $v) = each($_POST['functions_to_group']))
{
$group_access |= $x->GetMask($v);
}All. The output is one binary row that can be stored in the groups table. When authorizing on the site, we make one query to the groups table: and bitwise add up the sets of all user groups: To check if there is access to the function, do this: the maximum size of the binary string = ceil (max_function_id / 8) bytes, in our case for 1000 elements , the maximum size will be 125 bytes. In a string 255 characters long, max_function_id = 2040.
"SELECT
`functions_bitmask`
FROM
`groups`
WHERE
`group_id` in (" .implode(",", $user_groups). ")"$user_private_access = $x->GetMask(0);
while (list($k, $v) = each($user_groups_access))
{
$user_private_access |= $v;
}if ($x->InMask($x->GetMask($function_id), $user_private_access))
{
// есть доступ
}
else
{
// нет доступа
}