Skip to content

Commit

Permalink
Make BinaryFlags iterable, countable, and jsonSerializable. (#7)
Browse files Browse the repository at this point in the history
v1.2.0

* Make BinaryFlags iterable, countable, and jsonSerializable.
  • Loading branch information
remenic authored and reinder83 committed Jul 30, 2019
1 parent 423f3fc commit 033cc31
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 4 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ When you want to match any of the given flags set `$checkAll` to `false`.
_Since: v1.0.1_ \
For you convenient I've added an alias to checkFlag with `$checkAll` set to `false`.

##### count(): int
_Since: v1.2.0_ \
Returns the number of flags that have been set.

##### jsonSerialize(): mixed
_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}'.

## Static Methods
The following static methods can be used:
Expand All @@ -65,6 +73,9 @@ which will be used by the `getFlagNames` method.
_Since: v1.1.0_ \
Return mask of all the flags together

## Iteration
_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).

## Example usage

Expand Down
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@
"psr-4": {
"Reinder83\\BinaryFlags\\Tests\\": "tests/"
}
},
"require": {
"ext-json": "*"
}
}
6 changes: 5 additions & 1 deletion src/BinaryFlags.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

namespace Reinder83\BinaryFlags;

use Countable;
use Iterator;
use JsonSerializable;

/**
* This class holds useful methods for checking, adding or removing binary flags
*
* @author Reinder
*/
abstract class BinaryFlags
abstract class BinaryFlags implements Iterator, Countable, JsonSerializable
{
use Traits\BinaryFlags;

Expand Down
120 changes: 119 additions & 1 deletion src/Traits/BinaryFlags.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,23 @@ trait BinaryFlags
{
/**
* This will hold the mask for checking against
*
* @var int
*/
protected $mask;

/**
* This will be called on changes
*
* @var callable
*/
protected $onModifyCallback;

/**
* @var int
*/
private $currentPos = 0;

/**
* Return an array with all flags as key with a name as description
*
Expand Down Expand Up @@ -73,6 +80,7 @@ function ($flag, $carry) {
*
* @param int [$mask = null]
* @param bool [$asArray = false]
*
* @return string|array
*/
public function getFlagNames($mask = null, $asArray = false)
Expand Down Expand Up @@ -113,6 +121,7 @@ protected function onModify()
* This method will set the mask where will be checked against
*
* @param int $mask
*
* @return BinaryFlags
*/
public function setMask($mask)
Expand Down Expand Up @@ -141,6 +150,7 @@ public function getMask()
* This will set flag(s) in the current mask
*
* @param int $flag
*
* @return BinaryFlags
*/
public function addFlag($flag)
Expand All @@ -151,13 +161,15 @@ public function addFlag($flag)
if ($before !== $this->mask) {
$this->onModify();
}

return $this;
}

/**
* This will remove a flag(s) (if it's set) in the current mask
*
* @param int $flag
*
* @return BinaryFlags
*/
public function removeFlag($flag)
Expand All @@ -168,6 +180,7 @@ public function removeFlag($flag)
if ($before !== $this->mask) {
$this->onModify();
}

return $this;
}

Expand All @@ -176,24 +189,129 @@ public function removeFlag($flag)
* 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
*
* @param int $flag
* @param int $flag
* @param bool $checkAll
*
* @return bool
*/
public function checkFlag($flag, $checkAll = true)
{
$result = $this->mask & $flag;

return $checkAll ? $result == $flag : $result > 0;
}

/**
* Check if any given flag(s) are set in the current mask
*
* @param int $mask
*
* @return bool
*/
public function checkAnyFlag($mask)
{
return $this->checkFlag($mask, false);
}

/**
* Return the current element
*
* @return string the description of the flag or the name of the constant
* @since 1.2.0
*/
public function current()
{
return $this->getFlagNames($this->currentPos);
}

/**
* Move forward to next element
*
* @return void
* @since 1.2.0
*/
public function next()
{
$this->currentPos <<= 1; // shift to next bit
while (($this->mask & $this->currentPos) == 0 && $this->currentPos > 0) {
$this->currentPos <<= 1;
}
}

/**
* Return the key of the current element
*
* @return int the flag
* @since 1.2.0
*/
public function key()
{
return $this->currentPos;
}

/**
* Checks if current position is valid
*
* @return boolean Returns true on success or false on failure.
* @since 1.2.0
*/
public function valid()
{
return $this->currentPos > 0;
}

/**
* Rewind the Iterator to the first element
*
* @return void
* @since 1.2.0
*/
public function rewind()
{
// find the first element
if ($this->mask === 0) {
$this->currentPos = 0;

return;
}

$this->currentPos = 1;
while (($this->mask & $this->currentPos) == 0) {
$this->currentPos <<= 1;
}
}

/**
* Returns the number of flags that are set
*
* @return int
*
* The return value is cast to an integer.
* @since 1.2.0
*/
public function count()
{
$count = 0;
$mask = $this->mask;

while ($mask != 0) {
if (($mask & 1) == 1) {
$count++;
}
$mask >>= 1;
}

return $count;
}

/**
* Specify data which should be serialized to JSON
*
* @return mixed data which can be serialized by <b>json_encode</b>,
* @since 1.2.0
*/
public function jsonSerialize()
{
return ['mask' => $this->mask];
}
}
30 changes: 28 additions & 2 deletions tests/BinaryFlagsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,32 @@ public function testNamedFlagNames()

public function testGetAllFlagsMask()
{
$this->assertEquals(1+2+4+8, ExampleFlags::getAllFlagsMask());
$this->assertEquals(1 + 2 + 4 + 8, ExampleFlags::getAllFlagsMask());
}
}

public function testCountable()
{
$this->assertEquals(2, $this->test->count());
}

public function testIterable()
{
$test = new ExampleFlagsWithNames(ExampleFlags::FOO | ExampleFlags::BAZ);
$expectedFlags = $test->getFlagNames(ExampleFlags::FOO | ExampleFlags::BAZ, true);

$result = [];
foreach ($test as $flag => $description) {
$result[$flag] = $description;
}

$this->assertEquals($expectedFlags, $result);
}

public function testJsonSerializable()
{
$this->assertEquals(
sprintf('{"mask":%d}', $this->test->getMask()),
json_encode($this->test)
);
}
}

0 comments on commit 033cc31

Please sign in to comment.