【问题标题】:Best practices for bit flags in PHPPHP 中位标志的最佳实践
【发布时间】:2010-09-30 13:20:38
【问题描述】:

我正在用 PHP + MySQL 编写一个小型应用程序,并且已经到了一个对象有几个(目前为 8 个,但预计不会增加)与之关联的标志的地步。这些标志几乎没有关系,尽管有些组合没有意义。该对象代表数据库中的一行(也有一些保存/加载它的方法),所以这个问题也适用于选择存储方法。

问题是 - 如何在代码和数据库中最好地表示它们?我可以想到几种方法:

将它们存储在数据库中的一种方法是在单个整数字段中作为按位标志。在 PHP 方面,我可以想象几种表示它们的方式:

  • 只需导出整数值并定义几个标志常量;让每个需要标志的地方发挥自己的按位魔法;
  • 定义类方法GetFlag()、SetFlag()和UnsetFlag(),对私有整数变量执行按位魔法;然后将这些方法作为参数传递给标志常量之一。
  • 定义方法GetFlagA()、GetFlagB() 等(以及Set 和Unset 对应项);
  • 定义一堆成员变量,每个变量代表一个标志;将它们设置为从数据库加载并在保存时收集它们。
  • 创建一个包含所有标志值的数组的成员变量。使用预定义的常量作为数组索引来访问每个标志。还在加载/保存时填充/收集数组。

另一种方法是将它们作为单独的 BIT 字段存储在数据库中。在 PHP 中,这将转换为几个成员变量。恕我直言,这会使查询复杂化。

最后一种方法是为所有标志定义另一个表,并为标志和原始对象之间的多对多关系定义一个中间表。恕我直言,这是所有解决方案中最混乱的,否则只有 3 张桌子。

我没有做过太多的 PHP 开发,所以我不知道最佳实践是什么。在 C# 中,我可能会将它们存储为按位标志,并创建对私有整数执行按位魔术的属性。但是 PHP 没有属性(我使用的是最新的稳定版本)...

【问题讨论】:

    标签: php mysql flags


    【解决方案1】:

    在您的model 中,该对象具有 8 个布尔属性。这意味着您的数据库表中有 8 个布尔(MySQL 为 TINYINT)列,对象中有 8 个 getter/setter 方法。简单而传统。

    重新考虑您当前的方法。想象下一个必须维护这个东西的人会说什么。

    CREATE TABLE mytable (myfield BIT(8));
    

    好的,看起来我们将在这里发生一些二进制数据。

    INSERT INTO mytable VALUES (b'00101000');
    

    等等,有人再告诉我这些 1 和 0 分别代表什么。

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

    什么?

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

    WTF!? WTF!?

    刺自己的脸


    -- 与此同时,在另一个宇宙中,仙女与独角兽玩耍,程序员不讨厌 DBA...--

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

    幸福和阳光!

    【讨论】:

    • 其实...SELECT * FROM mytable WHERE field3 AND !field5;
    • 这很容易成为我在一段时间内看到的关于这个主题的最幽默的回答。谢谢你的笑声,点个赞。
    • 哈哈哈... WTF---很好的答案!
    • 标志没有错……它们是可扩展的。我使用普通的 int 字段而不是位。在程序员讨厌 DBA 的世界中,您永远不必向表中添加另一个字段以获得新标志。同时,在程序员喜欢 DBA 的世界里,每当一些新客户或公司想要添加新标志时,您都必须跑到 DBA 并让他在表中添加另一个字段。
    • @Christian 我会避免在 MySQL 中使用 !,它们有一些……奇怪之处。
    【解决方案2】:

    如果您确实必须使用位标志,则使用 SET 列将它们存储在数据库中,代码中的关联数组以及打开/关闭标志的方法。如果您需要该格式的数组,请添加单独的方法以将数组转换为单个整数。

    使用低级位运算符没有任何好处,所以你不妨让代码可读。

    【讨论】:

    • 没错。问题只是 - 什么更具可读性?另外,我想避免在 DB 端定义值,因为这样任何更改都必须反映在 DB 和 PHP 中。按位标志的更改将仅限 PHP。
    • 如果您已经查看了这两种方法并且无法确定哪种方法更具可读性,那么该决定似乎是任意的。尝试向其他开发人员展示示例,或者假设您必须在忘记代码很久之后重新熟悉代码才能更好地了解可读性。
    • Vilx:不是这样。无论采用哪种方法,更改都必须反映在数据库中。
    【解决方案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】:

      假设您使用 5.0.5 之后的 MySQL 版本,您可以将列定义为 BIT[此处的位数]。至于 PHP 方面,我可能会使用 Get/SetFlagA、Get/SetFlagB 方法,不需要 unset 方法,因为您可以简单地将布尔值传入 set 方法。

      【讨论】:

        【解决方案5】:

        我会远离按位运算,因为仅通过查看数据库就很难知道设置了哪些标志(除非您出于某种原因试图变得超级高效)。在其他两个选项之间,我认为这取决于这些标志中的每一个具有多少意义。鉴于已经有 8 个,我会倾向于另一张表,它们之间存在多对多关系(抱歉选择了你最不喜欢的)。

        【讨论】:

          【解决方案6】:

          将它们存储在数据库中的一种方法是 单个整数字段按位 标志。

          如果您想这样做,您可以使用 __get 和 __set overload methods,这样您就可以简单地检索该字段,然后根据需要进行按位运算。

          【讨论】:

          • 是的,我忘了提到这种可能性。这是我不喜欢的另一个,因为它几乎会破坏 IDE 中的代码完成,而且恕我直言,这使代码的可读性更差。
          【解决方案7】:

          好的,在考虑了更多之后,我决定为每个标志使用一个成员变量。我本可以使用 getter/setter 方法,但我不会在代码中的其他任何地方使用它们,所以这会过时。另外,通过这种方式,我还可以从 DB 存储方法中抽象出来,以后可以根据需要轻松更改它。

          至于数据库 - 我现在将继续使用按位整数 - 主要是因为我几乎完成了该软件并且不想再更改它。它根本不会改变可读性。

          【讨论】:

            猜你喜欢
            • 2015-05-28
            • 2014-07-22
            • 2010-09-12
            • 2015-07-14
            • 1970-01-01
            • 2011-08-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多