逻辑运算符 位运算符_在现代PHP中,按位运算符仍然有意义吗?

逻辑运算符 位运算符

Many of you probably scratched your heads reading this title. “Bitwhat?”

你们中的许多人可能会reading头阅读此标题。 “什么?”

In this article, we’ll look at what bitwise operators are, and whether or not their use is still relevant in this modern age of computing.

在本文中,我们将研究什么是按位运算符,以及在现代计算时代是否仍需要使用按位运算符。

Ones and zeros stock picture

示例用例 (Example Use Case)

Bitwise operators are listed here, but to really drive the example home, we’ll focus on just one: the bitwise and (&). An example made it click for me. So that’s what we’ll do – dive straight into an example.

此处列出了按位运算符,但是为了真正开始示例,我们将仅关注一个: 按位和 ( & )。 一个例子让我点击了。 这就是我们要做的–直接研究一个例子。

Imagine you have a website on which a given user can have specific permissions. For example, a magazine like SitePoint:

假设您有一个网站,给定用户可以在该网站上拥有特定权限。 例如,像SitePoint这样的杂志:

  • a author can CRUD drafts, and and edit their profile.

    作者可以CRUD草稿,然后编辑其个人资料。
  • an editor can, in addition to the above, CRUD drafts and finished posts, and CRUD author profiles.

    除上述内容外,编辑者还可以填写CRUD的草稿和完成的文章以及CRUD作者的个人资料。
  • an administrator can, in addition to the above, add administrator permissions.

    除上述内容外,管理员还可以添加管理员权限。

Since a user can have multiple permissions, there are several ways of defining permissions in a database and the system using it.

由于用户可以具有多个权限,因此有几种方法可以在数据库和使用该数据库的系统中定义权限。

双重加入 (The Double Join)

Add roles, add permissions, attach permissions to roles in a join table, then create another join table and bind some roles to some users.

添加角色,添加权限,将权限附加到联接表中的角色,然后创建另一个联接表并将某些角色绑定到某些用户。

This approach creates four extra tables:

这种方法创建了四个额外的表:

  • permissions

    权限
  • roles

    角色
  • permissions<->roles

    权限<->角色
  • roles<->users

    角色<->用户

Quite a bit of overhead. Imagine having to edit these or list them in the app regularly in some frequently visited lists. Only heavy caching would save this app from collapsing under heavy load.

相当多的开销。 想象一下,必须编辑这些文件或在一些经常访问的列表中定期在应用程序中列出它们。 只有繁重的缓存才能避免此应用在重负载下崩溃。

One advantage, though, is that by defining roles really well with intricate permissions, you only have to stick users into roles and you’re good – it keeps that join table light and fast.

但是,优点之一是,通过使用复杂的权限很好地定义角色,您只需将用户吸引到角色中就可以了,而且您还不错–它使联接表保持轻快。

单一加盟 (The Single Join)

Add permissions, add a join table, attach some permissions to some users

添加权限,添加联接表,将权限授予某些用户

This approach creates two extra tables:

这种方法创建了两个额外的表:

  • permissions

    权限
  • permissions<->users

    权限<->用户

Much less overhead than the previous example, but you have many more entries in the join table because a user can have a LOT of permissions (just the CRUD for drafting is 4 permissions on its own). With a lot of users and a lot of permissions, this table can get heavy quickly.

与前面的示例相比,开销要少得多,但是由于用户可以具有很多权限(因此草稿的CRUD本身就是4个权限),所以联接表中的条目更多。 由于拥有大量用户和大量权限,此表很快就会变得繁重。

纵队踩踏 (The Column Stampede)

Add a column into the users table for each permission, then make its datatype a tinyint(1) (basically a boolean) to check the permission as “on” or “off”.

在每种权限的用户表中添加一列,然后将其数据类型设置为tinyint(1)(基本上是布尔值),以将权限检查为“打开”或“关闭”。

Setting permissions for a user would then look something like this:

为用户设置权限将如下所示:

UPDATE `users` SET `editProfile` = 1, `deleteProfile` = 0, `createDraft` = 1, `publishDraft` = 0 ... WHERE `id` = 5

