JustPaste.it

Wyrażenia w PHP

Czy wiesz, co ma wspólnego ze sobą zapis "$zmienna = 15;" i "$licznik >= $maks" w instrukcji IF? A zapis "1;"? A "$zmienna;"? Z pozoru nic, w praktyce wszystko - wszystkie podane przykłady są tym samym tworem - wyrażeniem. Jest to jedno z najważniejszych zagadnień programowania, lecz zwykle jest ono pomijane, lub omawiane "po macoszemu" w rozmaitych kursach i publikacjach. Jeśli je dobrze poznasz, jedną prostą linijką będziesz mógł wykonać najdziwniejsze i najbardziej niesamowite operacje, jakie kiedykolwiek przyśniły się programistom!

Zaczynamy

Ale zacznijmy od początku. Musimy sobie wpierw postawić pytanie, czym właściwie jest wyrażenie, że możemy do niego podstawiać najdziwniejsze rzeczy? Oto definicja:

Wyrażenie - zbiór składający się z jednej, lub dwóch danych/zmiennych/funkcji/innych wyrażeń (niepotrzebne skreślić :]) połączonych operatorem opisującym realcje pomiędzy nimi, zwracający jakąś wartość.

Opis dość skomplikowany. Przeanalizujmy pierwszą część tekstu. Dowiadujemy się z niego, że wyrażeniem może być pojedyncza dana (np. liczba 10):

<?php
 
10;
 
?>

pojedyncza zmienna:

<?php
 
$jakas_kurde_zmienna;
 
?>

pojedyncza funkcja:

<?php
 
jakas_kurde_funkcja();
 
?>

lub jedno wyrażenie, lecz to akurat nie ma znaczenia, bo po co niepotrzebnie komplikować sprawę? Przyjmijmy więc, że wyrażenie nie może składać się z jednego wyrażenia, lecz min. z dwóch. Ale idźmy dalej. Pisze, mogą być także dwie dane/zmienne/funkcje/wyrażenia w naszym wyrażeniu. Dobra, ale jak to w takim razie zapisać? Wyjaśnione jest wszystko kilka kolejnych znaków dalej: "połączonych operatorem opisującym realcje pomiędzy nimi". Co to jest operator - dobrze już wiesz. Operatorem może być np. "=", ">=", "&&", ".", "*" itd. Każdy operator opisuje inną relację (zależność) pomiędzy lewą i prawą stroną (są też operatory, które wyrażenie mogą mieć tylko z jednej strony, ale o tym dalej) i zwraca wynik tejże zależności. Np. operator "==" opisuje relację równości i zwróci nam 1, gdy taka równość zajdzie pomiędzy lewym i prawym wyrażeniem, lub 0, gdy równości nie ma. Tak samo operator "=" opisuje relację przypisania, lecz przy tym próbuje wykonać jakąś akcję (w tym przypadku przypisuje wynik wyrażenia po prawej stronie do wyrażenia po lewej) w zależności od wyniku relacji. Tak więc widzimy, że mamy dwie grupy operatorów:

  1. Opisujące relację
  2. Wykonujące akcję

Pamiętaj jednak, że są to jednak operatory i one bez względu na to, do której grupy należą, wartość jakąś muszą zwracać. I zwrócona przez operator wartość jest jednocześnie wynikiem samego wyrażenia. No i teraz wszystko jasne.

Po przeanalizowaniu powyższego tekstu pomyślałeś sobie zapewne: "Dobrze, lecz co w takim razie z czymś takim?"

<?php
 
$wynik = 1837382 + 4949394;
 
?>

Na pierwszy rzut oka widoczne tu jest jedno wyrażenie... lecz z dwoma operatorami! Coś tu się nie zgadza. Komputer rozwiązuje ten problem po prostu dzieląc to pseudo-wyrażenie na dwa normalne zgodnie z kolejnością wykonywania operatorów. Podobnie jak w matematyce, tak i tu każdy operator ma na stałe przypisany priorytet. Pierwsze zostaną wykonane operatory o najwyższym priorytecie, a potem dopiero te z niższymi. Wróćmy jednak do naszego przykładu. Dla komputera są tu dwa różne wyrażenia:

