Question

Bits et bitmask sont quelque chose que j'ai du mal à comprendre pendant un certain temps, mais je voudrais apprendre comment les utiliser pour les paramètres et les choses comme ça en PHP.

J'ai enfin trouvé une classe qui prétend faire exactement cela, et que je peux dire, il semble fonctionner, mais je ne suis pas sûr si elle est la meilleure façon de le faire. Je signalerai le fichier de classe avec le code exemple ci-dessous pour l'afficher en ordre de marche.

S'il vous plaît, si vous avez de l'expérience, dites-moi si elle peut être améliorée, la performance ou toute autre chose. Je veux vraiment apprendre, et je l'ai lu sur le sujet, mais il est difficile pour moi de saisir jusqu'à présent.

La classe ...

<?php
    class bitmask
    {
        /**
         * This array is used to represent the users permission in usable format.
         *
         * You can change remove or add valuesto suit your needs.
         * Just ensure that each element defaults to false. Once you have started storing
         * users permsisions a change to the order of this array will cause the
         * permissions to be incorectly interpreted.
         *
         * @type Associtive array
         */
        public $permissions = array(
                                    "read" => false,
                                    "write" => false,
                                    "delete" => false,
                                    "change_permissions" => false,
                                    "admin" => false
                                    );

        /**
         * This function will use an integer bitmask (as created by toBitmask())
         * to populate the class vaiable
         * $this->permissions with the users permissions as boolean values.
         * @param int $bitmask an integer representation of the users permisions.
         * This integer is created by toBitmask();
         *
         * @return an associatve array with the users permissions.
         */
        public function getPermissions($bitMask = 0)
        {
            $i = 0;
            foreach ($this->permissions as $key => $value)
            {
                $this->permissions[$key] = (($bitMask & pow(2, $i)) != 0) ? true : false;

                // Uncomment the next line if you would like to see what is happening.
                //echo $key . " i= ".strval($i)." power=" . strval(pow(2,$i)). "bitwise & = " . strval($bitMask & pow(2,$i))."<br>";
                $i++;
            }
            return $this->permissions;
        }

        /**
         * This function will create and return and integer bitmask based on the permission values set in
         * the class variable $permissions. To use you would want to set the fields in $permissions to true for the permissions you want to grant.
         * Then call toBitmask() and store the integer value.  Later you can pass that integer into getPermissions() to convert it back to an assoicative
         * array.
         *
         * @return int an integer bitmask represeting the users permission set.
         */
        function toBitmask()
        {
            $bitmask = 0;
            $i = 0;
            foreach ($this->permissions as $key => $value)
            {

                if ($value)
                {
                    $bitmask += pow(2, $i);
                }
                $i++;
            }
            return $bitmask;
        }
    }
?>

Comment puis-je configurer / enregistrer les autorisations en tant que valeur bitmask?

<?php
    /**
     * Example usage
     * initiate new bitmask object
     */
    $perms = new bitmask();

    /**
     * How to set permissions for a user
     */
    $perms->permissions["read"] = true;
    $perms->permissions["write"] = true;
    $perms->permissions["delete"] = true;
    $perms->permissions["change_permissions"] = true;
    $perms->permissions["admin"] = false;

    // Converts to bitmask value to store in database or wherever
    $bitmask = $perms->toBitmask();  //in this example it is 15
    $sql = "insert into user_permissions (userid,permission) values(1,$bitmask)";
    echo $sql; //you would then execute code to insert your sql.
?>

Exemple de prendre la valeur bitmask et le retour d'un vrai / faux pour chaque élément du tableau en fonction de la valeur du bit ....

<?php
    /**
     * Example usage to get the bitmask value from database or session/cache.... then put it to use.
     * $permarr returns an array with true/false for each array value based on the bit value
     */
    $permarr = $perms->getPermissions($bitmask);

    if ($permarr["read"])
    {
        echo 'user can read: <font color="green">TRUE</font>';
    }
    else {
        echo 'user can read: <font color="red">FALSE</font>';
    }

    //user can WRITE permission
    if ($permarr["write"])
    {
        echo '<br>user can write: <font color="green">TRUE</font>';
    }
    else {
        echo '<br>user can write: <font color="red">FALSE</font>';
    }
?>
Était-ce utile?

La solution

Les champs de bits sont un outil très pratique et efficace pour traiter les drapeaux ou tout ensemble de valeurs booléennes en général.

Pour les comprendre, vous devez d'abord savoir comment les nombres binaires fonctionnent. Après cela, vous devriez vérifier les entrées manuelles sur et les opérateurs au niveau du bit assurez-vous vous savez comment bitwise AND, OR et les travaux de déplacement vers la gauche / droite.

