IntMaskComparison
Emitted when a bit set, i.e. a value typed as int-mask<…> or int-mask-of<…>, is compared by value with something other than 0 or the set of all its bits.
<?php
final class Permission
{
public const READ = 1;
public const WRITE = 2;
public const DELETE = 4;
}
/** @param int-mask-of<Permission::*> $permissions */
function canRead(int $permissions): bool
{
return $permissions === Permission::READ;
}
Why this is bad
$permissions === Permission::READ is only true when READ is the only bit set: canRead(Permission::READ | Permission::WRITE) returns false. Comparing a bit set by value is almost always a mistake for a test of some of its bits.
How to fix
Test the bits you need with &:
<?php
final class Permission
{
public const READ = 1;
public const WRITE = 2;
public const DELETE = 4;
}
/** @param int-mask-of<Permission::*> $permissions */
function canRead(int $permissions): bool
{
return ($permissions & Permission::READ) === Permission::READ;
}
($mask & B) === B: every bit ofBis set;($mask & B) !== 0: at least one bit ofBis set;($mask & ~B) === 0: no bit outside ofBis set, i.e. the mask is a subset ofB;$mask === 0: no bit is set;$mask === ALL, whereALLhas every bit of the mask's type: every bit is set.
$mask & B is itself a bit set, of the bits of B, so it may only be compared with 0 or B.
$a & $b may also be compared with $a or $b, as in ($available & $required) === $required.
The check covers ===, !==, ==, !=, <, <=, >, >= and <=> (ordering comparisons only allow 0), match arms, switch cases, and the search value of in_array(), array_search() and array_keys().
When a comparison of the exact set of bits is intended, for instance to tell whether a fixed point was reached, pass the masks to a function taking a plain int that documents that intent.