Yii2 и PHP, побитовое сравнение

В представлении памяти вычислительной техники, вся информация кодируется в двоичной форме, т.к. ее легко сохранять как одно из двух устойчивых состояний, а именно, 1 и 0.

Поверхностно освежим память о двоичной форме исчисления.

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

Двоичная, потому, что всего фигурирует две цифры: 1 и 0. Дискретная математика объединяет много форм представления таких чисел. К примеру, истина и ложь, присутствие и отсутствие, любит и не любит, а также, многое другое.

Пример двоичных чисел с их десятичными аналогами:

Десятичное представление => Двоичная форма

0 => 0

1 => 1

2 => 10

3 => 11

4 => 100

5 => 101

Вообще, есть много интересного материала о двоичных представлений и их смежных форматах, даже с академических лекций. К примеру, двоично-десятичном кодирование десятичных цифр. Когда на один десятичный разряд приходится 4 двоичных – тетрада. И почему, в таком представлении стали популярны весовые коэффициенты: 8-4-2-1 и 2-4-2-1. Почему их придумали два разных вида, хотя исследования велись в одной единой стране. А также, было бы не плохо вспомнить о кодировании в целом, кодах Грея и многом интересном, но не будем выходить за рамки статьи.

Каждый порядок двоичного числа в памяти кодируется как один бит, 8 бит являют собой байт, 1024 байта – килобайт, 1024 килобайт – мегабайт и т.д.

Таким образом мы имеет число 5, которое, в двоичной системе исчисления, кодируется как 101, что занимает 3 бита, хотя в памяти, будет зарезервировано, по крайней мере, 4 бита, т.е. 0101.

Побитовое сравнение сопоставляет каждый бит двоичного числа, и с первых взглядов оно нам <del>нахрен</del> совсем не нужно в php и yii2, в частности. Но главное преимущество в том, что мы можем задать каждому порядку двоичного числа логический смысл, но хранить все это как одно число, делая определенные вещи очень компактными.

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

На сайте есть 5 разделов, каждый пользователь может иметь доступ к разным разделам, настраивать нужно гибко, разделы могут добавляться и убираться.

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

В итоге, мы получаем, что “Раздел 1” кодируется как 1 (1 в двоичной форме), “Раздел 2” – 2 (10), “Раздел 3” – 4 (100), “Раздел 4” – 8 (1000), “Раздел 5” – 16 (10000). Можно заметить, что каждый порядковый номер в двоичной форме занимает только один бит, и оставляет свободными все предыдущие порядки, это очень важно.

Далее, к примеру, мы наделяем пользователя правами доступа ко всем разделам. Такой доступ мы закодируем как 11111, где каждый бит отвечает за свой раздел и показывает, разрешен он нам или нет, если порядковый номер раздела равен 1, то доступ разрешен. К примеру, если мы разрешим доступ к первому и пятому разделу, кодировать будем так – 10001, первый, четвертый, пятый – 11001. Считаем справа налево, 5-4-3-2-1. Эти закодированные “послания” переводим в десятичный формат и получаем удобную для хранения информацию. В итоге, что бы дать доступ к первому, четвертому и пятому разделу, мы определили число 11001, что равняется десятичному числу 25. Или же доступ разрешен ко всем разделам, десятичная форма числа будет равна 31.

Теперь, при проверке доступа к разделу мы используем побитовое сравнение. Рассмотрим наш пример, в котором пользователь имеет доступ к первому, четвертому и пятому разделу, закодировано так: 11001/25. пользователь хочет посетить или выполнить действие в разделе 5, который закодирован у нас как 10000/16, т.е. пятый бит. Проверяем, определен ли у нас пятый бит в правах доступа пользователя, он определен, значит мы можем разрешить доступ. В закодированном варианте прав доступа второй бит равен 0, это значит, что пользователь не имеет прав доступа ко второму разделу.

Абстрагируясь от двоичного представления, мы записываем все в десятичной форме исчисления, а это значит, что для проверки доступа к пятому разделу мы сравниваем 25 и 16. В php для этого есть заложенные в ядро возможности, приведенный ниже код демонстрирует принцип работы:

if (16 == (25&16)) {
    return true;
} else {
    return false;
}

Оператор & устанавливает только те биты, которые есть и в 25, и в 16, что равно 16. Вынесем все закодированные веса в подходящие места, к примеру, в бд или константы, и сможем очень легко пользоваться такой конструкцией.

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