I’ve been working on a project lately where I’m rolling my own basic role-based access control (RBAC) system. In experimenting with different ways to handle the role definitions, I realized that it’d be pretty nice to have a basic enum() function or construct, as a quick, convenient way to define a set of constants. There’s this option:
1 2 3 4 5 | <?php define('ADMIN', 0); define('MODERATOR', 1); define('EDITOR', 2); ?> |
but that isn’t exactly succinct. I’d rather have something like this:
1 2 3 4 5 | <?php enum('ADMIN', 'MODERATOR', 'EDITOR'); // ADMIN == 0 (true) // MODERATOR == 1 (true), etc. ?> |
You can kind of mimic the enumerated values with arrays like so:
1 2 3 4 5 6 7 | <?php $roles = array( 'ADMIN' => 0, 'MODERATOR' => 1, 'EDITOR' => 2, ); ?> |
but having to specify those values is just tedious. There’s an easy trick to get around that though:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php $roles = array( 'ADMIN', 'MODERATOR', 'EDITOR', ); $roles = array_flip($roles); /* Array ( [ADMIN] => 0 [EDITOR] => 1 [MODERATOR] => 2 ) */ ?> |
Which is cool, but I want constants, not just array values. Which is cleaner?
if ($role == $roles['ADMIN'])if ($role == ADMIN)
So a bit more digging, and I found that APC can turn an array into a bunch of constants in one fell swoop!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?php /** * Try loading the constants from APC */ if (!apc_load_constants('roles')) { /** * they aren't in APC, so make 'em and define 'em */ $roles = array_flip(array( 'ADMIN', 'EDITOR', 'MODERATOR' )); apc_define_constants('roles', $roles); } /** * Now we've got enumerated constants... * ADMIN == 0 * MODERATOR == 1 * EDITOR == 2 */ ?> |
Obviously you need APC to make this work. One great thing about using apc_load_constants() is that you get a modest speed boost because you don’t have to define() all of your constants every time the script runs. Another benefit is that you can load up your array of roles (or whatever your constants are, e.g., permissions, etc.) from a database or other dynamic source, and convert them into global constants with one function call.
Now what would be even cooler is if you could tell apc_load_constants() to load the constants into a specific namespace, maybe something like:
1 2 3 4 5 | <?php apc_load_constants('roles', true, 'drew'); echo "ADMIN's value: " . drew::ADMIN; ?> |
Though I assume that would require at least PHP 5.3.
Responses to “Enums in PHP”
August 15th, 2008 at 1:19 pm
Man you’re seriously a mind reader. A few days after coming across apc_load_constants() my RBAC system ended up evolving into a very similar kind of OR’d bitfield system you’re describing (roles, task groups, and tasks).
It became unwieldy though; this project doesn’t need quite that amount of flexibility so I scrapped most of it, including the cool APC constant stuff. I’m sure I’ll come back to it down the road, but for now, it makes for decent blog fodder ![]()
Leave a Reply
You must be logged in to post a comment.
August 15th, 2008 at 11:25 am
It sounds like what you need is a sort of access control lists, but maybe not as full blown as is usually done with a large tree structure.
A little while ago my brother made such a system. It was split into three parts: roles, objects, and permissions.
Permissions are defined only as the names you give to something, much like what is in an enum. A permissions are contextually relevant to objects.
A permission object is something like a blog post or a forum thread. Its permissions would be read, write, edit, etc.
Finally, a role is a state that the user is in, eg: guest, member, admin.
The way this is laid out in a database is as such:
there is a many-to-many relationship between users and roles; roles each have a unique permission level that is a power of two (1, 2, 4, 8, …); objects are interesting because they are what contain the permissions in a very non-relational way (a comma-separated list of permissions). This order of permissions is important as each permission is implied to have a value that is a power of two. Finally, the permissions table is all numbers; there is a one-to-one relationship between permissions and roles, a one-to-one relationship between permissions and objects, and each permission has a ‘grant’ and ‘deny’ value. These values are just the implied values of the permissions that are in the CSV list in each row of the objects table ORd together.
How does this all end up working? Given a user id, you can find all permissions (from the permissions table) and join on the objects to build up a multidimensional array of bitfields. You can then perform queries of sorts on those to find out if a user can do a certain operation or not.
If you would like further info, feel free to email me. You obviously have my email address stored in your WP db.