Bitwise operators are like logical operators but executed per bit rather than per boolean value.
// bitwise NOT ~: sets all unset bits and unsets all set bits
printf("%'06b", ~0b110110); // 001001
Bitwise AND &
: a bit is set only if it is set in both operands
printf("%'06b", 0b110101 & 0b011001); // 010001
Bitwise OR |
: a bit is set if it is set in either or both operands
printf("%'06b", 0b110101 | 0b011001); // 111101
Bitwise XOR ^
: a bit is set if it is set in one operand and not set in another operand, i.e. only if that bit is in different state in the two operands
printf("%'06b", 0b110101 ^ 0b011001); // 101100
These operators can be used to manipulate bitmasks. For example:
file_put_contents("file.log", LOCK_EX | FILE_APPEND);
Here, the |
operator is used to combine the two bitmasks. Although +
has the same effect, |
emphasizes that you are combining bitmasks, not adding two normal scalar integers.
class Foo{
const OPTION_A = 1;
const OPTION_B = 2;
const OPTION_C = 4;
const OPTION_A = 8;
private $options = self::OPTION_A | self::OPTION_C;
public function toggleOption(int $option){
$this->options ^= $option;
}
public function enable(int $option){
$this->options |= $option; // enable $option regardless of its original state
}
public function disable(int $option){
$this->options &= ~$option; // disable $option regardless of its original state,
// without affecting other bits
}
/** returns whether at least one of the options is enabled */
public function isOneEnabled(int $options) : bool{
return $this->options & $option !== 0;
// Use !== rather than >, because
// if $options is about a high bit, we may be handling a negative integer
}
/** returns whether all of the options are enabled */
public function areAllEnabled(int $options) : bool{
return ($this->options & $options) === $options;
// note the parentheses; beware the operator precedence
}
}
This example (assuming $option
always only contain one bit) uses:
^
operator to conveniently toggle bitmasks.|
operator to set a bit neglecting its original state or other bits~
operator to convert an integer with only one bit set into an integer with only one bit not set&
operator to unset a bit, using these properties of &
:
&=
with a set bit will not do anything ((1 & 1) === 1
, (0 & 1) === 0
), doing &=
with an integer with only one bit not set will only unset that bit, not affecting other bits.&=
with an unset bit will unset that bit ((1 & 0) === 0
, (0 & 0) === 0
)&
operator with another bitmask will filter away all other bits not set in that bitmask.
Bear in mind that these comparison operators: (<
>
<=
>=
==
===
!=
!==
<>
<=>
) have higher precedence than these bitmask-bitmask operators: (|
^
&
). As bitwise results are often compared using these comparison operators, this is a common pitfall to be aware of.
Bitwise left shift <<
: shift all bits to the left (more significant) by the given number of steps and discard the bits exceeding the int size
<< $x
is equivalent to unsetting the highest $x
bits and multiplying by the $x
th power of 2
printf("%'08b", 0b00001011<< 2); // 00101100
assert(PHP_INT_SIZE === 4); // a 32-bit system
printf("%x, %x", 0x5FFFFFFF << 2, 0x1FFFFFFF << 4); // 7FFFFFFC, FFFFFFFF
Bitwise right shift >>
: discard the lowest shift and shift the remaining bits to the right (less significant)
>> $x
is equivalent to dividing by the $x
th power of 2 and discard the non-integer part
printf("%x", 0xFFFFFFFF >> 3); // 1FFFFFFF
Fast division by 16 (better performance than /= 16
)
$x >>= 4;
On 32-bit systems, this discards all bits in the integer, setting the value to 0. On 64-bit systems, this unsets the most significant 32 bits and keep the least
$x = $x << 32 >> 32;
significant 32 bits, equivalent to $x & 0xFFFFFFFF
Note: In this example, printf("%'06b")
is used. It outputs the value in 6 binary digits.