This approach adds no extra tables, but needlessly expands the table into gargantuan width, and requires a modification of the database every time a new permission is added. It’s a fine approach for when you know you’ll have at most two or three permissions for the foreseeable future, but shouldn’t be used for anything more than that.

这种方法不添加任何额外的表,但是不必要地将表扩展为巨大的宽度,并且每次添加新权限时都需要修改数据库。 当您知道在可预见的将来最多拥有两三个权限时,这是一种很好的方法,但不应将其用于更多用途。

However, because the list of columns, when looked at from afar, resembles a binary number (1010), this approach is an excellent segway into another…

但是,由于从远处看时,列的列表类似于二进制数(1010),因此这种方法非常适合于另一种方法。

按位方法 (The Bitwise Approach)

Before we dive deeper into this approach, let’s have a crash course in binary.

在更深入地研究这种方法之前,让我们先学习一下二进制的速成课程。

二进制数 (Binary Numbers)

All computers store data as binary: 0 or 1. So, the number 14 is actually stored as: 1110. How so?

所有计算机都以二进制格式存储数据:0或1。因此,数字14实际上存储为:1110。

Binary numbers are evaluated from right to left when calculating their value, just like real numbers. So the number 1337 means:

就像实数一样,在计算二进制数的值时,也会从右到左评估二进制数。 因此,数字1337表示:

  • 1 x 7

    1 x 7
  • + 3 x 10

    + 3 x 10
  • + 3 x 100

    + 3 x 100
  • + 1 x 1000

    + 1 x 1000

Because each digit in the decimal system (base 10) gets multiplied by 10. The first one is 1, the next one is 10, the next after that 100, the next 1000, etc.

因为十进制系统(以10为底)中的每个数字都乘以10。第一个数字为1,下一个数字为10,下一个数字为100,下一个数字,依此类推。

In binary, the base is 2, so each digit gets multiplied by 2. The number 1110 is therefore:

在二进制中,底数为2,因此每个数字都乘以2。因此,数字1110为:

  • 0 x 1

    0 x 1
  • + 1 x 2

    + 1 x 2
  • + 1 x 4

    + 1 x 4
  • + 1 x 8

    + 1 x 8

That’s 2 + 4 + 8, which is 14.

那是2 + 4 + 8,即14。

Yes, it’s that simple to convert binary numbers to decimal.

是的,将二进制数字转换为十进制就这么简单。

So when we look at our columns of permissions from before being 1010, that might as well be seen as the number 10 written in binary form. Hmm, maybe we’re onto something here.

因此,当我们查看1010之前的权限列时,也可以将其视为二进制形式的数字10。 嗯,也许我们在这里。

If we have 1010 as permissions, that means the 2nd and 4th bit are set, whereas the first and third are not (because they are 0).

如果我们有1010作为权限,则意味着第2位和第4位被设置,而第1位和第3位未设置(因为它们为0)。

In binary parlance, we actually say the 0th and 2nd bit are not set, because they’re counted from 0, just like arrays. This is because their ordinal number (1st, 2nd, 3rd) corresponds to their exponent. The 0th bit is actually 2 to the power of 0 (2^0) which equals 1. The 1st bit is 2 to the power of 1 (2^1) which is 2. The 2nd is 2 squared (2^2) which equals 4, etc. That way it’s all very easy to remember.

用二进制的话来说,我们实际上是说未设置第0和第2位,因为它们是从0开始计数的,就像数组一样。 这是因为它们的序数(第一,第二,第三)与它们的指数相对应。 第0位实际上是2等于0的幂(2 ^ 0)等于1。第1位是2等于1的幂(2 ^ 1)等于2。第二个是2的平方(2 ^ 2),等于4,依此类推。这样很容易记住。

So how does this help us?

那么这对我们有什么帮助呢?

按位方法 (The Bitwise Approach)

Well, by looking at permissions from afar, we can represent the state of all the columns at once with a single binary number. If we can represent all the columns at once with a single binary number, that means we can also represent it with a single integer when translated into decimal!

好吧,通过远方查看权限,我们可以用一个二进制数一次表示所有列的状态。 如果我们可以用一个二进制数一次表示所有的列,这意味着当转换成十进制时,我们也可以用一个整数来表示它!

