Ca faisait longtemps que je n'avais pas posté quelque chose...alors zou...
Ce code est plutôt un prétexte : un post sur le forum posait des questions sur les opérateurs bit à bit. Je me suis dit "tien, c'est vrai qu'ils sont peu utilisés, et donc peu connus, par la plupart des codeurs PHP". J'ai donc décidé de faire un code prétexte, servant d'exemple pour une utilisation de ces opérateurs.
Ce code est néanmoins implémentable et extensible, donc utilisable (à condition de l'étendre hein...). Et il ne prétend pas détenir LA solution pour gérer des droits : c'est une solution comme une autre...elle a ses avantages et ses inconvénients.
Mais d'abord, les opérateurs bit à bit...le code n'en montrera qu'un, donc une petite explication préalable est nécessaire:
Pour rappel, on lit les bits de droite à gauche, le bit le plus faible se trouvant à droite, et le plus fort à gauche.
Du coup, pour comparer 10 et 110, on peut aussi lire : 010 et 110.
Les opérateurs :
AND ou & : les bits qui sont à 1 dans l'argument de gauche et de droite sont mis à 1
101 & 111 = 101
110 & 10 = 10
101 & 10 = 0
OR ou | : les bits qui sont à 1 dans l'argument de gauche ou de droite sont mis à 1
101 | 111 = 111
110 | 10 = 110
101 | 10 = 111
XOR ou ^ : les bits qui sont à 1 dans l'argument de gauche ou dans l'argument de droit mais pas dans les deux arguments en même temps sont mis à 1
101 ^ 111 = 10
110 ^ 10 = 100
101 ^ 10 = 111
NOT ou ~ : les bits à 1 dans l'argument sont mis à 0, et inversement :
~ 100 = 11111111111111111111111111111011 (et oui, on est sur 32 bits!)
le Shift left ou << : décale les bits de l'argument de gauche X fois (ou X est l'argument de droite) vers la gauche. Cela revient à multiplier par 2 à chaque fois
100 << 2 = 10000
Ou avec des entiers :
1 << 2 = 4 (en binaire : 1 (ou 001 donc) << 2 = 100)
Le shift right, ou >> décale les bits de l'argument de gauche X fois (ou X est l'argument de droite) vers la droite. Cela revient à diviser par 2 à chaque fois
100 >> 2 = 1 (ou 001 donc)
Ou avec des entiers :
4 >> 1 = 2 (en binaire : 100 >> 1 = 10 (ou 010))
Facile, non ?
Ce code va utiliser le | pour vérifier des droits. Le principe est très simple une fois que vous avez compris le fonctionnement des opérateurs bit à bit.
On imagine, pour ce code, que l'on crée la gestion des messages d'un forum, par exemple.
On a 3 types d'utilisateurs :
le visiteur, qui n'a que le droit de lire les messages
le membres, qui peut lire les messages et en écrire de nouveau
l'administrateur, qui peut lire, écrire, mais aussi éditer des messages existants.
On affecte donc des niveaux de droit aux 3 accès :
1 pour lire
10 pour écrire
100 pour éditer
Et on fixe les accès des utilisateurs :
1 pour le visiteur
11 pour le membre
111 pour l'administrateur
Et c'est tout :-)
A noter que decbin() permet de transformer un entier en octets: 4 devient alors 100 (sous forme de chaîne...on ne peut pas écrire directement en binaire en php), et que bindec fait l'inverse : il prend une chaîne représentant un nombre binaire, et le transforme en son équivalent entier : 100 devient 4.
Pour les droit, on pourrait aussi utiliser des entiers :
1 pour lire
2 pour écrire
4 pour éditer
et
1 pour le visiteur
3 pour le membre
7 pour l'administrateur
mais c'était plus parlant avec des bits, pour suivre la petite explication :-) Ceci dit, les entiers seraient plus performants, car on n'aurait alors pas besoin de passer par decbin() et bindec().
Source / Exemple :
<?php
abstract class rights {
protected $bRights = '0';
public function __get($sProp) {
switch($sProp) {
case 'RIGHTS':
return $this->bRights;
default:
throw new Exception('Cannot get property '.$sProp);
}
}
}
class read extends rights {
protected $bRights = '1';
}
class write extends rights {
protected $bRights = '10';
}
class edit extends rights {
protected $bRights = '100';
}
abstract class user {
protected $bRights = '0';
public function hasRights(rights $rights) {
return (decbin(bindec($rights->RIGHTS) | bindec($this->bRights)) === $this->bRights);
}
}
class visitor extends user {
protected $bRights = '1';
}
class member extends user {
protected $bRights = '11';
}
class admin extends user {
protected $bRights = '111';
}
$read = new read;
$write = new write;
$edit = new edit;
$visitor = new visitor;
echo (int)$visitor->hasRights($read),"\n";
echo (int)$visitor->hasRights($write),"\n";
echo (int)$visitor->hasRights($edit),"\n";
$member = new member;
echo (int)$member->hasRights($read),"\n";
echo (int)$member->hasRights($write),"\n";
echo (int)$member->hasRights($edit),"\n";
$admin = new admin;
echo (int)$admin->hasRights($read),"\n";
echo (int)$admin->hasRights($write),"\n";
echo (int)$admin->hasRights($edit),"\n";
?>
Conclusion :
A noter que l'on peut modifier la méthode user::hasRights() pour utiliser le & :
return (decbin(bindec($rights->RIGHTS) & bindec($this->bRights)) === $rights->RIGHTS);