Un champ de bits est rien de plus qu'une valeur entière. Supposons que la taille de notre champ de bits est fixe et un seul octet. Les ordinateurs fonctionnent avec des nombres binaires, donc si la valeur de notre numéro est 29, vous trouverez effectivement 0001 1101 dans la mémoire.

Utilisation au niveau du bit (&) et opérateur OR (|), vous pouvez lire et régler chaque bit du nombre individuellement. Ils prennent tous les deux deux entiers en entrée et effectuer une et / ou sur chaque bit individuellement.

Pour lire le premier bit de votre numéro, vous pouvez faire quelque chose comme ceci:

  0001 1101 (=29, our number)
& 0000 0001 (=1, bit mask)
= 0000 0001 (=1, result)

Comme vous pouvez le voir besoin d'un numéro spécial où seul le bit qui nous intéresse est fixé, qui est le soi-disant « masque de bits ». Dans notre cas, il est 1. Pour lire le second bit il faut « pousser » l'un dans l'un bitmask chiffre à gauche. Nous pouvons le faire avec l'opérateur de décalage gauche ($number << 1) ou en multipliant notre par deux.

  0001 1101
& 0000 0010
= 0000 0000 (=0, result) 

Vous pouvez le faire pour chaque bit dans notre numéro. Le binaire et de notre nombre et les fils de masque de bits, soit à zéro, ce qui signifie que le bit n'était pas « ensemble », ou à un entier non nul, ce qui signifie que le bit a été défini.

Si vous souhaitez définir un des bits, vous pouvez utiliser un OU binaire:

  0001 1101
| 0010 0000 (=32, bit mask)
= 0011 1101 (=29+32)

Cependant, vous devrez aller d'une manière différente quand vous voulez « clair » un peu.

Une approche plus générale serait:

// To get bit n
$bit_n = ($number & (1 << $n)) != 0
// Alternative
$bit_n = ($number & (1 << $n)) >> $n

// Set bit n of number to new_bit
$number = ($number & ~(1 << $n)) | ($new_bit << $n)

Tout d'abord, il peut sembler un peu cryptique, mais en réalité il est assez facile.

Maintenant vous avez probablement découvert que les champs de bits sont tout à fait technique de bas niveau. Voilà pourquoi je recommande de ne pas les utiliser dans PHP ou les bases de données .. Si vous voulez avoir un tas de drapeaux, il est probablement correct, mais pour tout ce que vous ne vraiment pas besoin.

La classe que vous avez affichée semble-moi un peu spécial. Par exemple, des choses comme ... ? true : false sont veery mauvaises pratiques. Si vous voulez utiliser des champs de bits, vous êtes probablement mieux définir certaines constantes et utiliser la méthode décrite ci-dessus. Il est pas difficile de trouver une classe simple.

define('PERM_READ', 0);
define('PERM_WRITE', 1);

class BitField {
    private $value;

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

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

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

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


$bf = new BitField($user->permissions);

if ($bf->get(PERM_READ)) {
    // can read
}

$bf->set(PERM_WRITE, true);
$user->permissions = $bf->getValue();
$user->save();

Je n'ai pas essayé tout morceau de code de cette réponse, mais il devrait vous aider à démarrer même si elle ne fonctionne pas hors de la boîte.

Notez que vous êtes limité à 32 valeurs par champ de bits.

Autres conseils

Voici comment définir bitmasks.

// the first mask.  In binary, it's 00000001
define('BITWISE_MASK_1', 1 << 0); // 1 << 0 is the same as 1

// the second mask.  In binary, it's 00000010
define('BITWISE_MASK_2', 1 << 1);

// the third mask.  In binary, it's 00000100
define('BITWISE_MASK_3', 1 << 2);

Pour vérifier si un masque de bits est présent (dans ce cas un argument de fonction), utilisez le niveau du bit opérateur.

function computeMasks($masks) {
    $masksPresent = array();
    if ($masks & BITWISE_MASK_1)
        $masksPresent[] = 'BITWISE_MASK_1';
    if ($masks & BITWISE_MASK_2)
        $masksPresent[] = 'BITWISE_MASK_2';
    if ($masks & BITWISE_MASK_3)
        $masksPresent[] = 'BITWISE_MASK_3';
    return implode(' and ', $masksPresent);
}

Cela fonctionne parce que quand vous ou deux octets (disons, 00000001 et 00010000), vous obtenez les deux ensemble: 00010001. Si vous et le résultat et le masque d'origine (00010001 et dire 00000001), vous obtenez un masque si elle est présente (dans ce cas 00000001). Dans le cas contraire, vous obtenez zéro.

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