Question

J'ai essayé de trouver la meilleure façon d'utiliser bitmask ou bitfields en PHP depuis longtemps maintenant pour les différents domaines de mon application pour les différents paramètres de l'utilisateur et les autorisations. Le plus loin que je suis venu à ce jour est d'une classe contribué par Svens dans le débordement de la pile après Bitmask en PHP pour les paramètres? . Je l'ai légèrement modifié ci-dessous, changer d'utiliser des constantes de classe au lieu de DEFINE et en vous assurant la méthode get est passé un entier seulement. J'ai aussi quelques exemples de code pour tester la fonctionnalité de la classe ci-dessous.

Je cherche des suggestions / code pour améliorer cette classe encore plus il peut être utilisé dans ma demande de paramètres et dans certains cas, les autorisations utilisateur.

Questions traitées dans le commentaire ci-dessous par mcrumley

En outre, j'ai une question sur la numérotation de mes constantes. Dans d'autres classes et exemple de code pour ce type il aura des choses énumérées dans les pouvoirs de 2. Toutefois, il semble fonctionner même pour autant que je peux dire, même si je numérote mes constantes 1,2,3,4,5,6 au lieu de 1, 2, 4, 8, 16, etc. donc, quelqu'un peut-il préciser aussi si je dois changer mes constantes?


Quelques idées ... Je voudrais vraiment trouver un moyen d'étendre cette classe de sorte qu'il est facile à utiliser avec d'autres classes. Disons que j'ai une classe User et une classe de Messages. Tant la classe User et Messages prolongera cette classe et être en mesure d'utiliser le masque de bits pour leurs paramètres / autorisations (ainsi que d'autres cours plus tard). Alors peut-être les constantes de classe actuelles doivent être modifiées afin qu'ils puissent être transmis ou une autre option? Je voudrais vraiment avoir assez de ne pas définir (define ( « PERM_READ », 1);) dans d'autres parties du site / script et je voudrais garder un peu encapsulé, mais souple aussi bien; Je suis ouvert aux idées. Je veux que ce soit je solide comme le roc et flexible comme l'a dit à utiliser avec plusieurs autres classes pour les paramètres ou les autorisations. Peut-être une sorte de tableau doit être utilisé? @Svens de ma question précédente lien ci-dessus posté un commentaire avec « mettre en œuvre des automagiques getters / setters ou ArrayAccess pour awesomness supplémentaires -. Svens »? Que pensez-vous de quelque chose comme ça aussi

Inclure un exemple de code source si possible, s'il vous plaît.

<?php

class BitField {

    const PERM_READ = 0;
    const PERM_WRITE = 1;
    const PERM_ADMIN = 2;
    const PERM_ADMIN2 = 3;
    const PERM_ADMIN3 = 4;

    private $value;

    public function __construct($value=0) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
        if (is_int($n)) {
            return ($this->value & (1 << $n)) != 0;
        }else{
            return 0;
        }
    }

    public function set($n, $new=true) {
        $this->value = ($this->value & ~(1 << $n)) | ($new << $n);
    }

    public function clear($n) {
        $this->set($n, false);
    }
}
?>

Exemple d'utilisation ...

<?php
    $user_permissions = 0; //This value will come from MySQL or Sessions
    $bf = new BitField($user_permissions);

    // Turn these permission to on/true
    $bf->set($bf::PERM_READ);
    $bf->set($bf::PERM_WRITE);
    $bf->set($bf::PERM_ADMIN);
    $bf->set($bf::PERM_ADMIN2);
    $bf->set($bf::PERM_ADMIN3);

    // Turn permission PERM_ADMIN2 to off/false
    $bf->clear($bf::PERM_ADMIN2); // sets $bf::PERM_ADMIN2 bit to false

    // Get the total bit value
    $user_permissions = $bf->getValue();

    echo '<br> Bitmask value = ' .$user_permissions. '<br>Test values on/off based off the bitmask value<br>' ;

    // Check if permission PERM_READ is on/true
    if ($bf->get($bf::PERM_READ)) {
        // can read
        echo 'can read is ON<br>';
    }

    if ($bf->get($bf::PERM_WRITE)) {
        // can write
        echo 'can write is ON<br>';
    }

    if ($bf->get($bf::PERM_ADMIN)) {
        // is admin
        echo 'admin is ON<br>';
    }

    if ($bf->get($bf::PERM_ADMIN2)) {
        // is admin 2
        echo 'admin 2 is ON<br>';
    }

    if ($bf->get($bf::PERM_ADMIN3)) {
        // is admin 3
        echo 'admin 3 is ON<br>';
    }
