Domanda

Bit e maschera di bit sono qualcosa che ho lottato per capire per un po ', ma mi piacerebbe imparare come usarli per le impostazioni e cose del genere in PHP.

Ho finalmente trovato una classe che pretende di fare esattamente questo, e posso dire, sembra funzionare, ma non sono sicuro se è il modo migliore di fare questo. Io posto il file di classe con il codice di esempio qui sotto per mostrare in ordine.

Per favore, se si dispone di esperienza, mi dica se può essere migliorato, per le prestazioni o qualsiasi altra cosa. Ho molta voglia di imparare questo, e ho letto su di esso, ma è un anno difficile per me capire finora.

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;
        }
    }
?>

Come faccio a impostare / Salva i permessi come un valore maschera di bit?

<?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.
?>

Esempio di prendere il valore di maschera di bit e restituisce un vero / falso per ogni elemento dell'array in base al valore di 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>';
    }
?>
È stato utile?

Soluzione

campi di bit sono uno strumento molto utile ed efficiente per trattare con le bandiere o qualsiasi insieme di valori booleani in generale.

Per capire loro è necessario prima di sapere come funzionano i numeri binari. Dopo di che si dovrebbe verificare le voci manuali su bit a bit operatori e assicurarsi si sa come un bit per bit AND, OR e sinistra / destra opere di turno.

Un campo di bit non è altro che un valore intero. Supponiamo dimensioni del nostro campo di bit è fisso e un solo byte. I computer funzionano con i numeri binari, per cui se il valore del nostro numero è 29, ci troveremo a trovare 0001 1101 nella memoria.

Utilizzo AND bit a bit (&) e OR bit a bit (|) si può leggere e impostare ogni bit del numero individualmente. Entrambi prendono due interi come input ed eseguire un AND / OR su ogni bit individualmente.

Per leggere fuori il primo bit del numero, si potrebbe fare qualcosa di simile:

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

Come si può vedere è necessario un numero speciale in cui viene impostato solo il bit Siamo in interessata, che è la cosiddetta "maschera di bit". Nel nostro caso si tratta di 1. Per leggere il secondo bit dobbiamo "spingere" l'uno nella maschera di bit una cifra a sinistra. Possiamo farlo con l'operatore spostamento a sinistra ($number << 1) oppure moltiplicando per due il nostro.

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

È possibile farlo per ogni bit nel nostro numero. Il binario AND del nostro numero e conduttori maschera di bit sia a zero, il che significa che il bit non è stato "set", oppure ad un intero diverso da zero, il che significa che il bit è stato impostato.

Se si vuole un insieme di bit, è possibile utilizzare OR bit a bit:

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

Tuttavia, dovrete andare un modo diverso quando si vuole "chiaro" un po '.

Un approccio più generale potrebbe essere:

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

In un primo momento potrebbe sembrare un po 'criptico, ma in realtà è abbastanza facile.

A questo punto probabilmente scoperto che i campi di bit sono piuttosto una tecnica di basso livello. Ecco perché vi consiglio di non usarli all'interno di PHP o database .. Se si vuole avere un po 'di bandiere è probabilmente ok, ma per tutto il resto davvero non hanno bisogno.

La classe che hai postato sembra un po 'speciale per me. Ad esempio, le cose come sono ... ? true : false veery cattiva pratica. Se si desidera utilizzare campi di bit, si è probabilmente meglio definire alcune costanti e utilizza il metodo sopra descritto. Non è difficile a venire con una semplice classe.

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

Non ho provato nessuno pezzo di codice di questa risposta, ma dovrebbe iniziare anche se non funziona out of the box.

Si noti che si è limitato a 32 valori per campo di bit.

Altri suggerimenti

Ecco come definire 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);

Per verificare se un bit è presente (in questo caso in un argomento della funzione), usa il bit operatore AND.

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

Questo funziona perché quando si o due byte (per esempio, 00000001 e 00010000), si ottiene i due insieme: 00010001. Se voi e il risultato e la maschera originale (00010001 e dire, 00000001), si ottiene una maschera, se è presente (in questo caso 00000001). In caso contrario, si ottiene zero.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top