Zend_Bitfield Proposal Review
| Work in Progress This proposal has entered incubation. Feedback, ideas, use cases, implementation suggestions, and design input are welcomed. If you see a potential problem, please don't wait until this component is released before responding. You may enter comments directly at the end of the document, or for more directed comments you may create a new issue in the issue tracker for this project. For developer notes and discussions of design and implementation issues, please see: http://framework.zend.com/wiki/display/ZFDEV/Zend_Bitfield |
Zend Framework: Zend_Bitfield Component Proposal
| Proposed Component Name | Zend_Bitfield |
|---|---|
| Developer Notes | http://framework.zend.com/wiki/display/ZFDEV/Zend_Bitfield |
| Proposers | Richard Thomas |
| Revision | 0.1 - 16 June 2006: Initial Proposal. (wiki revision: 22) |
Table of Contents
1. Overview
Zend_Bitfield is a simple bit flag manager that allows easy handling of flags for settings, storage and permissions
2. References
- In progress
3. Component Requirements, Constraints, and Acceptance Criteria
- Provide support for 32bit, GMP and a fake "64bit" system
- Provide both database and direct input for configuration
4. Dependencies on Other Framework Components
- Zend_Exception
5. Theory of Operation
The idea behind Zend_Bitfield is to provide an easy method to create, access and compare bit values.
6. Milestones / Tasks
- In progress
7. Class Index
- Zend_Bitfield_Exception
- Zend_Bitfield_initBitfield
- Zend_Bitfield
- Zend_checkBit
- Zend_createBit
- Zend_getBits
- Zend_loadBits
- Zend_Bitfield_32bit
- Zend_Bitfield_64bit
- Zend_Bitfield_GMP
- Zend_compareBit
8. Use Cases
Setting up your bitfields
9. Class Skeletons
Not clear what exactly is supposed to be loaded and by what key. E.g. in DB case it may be logical to load permissions for given user, but it is not quite clear how these would work - what if the user has different permissions in different contexts? How do you tell what exactly to load?
Also if it not completely clear if the permission levels have to be bitmasks or can be combinations too, e.g.:
read = 1
modify = 2
create = 4
admin = 7
and maximum 32 levels of permissions - if thery are represented as int - can be too little.
Constants are normally defined during creation of the class and are fixed across all uses of that class unless edited, By using an array the key names and values are controller by the developer.
I do agree there should be some sort of group flags, the 32 value limit can be gotten around by having the option to use gmp functions.
I agree, removing dbload, making ArrayLoad just load and maybe making it possible to define a call_back function this way loading of permissions doesn't happen until its "needed". This way pages that don't need to check permissions don't incur the overhead.
The array loaded should be all possible bitmasks possible so any combination can be checked against.
A way to "extend" the whole 32 levels issue would be to have seperate group and standard bitmask, this would allow 32 seperate masks + an unlimited number of combinations.
Instead of defining admin = 8 you could define admin as a group of masks. Due to the complex nature though groups might need to be handled through a diffrent function set.. This would ensure no overhead for those that only wanted 32 masks but allow the flexibility to have groups only if needed.
Ok most of the above comments have been addressed, would just like to comment on groups.
Groups have been worked in and allow you to reference a "group" of masks using a single key.
In reality, Instead of defining an "admin" key and wasting a bit you could assume anyone with Read, Write, Delete = Admin. The current code leaves that level of detail up to the developer.
The group functions just make handling that easier
Instead of
permCheck('read') AND permCheck('write') AND permCheck('delete') to check for Admin it can just be
groupCheck('Admin')
If a page requires Mod or Edit you can do permCheck('Edit') OR groupCheck('Mod') if your permissions are setup right theres no need to do a permCheck('Admin').
If you want you can also do something like groupCheck('Mod') AND !permCheck('Suspended') something like this would allow you to suspend a users permissions giving them limited access but not destorying there previous permission settings.
So while without using gmp your still limited to 32 bit limits theres no need to waste space defining Admin, or Moderator, you can instead just create a group with all those permissions in there.
Now an Admin with all permissions by default passes ANY perm or group check you do.
In the end though limits and "denied" methods are in the hands of the developer not this class. Its the developers job to ensure a normal user shouldn't have X permissions.
I don't think relying on gmp functions is good - and limit of 32 permissions would be exhausted quite quickly, think about every site part - like news, catalog, etc. - having at least 2 access levels - read & write, so once you have more than 16 different parts on your site, you're out of luck.
What is still unclear for me is the database link - what exactly is supposed to be stored in the DB and what should be loaded?
It doesn't rely on gmp, using gmp is just an extra that is only used if its available and wanted.
As to the 16 page limit thats only if your micromanaging permissions in which case in reality you might need something more robust.
The only way around the 32 bit limit is with gmp or to do multi column permission checks like
$array['read'][bita] = 1
$array['read'][bitb] = 2
then checking for read becomes if (user_bita & bita) AND (user_bitb & bitb)
I can and have done this before, so if people feel a deeper level of base permissions are needed we can do that it just becomes harder to manage and requires 2 fields for the user storage.
I stuck with the simple method to keep it lightweight, maybe a Zend_Perms and a Zend_Perms_64bit or something that way a person can choose there level of need?
The database stuff has been removed, now theres just a Load function which you pass your array of keys and bits.
I think a better way might be:
$permissions = Zend_Permissions();
$permissions->define("read");
$permissions->define("write");
$permissions->define("delete");
$permissions->define("drink from carton");
$permissions->define("security council veto");
$permissions->createGroup("administrator")
->addPermission("read")
->addPermission("write")
->addPermission("delete");
$permissions->createGroup("contributor")
->addPermission("read")
->addPermission("write");
$permissions->createGroup("visitor")
->addPermission("read");
->addPermission("drink from carton");
$permissions->createGroup("president")
->addPermission("read")
->addPermission("write")
->addPermission("delete")
->addPermission("security council veto");
Of course, these could added as an array, or a list, or whatever.
In the class, this would automatically have the effect of:
read = 1
write = 2
delete = 4
drink from carton = 8
security council veto = 16
administrator = 7
contributor = 3
visitor = 9
president = 23
It would just be a matter of bitwise operations:
public function addPermission($permission)
{
$this->flags = $this->flags | $permission;
return $this;
}
public function removePermission($permission)
{
$this->flags = $this->flags & ~$permission;
return $this;
}
public function hasPermission($permission)
{
return ($this->flags & $permission);
}
First you have now gone back to the same 32 field limit, you just have a nice pretty way to get there
.
Second you can't and 2 combined values, if any one bit matches in each side it returns true.
Example
$user = 3 ( read + write );
$admin = 7 ( read + write + delete );
$admin & $user = true when it should be false, the user only has read and write not delete.
You have to check each flag by itself or it won't work. This is the reason I seperated the group functionality from the normal permissions
That's why groups are added separately... you would check group membership with a different function (isMemberOf).
Yea I have that in there, I just forgot to have a way to load the group array directly.. This is now adjusted.
Thanks for the input, the group idea makes it easier to manage a large setup.
Still looking into adding some simple permission/group management functions, Possible in a seperate class since they would only be needed when managing permissions not when actively using them in most cases.
4 more comments by: Richard Thomas, Matthew Ratzloff
Not meaning to throw a ranch in there but would not a solution like LiveUser be more appropriate. It should be possible to implement such a bitwise mechanism.
It does not currently run under E_STRICT but an effort could be made to have PHP5 E_STRICT compliant code.
It does seem to me like reinventing the wheel when options exist.
Disclaimer: I am one of the developers of LiveUser.
ZF Home Page
Code Browser
Wiki Dashboard
Couple items.
Instead of an array for the perm types I suggest that proper constants be used. It should be allowed to combine flags. It should also be noted that the maximum number of flags in a system such is this is approximately 32 (give or take one depending on the storage mechanism).
There should probably also be a mechanism to create groups and load flags from groups. Also to deny a specific flag to a user that might be granted in a group. Eg 3 columns per user perms, groups, denied. Groups being a mask of 32 possible groups (eg user, admin, etc). The combination of which could allow for a read-only admin with a debugging flag by adding (debug | (sumof(admingroup|usergroup)) &^ write
Also, the storage mechanism eg a db should be offloaded from this class. Eg DBLoad should just be Load($whateverdataisneeded) leaving it to the user to define the db methods
$0.02
Kevin