?>
Était-ce utile?

La solution

D'autres ont aidé à expliquer plus le bit de masquage peu de cela, donc je vais me concentrer sur

"Je aime l'idée de le rendre plus extensible / générique si différent les classes peuvent étendre ce et l'utiliser pour différentes sections, je ne suis pas sûr comment le faire encore "

de votre commentaire sur le post de @Charles.

Comme Charles dit à juste titre, vous pouvez réutiliser les fonctionnalités de votre classe Bitmask en extrayant la fonctionnalité dans une classe abstraite, et de mettre les « paramètres » réels (dans ces autorisations de cas) dans des classes de béton dérivées.

Par exemple:

<?php

abstract class BitField {

    private $value;

    public function __construct($value=0) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
        if (is_int($n)) {
            return ($this->value & (1 << $n)) != 0;
        }else{
            return 0;
        }
    }

    public function set($n, $new=true) {
        $this->value = ($this->value & ~(1 << $n)) | ($new << $n);
    }

    public function clear($n) {
        $this->set($n, false);
    }
}

class UserPermissions_BitField extends BitField
{
    const PERM_READ = 0;
    const PERM_WRITE = 1;
    const PERM_ADMIN = 2;
    const PERM_ADMIN2 = 3;
    const PERM_ADMIN3 = 4;
}

class UserPrivacySettings_BitField extends BitField
{
    const PRIVACY_TOTAL = 0;
    const PRIVACY_EMAIL = 1;
    const PRIVACY_NAME = 2;
    const PRIVACY_ADDRESS = 3;
    const PRIVACY_PHONE = 4;
}

Et l'utilisation devient simplement alors:

<?php
$user_permissions = 0; //This value will come from MySQL or Sessions
$bf = new UserPermissions_BitField($user_permissions); 

// turn these permission to on/true
$bf->set($bf::PERM_READ);
$bf->set($bf::PERM_WRITE);
$bf->set($bf::PERM_ADMIN);
$bf->set($bf::PERM_ADMIN2);
$bf->set($bf::PERM_ADMIN3);

Et pour définir les paramètres de confidentialité, vous instanciez un nouvel objet UserPrivacySettings_BitField et utilisez-le.

De cette façon, vous pouvez créer autant de différents ensembles de BitField objets que votre application requiert simplement en définissant un ensemble de constantes qui représentent vos options.

J'espère que cela est d'une certaine utilité pour vous, mais sinon, ce sera peut-être utile à quelqu'un d'autre qui lit cela.

Autres conseils

Dans d'autres classes et exemple de code pour ce type, il aura des choses énumérées dans des puissances de 2 mais il semble fonctionner même pour autant que je peux dire, même si je numérote mes constantes 1,2,3,4,5, 6 au lieu de 1,2,4,8,16 etc. quelqu'un So peut également préciser si je devrais changer mes constantes?

Vous n'avez pas besoin, parce que le code est déjà en charge de cela. Cette explication va être un rond-point bit.

La raison pour laquelle les champs de bits sont traités comme puissances de deux est que chaque puissance de deux est représenté par un seul bit. Ces bits individuels peuvent être ORed au niveau du bit-ensemble dans un seul nombre entier qui peut être passé autour. Dans les langues de niveau inférieur, il est « plus facile » de passer autour d'un nombre que, disons, un struct.

Permettez-moi de montrer comment cela fonctionne. L'ensemble Let quelques autorisations en utilisant les pouvoirs de deux:

define('PERM_NONE', 0);
define('PERM_READ', 1);
define('PERM_WRITE', 2);
define('PERM_EDIT', 4);
define('PERM_DELETE', 8);
define('PERM_SUPER', 16);

nous allons inspectent les valeurs de bits de ces autorisations au PHP interactif invite:

php > printf('%08b', PERM_SUPER);
00010000
php > printf('%08b', PERM_DELETE);
00001000
php > printf('%08b', PERM_EDIT);
00000100
php > printf('%08b', PERM_WRITE);
00000010
php > printf('%08b', PERM_READ);
00000001
php > printf('%08b', PERM_NONE);
00000000

Maintenant, nous allons créer un utilisateur qui a accès en lecture et accès en écriture.

php > printf('%08b', PERM_READ | PERM_WRITE);
00000011