If we had a single permissions column which contained the value 14, we would now know that this is actually 1110, and we would know that we have three out of four permissions! But which 3 our of 4?

如果我们只有一个包含值14 permissions列,那么我们现在知道实际上是1110 ,并且我们知道我们拥有四个权限中的三个! 但是,我们4个中的哪3个?

Imagine the following mapping of permissions:

想象一下以下权限映射:

CHANGE PERMISSIONSPROFILE CREATEPROFILE EDITPROFILE DELETEDRAFT CREATEDRAFT EDITDRAFT DELETEDRAFT PUBLISHFINISHED EDITFINISHED DELETE
5122561286432168421
更改权限 建立个人资料 个人资料编辑 个人资料删除 创建草案 草稿编辑 删除草稿 草稿 完成的编辑 完成删除
512 256 128 64 32 16 8 4 2 1个

The number 14 in binary is 1110, but the number of zeroes on the left doesn’t matter, so we can pad it until we reach the number of the permissions in the table: 0000001110. This is still 14, only representative of the permissions from the table above. For all intents and purposes, 0000001110 === 1110.

二进制中的数字14是1110,但是左侧的零数字无关紧要,因此我们可以填充它,直到达到表中的许可权编号:0000001110。这仍然是14,仅代表许可权从上表。 对于所有意图和目的,0000001110 === 1110。

According to this, we see that the account with a permission of 14 has the permissions: DRAFT_DELETE, DRAFT_PUBLISH, and FINISHED_EDIT. Granted, not exactly representative of a real world permission setup, but it’s just an example through which we can extrapolate that if one were to have 1111111111, they would have ALL the permissions (likely an admin user). In decimal, this is 1023. So, someone with the value 1023 in the permissions column is someone with all the permissions.

据此,我们看到具有14权限的帐户具有以下权限: DRAFT_DELETEDRAFT_PUBLISHFINISHED_EDIT 。 当然,不能完全代表现实中的权限设置,但这只是一个示例,我们可以通过该示例推断出,如果要拥有1111111111,则他们将拥有所有权限(可能是管理员用户)。 用十进制表示,是1023。因此, permissions列中值为1023的人就是所有权限的人。

But how would we check for this in our code? In other words, how can we know if a permission’s bit is set (1) or not (0), especially if a number is stored as decimal, and not binary?

但是,我们将如何在代码中检查这一点? 换句话说,如何知道权限的位是否设置为(1)(0),特别是如果数字存储为十进制而不是二进制时?

That’s what bitwise operators are for – particularly the single ampersand &, also known as the bitwise and.

这就是按位运算符的用途–尤其是单个&符& ,也称为按位和

MySQL supports it like so:

MySQL像这样支持它:

SELECT * FROM user WHERE id = 5 AND 512 & permissions

This translates literally to “select all users with the ID 5 who also have the 512 bit of the column permissions set to 1″. You would check for other bits by just changing their value: 256, 128, 64, 32, 16, 8, 4, 2, or 1.

字面意思是“选择ID为5的所有用户,这些用户还将512位的列permissions设置为1”。 您可以通过更改其他位的值来检查其他位:256、128、64、32、16、8、4、2或1。



[可选]“让我们掌握技术”旁注 (The [optional] “let’s get technical” side-note)

Skip this divided section if you don’t want to know how this operator, or similar operators work, but are just interested in continuing with the example.

如果您不想知道此运算符或类似运算符的工作方式,而只想继续看示例,请跳过此部分。

When we say AND 512 & permissions we’re looking for the part after AND to be TRUE, because that’s how SQL queries operate – they evaluate conditions and return those rows which return true in regards to requirements.

当我们说AND 512 & permissions我们正在寻找AND之后为TRUE的部分,因为这就是SQL查询的工作方式–它们评估条件并返回那些在需求方面返回true的行。

Therefore, 512 & permissions has to evaluate to true. We know that any non-zero value, be it an integer, a boolean that says “true”, or a string that is not empty, is actually considered “true”. So 512 is true. 1 is true. 0 is false. 128 is true. Etc.

