I have been trying to figure out the best way to use bitmask or bitfields in PHP for a long time now for different areas of my application for different user settings and permissions. The farthest I have come so far is from a class contributed by svens in the Stack Overflow post Bitmask in PHP for settings?. I have slightly modified it below, changing it to use class constants instead of DEFINE and making sure the get method is passed an int only. I also have some sample code to test the class's functionality below.
I am looking for any suggestions/code to improve this class even more so it can be used in my application for settings and in some cases user permissions.
Answered in the comment below by mcrumley
In addition, I have a question about the numbering of my constants. In other classes and code sample for this type it will have things listed in powers of 2. However, it seems to work the same as far as I can tell even if I number my constants 1,2,3,4,5,6 instead of 1, 2, 4, 8, 16, etc. So can someone also clarify if I should change my constants?
Some ideas... I would really like to figure out a way to extend this class so it is easy to use with other classes. Let's say I have a User
class and a Messages
class. Both the User
and Messages
class will extend this class and be able to use the bitmask for their settings/permissions (along with other classes later on). So maybe the current class constants should be changed so they can be passed in or some other option? I really would rather not have to define (define('PERM_READ', 1);) in other parts of the site/script and would like to keep it somewhat encapsulated, but flexible as well; I am open to ideas. I want this to be rock solid and flexible like I said to use with multiple other classes for settings or permissions. Possibly some kind of array should be used? @Svens from my previous question linked above posted a comment with "implement some automagic getters/setters or ArrayAccess for extra awesomness. – svens" What do you think about something like that as well?
Include example source code if possible, please.
<?php
class BitField {
const PERM_READ = 0;
const PERM_WRITE = 1;
const PERM_ADMIN = 2;
const PERM_ADMIN2 = 3;
const PERM_ADMIN3 = 4;
private $value;
public function __construct($value=0) {
$this->value = $value;
}
public function getValue() {
return $this->value;
}
public function get($n) {
if (is_int($n)) {
return ($this->value & (1 << $n)) != 0;
}else{
return 0;
}
}
public function set($n, $new=true) {
$this->value = ($this->value & ~(1 << $n)) | ($new << $n);
}
public function clear($n) {
$this->set($n, false);
}
}
?>
Example Usage...
<?php
$user_permissions = 0; //This value will come from MySQL or Sessions
$bf = new BitField($user_permissions);
// Turn these permission to on/true
$bf->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
// Get the total bit value
$user_permissions = $bf->getValue();
echo '<br> Bitmask value = ' .$user_permissions. '<br>Test values on/off based off the bitmask value<br>' ;
// Check if permission PERM_READ is on/true
if ($bf->get($bf::PERM_READ)) {
// can read
echo 'can read is ON<br>';
}
if ($bf->get($bf::PERM_WRITE)) {
// can write
echo 'can write is ON<br>';
}
if ($bf->get($bf::PERM_ADMIN)) {
// is admin
echo 'admin is ON<br>';
}
if ($bf->get($bf::PERM_ADMIN2)) {
// is admin 2
echo 'admin 2 is ON<br>';
}
if ($bf->get($bf::PERM_ADMIN3)) {
// is admin 3
echo 'admin 3 is ON<br>';
}
?>
1 << $n
raises 2 to the $nth power using bit shifting. 1<<0 == 1, 1<<1 == 2, 1<<2 == 4, 1<<3 == 8, etc. – Misadventure