diff --git a/src/EnumBitMask.php b/src/EnumBitMask.php index 46a0bd0..1c5ae61 100644 --- a/src/EnumBitMask.php +++ b/src/EnumBitMask.php @@ -4,6 +4,8 @@ namespace BitMask; +use BackedEnum; +use BitMask\Exception\InvalidEnumException; use BitMask\Exception\NotSingleBitException; use BitMask\Exception\UnknownEnumException; use BitMask\Util\Bits; @@ -19,16 +21,27 @@ final class EnumBitMask implements BitMaskInterface /** * @param class-string $enum + * @throws InvalidEnumException * @throws UnknownEnumException */ public function __construct( private readonly string $enum, int $mask = 0, + bool $isIntBacked = false, ) { if (!is_subclass_of($this->enum, UnitEnum::class)) { throw new UnknownEnumException('EnumBitMask enum must be subclass of UnitEnum'); } foreach ($this->enum::cases() as $index => $case) { + if ($isIntBacked) { + if (!is_subclass_of($this->enum, BackedEnum::class)) { + throw new InvalidEnumException('Enum must be a backed enum'); + } + /** @phpstan-var BackedEnum $case */ + if (!is_int($case->value)) { + throw new InvalidEnumException('Enum must be an int-backed enum with integer values'); + } + } $this->map[$case->name] = Bits::indexToBit($index); } $this->bitmask = new BitMask($mask, count($this->enum::cases()) - 1); @@ -38,7 +51,7 @@ public function __construct( * Create an instance with given flags on * * @param class-string $enum - * @throws UnknownEnumException|NotSingleBitException + * @throws UnknownEnumException|NotSingleBitException|InvalidEnumException */ public static function create(string $enum, UnitEnum ...$bits): self { @@ -50,7 +63,7 @@ public static function create(string $enum, UnitEnum ...$bits): self * * @psalm-suppress MixedMethodCall, MixedArgument * @param class-string $enum - * @throws UnknownEnumException|NotSingleBitException + * @throws UnknownEnumException|NotSingleBitException|InvalidEnumException */ public static function all(string $enum): self { @@ -62,7 +75,7 @@ public static function all(string $enum): self * * @psalm-suppress PossiblyUnusedMethod * @param class-string $enum - * @throws UnknownEnumException|NotSingleBitException + * @throws UnknownEnumException|NotSingleBitException|InvalidEnumException */ public static function none(string $enum): self { @@ -74,7 +87,7 @@ public static function none(string $enum): self * * @psalm-suppress PossiblyUnusedMethod * @param class-string $enum - * @throws UnknownEnumException|NotSingleBitException + * @throws UnknownEnumException|NotSingleBitException|InvalidEnumException */ public static function without(string $enum, UnitEnum ...$bits): self { diff --git a/src/Exception/InvalidEnumException.php b/src/Exception/InvalidEnumException.php new file mode 100644 index 0000000..a777338 --- /dev/null +++ b/src/Exception/InvalidEnumException.php @@ -0,0 +1,9 @@ +get()); } + + public function testIsIntBackedEnumInvalidEnum(): void + { + // UnitEnum + $this->expectException(InvalidEnumException::class); + new EnumBitMask(Permissions::class, 3, isIntBacked: true); + } + + public function testIsIntBackedEnum(): void + { + // backed int + $backedIntEnumBitmask = new EnumBitMask(BackedInt::class, 3, isIntBacked: true); + assertTrue($backedIntEnumBitmask->has(BackedInt::Create, BackedInt::Read)); + assertFalse($backedIntEnumBitmask->has(BackedInt::Update, BackedInt::Delete)); + + // backed string + $this->expectException(InvalidEnumException::class); + $backedStringEnumBitmask = new EnumBitMask(BackedString::class, 3, isIntBacked: true); + assertTrue($backedStringEnumBitmask->has(BackedString::Create, BackedString::Read)); + assertFalse($backedStringEnumBitmask->has(BackedString::Update, BackedString::Delete)); + } }