1837382 + 4949394
$wynik = [wyrażenie]

Dlaczego podzielone to zostało tak, a nie inaczej? Operator + ma wyższy priorytet od "=" i dlatego dana 1837382 jest lewą stroną właśnie jego, a nie prawą drugiego. Teraz: komputer wykonuje operację dodawania i wynik jest jednocześnie wynikiem pierwszego wyrażenia. Teraz dopiero zostaje on przypisany do zmiennej.

A teraz pokażę Ci Dziwne Wyrażenie #1:

<?php
 
$zmienna1 = 4;
$zmienna2 = 5;
$zmienna3 = 20;
$zmienna4 = 21;
$zmienna3 + $zmienna4 = $zmienna1 + $zmienna2;
echo $zmienna3."<br>s".$zmienna4;
 
?>

Zleciliśmy komputerowi trochę karkołomne zadanie - musi przypisać wynik dodawania $zmienna1 i $zmienna2 do... wyniku dodawnania $zmienna3 i $zmienna4. Niestety nie zna on algebry :] i wykonuje wszystko po swojemu. Postaramy się prześledzić, jak...

  1. Na początku sprawdza priorytety operatorów. Określił, że najpierw musi wykonać dodawanie, a potem przypisanie.
  2. Próbuje dzielić ciąg na wyrażenia, lecz tu coś mu nie pasuje, a mianowicie lewa strona operatora "=".
  3. Sprawdza w słowniczku składnię i otrzymuje, że po jego lewej stronie nie może być wyrażenie.
  4. Póki co zajmuje się dodaniem $zmienna1 do $zmienna2 i zapisuje sobie to w kolejce wyrażeń do wykonania.
  5. Następnie wraca do nieszczęsnego operatora przypisania. Stwierdza, że owszem, po lewej stronie jest wyrażenie, lecz jest także zmienna $zmienna4!
  6. Eureka, dopisuje radośnie do kolejki kolejne wyrażenie :].

$zmienna4 = [wyrażenie b)]

  1. No i teraz dopiero tworzy ostatnie wyrażenie, mianowicie $zmienna3 + $zmienna4;
  2. Wykonanie

Zauważ, że interpreter PHP wykazał się tu niemałym sprytem. Nie mógł przypisać wyniku dodawania do dodawania, ale zauważył, że po lewej stronie jest $zmienna4 i do niej przypisał wynik :]. Wykonaj skrypt i zauważ, że wartość $zmienna3 się nie zmieniła, lecz pod $zmienna4 mamy teraz 9, czyli wynik dodawnia 4 i 5.

Dobra, teraz pokażę Ci jeszcze jedną fajną sztuczkę: Dziwne Wyrażenie #2:

<?php
 
$zmienna1 = 3;
$zmienna2 = 4;
$zmienna3 = 5;
$zmienna4 = 6;
 
$wynik = $zmienna3 + $zmienna4 = $zmienna2 + $zmienna1;
 
echo $wynik;
 
?>

