Operatory bitowe PHP

Operatory bitowe PHP operują na binarnych reprezentacjach liczb całkowitych, czyli wykonują działania na pojedynczych bitach swoich argumentów. Choć liczby binarne i operacje na nich nie są pierwszym skojarzeniem, kiedy ktoś myśli o programowaniu w języku PHP, to warto się z nimi zaznajomić. Są proste i doskonale sprawdzają się w przypadku niektórych rodzajów problemów.

Operatory bitowe PHP mogą być używane na przykład do przechowywania wielu opcji w jednej liczbie całkowitej albo do weryfikacji uprawnień użytkowników przy użyciu jednej liczby.

Zastanawiasz się, jak to możliwe? Uchylę rąbka tajemnicy już teraz. Podstawę tych technik stanowi maska bitowa. Tylko co to jest maska bitowa? Niedługo się dowiesz, ale zanim ci o tym opowiem, najpierw zabiorę cię na chwilę do niezwykłego świata bitów.

W tym rozdziale poznasz odpowiedzi między innymi na następujące pytania:

  1. Czym są operatory bitowe PHP?
  2. Jakie są operatory bitowe w PHP?
  3. Czym są liczby binarne?
  4. Co to jest maska bitowa?

Zaczniemy od zwięzłego podsumowania najważniejszych informacji o operatorach bitowych PHP w formie tabeli, a następnie przejdziemy do ich szczegółowego omówienia, z krótkim przystankiem na omówienie liczb binarnych po drodze.

Jakie są operatory bitowe w PHP

W języku PHP jest 6 operatorów bitowych, za pomocą których można przetwarzać liczby binarne. Operatory te działają na liczbach całkowitych. To znaczy, że jeśli zostanie im przekazana wartość innego typu, np. zmiennoprzecinkowa, to i tak zamienią ją na liczbę całkowitą, np. przez odrzucenie części dziesiętnej.

Poniższa tabela zawiera zwięzłe omówienie wszystkich operatorów binarnych PHP.

Operatory binarne PHP
Operator binarnyNazwaPrzykładOpis
&AND (koniunkcja)$a & $bWykonuje operację logiczną AND na parach odpowiadających sobie bitów z liczb przekazanych jako argumenty
|OR (suma logiczna)$a | $bWykonuje operację logiczną OR na parach odpowiadających sobie bitów z liczb przekazanych jako argumenty
^XOR (różnica symetryczna)$a ^ $bWykonuje operację logiczną XOR na parach odpowiadających sobie bitów z liczb przekazanych jako argumenty
~NOT (negacja)$a ~ $bWykonuje operację logiczną NOT na parach odpowiadających sobie bitów z liczb przekazanych jako argumenty
<<Przesunięcie w lewo$a << $bPrzesuwa bity wartości $a o $b pozycji w lewo
>>Przesunięcie w prawo$a >> $bPrzesuwa bity wartości $a o $b pozycji w prawo

Czy nazwy czterech pierwszych operatorów brzmią znajomo? Jeśli nie, to przeczytaj poprzedni rozdział — Operatory logiczne PHP.

Trzy pierwsze operatory binarne wymienione w tej tabeli wykonują działania algebry Boole’a na parach odpowiadających sobie bitów wartości przekazanych im argumentów. Czwarty operator również reprezentuje operację Boole’a, ale działa tylko na jednym argumencie — odwraca wartość każdego jego bitu (tzn. 1 zamienia na 0 a 0 zamienia na 1).

Pozostałe dwa operatory przesuwają bity w lewo lub w prawo, czego efektem jest odpowiednio podwojenie oryginalnej wartości liczby lub jej podzielenie przez dwa.

Jeśli te objaśnienia nic ci nie mówią albo mówią niewiele, to znaczy, że musisz uzupełnić wiedzę na temat tego, czym są liczby binarne, jak są przechowywane liczby w pamięci komputera itd. Zapraszam więc do tańca. Gdy nauczysz się jego kroków, na liczby binarne zaczniesz patrzeć znacznie czulszym wzrokiem.

