HiveBrain v1.2.0
Get Started
← Back to all entries
patternphpMinor

PHP Bitmask class

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
phpclassbitmask

Problem

I have been trying to learn more about using bits/bitfields/bitmask or whatever you call them exactly in PHP for different settings and permissions. With the help of others, I have come up with this class. It has come a long way from when I started it but I am looking for any ideas on how to improve it more.

value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
        if (is_int($n)) {
            return ($this->value & (1 value = ($this->value & ~(1 set($n, false);
    }
}

class UserPermissions_BitField extends BitField
{
    const PERM_READ = 0;
    const PERM_WRITE = 1;
    const PERM_ADMIN = 2;
    const PERM_ADMIN2 = 3;
    const PERM_ADMIN3 = 4;
}

class UserPrivacySettings_BitField extends BitField
{
    const PRIVACY_TOTAL = 0;
    const PRIVACY_EMAIL = 1;
    const PRIVACY_NAME = 2;
    const PRIVACY_ADDRESS = 3;
    const PRIVACY_PHONE = 4;
}
?>


Example usage:

set($bf::PERM_READ);
$bf->set($bf::PERM_WRITE);
$bf->set($bf::PERM_ADMIN);
$bf->set($bf::PERM_ADMIN2);
$bf->set($bf::PERM_ADMIN3);

//turn permission PERM_ADMIN2 to off/false
$bf->clear($bf::PERM_ADMIN2); // sets $bf::PERM_ADMIN2 bit to false

// check if permission PERM_READ is on/true
if ($bf->get($bf::PERM_READ)) {
    // can read
    echo 'can read is ON';
}

if ($bf->get($bf::PERM_WRITE)) {
    // can write
    echo 'can write is ON';
}

if ($bf->get($bf::PERM_ADMIN)) {
    // is admin
    echo 'admin is ON';
}

if ($bf->get($bf::PERM_ADMIN2)) {
    // is admin 2
    echo 'admin 2 is ON';
}

if ($bf->get($bf::PERM_ADMIN3)) {
    // is admin 3
    echo 'admin 3 is ON';
}

?>

Solution

You are kind of missing the point of using bitmasks.

You should be making use of simple bit operations.

If each bit represents a privacy settings, then the operations should be:

-
Check if a value is set, using & operator.

($this->value & $n) == $n;

-
Set a value using the | operator (or |=).

$this->value |= $n;

-
Clear a value using the & operator (or &=).

$this->value &= ~$n;

And then your values should be powers of 2. Usually 0 is saved for a null value or invalid value.

class UserPrivacySettings_BitField extends BitField
{
    const PRIVACY_EMAIL = 1;
    const PRIVACY_NAME = 2;
    const PRIVACY_ADDRESS = 4;
    const PRIVACY_PHONE = 8;
    const PRIVACY_ALL = 15;
}


Now the first 4 values correspond to 1 bit. And you can make use of PRIVACY_ALL by using the combination of all the other values.

i.e. In bits

PRIVACY_EMAIL   = 0001
PRIVACY_NAME    = 0010
PRIVACY_ADDRESS = 0100
PRIVACY_PHONE   = 1000
PRIVACY_ALL     = 1111


This way if you set all the values individually, and then check PRIVACY_ALL, that will evaluate to true.

Here's some revised code, with examples at the end.

value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
        return ($this->value & $n) == $n;
    }

    public function set($n) {
        $this->value |= $n;
    }

    public function clear($n) {
        $this->value &= ~$n;
    }
}

class UserPrivacySettings_BitField extends BitField
{
    const PRIVACY_EMAIL = 1;
    const PRIVACY_NAME = 2;
    const PRIVACY_ADDRESS = 4;
    const PRIVACY_PHONE = 8;
    const PRIVACY_ALL = 15;
}

$bf = new UserPrivacySettings_BitField();
echo "Setting PRIVACY_EMAIL";
$bf->set(UserPrivacySettings_BitField::PRIVACY_EMAIL);
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_EMAIL));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_NAME));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_ADDRESS));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_PHONE));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_ALL));
echo "Setting PRIVACY_NAME";
$bf->set(UserPrivacySettings_BitField::PRIVACY_NAME);
echo "Setting PRIVACY_ADDRESS";
$bf->set(UserPrivacySettings_BitField::PRIVACY_ADDRESS);
echo "Setting PRIVACY_PHONE";
$bf->set(UserPrivacySettings_BitField::PRIVACY_PHONE);
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_EMAIL));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_NAME));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_ADDRESS));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_PHONE));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_ALL));


As an example, look at the values for the Error Constants in PHP. http://php.net/manual/en/errorfunc.constants.php

Code Snippets

class UserPrivacySettings_BitField extends BitField
{
    const PRIVACY_EMAIL = 1;
    const PRIVACY_NAME = 2;
    const PRIVACY_ADDRESS = 4;
    const PRIVACY_PHONE = 8;
    const PRIVACY_ALL = 15;
}
PRIVACY_EMAIL   = 0001
PRIVACY_NAME    = 0010
PRIVACY_ADDRESS = 0100
PRIVACY_PHONE   = 1000
PRIVACY_ALL     = 1111
<?php
abstract class BitField {

    private $value;

    public function __construct($value=0) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
        return ($this->value & $n) == $n;
    }

    public function set($n) {
        $this->value |= $n;
    }

    public function clear($n) {
        $this->value &= ~$n;
    }
}

class UserPrivacySettings_BitField extends BitField
{
    const PRIVACY_EMAIL = 1;
    const PRIVACY_NAME = 2;
    const PRIVACY_ADDRESS = 4;
    const PRIVACY_PHONE = 8;
    const PRIVACY_ALL = 15;
}

$bf = new UserPrivacySettings_BitField();
echo "Setting PRIVACY_EMAIL<br/>";
$bf->set(UserPrivacySettings_BitField::PRIVACY_EMAIL);
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_EMAIL));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_NAME));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_ADDRESS));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_PHONE));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_ALL));
echo "Setting PRIVACY_NAME<br/>";
$bf->set(UserPrivacySettings_BitField::PRIVACY_NAME);
echo "Setting PRIVACY_ADDRESS<br/>";
$bf->set(UserPrivacySettings_BitField::PRIVACY_ADDRESS);
echo "Setting PRIVACY_PHONE<br/>";
$bf->set(UserPrivacySettings_BitField::PRIVACY_PHONE);
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_EMAIL));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_NAME));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_ADDRESS));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_PHONE));
var_dump($bf->get(UserPrivacySettings_BitField::PRIVACY_ALL));

Context

StackExchange Code Review Q#1509, answer score: 6

Revisions (0)

No revisions yet.