Tutaj z kolei próbuję zmusić interpreter, by do wyniku przypisał wynik dodawania i wynik dodawania :]. Doświadczenie szalenie ciekawe, gdyż można się z niego dowiedzieć, jak PHP poradzi sobie z takim problemem. A jeszcze ciekawsze jest to, że wynik wychodzi zupełnie inny, niżby to można było wywnioskować z tabeli priorytetów operatorów. Po uruchomieniu przykładu na ekranie ukazał się wynik 12, a z tejże tabelki można wywnioskować, że powinien on wynosić... 7. Zadasz pytanie: czemu wynik jest inny? Niestety to, co dalej napiszę, jest tylko moim przypuszczeniem (nie studiowałem kodu źródłowego PHP i dokładnie nie wiem, jak to oni tam porobili), lecz przypuszczeniem sprawdzającym się w praktyce. Możliwe, że interpreter, gdy zauważy, że dwa wyrażenia nachodzą na siebie (tak, jak my to mamy: "$zmienna3 + $zmienna4" oraz "$zmienna4 = [wyrażenie]"), przestawia je tak, aby wszystko pasowało do schematu, lecz to nie jest z kolei zgodne z priorytetami operatorów. W naszym przypadku więc zamiast wykonać najpierw "$zmienna3 + $zmienna4", przetworzone zostaje w pierwszej kolejności wyrażenie "$zmienna4 = [wyrażenie]" - wynik musi być przypisany do zmiennej, więc to jest jedyne wyjście, by być w zgodzie ze schematami. No i dzięki temu zabiegowi otrzymujemy nie 7, lecz 12. (3 + 4 jest 7, 7 wędruje do $zmienna4, a potem $zmienna4 [7] dodaje się do 5, a wynik tego działania to właśnie 12). Komputer dokładnie taką samą taktykę zastosował w pierwszym przykładzie, lecz tam nie mieliśmy możliwości sprawdzenia tego. Gratuluję spostrzegawczym :].

Teraz zajmiemy się czymś bardziej praktycznym - mianowicie jak przypisać kilku zmiennym naraz tę samą wartość bez zbędnego tworzenia kilku osobnych wyrażeń. Lecz aby to wykonać, będziemy potrzebowali narzędzia, które pozwoli nam wpłynąć bezpośrednio na kolejność wykonywania operatorów - nawiasy. Pamiętaj - wyrażenia w nawiasach mają zawsze pierwszeństwo przed tymi poza nimi. W nawiasach mogą znajdować się także inne nawiasy: "$x = (5945 * (444*17) - 9 / (6+(8-1)));". Dobra, ale po co nam nawiasy w operacji przypisywania? Przyjrzyjmy się operatorowi "=". Dla nas ważne jest nie to, co on robi, lecz wartość, jaką zwraca. Jeśli nie mógł przypisać nic do wyrażenia po lewej stronie, zwraca 0. OK. Ale co takiego otrzymamy, gdy przypisanie zajdzie? Otrzymamy wtedy wartość... jaka została przypisana. Posiadając te wiadomości, możemy to wykorzystać:

<?php
 
$d = ($c = ($b = ($a = 5)));
 
echo $d."<br>";
echo $c."<br>";
echo $b."<br>";
echo $a."<br>";
 
?>

Sam widzisz teraz, jak prosto przypisać paru zmiennym tę samą wartość :].

Flagi

Czasem zdarzy Ci się, że do funkcji będziesz musiał wprowadzić kilkanaście parametrów opisujących np. jak dana rzecz ma się zachować, np.

<?php
function rob($chodz, $mow, $rozmawiaj, $wstan, $idz_gdzies){
if($chodz == 1){ echo "Kazales mi chodzic<br>"; }
if($mow == 1){ echo "Kazales mi mowic<br>"; }
if($rozmawiaj == 1){ echo "Kazales mi rozmawiac<br>"; }
if($wstan == 1){ echo "Kazales mi wstac<br>"; }
if($idz_gdzies == 1){ echo "Kazales mi isc gdzies<br>"; }
}
rob(1,0,1,1,0);
?>

Jednak rozwiązanie z Bóg wie, iloma parametrami jest bezsensowne. Załóżmy, że po pewnym czasie zechcesz dodać nową akcję i co? I rozwali się wszystko, co używało tej funkcji, gdyż wszędzie wywoływana jest ona z mniejszą ilością parametrów. Inna sprawa jest taka, że w prawdziwym programowaniu wywołanie funkcji z większą ilością parametrów zajmuje więcej czasu, niż analogicznej z mniejszą. Jednak w programowaniu, a konkretniej w dziedzinie operacji logicznych takie cos to drobnostka - stosuje się tzw. flagi. Dzięki temu możesz przekazać do funkcji tyle danych, ile chcesz przy użyciu tylko jednego parametru!

Na początku deklarujemy sobie różne czynności, czy co tam chcemy - najlepiej, by były to stałe:

define("WALK", 1);
define("TALK", 2);
define("SPEAK", 4);
define("WAKE_UP", 8);
define("GO_SOMEWHERE", 16);

Ważne jest, by kolejnym pozycjom nadawać numer, który jest kolejną potęgą dwójki, tzn. najpierw 1, potem 2, następnie 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, itd. :]. Następnie jako parametr przekazujemy wyrażenie, w którym za pomocą operatora sumy logicznej ("|") połączymy te czynności, które chcemy. Ja nie będę tego robił na funkcji, tylko przypiszę wynik do zmiennej:

$zmienna = WALK | SPEAK | GO_SOMEWHERE;

Dobra, tylko teraz mamy to zapisane w jednej zmiennej w postaci jakiejś liczby, która dla osoby postronnej nic nie znaczy. Więc jak wyciągnąć z tego poszczególne stany? Czy trzeba deklarować tysiące IF'ów dla wszystkich możliwych kombinacji? Nie!Jest jeszcze jeden operator - "&" zwany operatorem koniukcji bitowej. Do czego takie coś nam się przyda? Ano za jego pomocą sprawdzimy, czy dany stan jest w zmiennej:

if($zmienna & GO_SOMEWHERE)
{
echo "Kazales mi gdzies isc<br>";
}

if($zmienna & WAKE_UP)
{
echo "Kazales mi wstac<br>";
}

if($zmienna & SPEAK)
{
echo "Kazales mi mowic<br>";
}

if($zmienna & TALK)
{
echo "Kazales mi rozmawiac<br>";
}

if($zmienna & WALK)
{
echo "Kazales mi chodzic<br>";
}

I tak zamiast 125 ifów (lub coś koło tego), których byśmy musieli użyć, gdybyśmy opisywali wszystkie możliwe kombinacje stanów, mamy tylko 5 - po jednym dla każdego stanu. Uruchom teraz skrypt. Zobaczysz, że wszystko wyświetla poprawnie! Pobaw się flagami i zobacz, że się nie myliłem.

Zabawa operatorami

Nigdzie nie jest powiedziane, że do działań na liczbach mamy używać tylko operatorów dodawania, odejmowania, mnożenia, dzielenia itp. Jest wręcz odwrotnie. Nic nie stoi na przeszkodzie, by zrobić instrukcję IF, która wykona się, jeśli dwie liczby zostaną pomnożone :], lub by do zmiennej przypisać wynik porównania. Takie coś ma duże możliwości. Wróćmy do operacji logicznych, czyli tych wykonywanych na bitach (1 lub 0). Załóżmy, że piszemy system przydzielania uprawnień użytkownikom. Mamy kilka pól w tablicy, w których mogą być 1, lub 0. Jedynka oznacza, że coś może dany luser wykonać, zero, że nie. Na uprawnienia wpływa kilka czynników. Lecz jak prosto zrobić, by połączyć ze sobą dwie jedynki, lub zera, przetworzyć tak, by wyszło odpowiednie uprawnienie i nadal mieć liczbę z zakresu 1 - 0? Niby można napisać instrukcję IF, lecz po co, skoro wszystko mamy w zasięgu ręki? Przypomnij sobie operatory "&&", "||", których używałeś dotychczas w instrukcjach IF, by wskazać, że dane dwa warunki muszą być spełnione, lub tylko jeden z nich. Jak one naprawdę działają? Oto mała tabelka opisująca, co zostanie zwrócone, gdy napuścimy na nie dwa bity:

 $X | $Y | $X AND [&&] $Y | $X OR [||] $Y | $X XOR $Y | NOT [!] $X | NOT [!] $Y
1 | 1 | 1 | 1 | 0 | 0 | 0
1 | 0 | 0 | 1 | 1 | 0 | 1
0 | 1 | 0 | 1 | 1 | 1 | 0
0 | 0 | 0 | 0 | 0 | 1 | 1