Liczby binarne

Liczby binarne to liczby wyrażone w dwójkowym (binarnym) systemie liczbowym, czyli takim w którym występują tylko dwie cyfry: 0 i 1.

Najłatwiej system ten jest zrozumieć przez porównanie z dobrze ci znanym systemem dziesiętnym. W tym systemie występuje 10 cyfr — 0, 1, 2, 3, 4, 5, 6, 7, 8 i 9.

To znaczy, że największą liczbą, jaką możemy wyrazić za pomocą jednego znaku w systemie dziesiętnym jest 9. Potem dodajemy kolejną cyfrę i zaczynamy cykl od początku — 10, 11, 12 itd. Gdy dojdziemy do 19, znowu wracamy do początku cyklu. Bierzemy 2 i liczymy dalej 21, 22, 23 itd. Gdy dojdziemy do 99, dorzucamy następną cyfrę i zaczynamy od nowa — 100, 101, 102 itd.

Na identycznej zasadzie opiera się system dwójkowy, ale zamiast 10 cyfr ma tylko 2. To znaczy, że największą możliwą wartością jednocyfrową jest 1. Potem musimy dodać kolejną cyfrę. Czyli 10 w systemie binarnym oznacza 2 w systemie dziesiętnym, 11 w systemie binarnym to 3 w systemie dziesiętnym itd.

Poniższa tabela zawiera zestawienie 16 pierwszych liczb w formatach dziesiętnym i binarnym.

Zestawienie 16 pierwszych liczb dziesiętnych i binarnych
Liczby dziesiętneLiczby binarne
11
210
311
4100
5101
6110
7111
81000
91001
101010
111011
121100
131101
141110
151111
1610000

Jeśli uważnie przyjrzysz się liczbom binarnym w tej tabeli, to dostrzeżesz wśród nich pewną prawidłowość. Poniżej w słupku wypisałem wartości binarne odpowiadające kolejno wartościom dziesiętnym 2, 4, 8 i 16.

10 — 2
100 — 4
1000 — 8
10000 — 16

Każda kolejna liczba jest dwa razy większa od poprzedniej. Tak jak w systemie dziesiętnym dodanie jednej cyfry oznacza pomnożenie wartości przez 10, tak w systemie dwójkowym dodanie jednej cyfry oznacza pomnożenie wartości przez 2. Spójrz na poniższy przykład z liczbami dziesiętnymi:

10 — dziesięć
100  — sto
1000 — tysiąc
10000 — dziesięć tysięcy

Jeśli weźmiemy dowolną liczbę dziesiętną, np. 1258 i „przesuniemy” ją o jedną pozycję w lewo (dodamy jedną cyfrę, zero, na końcu), to otrzymamy 12580, a więc 10 razy większą wartość. Analogicznie, jeśli „przesuniemy” 1258 o jedną pozycję w prawo (zabierzemy jedną cyfrę z początku, w tym przypadku 1), to otrzymamy 125, czyli wartość mniej więcej dziesięciokrotnie mniejszą (ewentualna część ułamkowa jest w tej operacji tracona).

Inaczej mówiąc, w systemie dziesiętnym każda kolejna cyfra w liczbie oznacza zwiększenie jej wartości o potęgę dziesiątki, np.:

12 x 100 = 12
12 x 101 = 120
12 x 102 = 1200
12 x 103 = 12000

Dokładnie tak samo to działa w przypadku liczb binarnych, tylko wartości z każdą kolejną cyfrą zwiększają się o potęgi dwójki, np.:

111 x 20 = 111
111 x 21 = 1110
111 x 22 = 11100
111 x 23 = 111000

Inaczej mówiąc, w systemie binarnym każda kolejna cyfra w liczbie oznacza zwiększenie jej wartości o potęgę dwójki.

