Skip to content

Commit ae6cb06

Browse files
authored
Prepare v2.1.0 release (#15)
* Update testing tools * Update workflow for parallel tests * Add named enum flags and split tests * Add enum mask conversion guide * Document enum conversion example * Run pint * Add phpstan templates for enum flags and mask typing * Add additional coverage tests for enum and mask edge cases * Fix docblock and reducer order * Fix enum flag docblock and reducer * Correct flag traits docblocks
1 parent abd28bb commit ae6cb06

38 files changed

Lines changed: 1749 additions & 494 deletions

.github/workflows/test.yml

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,76 @@ run-name: test
33
on:
44
workflow_dispatch:
55
push:
6-
branches: master
6+
branches:
7+
- master
8+
- main
79
pull_request:
8-
branches: master
10+
branches:
11+
- master
12+
- main
13+
concurrency:
14+
group: ${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
16+
permissions:
17+
contents: read
918
jobs:
1019
test:
1120
runs-on: ubuntu-latest
21+
timeout-minutes: 15
1222
strategy:
23+
fail-fast: false
1324
matrix:
14-
php-versions:
15-
- 8.5
16-
- 8.4
17-
- 8.3
25+
php-version:
1826
- 8.2
19-
- 8.1
27+
- 8.3
28+
- 8.4
29+
- 8.5
2030

21-
name: PHP ${{ matrix.php-versions }}
31+
name: Test PHP ${{ matrix.php-version }}
2232
steps:
2333
- name: Checkout code
24-
uses: actions/checkout@v3
34+
uses: actions/checkout@v4
2535

2636
- name: Setup PHP
2737
uses: shivammathur/setup-php@v2
2838
with:
29-
php-version: ${{ matrix.php-versions }}
39+
php-version: ${{ matrix.php-version }}
3040
extensions: json
3141
coverage: none
42+
tools: composer:v2
43+
cache: composer
3244

3345
- name: Install dependencies
34-
run: composer install
46+
run: composer install --prefer-dist --no-interaction --no-progress
3547

3648
- name: Run tests
37-
run: vendor/bin/phpunit
49+
run: vendor/bin/pest --parallel --colors=never
50+
3851
- name: Run static analysis
39-
run: vendor/bin/phpstan
52+
run: vendor/bin/phpstan analyse --no-progress --configuration=phpstan.neon.dist
53+
54+
quality:
55+
runs-on: ubuntu-latest
56+
timeout-minutes: 15
57+
name: Code Quality
58+
steps:
59+
- name: Checkout code
60+
uses: actions/checkout@v4
61+
62+
- name: Setup PHP
63+
uses: shivammathur/setup-php@v2
64+
with:
65+
php-version: 8.5
66+
extensions: json
67+
coverage: none
68+
tools: composer:v2
69+
cache: composer
70+
71+
- name: Install dependencies
72+
run: composer install --prefer-dist --no-interaction --no-progress
73+
74+
- name: Run Pint
75+
run: vendor/bin/pint --test -v
76+
77+
- name: Run Rector (dry run)
78+
run: vendor/bin/rector process --dry-run --no-progress-bar

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/.idea/
22
/.phpunit.result.cache
3+
/.phpunit.cache
34
/cache.properties
45
/vendor/
56
composer.lock

README.md

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,44 @@
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-bits 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 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.
88

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

11+
### Trait naming
12+
For new code, prefer `Reinder83\BinaryFlags\Traits\InteractsWithNumericFlags`.
13+
`Reinder83\BinaryFlags\Traits\BinaryFlags` remains available for backward compatibility.
14+
For enum-based usage, use `Reinder83\BinaryFlags\BinaryEnumFlags` (which uses `Traits\InteractsWithEnumFlags`).
15+
1116

1217
## Installing
1318
To install this package simply run the following command in the root of your project.
1419
```
1520
composer require reinder83/binary-flags
1621
```
1722

23+
## Deprecation Notice (Upcoming v3.0.0 Breaking Change)
24+
Starting in `v2.1.0`, passing `float` values as masks or flags is deprecated.
25+
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.
29+
30+
### 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.
32+
33+
Using integer-compatible bits (`BIT_1` through `BIT_63`) prevents these issues and is the supported path for `v3.0.0`.
34+
35+
To prepare for `v3.0.0`, cast incoming values before using the API:
36+
37+
```php
38+
$flags->setMask((int) $maskFromLegacySource);
39+
$flags->addFlag((int) $incomingFlag);
40+
```
41+
42+
See [UPGRADE-v3.md](UPGRADE-v3.md) for migration details.
43+
1844
## Methods
1945
The following methods can be used:
2046

@@ -25,6 +51,14 @@ This can be passed as first argument in the constructor.
2551
##### getMask(): int
2652
Retrieve the current mask.
2753

54+
When using `BinaryEnumFlags`, `getMask()` returns a `Mask` object instead.
55+
Use `getMaskValue(): int` on enum-based flags if you need the numeric mask.
56+
57+
##### getMaskValue(): int
58+
_Since: v2.1.0_ \
59+
Returns the numeric mask value for storage/interoperability.
60+
This method is only available on enum-backed flags (`BinaryEnumFlags`).
61+
2862
##### setOnModifyCallback(callable $onModify)
2963
Set a callback function which is called when the mask changes.
3064
This can be passed as second argument in the constructor.
@@ -47,7 +81,7 @@ When you want to match any of the given flags set `$checkAll` to `false`.
4781

4882
##### checkAnyFlag(int $mask): bool
4983
_Since: v1.0.1_ \
50-
For you convenient I've added an alias to checkFlag with `$checkAll` set to `false`.
84+
For your convenience I've added an alias to checkFlag with `$checkAll` set to `false`.
5185

5286
##### count(): int
5387
_Since: v1.2.0_ \
@@ -77,7 +111,7 @@ You can treat a BinaryFlags object as an iterable, where each iteration will ret
77111

78112
## Example usage
79113

80-
Below some example usage code
114+
Below is some example usage code
81115

82116
##### Create classes
83117
```php
@@ -137,6 +171,78 @@ var_export($exampleFlags->checkAnyFlag(ExampleFlags::FOO | ExampleFlags::BAZ));
137171

138172
```
139173

174+
##### Enum usage (optional)
175+
```php
176+
use Reinder83\BinaryFlags\BinaryEnumFlags;
177+
use Reinder83\BinaryFlags\Mask;
178+
179+
enum Permission: int
180+
{
181+
case CanView = Bits::BIT_1;
182+
case CanBook = Bits::BIT_2;
183+
case CanCancel = Bits::BIT_3;
184+
}
185+
186+
class PermissionFlags extends BinaryEnumFlags
187+
{
188+
protected static function getFlagEnumClass(): string
189+
{
190+
return Permission::class;
191+
}
192+
}
193+
194+
$flags = new PermissionFlags(Permission::CanView);
195+
$flags->addFlag(Permission::CanBook);
196+
$flags->addFlag(Mask::forEnum(Permission::class, Permission::CanCancel));
197+
198+
var_export($flags->checkFlag(Permission::CanBook));
199+
// true
200+
201+
var_export($flags->getFlagNames());
202+
// 'Can View, Can Book, Can Cancel'
203+
```
204+
205+
##### Migrating from numeric flags to enum flags
206+
```php
207+
// Before (numeric PermissionFlags)
208+
use Reinder83\BinaryFlags\BinaryFlags;
209+
210+
class PermissionFlags extends BinaryFlags
211+
{
212+
public const CAN_VIEW = Bits::BIT_1;
213+
public const CAN_BOOK = Bits::BIT_2;
214+
}
215+
216+
$flags = new PermissionFlags($storedMask);
217+
$flags->addFlag(PermissionFlags::CAN_VIEW | PermissionFlags::CAN_BOOK);
218+
$storedMask = $flags->getMask(); // int
219+
220+
// After (enum PermissionFlags)
221+
use Reinder83\BinaryFlags\BinaryEnumFlags;
222+
use Reinder83\BinaryFlags\Mask;
223+
224+
enum Permission: int
225+
{
226+
case CanView = Bits::BIT_1;
227+
case CanBook = Bits::BIT_2;
228+
}
229+
230+
class PermissionFlags extends BinaryEnumFlags
231+
{
232+
protected static function getFlagEnumClass(): string
233+
{
234+
return Permission::class;
235+
}
236+
}
237+
238+
$flags = new PermissionFlags(Mask::fromInt($storedMask, Permission::class));
239+
$flags->addFlag(Permission::CanView);
240+
$flags->addFlag(Permission::CanBook);
241+
242+
// Save as integer for storage/interop
243+
$storedMask = $flags->getMaskValue();
244+
```
245+
140246
##### Flag names example
141247
_By default, the flag names are based on the constant names_
142248
```php
@@ -228,7 +334,7 @@ class Test extends Model
228334
$test = Test::find(1);
229335

230336
// do binary operations on the flags class as described earlier
231-
$test->flags->checkFlag(ExampleFlag::FOO);
337+
$test->flags->checkFlag(ExampleFlags::FOO);
232338
```
233339

234340

RELEASE_NOTES.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Release Notes - v2.1.0
2+
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.
15+
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`.
20+
21+
## 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.
23+
24+
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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Upgrade Guide for v3.0.0
2+
3+
## Summary
4+
`v3.0.0` removes support for `float` values in masks and flags.
5+
`v3.0.0` also removes `Bits::BIT_64`.
6+
7+
## What Changed
8+
- `v2.x`: `int|float` accepted in mask/flag methods.
9+
- `v3.0.0`: only `int` is accepted.
10+
- `v2.x`: `Bits::BIT_64` exists but is not reliable in real bitwise usage.
11+
- `v3.0.0`: `Bits::BIT_64` is removed. Use `BIT_1` through `BIT_63`.
12+
13+
## How to Migrate
14+
1. Find every call that passes mask/flag values into BinaryFlags methods.
15+
2. Ensure values are cast to `int` before passing them.
16+
3. Ensure database or external sources provide integer-compatible values.
17+
18+
## Example
19+
Before:
20+
```php
21+
$flags->setMask($legacyValue);
22+
$flags->addFlag($legacyFlag);
23+
```
24+
25+
After:
26+
```php
27+
$flags->setMask((int) $legacyValue);
28+
$flags->addFlag((int) $legacyFlag);
29+
```
30+
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`.
33+
34+
## 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+
37+
Staying with integer-compatible bits prevents those runtime issues.

composer.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,21 @@
2828
},
2929
"config": {
3030
"platform": {
31-
"php": "8.1"
31+
"php": "8.2"
32+
},
33+
"allow-plugins": {
34+
"pestphp/pest-plugin": true
3235
}
3336
},
3437
"require": {
35-
"php": "^8.1",
38+
"php": "^8.2",
3639
"ext-json": "*"
3740
},
3841
"require-dev": {
39-
"phpunit/phpunit": "^9.5",
40-
"phpstan/phpstan": "^1.10"
42+
"phpstan/phpstan": "^1.10",
43+
"pestphp/pest": "^2.36",
44+
"laravel/pint": "^1",
45+
"rector/rector": "^1"
4146
},
4247
"autoload-dev": {
4348
"psr-4": {

0 commit comments

Comments
 (0)