Frage

Ich habe versucht, die beste Möglichkeit zu finden, Bitmaske oder Bitfields in PHP seit langem für verschiedene Bereiche meiner Anwendung für verschiedene Benutzereinstellungen und -berechtigungen zu verwenden. Am weitesten bin ich so weit von einer Klasse gekommen, die von SVens im Stapelüberlaufpost beigetragen hat Bitmaske in PHP für Einstellungen?. Ich habe es unten leicht modifiziert, es geändert, um Klassenkonstanten zu verwenden, anstatt zu definieren und sicherzustellen, dass die GET -Methode nur ein int übergeben wird. Ich habe auch einen Beispielcode, um die Funktionalität der Klasse unten zu testen.

Ich suche nach Vorschlägen/Code, um diese Klasse noch mehr zu verbessern, damit sie in meiner Anwendung für Einstellungen und in einigen Fällen Benutzerberechtigungen verwendet werden kann.

Beantwortet im Kommentar unten von McRumley

Außerdem habe ich eine Frage zur Nummerierung meiner Konstanten. In anderen Klassen und Code -Beispielen für diesen Typ werden Dinge in Befugnissen von 2 aufgelistet. Es scheint jedoch genauso zu funktionieren, soweit ich das beurteilen kann, auch wenn ich meine Konstanten 1,2,3,4,6 zähle Anstelle von 1, 2, 4, 8, 16 usw. Kann jemand auch klarstellen, ob ich meine Konstanten ändern sollte?


Einige Ideen ... Ich möchte wirklich einen Weg finden, um diese Klasse zu erweitern, damit sie mit anderen Klassen einfach zu verwenden ist. Nehmen wir an, ich habe eine User Klasse und a Messages Klasse. Beide User und Messages Die Klasse erweitert diese Klasse und kann die Bitmaske für ihre Einstellungen/Berechtigungen verwenden (zusammen mit anderen Klassen später). Vielleicht sollten die aktuellen Klassenkonstanten geändert werden, damit sie in oder eine andere Option übergeben werden können? Ich müsste in anderen Teilen der Site/des Skripts wirklich nicht lieber ('Perm_read', 1) nicht definieren ('Perm_read', 1) und möchte es etwas verkapselt, aber auch flexibel; Ich bin offen für Ideen. Ich möchte, dass dies felsig und flexibel ist, wie ich es mit mehreren anderen Klassen für Einstellungen oder Berechtigungen verwenden soll. Möglicherweise sollte eine Art Array verwendet werden? @Svens aus meiner vorherigen Frage, die oben verlinkt wurde, veröffentlichte einen Kommentar mit "Implementieren Sie einige automatische Getters/Setzer oder ArrayAccess für zusätzliche Awesomness. - SVENS" Was denkst du auch über so etwas?

Wenn möglich Beispiel -Quellcode einschließen, bitte.

<?php

class BitField {

    const PERM_READ = 0;
    const PERM_WRITE = 1;
    const PERM_ADMIN = 2;
    const PERM_ADMIN2 = 3;
    const PERM_ADMIN3 = 4;

    private $value;

    public function __construct($value=0) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
        if (is_int($n)) {
            return ($this->value & (1 << $n)) != 0;
        }else{
            return 0;
        }
    }

    public function set($n, $new=true) {
        $this->value = ($this->value & ~(1 << $n)) | ($new << $n);
    }

    public function clear($n) {
        $this->set($n, false);
    }
}
?>

Beispiel Verwendung ...

<?php
    $user_permissions = 0; //This value will come from MySQL or Sessions
    $bf = new BitField($user_permissions);

    // Turn these permission to on/true
    $bf->set($bf::PERM_READ);
    $bf->set($bf::PERM_WRITE);
    $bf->set($bf::PERM_ADMIN);
    $bf->set($bf::PERM_ADMIN2);
    $bf->set($bf::PERM_ADMIN3);

    // Turn permission PERM_ADMIN2 to off/false
    $bf->clear($bf::PERM_ADMIN2); // sets $bf::PERM_ADMIN2 bit to false

    // Get the total bit value
    $user_permissions = $bf->getValue();

    echo '<br> Bitmask value = ' .$user_permissions. '<br>Test values on/off based off the bitmask value<br>' ;

    // Check if permission PERM_READ is on/true
    if ($bf->get($bf::PERM_READ)) {
        // can read
        echo 'can read is ON<br>';
    }

    if ($bf->get($bf::PERM_WRITE)) {
        // can write
        echo 'can write is ON<br>';
    }

    if ($bf->get($bf::PERM_ADMIN)) {
        // is admin
        echo 'admin is ON<br>';
    }

    if ($bf->get($bf::PERM_ADMIN2)) {
        // is admin 2
        echo 'admin 2 is ON<br>';
    }

    if ($bf->get($bf::PERM_ADMIN3)) {
        // is admin 3
        echo 'admin 3 is ON<br>';
    }
