Jump to content

usergroup permissions


newb

Recommended Posts

Well, since you're creating your own, you have complete and full control over the groups you create. I would suggest that you not create a fixed number of groups. Instead create a table `groups` and then you can create as many groups as you want to. And the specific groups you create will depend on the needs of your system - not everybody's needs are the same.
Link to comment
Share on other sites

I don't use groups, I specify permissions, then leave it for the administrator to modify a users permissions as they see fit.

Each function of my site will have a permission associated with it, e.g. Posting has it's own, Editing will have it's own, User administration has it's own etc.

I use bitwise comparison of an integer to store the permission and for checking.
Link to comment
Share on other sites

We'll start from the beginning.

Computers are run by electrical current; the current is either there or it isn't.  At the most fundamental levels, computers are only checking for the presence or lack of electrical current.

This leads to the [i]b[/i]inary dig[i]it[/i], or bit.  It is called a binary digit because it can only contain two possible values, 0 or 1.  We call it a bit for short because we're lazy.

Now, humans like to group things, it makes our lives easier.  So we arbitrarily decided that 8 bits is called a byte, and 4 bytes is a word.  (On some platforms a word could be more than 4 bytes, but that's irrelevant).

PHP is different than most programming languages in that it isn't strongly typed, meaning that PHP converts relatively freely from an integer to a string to a floating point number.  Other languages, like C, are very strongly typed.  An integer can only interact with other data types that are a type of a number.

Let's say that you're programming in C and that you happen to know that an integer in C is 1 word, which is the same as 4 bytes, which is the same as 32 bits.

This means that you can think of any integer in C has being a string of length 32 where every place in the string is a 0 or a 1.

For example:
[code]
0000 0000 0000 0000 0000 0000 0000 0000
0110 0010 0101 1111 0101 1000 0000 0001
[/code]

Let's throw a little bit of math in here for fun.  Ignoring negative numbers, how many different numbers can we represent with such a string?

Starting small...

Bit string of length 1
[code]
0
1
[/code]
We can store two values in a string of length 1, notice that this is the same as 2^1

Bit string of length 2
[code]
00
01
10
11
[/code]
We can store four values in a string of length 2, notice that this is the same as 2^2

If we keep going, we find that we can store 2^n possible values in a bit string of length n.  A bit string of length 32 can store 2^32 possible integers, which is why you can store some really large numbers in an integer variable!  (Note to the more advanced reader: Yes, I'm ignoring sign bits.  Don't get hung up on the details.)

Explained in other words, every unique combination of a bit string is mapped to a unique integer.  It's done through a mathematical formula which isn't really important for this discussion.  But, rest assured that the following is true:
0000 equals 0
0001 equals 1
0010 equals 2
0011 equals 3
...
1110 equals 14
1111 equals 15

Now, I ask you a question.  You're designing a website that has 16 permissions that are on or off.  The user has permission to do this or the user doesn't.

Here is one approach.  You can create 16 integer variables.  Set a variable to 0 to indicate the user doesn't have permission.  Set a variable to 1 to indicate that they do.  But what have you done, you've created 16 integers, each requiring 4 bytes of memory space.  That's 64 total bytes of storage.

Wouldn't it be really cool if we could store all those settings in just 4 bytes?  The answer is yes and here's how you do it.  You let each [b]bit[/b] of a single integer variable represent a permission.  We count bit positions starting from 0 and from the right of the bit string.

0101 - bit string
3210 - bit position

In position 0 (bottom) we have a value of 1 (top).  In position 1 we have a value of 0.  In position 2 we have a value of 1.  In position 3 we have a value 0.

If each position (0, 1, 2, & 3) represent a different permission, we can store 4 permissions in just 4 bits!  This means that a 32 bit integer can be used to store 32 permissions that have an on or off setting.

So how do we get at the individual bits?  We use a mask.  This would be a good point to look up a tutorial on hexadecimal, but for reference:
0000 =  0 = 0
0001 =  1 = 1
0010 =  2 = 2
0011 =  3 = 3
0100 =  4 = 4
0101 =  5 = 5
0110 =  6 = 6
0111 =  7 = 7
1000 =  8 = 8
1001 =  9 = 9 We have now run out of 0-9 digits to represent values, so we use letters!
1010 = 10 = A
1011 = 11 = B
1100 = 12 = C
1101 = 13 = D
1110 = 14 = E
1111 = 15 = F

What we have here is a mapping of 4 bits to a single value in the range of 0 through 9, A through F.  You start a hexadecimal number with 0x and then end it with the digits (0-9, A-F).

For example:
The bit string:
0110 0010 0101 1111 0101 1000 0000 0001
is equivalent to the hex number:
0x625F5801

Look at that and absorb it until it makes sense; it's nothing more than a shorter way to write a bit string!

You might be familiar with the [b]logical[/b] operators in PHP that compare values.  These are && and ||.  Well guess what, there are binary operators that do very similar tasks.

& - From two values, make a bitwise comparison and return a composite result based on AND.
For example:
0111 & 1010 = 0010   - Notice that the result only contains a 1 where [b]both[/b] bit strings also contained a 1.

| - From two values, make a bitwise comparison and return a composite result based on OR.
For example:
0101 | 1010 = 1111    - Notice that the result contains a 1 where [b]either[/b] bit string contained a 1.

There is also a negation operator:
~ From a single value, flip all of the bits.
For example:
~0101 = 1010      - Notice how all the bits changed to the opposite value of what they were.

Now we can start packing multiple values into a single set of 32 bits (or an integer).
[code]<?php
define('MOD_FORUMS', 0x00000001);
define('DEL_THREAD', 0x00000002);
...
define('BOOT_USER', 0x80000000);

var $perms = 0x00000000; // We init our current permissions to NONE

// Let's turn on DEL_THREAD
$perms = $perms | DEL_THREAD; // $perms now contains 0x00000002

// Let's turn on MOD_FORUMS
$perms = $perms | MOD_FORUMS; // $perms now contains 0x00000003 (the right most set of four bits is 0011, or 3 in hex)

// Let's turn on our last permission
$perms = $perms | BOOT_USER; // perms now contains 0x80000003

// Now lets check for a permission
if($perms & MOD_THREAD){
  // Check if the MOD_THREAD bit is turned on!
  echo 'user can mod a thread';
}

// Now let's turn a permission off
// here we use the negation operator, we flip all of the bits in the permission constant and
// AND it with the current set of permissions
// Do it on paper and see why this turns off only one permission out of all of them :D
$perms = $perms & ~MOD_THREAD;
?>[/code]

Wow!  That's great for on or off type permissions.

But now you might be thinking what if a permission has more than one value?  You can still pack those types of settings using bitwise operators.  I'll leave that as an exercise for you to research or discover on your own though.

(EDIT) Because you turn permissions on with |, silly me.
Link to comment
Share on other sites

[quote author=roopurt18 link=topic=110890.msg450649#msg450649 date=1160602201]
Now, humans like to group things, it makes our lives easier.  So we arbitrarily decided that 8 bits is called a byte, and 4 bytes is a word.  (On some platforms a word could be more than 4 bytes, but that's irrelevant).[/quote]

I just had to nitpick.

"We" didn't decide anything - IBM decided. When IBM was the only company successfully making computers, their machines used 8-bit bytes. To maintain compatibility with the old IBM equipment, they simply continued using 8-bit bytes, and when other companies jumped in, they made their equipment use 8-bit bytes to maintain compatibility with IBM equipment. And a word is simply the amount of bits a processor can handle at one time, not 4 bytes and that's it. 32-bit (4 byte) processors are simply the most common right now among end-users.

And newb, this is one of the easiest ways to do user permissions. One 'requirement' for being in this business is that you have to be willing to learn. And if you're not willing to learn how binary and hex work, then you'd better find a different occupation/hobby.
Link to comment
Share on other sites

I used "we" as in us, the human species, not as in you and me. I could very well argue that by not doing anything to change the norm that we have also decided to do this as well though.  And I did make a somewhat clouded reference to the size of a word being platform dependent.

Binary and hex are so fundamental to computing and programming, shame to any programmer that doesn't understand them.  And really, they're not all that difficult.
Link to comment
Share on other sites

I recommend using named constants to represent the values of your permissions and not sticking $perm ^ 2 all over the place.  Also, hex is more convenient as you add more permissions.  You might know the value of 2^11 or 2^12 off the top of your head, but after that you're gonna need a calculator.  Not to mention when you look at the code later you're going to go, "What the Hell does $perm ^ 2197987252197 do again?"
Link to comment
Share on other sites

Constants break encapsulation.

Not to mention my example, was merely nothing but that... an example.

For a real world example taken from a class:

[code]<?php

public function checkPermision($perm)
{
    return (bool) $this->_permissions ^ $perm;
}

?>[/code]

Hex is not more convinient, it's less readable.

It's not hard to read.. 1, 2, 4, 8, 16, 32, 64.. etc.
Link to comment
Share on other sites

Like I said, the first 12 or so are not difficult to read.  But what about when you get up to your 16th or 21st permission?

Constants do not break encapsulation.  It is much easier 6 months later to read:
[code]
if($perm & _MOD_FORUMS){
}
[/code]

than

[code]
if( $perm & 2097152 ){
}
[/code]

Not to mention repeatedly typing 2097152 through out your code, what if you fat finger an 8 instead of a 7?  It will be very hard to catch.

Use constants.
Link to comment
Share on other sites

Do we have to argue about constants breaking encapsulation? What if [using PHP 5's features] you create a class with a static array and build static functions that allow you to add and retrieve permission 'constants' ie array keys? Everyone decides what's best for him and works with it.

As for the tutorial, I do understand binary and hex but never thought of the bitwise operators this way. Thanks for the [url=http://www.phpfreaks.com/forums/index.php/topic,110890.msg450649.html#msg450649]tutorial[/url]. Now I know just what to do to implement a permission-based system.
Link to comment
Share on other sites

Le sigh, this is what I think of when you say encapsulation:
http://en.wikipedia.org/wiki/Information_hiding

Please explain to me how this breaks the concept of encapsulation:
[code]
<?php
  // NPermissions.php
  // a simple example of a permission system

  // define our permission constants
  define( '_PERM_MOD_THREAD',    0x00000001 );
  define( '_PERM_MOV_THREAD',    0x00000002 );
  define( '_PERM_DEL_THREAD',    0x00000004 );
  define( '_PERM_BAN_USER',      0x00000008 );
  define( '_PERM_UNBAN_USER',    0x00000010 );
  // more constants would follow depending on the module

  // In this example, I'm going to define a class that will not be instantiated.
  // Instead, the class is being used to create a namespace.
  // The assumption for the functions within the class is that the user
  // permissions are loaded from a DB elsewhere in the program.  The first
  // param of the functions is this value, named $user_perms

  class NPermissions{
    // $user_perms - current user permissions
    // RETURN: true if user has permission, false otherwise
    function Can_ModThread($user_perms){
      return $user_perms & _PERM_MOD_THREAD;
    }

    // $user_perms - current user permissions
    // Update user's permissions to allow modding of a thread
    function Set_ModThread($user_perms){
      return $user_perms | _PERM_MOD_THREAD;
    }

    // $user_perms - current user permissions
    // Update user's permissions to not allow thread modding
    function Rem_ModThread($user_perms){
      return $user_perms & ~_PERM_MOD_THREAD;
    }

    // You would continue in this manner for each of your permissions
    // and possibly provide a few more utility functions as the case may
    // be.
  }
?>
[/code][code]
<?php
  // AnotherFile.php
  // Meanwhile, in another part of the city...
  require_once('NPermissions.php');

  // Build our forums
  $Posts = GetForumPosts();
  if(is_array($Posts) && count($Posts)){
    foreach($Posts as $Post){
      // .. some code
      if(NPermissions::Can_ModThread($User->GetPerms())){
        $html .= Link_ModThread();
      }
      // .. some more code
    }
  }
?>
[/code]

I have to say that's pretty neat, simple, and easy to maintain.  6 months later when you [i]do[/i] need to change the implementation, you can update the constants, do away with the constants, or whatever you want.  Just as long as you keep the [i]interface[/i] the same, nothing else in the program breaks.

Is this not what encapsulation is?  If this fits your definition of encapsulation, I would be curious how the constants are breaking it.  If it's not, I'd love for you to clarify.
Link to comment
Share on other sites

guys guys - back on topic please  ;D the topic of encapsulation seems very irrelevent to the objective, as it's not ESSENTIAL to obey these 'rules' for what newb first asked. (although i'm with roopurt anyway a bit actually on this one. the idea of not using global constants or functions as it breaks 'encapsulation' seems slightly contradicted by using php's own built in functions, constants, etc.)

i'm following this topic myself as i'm keen to find out more - so would be good if the initial question got answered without over complication  ;)
Link to comment
Share on other sites

[quote author=roopurt18 link=topic=110890.msg451025#msg451025 date=1160674959]
Le sigh, this is what I think of when you say encapsulation:
http://en.wikipedia.org/wiki/Information_hiding

Please explain to me how this breaks the concept of encapsulation:
[code]
<?php
  // NPermissions.php
  // a simple example of a permission system

  // define our permission constants
  define( '_PERM_MOD_THREAD',     0x00000001 );
  define( '_PERM_MOV_THREAD',     0x00000002 );
  define( '_PERM_DEL_THREAD',     0x00000004 );
  define( '_PERM_BAN_USER',       0x00000008 );
  define( '_PERM_UNBAN_USER',     0x00000010 );
  // more constants would follow depending on the module

  // In this example, I'm going to define a class that will not be instantiated.
  // Instead, the class is being used to create a namespace.
  // The assumption for the functions within the class is that the user
  // permissions are loaded from a DB elsewhere in the program.  The first
  // param of the functions is this value, named $user_perms

  class NPermissions{
    // $user_perms - current user permissions
    // RETURN: true if user has permission, false otherwise
    function Can_ModThread($user_perms){
      return $user_perms & _PERM_MOD_THREAD;
    }

    // $user_perms - current user permissions
    // Update user's permissions to allow modding of a thread
    function Set_ModThread($user_perms){
      return $user_perms | _PERM_MOD_THREAD;
    }

    // $user_perms - current user permissions
    // Update user's permissions to not allow thread modding
    function Rem_ModThread($user_perms){
      return $user_perms & ~_PERM_MOD_THREAD;
    }

    // You would continue in this manner for each of your permissions
    // and possibly provide a few more utility functions as the case may
    // be.
  }
?>
[/code][code]
<?php
  // AnotherFile.php
  // Meanwhile, in another part of the city...
  require_once('NPermissions.php');

  // Build our forums
  $Posts = GetForumPosts();
  if(is_array($Posts) && count($Posts)){
    foreach($Posts as $Post){
      // .. some code
      if(NPermissions::Can_ModThread($User->GetPerms())){
        $html .= Link_ModThread();
      }
      // .. some more code
    }
  }
?>
[/code]

I have to say that's pretty neat, simple, and easy to maintain.  6 months later when you [i]do[/i] need to change the implementation, you can update the constants, do away with the constants, or whatever you want.  Just as long as you keep the [i]interface[/i] the same, nothing else in the program breaks.

Is this not what encapsulation is?  If this fits your definition of encapsulation, I would be curious how the constants are breaking it.  If it's not, I'd love for you to clarify.
[/quote]The constants are references to information outside of the 'capsule' that is a module.. thus: encapsualtion broken. Hard to understand?
Link to comment
Share on other sites

So you're saying if you replace the constants with literals it makes all the difference?  It would be the same thing.

The reality is every single, separate module needs to define it's own permission constants that can be imported / referenced by a central controller.  If you are designing a modular site with forums and photo galleries, each of those modules is likely to have it's own permissions.  It is impossible for the controller to store those preferences within the same 16, 32, ..., n-bit field as the modules will step on each other.

The reality is that every single module will need to define it's own permission system to be used only within that module.

Every module will also need to provide a mechanism, designed by the controller, of exporting those preferences so that the controller, and not the module, can set preferences up for specific users or groups of users.

Encapsulation is not broken.  The forum module's constants only exist within that module, the photo gallery module's constants only exist within that module, etc.  The only thing the controller ever needs to know is that this particular user logged in right now has a forum permission of 0xA0F2E0D8 and a photo gallery permission of 0x000013AB

When did I ever say that all constants across all modules will all be placed in some central file?  The situation called for a simple explanation of how bitwise operators could be used to pack multiple values into a single field.
Link to comment
Share on other sites

Where did I specify "literals instead of constants"?

Encapsulation is broken. Re-read the link you posted - the first paragraph says it all.

You are bypassing all interfaces to your class, so you can stop mentioning "it's part of the interface"
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.