Лучшие практики для бит-флагов в PHP

Я пишу небольшое приложение в PHP + MySQL и дошел до того, что есть объект, у которого есть пара (до сих пор, но не ожидается увеличения) связанных с ним флагов. Флаги довольно несвязаны, хотя есть несколько комбинаций, которые не имели бы никакого смысла. Объект представляет строку в БД (имеет некоторые методы для ее сохранения и загрузки), поэтому вопрос также относится к выбору метода хранения.

Вопрос в том, как наилучшим образом представлять их как в коде, так и в БД? Я могу думать о нескольких путях:

Один из способов их хранения в БД состоит в одном целочисленном поле в виде поразрядных флагов. На стороне PHP я могу представить несколько способов их представления:

  • Просто экспортируйте целочисленное значение и определите пару констант флага; Пусть каждое место, где нужны флаги, делает свою собственную поразрядную магию;
  • Определить методы класса GetFlag() , SetFlag() и UnsetFlag() которые выполняют UnsetFlag() магию для частной целочисленной переменной; Эти методы затем будут переданы одной из констант флага в качестве параметров.
  • Определить методы GetFlagA() , GetFlagB() и т. Д. (Вместе с Set and Unset);
  • Определите группу переменных-членов, каждая из которых представляет один флаг; Установите их при загрузке из БД и соберите их при сохранении.
  • Создайте переменную-член, представляющую собой массив всех значений флага. Используйте предопределенные константы в качестве индексов массива для доступа к каждому флагу. Также заполнить / собрать массив при загрузке / сохранении.

Другой способ – сохранить их в БД в виде отдельных полей BIT. В PHP, который затем переводится на несколько переменных-членов. ИМХО это усложнит запросы.

И последний способ – определить анонированную таблицу для всех флагов и промежуточную таблицу для отношений «многие ко многим» между флагами и исходными объектами. ИМХО – самый грязный из всех решений, учитывая, что в противном случае будет только 3 таблицы.

Я не делал много PHP-разработки, поэтому я не знаю, какой будет лучшая практика. В C # я, вероятно, сохранил бы их как побитовые флаги и делал бы свойства, которые выполняют побитную магию на частном целое. Но у PHP нет свойств (я использую последнюю стабильную версию) …

    В вашей модели объект имеет 8 булевых свойств. Это означает 8 столбцов boolean (TINYINT для MySQL) в вашей таблице базы данных и 8 методов getter / setter в вашем объекте. Простой и обычный.

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

     CREATE TABLE mytable (myfield BIT(8)); 

    Хорошо, похоже, что у нас будут бинарные данные, которые здесь происходят.

     INSERT INTO mytable VALUES (b'00101000'); 

    Подождите, кто-нибудь снова скажет мне, что означает каждый из этих 1s и 0s.

     SELECT * FROM mytable; +------------+ | mybitfield | +------------+ | ( | +------------+ 

    Какие?

     SELECT * FROM mytable WHERE myfield & b'00101000' = b'00100000'; 

    WTF !? WTF !?

    бросается в глаза


    – между тем, в альтернативной вселенной, где феи играют с единорогами, а программисты не ненавижу администраторов баз данных … –

     SELECT * FROM mytable WHERE field3 = 1 AND field5 = 0; 

    Счастье и солнце!

    Если вам действительно нужно использовать битовые флаги, используйте столбец SET для их хранения в БД, ассоциативный массив в коде и методы включения / выключения флагов. Добавьте отдельные методы для преобразования массива в / из одного целого числа, если оно вам нужно в этом формате.

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

    Я закодировал эту простую функцию, чтобы заполнить пробел между PHP и MySQL ENUM:

     function Enum($id) { static $enum = array(); if (func_num_args() > 1) { $result = 0; if (empty($enum[$id]) === true) { $enum[$id] = array(); } foreach (array_unique(array_map('strtoupper', array_slice(func_get_args(), 1))) as $argument) { if (empty($enum[$id][$argument]) === true) { $enum[$id][$argument] = pow(2, count($enum[$id])); } $result += $enum[$id][$argument]; } return $result; } return false; } 

    Применение:

     // sets the bit flags for the "user" namespace and returns the sum of all flags (15) Enum('user', 'anonymous', 'registed', 'active', 'banned'); Enum('user', 'anonymous'); // 1 Enum('user', 'registed', 'active'); // 2 + 4 = 6 Enum('user', 'registed', 'active', 'banned'); // 2 + 4 + 8 = 14 Enum('user', 'registed', 'banned'); // 2 + 8 = 10 

    Вам просто нужно убедиться, что вы указали нумерованный список в том же порядке MySQL ENUM.

    Предполагая, что вы используете версию MySQL после 5.0.5, вы можете определить столбец как BIT [ количество бит здесь ]. Что касается стороны PHP, я бы, вероятно, пошел бы с помощью метода Get / SetFlagA, Get / SetFlagB, нет необходимости в методе unset, поскольку вы можете просто использовать логический метод set.

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

    Один из способов их хранения в БД состоит в одном целочисленном поле в виде поразрядных флагов.

    Если вы хотите сделать это, вы можете использовать методы перегрузки __get и __set, чтобы вы могли просто получить поле, а затем выполнить поразрядную арифметику по мере необходимости.

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

    Что касается БД – я собираюсь остаться с побитовым целым на данный момент – в основном потому, что я почти закончил с программным обеспечением и больше не хочу его менять. Это не изменяет читаемость вообще.