?>
War es hilfreich?

Lösung

Andere haben dabei geholfen, das Stück Maskieren weiter zu erklären, also werde ich mich darauf konzentrieren

"Ich mag die Idee, es erweiterbarer/generischer zu machen, sodass verschiedene Klassen dies erweitern und für verschiedene Abschnitte verwenden können. Ich bin mir einfach nicht sicher, wie es geht."

Aus Ihrem Kommentar zu @Charles 'Beitrag.

Wie Charles zu Recht sagte, können Sie die Funktionalität Ihrer Bitmask-Klasse wiederverwenden, indem Sie die Funktionalität in eine abstrakte Klasse extrahieren und die tatsächlichen "Einstellungen" (in diesem Fall Berechtigungen) in abgeleitete konkrete Klassen einfügen.

Zum Beispiel:

<?php

abstract class BitField {

    private $value;

    public function __construct($value=0) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
        if (is_int($n)) {
            return ($this->value & (1 << $n)) != 0;
        }else{
            return 0;
        }
    }

    public function set($n, $new=true) {
        $this->value = ($this->value & ~(1 << $n)) | ($new << $n);
    }

    public function clear($n) {
        $this->set($n, false);
    }
}

class UserPermissions_BitField extends BitField
{
    const PERM_READ = 0;
    const PERM_WRITE = 1;
    const PERM_ADMIN = 2;
    const PERM_ADMIN2 = 3;
    const PERM_ADMIN3 = 4;
}

class UserPrivacySettings_BitField extends BitField
{
    const PRIVACY_TOTAL = 0;
    const PRIVACY_EMAIL = 1;
    const PRIVACY_NAME = 2;
    const PRIVACY_ADDRESS = 3;
    const PRIVACY_PHONE = 4;
}

Und dann wird die Verwendung einfach:

<?php
$user_permissions = 0; //This value will come from MySQL or Sessions
$bf = new UserPermissions_BitField($user_permissions); 

// turn these permission to on/true
$bf->set($bf::PERM_READ);
$bf->set($bf::PERM_WRITE);
$bf->set($bf::PERM_ADMIN);
$bf->set($bf::PERM_ADMIN2);
$bf->set($bf::PERM_ADMIN3);

Um die Datenschutzeinstellungen festzulegen, werden Sie einfach ein neues Objekt von UserPrivacysetts_bitfield instanziiert und verwenden dies stattdessen.

Auf diese Weise können Sie so viele verschiedene Sätze von Bitfield -Objekten erstellen, da Ihre Anwendung einfach eine Reihe von Konstanten definiert, die Ihre Optionen darstellen.

Ich hoffe, das ist für Sie von Nutzen, aber wenn nicht, wird es vielleicht von jemand anderem von Nutzen sein, der dies liest.

Andere Tipps

In anderen Klassen und Code -Beispielen für diesen Typ werden Dinge in Kräften von 2 aufgeführt, aber es scheint genauso zu funktionieren, soweit ich das beurteilen kann, auch wenn ich meine Konstanten 1,2,3,4,5,6 statt dauerte 1,2,4,8,16 usw. Kann jemand auch klarstellen, ob ich meine Konstanten ändern sollte?

Sie müssen nicht, weil sich der Code bereits darum kümmert. Diese Erklärung wird ein bisschen Kreisverkehr sein.

Der Grund, warum Bit -Felder als behandelt werden Kräfte von zwei ist, dass jede Kraft von zwei zwei durch ein einzelnes Bit dargestellt wird. Diese einzelnen Bits können zusammen in einer einzigen Ganzzahl zusammengeführt werden, die weitergegeben werden kann. In Sprachen auf niedrigerer Ebene ist es "einfacher", eine Zahl zu übergeben als beispielsweise eine Struktur.

