Skip to content

Commit 68d4405

Browse files
authored
Binary Flags v3 (#16)
# Release Notes - v3.0.0 ## Changed - Numeric `BinaryFlags` APIs now accept `int` values only. - Passing `float` values to numeric mask/flag methods now raises `TypeError` for `strict_types=1` callers. - Non-strict callers should still cast or validate external values before calling the API because PHP scalar coercion can convert `float` to `int` at the call boundary. - The numeric iterator and JSON serialization contracts are now documented as `int`-based. ## Removed - `Bits::BIT_64`. - The v2.x float-normalization/deprecation path in `Traits\InteractsWithNumericFlags`. ## BIT_64 Notice `Bits::BIT_64` was removed because PHP numbers for bitwise flags are signed. The 64th bit is the sign bit, so it cannot be used reliably as a normal flag. Using integer-compatible bits avoids these issues.
1 parent ae6cb06 commit 68d4405

34 files changed

Lines changed: 142 additions & 164 deletions

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# BinaryFlags
44
With this class you can easily add flags to your projects.
55

6-
The number of flags you can use is limited to the architecture of your system, e.g.: 32 flags on a 32-bit system or 64 flags on 64-bit system.
7-
To store 64-bit flags in a database, you will need to store it as UNSIGNED BIGINT in MySQL or an equivalent in your datastore.
6+
The number of usable flags is limited by PHP's signed integer size: 31 flags on a 32-bit system or 63 flags on a 64-bit system.
7+
If you store 64-bit integer masks in a database, use a type that can hold signed 64-bit values, such as `BIGINT` in MySQL or the equivalent in your datastore.
88

99
This package also comes with a trait which you can use to implement binary flags directly in your own class.
1010

@@ -20,19 +20,19 @@ To install this package simply run the following command in the root of your pro
2020
composer require reinder83/binary-flags
2121
```
2222

23-
## Deprecation Notice (Upcoming v3.0.0 Breaking Change)
24-
Starting in `v2.1.0`, passing `float` values as masks or flags is deprecated.
23+
## v3.0.0 Breaking Changes
24+
As of `v3.0.0`, masks and flags are `int`-only.
2525

26-
- Current `v2.x` behavior: floats are still accepted for backward compatibility, but trigger a deprecation warning.
27-
- `v3.0.0` behavior: masks and flags will be `int`-only.
28-
- `v3.0.0` behavior: `Bits::BIT_64` will be removed.
26+
- Passing `float` values to numeric mask/flag methods from `strict_types=1` call sites now throws a `TypeError`.
27+
- For non-strict callers, PHP scalar coercion can still convert `float` to `int` before the method is entered, so validate or cast external values before calling the API.
28+
- `Bits::BIT_64` has been removed.
2929

3030
### BIT_64 Notice
31-
`Bits::BIT_64` is being removed because PHP numbers for bitwise flags are signed. The 64th bit is the sign bit, so it cannot be used reliably as a normal flag.
31+
`Bits::BIT_64` was removed because PHP numbers for bitwise flags are signed. The 64th bit is the sign bit, so it cannot be used reliably as a normal flag.
3232

33-
Using integer-compatible bits (`BIT_1` through `BIT_63`) prevents these issues and is the supported path for `v3.0.0`.
33+
Use `BIT_1` through `BIT_63` for portable numeric flags.
3434

35-
To prepare for `v3.0.0`, cast incoming values before using the API:
35+
If you still receive mask values from loose legacy sources, cast them before using the API:
3636

3737
```php
3838
$flags->setMask((int) $maskFromLegacySource);

RELEASE_NOTES.md

Lines changed: 10 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,16 @@
1-
# Release Notes - v2.1.0
1+
# Release Notes - v3.0.0
22

3-
## Added
4-
- New enum-backed API:
5-
- `BinaryEnumFlags`
6-
- `Traits\InteractsWithEnumFlags`
7-
- `Flag` enum and `Mask` value object
8-
- Enum-backed flags now return a `Mask` object from `getMask()`.
9-
- New `getMaskValue(): int` method for enum-backed flags to persist/interoperate with integer masks.
10-
- Deprecation warnings for passing `float` values as masks/flags.
11-
- README migration notice for the upcoming `v3.0.0` integer-only API.
12-
- `UPGRADE-v3.md` with migration instructions.
13-
- New primary numeric trait: `Traits\InteractsWithNumericFlags`.
14-
- `Traits\BinaryFlags` is now deprecated and kept for backward compatibility.
3+
## Changed
4+
- Numeric `BinaryFlags` APIs now accept `int` values only.
5+
- Passing `float` values to numeric mask/flag methods now raises `TypeError` for `strict_types=1` callers.
6+
- Non-strict callers should still cast or validate external values before calling the API because PHP scalar coercion can convert `float` to `int` at the call boundary.
7+
- The numeric iterator and JSON serialization contracts are now documented as `int`-based.
158

16-
## Deprecated
17-
- Passing `float` to BinaryFlags mask/flag methods is deprecated in `v2.1.0`.
18-
- Float support will be removed in `v3.0.0`.
19-
- `Bits::BIT_64` will be removed in `v3.0.0`.
9+
## Removed
10+
- `Bits::BIT_64`.
11+
- The v2.x float-normalization/deprecation path in `Traits\InteractsWithNumericFlags`.
2012

2113
## BIT_64 Notice
22-
`Bits::BIT_64` is being removed because PHP numbers for bitwise flags are signed. The 64th bit is the sign bit, so it cannot be used reliably as a normal flag.
14+
`Bits::BIT_64` was removed because PHP numbers for bitwise flags are signed. The 64th bit is the sign bit, so it cannot be used reliably as a normal flag.
2315

2416
Using integer-compatible bits avoids these issues.
25-
26-
## Migration Recommendation
27-
Cast external/legacy mask and flag values to `int` before calling BinaryFlags methods.
28-
29-
```php
30-
$flags->setMask((int) $mask);
31-
$flags->addFlag((int) $flag);
32-
```
33-
34-
## Enum Migration Example
35-
```php
36-
// Before: numeric PermissionFlags
37-
class PermissionFlags extends BinaryFlags
38-
{
39-
public const CAN_VIEW = Bits::BIT_1;
40-
public const CAN_BOOK = Bits::BIT_2;
41-
}
42-
43-
$flags = new PermissionFlags($storedMask);
44-
$flags->addFlag(PermissionFlags::CAN_VIEW | PermissionFlags::CAN_BOOK);
45-
$storedMask = $flags->getMask();
46-
47-
// After: enum-backed PermissionFlags
48-
enum Permission: int
49-
{
50-
case CanView = Bits::BIT_1;
51-
case CanBook = Bits::BIT_2;
52-
}
53-
54-
class PermissionFlags extends BinaryEnumFlags
55-
{
56-
protected static function getFlagEnumClass(): string
57-
{
58-
return Permission::class;
59-
}
60-
}
61-
62-
$flags = new PermissionFlags(Mask::fromInt($storedMask, Permission::class));
63-
$flags->addFlag(Permission::CanView);
64-
$flags->addFlag(Permission::CanBook);
65-
$storedMask = $flags->getMaskValue();
66-
```

UPGRADE-v3.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
## How to Migrate
1414
1. Find every call that passes mask/flag values into BinaryFlags methods.
15-
2. Ensure values are cast to `int` before passing them.
15+
2. Ensure values are cast or validated as `int` before passing them.
1616
3. Ensure database or external sources provide integer-compatible values.
1717

1818
## Example
@@ -28,10 +28,11 @@ $flags->setMask((int) $legacyValue);
2828
$flags->addFlag((int) $legacyFlag);
2929
```
3030

31-
## v2.1+ Deprecation Signal
32-
Starting in `v2.1.0`, float inputs trigger deprecation warnings to help detect call sites before moving to `v3.0.0`.
31+
## Runtime Impact
32+
Code paths that still pass `float` values into numeric flag APIs now fail with `TypeError` when called from `strict_types=1` code.
33+
For non-strict callers, PHP scalar coercion can still convert `float` to `int` at the call boundary, so external values should be validated or cast before calling the API.
3334

3435
## Why BIT_64 Is Being Removed
35-
`BIT_64` is being removed because PHP numbers for bitwise flags are signed. The 64th bit is the sign bit, so it cannot be used reliably as a normal flag.
36+
`BIT_64` was removed because PHP numbers for bitwise flags are signed. The 64th bit is the sign bit, so it cannot be used reliably as a normal flag.
3637

3738
Staying with integer-compatible bits prevents those runtime issues.

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@
3939
"ext-json": "*"
4040
},
4141
"require-dev": {
42-
"phpstan/phpstan": "^1.10",
43-
"pestphp/pest": "^2.36",
42+
"phpstan/phpstan": "^2.1",
43+
"pestphp/pest": "^3.8",
4444
"laravel/pint": "^1",
45-
"rector/rector": "^1"
45+
"rector/rector": "^2.4"
4646
},
4747
"autoload-dev": {
4848
"psr-4": {

phpstan.neon.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ parameters:
44
- src
55
ignoreErrors:
66
- message: '#Dead catch - ReflectionException is never thrown in the try block#'
7+
- message: '#Trait Reinder83\\BinaryFlags\\Traits\\BinaryFlags is used zero times and is not analysed#'

src/BinaryEnumFlags.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Reinder83\BinaryFlags;
46

57
use BackedEnum;

src/BinaryFlags.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Reinder83\BinaryFlags;
46

57
use Closure;
@@ -12,7 +14,7 @@
1214
*
1315
* @author Reinder
1416
*
15-
* @implements Iterator<int|float, string>
17+
* @implements Iterator<int, string>
1618
*/
1719
abstract class BinaryFlags implements Countable, Iterator, JsonSerializable
1820
{
@@ -23,7 +25,7 @@ abstract class BinaryFlags implements Countable, Iterator, JsonSerializable
2325
/**
2426
* Initiate class
2527
*/
26-
public function __construct(int|float $mask = 0, ?Closure $onModify = null)
28+
public function __construct(int $mask = 0, ?Closure $onModify = null)
2729
{
2830
$this->setMask($mask);
2931

@@ -64,11 +66,11 @@ public function next(): void
6466
/**
6567
* Return the key of the current element
6668
*
67-
* @return int|float the flag
69+
* @return int the flag
6870
*
6971
* @since 1.2.0
7072
*/
71-
public function key(): int|float
73+
public function key(): int
7274
{
7375
return $this->currentPos;
7476
}
@@ -132,7 +134,7 @@ public function count(): int
132134
/**
133135
* Specify data which should be serialized to JSON
134136
*
135-
* @return array{mask: int|float} data which can be serialized by <b>json_encode</b>,
137+
* @return array{mask: int} data which can be serialized by <b>json_encode</b>,
136138
*
137139
* @since 1.2.0
138140
*/

src/Bits.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Reinder83\BinaryFlags;
46

57
/**
@@ -70,7 +72,4 @@ class Bits
7072
public const BIT_61 = 0x1000000000000000; // 0001000000000000000000000000000000000000000000000000000000000000
7173
public const BIT_62 = 0x2000000000000000; // 0010000000000000000000000000000000000000000000000000000000000000
7274
public const BIT_63 = 0x4000000000000000; // 0100000000000000000000000000000000000000000000000000000000000000
73-
74-
/** @deprecated BIT_64 will be dropped in v3.0.0 */
75-
public const BIT_64 = 0x8000000000000000; // 1000000000000000000000000000000000000000000000000000000000000000
7675
}

src/Flag.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Reinder83\BinaryFlags;
46

57
/**

src/Mask.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Reinder83\BinaryFlags;
46

57
use BackedEnum;

0 commit comments

Comments
 (0)