Obrazek użytkownika cytrynek

Kurs PHP

Treść: 

Z poprzednich artykułów dowiedzieliście się jak zainstalować serwer z obsługą protokołu HTTP w taki sposób aby mógł wyświetlać strony internetowe (instalacja LAMP). Dowiedzieliście się także jak zainstalować aplikację służącą zarządzaniu treścią (CMS) w postaci CMS DRUPAL (Drupal na wirtualnej maszynie).

Według definicji PHP to obiektowy język programowania zaprojektowany do generowania stron internetowych i budowania aplikacji webowych w czasie rzeczywistym.

Poniższy kurs pochodzi ze strony pl.wikibooks.org/wiki/PHP

Pierwszy skrypt

W tym rozdziale napiszemy pierwszy skrypt PHP.

Skrypt PHP

Język PHP umożliwia zagnieżdżanie skryptów wykonywanych po stronie serwera.

Porada Ćwiczenie
Utwórz taki plik i zapisz z rozszerzeniem PHP:

Witaj Świecie <?php echo 2*2; ?>

Następnie uruchom go w przeglądarce www, z poziomu serwera (np. http://localhost/skrypt.php).

Wybierz w przeglądarce opcję podglądu źródła i porównaj źródło otrzymane przez przeglądarkę z oryginalnym plikiem.

Przeglądarka otrzymuje tylko już przetworzony kod, w tym przypadku <? echo 2*2; ?> zostało zamienione na 4. O szczegółach tej instrukcji - w dalszej części podręcznika.

Interpreter PHP rozpoznaje kod do przetworzenia po znakach <?php i ?>. Każdy kod między nimi jest programem PHP.

Na początku będziesz musiał poznać instrukcję echo, która wysyła tekst do przeglądarki:

 <?php
 echo 42;
 ?>

Funkcja ta została omówiona tutaj, ponieważ jej znajomość przydaje się do nauki zmiennych i wyrażeń; bardziej szczegółowo zostanie to omówione w kolejnych rozdziałach.

Skrypt PHP wewnątrz dokumentu HTML

Jak wspomnieliśmy wcześniej, skrypty PHP możemy mieszać ze zwykłym kodem HTML. Kod naszych algorytmów zamykany jest wewnątrz specjalnych wstawek wyłapywanych przez interpreter oraz zmienianych później na wygenerowany kod. Tak też zrobimy w naszym pierwszym skrypcie, który tradycyjnie wyświetli na ekranie przeglądarki napis "Hello world!".

  1. <?xml version="1.0" encoding="utf-8" standalone="no"?>
    
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    
  3. <html xmlns="http://www.w3.org/1999/xhtml">
    
  4.  <head>
    
  5.   <title>Pierwszy skrypt PHP</title>
    
  6.  </head>
    
  7.  <body>
    
  8.  <?php
    
  9.    echo 'Hello world!';
    
  10.  ?>
    
  11.  </body>
    
  12. </html>
    

W powyższym przykładzie widzimy skrypt PHP osadzony za pomocą znaczników <?php oraz ?> w zwyczajnym kodzie HTML, który w następnych przykładach będziemy już pomijać, aby nie marnować miejsca. Wewnątrz mamy jedną linijkę:

echo 'Hello world!';

Nakazuje ona wyświetlenie tekstu "Hello world!" w przeglądarce. Tekst do wyświetlenia ograniczyliśmy apostrofami. Średnik na końcu informuje o zakończeniu komendy. Możemy rozbić to na kilka linijek, ale dla PHP nie będzie to miało większego znaczenia - końcem komendy jest właśnie średnik.

  1. <?php
    
  2. echo 
    
  3.      'Hello world!';
    
  4. ?>
    

Przejście do nowej linii poza apostrofami jest jednym z tzw. białych znaków ignorowanych przez interpreter. Innymi są spacja oraz tabulacja. Między tekstem, a komendą echo możemy wstawić niezliczoną liczbę tabulatorów i zejść do nowej linii, ale nie zmieni to w żaden sposób tego, jak PHP wykona nasz skrypt, gdyż znaki te zostaną zignorowane.

W skrypcie możemy umieścić więcej wyrażeń, oczywiście odseparowanych średnikami:

  1. <?php
    
  2. echo 'To jest tekst 1';
    
  3. echo 'To jest tekst 2';
    
  4. echo 'A to jest tekst 3';
    
  5. ?>
    

Zauważ, że choć w skrypcie mamy trzy komendy wyświetlenia trzech tekstów, przeglądarka wyświetli je nam w jednej linijce. Jest tak dlatego, ponieważ nowa linia oznaczana jest specjalnym znacznikiem HTML, którego tam nie umieściliśmy. Oto poprawiona wersja skryptu:

  1. <?php
    
  2. echo 'To jest tekst 1<br>';
    
  3. echo 'To jest tekst 2<br>';
    
  4. echo 'A to jest tekst 3<br>';
    
  5. ?>
    
Porada Mieszanie kodu PHP z HTML-em spotykane jest najczęściej jedynie w prostych skryptach. W złożonych aplikacjach znacznie utrudnia jakiekolwiek modyfikacje wyglądu. W dalszych rozdziałach tego podręcznika nauczymy się korzystać z szablonów, które pomogą nam całkowicie oddzielić jedno od drugiego, lecz na razie wszystko będzie wymieszane.

Komentarze

Kiedy twoje skrypty staną się bardziej rozbudowane, w kodzie przyda się pewna organizacja. Pomogą tu z pewnością komentarze służące do opisywania, co robi dana część algorytmu, jak działa, jakie ma wymagania itd. Mogą także pomóc w usystematyzowaniu całości lub też zamieszczeniu informacji o autorze oraz prawach autorskich. Komentarz jest całkowicie ignorowany przez interpreter PHP i nie wpływa na wynik jego działania.

Istnieją trzy rodzaje komentarzy:

  1. <?php
    
  2. /*
    
  3.   komentarz wieloliniowy
    
  4.   może być rozbijany na wiele linijek.
    
  5.   Cały ten tekst jest ignorowany przez interpreter
    
  6. */
    
  7.  
  8. // to jest komentarz jednoliniowy - obowiązuje do końca danej linijki
    
  9. # to jest jeszcze jeden komentarz jednoliniowy
    
  10.  
  11. ?>
    

Oto przykładowe zastosowanie komentarzy:

  1. <?php
    
  2. /*
    
  3.  * Generator miniaturek obrazków
    
  4.  *  Wersja: 1.0
    
  5.  *  Autor: Adam Kowalski (adres@example.com)
    
  6.  *  Licencja: GNU GPL
    
  7.  *
    
  8.  * Użycie: tu opis użycia...
    
  9.  */
    
  10.  
  11. // początek generowania obrazka
    
  12. echo 'Tu są jakieś komendy';
    
  13. echo 'Dużo komend...';
    
  14.  
  15. // zmniejszanie
    
  16. echo 'Tu obrazek się zmniejsza';
    
  17.  
  18. // wysyłanie wyniku
    
  19. echo 'Tu wysyłamy obrazek do przeglądarki';
    
  20.  
  21. ?>
    

Cały kod został opisany i dzięki temu nawet po wielu miesiącach autor będzie wiedział, co do czego służy. Komentarze przydają się przy zbiorowych pracach nad projektem. Można w nich umieszczać informacje, co należy w danym fragmencie poprawić albo jak on funkcjonuje. W procesie usuwania błędów (debugowania) komentarzy można używać do chwilowego wyłączania kawałków kodu, aby zobaczyć, czy to przypadkiem one nie powodują problemu oraz jak aplikacja radzi sobie bez nich. Stosowanie komentarzy należy do dobrych praktyk programistycznych i nigdy nie należy o nich zapominać.

Znaczniki wstawek PHP

Oficjalnymi znacznikami rozpoczynającymi i kończącymi kod PHP są <?php ... ?>, jednak wciąż można spotkać starszą formę <? ... ?>. Nie zalecamy jej stosowania, ponieważ wiele serwerów ma ją wyłączoną i Twoje skrypty bez przeróbek nie będą na nich działać.

Wartą odnotowania rzeczą jest możliwość pominięcia końcowego ?> jeśli chcemy, aby kod PHP ciągnął się do końca pliku. Taka też konwencja zostanie przyjęta w dalszych rozdziałach. Jest ona stosowana przez wiele skryptów, gdyż pozwala uniknąć przypadkowego pozostawienia np. spacji czy pustej linii na końcu pliku, co niekiedy może być źródłem poważnych problemów, o czym przekonamy się z dalszych rozdziałów. Nasz skrypt będzie zatem prezentować się następująco:

  1. <?php
    
  2. echo 'To jest tekst 1<br>';
    
  3. echo 'To jest tekst 2<br>';
    
  4. echo 'A to jest tekst 3<br>';
    

Uruchom go i przekonaj się, że to rzeczywiście działa!

 

Zmienne i tablice

Samo wyświetlanie tekstu jest niezbyt ciekawe. Wszystkie programy komputerowe operują na danych, zatem w językach programowania muszą istnieć mechanizmy do przechowywania informacji w pamięci. Do tego służą zmienne i tablice, którymi zajmiemy się właśnie teraz.

Dane

PHP, jak każdy inny język programowania, operuje na danych. Niektóre z nich są zapisane na sztywno w skrypcie. Każda rzecz, która reprezentuje jakąkolwiek informację, zwana jest wyrażeniem. Oto prosty przykład:

 8

To jest wyrażenie reprezentujące liczbę całkowitą.

 6.454

To jest wyrażenie reprezentujące liczbę zmiennoprzecinkową będącą komputerowym, skończonym przybliżeniem (nie wartością dokładną) liczby rzeczywistej.

 0x6F44

To jest wyrażenie reprezentujące liczbę zapisaną w systemie szesnastkowym.

 07647

To jest wyrażenie reprezentujące liczbę zapisaną w systemie ósemkowym.

 'To jest tekst bez znaków specjalnych'
 "To też jest tekst, ale \t-\t ze znakami specjalnymi (tabulatorami)"

Powyżej mamy dwa wyrażenia reprezentujące tekst. Pomiędzy nimi istnieje istotna różnica. Apostrofy korzystają z jednego tylko znaku specjalnego: \' - pozwala on oznaczyć wewnątrz tekstu znak apostrofu. Gdybyśmy zapomnieli o poprzedzającym backslashu, PHP uznałby, że w tym momencie kończy się wyrażenie tekstowe i dalej jest już normalny skrypt.

Cudzysłów posiada więcej takich kodów formatujących:

 "\n - tak robimy zejście do nowej linijki w systemach uniksowych (np. Linux, Mac OS X)"
 "\r - tak kiedyś się robiło w Mac OS Classic"
 "\r\n - a tak wciąż się robi w systemach Windows"
 "Wyświetlimy cudzysłów: \" ..."
 "Wstawimy tabulator: \t"
 "Wstawiamy zmienną $zmienna"

W obu przypadkach, aby wyświetlić backslash, należy napisać go podwójnie:

 'Oto backslash: \\ '

Otrzymamy dzięki temu tekst:

 Oto backslash: \

Oto złożone wyrażenie (reprezentuje ono sumę dwóch mniejszych wyrażeń):

 5 + 7

Oto wyrażenie będące połączeniem dwóch mniejszych wyrażeń tekstowych.

 'Tekst A '.'Tekst B'

Znak kropki oraz plusa to tzw. operatory. Wykonują one pewne operacje na dwóch innych wyrażeniach i zwracają jakąś wartość, zatem same także stają się wyrażeniem. Podobnie jak w matematyce, obowiązuje ściśle określona kolejność ich wykonywania, a zmieniać ją możemy za pomocą nawiasów:

 5 * (6 + 8)
Uwaga! Wyrażeniem nazywamy dowolny element języka PHP reprezentujący jakąś informację, którą można przetwarzać.

Pamiętaj, że PHP potrafi automatycznie rozpoznawać typ wyrażenia i w razie potrzeby odpowiednio go konwertować. Dla przykładu instrukcja echo wymaga danych tekstowych. W skrypcie:

  1. <?php
    
  2. echo 7;
    

Interpreter dokona konwersji liczby na tekst, a dopiero potem spowoduje jego wyświetlenie.

Funkcje

Informatyka wiele zawdzięcza matematyce. W programowaniu występuje wiele pojęć zaczerpniętych bezpośrednio od królowej nauk. Jednym z nich jest funkcja, do której możemy wprowadzać parametry, a w zamian otrzymujemy jakiś wynik. Poniżej będzie pierwszy "naprawdę" dynamiczny skrypt, jaki stworzymy. Skorzystamy w nim z dwóch funkcji, aby wyświetlić aktualny czas:

  1. <?php
    
  2.  
  3. echo 'Dzisiaj mamy: '.date('d.m.Y').'<br/>';
    
  4. echo 'Od 1.1.1970 minęło: '.time().' sekund';
    

Jak widać, składnia funkcji jest następująca: nazwaFunkcji(parametry). Jeśli funkcja nie posiada parametrów, nawiasy są puste. Jeżeli jest ich więcej, niż jeden, oddzielamy je od siebie przecinkiem. Zaś sam parametr jest niczym innym, jak... pewnym wyrażeniem. Wynik działania funkcji również jest wyrażeniem, dlatego możemy ją wpleść w nasz tekst za pomocą operatora kropki.

Powyższy kod zachowa się następująco:

  1. Wykonana zostanie funkcja date(), przyjmując za argument tekst 'd.m.Y', a jej wynikiem będzie aktualna data.
  2. Następnie powyżej opisany wynik zostanie połączony (operator .) z sąsiednimi tekstami, przez co powstanie wyrażenie, np. "Dzisiaj mamy: 01.01.2010". Echospowoduje wstawienie tego wyrażenia do kodu HTML strony wynikowej.
  3. Następna linijka wywoła się analogicznie, wywołanie funkcji zakończy się zwróceniem wyniku, który zostanie połączony z sąsiednimi wyrażeniami w jeden tekst.
Uwaga! Uwaga!
Funkcja time() wyświetla czas uniksowy, dlatego nie można używać tej funkcji bezpośrednio do określenia ile czasu minęło od konkretnej daty.
Uwaga! Uwaga!
echo oraz podobna instrukcja print nie są funkcjami ani wyrażeniami! Są to instrukcje języka PHP - mają tę cechę, że nie musimy stosować przy nich nawiasów. print może zachowywać się jak wyrażenie, które zawsze wartościowane jest przez 1.

Zmienne

Innym pojęciem matematycznym jest zmienna, zawierająca pewną informację, przeważnie uzyskaną w trakcie wykonywania skryptu. Można traktować ją jako pojemnik, do którego będziemy mogli w trakcie wykonywania skryptu wstawić dowolną informację, zapamiętując ją w ten sposób. Umożliwia to przechowywanie i przetwarzanie danych w potrzebnym nam celu.

Każdej zmiennej przypisujemy własną, unikalną nazwę, która jednoznacznie ją identyfikuje. Język PHP wymaga, aby zaczynała się ona od znaku dolara, a następnie od litery (ew. podkreślenia). Dalsza część nazwy może już zawierać cyfry. Stosując wielkie litery trzeba uważać, ponieważ dla interpretera są one rozróżnialne od małych, co ma istotne znaczenie. $zmienna i $Zmienna to dwie różne zmienne. Przykłady poprawnych nazw zmiennych:

$a, $b, $foo, $_50, $_Foo, $moja_zmienna, $mojaZmienna3

Przykłady nieprawidłowych nazw:

$5a, $'a', $
Porada PHP zezwala na używanie w nazwach zmiennych także znaków o kodach od 128 do 255, wśród których znajdują się m.in. polskie litery.

Aby przypisać wartość do zmiennej, należy skorzystać z operatora =. Po lewej stronie umieszczamy naszą zmienną, a po prawej dowolne wyrażenie określające wartość, która zostanie zapisana w zmiennej. Oto, jak wygląda to w praktyce:

  1. <?php
    
  2. // inicjujemy zmienna $czas aktualnym czasem w sekundach od 1.1.1970
    
  3. $czas = time();
    
  4. $czas2 = $czas / 60;
    
  5.  
  6. echo 'Od 1.1.1970 minęło '.$czas.' sekund<br/>';
    
  7. echo 'Od 1.1.1970 minęło '.$czas2.' minut<br/>';
    
  8. echo 'Od 1.1.1970 minęło '.($czas / 3600).' godzin';
    

W powyższym przykładzie stworzyliśmy zmienne $czas i $czas2, zapisując w pierwszej z nich liczbę sekund zwróconą przez wywołanie funkcji time(). Następnie wykorzystaliśmy ją w obliczeniach w kolejnej linijce. Poznaliśmy w ten sposób jedno z zastosowań zmiennych. Zachowaliśmy w nich wynik działania jednej sekcji programu, aby potem używać go wielokrotnie gdzie indziej, bez konieczności każdorazowego odwoływania się do funkcji i zbędnego liczenia tego samego.

PHP, w przeciwieństwie do innych języków programowania, ma bardzo liberalne reguły stosowania zmiennych. Nie trzeba ich nigdzie uprzednio deklarować, a interpreter sam nam dopasuje rodzaj informacji do naszych potrzeb (ustali tzw. typ zmiennej). Dana zmienna jest tworzona podczas pierwszego jej wykorzystania w skrypcie. Jest sporo sytuacji, w których zachowanie to jest pożądane, lecz może też utrudnić pracę. Aby mieć świadomość zagrożenia, wyobraź sobie taką sytuację: programista pisząc szybko, może popełnić literówkę. Jeżeli zostanie ona popełniona podczas wpisywania nazwy, PHP utworzy zmienną zawierającą tę literówkę. W teorii nie jest to żadnym błędem, jednak możemy się domyślać, że zapis obliczeń do innej zmiennej spowoduje błędne działanie programu, a programista będzie musiał spędzić dużo czasu na odnalezienie przyczyny. Słowo "teoria" nie znalazło się tu przypadkowo. Podczas instalowania PHP wspominaliśmy o poziomach raportowania błędów. Im wyższy poziom, tym większej ilości rzeczy czepia się PHP. Na poziomie E_ALL zdefiniowanym w rekomendowanym pliku php.ini takie beztroskie podejście do zmiennych nie jest tolerowane. Tutaj PHP wymaga już, aby podczas pierwszego użycia zmiennej została jej przypisana jakaś wartość, ponieważ inaczej otrzymamy powiadomienie (ang. notice) o próbie odwołania się do nieistniejącej zmiennej. Popatrzmy sobie na ten przykład:

  1. <?php
    
  2.  
  3. echo $a * 16 + 5;
    

Zmienna $a nie została w tym kodzie nigdzie początkowo zadeklarowana. Otwórz swój plik php.ini, odnajdź dyrektywę error_reporting i zmień jej wartość naE_ALL | ~E_NOTICE. Wyłączysz w ten sposób wyświetlanie powiadomień. Zrestartuj serwer i uruchom powyższy skrypt. Wynikiem powinno być "5". PHP bez pytania podstawił do $a wartość neutralną 0. Przywróć teraz poprzedni poziom (E_ALL | E_STRICT) i ponownie uruchom ten skrypt. Oprócz wyniku, ujrzysz też komunikat:

Notice: Undefined variable: a in D:\Serwer\www\katalog\twojskrypt.php on line 3

Sytuację można rozwiązać, ręcznie inicjując zmienną $a wartością 0:

  1. <?php
    
  2. $a = 0;
    
  3. echo $a * 16 + 5;
    

Zalecane jest, aby wszystkie skrypty pracowały bezproblemowo przy włączonych powiadomieniach. Jeżeli zajdzie sytuacja, że odwołanie się do zmiennej, która może jeszcze nie istnieć, jest potrzebne, istnieje kilka sposobów "oszukania" PHP, lecz poznamy je w dalszej części podręcznika.

Początkujący programiści mają tendencję do tworzenia dużej liczby tzw. zmiennych tymczasowych, które nie wnoszą absolutnie niczego do programu poza wydłużeniem kodu i zmniejszeniem wydajności. Po każdym etapie przetworzenia jakiejś informacji, umieszczana jest ona w nowej zmiennej. Takie podejście jest nieprawidłowe. Oto przykłady "złych" skryptów:

  1. <?php
    
  2.  
  3. $tekst = 'To jest jakiś tekst';
    
  4. $tekstMaly = strtolower($tekst);
    
  5. $tekstBezpieczny = addslashes($tekstMaly);
    
  6. echo $tekstBezpieczny;
    

Przykład 2:

  1. <?php
    
  2.  
  3. $format = 'd.m.Y';
    
  4. echo date($format);
    

W pierwszym skrypcie niepotrzebnie po każdym etapie przetwarzania tekstu tworzymy dla niego nową zmienną. Możemy to poprawić na dwa sposoby. Pierwszy:

  1. <?php
    
  2.  
  3. $tekst = 'To jest jakiś tekst';
    
  4. $tekst = strtolower($tekst);
    
  5. $tekst = addslashes($tekst);
    
  6. echo $tekst;
    

Drugi sposób rozwiązania tego problemu - bez użycia zmiennych (już trochę mniej czytelny):

  1. <?php
    
  2. echo addslashes(strtolower('To jest jakiś tekst'));
    

W drugim "złym" skrypcie w ogóle niepotrzebnie tworzymy zmienną - format daty możemy wpisać bezpośrednio do funkcji.

  1. <?php
    
  2. echo date('d.m.Y');
    

Jednak nie zawsze jest to lepsza wersja. Jeżeli nasz skrypt bardzo często będzie formatować różne daty, a my będziemy chcieli mieć możliwość zmieniania tych formatów w przyszłości, użycie zmiennych bardzo ułatwiłoby sprawę - zmieniamy format daty w jednej zmiennej, zamiast w kilku czy kilkudziesięciu wywołaniach date().

Nauczenie się, kiedy warto użyć zmiennych, a kiedy nie, to kwestia praktyki. W niniejszym podręczniku będziemy zwracali na tę kwestię baczną uwagę. Jeśli zajdzie potrzeba użycia zmiennych tymczasowych - wyjaśnimy, dlaczego, bowiem całkowite rezygnowanie z ich użycia także może rodzić wiele problemów.

Typy

Do tej pory miałeś okazję zauważyć, że istnieje w PHP pewne rozróżnienie na tekst i liczby. Skoncentrujemy się teraz na poznaniu większej ilości typów oraz pokazaniu, jak PHP dokonuje konwersji między nimi.

Istnieją trzy kategorie typów: wielkości skalarne, typy złożone oraz typy specjalne. Dokumentacja wymienia jeszcze jedną, lecz stworzoną na jej własne potrzeby do zaznaczania niektórych rzeczy (powiemy o niej później).

Wielkości skalarne

Pierwszym typem skalarnym jest liczba całkowita. Jej angielskim określeniem jest integer, używany bywa skrót int. Może być ona zapisana w trzech systemach liczbowych: dziesiętnym, szesnastkowym albo ósemkowym:

  1. <?php
    
  2. $a = 1234; // liczba całkowita
    
  3. $a = -123; // liczba całkowita ujemna
    
  4. $a = 0123; // zapis ósemkowy (odpowiednik dziesiętnego 83)
    
  5. $a = 0x1A; // zapis szesnastkowy (odpowiednik dziesiętnego 26)
    

Możemy także korzystać z wartości ułamkowych zwanych liczbami zmiennoprzecinkowymi (ang. floating point numbers albo skrótowo float), które są przybliżeniem liczb rzeczywistych (ważne - nigdy nie zawierają dokładnej wartości, prawie zawsze jest to odrobinę różniąca się liczba, dlatego mówi się o "przybliżeniu"). Przy ich zapisywaniu obowiązują reguły języka angielskiego, więc części całkowite od ułamkowych oddzielamy za pomocą kropki. Także i tu mamy do wyboru kilka sposobów zapisu:

  1. <?php
    
  2. $a = 1.234; 
    
  3. $a = 1.2e3; 
    
  4. $a = 7E-10;
    

Kolejnym typem jest typ logiczny (boolean), przyjmujący jedynie wartości FALSE i TRUE. Jest on używany przez wiele funkcji do zwracania rezultatu, czy operacja się powiodła. Wyrażenia porównawcze (czy równy, czy większy itd.) także generują wartości logiczne.

Ostatnim z typów skalarnych jest ciąg tekstowy (ang. string). Zdążyliśmy już wspomnieć nieco o nim, m.in. o istnieniu dwóch składni zapisywania ciągów. Ta oparta na apostrofach dopuszcza mniejszy zestaw kodów formatujących (pozwalających na wstawienie do tekstu innych apostrofów oraz ukośników wstecznych):

  1. <?php
    
  2. echo 'To jest tekst zapisany w apostrofach. Kody formatujące pozwalają
    
  3.    umieścić w tekście wyłącznie inne apostrofy: \' albo backslashe: \\. Wszystko inne,
    
  4.    np. \n zostanie wyświetlone jako zwyczajny tekst, zamiast znaku nowej linii';
    

Uruchom powyższy skrypt, aby zobaczyć jaki tekst zostanie wyświetlony. Spróbuj usunąć backslash sprzed apostrofu (w tekście: \') i zobacz, co się stanie. Skrypt się nie uruchomi, ponieważ wystąpił błąd składni. PHP napotka w sumie trzy apostrofy, a więc między drugim i trzecim będzie nierozpoznany dla parsera tekst, natomiast trzeci będzie niedomknięty.

Więcej możliwości formatowania posiada tekst ograniczony cudzysłowami:

  1. <?php
    
  2. echo "To jest tekst zapisany w cudzysłowach. Za pomocą kodów formatujących możemy
    
  3.   umieszczać wiele rzeczy: znak cudzysłowu \" backslash \\ znak nowej linii \n i inne: \t \r \$";
    

Cudzysłowy zezwalają na "proste" umieszczanie wewnątrz tekstu wartości zmiennych, co zilustrujemy w prymitywnym przykładzie:

  1. <?php
    
  2. $czas = time();
    
  3. echo "Aktualny czas w sekundach: $czas sek.";
    

Wartym zapamiętania jest fakt, że wstawianie zmiennych w ten sposób jest kilka razy wolniejsze, niż łączenie ich z ciągiem operatorem kropki.

  1. <?php
    
  2. // tutaj można użyć cudzysłowu jak i apostrofu
    
  3. echo "Aktualny czas w sekundach: ".time()." sek.";
    

Niektórzy początkujący programiści niezbyt rozumieją ideę tej możliwości - próbują wykorzystywać ciągi do wprowadzania wartości zmiennych jako parametrów do funkcji:

  1. <?php
    
  2. $formatDaty = 'd.m.Y';
    
  3. echo date("$formatDaty");
    

Powinno się unikać takiej konstrukcji, i choć PHP ją akceptuje, nie jest to prawidłowe użycie tej struktury języka. Co więcej, przy złożonych typach powoduje zniekształcenie danych. Jeżeli spotkasz kogoś piszącego w ten sposób, poinformuj go o tym. Do tego wyświetlanie danych zawartych w cudzysłowiach przebiega wolniej niż w apostrofach.

Inne typy

Typami złożonymi w PHP są tablice oraz obiekty. Tablice poznamy jeszcze w tym rozdziale, natomiast obiektami oraz samym programowaniem obiektowym zajmiemy się w dalszej części podręcznika.

Istnieją jeszcze dwa typy specjalne: resource oraz NULL. Pierwszy z nich reprezentuje wszelkiego rodzaju połączenia z bazami danych, otwarte przez PHP pliki itd. Drugi to wartość pusta. Za jego pomocą możemy "zasymulować", że zmienna nie istnieje lub nie zawiera wartości. Pojawia się w trzech sytuacjach:

  • Do zmiennej przypisana została stała NULL.
  • Do zmiennej nie została przypisana jeszcze żadna wartość (zgłaszane jest wtedy powiadomienie)
  • Zmienna została zniszczona poleceniem unset().

Przyjrzyjmy się wspomnianemu w trzecim punkcie poleceniu. Czasem jakąś zmienną trzeba zniszczyć. Najlepiej nadaje się do tego polecenie unset():

  1. <?php
    
  2. $zmienna = 4856;
    
  3.  
  4. unset($zmienna);
    
  5.  
  6. echo $zmienna;
    

Zmiennej już nie ma, dlatego polecenie echo pokaże nam powiadomienie o nieistniejącej zmiennej.

Konwersja typów

PHP potrafi sam rozpoznać typ informacji przypisanej do zmiennej oraz automatycznie konwertować go w zależności od potrzeb. Przykładowo liczby ułamkowe użyte tam, gdzie potrzeba całkowitych, są zaokrąglane w górę do zera. Wartości logiczne mogą być reprezentowane cyframi 0 (FALSE) oraz 1 (TRUE). Ciągi tekstowe mogą być konwertowane do liczb, jeżeli pierwszy z nich (pomijając wiodące białe znaki) znaków jest cyfrą. W przeciwnym przypadku PHP dobiera wartość 0.

Możemy sami wymusić konwersję typów:

  1. <?php
    
  2. // wyświetl liczbę całkowitą jako ułamek
    
  3. echo (float) 10;
    

W nawiasie przed wartością piszemy angielską nazwę typu: integer, int, string, boolean, float, double (liczba zmiennoprzecinkowa mogąca przybierać znacznie większe wartości). Unikaj jakiejkolwiek konwersji typów złożonych: tablic, obiektów, zasobów, gdyż w każdym z tych przypadków informacje zostają całkowicie utracone; zamiast nich zwracana jest po prostu nazwa typu złożonego - to dlatego ostrzegaliśmy przed wprowadzaniem wartości zmiennych do funkcji przez cudzysłowy.

PHP posiada funkcję gettype() zwracającą nazwę aktualnego typu danych zawartych w zmiennej:

  1. <?php
    
  2. $a = 547;
    
  3. echo 'Typ zmiennej $a to: '.gettype($a);
    

Więcej o operatorach

Dotychczas poznałeś już kilka operatorów, np. + czy =. Jak zdążyłeś już zauważyć, po obu stronach takiego operatora stoją wyrażenia, na których wykonuje on operacje i zwraca wynik. Sam jest zatem wyrażeniem. Operatory mają określoną kolejność wykonywania wzorowaną na matematyce. Możemy ją naginać do własnych potrzeb, stosując nawiasy.

Oto wykaz najciekawszych operatorów na początek.

Operator Nazwa Składnia Opis
/ Dzielenie wyrażenie / wyrażenie Reprezentuje wynik dzielenia. Drugie wyrażenie nie może być zerem.
% Dzielenie modulo wyrażenie % wyrażenie Reprezentuje resztę z dzielenia. Drugie wyrażenie nie może być zerem.
* Mnożenie wyrażenie * wyrażenie Reprezentuje iloczyn dwóch wyrażeń.
+ Dodawanie wyrażenie + wyrażenie Reprezentuje sumę dwóch wyrażeń.
- Odejmowanie wyrażenie - wyrażenie Reprezentuje różnicę dwóch wyrażeń.
. Łączenie (Konkatenacja) wyrażenie . wyrażenie Reprezentuje połączenie dwóch wyrażeń w ciąg tekstowy.
++ Postinkrementacja (zwiększenie) $zmienna++ Reprezentuje wartość zmiennej, a następnie zwiększa ją o 1.
++ Preinkrementacja (zwiększenie) ++$zmienna Zwiększa wartość zmiennej o 1, a następnie reprezentuje ją.
-- Postdekrementacja (zmniejszenie) $zmienna-- Reprezentuje wartość zmiennej, a następnie zmniejsza ją o 1.
-- Predekrementacja (zmniejszenie) --$zmienna Zmniejsza wartość zmiennej o 1, a następnie reprezentuje ją.

Zwróć uwagę na cztery ostatnie pozycje. Wyszczególnione zostały tam tzw. operatory jednoargumentowe, czyli takie, które operują wyłącznie na jednym wyrażeniu. W dodatku koniecznie musi być ono zmienną, ponieważ modyfikują one jej wartość, powiększając lub zmniejszając o jeden. Każdy z tych operatorów został podany podwójnie, ponieważ w zależności od tego, czy postawimy go przed, czy po zmiennej, otrzymamy nieco inne rezultaty. Porównaj:

  1. <?php
    
  2. // najpierw składnia $zmienna++
    
  3.  
  4. $zmienna = 5;
    
  5.  
  6. echo 'Stan 1: '.($zmienna++).'<br/>';
    
  7. echo 'Stan 2: '.$zmienna.'<br/><br/>';
    
  8.  
  9. // teraz składnia ++$zmienna
    
  10. echo 'Restart zmiennej...<br/>';
    
  11. $zmienna = 5;
    
  12.  
  13. echo 'Stan 1: '.(++$zmienna).'<br/>';
    
  14. echo 'Stan 2: '.$zmienna.'<br/>';
    

Skrypt ten wygeneruje nam kilka linijek:

Stan 1: 5
Stan 2: 6

Restart zmiennej...
Stan 1: 6
Stan 2: 6

Przeanalizuj wyniki działania. Okazuje się, że w składni $zmienna++ najpierw dostajemy wartość zmiennej, a dopiero potem zwiększamy ją o jeden (dlatego zmiana widoczna jest dopiero w drugim stanie). ++$zmienna najpierw powiększa, potem zwraca, w efekcie czego otrzymujemy w obu stanach liczbę "6". Identyczna zasada obowiązuje operator --.

Zajmijmy się teraz przypisywaniem danych do zmiennej. Wiemy już, że operator przypisania po lewej stronie wymaga zmiennej, po prawej wyrażenia, którego wartość trzeba w niej umieścić. Skoro operator to też jest wyrażenie, to jaką wartość ono reprezentuje? Okazuje się, że tę, która jest przypisywana. Możemy wobec tego zastosować sprytną sztuczkę, o której wbrew pozorom wie niezbyt wielu programistów. Zainicjujmy pięć zmiennych naraz tą samą wartością:

  1. <?php
    
  2. $a = $b = $c = $d = $e = 5;
    

PHP najpierw przypisze "5" do zmiennej $e, zwracając jednocześnie "5" tak, by mogło być ono przypisane do $d, potem do $c, $b i na końcu $a. W ten sposób jednym wielkim wyrażeniem zainicjowaliśmy pięć zmiennych naraz.

Poznany już operator przypisania nie jest jedynym, jaki istnieje w PHP. Aby ułatwić modyfikację wartości zmiennych o liczby inne niż jeden, stworzono całą gamę operatorów łączących w sobie przypisywanie oraz jakąś operację matematyczną. Oto i one.

Operator Składnia Równoważna postać Opis
/= $zmienna /= wyrazenie $zmienna = $zmienna / wyrażenie Dzieli zmienną przez wyrażenie i umieszcza w niej wynik. Wyrażenie nie może być zerem.
%= $zmienna %= wyrazenie $zmienna = $zmienna % wyrażenie Umieszcza w zmiennej resztę z dzielenia tej zmiennej przez wyrażenie, które oczywiście nie może być zerem.
*= $zmienna *= wyrazenie $zmienna = $zmienna * wyrażenie Mnoży zmienną przez wyrażenie i zapisuje w niej wynik.
+= $zmienna += wyrazenie $zmienna = $zmienna + wyrażenie Dodaje do zmiennej wyrażenie i zapisuje w niej wynik.
-= $zmienna -= wyrazenie $zmienna = $zmienna - wyrażenie Odejmuje od zmiennej wyrażenie i zapisuje w niej wynik.
.= $zmienna .= wyrazenie $zmienna = $zmienna . wyrażenie Dołącza do zmiennej tekstowej nowy fragment.

Te operatory po prostu skracają zapis i czynią go czytelniejszym. Warto o nich pamiętać szczególnie przy operacjach na ciągach tekstu, kiedy nasz algorytm składa jakąś treść, doczepiając kolejne jej partie do wybranej zmiennej:

  1. <?php
    
  2.  
  3. $tekst = 'Litwo, ojczyzno moja! Ty jesteś jak zdrowie<br/>';
    
  4. $tekst .= 'Ile Cię trzeba cenić, ten tylko się dowie<br/>';
    
  5. $tekst .= 'Kto Cię stracił, dziś piękność twą w całej ozdobie<br/>';
    
  6. $tekst .= 'Widzę i opisuję, bo tęsknię po tobie.<br/>';
    
  7.  
  8. echo $tekst;
    

Rezultat:

Litwo, ojczyzno moja! Ty jesteś jak zdrowie
Ile Cię trzeba cenić, ten tylko się dowie
Kto Cię stracił, dziś piękność twą w całej ozdobie
Widzę i opisuję, bo tęsknię po tobie.

Kolejne partie tekstu były doklejane do właściwej zmiennej tak, że na końcu otrzymaliśmy spójny i kompletny tekst. Identycznie jest z pozostałymi operatorami. +=dodaje wartość do zmiennej, -= odejmuje itd.

Na tym zakończymy na razie temat operatorów, lecz to jeszcze nie wszystko. Już niedługo zapoznamy się z operatorami służącymi do porównywania danych oraz podstawami operatorów logicznych. Będą nam one potrzebne przy omawianiu instrukcji warunkowych i pętli.

Tablice

Zaznajomimy się teraz z tablicami, pierwszym złożonym typem danych. Cofnijmy się do czasów szkoły podstawowej/gimnazjum na lekcję matematyki i przypomnijmy nasz pierwszy kontakt z pojęciem funkcji. Była tam mowa, że funkcję można przedstawiać w postaci tabelki:

x 0 1 2 3 4 5 6 7 8
y 5 3 8 7 9 24 15 2 19

Spróbujmy przenieść taką tabelkę w świat programowania. Widzimy, że wszystkie "igreki" są ze sobą powiązane, ponieważ wszystkie są możliwymi wynikami funkcji. Gdyby to zapisać jako zupełnie osobne zmienne, mielibyśmy problem z późniejszym dostaniem się do nich. Popatrz na to w ten sposób: użytkownik wprowadza argument, a my mamy dla niego odnaleźć wartość spośród tych podanych. Skąd PHP może wiedzieć, że akurat ta grupa zmiennych jest ze sobą w jakiś sposób powiązania, a tym bardziej znać regułę wybierania "tej właściwej"? Na razie nie jest to w ogóle możliwe. Co nam pozostaje? Zapisać wszystkie wartości w strukturze zwanej tablicą.

Uwaga! Tablica to zmienna zbiorcza, grupująca pojedyncze elementy mające właściwości zmiennych, do których odwoływać możemy się za pomocą indeksów.

Wiemy już, że tablica jest zmienną, która grupuje sobie mniejsze zmienne opisywane przez ich indeksy (wartości x w naszej tabelce funkcji). Najważniejsze jest jednak to, że przy odwoływaniu się do nich, potrzebny nam indeks możemy określić zwyczajnym wyrażeniem! To oznacza, że zadanie wymienione akapit wyżej staje się realne. Zanim je zaprogramujemy, nieco informacji o składni tablic. Na początek utwórzmy pustą tablicę:

  1. <?php
    
  2. $tablica = array();
    

Spróbujmy wprowadzić do niej wartości naszej funkcji:

  1. <?php
    
  2. $tablica = array(0 => 5, 3, 8, 7, 9, 24, 15, 2, 19);
    

Początkowe "0" określa, od jakiej liczby zacząć numerowanie kolejnych elementów. Po strzałce wymieniamy ich wartości oddzielone przecinkiem. Należy pamiętać, że PHP (tak jak wiele popularnych języków programowania) zaczyna numerację zmiennych (indeksów) w tablicy od 0. Dlatego też możemy śmiało pominąć zapis 0 =>. Zapis ten przyda się nam jeżeli nie chcemy zacząć numeracji od 0, ale np. od 1, piszemy wtedy array( 1=> 5.... Teraz spróbujmy dostać się do jakiejś z nich.

  1. <?php
    
  2. $tablica = array(0 => 5, 3, 8, 7, 9, 24, 15, 2, 19);
    
  3. echo 'Pod numerem 5 kryje się wartość '.$tablica[5];
    

Pomiędzy nawiasami kwadratowymi wprowadzić musimy wyrażenie określające indeks tablicy, który chcielibyśmy odczytać. Możemy teraz pokusić się o napisanie skryptu losującego elementy:

  1. <?php
    
  2. $tablica = array(0 => 5, 3, 8, 7, 9, 24, 15, 2, 19);
    
  3. $liczba_losowa = rand(0, 8);
    
  4. echo 'Pod numerem '.$liczba_losowa.' kryje się wartość '.$tablica[$liczba_losowa];
    

W tym skrypcie za wybranie indeksu tablicy odpowiada funkcja rand() zwracająca losową[1] wartość z tablicy.

Indeksy tablicy wcale nie muszą być numeryczne. PHP dopuszcza także tekstowe wersje. Mamy wtedy do czynienia z tablicami asocjacyjnymi.

  1. <?php
    
  2. $artykul = array(
    
  3.   'tytul' => 'Tytuł artykułu',
    
  4.   'data' => date('d.m.Y'),
    
  5.   'tresc' => 'To jest treść artykułu'
    
  6. );
    
  7.  
  8. echo '<h1>'.$artykul['tytul'].'</h1>';
    
  9. echo '<p>Napisany dnia '.$artykul['data'].'</p>';
    
  10. echo '<p>'.$artykul['tresc'].'</p>';
    

Mogą istnieć także tablice mieszane, w których występują zarówno indeksy tekstowe, jak i numeryczne. Pamiętaj, że każdy element tablicy zachowuje się, jak zwykła zmienna, dlatego także możesz przypisywać do niego dowolne wartości już po utworzeniu tablicy.

  1. <?php
    
  2.  
  3. $tablica = array(0 => 5, 3, 8, 7, 9, 24, 15, 2, 19);
    
  4.  
  5. // modyfikuj losowy element tablicy
    
  6. $tablica[rand(0, 8)] = 6;
    
  7.  
  8. var_dump($tablica);
    

Najpierw przypisaliśmy do losowego elementu tablicy nową wartość: 6. Całość wyświetlamy funkcją var_dump(). Przydaje się ona przy poszukiwaniu błędów w skrypcie. Potrafi zaprezentować w czytelnej formie każdy typ danych, więc możemy za jej pomocą kontrolować, czy na danym etapie wykonywania rezultaty są takie, jakie być powinny. Podobne działanie ma funkcja print_r(), przy czym var_dump() generuje gotowy kod HTML, otoczony znacznikami <pre> </pre> i sformatowany, podczas gdy print_r() zwraca tekst sformatowany, ale bez znaczników HTML.

Zobacz teraz, jak zachowuje się $tablica, kiedy próbujemy ją wywołać samodzielnie:

  1. <?php
    
  2. $tablica = array(0 => 5, 3, 8, 7, 9, 24, 15, 2, 19);
    
  3.  
  4. echo $tablica;
    

Skrypt ten pokaże nam tylko jeden napis: Array, czyli... nazwę typu. Właśnie z tego powodu ostrzegaliśmy przed wprowadzaniem danych do funkcji jakofunkcja("$zmienna");. Gdyby w zmiennej była tablica, do funkcji zamiast niej dotarłby napis Array i całość przestałaby działać. Osobną kwestią jest wydajność takiego zmuszania do konwersji do tekstu, a potem z powrotem na tekst właściwy.

Upewnij się, że rozumiesz już istotę działania tablic, gdyż bardzo przydadzą się nam one w następnym rozdziale.

Przypisy

  1. Skocz do góry Tak naprawdę komputer nie potrafi losować liczb. Za całą tą zasłonką kryją się różne skomplikowane wzory matematyczne inicjowane najczęściej aktualnym czasem, dające wrażenie losowości wyników.

 

Poprzedni rozdział: Zmienne i tablice
Spis treści
Następny rozdział: Struktury kontrolne

Formularze

W tym rozdziale zajmiemy się podstawami komunikacji skryptu PHP z przeglądarką.

Protokół HTTP


Działanie protokołu HTTP.

Podstawą funkcjonowania stron WWW jest protokół HTTP, którego używa przeglądarka i serwer. Zasada jego działania jest bardzo prosta. Gdy chcemy obejrzeć dokument pod podanym adresem URL, wysyłamy do serwera tzw. żądanie HTTP zawierające lokalizację zasobu oraz garść informacji o nas samych. Serwer odnajduje lub generuje (np. przy pomocy PHP) odpowiedni dokument i odsyła wszystko jako odpowiedź HTTP. Odpowiedź zawsze zawiera pewną ilość nagłówków informacyjnych oraz opcjonalną treść, w której przesyłany jest dokument. Przeglądarka odbiera wszystko i rozpoczyna działanie. Protokół jest stosowany zarówno do pobierania kodu HTML strony, jak i znajdujących się na niej obrazków, ściągania plików i innych danych multimedialnych. Rodzaj pobieranej zawartości jest określany przez nagłówki.

Skrypty PHP zawsze pracują po stronie serwera, generując odpowiedzi HTTP na przychodzące do niego żądania. Przeważnie koncentrujemy się jedynie na budowaniu treści, ponieważ interpreter potrafi samodzielnie skonstruować podstawowy zestaw odpowiednich nagłówków, który co najwyżej uzupełniamy. Bardzo często do wygenerowania strony potrzebne są dodatkowe informacje, które najczęściej przechowywane są w bazie danych, a rzadziej - w plikach. Skrypty pobierają z nich dane, poddają je obróbce i osadzają w kodzie HTML, który jest odsyłany do klienta.

Uwaga! Uwaga!
Gdy przeglądarka zaczyna wyświetlać dokument, skrypt PHP już dawno skończył swoją pracę. Pamiętaj, że działanie Twoich skryptów nie może wykroczyć poza ramy protokołu HTTP, w szczególności reagować na zdarzenia w przeglądarce (np. kliknięcie myszką), ani modyfikować fizycznie wysłanej treści. Do takich celów wykorzystywane są inne języki i technologie: JavaScript, AJAX.

Istotną częścią protokołu HTTP są rodzaje żądań (zwane "metodami") informujące o tym, co próbujemy zrobić. Dwa podstawowe to:

  1. Żądania GET - zwyczajne pobieranie dokumentu z serwera.
  2. Żądania POST - wysłanie pewnych danych na serwer.

Istnieją jeszcze inne metody, które są coraz powszechniej stosowane w większych aplikacjach WWW, jednak na niektórych serwerach są one z nieznanych powodów poblokowane. Protokołem HTTP zajmiemy się dokładniej w dalszej części podręcznika, tymczasem na razie będą nas interesować te dwie metody. Pierwsza z nich jest wykorzystywana podczas zwykłego pobierania z serwera dokumentu, natomiast druga - przy formularzach.

Ogólnie o danych wejściowych

W żądaniu HTTP przenoszonych jest wiele informacji o tym, co użytkownik chce obejrzeć oraz o nim samym. Wielu danych dostarcza także sam serwer HTTP. Wszystkie one są podstawą dla aplikacji PHP do wygenerowania odpowiedzi. Mechanizm ich odbierania ewoluował stopniowo. Pierwsze wersje języka rejestrowały wszystkie nadesłane parametry, dane formularzy itd. jako zmienne, lecz było to wyjątkowo niebezpieczne. Dodając nowe parametry można było rejestrować nowe zmienne, które mogły nadpisać zmienne używane w kodzie aplikacji powodując jego błędne działanie (np. zyskanie praw administratora strony przez atakującego). Programista nie mógł także policzyć, ile danych właściwie trafiało do skryptu i gdzie są one zawarte.

Wszystko zmieniło się wraz z pojawieniem się PHP4. Obecnie wszystkie pola są rejestrowane w specjalnych, tworzonych przez skrypt tablicach asocjacyjnych posegregowane według miejsca, z którego nadeszły. PHP potrafi odbierać informacje:

  • Charakterystyczne dla metody GET (adresy URL)
  • Charakterystyczne dla metody POST (zawartość formularzy)
  • z serwera
  • z ciasteczek
  • z sesji (emulowanych przez PHP)

Tutaj zajmiemy się trzema pierwszymi pozycjami. Ciastka oraz sesje zostaną omówione w dalszych rozdziałach podręcznika.

Adresy URL

Do adresu URL można dołączać po znaku zapytania dodatkowe zmienne, np. www.example.com/strona.php?zmienna=wartosc&inna_zmienna=wartosc. Są one przechowywane w tablicy $_GET. Ten sposób przekazywania danych wykorzystujemy tylko, gdy skrypt nie wykonuje operacji mających efekty uboczne (np. może być wyszukiwanie, ale już nie dodawanie lub usuwanie rekordów) lub do przesyłania danych kontrolnych. W przeciwnym wypadku roboty indeksujące stronę i proxy ładujące strony z wyprzedzeniem mogą niechcący wykonywać operacje na serwerze. Ponadto cache przeglądarki i dostawców internetowych może spowodować zignorowanie zapytań.

Przyjrzymy się zawartości tablicy:

  1. <?php
    
  2. var_dump($_GET);
    

Wywołując skrypt normalnie: http://localhost/~programowanie_php/nazwaskryptu.php otrzymamy następujący rezultat:

array(0) { }

Oznacza to, że nie otrzymaliśmy tą drogą żadnych danych. Dodajmy teraz do adresu ciąg ?parametr=wartosc. Po odświeżeniu zobaczymy:

array(1) { ["parametr"]=> string(7) "wartosc" }

Tablica zawiera jeden element o nazwie parametr przechowujący wpisaną w adresie wartość.

Napiszemy teraz prosty skrypt wyświetlający informacje powitalne na podstawie danych z adresu:

  1. <?php
    
  2. if(sizeof($_GET) == 2)
    
  3. {
    
  4.    echo 'Witaj, '.$_GET['imie'].' '.$_GET['nazwisko'].'!';
    
  5. }
    
  6. else
    
  7. {
    
  8.    echo 'Nieprawidłowa liczba parametrów!';
    
  9. }
    

Nie przejmuj się istnieniem konstrukcji, której jeszcze nie poznaliśmy. Niektórzy pewnie domyślili się, co ona robi, ale szczegóły będą podane już w następnym rozdziale. Na razie wpiszmy ją tak, jak jest. Funkcja sizeof() pojawiająca się w kodzie zwraca ilość elementów w tablicy. Sprawdzamy w ten sposób, czy użytkownik podał to, co trzeba. Kontrola nadchodzących danych jest niezwykle istotna i nigdy nie wolno jej zlekceważyć. Pominięcie tego aspektu zazwyczaj kończy się dla skryptu tragicznie, bo jeżeli coś jest do zepsucia, na pewno znajdzie się ktoś, kto tego dokona.

Wywołując skrypt z parametrami "imie" oraz "nazwisko" możemy wpływać na wyświetlane informacje: http://localhost/~programowanie_php/nazwaskryptu.php?imie=Adam&nazwisko=Kowalski. Dla lepszego efektu stwórzmy prosty formularz XHTML wysyłający dane metodą GET:

  1. <?xml version="1.0" encoding="utf-8" standalone="no"?>
    
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    
  3. <html xmlns="http://www.w3.org/1999/xhtml">
    
  4.  <head>
    
  5.   <title>Formularz XHTML</title>
    
  6.  </head>
    
  7.  <body>
    
  8.   <form method="get" action="nazwaskryptu.php">
    
  9.    <p>
    
  10.     <label>Podaj imię: <input type="text" name="imie"/></label>
    
  11.    </p>
    
  12.    <p>
    
  13.     <label>Podaj nazwisko: <input type="text" name="nazwisko"/></label>
    
  14.    </p>
    
  15.    <p>
    
  16.     <input type="submit" value="OK"/>   
    
  17.    </p>
    
  18.   </form>
    
  19.  </body>
    
  20. </html>
    

Choć tworzenie formularzy teoretycznie pasuje do następnej sekcji, tak naprawdę w tym momencie zwyczajnie oszukujemy. Wypełnij ten formularz i wyślij go, a zobaczysz, że przeglądarka dokleiła do pliku podanego w znaczniku FORM parametry utworzone na podstawie pól! Tak więc nasz skrypt nawet nie ma możliwości stwierdzenia, skąd dane do niego przyszły! Poznajmy zatem metodę POST...

Formularze

Obsługa formularzy z prawdziwego zdarzenia, którymi można przesyłać setki informacji, odbywa się dosyć podobnie, jak w przypadku użycia w formularzu metody "get" do przesyłania adresów z parametrami. Różnica jest taka, że wszystko wysyła się wyłącznie z formularza, który posiada parametr "method" ustawiony na "post" oraz że korzysta się z tablicy $_POST wewnątrz samego skryptu. Przeróbmy nasze ostatnie dzieło tak, aby pracowało w ten sposób.

  1. <?php
    
  2. if(count($_POST) == 2)
    
  3. {
    
  4.    echo 'Witaj, '.$_POST['imie'].' '.$_POST['nazwisko'].'!';
    
  5. }
    
  6. else
    
  7. {
    
  8.    echo 'Nieprawidłowa liczba parametrów!';
    
  9. }
    

W skrypcie podmieniamy jedynie nazwy tablic na $_POST. W formularzu musimy jeszcze zmienić metodę:

  1. <html>
    
  2. <head>
    
  3.  <title>Formularz HTML</title>
    
  4. </head>
    
  5. <body>
    
  6.   <form method="post" action="nazwaskryptu.php">
    
  7.   Podaj imię: <input type="text" name="imie"/><br/>
    
  8.   Podaj nazwisko: <input type="text" name="nazwisko"/><br/>
    
  9.   <input type="submit" value="OK"/>   
    
  10.   </form>
    
  11. </body>
    
  12. </html>
    

I gotowe. Wyślij teraz formularz. Zauważ, że żadna informacja nie jest doklejana do adresu, ponieważ transmisja odbywa się niejako innym kanałem.

Porada Nazwy tablic są trochę nieprecyzyjne. To, że wysłaliśmy żądanie metodą POST wcale nie oznacza, że tablica $_GET będzie wtedy zawsze pusta. Możliwe jest jednocześne wysłanie danych formularzem wraz ze zmiennymi w adresie URL, co sprawi, że obie tablice będą posiadać jakąś zawartość.

Z kursów języka HTML wiadomo, że istnieją różne typy pól formularzy. Oto, jakie wartości otrzymuje od nich PHP:

  • <input type="text" name="nazwa"/>
    

    - skrypt otrzymuje $_POST['nazwa'] z wartością wpisaną w pole formularza.

  • <input type="hidden" name="nazwa" value="wartosc"/>
    

    - skrypt otrzymuje $_POST['nazwa'] z wartością wpisaną w danym znaczniku. Użyteczne do przesyłania formularzem ukrytych informacji, o których typowy użytkownik wiedzieć nie musi.

  • <input type="radio" name="nazwa" value="wartosc"/>
    

    - pozycje należące do tej samej grupy muszą mieć identyczną nazwę. $_POST['nazwa'] będzie zawierać wartość tej pozycji, która jest aktualnie zaznaczona.

  • <input type="checkbox" name="nazwa"/>
    

    - jeśli pole jest zaznaczone, $_POST['nazwa'] zawierać będzie słowo "on".

  • <select name="nazwa">...</select>
    

    - $_POST['nazwa'] zawierać będzie wartość wybranego z listy elementu.

  • <input type="submit" name="nazwa"/>
    

    - zmienna $_POST['nazwa'] zostanie utworzona, jeżeli akurat ten przycisk zostanie wciśnięty.

Dzięki temu można do formularzy wstawiać kilka przycisków "submit" i reagować inaczej w zależności od tego, który z nich został naciśnięty.

Serwer

Na sam koniec zostawiliśmy sobie kilka informacji przekazywanych interpreterowi przez serwer WWW (np. Apache). Zacznijmy od określenia adresu IP gościa:

  1. <?php
    
  2. echo 'Witaj, twój adres IP to '.$_SERVER['REMOTE_ADDR'].'!';
    

Ten skrypt pokaże nam adres IP komputera lub sieci, w której znajduje się internauta. W tym drugim przypadku dalsze informacje są niepewne. Serwerowi potrzebny jest wyłącznie ten adres, aby móc gdzieś wysłać rezultat, a pola $_SERVER['HTTP_X_FORWARDED_FOR'] lub $_SERVER['HTTP_CLIENT_IP'] są bardzo niepewne - generuje je właśnie serwer proxy, ale nic nie stoi na przeszkodzie, by podszył się pod nie hacker. Uznawanie ważności informacji tu zawartych było przyczyną wielu błędów bezpieczeństwa w popularnym skrypcie forów dyskusyjnych phpBB i jeżeli naprawdę zależy Ci na nim, pozostań przy REMOTE_ADDR, a resztę traktuj wyłącznie jako ciekawostkę.

Korzystając z funkcji gethostbyaddr() możemy uzyskać nazwę hosta, którego dotyczy adres IP:

  1. <?php
    
  2. echo 'Witaj, twój host to '.gethostbyaddr($_SERVER['REMOTE_ADDR']).'!';
    

Spróbujmy zidentyfikować przeglądarkę użytkownika:

  1. <?php
    
  2. echo 'Twoja przeglądarka została zidentyfikowana jako: '.$_SERVER['HTTP_USER_AGENT'].'!';
    

Pole to zawiera wyłącznie surowe informacje. Każda przeglądarka wpisuje tu co innego i dlatego wyciągnięcie ładnych i pogrupowanych w odpowiednie kategorie danych to zadanie na więcej, niż jeden artykuł. Pozostańmy zatem przy takiej postaci, a przetwarzaniem nagłówków przeglądarek zajmiemy się kiedy indziej.

Adres strony, z której przybyliśmy do naszego skryptu, znajduje się w zmiennej $_SERVER['HTTP_REFERER']. Można wykorzystać go do utworzenia linków "Cofnij" albo do detekcji, jakich słów kluczowych używają ludzie trafiający do nas z wyszukiwarek. Zauważ - szukane frazy zazwyczaj dołączane są właśnie do adresów URL i w ten sposób można je zdobyć.

Uwaga: nie można polegać na obecności i prawidłowości tej informacji. Istnieją przeglądarki i zapory ogniowe, które ją usuwają lub wstawiają tam dowolny adres podany przez użytkownika.

  1. <?php
    
  2. echo 'Przybyłeś do nas ze strony: '.$_SERVER['HTTP_REFERER'].'!';
    

W chwili obecnej to tyle, jeżeli chodzi o pobieranie danych. Dalszych informacji dowiemy się w trakcie następnych rozdziałów w miarę potrzeby.

Poprzedni rozdział: Formularze
Spis treści
Następny rozdział: Instrukcja if

Struktury kontrolne

Programy wykonujące szereg zawsze tych samych instrukcji nie pozwalają rozwinąć skrzydeł. Jeżeli aplikacja ma być w pełni interaktywna, musi umieć reagować na poczynania użytkownika lub sytuacje kryzysowe na podstawie zadanych warunków. Tu do akcji wkraczają instrukcje kontrolne. Są one podstawowym budulcem wielu algorytmów i ich poznanie jest niezbędne. Za to my zyskamy pełną kontrolę nad wszystkim, mogąc efektywnie reagować na wszystkie zdarzenia.

Jedną z instrukcji kontrolnych - if - widziałeś już w poprzednim rozdziale. Zauważyłeś też pewnie, iż nie była ona zakończona średnikiem, a zamiast tego pojawiały się przy niej nawiasy klamrowe tworzące tzw. blok kodu. Jest to ciąg komend złożony z wyrażeń i innych struktur kontrolnych, czytelnie oznakowany. W połączeniu z odpowiednią instrukcją, PHP może decydować, czy wykonać go, czy nie, czy też powtórzyć raz jeszcze. Klamry pełnią tutaj rolę drogowskazu pokazującego, co się danej instrukcji tyczy.

PHP posiada siedem struktur kontrolnych, lecz my poznamy sześć z nich. Siódma jest tak rzadko stosowana, że większość zaawansowanych programistów nie umie z niej korzystać, a ponadto wymaga ona od nas pewnej dodatkowej wiedzy. Oto lista wszystkich instrukcji:

  1. Instrukcja if
  2. Instrukcja switch
  3. Instrukcja for
  4. Instrukcja while
  5. Instrukcja do while
  6. Instrukcja foreach
Poprzedni rozdział: Struktury kontrolne
Spis treści
Następny rozdział: Instrukcja switch

Instrukcja if

Z tą instrukcją zetknęliśmy się już przy okazji omawiania formularzy. Przypomnijmy jeszcze raz ten przykład:

  1. <?php
    
  2. if(count($_GET) == 2)
    
  3. {
    
  4.    echo 'Witaj, '.$_GET['imie'].' '.$_GET['nazwisko'].'!';
    
  5. }
    
  6. else
    
  7. {
    
  8.    echo 'Nieprawidłowa liczba parametrów!';
    
  9. }
    

Instrukcja if pozwala na wykonanie części kodu tylko wtedy, kiedy spełniony jest określony warunek, oraz opcjonalne dodawanie alternatywnych instrukcji w razie jego fałszywości. Z if-a korzysta się niezwykle często, ponieważ niemal zawsze musimy sprawdzać, czy dane informacje są prawidłowe, czy funkcja poprawnie połączyła się z plikiem itd. Poniżej napiszemy skrypt, który będzie potrafił rozwiązywać równanie kwadratowe y = ax^2 + bx + c. Przypomnijmy, że ilość jego rozwiązań rzeczywistych zależy od wartości tzw. współczynnika Δ (\Delta = b^2 - 4ac). Jeżeli obliczony wynik (Δ) jest dodatni, istnieją dwa rozwiązania. Jeżeli ujemny - nie ma żadnych. Dla zera równanie daje jedno rozwiązanie (podwójne).

  1. <?php
    
  2. // 1
    
  3. if(!isset($_GET['a']))
    
  4. {
    
  5.    $_GET['a'] = 0;
    
  6. }
    
  7. if(!isset($_GET['b']))
    
  8. {
    
  9.    $_GET['b'] = 0;
    
  10. }
    
  11. if(!isset($_GET['c']))
    
  12. {
    
  13.    $_GET['c'] = 0;
    
  14. }
    
  15.  
  16. // 2
    
  17. if($_GET['a'] == 0)
    
  18. {
    
  19.    die('Nieprawidłowy parametr A!');
    
  20. }
    
  21.  
  22. // 3
    
  23. $delta = pow($_GET['b'], 2) - 4 * $_GET['a'] * $_GET['c'];
    
  24.  
  25. // 4
    
  26. if($delta > 0)
    
  27. {
    
  28.    // 5
    
  29.    echo 'Delta dodatnia. Dwa rozwiązania:<ul>';
    
  30.    echo '<li>'.round((-$_GET['b']-sqrt($delta))/(2*$_GET['a']), 5).'</li>';
    
  31.    echo '<li>'.round((-$_GET['b']+sqrt($delta))/(2*$_GET['a']), 5).'</li>';
    
  32.    echo '</ul>';
    
  33. }
    
  34. elseif($delta < 0)
    
  35. {
    
  36.    // 6
    
  37.    echo 'Delta ujemna. Brak rozwiązań w zbiorze liczb rzeczywistych!';
    
  38. }
    
  39. else
    
  40. {
    
  41.    // 7
    
  42.    echo 'Delta = 0. Jedno rozwiązanie: '.round((-$_GET['b'])/(2*$_GET['a']), 5);
    
  43. }
    

Jest to pierwszy tak długi kod zawarty w tym podręczniku, dlatego omówimy go sobie w punktach. Numery odpowiednich fragmentów zaznaczone są w kodzie komentarzami jednolinijkowymi.

  1. Na początek sprawdzamy, czy użytkownik podał wszystkie parametry. Funkcja isset() zwraca wartość TRUE, jeżeli zmienna istnieje, a operator negacji (!) sugeruje, że kod w nawiasie chcemy wykonać wtedy, gdy tej zmiennej nie ma. Musimy wtedy podstawić za nią neutralną wartość 0, ponieważ inaczej skrypt będzie nam zgłaszać powiadomienia.
  2. Tutaj zaczynamy właściwy algorytm. Najpierw sprawdzamy, czy rzeczywiście mamy do czynienia z równaniem kwadratowym. Parametr a musi być różny od zera. W razie problemów instrukcją die() zatrzymujemy skrypt w tym miejscu.
  3. Liczymy współczynnik Δ. Funkcja pow(liczba, potega) podnosi podaną liczbę do odpowiedniej potęgi i działa szybciej, niż ręczne mnożenie wartości.
  4. Pierwszy wariant - kiedy Δ jest dodatnia...
  5. Oblicz każde z dwóch rozwiązań równania odpowiednim wzorem. Funkcja round(liczba, miejsca) zaokrągla nam wynik do określonej ilości miejsc po przecinku, natomiast sqrt(liczba) zwraca pierwiastek z podanej liczby. Zwróć uwagę na użycie nawiasów do zasugerowania właściwej kolejności działań.
  6. Gdy Δ jest ujemna, równanie nie ma rozwiązania.
  7. Ostatni z wariantów jest oczywisty, dlatego nie piszemy już warunku. To przecież ostatnia z możliwości. Równanie ma tylko jedno rozwiązanie i także je wyliczamy.

Jest to nasz pierwszy prawdziwie dynamiczny skrypt, który potrafi reagować inaczej w zależności od sytuacji. Poznaliśmy tutaj nie tylko kilka nowych funkcji, ale także sporo operatorów i zasadę działania instrukcji if. Jej formalna składnia jest następująca:

  1. <?php
    
  2. if(wyrazenie)
    
  3. {
    
  4.    // blok kodu
    
  5. }
    
  6. elseif(wyrazenie)
    
  7. {
    
  8.    // blok kodu
    
  9. }
    
  10. else
    
  11. {
    
  12.    // blok kodu
    
  13. }
    

Obowiązkowe jest podawanie pierwszego z członów zaczynającego się od if. Dwa pozostałe są opcjonalne, przy czym ilość elseif może być dowolna. Kolejność podawania kolejnych typów członów ukazana jest na przykładzie.

  • if - wykonuje się, gdy spełniony został podany warunek
  • elseif - jeżeli nie został spełniony poprzedni warunek, PHP testuje aktualny i jeżeli jest prawdziwy, wykonuje ten kawałek kodu.
  • else - wykonywane, jeżeli żaden z powyższych warunków nie został spełniony.

Jeżeli blok kodu zawiera tylko jedną instrukcję, PHP dopuszcza możliwość opuszczenia nawiasów klamrowych, np.

  1. <?php
    
  2. if($zmienna == 6)
    
  3.    echo 'Wartość zmiennej wynosi 6';
    

W tym podręczniku jednak nie będziemy jej stosować z powodu pogorszenia czytelności kodu, niemniej warto wiedzieć, iż składnia ta jest w pełni poprawna, ponieważ część programistów stosuje ją w praktyce.

Wyrażenie warunkowe powinno przyjmować wartości logiczne TRUE lub FALSE. Oto kilka przydatnych operatorów:

Operator Nazwa Składnia Opis
== Równość wyrażenie == wyrażenie Zwraca prawdę, jeżeli oba wyrażenia mają identyczną wartość.
=== Równość wyrażenie === wyrażenie Zwraca prawdę, jeżeli oba wyrażenia mają identyczną wartość oraz typ.
!= Nierówność wyrażenie != wyrażenie Zwraca prawdę, jeżeli oba wyrażenia mają różne wartości.
!== Nierówność wyrażenie !== wyrażenie Zwraca prawdę, jeżeli oba wyrażenia mają różne wartości i/lub typ.
< Mniejsze niż wyrażenie < wyrażenie Zwraca prawdę, jeżeli lewe wyrażenie ma mniejszą wartość od prawego.
> Większe niż wyrażenie > wyrażenie Zwraca prawdę, jeżeli lewe wyrażenie ma większą wartość od prawego.
<= Mniejsze lub równe wyrażenie <= wyrażenie Zwraca prawdę, jeżeli lewe wyrażenie ma mniejszą lub równą wartość prawemu.
>= Większe lub równe wyrażenie >= wyrażenie Zwraca prawdę, jeżeli lewe wyrażenie ma większą lub równą wartość prawemu.
! Negacja (nie) !wyrażenie Zwraca prawdę, jeżeli wyrażenie jest fałszywe i fałsz, jeśli prawdziwe.
&& Koniunkcja logiczna (i) wyrażenie && wyrażenie Zwraca prawdę, jeżeli oba wyrażenia są prawdziwe.
|| Alternatywa logiczna (lub) wyrażenie || wyrażenie Zwraca prawdę, jeżeli przynajmniej jedno z wyrażeń jest prawdziwe.

A teraz kilka przykładów...

  1. <?php
    
  2. //Zakładamy, że do skryptu wysłano formularz z polami liczba1 i liczba2
    
  3. if($_POST['liczba1'] == 1 && $_POST['liczba2'] == 2) 
    
  4. {
    
  5.    die('Liczba 1 wynosi 1, a liczba 2 wynosi 2');
    
  6. }
    

Powyższy skrypt sprawdza, czy zmienna "liczba1" wynosi 1 i zmienna "liczba 2" wynosi 2.

  1. <?php
    
  2. //Zakładamy, że do skryptu wysłano formularz z polami liczba1 i liczba2
    
  3. if($_POST['liczba1'] == 1 || $_POST['liczba2'] == 1) 
    
  4. {
    
  5.    die('Liczba 1, lub liczba 2 wynosi 1');
    
  6. }
    

Natomiast ten skrypt sprawdza, czy zmienna "liczba1", lub zmienna "liczba2" wynosi 1.

Wszystkie operatory podane wcześniej w tabelce przydają się przy konstruowaniu warunków. Pewnego wyjaśnienia domagają się == oraz ===. Popatrz sobie na taki przykład:

  1. <?php
    
  2. if(FALSE == 0)
    
  3. {
    
  4.    echo 'Prawda!';
    
  5. }
    

PHP automatycznie sprowadzi tu sobie obie wartości do identycznego typu i wtedy dopiero je porówna. Dlatego skrypt wyświetli napis "Prawda!". Zamień teraz ten operator na ===. Po odświeżeniu zobaczymy, że teraz nic się nie pokazało. To dlatego, że zażądaliśmy, aby i typy obu wyrażeń były identyczne, podczas gdy nie są. Operator ten przydaje się przy niektórych funkcjach zwracających różne typy wartości w zależności od powodzenia operacji.

Uwaga! Uwaga!
Operatory = i == są w PHP bardzo podobne, dlatego czasem przy ich wpisywaniu zdarzają się pomyłki. Jeżeli twoja instrukcja warunkowa zachowuje się tak, jakby jej warunek był zawsze prawdziwy, upewnij się, że wstawiłeś tam właściwy operator!

Oprócz tego w warunkach przyda się nam kilka funkcji:

  • isset($zmienna) - zwraca prawdę, jeżeli zmienna istnieje.
  • empty($zmienna) - zwraca prawdę, jeżeli zmienna ma wartość pustą (np. NULL, 0 albo pusty ciąg tekstowy).
  • is_null($zmienna) - zwraca prawdę, jeżeli zmienna ma wartość NULL.
  • is_string($zmienna) - zwraca prawdę, jeżeli zmienna jest ciągiem tekstowym.
  • is_integer($zmienna) - zwraca prawdę, jeżeli zmienna jest liczbą całkowitą.
  • is_float($zmienna) - zwraca prawdę, jeżeli zmienna jest liczbą zmiennoprzecinkową.
  • is_numeric($zmienna) - zwraca prawdę, jeżeli zmienna jest liczbą.
  • is_bool($zmienna) - zwraca prawdę, jeżeli zmienna ma wartość logiczną.
  • is_array($zmienna) - zwraca prawdę, jeżeli zmienna jest tablicą.
Poprzedni rozdział: Instrukcja if
Spis treści
Następny rozdział: Instrukcja for

Instrukcja switch

Instrukcja switch zwana jest także instrukcją wyboru. Jej działanie jest podobne do szczególnego przypadku poznanej ostatnio instrukcji warunkowej. Pokażemy to na przykładzie.

Nietrudno znaleźć witryny internetowe, które w jednym pliku grupują kilka różnych zadań wykonywanych w zależności od parametru, np. index.php?act=dodaj,index.php?act=usun itd. Możemy to zaprogramować za pomocą dużego ifa:

  1. <?php
    
  2. if($_GET['act'] == 'dodaj')
    
  3. {
    
  4.    echo 'Dodawanie danych';
    
  5. }
    
  6. elseif($_GET['act'] == 'edytuj')
    
  7. {
    
  8.    echo 'Edycja danych';
    
  9. }
    
  10. elseif($_GET['act'] == 'usun')
    
  11. {
    
  12.    echo 'Usuwanie danych';
    
  13. }
    
  14. else
    
  15. {
    
  16.    echo 'Wyświetlanie danych';
    
  17. }
    

Rozwiązanie to nie jest wygodne nie tylko ze względu na objętość takiego kodu, ale również czytelność. Przypuśćmy, że z jakiegoś powodu musimy zmienić miejsce, z którego pobieramy informację o akcji. Trzeba to zrobić w czterech miejscach, a tych może być w teorii jeszcze więcej. Znacznie łatwiejszą w użyciu, a przy tymwydajniejszą alternatywą jest instrukcja switch. Działa ona w ten sposób, że wybiera spośród dostępnego zbioru określoną wartość na podstawie wartości pewnego wyrażenia i wykonuje zdefiniowany dla niej kod. Przepiszmy raz jeszcze powyższy przykład:

  1. <?php
    
  2. if(!isset($_GET['act']))
    
  3. {
    
  4.    $_GET['act'] = 'index';
    
  5. }
    
  6. switch($_GET['act'])
    
  7. {
    
  8.    case 'dodaj':
    
  9.       echo 'Dodawanie danych';			
    
  10.       break;
    
  11.    case 'edytuj':
    
  12.       echo 'Edycja danych';
    
  13.       break;
    
  14.    case 'usun':
    
  15.       echo 'Usuwanie danych';
    
  16.       break;
    
  17.    default:
    
  18.       echo 'Wyświetlenie danych';	
    
  19. }
    

W nawiasie polecenia switch definiujemy, jakiemu wyrażeniu pragniemy sprawdzić wartość. Wewnątrz nawiasów klamrowych używamy struktury case wartość:, aby zdefiniować dopuszczalne wartości, czyli stany wyrażenia. Po dwukropku piszemy odpowiedni kod. Jeżeli żaden ze stanów nie spełnia naszych oczekiwań, istnieje także klauzula default: pisana na samym końcu opisująca domyślne zachowanie. Jej już nie musimy dodawać, jeżeli tego nie potrzebujemy, niemniej często się ona przydaje. Zwróć uwagę na komendę break; stojącą na końcu każdego kodu przypisanego do case. Mówi ona, że wykonywanie przerywane jest w tym miejscu i PHP ma skoczyć do końca switcha, a nie przypadkiem wykonać kolejny stan. Taka oryginalna budowa wynika z jednego prostego powodu: jeżeli chcemy dla trzech różnych stanów wykonać to samo zadanie, po prostu piszemy pod rząd trzy case'y, potem kod i na końcu przerwanie. W powyższym przykładzie dodaliśmy alternatywną nazwę pierwszej akcji: "dod". Kod po przeróbkach wygląda tak:

  1. <?php
    
  2. if(!isset($_GET['act']))
    
  3. {
    
  4.    $_GET['act'] = 'index';
    
  5. }
    
  6. switch($_GET['act'])
    
  7. {
    
  8.    case 'dod':
    
  9.       echo 'Jak nie damy komendy "break", to pokaże nam się też...<br/>';
    
  10.    case 'dodaj':
    
  11.       echo 'Dodawanie danych';			
    
  12.       break;
    
  13.    case 'edytuj':
    
  14.       echo 'Edycja danych';
    
  15.       break;
    
  16.    case 'usun':
    
  17.       echo 'Usuwanie danych';
    
  18.       break;
    
  19.    default:
    
  20.       echo 'Wyświetlenie danych';	
    
  21. }
    

Wywołując skrypt jako nazwapliku.php?act=dodaj, zobaczymy:

Dodawanie danych

Wywołując skrypt jako nazwapliku.php?act=dod, zobaczymy:

Jak nie damy komendy "break", to pokaże nam się też...
Dodawanie danych

PHP wykonał zarówno stan "dod", jak i następujący po nim "dodaj", gdyż w tym pierwszym brakowało komendy break.

Kod stanu może zawierać inne struktury kontrolne:

  1. <?php
    
  2. if(!isset($_GET['act']))
    
  3. {
    
  4.    $_GET['act'] = 'index';
    
  5. }
    
  6. switch($_GET['act'])
    
  7. {
    
  8.    case 'dodaj':
    
  9.       if($_SERVER['REQUEST_METHOD'] == 'POST')
    
  10.       {
    
  11.          echo 'Dodawanie danych';
    
  12.       }
    
  13.       else
    
  14.       {
    
  15.           echo 'Formularz dodawania';
    
  16.       }
    
  17.       break;
    
  18.    case 'edytuj':
    
  19.       echo 'Edycja danych';
    
  20.       break;
    
  21.    case 'usun':
    
  22.       echo 'Usuwanie danych';
    
  23.       break;
    
  24.    default:
    
  25.       echo 'Wyświetlenie danych';	
    
  26.  }
    

Tutaj dodaliśmy instrukcję if, która sprawdza, czy żądanie nadeszło do nas z formularza HTTP, który należy przetworzyć, czy normalnie (wtedy wyświetlamy formularz). $_SERVER['REQUEST_METHOD'] zawiera nazwę metody, za pomocą której odbyło się żądanie HTTP.

Instrukcję switch warto stosować, kiedy wybieramy konkretną możliwość z określonego w kodzie zbioru. Dla PHP zaletą jest, że "wie", jakie są wszystkie stany. Instrukcja warunkowa if jest bardziej ogólna i tam interpreter sprawdza po prostu po kolei wszystkie warunki, aż natrafi na pasujący, nie zagłębiając się w jakieś większe zależności między danymi. Switcha nie należy używać, kiedy mamy tylko dwa stany, gdyż takie zagranie przypominałoby wytoczenie haubicy do zabicia komara. Instrukcja ta nie sprawdza się także przy wszystkich bardziej ogólnych warunkach rodzaju "mniejszy, większy".

Poprzedni rozdział: Instrukcja switch
Spis treści
Następny rozdział: Instrukcja while

Instrukcja for

Pętle

Wszystkie kolejne struktury kontrolne, jakie poznamy, określa się jednym wspólnym terminem: pętle.

Uwaga! Pętlą nazywamy strukturę kontrolną powtarzającą dany kod do czasu spełnienia określonego warunku.

Wiemy już, że pętla powtarza w kółko pewien fragment kodu. Różnice między poszczególnymi rodzajami dotyczą tego, jak i kiedy jest ona przerywana. Na początek zajmiemy się pętlą for. Pokazuje ona pazurki, kiedy zliczamy ilość wywołań pętli i na podstawie tego określamy, czy trzeba ją przerwać, czy nie. W for definiujemy trzy wyrażenia:

  • Startu - najczęściej inicjuje licznik wywołań
  • Końca - warunek zakończenia
  • Iteracji - najczęściej zwiększa licznik wywołań

Oddzielone są one średnikami. Pokażemy to na przykładzie skryptu wyświetlającego liczby od 0 do 9.

  1. <?php
    
  2. for($i = 0; $i < 10; $i++)
    
  3. {
    
  4. 	echo $i.'<br/>';	
    
  5. }
    

Warunek startu tworzy nową zmienną $i z wartością zero. Następnie określamy, że dopóki $i jest mniejsze od 10, pętla ma się powtarzać. Przy każdym cyklu należy zwiększyć wartość $i o 1.

Uwaga! Uwaga!
Uważaj na warunek końca pętli! Jeżeli niepoprawnie go zdefiniujesz, pętla może nie wykonać się wcale albo też powtarzać się w nieskończoność. Drugi przypadek nie jest aż taki groźny, ponieważ PHP automatycznie przerywa wykonywanie skryptu, jeżeli trwa ono ponad 30 sekund.

Proste wyświetlanie tablic

Pętla for jest użyteczna przy wyświetlaniu tablic z indeksami numerycznymi. Mamy plik tekstowy z zawartością:

Litwo, ojczyzno moja! Ty jesteś jak zdrowie,
Ile cię trzeba cenić, ten tylko się dowie,
Kto cię stracił, dziś piękność twą w całej ozdobie
Widzę i opisuję, bo tęsknię po tobie.

Zastosujemy funkcję file(), aby wczytać go do pamięci z jednoczesnym rozbiciem na poszczególne wiersze zapisane w tablicy. W ten sposób będziemy je mogli wyświetlić jako elementy listy wypunktowanej:

  1. <?php
    
  2. $plik = file('plik.txt');
    
  3.  
  4. echo '<ul>';
    
  5. for($i = 0, $x = count($plik); $i < $x; $i++)
    
  6. {
    
  7. 	echo '<li>'.trim($plik[$i]).'</li>';	
    
  8. }
    
  9. echo '</ul>';
    

Do określenia ilości wierszy użyliśmy poznanej już wcześniej funkcji count(). Przy wyświetlaniu stosujemy jeszcze jedną: trim(). Usuwa ona z początku i końca każdego wiersza białe znaki, tj. spacje, zejścia do nowej linii, tabulatory. Wynikiem działania skryptu jest zawartość pliku wyświetlona w liście wypunktowanej.

Zwróć uwagę na specyficzną budowę wyrażenia inicjacji pętli. Pragniemy utworzyć dwie zmienne, dlatego oddzielamy je przecinkami. Podobną sztuczkę możemy zastosować również w wyrażeniu iteracyjnym. Można się zapytać, dlaczego zastosowaliśmy tak rozbudowaną konstrukcję. Przecież dopuszczalne jest także napisanie:

  1. for($i = 0; $i < count($plik); $i++)
    

W typowych sytuacjach obie konstrukcje zachowają się podobnie, lecz warto pamiętać o pewnym niuansie technicznym. Pierwsza z konstrukcji pobiera ilość elementów tablicy na samym początku. Jeżeli któryś cykl pętli doda jakiś element, nie zostanie on uwzględniony. W drugim przypadku ilość ta jest pobierana po każdym cyklu, zatem pętla dysponuje bieżącymi informacjami o wielkości tablicy i wszelka jej zmiana zostanie uwzględniona w ilości wykonanych cykli. Sposób ten jest jednak mniej wydajny od pierwszego.

Break i Continue

Przy okazji omawiania instrukcji switch poznaliśmy komendę break. Ma ona bardzo duże zastosowanie przy pętlach, które potrafi przerywać. Istnieje także kolejne polecenie: continue. Przerywa ono jedynie aktualny cykl pętli i powoduje rozpoczęcie następnego.

Mamy prosty ciąg tekstowy:

Komenda; Komenda; Komenda; Komenda. To już pomijamy.

Wiemy o nim trzy rzeczy:

  1. Spacje ignorujemy
  2. Kropka oznacza koniec wprowadzania komend
  3. Średnik separuje komendy

Naszym zadaniem jest wprowadzenie komend do tablicy, aby można je było łatwiej przetwarzać. Skrypt ten będziemy pisać kawałek po kawałku. Na początek stwórzmy sobie parę zmiennych:

  1. <?php 
    
  2. $tekst = 'Komenda; Komenda; Komenda; Komenda. To już pomijamy.';
    
  3. $tablica = array(0 => '');
    
  4. $t = 0;
    

$tekst to tekst do przetworzenia. $tablica jest miejscem docelowym komend z "firmowo" utworzonym pierwszym pustym elementem. $t to licznik mówiący, do którego elementu tablicy wprowadzamy znaki.

Rozpoczynamy pętlę. Do pobrania długości ciągu użyjemy funkcji strlen(). $i to licznik położenia w ciągu tekstowym. Wskazuje na aktualnie przetwarzany znak:

  1. for($i = 0; $i < strlen($tekst); $i++)
    
  2. {
    

Implementujemy możliwość pierwszą. Spacje ignorujemy, dlatego przy ich napotkaniu przerywamy aktualny cykl pętli komendą continue i przechodzimy do następnego:

  1. 	if($tekst[$i] == ' ')
    
  2. 	{
    
  3.  		continue;
    
  4.  	}
    

Zauważ, jak odwołujemy się do określonego znaku wewnątrz ciągu: $tekst[$i]. Numer znaku (począwszy od zera) podajemy jako indeks w nawiasach kwadratowych, identycznie jak w tablicach.

Druga możliwość - po napotkaniu kropki przerwać pętlę wcześniej:

  1. 	if($tekst[$i] == '.')
    
  2.  	{
    
  3.  		break;
    
  4.  	}
    

Przechodzimy do ewentualności trzeciej. Przy średniku należy przesunąć się na nowy element tablicy wynikowej i zainicjować go pustym ciągiem. Każdy inny znak wprowadzamy do aktualnego elementu tablicy:

  1. 	if($tekst[$i] == ';')
    
  2.  	{
    
  3.  		$t++;
    
  4.  		$tablica[$t] = '';
    
  5.  	}
    
  6.  	else
    
  7.  	{
    
  8.  		$tablica[$t] .= $tekst[$i];
    
  9.  	}
    

Teraz dopełnienie formalności, tj. zamknięcie pętli i wyświetlenie zawartości tablicy funkcją var_dump():

  1. }
    
  2.  
  3. echo '<pre>';
    
  4. var_dump($tablica);
    
  5. echo '</pre>';
    

Zapytajmy się, jak przerwać pętlę, jeżeli jesteśmy aktualnie w instrukcji switch? Wywołanie break i continue będzie się przecież odnosiło do niej, a tego nie chcemy. Rozwiązaniem jest podanie po nich numeru określającego, której instrukcji wzwyż dotyczy wywołanie. Przepiszmy jeszcze raz powyższy kod z wykorzystaniem instrukcji wyboru (notabene nawet bardziej pasującej w tym przypadku):

  1. <?php
    
  2. $tekst = 'Komenda; Komenda; Komenda; Komenda. To już pomijamy.';
    
  3. $tablica = array(0 => '');
    
  4. $t = 0;
    
  5.  
  6. for($i = 0; $i < strlen($tekst); $i++)
    
  7. {
    
  8. 	switch($tekst[$i])
    
  9. 	{
    
  10. 		case ' ':
    
  11. 			continue 2;
    
  12. 		case '.':
    
  13. 			break 2;
    
  14. 		case ';':
    
  15. 			$t++;
    
  16. 			$tablica[$t] = '';
    
  17. 			break;
    
  18. 		default:
    
  19. 			$tablica[$t] .= $tekst[$i];		
    
  20. 	}
    
  21. }
    
  22.  
  23. echo '<pre>';
    
  24. var_dump($tablica);
    
  25. echo '</pre>';
    

Przy stanach spacji oraz kropki wywołujemy komendy continue oraz break z parametrem 2, aby podkreślić, że dotyczą one pętli for, a nie instrukcji switch. breakw kodzie obsługi średnika nie ma parametru, więc odnosi się do instrukcji switch.

Poprzedni rozdział: Instrukcja for
Spis treści
Następny rozdział: Instrukcja do while

Instrukcja while

Kolejną pętlą jest while, będąca znacznie prostszą odmianą poznanej ostatnio pętli for. Wymagany jest tu jedynie warunek zakończenia, a pętla wykonuje się, dopóki jest on prawdziwy. Oto prosty przykład:

  1. <?php
    
  2. while(rand(0,10) != 8)
    
  3. {
    
  4. 	echo 'Jeszcze nie wylosowałem!<br/>';
    
  5. }
    

Pętla ta będzie wykonywała się, dopóki funkcja rand() nie wylosuje liczby 8. Jeżeli dostaniemy ją już w pierwszym sprawdzeniu, napis nie pojawi się w ogóle.

Ze względu na taką ogólną konstrukcję while przydaje się tam, gdzie musimy coś powtarzać do czasu osiągnięcia pewnego stanu. Sztandarowym przykładem jest czytanie pliku, gdzie takim specyficznym stanem, w którym musimy przerwać naszą pracę, jest jego koniec. Zastosowanie pętli while będzie tu o wiele lepsze, niż obliczanie na podstawie wielkości pliku, ile "segmentów" musimy pobrać i zabawa z licznikami.

  1. <?php
    
  2. $f = fopen('plik.txt', 'r'); // 1
    
  3.  
  4. while(!feof($f)) // 2
    
  5. {
    
  6. 	echo fgets($f, 16); // 3
    
  7. }
    
  8.  
  9. fclose($f); // 4
    

Opis działania:

  1. Otwieramy plik do odczytu. Uchwyt do niego zapisujemy w zmiennej $f. W ten sposób poznaliśmy nowy typ danych: Resource, czyli zasób.
  2. Dopóki nie osiągniemy końca pliku...
  3. Pobieraj z niego kolejne 16-znakowe bloki.
  4. Na koniec zamykamy połączenie z plikiem.

Pętlę while można przerobić na pętlę for bez większych trudności. Oto nowa wersja pierwszego przykładu z rozdziału poprzedniego:

  1. <?php
    
  2. $i = 0;
    
  3. while($i < 10)
    
  4. {
    
  5. 	echo $i.'<br/>';
    
  6. 	$i++;
    
  7. }
    

Tu także można stosować komendy break oraz continue poznane w poprzednim rozdziale.

Pętla while przyda się nam, gdy zaczniemy omawiać komunikację z bazami danych. Zostanie tam wykorzystana do pobierania rekordów.

Poprzedni rozdział: Instrukcja while
Spis treści
Następny rozdział: Instrukcja foreach

Instrukcja do while

W przeciwieństwie od normalnego while, tutaj warunek sprawdzany jest na końcu, tak więc pętla zostanie wykonana przynajmniej raz. Nie jest to często wykorzystywana właściwość, ale warto o niej pamiętać.

Składnia pętli do while jest dość specyficzna. Przed nawiasem klamrowym pojawia się jedynie słowo kluczowe do, a while z warunkiem znajduje się na samym końcu. Przedstawimy to na przykładzie takiego oto skryptu:

  1. <?php
    
  2. do
    
  3. {
    
  4. 	echo "Podaj i: \n";
    
  5. 	fscanf(STDIN, "%d\n", $i); // 1
    
  6. }
    
  7. while($i < 10);
    

Nie będziemy uruchamiali go w przeglądarce, ale w linii komend. Powyższy skrypt pracuje w konsoli systemowej i może pobierać stamtąd dane poprzez wiersz zaznaczony jako 1 (nie przejmuj się, że nie rozumiesz jego budowy. Poznamy ją dalej). Aby uruchomić skrypt, uruchom konsolę i ustaw się poleceniem cd na katalogu, w którym zainstalowałeś PHP. Następnie wydaj następujące polecenie:

php -f /scieżka/do/skrypt.php

Jako wartość parametru -f musisz podać pełną ścieżkę do skryptu, który chcesz uruchomić.

Zauważ, że dzięki użyciu pętli do while, nie musimy umieszczać w skrypcie dwa razy kodu do pytania się o zmienną $i. Oto analogiczny kod z wykorzystaniem normalnego while:

  1. <?php
    
  2. echo "Podaj i: \n";
    
  3. fscanf(STDIN, "%d\n", $i);
    
  4.  
  5. while($i < 10)
    
  6. {
    
  7. 	echo "Podaj i: \n";
    
  8. 	fscanf(STDIN, "%d\n", $i);
    
  9. }
    

Tutaj musimy powielić kod dwa razy, bo przecież przed sprawdzeniem warunku wypada się choć raz zapytać użytkownika, co należy sprawdzić. W poprzednim przykładzie mogliśmy do tego celu użyć kodu wewnątrz pętli, ponieważ mieliśmy zagwarantowane wykonanie jej kodu przynajmniej raz.

Poprzedni rozdział: Instrukcja do while
Spis treści
Następny rozdział: Funkcje

Instrukcja foreach

Ostatnią pętlą jest foreach. Ma ona specyficzne zastosowanie, ponieważ służy wyłącznie do przeglądania zawartości typów złożonych: tablic oraz obiektów. Kod wewnątrz niej jest powtarzany dla każdego z elementów tablicy, a on sam jest na ten czas przenoszony do tworzonej przez pętlę zmiennej. Wróćmy do naszego przykładu z pętlą for odczytującego zawartość pliku. Przepiszemy go z wykorzystaniem foreach:

  1. <?php
    
  2. $plik = file("plik.txt");
    
  3.  
  4. echo '<ul>';
    
  5. foreach($plik as $linia)
    
  6. {
    
  7. 	echo '<li>'.trim($linia).'</li>';	
    
  8. }
    
  9. echo '</ul>';
    

Teraz skrypt ma o wiele bardziej przejrzystą budowę. Przyjrzyjmy się deklaracji pętli:

  1. foreach($plik as $linia)
    

Mówi nam ona, że pętla ma analizować tablicę $plik, a aktualnie przetwarzany element ma być zapisany w zmiennej $linia.

Foreach umożliwia nam także zwracanie nazw indeksów elementów:

  1. <?php
    
  2. $plik = file('plik.txt');
    
  3.  
  4. echo '<ul>';
    
  5. foreach($plik as $numer => $linia)
    
  6. {
    
  7. 	echo '<li>Linia #'.$numer.': '.trim($linia).'</li>';	
    
  8. }
    
  9. echo '</ul>';
    

Foreach ma tę przewagę nad innymi pętlami, że wie, jakie elementy należą do tablicy i zawsze przetworzy tylko je. Gdybyśmy przed wyświetleniem pliku usunęli z niego np. linijkę 1, pętla for nie dałaby rady, próbując przetworzyć nieistniejący element. Nie robi oczywiście tego dla złośliwości, lecz dlatego, że operuje na liczniku i nie wie, do czego jest on przez nas dalej wykorzystywany.

  1. <?php
    
  2. $plik = file('plik.txt');
    
  3. unset($plik[1]); // usuwamy linijkę o indeksie 1
    
  4. echo '<ul>';
    
  5. foreach($plik as $numer => $linia)
    
  6. {
    
  7. 	echo '<li>Linia #'.$numer.': '.trim($linia).'</li>';	
    
  8. }
    
  9. echo '</ul>';
    

Tworzone przez foreach zmienne są jedynie kopiami oryginalnych wartości, dlatego próba ich modyfikacji wewnątrz pętli w żaden sposób nie wpłynie na zawartość tablicy:

  1. <?php
    
  2. $plik = file('plik.txt');
    
  3.  
  4. foreach($plik as $linia)
    
  5. {
    
  6. 	$linia = 'Próba skasowania';
    
  7. }
    
  8.  
  9. echo '<pre>';
    
  10. var_dump($plik);
    
  11. echo '</pre>';
    

Wewnątrz pętli próbujemy przypisać wartość do zmiennej $linia. Owszem, udaje nam się to, ale nowa treść nie trafia w ogóle do tablicy i systemowe wyświetlenie jej zawartości ukazuje brak jakiejkolwiek reakcji. Czy zatem możliwe jest dokonywanie przypisań wewnątrz foreach? Oczywiście. Są dwie sztuczki. Pierwsza polega na wykorzystaniu zwracanego przez pętlę indeksu. Pousuwajmy z tablicy zbędne białe znaki:

  1. <?php
    
  2. $plik = file('plik.txt');
    
  3.  
  4. foreach($plik as $i => $linia)
    
  5. {
    
  6. 	// jeszcze jakiś napis sobie doklejmy
    
  7. 	$plik[$i] = trim($linia).' [OK]';
    
  8. }
    
  9.  
  10. echo '<pre>';
    
  11. var_dump($plik);
    
  12. echo '</pre>';
    

Rozwiązanie to jest nieco trikowe, ale działa. Możemy jednak zastosować coś innego. PHP posiada pewien element zwany referencją. Ogólnie rzecz biorąc jest to odnośnik do zmiennej, który zachowuje się tak, jak ona. Modyfikacja referencji powoduje także modyfikację oryginalnego elementu. Począwszy od PHP 5, referencje można używać w pętli foreach. Wystarczy poprzedzić w jej deklaracji zmienną $linia znakiem &:

  1. <?php
    
  2. $plik = file('plik.txt');
    
  3.  
  4. foreach($plik as &$linia)
    
  5. {
    
  6. 	$linia = trim($linia).' [OK]';
    
  7. }
    
  8.  
  9. echo '<pre>';
    
  10. var_dump($plik);
    
  11. echo '</pre>';
    

Teraz modyfikacja zmiennej $linia jest równoznaczna z modyfikacją aktualnego elementu w tablicy $plik, ponieważ zmienna jest takim właśnie odnośnikiem. O referencjach szerzej powiemy w rozdziale Inne elementy składni.

Porada Porada
Jeżeli elementy twojej tablicy są bardzo duże i liczą np. po kilka kilobajtów, użyj referencji do zwiększenia wydajności. Normalnie musiałyby być one w całości kopiowane, co tylko pochłaniałoby zbędnie moc. Referencja natomiast utworzy do nich zwyczajny odnośnik i czasochłonne kopiowanie nie będzie miało miejsca.
Poprzedni rozdział: Instrukcja foreach
Spis treści
Następny rozdział: Inne elementy składni

Funkcje

Uwaga! Z Wikipedii: Funkcja jest definiowana jako relacja pomiędzy elementami zbioru X (dziedziny) i elementami zbioru Y (przeciwdziedziny), o tej własności, że każdy element zbioru X jest w relacji z dokładnie jednym elementem zbioru Y.

Funkcje są pojęciem znanym z matematyki. Przeniesione na grunt informatyki, zachowują się podobnie i mają podobne zastosowanie: reprezentują jakieś przekształcenie, jakie można wykonać na danych. Dane wejściowe, czyli patrząc na definicję - element zbioru X - wymieniamy jako argumenty funkcji, a po jej wykonaniu otrzymujemy wynik, czyli element zbioru Y. W matematyce jedną z najbardziej znanych funkcji jest sinus, który dla każdej wartości kąta "produkuje" stosunek długości odpowiednich boków w trójkącie prostokątnym zawierających ten kąt. Sinus może być jak najbardziej poprawną funkcją w PHP (i rzeczywiście, język ten udostępnia programiście funkcję sin()), ale ponieważ programy wykonują dużo więcej różnorodnych operacji, możemy spotkać również bardziej praktyczne funkcje z punktu widzenia generowania stron WWW, np. strip_tags(), która z podanego tekstu usuwa znaczniki HTML. Oprócz tego, programista może samodzielnie tworzyć własne funkcje i omówienie tego zagadnienia jest głównym celem rozdziału, który właśnie czytasz.

W programowaniu będziemy przede wszystkim chcieli, aby zdefiniować sobie zestaw funkcji, w którym zamkniemy często wykonywane operacje. Może to być np. obsługa błędów - zamiast kopiować i wklejać odpowiedzialny za nią kod w różne miejsca skryptu, opakowujemy go w funkcję, do której niezbędne dane podajemy jako argumenty i po prostu wywołujemy ją. Dobry podział skryptu na funkcje ma jeszcze jedną zaletę - jeśli w jakimś kawałku kodu znajdziemy błąd, wystarczy poprawić go tylko w jednej funkcji, a zmiana będzie widoczna wszędzie.

Znajomość funkcji to jeden z fundamentów programowania, dlatego w tym rozdziale niezbędna jest szczególna uwaga.

Tworzenie własnych funkcji

Każda funkcja musi posiadać pewną unikalną nazwę, która pozwoli odróżnić ją od innych. Musimy także określić, jakie argumenty przyjmuje i co właściwie robi. Odpowiada za to następująca konstrukcja:

  1. function nazwaFunkcji(argumenty)
    
  2. {
    
  3.    // kod funkcji
    
  4. }
    

Od tego miejsca możemy wywoływać naszą funkcję w identyczny sposób, jak te dostępne w PHP.

  1. <?php
    
  2. function formatujTekst($tekst)
    
  3. {
    
  4.    echo '<font color="red">'.strtoupper($tekst).'</font>';	
    
  5. }
    
  6.  
  7. formatujTekst('to jest tekst 1');
    
  8. formatujTekst('to jest tekst 2');
    

Stworzyliśmy tutaj funkcję formatujTekst(), dzięki której ustalimy jednolite formatowanie dla tekstów prezentowanych na stronie. Pobiera ona jeden argument:$tekst. Zauważmy, że nazwę tę piszemy ze znakiem dolara. Gdybyśmy chcieli podać więcej argumentów, oddzielamy je od siebie przecinkami. Jeżeli funkcja nie będzie używać żadnego argumentu, za nazwą pozostawiamy puste nawiasy. Abyśmy mogli z argumentów skorzystać, muszą one mieć swoje nazwy, gdyż wewnątrz funkcji stają się zwykłymi zmiennymi.

Kod funkcji jest dowolnym poprawnym kodem PHP i można w nim umieścić dowolną rzecz, z tworzeniem kolejnej funkcji włącznie. Jednak zauważmy, że nasza pierwsza funkcja nie zwraca wartości. Zamiast tego wynik wysyła od razu na ekran i próba wykonania

$zmienna = formatujTekst('to jest tekst 1');

nic by nam nie dała. Aby zwrócić cokolwiek jako wynik, musimy skorzystać z komendy return:

  1. <?php
    
  2. function formatujTekst($text)
    
  3. {
    
  4.    return '<font color="red">'.strtoupper($text).'</font>';	
    
  5. }
    
  6.  
  7. echo formatujTekst('to jest tekst 1').'<br/>';
    
  8. echo formatujTekst('to jest tekst 2').'<br/>';
    

Po return podajemy wyrażenie generujące wartość do zwrócenia.

Uwaga! Uwaga!
Słowo kluczowe return możemy umieścić w dowolnym miejscu funkcji, lecz musimy mieć świadomość, że zwrócenie przez funkcję wartości równoznaczne jest z jej zakończeniem! Kod umieszczony za return nie wykona się, a jeśli umieścimy to w pętli, zwrócenie wartości spowoduje jej automatyczne przerwanie wraz z całą funkcją.

Zwróćmy uwagę na fakt, iż przy deklarowaniu argumentów nie podajemy żądanego typu danych. O to musimy zadbać sami, umieszczając na początku funkcji odpowiednie instrukcje warunkowe i w razie kłopotów zgłosić błąd. Napiszmy sobie skrypt wyświetlający zawartość katalogu. Stworzymy w nim jedną funkcję zwracającą zawartość podanego katalogu jako tablicę. Druga funkcja będzie uniwersalnym wyświetlaczem tablic. Dlaczego tak - zaraz wyjaśnimy.

  1. <?php
    
  2. function wyswietlKatalog($sciezka, $tylkoPliki = 0) // 1
    
  3. {
    
  4. 	$dir = opendir($sciezka); // 2
    
  5. 	$wynik = array();
    
  6. 	while($f = readdir($dir)) // 3
    
  7. 	{
    
  8. 		if(is_file($sciezka.$f)) // 4
    
  9. 		{
    
  10. 			$wynik[] = $f; // 5
    
  11. 		}
    
  12. 		elseif(is_dir($sciezka.$f) && $f != '.' && $f != '..' && !$tylkoPliki) // 6
    
  13. 		{
    
  14. 			$wynik[] = $f;
    
  15. 		}	
    
  16. 	}
    
  17. 	closedir($dir); // 7
    
  18.  
  19. 	return $wynik; // 8
    
  20. } // end wyswietlKatalog();
    
  21.  
  22. function pokazListe(array $lista) // 9
    
  23. {
    
  24. 	echo '<ul>';
    
  25. 	foreach($lista as $element)
    
  26. 	{
    
  27. 		echo '<li>'.$element.'</li>';		
    
  28. 	}
    
  29. 	echo '</ul>';	
    
  30. } // end pokazListe();
    
  31.  
  32. pokazListe(wyswietlKatalog('./katalog1/')); // 10
    
  33. echo '<br/>';
    
  34. pokazListe(wyswietlKatalog('./katalog2/', true)); // 11
    

Opis skryptu:

  1. Oto deklaracja funkcji wyświetlania katalogów. Znak równości oraz wartość po drugim argumencie oznacza, że jest on opcjonalny. Jeżeli go nie podamy przy wywołaniu, przyjmie on wartość domyślną. Opcjonalnych argumentów może być więcej, z tym że podajemy je zawsze na końcu. W naszej funkcji opcjonalny argument określa, czy funkcja ma zwracać wszystko (domyślny stan: 0), czy jedynie pliki (stan 1).
  2. Otwieramy katalog o podanej ścieżce
  3. Pętla pobierająca kolejne elementy katalogu, dopóki istnieją.
  4. Sprawdzamy, czy zwrócony element jest plikiem. Zauważ, że do zwróconej nazwy elementu musimy dokleić ścieżkę, ponieważ funkcja is_file() jest niezależna od opendir() i nie obchodzi jej, że w takim kontekście ją wywołujemy. Jeżeli rzeczywiście mamy plik, dodajemy go do tablicy wynikowej jako kolejny element.
  5. Niepodanie indeksu oznacza: "utwórz nowy element o indeksie MAX+1".
  6. Warunek sprawdzający, czy mamy do czynienia z katalogiem, jest dość skomplikowany. Użyliśmy tu operatorów && (logiczne "oraz"), aby zagwarantować, że wszystkie muszą być spełnione, aby dodać element do listy. Mamy tu kolejno: czy element jest katalogiem, czy nie ma on nazwy "." i ".." oraz czy funkcja ma od programisty zezwolenie na pobieranie katalogów.
  7. Zamykamy katalog
  8. Zwracamy tablicę jako wynik
  9. A oto mała niespodzianka. Począwszy od PHP 5 można definiować typy argumentów obiektowych, a od PHP 5.1 - tablic, które również są typem złożonym. Robimy to właśnie w taki sposób. Jeśli próbowalibyśmy wysłać tutaj np. liczbę, PHP zgłosiłby błąd.
  10. Wywołanie funkcji z jednym argumentem i przekierowanie wyniku do funkcji wyświetlającej listę.
  11. Ponowne wywołanie, lecz tym razem żądamy wyłącznie plików.

W ten sposób poznaliśmy już niemal wszystko, co dotyczy definiowania argumentów. Pozostała jeszcze jedna rzecz, a mianowicie pobieranie ich zupełnie nieokreślonej liczby. Uruchom taki oto skrypt:

  1. <?php 
    
  2. function funkcja($a)
    
  3. {
    
  4. 	echo $a;
    
  5. }
    
  6.  
  7. funkcja(1, 2, 3, 4, 5);
    

Nasza funkcja pobiera tylko jeden argument, lecz my podajemy mu pięć. Mogłoby się wydawać, że spowodujemy tym samym błąd, jednak tak się nie stanie. PHP nadmiarowych argumentów nie ignoruje. Choć nie zadeklarowaliśmy żadnego z nich podczas tworzenia funkcji, istnieje pewien sposób, aby je wydostać. Jest to funkcja func_get_args() zwracająca tablicę z wartościami wszystkich argumentów, które przekazaliśmy do funkcji.

  1. function funkcja()
    
  2. {
    
  3. 	$argumenty = func_get_args();
    
  4. 	echo '<ul>';
    
  5. 	foreach($argumenty as $id => $wartosc)
    
  6. 	{
    
  7. 		echo '<li>'.$id.' - '.$wartosc.'</li>';
    
  8. 	}
    
  9. 	echo '</ul>';
    
  10. }
    
  11.  
  12. funkcja(1, 2, 3, 4, 5);
    

Istnieje także func_get_arg(numer) pobierająca wartość konkretnego argumentu. Obie te funkcje operują bezpośrednio na funkcji, dlatego PHP nakłada kilka ograniczeń na ich stosowanie. Najlepiej jest wywołać je na samym początku tworzonej funkcji, aby uniknąć kłopotów.

Widzialność zmiennych

Napiszmy taki skrypt:

  1. <?php
    
  2. $zmienna = 'To jest zmienna';
    
  3.  
  4. function funkcja()
    
  5. {
    
  6. 	echo $zmienna.'<br/>';
    
  7. }
    
  8.  
  9. funkcja();
    
  10. echo $zmienna.'<br/>';
    

Próbuje on wyświetlić dwa razy wartość tej samej zmiennej: z wnętrza funkcji oraz bezpośrednio w skrypcie. Po uruchomieniu okazuje się, że tylko bezpośrednie wyświetlenie podało nam prawidłowy wynik. echo wewnątrz funkcji nie pokazało żadnej wartości. Dlaczego? Zmienna przecież istnieje. I owszem, lecz tylko w tej części skryptu, w której została utworzona. PHP ma zaimplementowaną tzw. widzialność zmiennych - dla każdej funkcji tworzony jest osobny stos, niezależny od drugiego. Jeżeli więc utworzymy zmienną bezpośrednio w skrypcie, nie będzie ona istnieć w żadnej z naszych funkcji, gdyż te mają własne stosy. Ma to wyeliminować konflikty nazewnictwa.

Istnieje jednak sposób na powiedzenie PHP, że używana w funkcji zmienna jest już stworzona w stosie głównym skryptu. Po lekkiej modyfikacji skryptu otrzymujemy:

  1. <?php
    
  2. $zmienna = 'To jest zmienna';
    
  3.  
  4. function funkcja()
    
  5. {
    
  6. 	global $zmienna;
    
  7. 	echo $zmienna.'<br/>';
    
  8. }
    
  9.  
  10. funkcja();
    
  11. echo $zmienna.'<br/>';
    

Słowo kluczowe global informuje PHP, że wymienione po nim zmienne mają zostać zaimportowane ze stosu głównego. Działa ono nawet wtedy, jeśli zmienna o danej nazwie nie istnieje, dlatego korzystanie z funkcji używających global musi być bardzo uważne.

Uwaga! Uwaga!
Używanie global w swoich funkcjach jest obecnie uznawane za bardzo złą praktykę programistyczną z powodu wielu problemów z testowaniem i przewidywalnością działania tak napisanych programów.

Static

Inną przydatną rzeczą jest przenoszenie niektórych zmiennych między wywołaniami tej samej funkcji. Dzięki temu nie musimy zapamiętywać ich wartości w globalnych tablicach, narażając się na konflikty nazewnictwa. Aby tego dokonać, wystarczy zadeklarować wybrane zmienne jako static, a ich wartość zostanie zapamiętana do następnego wywołania.

  1. <?php
    
  2. function koloruj()
    
  3. {
    
  4.    static $i = 0;
    
  5.  
  6.    $i++;
    
  7.  
  8.    if($i % 2 == 0)
    
  9.    {
    
  10.       return '#ffffff';
    
  11.    }
    
  12.    return '#cccccc';	
    
  13. } // end koloruj();
    
  14.  
  15. echo '<table width="30%">';
    
  16. for($x = 0; $x < 10; $x++)
    
  17. {
    
  18.    echo '<tr><td bgcolor="'.koloruj().'">'.$x.'</td></tr>';	
    
  19. }
    
  20. echo '</table>';
    

Powyższy przykład koloruje naprzemiennie wiersze w tablicy. Sztuczka ta nie wymaga finezji: po prostu zwiększamy licznik i sprawdzamy, czy dzieli się bez reszty przez dwa. Jeśli tak, wstawiamy jeden kolor, jeśli nie - drugi. Z pomocą instrukcji switch można rozszerzyć algorytm na więcej kolorów.

Tutaj kolor jest zwracany przez odpowiednią funkcję. Zapamiętuje ona sobie stan wewnętrznego iteratora $i między kolejnymi wywołaniami przy pomocy słowa kluczowego static. Gdybyś usunął tę linijkę, funkcja cały czas zwracałaby ten sam kolor, gdyż zmienna tworzona byłaby w kółko od nowa z domyślną wartością 0.

Rekurencja

Rekurencja w programowaniu oznacza odwoływanie się funkcji do samej siebie. Jest użyteczna, w niektórych sytuacjach wręcz niezbędna, lecz pochłania znacznie więcej zasobów, dlatego należy korzystać z niej ostrożnie.

Za pomocą rekurencji możemy wyświetlić w PHP drzewo katalogów:

  1. <?php
    
  2. function wyswietlKatalog($sciezka)
    
  3. {
    
  4. 	$dir = opendir($sciezka);
    
  5. 	echo '<ul>';
    
  6. 	while($f = readdir($dir))
    
  7. 	{
    
  8. 		if(is_dir($sciezka.$f) && $f != '.' && $f != '..')
    
  9. 		{
    
  10. 			echo '<li>'.$f;
    
  11. 			wyswietlKatalog($sciezka.$f.'/'); // 1
    
  12. 			echo '</li>';
    
  13. 		}
    
  14. 	}
    
  15. 	echo '</ul>';
    
  16. 	closedir($dir);
    
  17. } // end wyswietlKatalog();
    
  18.  
  19. wyswietlKatalog('../../');
    

Funkcja wyswietlKatalog() w przypadku napotkania katalogu w aktualnie sprawdzanej ścieżce, wywołuje samą siebie (1), z doklejoną do dotychczasowej ścieżki nazwą tego katalogu. W ten sposób możemy pobrać całe drzewo, niemniej w przypadku rozbudowanych struktur może trwać to nawet kilkanaście sekund!

Sprawdźmy następujący skrypt:

  1. <?php
    
  2. function wypisz($tekst, $ile)
    
  3. {
    
  4.    echo $ile.': '.$tekst.'<br/>';
    
  5.    if($ile > 0)
    
  6.    {
    
  7.       wypisz($tekst, $ile - 1);
    
  8.    }
    
  9. } // end wypisz();
    
  10.  
  11. wypisz('Witaj', 30);
    

Demonstruje on pewną właściwość rekurencji - możemy nią zastąpić pętle, odpalając naszą funkcję rekurencyjnie określoną liczbę razy. Jako licznik służy nam wartość argumentu $ile. Gdy jest ona większa od zera, funkcja wywołuje samą siebie, zmniejszając go o 1, aż dojdziemy do zera. Nie ma w tym nic dziwnego. Takie rozumienie funkcji jest podstawą tzw. programowania funkcyjnego charakterystycznego dla takich języków programowania, jak Ocaml czy Erlang. Zamiast pętli, tworzymy funkcje wywoływane rekurencyjnie.

Brzmi to interesująco, lecz w PHP natrafia na bardzo ważną przeszkodę. Zmieńmy powyższy kod tak, aby wypisał nasz tekst 200 razy i wykonajmy go. Niespodzianka! Po dojściu do mniej więcej połowy otrzymaliśmy błąd:

Fatal error: Maximum function nesting level of '100' reached, aborting!

Gdy parser wywołuje nową funkcję, musi zapamiętać gdzieś wartości wszystkich zmiennych oraz ogólnie cały stan dotychczasowej. Odkłada go na tzw. stos i po zakończeniu wewnętrznej funkcji, pobiera go stamtąd z powrotem. Stos ten ma jednak ograniczoną głębokość (w PHP wynoszącą 100), dlatego nie możemy w sposób zagnieżdżony wywoływać funkcji w nieskończoność. Doprowadziłoby to bowiem do szybkiego wyczerpania się pamięci.

Wiemy, że każdą pętlę da się zapisać w postaci rekurencyjnej, ale zależność ta działa też w drugą stronę. Każdą rekurencję da się zapisać przy pomocy zwykłych pętli oraz instrukcji warunkowych, choć w pewnych przypadkach może to być zadanie bardzo trudne. Oto prosta implementacja rekurencyjna funkcji silnia, która w matematyce zdefiniowana jest następująco: n! = 1 \cdot 2 \cdot 3 \dots (n - 1) \cdot n

  1. <?php
    
  2. function silnia($n)
    
  3. {
    
  4.   if($n > 0)
    
  5.   {
    
  6.      return $n * silnia($n - 1);
    
  7.   }
    
  8.   return 1;
    
  9. } // end silnia();
    
  10.  
  11. echo silnia(6);
    

Jej wersja iteracyjna, czyli zapisana przy pomocy pętli, jest w PHP dużo wydajniejsza:

  1. <?php
    
  2. function silnia($n)
    
  3. {
    
  4.    $wynik = 1;
    
  5.    while($n > 0)
    
  6.    {
    
  7.       $wynik *= $n--;
    
  8.    }
    
  9.    return $wynik;
    
  10. } // end silnia();
    
  11.  
  12. echo silnia(6);
    

Użyteczne funkcje

PHP dysponuje kilkoma funkcjami do zarządzania funkcjami. Brzmi to może dość śmiesznie, lecz w praktyce bywa bardzo przydatne.

Na początek zastanówmy się, kiedy PHP sprawdza, że funkcja nie istnieje. Okazuje się, że nie dzieje się to w momencie kompilacji, lecz wykonywania skryptu. Ma to swoje uzasadnienie przy konstruowaniu modułowych skryptów (zajmiemy się nimi w następnym rozdziale). Pierwszy plik PHP odwołuje się do funkcji zdefiniowanych w drugim, lecz ten z kolei ładowany jest później. Gdyby z powodu nieistnienia jednej z nich skrypt byłby przerywany w momencie kompilacji, skrypt nie miałby żadnych szans na działanie. Ponadto nie dałoby się pracować ze skryptami korzystającymi z rozszerzeń, których na serwerze nie ma. Ma to sens, przecież instrukcją warunkową możemy zdefiniować alternatywny kod dla tych uboższych serwerów.

PHP ułatwia nam zadanie jeszcze bardziej. Za pomocą function_exists() możemy sprawdzić, czy podana przez nas funkcja istnieje. Narzędziem tym można sondować zarówno nasze własne, jak i definiowane przez rozszerzenia funkcje. W poniższym przykładzie wykorzystamy to do sprawdzenia, czy serwer posiada obsługę protokołu IMAP:

  1. <?php
    
  2. if(function_exists('imap_open'))
    
  3. {
    
  4.    echo 'IMAP dostępny';
    
  5. }
    
  6. else
    
  7. {
    
  8.    echo 'IMAP niedostępny';
    
  9. }
    

Innym sposobem sprawdzenia, czy rozszerzenie jest załadowane, jest skorzystanie z funkcji extension_loaded(), która ma tę przewagę, że działa także z rozszerzeniami obiektowymi, w których zwykłych funkcji nie ma:

  1. <?php
    
  2. if(extension_loaded('imap'))
    
  3. {
    
  4.    echo 'IMAP dostępny';
    
  5. }
    
  6. else
    
  7. {
    
  8.    echo 'IMAP niedostępny';
    
  9. }
    

Jak dobrze korzystać z funkcji?

Funkcje to jedno z podstawowych narzędzi programisty. Omówiliśmy techniczne aspekty ich działania w języku PHP, lecz nie poruszaliśmy dotąd tematu, jak je tworzyć, aby faktycznie były dla nas użyteczne. Istnieje kilka zasad, których przestrzeganie daje nam pewność, że nie natkniemy się gdzieś na problemy z utrzymaniem projektu.

  1. Funkcje powinny realizować jedno, konkretne zadanie. Unikamy tworzenia funkcji w stylu mydło i powidło. Jeśli nie wiemy bądź nie rozumiemy, co dana funkcja tak naprawdę pozwoli nam osiągnąć, przerywamy pisanie kodu i wracamy nad kartkę papieru. Oczywiście nic nie stoi na przeszkodzie, by zadanie było bardzo złożone (np. obsługa formatowania BBCode); dopóki jest to jedno zadanie, jesteśmy w domu.
  2. Jeśli kod danej funkcji staje się bardzo długi, możemy rozważyć jej rozbicie na podproblemy, które zapiszemy w mniejszych funkcjach. Powinniśmy to zrobić zwłaszcza wtedy, gdy dany problem pojawia się wielokrotnie w różnych postaciach. Próbujemy wtedy wyciągnąć wspólny mianownik, a różnice obsłużyć poprzez konfigurację argumentami.
  3. Funkcje nie powinny być zbyt długie.

Oczywiście zalecenia te stanowią punkt odniesienia, a nie prawo, za którego nieprzestrzeganie czeka nas lincz. Wielu początkujących programistów zadaje pytania, kiedy pisać tak, a kiedy inaczej. Odpowiedź jest bardzo prosta: nie ma jednej, uniwersalnej reguły, która mówi, że w przypadku danej funkcji mamy ją rozbić, a w przypadku innej - nie (zauważmy, że reguła taka musiałaby być albo bardzo błyskotliwa, albo obejmować nieskończoną liczbę przypadków). Kluczem jest zwyczajnemyślenie. Dobry programista myśli podczas pisania kodu i każdy jego krok ma uzasadnienie. Punkt odniesienia jest pomocą, wokół którego się obraca, ale jeśli widać, że rozbijanie jakiegoś skomplikowanego i długiego algorytmu, który stanowi jedną całość, będzie niepotrzebną komplikacją, nikt poważny nie będzie tego robił. Oczywiście zdarzają się pomyłki; nie wszystkie uzasadnienia okazują się poprawne, ale duże projekty ulepszane są cały czas. Jeśli coś się nie sprawdziło, jest po prostu przerabiane. Sztuka polega na tym, że jeśli już się pomylimy, pomyłka nie powinna być poważna.

Aby zrozumieć inny aspekt tworzenia dobrej funkcji, porozmawiajmy nieco o tym, co one robią. Być może niektórzy uważni czytelnicy już zauważyli, że w dotychczasowych przykładach pojawiały się zarówno funkcje, które np. wypisywały coś na ekran, oraz takie, które wyłącznie obrabiały zewnętrzne argumenty i zwracały wynik. Okazuje się, że taki podział ma bardzo duże znaczenie praktyczne i teoretyczne, zwłaszcza przy dowodzeniu poprawności programu. Każda operacja języka programowania może mieć tzw. skutki uboczne. Jest to dowolny efekt jej działania, który zmienia stan programu. Przykładowo operacja $a + $bnie ma skutków ubocznych. Możemy ją wywołać 1000 razy, ale dopóki nie zaczniemy czegoś robić z wynikiem, nie zmieni to ani o jotę działania programu. Z drugiej strony operacja $a = 5 ma już skutek uboczny - od tego momentu zmienna $a ma już inną wartość, co potencjalnie może wpłynąć na działanie dalszej części kodu. Pomimo swojej nazwy, skutki uboczne często są właśnie spodziewanymi rezultatami jakiejś operacji. Nie należy ich rozumieć dosłownie, lecz właśnie jako taką zmianę stanu programu, która może wpłynąć na dalszy kod.

Także i funkcje możemy sklasyfikować względem tego czy mają one jakieś skutki uboczne czy nie. Spoglądając na napisany wyżej przykład funkcji silnia() łatwo stwierdzimy, że nie ma ona żadnych skutków ubocznych, ponieważ jedyne, co robi, to przetwarza podany jej argument i zwraca wynik, nie zajmując się tym, jak będzie on wykorzystany. Poza tym nie wypisujemy w niej nic na ekran, ani nie korzystamy z żadnych innych zewnętrznych źródeł danych. Przykład pobierający zawartość katalogu ma już skutek uboczny; w wyniku jej wykonania do przeglądarki wysyłany jest tekst z listą katalogów. Wywołując ją dwukrotnie, użytkownik dostanie taką listę dwa razy.

Jeśli chcemy napisać dobrą funkcję, po prostu musimy znać wszystkie jej skutki uboczne i wiedzieć, czy faktycznie są one dla nas pożądane czy nie. W tym drugim przypadku powinniśmy funkcję przepisać tak, aby ich nie zawierała. Rozpatrzmy funkcję dodającą jakiś kod HTML do podanego tekstu:

  1. <?php
    
  2. function kolorujTekst($tekst)
    
  3. {
    
  4.    echo '<font color="red">'.$tekst.'</font>';	
    
  5. }
    

Jej skutkiem ubocznym jest wypisanie tekstu bezpośrednio na ekran. Oprócz tego mamy też drugą funkcję:

  1. <?php
    
  2. function pogrubTekst($tekst)
    
  3. {
    
  4.    echo '<strong>'.$tekst.'</strong>';	
    
  5. }
    

Jeśli będziemy chcieli tworzyć złożenie w stylu kolorujTekst(pogrubTekst('tekst')), które wyświetli pogrubiony i pokolorowany tekst, taki skutek uboczny jest nie do przyjęcia. Zamiast echo powinniśmy użyć return tak, aby funkcja zwracała wynik, dzięki czemu jej użytkownik może zdecydować w miejscu wywołania, co tak naprawdę chce z nim zrobić. Przecież takiego kodu HTML nie musimy wcale wysyłać przeglądarki, lecz np. zapisać do pliku. Nietrudno zauważyć, że dopiero wyeliminowanie skutku ubocznego zwiększyło nasze możliwości wykorzystania naszych funkcji do tego celu.

Poprzedni rozdział: Funkcje
Spis treści
Następny rozdział: Każdy popełnia błędy

Inne elementy składni

Include i require

Tworzenie dynamicznych stron byłoby bardzo kłopotliwe, gdybyśmy musieli pracowicie kopiować wszystkie utworzone przez nas funkcje do każdego pliku PHP z osobna. Na szczęście PHP udostępnia mechanizmy na dołączanie jednego skryptu do drugiego. Służą do tego instrukcje include, require, include_once orazrequire_once.

Rozpatrzmy taką sytuację: mamy dwa pliki z funkcjami definiującymi wygląd treści: normalny.php oraz opcjonalny.php. Stworzone w nich są identyczne funkcje różniące się jedynie zawartością. We właściwym skrypcie dołączamy jeden z tych plików decydując o tym, w jakim stylu zaprezentowane zostaną dane na stronie. Oto kod dwóch dołączanych plików.

normalny.php:

  1. <?php
    
  2. function pokazTytul($tytul)
    
  3. {
    
  4. 	echo '<h1>'.$tytul.'</h1>';	
    
  5. } // end pokazTytul();
    
  6.  
  7. function pokazParagraf($tekst)
    
  8. {
    
  9. 	echo '<p>'.$tekst.'</p>';
    
  10. } // end pokazParagraf();
    
  11.  
  12. function pokazListe(array $tablica)
    
  13. {
    
  14. 	echo '<ul>';
    
  15. 	foreach($tablica as $element)
    
  16. 	{
    
  17. 		echo '<li>'.$element.'</li>';
    
  18. 	}
    
  19. 	echo '</ul>';
    
  20. } // end pokazListe();
    

opcjonalny.php:

  1. <?php
    
  2. function pokazTytul($tytul)
    
  3. {
    
  4. 	echo '<h1>'.$tytul.'</h1>';	
    
  5. } // end pokazTytul();
    
  6.  
  7. function pokazParagraf($tekst)
    
  8. {
    
  9. 	echo '<p style="font-weight:bold;">'.$tekst.'</p>';
    
  10. } // end pokazParagraf();
    
  11.  
  12. function pokazListe(array $tablica)
    
  13. {
    
  14. 	echo '<ol>';
    
  15. 	foreach($tablica as $element)
    
  16. 	{
    
  17. 		echo '<li>'.$element.'</li>';
    
  18. 	}
    
  19. 	echo '</ol>';
    
  20. } // end pokazListe();
    

Jeszcze raz zwracamy uwagę, że oba pliki tworzą funkcje o takich samych nazwach, dlatego naraz może być załadowany tylko jeden z nich. Oto plik index.php, który na podstawie tego czy ustawiony jest parametr "styl", decyduje, który z powyższych skryptów zostanie załadowany:

  1. <?php
    
  2. if(!isset($_GET['styl']))
    
  3. {
    
  4. 	require('./normalny.php');
    
  5. }
    
  6. else
    
  7. {
    
  8. 	require('./opcjonalny.php');
    
  9. }
    
  10.  
  11. pokazTytul('Tytuł');
    
  12. pokazParagraf('To jest paragraf');
    
  13.  
  14. pokazListe(array(0 =>
    
  15. 	'To',
    
  16. 	'Jest',
    
  17. 	'Lista'	
    
  18. ));
    

Choć require wywołuje się identycznie, jak funkcję, funkcją nie jest, dlatego zapis np. $zmienna = require('skrypt.php'); jest nieprawidłowy. Różnica między nim, a include polega na sposobie obsługi błędów. Instrukcja require generuje komunikat Fatal error zatrzymujący skrypt, druga tylko ostrzeżenie. Istnieją także include_once oraz require_once, które są ignorowane, jeśli próbujemy po raz drugi dołączyć ten sam plik. Pozwala to uniknąć omyłkowego, kilkukrotnego ładowania tych samych funkcji, co oczywiście prowadziłoby do błędu. Powinniśmy używać ich tylko wtedy, kiedy liczymy się z taką możliwością.

Budowanie kompletnej strony z mniejszych plików jest bardzo pożyteczne. Generalnie nie zaleca się pisania wszystkiego ciurkiem bez podziału na funkcje, mniejsze moduły itd., gdyż zmniejsza to odporność skryptu na błędy, wprowadza chaos i utrudnia dodawanie/modyfikowanie nowych opcji. Przyjrzyjmy się, jak zatem zorganizować naszą witrynę. Przede wszystkim zróbmy sobie jeden katalog na wszystkie pliki z funkcjami. Może on się nazywać np. includes. Umieszczamy w nim funkcje ułatwiające komunikację z bazą danych, przetwarzanie tekstu, autoryzację i wykonujące wszystkie inne rutynowe operacje. Dodatkowo tworzymy katalogactions, w którym będzie zawarty kod różnych podstron takich, jak rejestracja czy lista artykułów. Oba katalogi powinny być umieszczone poza katalogiem publicznym dostępnym z przeglądarki. Tam umieścimy tylko jeden plik, index.php, który będzie zarządzać uruchamianiem poszczególnych akcji. Oto i on:

  1. <?php
    
  2. require('../includes/config.php');
    
  3. require('../includes/dispatch.php');
    
  4. require('../includes/session.php');
    
  5. require('../includes/authorize.php');
    
  6. require('../includes/templates.php');
    
  7. require('../includes/functions.php');
    
  8. require('../includes/layout.php');
    
  9.  
  10. initSystem();
    
  11. dispatchAction(isset($_GET['act']) ? $_GET['act'] : 'index');
    

Funkcję dispatchAction() umieszczamy w jednym z plików w katalogu /includes jako funkcję silnika naszej strony (np. w dispatch.php). Jej zadaniem jest odpalenie wybranej akcji, a może ona wyglądać następująco:

  1. <?php
    
  2. function dispatchAction($action)
    
  3. {
    
  4.    if(!ctype_alpha($action))
    
  5.    {
    
  6.       displayError('Nazwa akcji zawiera nieprawidłowe znaki.');
    
  7.    }
    
  8.    if(!file_exists('../actions/'.$action.'.php'))
    
  9.    {
    
  10.       displayError('Podana akcja nie istnieje.');
    
  11.    }
    
  12.    require_once('../actions/'.$action.'.php');
    
  13.    $action .= 'Action';
    
  14.  
  15.    $action();
    
  16. } // end dispatchAction();
    

Nazwa akcji jest tłumaczona na ścieżkę do pliku w katalogu /actions. Wklejając jakiekolwiek dane zewnętrzne do ścieżek używanych przez system musimy zwracać szczególną uwagę na bezpieczeństwo. Gdyby jakiś wścibski użytkownik wywołał naszą stronę jako index.php?act=../includes/dispatch, albo jeszcze jakiś inny plik, moglibyśmy mieć poważne problemy. Dlatego najpierw sprawdzamy funkcją ctype_alpha() czy podana nazwa akcji składa się wyłącznie z liter. Teraz mamy pewność, że działalność użytkownika będzie ograniczona wyłącznie do plików PHP w katalogu /actions. Oczywiście musimy jeszcze funkcjąfile_exists() upewnić się, że odpowiedni plik istnieje i dopiero wtedy możemy go bezpiecznie załadować.

Zwróćmy uwagę, co dzieje się później. Zakładamy, że plik z akcją będzie zawierać funkcję o nazwie nazwaakcjiAction(). Dlatego do nazwy akcji doklejamy słowo Action i wywołujemy funkcję, wczytując jej nazwę ze zmiennej (podświetlona linia 14). Jest to jedna z ciekawych właściwości PHP i warto o niej pamiętać, aczkolwiek w przypadku przyjmowania danych z zewnątrz także zwracamy uwagę na bezpieczeństwo. W naszym przypadku załatwiają je już mechanizmy przeznaczone dla require_once.

Uwaga! Uwaga!
Nigdy nie używaj danych przysłanych z przeglądarki do dynamicznego wczytywania plików, wywoływania funkcji itd. bez upewnienia się, że są one bezpieczne. Przez tak banalne błędy padło już wiele witryn, należących nawet do ważnych publicznych instytucji. Niefiltrowanie danych to zaproszenie hackera do ataku na naszą stronę.

W ramach ćwiczenia spróbuj uzupełnić czymś pozostałe pliki i napisać akcję index, która wyświetli jakiś tekst powitalny.

Stałe

Spójrzmy raz jeszcze na przykład z plikiem index.php. Jak nietrudno się domyślić, większe projekty składają się z pewnej liczby katalogów. Pojawia się tu problem, skąd skrypt ma wiedzieć, gdzie leżą potrzebne mu pliki? Teoretycznie możemy ścieżki wpisywać ręcznie przy każdej konieczności, lecz jest to bardzo niewygodne zwłaszcza, gdy trzeba je będzie z jakiegoś powodu zmienić.

Zdefiniujmy zatem wszystkie używane ścieżki w pliku index.php za pomocą zmiennych. Już na pierwszy rzut oka widać, iż rozwiązanie to nie jest najlepsze, bowiem każdą zmienną ze ścieżką musimy przenosić do funkcji z użyciem global, a ponadto istnieje ryzyko, że w którymś miejscu omyłkowo ją nadpiszemy. Remedium na te kłopoty są stałe. Są to aliasy na pewne wartości, których po utworzeniu nie można już modyfikować. Najogólniej wykorzystuje się je dla często powtarzających się w skrypcie wartości. Przyjrzyjmy się poniższemu skryptowi:

  1. <?php
    
  2. define('DIR_GLOWNY', './');
    
  3. define('DIR_SILNIK', './includes/');
    
  4. define('DIR_ZDJECIA', './zdjecia/');
    
  5.  
  6. require(DIR_SILNIK.'autoryzacja.php');
    
  7. require(DIR_SILNIK.'funkcje.php');
    

Do zdefiniowania stałych używamy funkcji define(), w której definiujemy nazwę stałej oraz jej wartość. Zwyczajowo stałe mają nazwy złożone z samych dużych liter, a wartościami mogą być wyłącznie typy skalarne (czyli nie tablice, nie obiekty oraz nie zasoby). Podczas wywoływania stałych nie poprzedzamy znakiem dolara.

Oto wszystkie cechy stałych:

  1. Stałe nie mają znaku dolara ($) przed nazwą
  2. Stałe mogą być definiowane oraz używane wszędzie bez zważania na zasady dotyczące zakresu ich dostępności
  3. Stałe nie mogą być ponownie definiowane lub "oddefiniowane" po tym jak raz zostały zdefiniowane
  4. Stałe mogą zawierać tylko wartości skalarne

Oprócz przechowywania ścieżek do katalogów, stałe mają zastosowanie podczas pisania bibliotek programistycznych. Często zdarza się, że do funkcji musimy przekazać jakąś wartość liczbową identyfikującą konkretny stan. Ponieważ spamiętywanie cyferek jest uciążliwe, tworzy się dla nich stałe o bardziej czytelnych nazwach. Możemy to pokazać na podstawie funkcji error_reporting() pozwalającej ustawić poziom raportowania błędów przez PHP. Da się ją wywoływać w ten sposób:

error_reporting(1 | 2 | 4 | 8);

Jednak w takiej postaci chyba żaden programista nie potrafiłby odczytać, jaki właściwie poziom raportowania błędów został ustawiony. Zamiast cyfr, można użyć odpowiadające im stałe zdefiniowane przez PHP:

error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);

Kolejnym ciekawym zagadnieniem, którego realizację ułatwiają stałe, jest przekazywanie parametrów do funkcji w sposób pokazany powyżej. Rozpatrzmy przypadek systemu raportowania stanu pojazdu. Stworzyliśmy sobie funkcję raportującą pobierającą pięć wartości logicznych (prawda-fałsz) opisujących, które elementy są sprawne, a które nie. Nasz skrypt wygląda tak:

  1. <?php
    
  2. // Raportowanie stanu pojazdu
    
  3. function stan($silnikOK, $kolaOK, $swiatlaOK, $skrzyniaOK, $paliwoOK)
    
  4. {
    
  5. 	if($silnikOK)
    
  6. 	{
    
  7. 		echo 'Silnik jest sprawny<br/>';		
    
  8. 	}
    
  9. 	if($kolaOK)
    
  10. 	{
    
  11. 		echo 'Koła są sprawne<br/>';		
    
  12. 	}
    
  13. 	if($swiatlaOK)
    
  14. 	{
    
  15. 		echo 'Światła są sprawne<br/>';		
    
  16. 	}
    
  17. 	if($skrzyniaOK)
    
  18. 	{
    
  19. 		echo 'Skrzynia jest sprawna<br/>';		
    
  20. 	}
    
  21. 	if($paliwoOK)
    
  22. 	{
    
  23. 		echo 'Paliwo jest w baku<br/>';		
    
  24. 	}
    
  25. } // end stan();
    
  26.  
  27. stan(true, false, false, true, true);
    

Znów mamy identyczny problem: wywołując funkcję gdzieś w skrypcie musimy pamiętać, jaka jest kolejność parametrów, a postronna osoba już w ogóle nie zrozumie, co oznacza która wartość logiczna. Wykorzystajmy więc fakt, iż komputer zapisuje wszystko w postaci zerojedynkowej i prześlijmy wszystkie parametry jako jedną liczbę o długości pięciu bitów (od 0 do 32). W stałych zdefiniujemy nazwy poszczególnych elementów przypisując im kolejne potęgi dwójki, czyli kolejne bity liczby. Następnie za pomocą operatora alternatywy bitowej zbudujemy z nich odpowiednią kombinację:

  1. <?php
    
  2. define('SILNIK', 1);
    
  3. define('KOLA', 2);
    
  4. define('SWIATLA', 4);
    
  5. define('SKRZYNIA', 8);
    
  6. define('PALIWO', 16);
    
  7.  
  8. // Raportowanie stanu pojazdu
    
  9. function stan($stan)
    
  10. {
    
  11. 	if($stan & SILNIK)
    
  12. 	{
    
  13. 		echo 'Silnik jest sprawny<br/>';		
    
  14. 	}
    
  15. 	if($stan & KOLA)
    
  16. 	{
    
  17. 		echo 'Koła są sprawne<br/>';		
    
  18. 	}