Lassen Sie mich zeigen, wie das funktioniert. Lassen Sie uns einige Berechtigungen mit den Kräften von zwei Personen einrichten:

define('PERM_NONE', 0);
define('PERM_READ', 1);
define('PERM_WRITE', 2);
define('PERM_EDIT', 4);
define('PERM_DELETE', 8);
define('PERM_SUPER', 16);

Überprüfen wir die Bitwerte dieser Berechtigungen in der interaktiven PHP -Eingabeaufforderung:

php > printf('%08b', PERM_SUPER);
00010000
php > printf('%08b', PERM_DELETE);
00001000
php > printf('%08b', PERM_EDIT);
00000100
php > printf('%08b', PERM_WRITE);
00000010
php > printf('%08b', PERM_READ);
00000001
php > printf('%08b', PERM_NONE);
00000000

Lassen Sie uns nun einen Benutzer erstellen, der Lesezugriffs- und Schreibzugriff hat.

php > printf('%08b', PERM_READ | PERM_WRITE);
00000011

Oder ein Benutzer, der lesen, schreiben, löschen, aber nicht bearbeiten kann:

php > printf('%08b', PERM_READ | PERM_WRITE | PERM_DELETE);
00001011

Wir können die Berechtigung mit bitgewiehen und sicherstellen, dass das Ergebnis nicht Null ist:

php > $permission = PERM_READ | PERM_WRITE | PERM_DELETE;
php > var_dump($permission & PERM_WRITE); // This won't be zero.
int(2)
php > var_dump($permission & PERM_EDIT); // This will be zero.
int(0)

(Es ist erwähnenswert PERM_NONE & PERM_NONE ist 0 & 0, was Null ist. Die von mir erstellte "None" -Erlaubnis funktioniert hier nicht wirklich und kann sofort vergessen werden.)

Ihre Klasse macht etwas etwas anders, aber das Endergebnis ist identisch. Es verwendet ein Bit -Wechsel, um ein "On" -Bit zum linken Mal zu bewegen, wobei x die Anzahl der Erlaubnis ist. Tatsächlich, Dies erhöht 2 zur Macht des Wertes der Erlaubnis. Eine Demonstration:

php > echo BitField::PERM_ADMIN3;
4
php > echo pow(2, BitField::PERM_ADMIN3);
16
php > printf('%08b', pow(2, BitField::PERM_ADMIN3));
00010000
php > echo 1 << BitField::PERM_ADMIN3;
16
php > printf('%08b', 1 << BitField::PERM_ADMIN3);
00010000

Während diese Methoden sind effektiv Identisch würde ich argumentieren, dass einfaches Anding und Oring leichter zu lesen sind als das Xoring und das Bitverändern.

Ich suche nach Vorschlägen/Code, um diese Klasse noch mehr zu verbessern, damit sie in meiner App für Einstellungen und in einigen Fällen Benutzerberechtigungen verwendet werden kann.

Ich habe einen Vorschlag und eine Warnung.

Mein Vorschlag wäre, die Klasse zu machen abstrakt und keine Berechtigungen darin zu definieren. Erstellen Sie stattdessen Klassen, die daraus erben und ihre eigenen Berechtigungen definieren. Sie möchten nicht in Betracht ziehen, die gleiche Erlaubnis zu teilen Namen In nicht verwandten Bitfeldern, und das Präfix mit Klassennamen ist ziemlich gesund. Ich gehe davon aus, dass Sie das sowieso tun würden.

Meine Warnung ist einfach, aber schlimm: PHP kann eine Ganzzahl von größer als 31 Bit nicht zuverlässig darstellen. Tatsächlich kann es nur 63-Bit-Ganzzahlen darstellen, wenn es auf einem 64-Bit-System zusammengestellt wird. Dies bedeutet, dass Sie, wenn Sie Ihre Bewerbung an die breite Öffentlichkeit verteilen, sein werden beschränkt auf nicht mehr als 31 Berechtigungen Wenn Sie die integrierten Mathematikfunktionen verwenden möchten.

Die GMP -Erweiterung Beinhaltet bitweise Operationen, die auf beliebigen Zahlen der Länge funktionieren können.

Eine andere Option könnte sein Verwenden Sie Code aus dieser Antwort auf DIVE Ganzzahlen, was es Ihnen ermöglichen könnte, eine riesige Ganzzahl als Zeichenfolge darzustellen, obwohl bitweise Operationen auf das ... interessant sein könnten. (Sie können es auf Basis-2 herunterversetzen und dann am erwarteten Ort einen Substr-Check für String "1" oder "0" durchführen, aber das wird ein großer Leistungsstreifen.)

Hier ist mein Vorschlag:

<?php

class BitField {

    const PERM_READ = 1;
    const PERM_WRITE = 2;
    const PERM_ADMIN = 4;
    const PERM_ADMIN2 = 8;
    const PERM_ADMIN3 = 16;

    private $value;

    public function __construct($value=0) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }

    public function get($n) {
                return $this->value & $n;
    }

    public function set($n, $new=true) {
        $this->value |= $n;
    }

    public function clear($n) {
        $this->value &= ~$n;
    }

}
?>

Wie Sie sehen können, habe ich 1, 2, 4, 8 usw. (Befugnisse von 2) verwendet, um die Berechnungen zu vereinfachen. Wenn Sie eine Erlaubnis zu einem Bit zuordnen, das Sie haben:

0 0 0 0 0 0 0 1 = PERM_READ = 1
0 0 0 0 0 0 1 0 = PERM_WRITE = 2
0 0 0 0 0 1 0 0 = PERM_ADMIN = 4
etc...

Anschließend können Sie logische Operationen verwenden, beispielsweise haben Sie dies anfangs:

    0 0 0 0 0 0 0 1 = PERM_READ = 1

Wenn Sie zum Schreiben Berechtigungen hinzufügen möchten, müssen Sie nur den Bitgewise oder den Operator verwenden:

    0 0 0 0 0 0 0 1 = PERM_READ = 1
OR  0 0 0 0 0 0 1 0 = PERM_WRITE = 2
=   0 0 0 0 0 0 1 1 = both bits enabled R & W

Um ein Bit zu entfernen, müssen Sie $ Value & ~ $ Bit verwenden, z. B. das Schreibbit entfernen:

    0 0 0 0 0 0 1 1 = both bits enabled R & W
AND 1 1 1 1 1 1 0 1 = Bitwise negated PERM_WRITE
=   0 0 0 0 0 0 0 1 = result, only the R bit

Wenn Sie schließlich testen möchten, ob ein Bit der Operation aktiviert ist, und den Wert $ gegenüber dem Testen von Perm_xxx:

    0 0 0 0 0 0 1 1 = both bits enabled R & W
AND 0 0 0 0 0 0 1 0 = Want to test PERM_WRITE
=   0 0 0 0 0 0 1 0 = result

Wenn das Ergebnis nicht Null ist, haben Sie die Erlaubnis, sonst nicht.

Der größte Fehler, den ich in Ihrer Klasse sehe, ist, dass Sie die Geschäftslogik in eine Datenstruktur mischen. Der Zweck Ihrer Klasse besteht darin, mehrere boolesche Werte (dh True/False) in einer einzigen Ganzzahl zu speichern. Das nicht haben in einer Klasse fertig zu werden, aber es ist praktisch. Und das ist sein Zweck.

Ich würde die Berechtigungsflaggen in der Klasse fallen lassen und sie in Ihre Geschäftslogikkurse auslagern.

u003CEDIT>

EIN Datenstruktur ist eine Entität, die eine Sache umgeht: Daten. Die Daten werden in keiner Weise interpretiert. EIN Stapel, Vor dem Beispiel ist eine Datenstruktur, in die Sie Dinge einfügen können und die Ihnen zuerst das letzte Element erhalten. Und hier ist der Punkt: Es ist egal, was Sie dort reinlegen: Ganzzahlen, Benutzerobjekte, Zeiger, Autos, Elefanten, die nur mit dem Speicher und Abrufen der Daten übertragen werden.

Geschäftslogik Andererseits definieren Sie, wie Ihre Datenstrukturen miteinander interagieren. Hier werden die Berechtigungen definiert, wo Sie feststellen, dass eine Person, die einen Blog -Beitrag erstellt hat, ihn bearbeiten kann und niemand sonst darf.

