Подтвердить что ты не робот

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

В model объект имеет 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;

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

Ответ 2

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

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

Ответ 3

Я закодировал эту простую функцию, чтобы заполнить пробел между 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.

Ответ 4

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

Ответ 5

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

Ответ 7

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

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