page contents

位运算在 PHP 实际项目当中的高级运用

有一个广告表,我们要对广告做显示控制:手动上下线。只允许 VIP 查看。

attachments-2020-04-czvCRvrf5e97f3b5c904b.png

我们首先来看一个系统中常见的需求:

有一个广告表,我们要对广告做显示控制:

  1. 手动上下线。
  2. 只允许 VIP 查看。
可能的表结构如下:
CREATE TABLE `finger_ad` (
  `ad_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `ad_name` varchar(50) NOT NULL COMMENT '广告名称',
  `ad_image_url` varchar(255) NOT NULL COMMENT '广告图片',
  `ad_url` varchar(255) NOT NULL COMMENT '广告图片URL跳转地址',
  `is_vip` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否仅限 VIP 显示',
  `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT '显示状态:1显示、0隐藏',
  PRIMARY KEY (`ad_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='广告表';

假如后期,我们需求更改了。需要再增加几种限制:

  • 已登录用户
  • 未登录用户
  • 30 天内未登录用户
  • 注册 30 天的用户

遇到这种限制条件的需求,开发同学是不是很伤脑筋?

可能很多开发第一反应就是在表结构增加这种新增的限制条件字段。一切看来似乎很美好。

的确,这样添加字段是最快最容易的方式。也能完成我们的需求。

但是,这样会引来如下毛病:

  • 每次增加限制条件。我们都要增加字段。这种对数据库的更动能少改就少改。毕竟,无限制的增加字段不可取。
  • 假如广告表数据量很大。大到增加一个字段需要几分钟的时候,这会给数据库服务器造成读写压力。
  • 条件越多,SQL 条件语句就会越来越长。

那么,还有没有更好的方式解决这些问题呢?

答案:有!

这就是我们今天要讲的按位与运算符的高级技巧。

我们把上面的表结构改一下:

DROP TABLE IF EXISTS `finger_ad`;
CREATE TABLE `finger_ad` (
  `ad_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `ad_name` varchar(50) NOT NULL COMMENT '广告名称',
  `ad_image_url` varchar(255) NOT NULL COMMENT '广告图片',
  `ad_url` varchar(255) NOT NULL COMMENT '广告图片URL跳转地址',
  `bit_condition` INT(11) UNSIGNED NOT NULL COMMENT '位运算条件:1-登录可访问、2-未登录可访问、4-30天注册可访问、8-30天未登录可访问、16-未消费可访问、32-VIP可访问',
  `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT '显示状态:1显示、0隐藏',
  PRIMARY KEY (`ad_id`)
) ENGINE=InnoDB DEFAULT CHARSET UTF8 COMMENT='广告表';

我们把所有的条件都去掉了。增加了一个字段: bit_condition 。把所有的条件都组合到一个字段。

那我们此时该如何写代码呢?

比如,现在要添加如下限制条件的广告:

只允许登录用户访问或已注册 30 天用户或是 VIP 用户才允许访问该广告。

那么,这个广告的 bit_condition 该如何设置值呢?很简单,把这几个条件的位值直接相加。此时值为:37。

很多可能会很奇怪。设置为 37 ,我怎么知道是这几个值的和呢?如果对 Linux 系统权限熟悉的同学就很容易理解这种做法。实际上,这里运用了按位与运算的特性:任意组合相加的值不会重复。

这个理解起来有一定难度。我三两句也很难给你梳理明白。大家可以在网上深入挖掘一下这方面儿的知识。你只需要知道这一点特点即可。

那么,现在我们该如何写 SQL 呢?

示例如下:

SELECT * FROM finger_ad WHERE display = 1 AND bit_condition & 3 = bit_condition

这条 SQL 语句当中的 3 对应的是当前用户针对这么多条件得到的数值。如果 bit_condition位值是与 3 按位与与 bit_condition 结果相同,说明条件符合。

我们通过一个字段解决了所有条件的问题。着实得感谢按位与运算符的特性。同时也对 MySQL能支持位运算符感到开心。

那么,它有什么缺点呢?

想必有经验的同学已经看出来了。这种写法只能满足包含关系。假如要实现同时满足 3 个条件才能访问就不行了。或者,一个满足另外一个取反。

优点明显,同样缺点也很明显。大家要根据实际情况来选用。

扩展学习文件:

<?php
//例子1
echo '<h2>demo1</h2><br>';

//定义常量
define('D1',1);
define('D2',2);
define('D3',4);
define('D4',8);
define('D5',16);

function showStatus($state){
    for($i= 1;$i<=5;$i++){
        $d = 'D'.$i;
        $dd = constant($d);     //取对应常量的值
        if(($state & $dd) > 0){
            echo '第'.$i.'盏灯亮'.'<br>'; //8
        }else{
            echo '<span style="color: red">第'.$i.'盏灯灭</span>'.'<br>';
        }
    }
    echo "sql语句写法:SELECT * FROM `table` WHERE state & {$state} = state;";
    echo '<br>';
    echo '<br>';
}
$state = D4;
showStatus($state);     //只开第4盏灯

$state = D1;
showStatus($state);     //只开第1盏灯

$state = D4 | D1;
showStatus($state);     //开第1盏灯和第4盏灯

$state = (D4 | D2 | D1) & (~D1);
showStatus($state);     //开第1盏灯,第2盏灯和第4盏灯,然后关闭第1盏灯
//例子2
echo '<h2>demo2</h2><br>';

/**
 * 1、权限应用
 * 拥有哪些权限,就把这些权限对应的数值加起来
 * 例如:版主拥有权限(增加、删除、修改、查询),则版主的权限值存储为15(8+4+2+1)
 * 然后【权限值之和】 与 【实际权限值】做【位于】比较
 * 结果是真则拥有权限
 * 结果是假则没有权限
 *
 * 注意:权限值必须是2的N次方,从0次方开始,31次方是2147483648
 * 32次方是4294967296,已超过了常用int(10)最大存储4294967295,所以必须注意权限数量(<31个)
 * 当然如果存储格式为bitint或varchar等可以存储更长数字的格式,那么权限数量可以继续增加
 */
// 赋予权限值-->删除:8、上传:4、写入:2、只读:1
define('mDELETE',8);
define('mUPLOAD',4);
define('mWRITE',2);
define('mREAD',1);
//vvvvvvvvvvvvv使用说明vvvvvvvvvvvvv
//部门经理的权限为(假设它拥有此部门的所有权限),| 是位或运行符,不熟悉的就查查资料
echo '全部权限为:'.(mDELETE|mUPLOAD|mWRITE|mREAD).'<br>';// 相当于是把上面的权限值加起来:8+4+2+1=15

/*
*赋予它多个权限就分别取得权限值相加,又比如某位员工拥有除了删除外的权限其余都拥有,那它的权限值是多少?
*应该是:4+2+1=7
*/

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//判断某人的权限可用,设权限值在$key中
/*
*判断权限用&位与符,
*/
// 设我只有 upload 和 read 权限,则
$key = (mUPLOAD|mREAD);//相当于是把上传、只读的权限值分别相加:4+1=5
echo '当前权限为:'.$key.'<br>';;
if($key & mDELETE){
    echo '有删除权限'.'<br>'; //8
}else{
    echo '<span style="color: red">无删除权限</span>'.'<br>'; //8
}
if($key & mUPLOAD){
    echo '有上传权限'.'<br>'; //4
} else{
    echo '<span style="color: red">无上传权限</span>'.'<br>'; //4
}
if($key & mWRITE){
    echo '有写入权限'.'<br>'; //2
} else{
    echo '<span style="color: red">无写入权限</span>'.'<br>'; //2
}
if($key & mREAD){
    echo '有只读权限'.'<br>'; //1
} else{
    echo '<span style="color: red">无只读权限</span>'.'<br>'; //1
}
?>

运行结果:

v2-ea0dc388cb023b872fdfe1dedd5547c8_720w.jpg


attachments-2020-04-nruDhYmq5e97f3cebae70.jpg

  • 发表于 2020-04-16 13:58
  • 阅读 ( 453 )
  • 分类:PHP开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1470 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章