Ou un utilisateur qui peut lire, écrire, supprimer, mais pas modifier:

php > printf('%08b', PERM_READ | PERM_WRITE | PERM_DELETE);
00001011

Nous pouvons vérifier l'autorisation à l'aide logique binaire et assurant que le résultat est zéro:

php > $permission = PERM_READ | PERM_WRITE | PERM_DELETE;
php > var_dump($permission & PERM_WRITE); // This won't be zero.
int(2)
php > var_dump($permission & PERM_EDIT); // This will be zero.
int(0)

(Il convient de noter que PERM_NONE & PERM_NONE est 0 & 0, ce qui est égal à zéro. La « none » permission, je créé ne fonctionne pas vraiment ici, et peut rapidement être oublié.)

Votre classe est en train de faire quelque chose de un peu différent , mais le résultat final est identique. Il utilise peu de décalage pour déplacer un « sur » peu plus de temps X gauche, où X est le numéro de l'autorisation. En effet, ce Raising 2 à la puissance de la valeur de l'autorisation . Une démonstration:

php > echo BitField::PERM_ADMIN3;
4
php > echo pow(2, BitField::PERM_ADMIN3);
16
php > printf('%08b', pow(2, BitField::PERM_ADMIN3));
00010000
php > echo 1 << BitField::PERM_ADMIN3;
16
php > printf('%08b', 1 << BitField::PERM_ADMIN3);
00010000

Bien que ces méthodes sont efficacement identiques, je dirais aussi simple que cela et ANDing ORing est plus facile à lire que le XOR et le bit de décalage.

Je cherche des suggestions / code pour améliorer encore plus cette classe de sorte qu'il peut être utilisé dans mon application pour les paramètres et dans certains cas, les autorisations utilisateur.

J'ai une suggestion, et un avertissement.

Ma suggestion serait faire la classe Résumé et ne pas définir les autorisations à l'intérieur. Au lieu de cela, les classes de construction qui en héritent et définir leurs propres autorisations. Vous ne voulez pas envisager de partager la même autorisation noms à travers les champs de bits, et sans rapport avec les préfixant avec les noms de classe est assez sain d'esprit. Je vous attends alliez faire de toute façon.

Mon avertissement est simple mais dire: PHP ne peut pas représenter de manière fiable un entier supérieur à 31 bits En fait, il ne peut représenter des entiers 63 bits quand il est compilé sur un système 64 bits.. Cela signifie que, si vous distribuez votre demande au grand public, vous serez limité à un maximum de 31 autorisations si vous souhaitez utiliser les fonctions mathématiques intégrées.

L'extension GMP comprend des opérations au niveau du bit qui peut fonctionner sur arbitraire entiers -Longueur.

Une autre option pourrait être utilisant le code de cette réponse sur les grands entiers , ce qui pourrait vous permettre de représenter un énorme entier comme une chaîne, mais faire des opérations sur ce pourrait bitwise être ... intéressant. (Vous pouvez convertir vers le bas à base 2, puis faire une vérification substr pour la chaîne « 1 » ou « 0 » à l'endroit prévu, mais ça va être une énorme traînée de performance.)

Voici ma proposition:

<?php

class BitField {

    const PERM_READ = 1;
    const PERM_WRITE = 2;
    const PERM_ADMIN = 4;
    const PERM_ADMIN2 = 8;
    const PERM_ADMIN3 = 16;

    private $value;

    public function __construct($value=0) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
                return $this->value & $n;
    }

    public function set($n, $new=true) {
        $this->value |= $n;
    }

    public function clear($n) {
        $this->value &= ~$n;
    }

}
?>

Comme vous pouvez le voir, j'ai utilisé 1, 2, 4, 8, etc. (puissances de 2) pour simplifier les calculs. Si vous associez une autorisation à un bit vous:

0 0 0 0 0 0 0 1 = PERM_READ = 1
0 0 0 0 0 0 1 0 = PERM_WRITE = 2
0 0 0 0 0 1 0 0 = PERM_ADMIN = 4
etc...

Ensuite, vous pouvez utiliser des opérations logiques, par exemple, vous avez ce départ:

    0 0 0 0 0 0 0 1 = PERM_READ = 1

Si vous voulez ajouter des autorisations à écrire, il vous suffit d'utiliser le opérateur OR:

    0 0 0 0 0 0 0 1 = PERM_READ = 1
