With this class you can easily add flags to your projects.
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.
For new code, prefer Reinder83\BinaryFlags\Traits\InteractsWithNumericFlags.
Reinder83\BinaryFlags\Traits\BinaryFlags remains available for backward compatibility.
For enum-based usage, use Reinder83\BinaryFlags\BinaryEnumFlags (which uses Traits\InteractsWithEnumFlags).
To install this package simply run the following command in the root of your project.
composer require reinder83/binary-flags
As of v3.0.0, masks and flags are int-only.
- Passing
floatvalues to numeric mask/flag methods fromstrict_types=1call sites now throws aTypeError. - For non-strict callers, PHP scalar coercion can still convert
floattointbefore the method is entered, so validate or cast external values before calling the API. Bits::BIT_64has been removed.
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.
Use BIT_1 through BIT_63 for portable numeric flags.
If you still receive mask values from loose legacy sources, cast them before using the API:
$flags->setMask((int) $maskFromLegacySource);
$flags->addFlag((int) $incomingFlag);See UPGRADE-v3.md for migration details.
The following methods can be used:
Overwrite the current mask. This can be passed as first argument in the constructor.
Retrieve the current mask.
When using BinaryEnumFlags, getMask() returns a Mask object instead.
Use getMaskValue(): int on enum-based flags if you need the numeric mask.
Since: v2.1.0
Returns the numeric mask value for storage/interoperability.
This method is only available on enum-backed flags (BinaryEnumFlags).
Set a callback function which is called when the mask changes. This can be passed as second argument in the constructor.
Give the name(s) for the given $mask or the current mask when omitted.
When $asArray is true the method will return an array with the names,
otherwise a comma separated string will be returned (default).
Adds one or multiple flags to the current mask.
Removes one or multiple flags from the current mask.
Check if given flag(s) are set in the current mask.
By default, it will check all bits in the given flag.
When you want to match any of the given flags set $checkAll to false.
Since: v1.0.1
For your convenience I've added an alias to checkFlag with $checkAll set to false.
Since: v1.2.0
Returns the number of flags that have been set.
Since: v1.2.0
Return a value that can be encoded by json_encode() in the form of ["mask" => 7]. You should not have to call this method directly,
instead you can pass the BinaryFlags object to json_encode which will convert it to '{"mask": 7}'.
The following static methods can be used:
Since: v1.1.0
Return all the flags with their names as an array, using their flag mask as key.
This method can also be overloaded to return custom names for the flags,
which will be used by the getFlagNames method.
Since: v1.1.0
Return mask of all the flags together
Since: v1.2.0
You can treat a BinaryFlags object as an iterable, where each iteration will return the next bit value that has been set including its description (or the name of the constant representing the bit value).
Below is some example usage code
// example classes which the following examples will refer touseReinder83\BinaryFlags\BinaryFlags;
useReinder83\BinaryFlags\Bits;
class ExampleFlags extends BinaryFlags
{
constFOO = Bits::BIT_1;
constBAR = Bits::BIT_2;
constBAZ = Bits::BIT_3;
}$exampleFlags = newExampleFlags();
// add BAR flag$exampleFlags->addFlag(ExampleFlags::BAR);
var_export($exampleFlags->checkFlag(ExampleFlags::FOO)); // falsevar_export($exampleFlags->checkFlag(ExampleFlags::BAR)); // true// remove BAR flag$exampleFlags->removeFlag(ExampleFlags::BAR);
var_export($exampleFlags->checkFlag(ExampleFlags::BAR)); // false$exampleFlags = newExampleFlags();
// add FOO and BAR$exampleFlags->addFlag(ExampleFlags::FOO | ExampleFlags::BAR); var_export($exampleFlags->checkFlag(ExampleFlags::FOO)); // truevar_export($exampleFlags->checkFlag(ExampleFlags::FOO | ExampleFlags::BAZ)); // false because BAZ is not setvar_export($exampleFlags->checkFlag(ExampleFlags::FOO | ExampleFlags::BAR)); // true because both flags are setvar_export($exampleFlags->checkFlag(ExampleFlags::FOO | ExampleFlags::BAZ, false)); // true because one of the flags is set (FOO)// alias of the above methodvar_export($exampleFlags->checkAnyFlag(ExampleFlags::FOO | ExampleFlags::BAZ)); // trueuseReinder83\BinaryFlags\BinaryEnumFlags;
useReinder83\BinaryFlags\Mask;
enum Permission: int
{
case CanView = Bits::BIT_1;
case CanBook = Bits::BIT_2;
case CanCancel = Bits::BIT_3;
}
class PermissionFlags extends BinaryEnumFlags
{
protectedstaticfunctiongetFlagEnumClass(): string
{
return Permission::class;
}
}
$flags = newPermissionFlags(Permission::CanView);
$flags->addFlag(Permission::CanBook);
$flags->addFlag(Mask::forEnum(Permission::class, Permission::CanCancel));
var_export($flags->checkFlag(Permission::CanBook));
// truevar_export($flags->getFlagNames());
// 'Can View, Can Book, Can Cancel'// Before (numeric PermissionFlags)useReinder83\BinaryFlags\BinaryFlags;
class PermissionFlags extends BinaryFlags
{
publicconstCAN_VIEW = Bits::BIT_1;
publicconstCAN_BOOK = Bits::BIT_2;
}
$flags = newPermissionFlags($storedMask);
$flags->addFlag(PermissionFlags::CAN_VIEW | PermissionFlags::CAN_BOOK);
$storedMask = $flags->getMask(); // int// After (enum PermissionFlags)useReinder83\BinaryFlags\BinaryEnumFlags;
useReinder83\BinaryFlags\Mask;
enum Permission: int
{
case CanView = Bits::BIT_1;
case CanBook = Bits::BIT_2;
}
class PermissionFlags extends BinaryEnumFlags
{
protectedstaticfunctiongetFlagEnumClass(): string
{
return Permission::class;
}
}
$flags = newPermissionFlags(Mask::fromInt($storedMask, Permission::class));
$flags->addFlag(Permission::CanView);
$flags->addFlag(Permission::CanBook);
// Save as integer for storage/interop$storedMask = $flags->getMaskValue();By default, the flag names are based on the constant names
$exampleFlags = newExampleFlags();
$exampleFlags->addFlag(ExampleFlags::FOO | ExampleFlags::BAR | ExampleFlags::BAZ);
var_export($exampleFlags->getFlagNames());
// 'Foo, Bar, Baz'// null will force current maskvar_export($exampleFlags->getFlagNames(null, true));
/*array ( 0 => 'Foo', 1 => 'Bar', 2 => 'Baz',)*/// get flag names of given maskvar_export($exampleFlags->getFlagNames(ExampleFlags::FOO | ExampleFlags::BAR));
// 'Foo, Bar'If you want custom flag names that are not equal to the constant names, you can override these with getAllFlags()
class ExampleFlagsWithNames extends BinaryFlags
{
constFOO = Bits::BIT_1;
constBAR = Bits::BIT_2;
constBAZ = Bits::BIT_3;
publicstaticfunctiongetAllFlags()
{
return [
static::FOO => 'My foo description',
static::BAR => 'My bar description',
static::BAZ => 'My baz description',
];
}
}
$exampleFlags = newExampleFlagsWithNames();
$exampleFlags->addFlag(ExampleFlags::FOO | ExampleFlags::BAR | ExampleFlags::BAZ);
// null will force current maskvar_export($exampleFlags->getFlagNames(null, true));
/*array ( 0 => 'My foo description', 1 => 'My bar description', 2 => 'My baz description',)*/useIlluminate\Database\Eloquent\Model;
class Test extends Model
{
private$flagsObject;
/** * Retrieve flags * @return ExampleFlags */publicfunctiongetFlagsAttribute()
{
if ($this->flagsObject === null) {
$this->flagsObject = newExampleFlags(
$this->attributes['flags'], // set current flags maskfunction (ExampleFlags$flags) { // set callback function// update the flags in this model$this->setAttribute('flags', $flags->getMask());
}
);
}
return$this->flagsObject;
}
}
// retrieve object from DB$test = Test::find(1);
// do binary operations on the flags class as described earlier$test->flags->checkFlag(ExampleFlags::FOO);For bugs or feature requests feel free to contact me or submit an issue or pull request. Or you can support me by buying me a coffee: