Як реалізувати бітову маску в php?


76

Я не впевнений, що бітмаска - це правильний термін. Дозволь пояснити:

У php error_reportingфункцію можна викликати кількома способами:

// Report simple running errors
error_reporting(E_ERROR | E_WARNING | E_PARSE);

// Reporting E_NOTICE can be good too (to report uninitialized
// variables or catch variable name misspellings ...)
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);

// Report all errors except E_NOTICE
// This is the default value set in php.ini
error_reporting(E_ALL ^ E_NOTICE);

Я отримав термін bitmask зі сторінки php.net тут

У будь-якому випадку суть цього полягає в тому, що я реалізував SIMPLE метод, lsякий викликається, який повертає вміст каталогу.

Ця функція приймає 3 аргументи ... ($ include_hidden = false, $ return_absolute = false, $ ext = false)

Тому, коли я викликаю функцію, я встановлюю, як я хочу результати. Чи хочу я, щоб результати повертали приховані каталоги, чи хочу я лише базові імена тощо.

тому, коли я викликаю функцію, я пишу

ls(true, false, true)
ls(false, false, true)
ls(true, true, true)
etc...

Я думав, що це було б набагато читабельніше, якби я міг просто позначити, як я хочу повернення даних?

так щось на зразок:

ls( INCLUDE_HIDDEN | HIDE_EXTS );
ls( SHOW_ABSOLUTE_PATHS | HIDE_EXTS );

тощо ...

Як я можу реалізувати це з точки зору тестування, які прапорці були викликані?

Відповіді:


149

Насправді це досить просто. Спочатку трохи коду, щоб продемонструвати, як його можна реалізувати. Якщо ви нічого не розумієте в тому, що робить цей код або як він працює, сміливо задавайте додаткові запитання в коментарях:

const FLAG_1 = 0b0001; // 1
const FLAG_2 = 0b0010; // 2
const FLAG_3 = 0b0100; // 4
const FLAG_4 = 0b1000; // 8
// Can you see the pattern? ;-)

function show_flags ($flags) {
  if ($flags & FLAG_1) {
    echo "You passed flag 1!<br>\n";
  }
  if ($flags & FLAG_2) {
    echo "You passed flag 2!<br>\n";
  }
  if ($flags & FLAG_3) {
    echo "You passed flag 3!<br>\n";
  }
  if ($flags & FLAG_4) {
    echo "You passed flag 4!<br>\n";
  }
}

show_flags(FLAG_1 | FLAG_3);

Демо


Оскільки прапори є цілими числами, на 32-бітовій платформі ви визначаєте до 32 прапорів. На 64-бітній платформі це 64. Також можна визначити прапори як рядки, і в цьому випадку кількість доступних прапорів є більш-менш нескінченною (звичайно, в межах системних ресурсів). Ось як це працює в двійковому (для простоти скоротити до 8-бітових цілих чисел).

FLAG_1
Dec:    1
Binary: 00000001

FLAG_2
Dec:    2
Binary: 00000010

FLAG_3
Dec:    4
Binary: 00000100

// And so on...

Коли ви поєднуєте прапори, щоб передати їх функції, ви АБО їх разом. Давайте подивимося, що відбувається, коли ми проїжджаємоFLAG_1 | FLAG_3

  00000001
| 00000100
= 00000101

І коли ви хочете побачити, які прапори були встановлені, ви І бітова маска з прапором. Отже, давайте візьмемо результат вище і подивимось, чи FLAG_3встановлено:

  00000101
& 00000100
= 00000100

... ми отримуємо значення прапора назад, ненульове ціле число, але якщо ми бачимо, чи FLAG_2було встановлено:

  00000101
& 00000010
= 00000000

... отримуємо нуль. Це означає, що ви можете просто оцінити результат операції І як логічну, перевіряючи, чи було передано значення.


9
Отже, основними побітовими операціями є: $flags & FLAG_1- перевірити, чи встановлено FLAG_1, $flags | FLAG_1- встановити FLAG_1, $flags & ~FLAG_1- скасувати FLAG_1, ~$flags- інвертувати прапори
Костянтин

7
Я не знаю PHP і, швидше за все, ніколи його не вивчу, але я радий, що натрапив на це запитання - ваше пояснення може допомогти кожному здійснити відображення бітів будь-якою мовою :)
Кріс Сірефіс

