-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
11 changed files
with
247 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
includes: | ||
- vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon | ||
parameters: | ||
level: 9 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
parameters: | ||
level: 9 | ||
excludePaths: | ||
analyseAndScan: | ||
- src/EnumBitMask.php | ||
- tests/EnumBitMaskTest.php |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php // phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BitMask; | ||
|
||
use BitMask\Exception\UnknownEnumException; | ||
use BitMask\Exception\UnsupportedPhpVersionException; | ||
use UnitEnum; | ||
|
||
use const PHP_VERSION_ID; | ||
|
||
PHP_VERSION_ID >= 80100 || throw new UnsupportedPhpVersionException('Requires PHP 8.1 interface UnitEnum'); | ||
class EnumBitMask | ||
{ | ||
private int $bitmask = 0; | ||
/** @var UnitEnum[] $keys */ | ||
private array $keys = []; | ||
|
||
/** | ||
* @param class-string $maskEnum | ||
* @throws UnknownEnumException | ||
*/ | ||
public function __construct( | ||
private readonly string $maskEnum, | ||
UnitEnum ...$bits, | ||
) { | ||
if (!is_subclass_of($this->maskEnum, UnitEnum::class)) { | ||
throw new UnknownEnumException('BitMask enum must be instance of UnitEnum'); | ||
} | ||
$this->keys = $this->maskEnum::cases(); | ||
$this->set(...$bits); | ||
} | ||
|
||
public function get(): int | ||
{ | ||
return $this->bitmask; | ||
} | ||
|
||
/** @throws UnknownEnumException */ | ||
public function set(UnitEnum ...$bits): void | ||
{ | ||
foreach ($bits as $bit) { | ||
if (!$this->isSet($bit)) { | ||
$this->bitmask += 1 << intval(array_search($bit, $this->keys)); | ||
} | ||
} | ||
} | ||
|
||
/** @throws UnknownEnumException */ | ||
public function unset(UnitEnum ...$bits): void | ||
{ | ||
foreach ($bits as $bit) { | ||
if ($this->isSet($bit)) { | ||
$this->bitmask -= 1 << intval(array_search($bit, $this->keys)); | ||
} | ||
} | ||
} | ||
|
||
/** @throws UnknownEnumException */ | ||
public function isSet(UnitEnum ...$bits): bool | ||
{ | ||
foreach ($bits as $bit) { | ||
$this->checkEnumCase($bit); | ||
$mask = 1 << intval(array_search($bit, $this->keys)); | ||
if (($this->bitmask & $mask) !== $mask) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
/** @throws UnknownEnumException */ | ||
private function checkEnumCase(UnitEnum $case): void | ||
{ | ||
$case instanceof $this->maskEnum || | ||
throw new UnknownEnumException(sprintf('Expected %s enum case, %s provided', $this->maskEnum, $case::class)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BitMask\Exception; | ||
|
||
use Exception; | ||
|
||
final class UnknownEnumException extends Exception | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BitMask\Exception; | ||
|
||
use Exception; | ||
|
||
final class UnsupportedPhpVersionException extends Exception | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BitMask\Tests; | ||
|
||
use BitMask\EnumBitMask; | ||
use BitMask\Exception\UnknownEnumException; | ||
use BitMask\Tests\fixtures\Enum\Permissions; | ||
use BitMask\Tests\fixtures\Enum\Unknown; | ||
use PHPUnit\Framework\TestCase; | ||
use function PHPUnit\Framework\assertFalse; | ||
use function PHPUnit\Framework\assertSame; | ||
use function PHPUnit\Framework\assertTrue; | ||
use const PHP_VERSION_ID; | ||
|
||
final class EnumBitMaskTest extends TestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
if (PHP_VERSION_ID < 80100) { | ||
$this->markTestSkipped('PHP ^8.1 only'); | ||
} | ||
} | ||
|
||
public function testNotAnEnum(): void | ||
{ | ||
$this->expectException(UnknownEnumException::class); | ||
new EnumBitMask(self::class); | ||
} | ||
|
||
public function testUnknownEnum(): void | ||
{ | ||
$this->expectException(UnknownEnumException::class); | ||
new EnumBitMask(Permissions::class, Unknown::Case); | ||
} | ||
|
||
public function testGet(): void | ||
{ | ||
$enumBitmask = new EnumBitMask(Permissions::class, Permissions::Create, Permissions::Read); | ||
assertSame(3, $enumBitmask->get()); | ||
$this->expectException(UnknownEnumException::class); | ||
$enumBitmask->isSet(Unknown::Case); | ||
} | ||
|
||
public function testIsSet(): void | ||
{ | ||
$enumBitmask = new EnumBitMask(Permissions::class, Permissions::Create, Permissions::Read); | ||
assertTrue($enumBitmask->isSet(Permissions::Create)); | ||
assertTrue($enumBitmask->isSet(Permissions::Read)); | ||
assertFalse($enumBitmask->isSet(Permissions::Update)); | ||
assertFalse($enumBitmask->isSet(Permissions::Delete)); | ||
$this->expectException(UnknownEnumException::class); | ||
$enumBitmask->isSet(Unknown::Case); | ||
} | ||
|
||
public function testSetUnset(): void | ||
{ | ||
$enumBitmask = new EnumBitMask(Permissions::class, Permissions::Create, Permissions::Read); | ||
$enumBitmask->unset(Permissions::Create, Permissions::Read); | ||
assertFalse($enumBitmask->isSet(Permissions::Create)); | ||
assertFalse($enumBitmask->isSet(Permissions::Read)); | ||
assertFalse($enumBitmask->isSet(Permissions::Read, Permissions::Update)); | ||
assertSame(0, $enumBitmask->get()); | ||
$enumBitmask->set(Permissions::Update, Permissions::Delete); | ||
assertTrue($enumBitmask->isSet(Permissions::Update)); | ||
assertTrue($enumBitmask->isSet(Permissions::Delete)); | ||
assertTrue($enumBitmask->isSet(Permissions::Update, Permissions::Delete)); | ||
assertSame(12, $enumBitmask->get()); | ||
$this->expectException(UnknownEnumException::class); | ||
$enumBitmask->unset(Unknown::Case); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
namespace BitMask\Tests\fixtures\Enum; | ||
|
||
enum Permissions | ||
{ | ||
case Create; | ||
case Read; | ||
case Update; | ||
case Delete; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
namespace BitMask\Tests\fixtures\Enum; | ||
|
||
enum Unknown | ||
{ | ||
case Case; | ||
} |