Understanding Bitwise Operators with PHP
One of the unknown features in PHP is Bitwise Operators. In the article, we will learn things through practice with a real example!
Bit operations are rare in the PHP world, however, in books, articles, or other sources, you might find something like:
$memory = memory_get_usage() >> 20;
You may get confused. What is ">>
"? So, let's look at the documentation .
$a >> $b
Shift the bits of$a $b
steps to the right (each step means "divide by two")
WTF?! Why do you need it at all? So let's dive into it.
Many people know that binary (or base-2) a numeric system that only uses two digits — 0
and 1
. Computers operate in binary, meaning they store data and perform calculations using only zeros and ones. So, for example, a computer will represent the number 6
as 00000110
.
Bit Rotation : A rotation (or circular shift) is an arithmetic operation.
- In the left rotation, the bits that fall off at the left end are put back at the right end.
- In the right rotation, the bits that fall off at the right end are put back at the left end.
Let's back to our operator ">>
".
$n = 6; // 00000110
$k = $n >> 1; // 00000011
We can see that the bits that fall off at the right end are put back at the left end. So the current binary value 00000011
in decimal is 3
. We just shifted one bit, and as a result, we divided the value by 2
.
If we were to shift 2
bits, we would divide by 4
.
If we were to shift 3
bits, we would divide by 8
.
Wow, that's the power of two!
Get back to the example >> 20
. It means that we are dividing the value by 2
to the power of 20.
It is easy to remember that 2 ^ 10 = 1024
. So
$memory = memory_get_usage() / (1024 * 1024);
Since memory_get_usage()
returns a value in bits, we just converted it to megabytes. It turns into a very handy function:
$memory = memory_get_usage() >> 10; // convert it to kilobytes (KB)
$memory = memory_get_usage() >> 20; // convert it to megabytes (MB)
$memory = memory_get_usage() >> 30; // convert it to gigabytes (GB)
$memory = memory_get_usage() >> 40; // convert it to terabyte (TB)
If right rotation means division, then left rotation, on the contrary, means multiplication.
$y = 5; // 000000101
echo $y << 2; // 000010100 (5 * 4 = 20)
Are there other operators besides right/left rotation?
There are 4 more operations - AND &
, OR |
, XOR ^
, NOT ~
. Let's see it in action below.
What's the best way to use bitwise operations?
The most convenient to use bitwise operations, not in multiplication and division, but is using a bitwise mask, for example, to differentiate access rights or other similar things.
We can turn four values into a four-bit value, in which 1
means the user has the given right, and 0
does not.
define('U_READ', 1 << 0); // 0001
define('U_CREATE', 1 << 1); // 0010
define('U_EDIT', 1 << 2); // 0100
define('U_DELETE', 1 << 3); // 1000
define('U_ALL', U_READ | U_CREATE | U_EDIT | U_DELETE); // 1111
In the first four lines
, we defined the constants by shifting bits to the left. After that, we used the OR |
operator on the last line. The bitwise OR operator (|) returns a 1
in each bit position for which the corresponding bits of either or both operands are 1s
. Example:
$x = 3; // 0011
$y = 5; // 0101
echo $x | $y; // 0111 (7)
Therefore, we can set any permissions for the user:
$userPermission = U_READ; // Only read
$userPermission = U_READ | U_CREATE; // Only read and create
$userPermission = U_ALL ^ U_DELETE; // All rights except deletion
$userPermission = U_ALL & ~ U_DELETE; // Again all rights except deletion
In the example above, there are 3
new operators.
- A bitwise XOR operator (^) (performs the logical exclusive OR). It returns a
1
in each bit position for which the corresponding bits of either but not both operands are1s
. - The bitwise AND operator (&) returns a
1
in each bit position for which the corresponding bits of both operands are1s
. - The bitwise NOT operator (~) inverts the bits of its operand.
Example:
#XOR
$x = U_ALL; // 1111
$y = U_DELETE; // 1000
echo $x ^ $y; // 0111
#AND
$x = U_ALL; // 1111
$y = U_DELETE; // 1000
echo $x & $y; // 1000
#NOT
$y = U_DELETE | U_READ; // 1001
echo ~$y; // 0110 (inverted)
What happens if we use AND
+ NOT
operators?
In the example below, use the two operators together. The bitwise NOT operator (~)
will be executed first, followed by the bitwise AND operator (&)
.
...
$userPermission = U_ALL & ~ U_DELETE; // Again all rights except deletion
U_ALL 1111
~ U_DELETE 0111
RESULT 0111
Is there any difference between the XOR
and AND
+ NOT
operators?
The difference between these options is that in the first case, the bit switches. If it was 1
, then it will become 0
, and vice versa. The second option makes the bit equal to 0
, regardless of its current value.
If we want to remove any access right, do the following:
$userPermission &= ~ U_DELETE; // prohibit deletion
A few more examples:
To check the bits (in our case, the access rights), we can use the following conditions.
if ($userPermission & U_READ) // is there a right to read?
if ($userPermission & (U_READ | U_DELETE)) // is there a right to read or/and delete?
One more example:
// A lot of code duplication
if ($error['type'] == E_ERROR
|| $error['type'] == E_PARSE
|| $error['type'] == E_COMPILE_ERROR) {}
//Better
if (in_array($error['type'], [E_ERROR, E_PARSE, E_COMPILE_ERROR])) {}
//Perfect)
if ($error['type'] & (E_ERROR | E_PARSE | E_COMPILE_ERROR)) {}
Although error codes in PHP are specially designed for bitwise operations, developers usually use comparison operators. But now you know a different approach 😉
Conclution
Thanks for reading this article! I hope you now understand bitwise operators and can utilize them in your PHP code (or in many other languages!). if you have any questions or comments, please leave them below. I appreciate any feedback!
Stay tuned for new articles by following me on Twitter or LinkedIn! Subscribe to my newsletter or RSS. Drop me an email if you have any questions.