因此, 512 & permissions必须评估为true。 我们知道,任何非零值(无论是整数,说“ true”的布尔值还是非空字符串)实际上都被视为“ true”。 因此512是正确的。 1是正确的。 0为假。 128是正确的。 等等。

512 is a base-10 integer, and permissions is a column which can contain a base-10 integer. The bitwise and actually looks at the cross-section of these two numbers, and returns the bits that are set (1) in both of them. So, if the number 512 is 1000000000, and if the permissions value is 1023, when converted into binary that’s 1111111111. The cross section of those returns 1000000000 because only the left-most bit is set in both numbers. When we convert this back into decimal, that’s 512, which is considered true.

512是一个以10为底的整数, permissions是一列,可以包含一个以10为底的整数。 按位和实际上查看这两个数字的横截面,并返回在两个数中均设置为(1)的位。 因此,如果数字512为1000000000,并且许可权值为1023,则将其转换为二进制时为1111111111。由于两个数字都只设置了最左边的位,所以它们的横截面返回1000000000。 当我们将其转换回十进制时,即为512,这被视为true

These are actually logical, not arithmetic operators, in that they check for truthiness based on a condition. If we have the numbers 1110 and 1010, here’s what they produce given the different bitwise operators:

这些实际上是逻辑运算符,而不是算术运算符,因为它们根据条件检查真实性。 如果我们有数字1110和1010,则这是给定不同按位运算符的结果:

&|^~
Operand A1110111011101110
Operand B101010101010/
Result1010111001000001
| ^
操作数A 1110 1110 1110 1110
操作数B 1010 1010 1010 /
结果 1010 1110 0100 0001
  • & returns a binary number in which all bits are set that are set in both operands.

    &返回一个二进制数,其中两个操作数中都设置了所有位。

  • | returns a binary number with all bits set that are set in either operand.

    | 返回一个二进制数,其中任何一个操作数中都设置了所有位。

  • ^ returns a binary number with all bits set that are set in either operand, but not both.

    ^返回一个二进制数,其中所有位均在一个操作数中设置,但不能同时在两个操作数中设置。

  • ~ just returns the opposite – all those not set in the original operand are now set.

    ~只是返回相反的结果–原来未在原始操作数中设置的所有值都已设置。

There are also the bitwise shift operators: left shift << and right shift >>. These dramatically change the values of binary numbers by literally moving all set bits one place to the right or left. Their use in our context is questionable, so we won’t be covering them here.

还有按位移位运算符:左移位<<和右移位>> 。 通过从字面上将所有设置的位向右或向左移动一位,这些操作会极大地改变二进制数的值。 在我们的上下文中,它们的使用是有问题的,因此我们将不在此处进行介绍。



And in PHP we can test if a bit is set like so:

在PHP中,我们可以测试是否设置了如下所示的代码:

if (1023 & 1) {

}

But this is really, really hard to decipher – just looking at raw numbers isn’t really readable or understandable. So, in PHP, it’s better to use constants defining permissions as bits, and fetching the permission’s integer value from the column. Then, you end up with something like this:

但这确实很难破译-仅查看原始数字并不真正可读或可理解。 因此,在PHP中,最好使用将权限定义为位的常量,并从列中获取权限的整数值。 然后,您将得到如下结果:

if ($user->permissions & \MyNamespace\Role::FINISHED_DELETE) {
  // 
}

Here we assume we’ve got a \MyNamespace\Role class defined and loaded with constants like these:

在这里,我们假设已经定义了\MyNamespace\Role类,并加载了如下常量:

const FINISHED_DELETE = 1;
const FINISHED_EDIT = 2;
const DRAFT_PUBLISH = 8;
...
const CHANGE_PERMISSIONS = 512;

Suddenly, you’ve got a really easy way of storing multiple permissions per user without using extra tables and creating unnecessary overhead.

突然之间,您就拥有了一种非常简单的方法来存储每个用户的多个权限,而无需使用额外的表,也不会造成不必要的开销。

