Search code examples
phpcomparisonbitwise-operatorsflagsbitflags

Checking for multiple bitflags - binary comparison in [PHP]


Checking for multiple flags. I just want to know if there is another way to check for multiple flags, different from &&?

class DemoClass
{

    public const MY_FLAG1 = 0b1;   // 0001 // 1
    public const MY_FLAG2 = 0b10;  // 0010 // 2
    public const MY_FLAG3 = 0b100; // 0100 // 4

    public $flags;

    public function __construct($flags = DemoClass::MY_FLAG1)
    {
        $this->flags = $flags;

        if ($this->flags & DemoClass::MY_FLAG1 && $this->flags & DemoClass::MY_FLAG2) {
            throw new Exception('Flags: MY_FLAG1 and MY_FLAG2 were set');
        }
    }
}

MY_FLAG1 | MY_FLAG2 | MY_FLAG3 = 0b111 // 0111 // 7

Flags passed to constructor MY_FLAG1 | MY_FLAG2 | MY_FLAG3 (0111).
Check if flags: MY_FLAG1 | MY_FLAG2 are set (0011).

Example :

new DemoClass(DemoClass::MY_FLAG1 | DemoClass::MY_FLAG2 | DemoClass::MY_FLAG3);

Solution

  • The result of $input & $mask is a value with bits set only if they are in both $input and $mask.

    If $mask has only one bit set, then the result will either be all zeros, or will have one bit set. This is what you are testing at the moment - all zeros is equivalent to false in the context of an if statement or boolean expression.

    But think about which bit is set if it matches - the same bit that is set in $mask, and only that bit. So the result of a successful match will be equal to $mask.

    If both $input and $mask have more than one bit set, checking for zero only proves that at least one bit in $mask is also in $input. But the only way for it to match $mask is if all the bits set in $mask are also set in $input.

    So if you construct a mask with all the bits you want set, you can test ($input & $mask) === $mask

    In your example:

    $mask = DemoClass::MY_FLAG1 | DemoClass::MY_FLAG2;
    if (($this->flags & $mask) === $mask) {
       throw new Exception('Flags: MY_FLAG1 and MY_FLAG2 were set');
    }