Бит-маска в PHP для настроек?

Биты и битовая маска – это то, над чем я пытался понять какое-то время, но я хотел бы узнать, как использовать их для настроек и т. Д. В PHP.

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

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

Класс…

<?php class bitmask { /** * This array is used to represent the users permission in usable format. * * You can change remove or add valuesto suit your needs. * Just ensure that each element defaults to false. Once you have started storing * users permsisions a change to the order of this array will cause the * permissions to be incorectly interpreted. * * @type Associtive array */ public $permissions = array( "read" => false, "write" => false, "delete" => false, "change_permissions" => false, "admin" => false ); /** * This function will use an integer bitmask (as created by toBitmask()) * to populate the class vaiable * $this->permissions with the users permissions as boolean values. * @param int $bitmask an integer representation of the users permisions. * This integer is created by toBitmask(); * * @return an associatve array with the users permissions. */ public function getPermissions($bitMask = 0) { $i = 0; foreach ($this->permissions as $key => $value) { $this->permissions[$key] = (($bitMask & pow(2, $i)) != 0) ? true : false; // Uncomment the next line if you would like to see what is happening. //echo $key . " i= ".strval($i)." power=" . strval(pow(2,$i)). "bitwise & = " . strval($bitMask & pow(2,$i))."<br>"; $i++; } return $this->permissions; } /** * This function will create and return and integer bitmask based on the permission values set in * the class variable $permissions. To use you would want to set the fields in $permissions to true for the permissions you want to grant. * Then call toBitmask() and store the integer value. Later you can pass that integer into getPermissions() to convert it back to an assoicative * array. * * @return int an integer bitmask represeting the users permission set. */ function toBitmask() { $bitmask = 0; $i = 0; foreach ($this->permissions as $key => $value) { if ($value) { $bitmask += pow(2, $i); } $i++; } return $bitmask; } } ?> 

Как установить / сохранить разрешения как значение битмаски?

 <?php /** * Example usage * initiate new bitmask object */ $perms = new bitmask(); /** * How to set permissions for a user */ $perms->permissions["read"] = true; $perms->permissions["write"] = true; $perms->permissions["delete"] = true; $perms->permissions["change_permissions"] = true; $perms->permissions["admin"] = false; // Converts to bitmask value to store in database or wherever $bitmask = $perms->toBitmask(); //in this example it is 15 $sql = "insert into user_permissions (userid,permission) values(1,$bitmask)"; echo $sql; //you would then execute code to insert your sql. ?> 

Пример принятия значения битмаски и возврата true / false для каждого элемента массива на основе значения бита ….

 <?php /** * Example usage to get the bitmask value from database or session/cache.... then put it to use. * $permarr returns an array with true/false for each array value based on the bit value */ $permarr = $perms->getPermissions($bitmask); if ($permarr["read"]) { echo 'user can read: <font color="green">TRUE</font>'; } else { echo 'user can read: <font color="red">FALSE</font>'; } //user can WRITE permission if ($permarr["write"]) { echo '<br>user can write: <font color="green">TRUE</font>'; } else { echo '<br>user can write: <font color="red">FALSE</font>'; } ?> 

    Бит-поля – очень удобный и эффективный инструмент для работы с флагами или любым набором логических значений в целом.

    Чтобы понять их, вам сначала нужно знать, как работают двоичные числа. После этого вы должны проверить записи руководства по побитовым операторам и убедиться, что знаете, как работает побитовая операция AND, OR и слева / справа.

    Поле бит – это не более чем целое значение. Предположим, что размер нашего битового поля является фиксированным и составляет только один байт. Компьютеры работают с двоичными числами, поэтому, если значение нашего номера равно 29 , вы действительно найдете 0001 1101 в памяти.

    Используя побитовое И ( & ) и побитовое ИЛИ ( | ), вы можете считывать и устанавливать каждый бит числа индивидуально. Они оба принимают два целых числа в качестве входных данных и выполняют AND / OR для каждого бита отдельно.

    Чтобы прочитать самый первый бит вашего номера, вы можете сделать что-то вроде этого:

      0001 1101 (=29, our number) & 0000 0001 (=1, bit mask) = 0000 0001 (=1, result) 

    Как вы видите, вам нужен специальный номер, где установлен только бит, который нам интересен, так называемая «бит-маска». В нашем случае это 1 . Чтобы прочитать второй бит, нам нужно «нажимать» одно в битовой маске на одну цифру влево. Мы можем это сделать с помощью левого оператора сдвига ( $number << 1 ) или путем умножения на два.

      0001 1101 & 0000 0010 = 0000 0000 (=0, result) 

    Вы можете сделать это за каждый бит в нашем номере. Двоичный И нашего числа и битовая маска ведет либо к нулю, что означает, что бит не был установлен «или», либо к ненулевому целому числу, что означает, что бит был установлен.

    Если вы хотите установить один из битов, вы можете использовать побитовое ИЛИ:

      0001 1101 | 0010 0000 (=32, bit mask) = 0011 1101 (=29+32) 

    Однако вам придется идти по-другому, если вы хотите немного «очистить».

    Более общий подход:

     // To get bit n $bit_n = ($number & (1 << $n)) != 0 // Alternative $bit_n = ($number & (1 << $n)) >> $n // Set bit n of number to new_bit $number = ($number & ~(1 << $n)) | ($new_bit << $n) 

    Сначала это может показаться немного загадочным, но на самом деле это довольно просто.

    К настоящему времени вы, вероятно, узнали, что битовые поля – это довольно низкоуровневая техника. Вот почему я рекомендую не использовать их в PHP или базах данных. Если вы хотите иметь кучу флагов, это, вероятно, нормально, но для чего-то еще вам они в действительности не нужны.

    Класс, который вы разместили, выглядит для меня немного особенным. Например, такие вещи, как ... ? true : false ... ? true : false – это неверная практика. Если вы хотите использовать битовые поля, вам, вероятно, лучше определить некоторые константы и использовать описанный выше метод. Нетрудно придумать простой класс.

     define('PERM_READ', 0); define('PERM_WRITE', 1); class BitField { private $value; public function __construct($value=0) { $this->value = $value; } public function getValue() { return $this->value; } public function get($n) { return ($this->value & (1 << $n)) != 0; } public function set($n, $new=true) { $this->value = ($this->value & ~(1 << $n)) | ($new << $n); } public function clear($n) { $this->set($n, false); } } $bf = new BitField($user->permissions); if ($bf->get(PERM_READ)) { // can read } $bf->set(PERM_WRITE, true); $user->permissions = $bf->getValue(); $user->save(); 

    Я не пробовал какой-либо фрагмент кода этого ответа, но он должен начать работать, даже если он не работает из коробки.

    Обратите внимание, что вы ограничены 32 значениями в поле бит.

    Вот как определить битмаски.

     // the first mask. In binary, it's 00000001 define('BITWISE_MASK_1', 1 << 0); // 1 << 0 is the same as 1 // the second mask. In binary, it's 00000010 define('BITWISE_MASK_2', 1 << 1); // the third mask. In binary, it's 00000100 define('BITWISE_MASK_3', 1 << 2); 

    Чтобы проверить, присутствует ли битовая маска (в данном случае в аргументе функции), используйте побитовый оператор И.

     function computeMasks($masks) { $masksPresent = array(); if ($masks & BITWISE_MASK_1) $masksPresent[] = 'BITWISE_MASK_1'; if ($masks & BITWISE_MASK_2) $masksPresent[] = 'BITWISE_MASK_2'; if ($masks & BITWISE_MASK_3) $masksPresent[] = 'BITWISE_MASK_3'; return implode(' and ', $masksPresent); } 

    Это работает, потому что, когда вы ИЛИ два байта (скажем, 00000001 и 00010000 ), вы получаете их вместе: 00010001 . Если вы и результат и оригинальная маска ( 00010001 и say, 00000001 ), вы получите маску, если она присутствует (в данном случае 00000001 ). В противном случае вы получите нуль.