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
The biggest mistake I see in your class is that you're mixing business logic into a data structure. The purpose of your class is to store multiple boolean values (i.e. true/false) in a single integer. This doesn't have to be done in a class, but it is convenient. And that is its purpose.
I would drop the permission flags in the class and outsource them into your business logic classes.
A data structure is an entity that handles one thing: data. The data is not interpreted in any way. A stack, fore example, is a data structure that you can put stuff into, that will give you the last item first. And here is the point: It doesn't care, what you put in there: integers, User objects, pointers, cars, elephants, it will just handle the storage and retrieval of the data.
Business logic on the other hand is where you define how your data structures interact with each other. This is where permissions are defined, where you state that a person who created a blog post may edit it, and no one else is allowed to.
These are two fundamentally different views of your application and should not be mixed. You can store your permissions in another data structure (as an array of integers, or a hash table of Permission objects, for example - or any other data structure) and you can store other flags in your BitField data structure (like boolean preferences of your users, like "wants to receive newsletter" or "email address was verified").
Another improvement is the usage of hex values for these constants, this will ensure that your 16th value is still readable. (I would rather recommend using bit-shift operators in the constant declarations, which is even more readable, but this is not possible with the current PHP interpreter for performance reasons.)
class Permission {
const READ = 0x0001;
const UPDATE = 0x0002;
const DELETE = 0x0004;
const COMMENT = 0x0008;
const GRANT = 0x0010;
const UNDELETE = 0x0020;
const WHATEVER = 0x0040;
}
$permissions = new BitField();
$permissions->set(Permission::READ);
$permissions->set(Permission::WRITE);
The same class without hexadecimal values is less readable, especially if you add more flags:
class Permission {
const READ = 1;
const UPDATE = 2;
const DELETE = 4;
const COMMENT = 8;
const GRANT = 16;
const UNDELETE = 32;
const WHATEVER = 64;
const PERMISSION8 = 128;
const PERMISSION9 = 256;
const PERMISSION10 = 512;
const PERMISSION11 = 1024;
const PERMISSION12 = 2048;
const PERMISSION13 = 4096;
const PERMISSION14 = 8192;
const PERMISSION15 = 16384;
const PERMISSION16 = 32768; # the 16th value I mentioned above. Would
# you immediately recognize this value as 2^16?
# I wouldn't.
const PERMISSION17 = 65536;
const PERMISSION18 = 131072;
const PERMISSION19 = 262144;
}
I would further define that the parameter to set() must be a single-bit integer, and not a flag number. The set() implementation by demon is what I mean:
$this->value |= $n;