Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
# BinaryFlags
With this class you can easily add flags to your projects.

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.
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.
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.
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.

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

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

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

- Current `v2.x` behavior: floats are still accepted for backward compatibility, but trigger a deprecation warning.
- `v3.0.0` behavior: masks and flags will be `int`-only.
- `v3.0.0` behavior: `Bits::BIT_64` will be removed.
- Passing `float` values to numeric mask/flag methods from `strict_types=1` call sites now throws a `TypeError`.
- 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.
- `Bits::BIT_64` has been removed.
Comment thread
reinder83 marked this conversation as resolved.

### BIT_64 Notice
`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.
`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 (`BIT_1` through `BIT_63`) prevents these issues and is the supported path for `v3.0.0`.
Use `BIT_1` through `BIT_63` for portable numeric flags.
Comment thread
reinder83 marked this conversation as resolved.

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

```php
$flags->setMask((int) $maskFromLegacySource);
Expand Down
70 changes: 10 additions & 60 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,16 @@
# Release Notes - v2.1.0
# Release Notes - v3.0.0

## Added
- New enum-backed API:
- `BinaryEnumFlags`
- `Traits\InteractsWithEnumFlags`
- `Flag` enum and `Mask` value object
- Enum-backed flags now return a `Mask` object from `getMask()`.
- New `getMaskValue(): int` method for enum-backed flags to persist/interoperate with integer masks.
- Deprecation warnings for passing `float` values as masks/flags.
- README migration notice for the upcoming `v3.0.0` integer-only API.
- `UPGRADE-v3.md` with migration instructions.
- New primary numeric trait: `Traits\InteractsWithNumericFlags`.
- `Traits\BinaryFlags` is now deprecated and kept for backward compatibility.
## 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.

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

## BIT_64 Notice
`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.
`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.

## Migration Recommendation
Cast external/legacy mask and flag values to `int` before calling BinaryFlags methods.

```php
$flags->setMask((int) $mask);
$flags->addFlag((int) $flag);
```

## Enum Migration Example
```php
// Before: numeric PermissionFlags
class PermissionFlags extends BinaryFlags
{
public const CAN_VIEW = Bits::BIT_1;
public const CAN_BOOK = Bits::BIT_2;
}

$flags = new PermissionFlags($storedMask);
$flags->addFlag(PermissionFlags::CAN_VIEW | PermissionFlags::CAN_BOOK);
$storedMask = $flags->getMask();

// After: enum-backed PermissionFlags
enum Permission: int
{
case CanView = Bits::BIT_1;
case CanBook = Bits::BIT_2;
}

class PermissionFlags extends BinaryEnumFlags
{
protected static function getFlagEnumClass(): string
{
return Permission::class;
}
}

$flags = new PermissionFlags(Mask::fromInt($storedMask, Permission::class));
$flags->addFlag(Permission::CanView);
$flags->addFlag(Permission::CanBook);
$storedMask = $flags->getMaskValue();
```
9 changes: 5 additions & 4 deletions UPGRADE-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

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

## Example
Expand All @@ -28,10 +28,11 @@ $flags->setMask((int) $legacyValue);
$flags->addFlag((int) $legacyFlag);
```

## v2.1+ Deprecation Signal
Starting in `v2.1.0`, float inputs trigger deprecation warnings to help detect call sites before moving to `v3.0.0`.
## Runtime Impact
Code paths that still pass `float` values into numeric flag APIs now fail with `TypeError` when called from `strict_types=1` code.
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.

## Why BIT_64 Is Being Removed
`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.
`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.

Staying with integer-compatible bits prevents those runtime issues.
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@
"ext-json": "*"
},
"require-dev": {
"phpstan/phpstan": "^1.10",
"pestphp/pest": "^2.36",
"phpstan/phpstan": "^2.1",
"pestphp/pest": "^3.8",
"laravel/pint": "^1",
"rector/rector": "^1"
"rector/rector": "^2.4"
},
"autoload-dev": {
"psr-4": {
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ parameters:
- src
ignoreErrors:
- message: '#Dead catch - ReflectionException is never thrown in the try block#'
- message: '#Trait Reinder83\\BinaryFlags\\Traits\\BinaryFlags is used zero times and is not analysed#'
2 changes: 2 additions & 0 deletions src/BinaryEnumFlags.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Reinder83\BinaryFlags;

use BackedEnum;
Expand Down
12 changes: 7 additions & 5 deletions src/BinaryFlags.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Reinder83\BinaryFlags;

use Closure;
Expand All @@ -12,7 +14,7 @@
*
* @author Reinder
*
* @implements Iterator<int|float, string>
* @implements Iterator<int, string>
*/
abstract class BinaryFlags implements Countable, Iterator, JsonSerializable
{
Expand All @@ -23,7 +25,7 @@ abstract class BinaryFlags implements Countable, Iterator, JsonSerializable
/**
* Initiate class
*/
public function __construct(int|float $mask = 0, ?Closure $onModify = null)
public function __construct(int $mask = 0, ?Closure $onModify = null)
{
$this->setMask($mask);
Comment thread
reinder83 marked this conversation as resolved.

Expand Down Expand Up @@ -64,11 +66,11 @@ public function next(): void
/**
* Return the key of the current element
*
* @return int|float the flag
* @return int the flag
*
* @since 1.2.0
*/
public function key(): int|float
public function key(): int
{
return $this->currentPos;
}
Expand Down Expand Up @@ -132,7 +134,7 @@ public function count(): int
/**
* Specify data which should be serialized to JSON
*
* @return array{mask: int|float} data which can be serialized by <b>json_encode</b>,
* @return array{mask: int} data which can be serialized by <b>json_encode</b>,
*
* @since 1.2.0
*/
Expand Down
5 changes: 2 additions & 3 deletions src/Bits.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Reinder83\BinaryFlags;

/**
Expand Down Expand Up @@ -70,7 +72,4 @@ class Bits
public const BIT_61 = 0x1000000000000000; // 0001000000000000000000000000000000000000000000000000000000000000
public const BIT_62 = 0x2000000000000000; // 0010000000000000000000000000000000000000000000000000000000000000
public const BIT_63 = 0x4000000000000000; // 0100000000000000000000000000000000000000000000000000000000000000

/** @deprecated BIT_64 will be dropped in v3.0.0 */
public const BIT_64 = 0x8000000000000000; // 1000000000000000000000000000000000000000000000000000000000000000
}
2 changes: 2 additions & 0 deletions src/Flag.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Reinder83\BinaryFlags;

/**
Expand Down
2 changes: 2 additions & 0 deletions src/Mask.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Reinder83\BinaryFlags;

use BackedEnum;
Expand Down
2 changes: 2 additions & 0 deletions src/Traits/BinaryFlags.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Reinder83\BinaryFlags\Traits;

/**
Expand Down
2 changes: 2 additions & 0 deletions src/Traits/InteractsWithEnumFlags.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Reinder83\BinaryFlags\Traits;

use BackedEnum;
Expand Down
Loading
Loading