Вопрос

Биты и Bitmask - это то, что я изо всех сил пытался понять, но я хотел бы научиться использовать их для настройки и тому подобного в PHP.

Я наконец -то нашел класс, который утверждает, что делает именно это, и, как я могу сказать, это, кажется, работает, но я не уверен, лучший ли это способ сделать это. Я опубликую файл класса с примером кода ниже, чтобы показать его в рабочем состоянии.

Пожалуйста, если у вас есть опыт, скажите мне, можно ли его улучшить, для производительности или чего -то еще. Я действительно хочу узнать это, и я читал об этом, но мне до сих пор трудно понять.

Класс...

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

Как мне установить/сохранить разрешения в качестве значения 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.
?>

Пример принятия значения битмаски и возврата истинного/false для каждого элемента массива на основе значения бита ....

<?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>';
    }
?>
Это было полезно?

Решение

Бит -поля являются очень удобным и эффективным инструментом для работы с флагами или любым набором логических значений в целом.

Чтобы понять их, вам сначала нужно знать, как работают бинарные цифры. После этого вы должны проверить ручные записи на Побитовые операторы И убедитесь, что вы знаете, как работает кусочек и или левый/правый сдвиг.

Немного поля - не что иное, как целочисленное значение. Давайте предположим, что размер нашего бита фиксирован и только один байт. Компьютеры работают с двоичными числами, поэтому, если значение нашего номера 29, ты действительно найдешь 0001 1101 в памяти.

Используя бить и (&) и побито или (|) Вы можете прочитать и установить каждый бит числа индивидуально. Они оба принимают два целых числа в качестве ввода и выполняют и/или на каждом бите индивидуально.

Чтобы прочитать самый первый кусочек вашего номера, вы можете сделать что -то вроде этого:

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

Как вы можете видеть, вам нужен специальный номер, в котором установлен только тот бит, который нас интересует, это так называемая «бит -маска». В нашем случае это 1. Анкет Чтобы прочитать второй бит, мы должны «подтолкнуть» тот, который в Bitmask One Digit влево. Мы можем сделать это с оператором левой смены ($number << 1) или умножая наши на два.

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

Вы можете сделать это на каждый бит в нашем номере. Бинарник и наш номер и битовая маска приводят либо к нулю, что означает, что бит не был «установлен», либо в ненулевое целое число, что означает, что бит был установлен.

Если вы хотите установить один из битов, вы можете использовать бить или:

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

Тем не менее, вам придется пойти по -другому, когда вы хотите немного "прояснить".

Более общим подходом будет:

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

Сначала это может выглядеть немного загадочно, но на самом деле это довольно просто.

К настоящему времени вы, вероятно, обнаружили, что эти битовые поля-довольно низкоуровневая техника. Вот почему я рекомендую не использовать их в PHP или базах данных.

Класс, который вы опубликовали, выглядит для меня немного особенным. Например, такие вещи, как ... ? true : false это плохая практика. Если вы хотите использовать битовые поля, вам, вероятно, лучше определить некоторые константы и использовать метод, описанный выше. Нетрудно придумать простой класс.

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

Я не пробовал ни одного кода этого ответа, но он должен запустить вас, даже если он не работает из коробки.

Обратите внимание, что вы ограничены 32 значения за бит поля.

Другие советы

Вот как определить битмаски.

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

Чтобы проверить, присутствует ли Bitmask (в данном случае в аргументе функции), используйте бить и оператор.

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

Это работает, потому что когда вы или два байта (скажем, 00000001 а также 00010000), вы собрать их вместе: 00010001. Анкет Если вы и результат и оригинальная маска (00010001 и скажи, 00000001), вы получаете маску, если она присутствует (в этом случае 00000001) В противном случае вы получаете ноль.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top