So how do we store this in the database when permissions change? Just sum the permissions together, and store them as integer! A person who can FINISHED_DELETE and FINISHED_EDIT has permission 1 and 2, as per the constants above. Therefore, to save their permissions, you simply sum them up (1+2=3) and save 3 into the permissions column. There is no other way to get the number 3 with binary combinations – the number 3 cannot be represented in binary in any other way than 0011 – so you can be 100% certain that number 3 always means the user has permission 1 and permission 2, corresponding to their values in constants.

那么,当权限更改时,我们如何将其存储在数据库中? 只需将权限加在一起,然后将它们存储为整数! 根据上述常量,可以进行FINISHED_DELETEFINISHED_EDIT具有权限1和2。 因此,要保存它们的权限,只需将它们加起来(1 + 2 = 3)并将3保存到permissions列中。 没有其他方法可以通过二进制组合来获取数字3 –数字3不能用除0011之外的任何其他方式用二进制表示–因此,您可以100%确定数字3始终表示用户具有权限1和权限2,对应于它们在常量中的值。

This seems too simple and practical, right? What’s the catch?

这似乎太简单实用了,对吧? 有什么收获?

注意事项 (Caveats)

There are two major caveats:

有两个主要警告:

  1. You need to keep in mind to use the power of 2 when calculating the next permission’s bit value. So if you need to add a new permission, you can’t just willy-nilly pick 543 if you already have 512 – it’ll have to be 1024. This gets a little more complex as the numbers get bigger.

    在计算下一个权限的位值时,请记住使用2的幂。 因此,如果您需要添加新的权限,则不能只随意选择543(如果您已经拥有512),它就必须是1024。随着数字的增加,这一点变得更加复杂。
  2. Since our computers are running 64 bit operating systems on 64 bit CPUs (mostly – some are even stuck on 32bit still!), that means a number can have a maximum of 64 bits only. What this means is that you can only store permutations of a maximum of 64 permission on a given user. For small to medium sites this is quite enough, but on enormous websites, this can turn into a problem. The solution there is to use different columns for different permission contexts (draft_permissions, account_permissions, etc.). Each of those columns can then contain permutations of 64 permissions on its own, which is enough for even the most demanding websites.

    由于我们的计算机在64位CPU上运行64位操作系统(大多数情况下-有些甚至还停留在32位上!),这意味着一个数字最多只能有64位。 这意味着您只能在给定用户上存储最多64个权限的排列。 对于中小型网站,这已经足够了,但是在庞大的网站上,这可能会成为一个问题。 那里的解决方案是针对不同的权限上下文( draft_permissionsaccount_permissions等)使用不同的列。 然后,这些列中的每列都可以单独包含64个权限的排列,即使是要求最苛刻的网站也足够了。

结论 (Conclusion)

Bitwise operations definitely still have a place in modern programming. While it may be counterintuitive to use something so seemingly complex (it’s really not – it’s just not nearly as familiar as modern day join tables), this approach brings many benefits – not the least of which is a dramatic boost in performance, both in data size (a lot less information to store in the database, and to subsequently fetch) and speed (a user object can have their permission value pre-fetched – it’s just an int – and thus can be checked for it at all times).

在现代编程中,按位运算肯定仍然占有一席之地。 尽管使用看起来看似复杂的东西可能是违反直觉的(实际上并没有–它不像现代联接表那样熟悉),但是这种方法带来了很多好处–不仅在数据方面,而且在性能方面也有了显着提高大小(存储在数据库中以及随后要提取的信息少得多)和速度(用户对象可以预先获取其许可权值-这只是一个int值,因此可以随时对其进行检查)。

Packages as those presented here certainly make things simple, but only if you’re not already aware of even simpler alternatives like the ones demonstrated above.

此处介绍的软件包确实使事情变得简单,但前提是您还没有意识到更简单的选择,例如上面演示的那些。

How do you feel about using bitwise operators for checking permissions and this approach to storing them? Any obvious pros / cons? Let us know how you do it, and why!

您对使用按位运算符检查权限以及这种存储权限的方式感觉如何? 有明显的优点/缺点吗? 让我们知道您的操作方式以及原因!

翻译自: https://www.sitepoint.com/bitwise-operators-still-relevant-modern-php/

逻辑运算符 位运算符

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值