OR  0 0 0 0 0 0 1 0 = PERM_WRITE = 2
=   0 0 0 0 0 0 1 1 = both bits enabled R & W

Pour supprimer un bit, vous devez utiliser $ valeur et ~ $ bit, par exemple, supprimer le bit d'écriture:

    0 0 0 0 0 0 1 1 = both bits enabled R & W
AND 1 1 1 1 1 1 0 1 = Bitwise negated PERM_WRITE
=   0 0 0 0 0 0 0 1 = result, only the R bit

Enfin, si vous voulez tester si un bit est activé l'opération que vous devez et $ valeur par rapport au PERM_XXX que vous voulez tester:

    0 0 0 0 0 0 1 1 = both bits enabled R & W
AND 0 0 0 0 0 0 1 0 = Want to test PERM_WRITE
=   0 0 0 0 0 0 1 0 = result

Si le résultat est zéro, vous avez la permission, sinon vous ne le faites pas.

La plus grande erreur que je vois dans votre classe est que vous mixez la logique métier dans une structure de données. Le but de votre classe est de stocker plusieurs valeurs booléennes (à savoir vrai / faux) dans un seul entier. Cela ne signifie pas Vous à faire dans une classe, mais il est pratique. Et c'est son but.

Je baisserait les drapeaux d'autorisation dans la classe et sous-traiter les dans vos classes de logique métier.

rel="nofollow"> est une entité qui gère une chose: données. Les données ne sont pas interprétées en aucune façon. Un pile , par exemple avant, est une structure de données que vous pouvez mettre des choses en, qui vous donnera le dernier élément premier. Et voici le point: Il ne se soucie pas, ce que vous mettez là-dedans :. Entiers, objets de l'utilisateur, les pointeurs, les voitures, les éléphants, il va juste gérer le stockage et la récupération des données

logique métier d'autre part est l'endroit où vous définissez comment Interagir vos structures de données les uns avec les autres . Ceci est où les autorisations sont définies, où vous dites qu'une personne qui a créé un blog peut le modifier, et personne d'autre est autorisé à.

Ce sont deux points de vue de votre application fondamentalement différentes et ne doit pas être mélangé. Vous pouvez stocker vos autorisations dans une autre structure de données (comme un tableau d'entiers, ou une table de hachage de objets d'autorisation, par exemple - ou toute autre structure de données ) et vous pouvez stocker d'autres drapeaux dans votre BitField la structure des données (comme les préférences booléennes de vos utilisateurs, comme « veut recevoir le bulletin » ou « adresse e-mail a été vérifiée »).

Une autre amélioration est l'utilisation des valeurs hexagonales pour ces constantes, cela fera en sorte que votre 16 valeur est toujours lisible. (Je recommande d'utiliser plutôt les opérateurs de décalage de bits dans les déclarations constantes, ce qui est encore plus facile à lire, mais cela est impossible avec l'interpréteur PHP en cours pour des raisons de performance.)

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);

La même classe sans valeur hexadécimale est moins lisible, surtout si vous ajoutez d'autres drapeaux:

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 serait en outre définir ce que le paramètre de set () doit être un entier à un seul bit, et non un numéro de drapeau. L'ensemble () mise en œuvre par démon est ce que je veux dire:

$this->value |= $n;

« Je aime l'idée de le rendre plus extensible des classes / si différents générique peut étendre cela et l'utiliser pour différentes sections, je ne suis pas sûr de savoir comment faire encore »

Ne pas le faire, il y a diverses raisons. Sans ordre spécifique et juste en bref: les classes fonctionnelles distinctes des objets de données. Ne pas prolonger ce qui ne l'héritage pas besoin. Utilisez une propriété à la place, les classes s'étendant normalement ne doivent pas nécessairement être étroitement avec la classe bitmask au travail du tout. De plus en PHP, vous ne pouvez prolonger d'une classe. Si vous utilisez cela pour un usage limité, l'extension des objets ont déjà brûlé cette fonction.

Il est donc probable que vous aimez pas besoin de faire des calculs binaires dans votre cerveau, mais ont une classe plutôt que encapsule le calcul binaire pour vous et qui offre une interface plus humaine (noms au lieu de chiffres à dire au moins) à interagir avec. Bien. Mais c'est juste est-il. Vous pouvez passer le long de la bitmask en passant la valeur binaire. Si vous n'avez pas besoin de valeurs binaires, ENUM classe au lieu peut-être ce que vous cherchez déjà (vérifiez la FlagsEnum en particulier).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top