Tabelka jest zbudowana w taki sposób: pierwsze dwie kolumny to wartości zmiennych $X i $Y. Kolejne pięć to wyniki napuszczenia tych zmiennych na różne operatory: dobrze znane Ci AND, OR, NOT, oraz tajemniczy XOR. Wiersze natomiast to wyniki działania tychże wyrażeń przy takich a takich parametrach. Spróbujmy je przeanalizować...

Wynika z nich, że operator AND zwraca 1 tylko wtedy, gdy obydwa wyrażenia są prawdziwe. OR zwróci 1 zawsze, gdy przynajmniej jedno z wyrażeń jest prawdziwe. XOR zwróci 1, tylko wtedy, gdy jedno z wyrażeń jest prawdziwe. Jednoargumentowy operator NOT natomiast "odwraca" wartość zmiennej. Jeśli dostał 1, zwróci 0, jeśli 0, zwróci 1 :].

Ale po co ja chrzanię te głupoty o operatorach? A bo one się wyśmienicie nadają do zbudowania naszego systemu uprawnień! Zobacz. Chcemy, by użytkownik miał dane uprawnienie tylko wtedy, gdy przynajmniej jedna usługa mu na to pozwala. Możemy napisać oczywiście odpowiednią funkcję, lecz bez problemu zastąpi ją... operator OR - przypatrz się uważnie wynikom i zobacz, że zwraca on 0 tylko wtedy, gdy obydwa warunki są fałszywe. Zatem wystarczy, że tylko jedna ze stu usług zezwoli na daną rzecz i reszta może jej nagwizdać :]. Jeśli nie podobają nam się takie zasady, możemy wykorzystać consensus, czyli powszechną zgodę, czyli operator AND :]. Możliwości jest dużo, zastosowań jeszcze więcej. Oto przykładowy kod:

<?php
$usluga1['newsy'] = 1;
$usluga1['komentarze'] = 1;
$usluga1['artykuly'] = 0;

$usluga2['newsy'] = 0;
$usluga2['komentarze'] = 0;
$usluga2['artykuly'] = 0;

$usluga3['newsy'] = 1;
$usluga3['komentarze'] = 0;
$usluga3['artykuly'] = 0;

$usluga4['newsy'] = 0;
$usluga4['komentarze'] = 1;
$usluga4['artykuly'] = 1;

$newsy = $usluga1['newsy'] || $usluga2['newsy'] || $usluga3['newsy'] || $usluga4['newsy'];
$komentarze = $usluga1['komentarze'] || $usluga2['komentarze'] || $usluga3['komentarze'] || $usluga4['komentarze'];
$artykuly = $usluga1['artykuly'] || $usluga2['artykuly'] || $usluga3['artykuly'] || $usluga4['artykuly'];

if($newsy){
echo "Masz dostęp do newsów<br>";
}
if($komentarze){
echo "Masz dostęp do komentarzy<br>";
}
if($artykuly){
echo "Masz dostęp do artykułów<br>";
}
?>

Spróbuj pobawić się uprawnieniami przydzielanymi standartowo przez każdą z usług :]. Jak to mawiają Anglicy, "It's cannons!" (tłum. "To działa!" :]).

Epilog

PHP jest (jak wiele innych języków) językiem zorientowanym wyrażeniowo i dlatego należy jak najwięcej czasu poświęcić, by przyswoić sobie wiedzę o nich. Tylko dzięki ich poznaniu będziesz mógł w pełni wykorzystać jego potencjał do końca. Nie odkładaj tego na później - wyszukuj informacje o różnych operatorach i eksperymentuj z nimi, z ich ułożeniem. Stosuj nawiasy, by zapewnić odpowiednią kolejność wykonywania działań. I najważniejsze: staraj się dojść do takiej wprawy, by intuicyjnie przekształcać i układać nawet najbardziej skomplikowane wyrażenia. Wierz mi: dzięki nim poznanie nowego języka programowania będzie dla Ciebie tylko kwestią czasu...

Autor: Tomasz "Zyx" Jędrzejewski, www.zyxist.com

 

Źródło: http://webcity.pl/webcity/wyrazenia_w_php

Licencja: Creative Commons - użycie niekomercyjne - bez utworów zależnych