Wie implementiere ich eine Bitmaske in PHP?

76

Ich bin mir nicht sicher, ob Bitmaske der richtige Begriff ist. Lassen Sie mich erklären:

In PHP kann die error_reportingFunktion auf mehrere Arten aufgerufen werden:

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

Ich habe den Begriff Bitmaske von der php.net Seite hier bekommen

Auf jeden Fall habe ich eine SIMPLE-Methode implementiert, lsdie den Inhalt eines Verzeichnisses zurückgibt.

Diese Funktion benötigt 3 Argumente ... ($ include_hidden = false, $ return_absolute = false, $ ext = false)

Wenn ich die Funktion aufrufe, lege ich fest, wie ich die Ergebnisse erzielen möchte. Ob ich möchte, dass die Ergebnisse versteckte Verzeichnisse zurückgeben, ob ich nur Basisnamen möchte usw.

Wenn ich also die Funktion aufrufe, schreibe ich

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

Ich dachte, es wäre viel besser lesbar, wenn ich nur markieren könnte, wie ich die Daten zurückgeben möchte?

so etwas wie:

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

etc...

Wie würde ich dies implementieren, um zu testen, welche Flags aufgerufen wurden?

AlexMorley-Finch
quelle

Antworten:

149

Eigentlich ist es ganz einfach. Zuerst ein bisschen Code, um zu demonstrieren, wie er implementiert werden kann. Wenn Sie nichts darüber verstehen, was dieser Code tut oder wie er funktioniert, können Sie in den Kommentaren weitere Fragen stellen:

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

Demo


Da die Flags Ganzzahlen sind, definieren Sie auf einer 32-Bit-Plattform bis zu 32 Flags. Auf einer 64-Bit-Plattform ist es 64. Es ist auch möglich, die Flags als Zeichenfolgen zu definieren. In diesem Fall ist die Anzahl der verfügbaren Flags mehr oder weniger unendlich (natürlich innerhalb der Grenzen der Systemressourcen). So funktioniert es in Binärform (der Einfachheit halber auf 8-Bit-Ganzzahlen reduziert).

FLAG_1
Dec:    1
Binary: 00000001

FLAG_2
Dec:    2
Binary: 00000010

FLAG_3
Dec:    4
Binary: 00000100

// And so on...

Wenn Sie die Flags kombinieren, um sie an die Funktion zu übergeben, ODER Sie sie zusammen. Werfen wir einen Blick darauf, was passiert, wenn wir vorbeikommenFLAG_1 | FLAG_3

  00000001
| 00000100
= 00000101

Und wenn Sie sehen möchten, welche Flags gesetzt wurden, UND die Bitmaske mit dem Flag. Nehmen wir also das obige Ergebnis und sehen, ob FLAG_3es eingestellt wurde:

  00000101
& 00000100
= 00000100

... wir bekommen den Wert des Flags zurück, eine Ganzzahl ungleich Null - aber wenn wir sehen, ob FLAG_2gesetzt wurde:

  00000101
& 00000010
= 00000000

... wir bekommen Null. Dies bedeutet, dass Sie das Ergebnis der AND-Operation einfach als Booleschen Wert auswerten können, wenn Sie überprüfen, ob der Wert übergeben wurde.