Dies sind zwei grundsätzlich unterschiedliche Ansichten Ihrer Anwendung und sollten nicht gemischt werden. Sie können Ihre Berechtigungen in einer anderen Datenstruktur speichern (als Array von Ganzzahlen oder a Hash-tabelle zum Beispiel von Berechtigungsobjekten - oder Jede andere Datenstruktur) und Sie können andere Flags in Ihrer Bitfield -Datenstruktur speichern (wie die Booleschen Einstellungen Ihrer Benutzer, wie "den Newsletter erhalten" oder "E -Mail -Adresse wurde überprüft").

u003C/EDIT>

Eine weitere Verbesserung ist die Verwendung von Hex -Werten für diese Konstanten. Dies wird sicherstellen, dass Ihr 16. Wert noch lesbar ist. (Ich würde lieber empfehlen, Bitverschiebungsoperatoren in den ständigen Deklarationen zu verwenden, was noch lesbarer ist, dies ist jedoch mit dem aktuellen PHP-Dolmetscher aus Leistungsgründen nicht möglich.)

class Permission {
    const READ     = 0x0001;
    const UPDATE   = 0x0002;
    const DELETE   = 0x0004;
    const COMMENT  = 0x0008;
    const GRANT    = 0x0010;
    const UNDELETE = 0x0020;
    const WHATEVER = 0x0040;
}

$permissions = new BitField();
$permissions->set(Permission::READ);
$permissions->set(Permission::WRITE);

u003CEDIT>

Die gleiche Klasse ohne Hexadezimalwerte ist weniger lesbar, insbesondere wenn Sie weitere Flags hinzufügen:

class Permission {
    const READ         = 1;
    const UPDATE       = 2;
    const DELETE       = 4;
    const COMMENT      = 8;
    const GRANT        = 16;
    const UNDELETE     = 32;
    const WHATEVER     = 64;
    const PERMISSION8  = 128;
    const PERMISSION9  = 256;
    const PERMISSION10 = 512;
    const PERMISSION11 = 1024;
    const PERMISSION12 = 2048;
    const PERMISSION13 = 4096;
    const PERMISSION14 = 8192;
    const PERMISSION15 = 16384;
    const PERMISSION16 = 32768; # the 16th value I mentioned above. Would
                                # you immediately recognize this value as 2^16?
                                # I wouldn't.
    const PERMISSION17 = 65536;
    const PERMISSION18 = 131072;
    const PERMISSION19 = 262144;
}

u003C/EDIT>

Ich würde ferner definieren, dass der Parameter set () eine Single-Bit-Ganzzahl und keine Flag-Nummer sein muss. Die SET () Implementierung mit Dämon ist das, was ich meine:

$this->value |= $n;

"Ich mag die Idee, es erweiterbarer/generischer zu machen, sodass verschiedene Klassen dies erweitern und für verschiedene Abschnitte verwenden können. Ich bin mir einfach nicht sicher, wie es geht."

Tu das nicht, es gibt verschiedene Gründe dafür. In keiner bestimmten Reihenfolge und in kurzer Zeit: Separate Funktionsklassen von Datenobjekten. Erweitern Sie nicht, was keine Vererbung benötigt. Verwenden Sie stattdessen eine Eigenschaft, die sich ausdehnenden Klassen normalerweise nicht eng mit der Bitmaskenklasse zusammenarbeiten müssen, um überhaupt zu arbeiten. Zusätzlich können Sie sich in PHP nur von einer Klasse erstrecken. Wenn Sie dies für eine so begrenzte Verwendung verwenden, haben die Erweiterung von Objekten diese Funktion bereits verbrannt.

Wahrscheinlich lieben Sie es, keine binären Berechnungen in Ihrem Gehirn durchzuführen, sondern stattdessen eine Klasse haben, die die binäre Berechnung für Sie verkörpert und eine Schnittstelle bietet, die menschlicher ist (Namen statt Zahlen zumindest), um zu interagieren. Bußgeld. Aber das ist es einfach. Sie können die Bitmaske über den Binärwert weitergeben. Wenn Sie keine Binärwerte benötigen, ist eine Auflauf Klasse stattdessen Könnte das sein, wonach Sie bereits suchen (überprüfen Sie das Flagsenum in spezifisch).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top