Лучшие практики для битовых флагов в PHP - PullRequest
13 голосов
/ 09 января 2009

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

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

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

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

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

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

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

Ответы [ 7 ]

35 голосов
/ 10 января 2009

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

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

CREATE TABLE mytable (myfield BIT(8));

ОК, похоже, что здесь будут какие-то двоичные данные.

INSERT INTO mytable VALUES (b'00101000');

Подождите, кто-нибудь еще раз скажет мне, что означает каждая из этих единиц и нулей.

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 голосов
/ 09 января 2009

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

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

1 голос
/ 24 июня 2010

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

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.

0 голосов
/ 10 января 2009

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

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

0 голосов
/ 09 января 2009

Один из способов сохранить их в БД - одно целочисленное поле как побитовое флаги.

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

0 голосов
/ 09 января 2009

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

0 голосов
/ 09 января 2009

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

...