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 pe
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?
You don't need to, because the code is already taking care of that. This explanation is going to be a bit roundabout.
The reason that bit fields are handled as powers of two is that each power of two is represented by a single bit. These individual bits can be bitwise-ORed together into a single integer that can be passed around. In lower-level languages, it's "easier" to pass around a number than, say, a struct.
Let me demonstrate how this works. Let's set up some permissions using the powers of two:
define('PERM_NONE', 0);
define('PERM_READ', 1);
define('PERM_WRITE', 2);
define('PERM_EDIT', 4);
define('PERM_DELETE', 8);
define('PERM_SUPER', 16);
Let's inspect the bit values of these permissions at the PHP interactive prompt:
php > printf('%08b', PERM_SUPER);
00010000
php > printf('%08b', PERM_DELETE);
00001000
php > printf('%08b', PERM_EDIT);
00000100
php > printf('%08b', PERM_WRITE);
00000010
php > printf('%08b', PERM_READ);
00000001
php > printf('%08b', PERM_NONE);
00000000
Now let's create a user that has READ access and WRITE access.
php > printf('%08b', PERM_READ | PERM_WRITE);
00000011
Or a user that can read, write, delete, but not edit:
php > printf('%08b', PERM_READ | PERM_WRITE | PERM_DELETE);
00001011
We can check permission using bitwise-AND and making sure the result is not zero:
php > $permission = PERM_READ | PERM_WRITE | PERM_DELETE;
php > var_dump($permission & PERM_WRITE); // This won't be zero.
int(2)
php > var_dump($permission & PERM_EDIT); // This will be zero.
int(0)
(It's worth noting that PERM_NONE & PERM_NONE
is 0 & 0
, which is zero. The "none" permission I created doesn't actually work here, and can promptly be forgotten about.)
Your class is doing something slightly different, but the end result is identical. It's using bit shifting to move an "on" bit over to the left X times, where X is the number of the permission. In effect, this is raising 2 to the power of the permission's value. A demonstration:
php > echo BitField::PERM_ADMIN3;
4
php > echo pow(2, BitField::PERM_ADMIN3);
16
php > printf('%08b', pow(2, BitField::PERM_ADMIN3));
00010000
php > echo 1 << BitField::PERM_ADMIN3;
16
php > printf('%08b', 1 << BitField::PERM_ADMIN3);
00010000
While these methods are effectively identical, I'd argue that simple ANDing and ORing is easier to read than the XORing and bit-shifting.
I am looking for any suggestions/code to improve this class even more so it can be used in my app for settings and in some cases user permissions.
I have one suggestion, and one warning.
My suggestion would be making the class abstract and not defining any permissions within it. Instead, build classes that inherit from it and define their own permissions. You don't want to consider sharing the same permission names across unrelated bit fields, and prefixing them with class names is pretty sane. I expect you were going to do this anyway.
My warning is simple but dire: PHP can not reliably represent an integer larger than 31 bits. In fact, it can only represent 63-bit integers when it's compiled on a 64-bit system. This means that, if you are distributing your application to the general public, you will be restricted to no more than 31 permissions if you wish to use the built-in math functions.
The GMP extension includes bitwise operations that can function on arbitrary-length integers.
Another option might be using code from this answer on large integers, which could allow you to represent a huge integer as a string, though doing bitwise operations on that might be ... interesting. (You could down-convert it to base-2, then do a substr check for string "1" or "0" at the expected location, but that's gonna be a huge performance drag.)