Po co ci o tym wszystkim opowiadam? Ponieważ wiedza ta jest potrzebna do zrozumienia wartości zwracanych przez operatory bitowe. W szczególności operator negacji mógłby wprawić cię w zdumienie, ponieważ wynikiem zanegowania za jego pomocą na przykład liczby dziesiętnej 9 jest liczba -10 (minus dziesięć).

Jesteś zdziwiony tym wynikiem? Nie dziwię się, jeśli tak. Dokładne zrozumienie przyczyny, czemu tak się dzieje, wymaga znajomości sposobu przechowywania liczb binarnych przez komputer. Przyjrzymy się mu teraz.

zdziwieni ludzie, operatory binarne PHP, liczby binarne
Aby nie dziwić się wynikami zwracanymi przez operatory binarne PHP, trzeba znać sposób przechowywania liczb binarnych w komputerze

Uzupełnienie dwójkowe

PHP przechowuje liczby przy użyciu metody o nazwie uzupełnienie dwójkowe (ang. two’s complement), która czasami nie do końca poprawnie jest nazywana uzupełnieniem do dwóch.

W metodzie tej bit o najwyższej wadze (ang. most significant bit — MSB), czyli pierwsza cyfra z lewej w naszym przypadku, to bit znaku. Jeśli ma on wartość 0, to liczba jest dodatnia. Jeśli ma wartość 1, to liczba jest ujemna.

To znaczy, że każda liczba w formacie binarnym w PHP ma z przodu dodatkowy bit, który określa jej znak. Na przykład dodatnia liczba dziesiętna 9, która w formacie binarnym ma postać 1001, w pamięci komputera jest przechowywana jako 01001. No prawie…

Dokładny format przechowywania liczb binarnych zależy od systemu operacyjnego. Jeśli masz system 32-bitowy, to wszystkie liczby są przechowywane w postaci 32 bitów.

Do reprezentacji dodatniej liczby binarnej 1001 (9 w systemie dziesiętnym) wystarczą 4 bity plus 1 dodatkowy bit na znak, czyli w sumie 5 bitów — 01001.

Aby każda liczba miała odpowiednią długość, brakujące bity są dostawiane z przodu. Jeśli liczba jest dodatnia, to z przodu dostawiane jest tyle zer, ile trzeba, aby otrzymać w sumie 32 bity. Zatem dziesiętna liczba +9, która jako liczba binarna ma postać 1001, a w systemie uzupełnienia dwójkowego ma postać 01001, w formacie 32-bitowym będzie przechowywana jako:

00000000000000000000000000001001

Aby przeliczyć taką liczbę binarną na dziesiętną, należy pomnożyć każdą jej cyfrę przed odpowiednią potęgę liczby 2 (w systemie dziesiętnym mnożylibyśmy przez potęgi liczby 10). Weźmy na przykład binarną liczbę w systemie uzupełnienia dwójkowego 01001:

010012 = (0 × 2) + (1 × 2³) + (0 × 2²) + (0 × 2¹) + (1 × 2) = 0 + 8 + 0 + 0 + 1 = 910
Uwaga: Liczby w indeksie dolnym to standardowy sposób oznaczania systemu liczbowego, w jakim jest wyrażona dana wartość.

Przeanalizujemy jeszcze jeden przykład:

0110000 = (0 × 26) + (1 × 2) + (1 × 2) + (0 × 2³) + (0 × 2²) + (0 × 2¹) + (0 × 2) = 0 + 32 + 16 + 0 + 0 + 0 + 0 = 48₁₀

W systemie 32-bitowym przed tymi wartościami zostałaby dostawiona odpowiednia liczba zer i liczby byłyby gotowe do przechowywania w pamięci.

Wiesz już, jak PHP przechowuje w pamięci liczby dodatnie. Teraz przyjrzymy się reprezentacji w PHP liczb ujemnych.

Liczby ujemne w uzupełnieniu dwójkowym

Liczby ujemne w PHP (czyli w systemie uzupełnienia dwójkowego) są przechowywane tak samo, jak liczby dodatnie. Mają jednak pewną subtelną cechę, która może cię zaskoczyć, a która po zapoznaniu stanie się oczywista. Zobaczmy.

