Битовые поля - очень удобный и эффективный инструмент для работы с флагами или любым набором булевых значений в целом.
Чтобы понять их, вам сначала нужно знать, как работают двоичные числа.После этого вы должны проверить ручные записи для побитовых операторов и убедиться, что вы знаете, как работает побитовое И, ИЛИ и сдвиг влево / вправо.
Битовое поле - не что иное, какцелочисленное значение.Давайте предположим, что размер нашего битового поля фиксирован и составляет всего один байт.Компьютеры работают с двоичными числами, поэтому, если значение нашего числа равно 29
, вы действительно найдете в памяти 0001 1101
.
Используя побитовое И (&
) и побитовое ИЛИ (* 1012)*) Вы можете считывать и устанавливать каждый бит числа индивидуально.Они оба принимают два целых числа в качестве входных данных и выполняют И / ИЛИ для каждого бита индивидуально.
Чтобы прочитать самый первый бит вашего числа, вы можете сделать что-то вроде этого:
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)
Однако вам придется пойти другим путем, если вы хотите «очистить» бит.
Aболее общий подход будет выглядеть так:
// 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
, очень плохая практика.Если вы хотите использовать битовые поля, вам лучше определить некоторые константы и использовать метод, описанный выше.Не сложно придумать простой класс.
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 значениями на битовое поле.