Почему я должен использовать bitwise / bitmask в PHP?

Я работаю над пользовательской ролью / системой разрешений в 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.'; } ?> 

Solutions Collecting From Web of "Почему я должен использовать bitwise / bitmask в PHP?"

Почему бы просто не сделать это …

 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 

Первый позволяет людям иметь множество разрешений – например, чтение / добавление / обновление. Во втором примере пользователь имеет только PERMISSION_UPDATE .

Побитовое тестирование работает путем тестирования бит для значений истинности.

Например, двоичная последовательность 10010 идентифицирует пользователя с PERMISSION_DELETE и PERMISSION_READ (бит, определяющий PERMISSION_READ является столбцом для 2, идентификатор бит PERMISSION_DELETE – это столбец для 16), 10010 в двоичном формате – 18 в десятичной форме (16 + 2 = 18 ). Второй образец кода не позволяет вам выполнять такое тестирование. Вы можете делать проверки более высокого качества, но это предполагает, что у всех с PERMISSION_DELETE также должно быть PERMISSION_UPDATE , что может быть недействительным.

Возможно, это связано с тем, что я не очень часто использую битмаски, но я нахожу, что на языке, таком как PHP, где производительность разработчиков и читаемость кода важнее скорости или использования памяти (очевидно, в пределах ограничений), нет реальной причины использовать битмаскирование ,

Почему бы вместо этого не создать класс, который отслеживает такие вещи, как разрешения, и регистрируется пользователями, и так далее? Назовем это 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» и т. Д.

Я считаю, что этот подход легче читать, чем битмаски, потому что имена методов говорят вам, что именно вы ищете.

Изменить : перечитывая вопрос, похоже, что разрешения пользователя возвращаются из вашей базы данных в битовом поле. Если это так, вам придется использовать побитовые операторы. Пользователь, у которого есть разрешение в базе данных, имеет значение PERMISSION_READ и PERMISSION_DENIED потому что (PERMISSION_READ & 5) != 0 и (PERMISSION_DENIED & 5) != 0 . У него не было PERMISSION_ADD , потому что (PERMISSION_ADD & 5) == 0

Имеет ли это смысл? Весь сложный материал в вашем побитом примере выглядит ненужным.


Если вы не полностью понимаете побитовые операции, не используйте их. Это приведет лишь к множеству головных болей. Если вам удобно с ними, то используйте их там, где вы считаете, что они уместны. Вы (или кто-то, кто написал побитовый код), похоже, не полностью разбираются в побитовых операциях. Есть несколько проблем с ним, например, тот факт, что функция pow() используется, что отрицательно скажется на производительности. (Вместо pow(2, $n) , вы должны использовать побитовое 1 << $n , например.)

Тем не менее, две части кода, похоже, не делают то же самое.

Попробуйте использовать то, что находится в файле bit.class.php по адресу http://code.google.com/p/samstyle-php-framework/source/browse/trunk/class/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) ?> 

проблема в том, что 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

Скрипт проверяет, какая маска была установлена ​​в десятичной форме. Может быть, кому-то это понадобится:

 <?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 

Я предполагаю, что первый пример дает вам больше контроля над тем, какие разрешения у пользователя есть. Во втором у вас просто есть пользовательский уровень; предположительно более высокие уровни наследуют все разрешения, предоставленные пользователю более низкого уровня, поэтому у вас нет такого тонкого контроля.

Кроме того, если я правильно понял, строка

 if($user_permission_level === 4) 

означает, что только пользователи с точно уровнем разрешений 4 имеют доступ к действию – наверняка, вы захотите проверить, что у пользователей есть хотя бы этот уровень?