Powiedzmy, że chcemy przedstawić w systemie uzupełnienia dwójkowego liczbę -910. Nie jest to tak proste, jak dostawienie bitu znaku ujemności (1), to znaczy 11001 nie przejdzie… Niemniej jednak, nadal jest to proste.

Aby uzyskać wartość liczby ujemnej w systemie uzupełnienia dwójkowego, należy wykonać następujące czynności:

  1. Przeliczyć wartość bezwzględną (czyli bez znaku) danej liczby na format binarny
  2. Dodać na początku bit znaku, czyli 0, bo zaczynamy od liczby dodatniej
  3. Dokonać inwersji wszystkich bitów, czyli wszystkie 0 zamienić na 1, a 1 zamienić na 0
  4. Dodać 1 do otrzymanej wartości
No to bierzemy się za obliczanie wartości -910 w systemie uzupełnienia dwójkowego:
  1. Przeliczamy wartość bezwzględną wybranej liczby na format binarny: 910 = 10012
  2. Dodajemy na początku bit znaku: 01001
  3. Dokonujemy inwersji wszystkich bitów: 10110
  4. Dodajemy 1 do otrzymanej wartości: 10110 + 1 = 10111

Teraz dochodzimy do tej subtelnej cechy, o której wspominałem. Chodzi o to, że bit znaku też liczy się jako składnik wartości i reprezentuje wartość ujemną. W przypadku liczb dodatnich bit znaku był nieistotny, ponieważ miał wartość 0.

W liczbie ujemnej ten bit ma wartość 1. W związku z tym przeliczenie liczby binarnej 10111 na format dziesiętny będzie wyglądało tak:

101112 = −(1 × 24) + (0 × 23) + (1 × 22) + (1 × 21) + (1 × 20) = −16 + 0 + 4 + 2 + 1 = −16 + 7 = −9

Przeanalizujemy jeszcze jeden przykład. Powiedzmy, że interesuje nas wartość −17.

  1. Przeliczamy wartość bezwzględną wybranej liczby na format binarny: 1710 = 100012
  2. Dodajemy na początku bit znaku: 010001
  3. Dokonujemy inwersji wszystkich bitów: 101110
  4. Dodajemy 1 do otrzymanej wartości: 101110 + 1 = 101111

Gotowe. −17 w systemie uzupełnienia dwójkowego to 101111. Upewnijmy się, czy na pewno tak jest:

1011112 = −(1 × 25) + (0 × 24) + (1 × 23) + (1 × 22) + (1 × 21) + (1 × 20) = −32 + 0 + 8 + 4 + 2 + 1 = −32 + 15 = −17

Zgadza się!

Gdybyśmy teraz chcieli zapisać tę liczbę w formacie 32-bitowym, to podobnie jak w przypadku liczb całkowitych, uzupełnilibyśmy ją odpowiednią liczbą bitów z przodu. Tyle że w przypadku liczb ujemnych dodaje się 1 a nie 0.

Zatem liczba w systemie uzupełnienia dwójkowego 101111 w formacie 32-bitowym wyglądałaby tak:

11111111111111111111111111101111

Proste? Oczywiście, jak kawałek sznurka w kieszeni…

Wiesz już wszystko, co trzeba, na temat liczb binarnych, uzupełnienia dwójkowego i reprezentowania zarówno liczb dodatnich, jak i ujemnych w tym systemie. Dzięki temu żaden wynik działania operatorów bitowych nie będzie ci straszny. Nawet termin maska bitowa już teraz powinien wydawać ci się o wiele mniej straszny, a wkrótce staniecie się prawdziwymi przyjaciółmi.

Po tym przydługim wstępie możemy przejść do omówienia operatorów bitowych PHP. Zaczniemy od koniunkcji.

Bitowy operator koniunkcji &

Operator bitowy & wykonuje operację logiczną AND na odpowiadających sobie parach bitów dwóch liczb przekazanych mu jako argumenty. Weźmy na przykład liczby dziesiętne 13 i 14:


