Как протестировать входные параметры побитовой функции PHP

Иногда в программировании они позволяют связать параметры в одну входную переменную функции, например вторую входную переменную ниже:

define('FLAGA',40);
define('FLAGB',10);
define('FLAGC',3);
function foo($sFile, $vFlags) {
  // do something
}
foo('test.txt',FLAGA | FLAGB | FLAGC);

PHP называет этот символ одной вертикальной черты (|) побитовым оператором OR. Как мне теперь добавить что-то внутри foo(), чтобы проверить $vFlags, чтобы увидеть, какие флаги были установлены?


person Volomike    schedule 19.12.2014    source источник


Ответы (3)


Я думаю, вы обнаружите, что подобные флаги обычно определяются как степени двойки, например:

define('FLAGA',1);
define('FLAGB',2);
define('FLAGC',4); /* then 8, 16, 32, etc... */

Как вы правильно заметили, их можно комбинировать с помощью побитового оператора ИЛИ:

foo('test.txt',FLAGA | FLAGB | FLAGC);

Чтобы проверить эти флаги внутри вашей функции, вам нужно использовать побитовый оператор AND следующим образом:

function foo($sFile, $vFlags) {
  if ($vFlags & FLAGA) {
    // FLAGA was set
  }
  if ($vFlags & FLAGB) {
    // FLAGB was set
  }
  //// etc...
}
person r3mainer    schedule 19.12.2014

Параметр flags будет называться битовой маской. Один байт содержит 8 битов, которые либо установлены, либо не установлены. Вы просто придаете каждому биту собственное значение; если он установлен, это означает да для этого конкретного бита, иначе нет.

Поэтому вам нужно начать с определения ваших флагов с правильными значениями, которые устанавливают правильные биты; просто произвольные числа не будут правильно сочетаться друг с другом:

define('FLAGA', 1);  // 00000001
define('FLAGB', 2);  // 00000010
define('FLAGC', 4);  // 00000100
define('FLAGD', 8);  // 00001000

Учитывая вышеизложенное, FLAGB | FLAGD создает битовую маску с установленными вторым и четвертым битами (00001010). Для этого вам нужно немного освоиться с преобразованием между основанием 2 (двоичное) и основанием 10 (десятичное).

Чтобы проверить это, вы используете &:

$flags = FLAGB | FLAGD;

if ($flags & FLAGA) {
    echo 'flag A is set';
}

if ($flags & FLAGB) {
    echo 'flag B is set';
}

..
person deceze♦    schedule 19.12.2014

Вам нужен побитовый оператор AND &:

define('FLAGA',40);
define('FLAGB',10);
define('FLAGC',3);

function foo($sFile, $vFlags) {
  if ($vFlags & FLAGA) {
    echo "FLAGA is set\n";
  }
  if ($vFlags & FLAGB) {
    echo "FLAGB is set\n";
  }
  if ($vFlags & FLAGC) {
    echo "FLAGC is set\n";
  }
}
foo('test.txt',FLAGA | FLAGB | FLAGC);

ДЕМО

Тем не менее, побитовые операции обязательно работают в терминах двоичного кода, где каждый бит представляет степень числа 2. Таким образом, вы, как правило, захотите определить флаги в степени числа 2, как в

define('FLAGA',1);
define('FLAGB',2);
define('FLAGC',4);
define('FLAGD',8); // etc.

В противном случае представьте себе такой сценарий:

define('FLAGA',8);
define('FLAGB',32);
define('FLAGC',40);

Если у вас есть значение 40 для $vFlags, вы не можете сказать, какие флаги установлены; это может быть любое из следующего:

FLAGA & FLAGB
FLAGA & FLAGB & FLAGC
FLAGA & FLAGC
FLAGB & FLAGC
FLAGC
person elixenide    schedule 19.12.2014
comment
Помните о приведении типов в Php при модульном тестировании :) Вычисленное значение ($vFlags & FLAGC) дает либо 0 (ноль), либо ненулевое целое число. Это будет оцениваться так же, как логическое значение, но если вы выполняете модульное тестирование и утверждаете логическое значение, утверждение не будет выполнено, несмотря ни на что. $test = $vFlags & FLAGC; assertTrue($test) // this will fail because $test is actually an integer, not a boolean assertTrue($test == FLAGC) // this will work because now there is an actual boolean value if($test){... // this will work because of type coersion - person andkrup; 17.02.2021