Bit Flags

von christian | 2 | 4 Kommentare | 5471 Aufrufe

Anzeige Hier werben

Mit Hilfe von bit flags kann man mehrere Zustände in einer einzigen Zahl unterbringen. Dies ist z.B. bei Rückgabewerten von Funktionen nützlich. In diesem Tutorial zeige ich die Verwendung von Bit-Flags anhand einer einfachen Formular-Überprüfung mit PHP.

Zunächst definieren wir einige Konstanten welche die einzelnen Flags repräsentieren. Diese verbessern später die Lesbarkeit des Codes. Sind aber nicht zwingend erforderlich.

 
PHP
1
2
3
4
5
6
define('_NO_USERNAME',      1);
define('_NO_PASSWORD',      2);
define('_NO_EMAIL',         4);
define('_INVALID_USERNAME', 8);
define('_INVALID_PASSWORD', 16);
define('_INVALID_EMAIL',    32);

Um zu verstehen was Bit-Flags eigentlich sind, schreiben wir das ganze in binärer Form.

 
PHP
1
2
3
4
5
6
define('_NO_USERNAME',      bindec(000001));
define('_NO_PASSWORD',      bindec(000010));
define('_NO_EMAIL',         bindec(000100));
define('_INVALID_USERNAME', bindec(001000));
define('_INVALID_PASSWORD', bindec(010000));
define('_INVALID_EMAIL',    bindec(100000));

Flag setzen

Nun initialisiert man zunächst die Fehlercode-Variable ($failure_code) mit 0. Möchte man ein bestimmtes Flag setzen bedient man sich einer sogenannten Bitmaske. Die Bitmaske entspricht dem zu setzenden Bit. Bitmaske und Wert verknüpft man mit einem bitweisen OR (|). Die beiden Werte werden dadurch bit für bit mit einem OR verknüpft. Im folgenden Beispiel wird das _NO_PASSWORD-bit gesetzt. Die entsprechende Bitmaske wäre also 000010 (Dezimal: 2)

 
Text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
   000000 Wert
OR 000010 Bitmaske
_________
   000010

Einen weiteres Flag setzen:

   000010 Wert
OR 010000 Bitmaske
_________
   010010

In PHP sieht dies wie folgt aus:

 
PHP
1
2
3
$failure_code = 0;

$failure_code |= _NO_PASSWORD;

Flag auslesen

Hier kommt wieder die Bitmaske ins Spiel. Diese verknüpft man diesmal aber mit einem bitweisen AND (&) mit dem Wert. Die beiden binären zahlen werden dadurch bit für bit mit einem AND verknüpft. Ergebnis dieser Operation ist entweder 1 (true) oder 0 (false). Im folgenden Beispiel wird überprüft ob das _NO_EMAIL-Bit gesetzt ist. Die entsprechende Bitmaske wäre also 000100 (Dezimal: 4).

 
Text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Fall 1: Bit/Flag ist gesetzt.

    000100 Wert
AND 000100 Bitmaske
__________
    000100 True

Fall 2: Bit/Flag ist nicht gesetzt.

    000000 Wert
AND 000100 Bitmaske
__________
    000000 False

In PHP sieht das ganze wie folgt aus:

 
PHP
1
2
3
4
5
6
$failure_code = 0;

if($failure_code &_NO_EMAIL)
    echo 'Flag/Bit ist gesetzt.';
else
    echo 'Flag/Bit ist nicht gesetzt.';

Beispiel

Nun ein praktisches Beispiel:

 
PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?php

define('_NO_USERNAME',      1);
define('_NO_PASSWORD',      2);
define('_NO_EMAIL',         4);
define('_INVALID_USERNAME', 8);
define('_INVALID_PASSWORD', 16);
define('_INVALID_EMAIL',    32);

function validate($username,$password,$email)
{
    $failure_code = 0;
    if($username == '')
        $failure_code |= _NO_USERNAME;

    if($password == '')
        $failure_code |= _NO_PASSWORD;

    if($email == '')
        $failure_code |= _NO_EMAIL;

    if(!preg_match('~^[a-zA-z0-9].*~',$username))
        $failure_code |= _INVALID_USERNAME;

    $strlen = strlen($password);
    if($strlen<20 && $strlen>4)
        $failure_code |= _INVALID_PASSWORD;

    if(!preg_match('~^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))$~',$email))
        $failure_code |= _INVALID_EMAIL;

    return $failure_code;
}

if($_GET['action']=='validate')
{
    $failure_code = validate($_POST['username'],$_POST['password'],$_POST['email']);
    
    if($failure_code==0)
        echo 'Alles korrekt.';
    else
    {
        if($failure_code &_NO_USERNAME)
            echo 'Username fehlt.';
    
        if($failure_code &_NO_EMAIL)
            echo 'E-Mail fehlt.';

        if($failure_code &_NO_PASSWORD)
            echo 'Passwort fehlt.';
    
        if($failure_code &_INVALID_USERNAME)
            echo 'Username ungültig.';
    
        if($failure_code &_INVALID_EMAIL)
            echo 'E-Mail ungültig.';

        if($failure_code &_INVALID_PASSWORD)
            echo 'Passwort ungültig.';
    }
?>

<form action="?action=validate" method="post">
Username<br/>
<input type="text" name="username" /><br/>
Passwort<br/>
<input type="password" name="password" /><br/>
E-Mail<br/>
<input type="text" name="email" /><br/>
</form>
Über den Autor: christian
Ich beschäftige mich seit mehreren Jahren mit Webdesign und ins besondere mit Webentwicklung. Derzeit studiere ich Angewandte Informatik.
Profilseite betrachten

Kommentare: Bit Flags

Neuen Kommentar schreiben
Hochkommata/Anführungsstriche
Beantworten

Also ich ersten Codebeispiel (die 6 defines) würde man schon 6 Notices bekommen bzgl. "undefined constant". Die Funktion define() verlangt als ersten Parameter einen String. Somit muss man den Namen der Konstante auch in Hochkommata (') oder Anführungsstrichen (") setzen.

Jan Pieper am 18.01.2008 um 12:57
Re: Hochkommata/Anführungsstriche
Beantworten

hmm ja sowas passiert wenn man nicht testet. Danke für den Hinweis...

Also ich ersten Codebeispiel (die 6 defines) würde man schon 6 Notices bekommen bzgl. "undefined constant". Die Funktion define() verlangt als ersten Parameter einen String. Somit muss man den Namen der Konstante auch in Hochkommata (') oder Anführungsstrichen (") setzen.

christian am 18.01.2008 um 22:43
Wie $failure_code erstellt wird
Beantworten

Du verwendest im Code um $failure_code zusammenzustellen immer sowas wie: $failure_code += _NO_USERNAME;. Sauberer wäre aber $failure_code |= _NO_USERNAME;, da hier keine "Überläufe" stattfinden können. Vielleicht kannst du das noch ändern. Im Beispiel macht es zwar keinen Unterschied, wär aber sauberer und sollte weniger fehleranfällig sein. ;-)

David Danier am 12.01.2008 um 21:32
Re: Wie $failure_code erstellt wird
Beantworten

danke für den Hinweis. Hab's mal verbessert...

Du verwendest im Code um $failure_code zusammenzustellen immer sowas wie: $failure_code += _NO_USERNAME;. Sauberer wäre aber $failure_code |= _NO_USERNAME;, da hier keine "Überläufe" stattfinden können. Vielleicht kannst du das noch ändern. Im Beispiel macht es zwar keinen Unterschied, wär aber sauberer und sollte weniger fehleranfällig sein. ;-)

christian am 12.01.2008 um 22:16