Do budowy bardzo prostych programów wystarczy już wiedza, którą zdobyliśmy z ostatnich części kursu. jeśli jednak chcielibyśmy napisać coś nieco bardziej skomplikowanego, szybko okaże się, że wiemy za mało. Dziś nauczymy się czym są funkcje oraz jak z nich korzystać, poznamy też sposób na dzielenie większych programów na kilka plików.
Operatory logiczne
Poznaliśmy już większość operatorów oferowanych przez PHP. W poprzedniej części były omówione instrukcje warunkowe. Do pełnego ich wykorzystania musimy zapoznać się jeszcze z operatorami logicznymi (tabela 1). Pozwalają one grupować wyrażenia warunkowe w jedną całość.
W tabeli 1 dla przykładu użyto zmiennych $a oraz $b, pamiętajmy jednak, że w prawdziwym wyrażeniu można użyć w ich miejsce innych wyrażeń, które zwracają wartość logiczną (lub dają się skonwertować do wartości true lub false):
$b);
$w2 = ($a == $b) || ($b < $c);
$w3 = ($a > $b) || ( ($a < $b) && ($b > $c) );
echo \'w1 = \'.($w1 ? \'true\' : \'false\').\'
\';
echo \'w2 = \'.($w2 ? \'true\' : \'false\').\'
\';
echo \'w3 = \'.($w3 ? \'true\' : \'false\').\'
\';
?>
Powyższy skrypt zwróci:
{stala}w1 = true{/stala}
{stala}w2 = true{/stala}
{stala}w3 = false{/stala}
Przeanalizujmy powyższe wyrażenia logiczne, przyjrzyjmy się także rysunkowi 1, który na prostym przykładzie stara się wytłumaczyć ich funkcjonowanie.
Zauważmy sposób używania nawiasów w powyższych wyrażeniach. Nie wszędzie są one tam potrzebne, jednak użycie ich sprawia, że całość staje się bardziej czytelna i mamy zawsze pewność, że PHP będzie analizował wyrażenia w takiej kolejności na jakiej nam zależy (poszczególne operatory posiadają tak zwane priorytety, według których wyrażenia są analizowane – np. * ma wyższy priorytet od +, dzięki czemu najpierw wykonywane jest mnożenie, a dopiero następnie dodawanie – http://www.php.net/manual/pl/
language.operators.php#language.operators. precedence).
Operatory bitowe
W PHP operatory bitowe są używane rzadko, warto jednak wiedzieć że istnieją Operatory bitowe, tak jak wskazuje na to ich nazwa, służą do operowania bitami. Wszystkie operatory bitowe obsługiwane przez PHP zebrane zostały w tabeli 2.
W tabeli 3 zostało zademonstrowane działanie operatorów &, | oraz ^.
Skrypt demonstrujący działanie kilku operatorów bitowych:
Poniższy skrypt pokazuje sposób wykorzystania kilku poznanych do tej pory technik do wyświetlenia binarnej postaci zadanej liczby:
= 0; $i-) {
echo ($liczba & $maska) > $i;
$maska >= 1;
}
?>
W wyniku działania programu otrzymamy:
{stala}10101111{/stala}
czyli to, czego oczekujemy.
Program króciutki, jednak na pierwszy rzut oka może wydawać się skomplikowany. Zmienna $maska jest tu wykorzystywana do sprawdzania czy bit na danej pozycji ma wartość 1 czy 0. Zmienna $liczba przechowuje liczbę, którą chcemy wyświetlić w postaci binarnej. Liczba jest 8-bitowa (ma bitowa wartość 10101111). W związku z tym budujemy pętlę wykonującą się 8 razy (od 7 w dół, aż do 0 włącznie).
W każdym przebiegu pętli wykonujemy operacje & na zmiennej $liczba za pomocą maski $maska. Następnie przesuwamy otrzymany wynik o $i w prawo. Na koniec przesuwamy maskę o jeden bit w prawo (otrzymuje wiec w kolejnych przebiegach maski 100000000 01000000 00100000 itd.). Poniżej przedstawiam krok po kroku trzy przebiegi programu:
Inicjalizacja zmiennych $maska wartością 1 oraz $liczba wartością 90.
1. Pierwszy przebieg pętli – $i = 7:
- wykonujemy operację $liczba & $maska, w wyniku której otrzymujemy wartość 128 (10101111 & 10000000 = 10000000),
- interesuje nas bit z lewej strony, więc przesuwamy otrzymany wynik o 7 w prawo i otrzymujemy 00000001 (100000000 > 7 = 00000001), czyli wypisujemy 1,
- przesuwamy maskę o jeden w prawo i otrzymujemy 01000000.
2. Drugi przebieg pętli – $i = 6:
- wykonujemy operację $liczba & $maska (pamiętajmy, że maskę mamy już przesuniętą o jeden w prawo), w wyniku której otrzymujemy wartość 0 (10101111 & 01000000 = 00000000),
- tak jak poprzednio interesuje nas bit zmiennej $liczba na miejscu, w którym ustawiony jest bit zmiennej $maska, czyli na pozycji 6 (licząc od 0), przesuwamy więc otrzymany wynik o $i w prawo i otrzymujemy 00000000 (00000000 > 6 = 00000000), wypisujemy więc 0,
- przesuwamy maskę o jeden w prawo i otrzymujemy 00100000.
3. Trzeci przebieg pętli – $i = 5:
- wykonujemy operację $liczba & $maska, w wyniku której otrzymujemy wartość 32 (10101111 & 00100000 = 00100000),
- tak jak poprzednio interesuje nas bit zmiennej $liczba na miejscu, w który ustawiony jest bit zmiennej $maska, czyli na pozycji 5 (licząc od 0), przesuwamy więc otrzymany wynik o $i w prawo i otrzymujemy 00100000 (00100000 > 6 = 00000001), wypisujemy więc 1,
- przesuwamy maskę o jeden w prawo i otrzymujemy 00010000.
I tak dalej, aż do końca pętli jeśli nadal jest problem ze zrozumieniem powyższego algorytmu, należy przyjrzeć się rysunkowi 2, wziąć kartkę i długopis do reki, a następnie uruchomić kalkulator w systemie Windows (przestawić go w tryb Naukowy – menu Widok | Naukowy – operacja & to AND, tryb binarny to Bin, tryb dziesiętny do Dec) i prześledzić kolejne kroki działania powyższego programu.
Funkcje
Prawie każdy pogram wykonuje w wielu miejscach dokładnie te same czynności. Mogą to być jakieś obliczenia, wyświetlenie napisu w odpowiedniej formie, zapisanie informacji do pliku bądź bazy danych, czy jakakolwiek inna czynność.
Niezbyt wygodne jest wielokrotne wpisywanie (bądź kopiowanie) tego samego kawałka kodu za każdym razem, gdy chcemy wykonać tę samą czynność. A co się stanie, gdy okaże się, że skopiowany w wiele miejsc fragment kodu jest błędny? Trzeba będzie odszukać ten fragment w programie i zmodyfikować go tyle razy, ile on tam wystąpi. Łatwo przy tym przegapić jakiś fragment bądź wprowadzić dodatkowy błąd. Na szczęście mamy do dyspozycji funkcje.
Funkcje są wydzielonymi i nazwanymi fragmentami programu, które mogą być wywoływane w dowolnym jego miejscu (mogą nawet wywoływać same siebie – są to tak zwane funkcje rekurencyjne). PHP oferuje wiele standardowych funkcji, jednak każdy programista tworząc program buduje wiele własnych.
A oto jak zbudowana jest typowa funkcja w PHP:
function NazwaFunkcji([$parametr1, $parametr2...]) {
//ciało funkcji
return WynikDziałania; //opcjonalny
}
Jak widać, funkcja może przyjmować parametry (ale nie musi – wówczas po jej nazwie pozostawiamy pusty nawias ()) oraz za pomocą instrukcji {stala}return{/stala} zwracać wynik. Zbudujmy wiec prostą funkcję podnoszącą liczbę do potęgi:
function potega($podstawa, $wykladnik) {
$wynik = $podstawa;
for ($i=1; $i<$wykladnik; $i++) {
$wynik *= $podstawa; /* jak pamiętamy to to samo, co $wynik = $wynik * $podstawa */
}
return $wynik;
}
Wywołanie tej funkcji sprowadza się do wpisania nazwy funkcji i podania parametrów:
Ale, jak widać, funkcja ta nie jest doskonała:
echo potega (2,0);
zwraca 2, podczas gdy powinna zwrócić 1. Korzystając z funkcji wystarczy zmodyfikować jej kod:
function potega($podstawa, $wykladnik) {
if ($wykladnik == 0) {
return 1;
}
$wynik = $podstawa;
for ($i=1; $i<$wykladnik; $i++) {
$wynik *= $podstawa; /* jak pamiętamy to to samo, co $wynik = $wynik * $podstawa */
}
return $wynik;
}
A jak zabezpieczyć się przed możliwością podania przez użytkownika ułamkowego wykładnika? Sprawdzi to funkcja {stala}is_int{/stala}:
function potega($podstawa, $wykladnik) {
if (!is_int($wykladnik)) {
echo \'Wykładnik musi być liczbą całkowitą!\';
return; /* nie zwracamy nic, jednak instrukcja return kończy działanie funkcji */
}
if ($wykladnik == 0) {
return 1;
}
$wynik = $podstawa;
for ($i=1; $i<$wykladnik; $i++) {
$wynik *= $podstawa; /* jak pamiętasz to to samo, co $wynik = $wynik * $podstawa */
}
return $wynik;
}
Zmodyfikujmy powyższą funkcję tak, aby podnosiła podstawę poprawnie do potęgi, również gdy wykładnik będzie liczbą ujemną.
Dla przypomnienia:
a-n = 1/an
Najczęściej funkcje zwracają jakieś wartości, nie jest to jednak konieczne. Jeśli nasza funkcja nie musi nic zwrócić, a jedynie ma za zadanie wykonać określoną operację, nie musimy w niej umieszczać instrukcji {stala}return{/stala}.
Pamiętajmy jednak, że instrukcja {stala}return{/stala} może występować samodzielnie (tak jak w powyższym przykładzie, w przypadku niecałkowitego wykładnika). Jeśli po return nie wystąpi żadna zmienna ani wyrażenie, PHP uzna, że funkcja nie zwraca co prawda żadnej wartości, jednak ma się zakończyć w miejscu wystąpienia instrukcji {stala}return{/stala}. Istotny jest bowiem fakt, że instrukcja {stala}return{/stala} zawsze - niezależnie od tego czy powoduje zwrócenie jakiej wartości, czy też nie - kończy działanie funkcji w miejscu, w którym została umieszczona, powodując dalsze wykonanie programu od miejsca występującego zaraz po wywołaniu tej funkcji - rys. 3.
Jak wspomniałem, funkcja może wywoływać się sama. Tak można by napisać funkcję obliczającą silnię:
function silnia($n) {
$wynik = $n;
if ($n>1) {
$wynik *= silnia($n-1);
}
return $wynik
}
Wersja funkcji nie jest idealna, jednak jej poprawienie pozostawiam Czytelnikom (informacje o silni na stronie http://pl.wikipedia.org/wiki/Silnia).
Podział dużych programów
Większe witryny internetowe zbudowane są zazwyczaj z dużej liczby skryptów. Każdy z nich wykorzystuje wiele identycznych fragmentów kodu. Wiemy już, że powtarzające się fragmenty programu możemy zgrupować w funkcje. Co jednak zrobić, gdy te same funkcje używane są przez różne skrypty zgromadzone w osobnych plikach?
Zapisujemy powiązane ze sobą funkcje w osobnych plikach, np. osobno funkcje wykonujące operacje matematyczne, osobno operacje na plikach i tak dalej. W skrypcie, w którym potrzebujemy możliwości oferowanych przez dane funkcje, dołączamy plik z nimi korzystając z instrukcji: {stala}include{/stala}, {stala}include_once{/stala}, {stala}require{/stala}, {stala}require_once{/stala}.
Z tych instrukcji korzysta się w analogiczny sposób, jednak ich działanie jest nieco inne. Instrukcje {stala}include{/stala} dołączają plik do skryptu, nie zgłaszają jednak błędu, jeśli wymagany plik nie istnieje - zgłaszają jedynie ostrzeżenie (wyświetlanie ostrzeżeń można wyłączyć w pliku php.ini), nie przerywając dalszego działania skryptu.
Instrukcja {stala}require{/stala} zgłasza błąd w przypadku braku pliku, który chcemy dołączyć do skryptu, przerywając jednocześnie dalsze wykonanie bieżącego skryptu.
Instrukcje z przyrostkiem {stala}_once{/stala} dołączają zadany plik tylko raz, niezależnie od tego, ile instrukcji {stala}require_once{/stala} bądź {stala}include_once{/stala} dotyczących danego pliku wystąpi w kodzie.