var_dump(13 & 14);

W wyniku tego działania otrzymamy liczbę dziesiętną 12:

int(12)

Sprawdzimy, czy zgadza się to z naszą wiedzą na temat wykonywania działań logicznych i liczb binarnych:

1310 = 11012
1410 = 11102

Więc:

  1101
& 1110
------
  1100

Jeśli zajrzysz do tabeli 16 pierwszych liczb dziesiętnych i binarnych, przekonasz się, że rzeczywiście 11002 = 1210.

Bitowy operator sumy logicznej |

Operator bitowy | wykonuje operację logiczną OR na odpowiadających sobie parach bitów dwóch liczb przekazanych mu jako argumenty. Weźmy na przykład liczby dziesiętne 9 i 10:


var_dump(9 | 10);

W wyniku tego działania otrzymamy liczbę dziesiętną 11:

int(11)

Sprawdzimy, czy zgadza się to z naszą wiedzą na temat wykonywania działań logicznych na liczbach binarnych:

910 =  10012
1010 = 10102

Więc:

  1001
| 1010
------
  1011

Jeśli zajrzysz do tabeli 16 pierwszych liczb dziesiętnych i binarnych, przekonasz się, że rzeczywiście 10112 = 1110.

Bitowy operator alternatywy rozłącznej ^

Operator bitowy ^ wykonuje operację logiczną XOR na odpowiadających sobie parach bitów dwóch liczb przekazanych mu jako argumenty. Weźmy na przykład liczby dziesiętne 7 i 8:


var_dump(7 ^ 8);

W wyniku tego działania otrzymamy liczbę dziesiętną 15:

int(15)

Sprawdzimy, czy zgadza się to z naszą wiedzą na temat wykonywania działań logicznych na liczbach binarnych:

710 = 01112
810 = 10002

Więc:

  0111
^ 1000
------
  1111

Jeśli zajrzysz do tabeli 16 pierwszych liczb dziesiętnych i binarnych, przekonasz się, że rzeczywiście 11112 = 1510.

Bitowy operator negacji ~

Operator bitowy ~ przyjmuje tylko jeden argument i wykonuje operację logiczną NOT na wszystkich jego bitach. Jednak dla niedoświadczonego oka wynik jego działania może być w pierwszej chwili zaskakujący.

Weźmy na przykład liczbę dziesiętną 7. Jak myślisz, jaki będzie wynik jej negacji? Spójrz:


var_dump(~7); // int(-8)

Sprawdzimy, czy zgadza się to z naszą wiedzą na temat wykonywania działań logicznych na liczbach binarnych:

710 = 1112

Wykonujemy obliczenia:

~ 111
-----
  000

Coś tu nie gra, bo powinniśmy otrzymać -8, a wychodzi nam 0. Przyczyną naszego „błędu” jest to, że do tej pory stosowaliśmy mały skrót.

Przypomnę, że PHP do przechowywania liczb binarnych używa systemu uzupełnienia dwójkowego, a jest to system do przechowywania liczb całkowitych ze znakiem. To znaczy, że tak naprawdę każda dodatnia liczba binarna ma na początku 0, a każda ujemna liczba binarna ma na początku 1.

Powiedzmy, że nasz system jest 8-bitowy. Jeśli tak, to w rzeczywistości operację negacji liczby binarnej 111 wykonujemy na takim ciągu bitów: 00000111.

Tu istotne są tylko cztery ostatnie bity. Trzy ostatnie (111) reprezentują wartość liczby, a czwarty od końca (0), znajdujący się przed nią, określa czy jest to liczba dodatnia, czy ujemna. Pozostałe są już tylko dopełnieniem, aby liczba bitów była odpowiednia dla danego formatu (w 32-bitowym tych zer z przodu byłoby jeszcze 28).

Jak wiemy, 0 oznacza liczbę dodatnią, a 1 — liczbę ujemną, a więc 01112 = +710. Teraz przyjrzymy się negacji omawianej liczby jeszcze raz, ale w pełnym 8-bitowym formacie uzupełnienia dwójkowego:

~ 00000111
----------
  11111000

Naszą liczbę reprezentują cztery ostatnie bity — 1000. Skoro pierwszym bitem tego ciągu jest 1, to znaczy, że mamy do czynienia z liczbą ujemną. Sprawdzimy więc, jaka jest jej wartość dziesiętna (zobacz Liczby ujemne w uzupełnieniu dwójkowym):

10002 = −(1 × 23) + (0 × 22) + (0 × 21) + (0 × 20) = −8 + 0 + 0 + 0 = −8

Okazuje się, że jednak wszystko gra!

muzycy grający na instrumentach, liczby binarne php, operatory bitowe php
PHP do przechowywania liczb całkowitych ze znakiem używa systemu uzupełnienia dwójkowego
Operacja negacji ma to do siebie, że bardzo łatwo można przewidzieć jej wynik w formacie dziesiętnym. Jest on zawsze równy ujemnej wartości argumentu pomniejszonej o 1. Na przykład ~10 = −11, ~35 = −36 itd.

Zastanawiasz się, dlaczego w działaniach &, | i ^ mogliśmy nie pamiętać o bicie znaku, a w negacji musimy mieć go na uwadze? Tak naprawdę we wszystkich operacjach bit znaku jest brany pod uwagę, tylko że w przypadku działań &, | i ^ na liczbach dodatnich nic on nie zmienia, np.:

  00000111 (7)
& 00001010 (10)
---------------
  00000010 (2)

Jeśli spojrzysz na tabele prawdy działań AND, OR i XOR, to zauważysz, że każde z nich dla dwóch 0 zawsze daje 0, czyli nie ma żadnej zmiany. Natomiast w negacji 0 zmienia się w 1, a więc dokonuje się zmiana znaku.

Bitowy operator przesunięcia w lewo <<

Operator bitowy PHP << (przesunięcie w lewo) „przesuwa” swój pierwszy argument w lewo o liczbę miejsc wskazywaną przez drugi argument. Puste miejsca po prawej stronie wypełnia zerami.

Weźmy na przykład liczbę dziesiętną 7, która w formacie 8-bitowym ma postać 00000111. Przesuniemy ją w lewo o 3 pozycje:


00000111 << 3 = 00111000

W formacie dziesiętnym jest to 56. A teraz sprawdźmy, jaki będzie wynik przesunięcia o 2 miejsca w lewo liczby 10, która w formacie binarnym ma postać 1010:


00001010 << 2 = 00101000

Ta liczba binarna w formacie dziesiętnym to 40. A teraz spójrz na przesunięcie w lewo o 4 miejsca liczby dziesiętnej 5:


00000101 << 4 = 01010000

W formacie dziesiętnym ta liczba binarna odpowiada liczbie 80.

Czy dostrzegasz prawidłowość w tych wszystkich wynikach?

Podpowiedź: 14 × 2 × 2 = 56, 10 × 2 × 2 = 40, 5 × 2 × 2 × 2 × 2 = 80.

Przesunięcie liczby w lewo o jedną pozycję bitową jest równoznaczne z pomnożeniem jej przez 2. Dotoczy to zarówno liczb ujemnych, jak i dodatnich.

Bitowy operator przesunięcia w prawo

Operator bitowy PHP >> (przesunięcie w prawo) „przesuwa” swój pierwszy argument w prawo o liczbę miejsc wskazywaną przez drugi argument. Puste miejsca po lewej stronie wypełnia bitem znaku, a bity znajdujące się po prawej stronie zostają utracone, np.:


00001011 >> 2 = 00000010
11110101 >> 2 = 11111101

W pierwszym przykładzie przesunęliśmy o 2 miejsca w prawo liczbę dziesiętną +11 w 8-bitowym formacie binarnym. W wyniku otrzymaliśmy 2.

W drugim przykładzie przesunęliśmy o 2 miejsca w prawo liczbę dziesiętną -11 w 8-bitowym formacie binarnym. W wyniku otrzymaliśmy -3.

Przesunięcie liczby binarnej w prawo o jedno miejsce jest równoznaczne z podzieleniem jej wartości przez 2 i zaokrągleniem w dół, jeśli powstanie część ułamkowa.

Jeśli liczbę +11 przesuwamy o 2 miejsca w prawo, to tak jakbyśmy 2 razy wykonali dzielenie całkowitoliczbowe: 11 ÷ 2 ÷ 2 = 5 ÷ 2 = 2.

Przy przesunięciu w prawo liczby ujemnej należy pamiętać, że zaokrąglenie w dół w przypadku liczb ujemnych oznacza zaokrąglenie w kierunku większej wartości bezwzględnej. Dlatego po przesunięciu liczby -11 o dwa bity w prawo otrzymaliśmy -3, a nie -2.

To był ostatni operator bitowy PHP. Na zakończenie tego rozdziału pokażę ci jeszcze jedno z możliwych praktycznych zastosowań tych operatorów.

Przykład z życia wzięty

Operatorów bitowych czasami używa się po to, aby uzyskać bardziej zwięzły, przejrzysty i efektywny kod.

Za ich pomocą można na przykład wygodnie kontrolować uprawnienia użytkowników w systemie. W tym celu tworzy się maski bitowe, czyli nadaje się zmiennym wartości o określonej wartości binarnej. Każda z tych zmiennych reprezentuje jedno uprawnienie, np.:


$read = 1; // 0001
$write = 2; // 0010
$edit = 4; // 0100
$delete = 8; // 1000 (itd. w razie potrzeby, mnożąc przez dwa)

Następnie za pomocą operatora bitowego | przypisujemy poszczególne uprawnienia (maski bitowe) wybranym rolom w systemie, np.:


$admin = $read | $write | $edit | $delete; // 0001 | 0010 | 0100 | 1000 === 1111
$editor = $read | $write | $edit; // 0001 | 0010 | 0100 === 0111
$contributor = $read | $write; // 0001 | 0010 === 0011
$user = $read; // 0001

Teraz za pomocą operatora & możemy wygodnie sprawdzać, jakie uprawnienia ma dany użytkownik, np.:


echo ($user & $delete) == true ? "ma" : "nie ma";

Jeśli nie jest dla ciebie do końca jasne, jak to działa, to już wyjaśniam.

Operator bitowy & zwraca w wyniku prawdę (1) tylko wtedy, gdy oba argumenty są prawdziwe (1). Wykonujemy więc bitową operację AND na masce bitowej roli i masce bitowej wybranego uprawnienia oraz sprawdzamy, czy w wyniku pojawi się przynajmniej jedna jedynka (inaczej mówiąc, czy wynik będzie różny od zera).

Sprawdźmy więc na przykład, czy zwykły użytkownik ($user) ma uprawnienie do usuwania ($delete) w naszym systemie:

  0001 ($user)
& 1000 ($delete)
----------------
  0000

W wyniku są same zera, więc użytkownik nie ma tego uprawnienia. A teraz sprawdzimy, czy administrator może edytować treść:

  1111 ($admin)
& 0111 ($edit)
---------------
  0111

Wynik jest różny od zera, więc administrator może edytować treść.

Podsumowanie

  1. Operatory bitowe PHP wykonują operacje logiczne na bitach, z których składają się liczby całkowite.
  2. W PHP jest 6 operatorów bitowych: & (logiczna operacja AND), | (logiczna operacja OR), ^ (logiczna operacja XOR), ~ (logiczna operacja NOT), << (przesunięcie bitowe w lewo) i >> (przesunięcie bitowe w prawo).
  3. Przesunięcie bitowe wartości w lewo o jedną pozycję jest równoznaczne z pomnożeniem jej przez 2.
  4. Przesunięcie bitowe wartości w prawo o jedną pozycję jest równoznaczne z podzieleniem jej przez 2.
  5. Aby mieć pewność uzyskania prawidłowego wyniku, wyrażenia operatorów bitowych powinno się umieszczać w nawiasach.
  6. Liczby binarne to liczby binarnego systemu liczbowego, czyli takie, w którym występują tylko dwie cyfry: 0 i 1.
  7. Uzupełnienie dwójkowe to najczęściej używany sposób przechowywania liczb całkowitych ze znakiem w pamięci komputera, który jest wykorzystywany także przez PHP.
  8. Maska bitowa to specjalna wartość binarna, którą następnie można poddać operacji & z inną wartością binarną, aby sprawdzić, czy któreś jej bity są ustawione (mają wartość 1).
  9. Maski bitowe bywają używane m.in. do implementacji przełączników i systemów sprawdzania uprawnień użytkowników.



Pytania

  1. Jakie są operatory bitowe PHP? Krótko opisz działanie każdego z nich.
  2. Czym są liczby binarne?
  3. Jakiego systemu przechowywania liczb całkowitych ze znakiem używa PHP?
  4. Czym jest maska bitowa?
  5. Do czego mogą być używane maski bitowe w połączeniu z operatorami bitowymi?
  6. Jaki będzie wynik wyrażenia ~8 w PHP? Odpowiedz „z głowy”.
  7. Jaki będzie wynik wyrażenia 30 << 2 w PHP? Odpowiedz „z głowy”.
  8. Jaki będzie wynik wyrażenia 30 >> 2 w PHP? Odpowiedz „z głowy”.



Ćwiczenia

  1. Wykonaj w pamięci lub zapisując odręcznie następujące działania bitowe:
    • 0101 & 1010
    • 101101 & 011011
    • 0101 | 1010
    • 111000 | 010101
    • 0101 ^ 1010
    • 100011 ^ 010010
    • 0101 >> 2
    • 000111 >> 3
    • 1010 << 2
    • 110011 << 3
  2. Oblicz wyniki poniższych wyrażeń PHP, a następnie sprawdź swoje odpowiedzi za pomocą odpowiedniego skryptu:
    • 3 | 5
    • 4 & 12
    • 8 ^ 7
    • 6 & 9
    • 8 | 1
    • 16 ^ 15
  3. Zastanów się: Jeśli masz do dyspozycji 32 cyfry (bity), to największa wartość binarna, jaką możesz wyrazić w systemie dziesiętnym wynosi 232 = 4294967296. Jeśli jednak sprawdzisz jaka jest największa możliwa wartość całkowita w PHP w systemie 32-bitowym, to otrzymasz w odpowiedzi liczbę dziesiętną 2147483648. Czy wiesz dlaczego?
  4. Napisz instrukcję PHP, która sprawdza wartość 4. bitu w liczbie dziesiętnej 16.
  5. Uogólnij poprzednią instrukcję do takiej postaci, która pobiera do zmiennej dowolną liczbę dziesiętną i liczbę określającą przesunięcie oraz wyświetla wynik.
  6. Napisz instrukcję PHP, która ustawia n-ty bit (nadaje mu wartość 1) podanej liczby.

Podobał Ci się ten artykuł?

Oceń go!

Średnia 5 / 5. Liczba głosów: 1

Jeszcze nikt nie głosował. Wyprzedź innych i zagłosuj.

Skoro spodobał Ci się ten artykuł...

Poleć go znajomym!

Ojej :( Powiedz nam, co powinniśmy poprawić!

Podoba Ci się ta strona?

Pomóż nam się rozwijać, wykupując płatne konto. Dzięki temu będziemy mogli tworzyć dla Ciebie jeszcze więcej ciekawych treści, a Ty pozbędziesz się reklam.

Subskrybuj
Powiadom o
guest

0 Komentarze
Najstarsze
Najnowsze Najwięcej głosów
Opinie w linii
Zobacz wszystkie komentarze
0
Chętnie poznam Twoje przemyślenia, skomentuj.x