6
Я б рекомендував визначити десяткові define('FLAG_1', 1<<0); define('FLAG_2', 1<<2); define('FLAG_3', 1<<3); define('FLAG_4', 1<<4);
знаки

6
@AmitP У PHP є кілька (незначних) проблем з цим підходом. 1) в даний час constключове слово не підтримує вирази, навіть якщо результат є постійним (тобто const FLAG_1 = 1 << 0;є помилкою синтаксичного аналізу) - очевидно, це не проблема з define()2) у C постійний вираз типу const int FLAG_1 = 1 << 0;буде вирішено під час компіляції, а фактичний скомпільований значення буде результатом виразу, тоді як у PHP це обчислюється щоразу, коли відбувається мікроскопічне виконання. Жодна з цих проблем не є вагомою причиною, щоб уникнути більш читабельної версії, яку ви пропонуєте.
DaveRandom

1
@Benjamin не пов'язаний з цим питанням, але корисно, що ви можете зробити з const skalar exprs: const IS_WINDOWS = \PHP_OS & "\xDF\xDF\xDF" === 'WIN';це еквівалент під час компіляції дуже поширеної stripos(\PHP_OS, 'WIN') === 0ідіоми. Це трапляється в даний час безглуздо ( PHP_OSнаразі це завжди 'WINNT'у всіх версіях Windows), але, мабуть, це
надійно

17
define( "INCLUDE_HIDDEN", 0x1 );
define( "HIDE_EXTS", 0x2 );
define( "SHOW_ABSOLUTE_PATHS", 0x4 );
//And so on, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800 etc..

Потім ви можете перевірити наявність окремих прапорів у вашій lsфункції:

if( $flags & INCLUDE_HIDDEN ) { //<-- note just a single &, bitwise and
    //$flags have INCLUDE_HIDDEN
}

7
Чи знаєте ви про перевагу використання шістнадцяткових чисел замість цілих чисел? Просто цікаво :)
AlexMorley-Finch

@ AlexMorley-Finch схоже, що це суто перевага, використання десяткових знаків або шістнадцяткових символів, здається, теж не змінює час виконання
Брайан Лейшман,

2

Інші запропонували хороші пропозиції, але в наш час набагато частіше передають асоціативні масиви замість бітових масок. Він набагато читабельніший і дозволяє передавати інші змінні, крім просто істинних / хибних значень. Щось на зразок цього:

myFunction(['includeHidden' => true, 'fileExts' => false, 'string' => 'Xyz']);

function myFunction($options) {
    // Set the default options
    $options += [
        'includeHidden' => false,
        'fileExts' => true,
        'string' => 'Abc',
    ];

    if ($options['includeHidden']) {
        ...
    }
    ...
}

3
Раніше я теж робив це таким чином, але товариші-партнери скаржаться, що в їх IDE немає "натяку на тип", немає належних коментарів до документації, і функція не видає помилок, коли не передаються необхідні параметри . В ідеалі "названі параметри" вирішили б усі ці проблеми, якби вони існували у php
Тимо Хуовінен

Привіт @TimoHuovinen - дякую за коментар. Легко додати коментарі до документа, які окреслюють параметри та за замовчуванням, створюють виняток, якщо потрібно, і роблять параметр типовим для масиву, що дозволяє натякати на тип. Це буде працювати для вас?
Саймон Іст

О Я бачу. Клас Symfony OptionsResolver робить крок далі, пропонуючи ще більше можливостей. Класно.
Саймон Іст

" Легко додати коментарі до документації, які окреслюють параметри та за замовчуванням " так, ви можете додавати їх так, як це робить гілочка, але це все одно обхідне рішення. " і встановити параметр за замовчуванням для масиву, що дозволяє натякати на тип ", як би ви це зробили? Я мав на увазі підказку типу IDE та заповнення коду, де редактор повідомляє вам, які параметри може прийняти функція. (Майте на увазі, я також за те, щоб використовувати масиви для опцій, просто для того, щоб мені було зрозуміти, як підтримувати супутників щасливими)
Тимо Хуовінен

Проблема такого підходу полягає в тому, що у вас вийде величезна маса заяв if else
numediaweb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.