DaveRandom
quelle
9
Grundlegende bitweise Operationen sind also: $flags & FLAG_1- Überprüfen Sie, ob FLAG_1 gesetzt ist, $flags | FLAG_1- Setzen Sie FLAG_1, $flags & ~FLAG_1- Deaktivieren von FLAG_1, ~$flags- Invertieren von Flags
Konstantin Pereiaslov
7
Ich kenne PHP nicht und werde es wahrscheinlich nie lernen, aber ich bin froh, dass ich auf diese Frage gestoßen bin - Ihre Erklärung kann jedem helfen, Bit-Mapping in jeder Sprache zu implementieren :)
Chris Cirefice
6
Ich würde empfehlen, die Dezimalstellen der Potenz 2 als bitweise Verschiebungsoperation von 1 zu definieren. define('FLAG_1', 1<<0); define('FLAG_2', 1<<2); define('FLAG_3', 1<<3); define('FLAG_4', 1<<4); Dies geschieht normalerweise in C
AmitP am
6
@AmitP Es gibt einige (kleinere) Probleme mit diesem Ansatz in PHP. 1) Derzeit unterstützt das constSchlüsselwort keine Ausdrücke, selbst wenn das Ergebnis konstant ist (dh const FLAG_1 = 1 << 0;ein Analysefehler ist) - offensichtlich ist dies kein Problem mit define()2). In C wird ein konstanter Ausdruck wie const int FLAG_1 = 1 << 0;beim Kompilieren aufgelöst und der tatsächliche kompiliert Der Wert ist das Ergebnis des Ausdrucks, während dies in PHP jedes Mal ausgewertet wird, wenn die mikroskopische Leistung beeinträchtigt wird. Keines dieser Probleme ist ein guter Grund, die von Ihnen vorgeschlagene besser lesbare Version zu vermeiden.
Dave Random
1
@Benjamin hat nichts mit dieser Frage zu tun, aber eine nützliche Sache, die Sie mit const scalar exprs machen können: const IS_WINDOWS = \PHP_OS & "\xDF\xDF\xDF" === 'WIN';ist ein Äquivalent zur Kompilierungszeit der sehr verbreiteten stripos(\PHP_OS, 'WIN') === 0Redewendung. Wie es passiert, ist dies derzeit sinnlos ( PHP_OSist derzeit immer 'WINNT'auf allen Windows-Versionen
verfügbar
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..

Sie können dann in Ihrer lsFunktion nach einzelnen Flags suchen :

if( $flags & INCLUDE_HIDDEN ) { //<-- note just a single &, bitwise and
    //$flags have INCLUDE_HIDDEN
}
Esailija
quelle
7
Kennen Sie einen Vorteil bei der Verwendung von Hex-Zahlen anstelle von ganzen Zahlen? Nur neugierig :)
AlexMorley-Finch
@ AlexMorley-Finch sieht aus wie es rein bevorzugt, die Verwendung von Dezimalstellen oder Hex scheint auch die Ausführungszeit überhaupt nicht zu ändern
Brian Leishman
2

Die anderen haben gute Vorschläge gemacht, aber heutzutage ist es viel üblicher, assoziative Arrays anstelle von Bitmasken zu übergeben. Es ist viel besser lesbar und ermöglicht es Ihnen, andere Variablen als nur wahre / falsche Werte zu übergeben. Etwas wie das:

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

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

    if ($options['includeHidden']) {
        ...
    }
    ...
}
Simon East
quelle
3
Früher habe ich das auch so gemacht, aber andere Begleiter beschweren sich, dass ihre IDEs keine "Typhinweise" enthalten, keine richtigen Dokumentkommentare und die Funktion keine Fehler auslöst, wenn die erforderlichen Parameter nicht übergeben werden . Idealerweise würden "Named Params" all diese Probleme lösen, wenn sie in PHP
Timo Huovinen
Hallo @TimoHuovinen - danke für den Kommentar. Es ist einfach, Dokumentkommentare hinzuzufügen, in denen die Optionen und Standardeinstellungen aufgeführt sind, bei Bedarf eine Ausnahme auszulösen und den Parameter standardmäßig auf ein Array festzulegen, das Typhinweise ermöglicht. Ist das in Ordnung für dich?
Simon East
Oh ich verstehe. Die Symfony OptionsResolver-Klasse geht noch einen Schritt weiter und bietet noch mehr Funktionen. Cool.
Simon East
" Es ist einfach, Dokumentkommentare hinzuzufügen, in denen die Optionen und Standardeinstellungen aufgeführt sind. " Ja, Sie können sie wie Twig hinzufügen, aber es ist immer noch eine Problemumgehung. " und setzen Sie den Parameter standardmäßig auf ein Array, das Typhinweise zulässt " Wie würden Sie dies tun? Ich meinte Hinweise auf IDE-Typen und Code-Vervollständigung, wobei der Editor Ihnen sagt, welche Parameter die Funktion akzeptieren kann. (Denken Sie daran, ich bin auch für die Verwendung von Arrays für Optionen, nur dass ich herausfinden muss, wie man die Gefährten glücklich macht)
Timo Huovinen
Das Problem mit diesem Ansatz ist, dass Sie am Ende eine große Anzahl von if else-Anweisungen haben werden
numediaweb