You are on page 1of 608

PUP 4. Biblia to rzetelnie opracowany podręcznik zawierający analizy konkretnych problemów.

Ułatwi on
zapoznanie się z najnowszą wersją języka umożliwiającego tworzenie skryptów dołączanych do stron HTML.
Książka jest przeznaczona dla każdego, kto chce tworzyć witryny WWW, bardziej złożone, niż pozwala
HTML. Mamy szczególnie na myśli trzy grupy:
* projektantów stron WWW. którzy znają HTML i chcą rozpocząć tworzenie dynamicznych witryn WWW;
* zaawansowanych programistów (C, Java, Perl itp.), ale bez doświadczenia w projektowaniu dla WWW,
którzy chcą szybko nabrać biegłości w programowaniu dla serwerów WWW;
* programistów WWW, którzy używali innych technik programowania (np.: Active Server Pages, Java
Server Pages, Cold Fusion) i chcą zmienić lub po prostu poznać inne narzędzie.

Tytuł oryginału: PHP 4. Bibie


Tłumaczenie: Paweł Oonera

ISBN: 83-7197-391-8

Original English language edition Copyright © 2000 by IDG Books Worldwide, Inc.
All rights reserved including the right of reproduction in whole or in part of any form. This translation pu-
blished by arrangement with IDG Books Worldwide, Inc.
The IDG Books Worldwide logo is a trademark or registred trademark in the United States and/or other co-
untries under exclusive to license to IDG Books Worldwide, Inc., from International Data Group, Inc. The
Bible series trade dress is a trademark of IDG Books Worldwide, Inc. in the United States and/or other coun-
tries. Used by permission.

Polish language edition published by Wydawnictwo Helion.


Copyright ©2001

Wydawnictwo HELION
ul. Chopina 6, 44-100 GLIWICE
tel. (32) 231-22-19, (32) 230-98-63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)

Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie7php4bi
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Plik z przykładami do książki znajduje się pod adresem
ftp://ftp.helion.pl/przyklady/php4bi.zip

Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właści-
cieli.

Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były
kompletne i rzetelne. Nie biorąjednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane
z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie po-
noszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawar-
tych w książce.

Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji
w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzna, fotograficzną, a także
kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich
niniejszej publikacji.

Printed in Poland.
Druk: INTERDRUK - Łódź, tel. 682-18-56
Książka ta jest dedykowana naszym rodzicom:
Za ich m ilość,
za poświęcenie
i za to, że pozwolili nam w dzieciństwie dużo czytać.
Podziękowania
Projekt tej książki powstał w trakcie rozmów z Debrą Williams Cauley, która jest re-
daktorem w wydawnictwie IDG Books. Prowadziła cały projekt, znajdowała dodatko-
wych współpracowników oraz izolowała naiwnych początkujących autorów od twardej
rzeczywistości przemysłu wydawniczego. Susan Christopherson pracowała jako redak-
tor projektu, zanim przekazała pałeczkę Barb Guerra, która z kolei przekształciła nasze
chaotyczne notatki w rękopis. Bob Campbell przepisał go z niesłychaną szybkością.

Richard Lynch był naszym recenzentem technicznym. Powstrzymał nas od napisania


wielu rzeczy, które nie są prawdziwe, dał nam wiele dobrych rad, w jaki sposób ulep-
szyć wskazówki i przykłady. Pomógł nam napisać dużo lepszą książkę. Jednak nie jego
należy winić za błędy i braki, które jeszcze pozostały.

Nie stworzyliśmy tej książki sami. Dustin Mitchell napisał rozdział o bezpieczeństwie
i szyfrowaniu, Patrick McCuller jest autorem pierwszego szkicu rozdziałów o XML
i OOP. Ariel Garcia współpracowała w trakcie powstawania pierwszych projektów roz-
działów na temat PHP i JavaScript. Współpracownicy Joyce w firmie Epinions (szcze-
gólnie Lou Montuli i Jay Ashton) również zasługują na wdzięczność za współpracę
przy tworzeniu przykładów kodu oraz za wskazówki, jak używać PHP w silnie obcią-
żonym środowisku.

Szczególne podziękowania należą się twórcom PHP (a to: Rasmus Lerdorf, Zeev Sura-
ski, Andi Gutmans, Thies Arntzen, Stig Bakken, Sasha Schumann, Andrei Zmievski
oraz wielu ich współpracowników) ludziom, którzy tworzyli dokumentację do PHP
(m.in. Stig Bakken, Alexander Aulbach, Egon Schmid, Lars Torben Wilson, Jim Win-
stead) oraz wszystkim z listy dyskusyjnej o PHP. Szczególnie dziękujemy Rasmusowi,
Sashay oraz Richardowi Lynchowi za odpowiedzi na liście dyskusyjnej.

Autorzy chcieliby móc podziękować za pomoc swoim małżonkom, ale niestety jest to
w tej chwili niemożliwe. :)
Rzut oka na książkę
O Autorach...............................................................................................................19
Przedmowa..............................................................................................................^!

Część l Podstawy PHP.................................................................... 27


Rozdział 1. Dlaczego PHP? ......................................................................................29
Rozdział 2. Skrypty wykonywane na serwerze WWW.................................................41
Rozdział 3. Rozpoczynamy pracę z PHP....................................................................55
Rozdział 4. Dodajemy PHP do HTML.........................................................................69
Rozdział 5. Składnia, zmienne i wyświetlanie............................................................75
Rozdział 6. Typy w PHP ...........................................................................................91
Rozdział 7. Sterowanie ..........................................................................................111
Rozdział 8. Użycie i definiowanie funkcji ................................................................133
Rozdział 9. Ciągi i funkcje operujące na ciągach ....................................................155
Rozdział 10. Matematyka........................................................................................177
Rozdział 11. Tablice i funkcje operujące na tablicach ...............................................199
Rozdział 12. Przekazywanie danych pomiędzy stronami ............................................225
Rozdział 13. Funkcje systemu operacyjnego i dostępu do plików ..............................235
Rozdział 14. Styl PHP..............................................................................................251
Rozdział 15. Podstawowe pułapki PHP.....................................................................277

Część II PHP i bazy danych ............................................................297


Rozdział 16. Wybór bazy danych dla PHP .................................................................299
Rozdział 17. Samouczek SQL...................................................................................311
Rozdział 18. Funkcje PHP i MySQL ..........................................................................325
Rozdział 19. Wyświetlanie zapytań w tabelach .........................................................337
6__________________________________________________PHP 4. Biblia

Rozdział 20. Tworzenie formularzy z zapytań.............................................................351


Rozdział 21. Dziennik sieciowy ................................................................................363
Rozdział 22. Sieciowe głosowanie............................................................................377
Rozdział 23. Styl i efektywność rozwiązań na podstawie PHP i bazy danych ..............389
Rozdział 24. Pułapki tandemu PHP-baza danych .......................................................399

Część III Techniki zaawansowane....................................................411


Rozdział 25. Sesje...................................................................................................413
Rozdział 26. Cookie i HTTP......................................................................................427
Rozdział 27. PHP i JavaScript ..................................................................................441
Rozdział 28. E-mail..................................................................................................455
Rozdział 29. PHP i XML...........................................................................................469
Rozdział 30. Programowanie obiektowe w PHP ........................................................493
Rozdział 31. Bezpieczeństwo i kryptografia ..............................................................515
Rozdział 32. Konfiguracja i dostrajanie.....................................................................535

Dodatki............................................................................................553
Dodatek A PHP dla programistów C ......................................................................555
Dodatek B PHP dla programistów ASP ..................................................................561
Dodatek C PHP dla programistów HTML................................................................571
Dodatek D Zasoby Sieci na temat PHP..................................................................579
Słownik..................................................................................................................587
Skorowidz...............................................................................................................597
Spis treści
O Autorach...............................................................................................................19
Przedmowa...............................................................................................................21

Część l Podstawy PHP....................................................................27


Rozdział 1. Dlaczego PHP? ......................................................................................29
Co to jest PHP?.......................................................................................................................29
Historia PHP......................................................... ................................................................. 30
Dlaczego kochamy PHP? ....................................................................................................... 31
PHP jest darmowy............................................................................................................ 31
PHP jest łatwy .................................................................................................................. 32
PHP można wbudować..................................................................................................... 33
PHP jest niezależny.......................................................................................................... 35
PHP nie bazuje na znacznikach........................................................................................ 35
PHP jest stabilny .............................................................................................................. 36
PHP jest szybki................................................................................................................. 36
PHP jest otwarty............................................................................................................... 37
PHP dobrze współpracuje z innymi produktami.............................................................. 38
Popularność PHP rośnie................................................................................................... 38
PHP nie jest niczyją własnością....................................................................................... 39
Społeczność PHP..............................................................................................................40
Podsumowanie........................................................................................................................ 40
Rozdział 2. Skrypty wykonywane na serwerze WWW.................................................41
Statyczny HTML.................................................................................................................... 41
Technologie wykonywane na kliencie.................................................................................... 44
Skrypty wykonywane na serwerze.......................................................................................... 47
Do czego są dobre skrypty serwera ........................................................................................ 51
Podsumowanie........................................................................................................................ 53
Rozdział 3. Rozpoczynamy pracę z PHP....................................................................55
Dzierżawa lub własny serwer................................................................................................. 55
Wariant z dostawcąInternetu........................................................................................... 55
Własny serwer: wady i zalety........................................................................................... 58
Rozwiązania pośrednie..................................................................................................... 59
Instalowanie PHP................................................................................................................... 59
Zanim zaczniesz............................................................................................................... 60
Procedura instalacji.......................................................................................................... 61
Narzędzia programistyczne.............................................................................................. 66
Podsumowanie........................................................................................................................ 67
8__________________________________________________PHP 4. Biblia

Rozdział 4. Dodajemy PHP do HTML.........................................................................69


HTML jest gotowy na PHP .................................................................................................... 69
Przełączanie się z HTML do PHP .......................................................................................... 70
Kanoniczne znaczniki PHP .............................................................................................. 70
Krótkie znaczniki otwierające (w stylu SGML)............................................................... 70
Witaj świecie.................................................................................................................... 71
Wejście i wyjście z trybu PHP ......................................................................................... 72
Dołączanie plików............................................................................................................ 73
Podsumowanie........................................................................................................................ 74
Rozdział 5. Składnia, zmienne i wyświetlanie............................................................75
PHP wiele wybacza................................................................................................................ 75
HTML to nie PHP...................................................................................................................76
Składnia PHP bazuje na C...................................................................................................... 76
PHP nie przejmuje się odstępami..................................................................................... 76
PHP jest czasami wrażliwy na wielkość liter ................................................................... 77
Instrukcje to wyrażenia zakończone średnikiem .............................................................. 77
Bloki................................................................................................................................. 80
Komentarze ............................................................................................................................ 80
Komentarze wielowierszowe w stylu C ........................................................................... 81
Komentarze jednowierszowe: # i //.................................................................................. 81
Zmienne.................................................................................................................................. 82
PHP skorzystał ze stylu zmiennych Perl........................................................................... 82
Deklarowanie zmiennych................................................................................................. 82
Przypisywanie zmiennym wartości.................................................................................. 82
Zmiana wartości zmiennych............................................................................................. 83
Nieprzypisane zmienne.................................................................................................... 83
Możesz dowolnie zmieniać tryby pracy ........................................................................... 85
Wyjście................................................................................................................................... 86
Echo i print....................................................................................................................... 86
Zmienne i ciągi................................................................................................................. 87
Podsumowanie........................................................................................................................ 88
Rozdział 6. Typy w PHP ...........................................................................................91
Pierwsza zasada: nie przejmuj się........................................................................................... 91
Brak deklaracji typów zmiennych.................................................................................... 91
Automatyczna konwersja typów ...................................................................................... 92
Typy w PHP ........................................................................................................................... 92
Typy proste............................................................................................................................. 93
Integer.............................................................................................................................. 93
Double..............................................................................................................................94
Boolean............................................................................................................................ 95
Przykłady.......................................................................................................................... 96
String................................................................................................................................ 97
Tablice.................................................................................................................................. 100
Implementacja tablic...................................................................................................... 101
Ciągi znaków jako indeksy tablicy................................................................................. 101
Czy w PHP są struktury?................................................................................................ 102
Inne własności tablic ...................................................................................................... 102
Obiekty................................................................................................................................. 102
Przegląd OOP................................................................................................................. 102
Jak bardzo obiektowy jest PHP? .................................................................................... 103
Definiowanie klas w PHP............................................................................................... 103
Tworzenie obiektów....................................................................................................... 104
Spis treści_________________________________________________ 9

Kontrola typów..................................................................................................................... 104


Przypisania i konwersje........................................................................................................ 105
Przepełnienie liczby całkowitej...................................................................................... 109
Szukamy największej liczby całkowitej......................................................................... 109
Podsumowanie...................................................................................................................... 110
Rozdział 7. Sterowanie ..........................................................................................111
Wyrażenia logiczne .............................................................................................................. 112
Stałe logiczne ................................................................................................................. 112
Operatory logiczne......................................................................................................... 112
Operatory porównania.................................................................................................... 114
Operator trójskładnikowy............................................................................................... 116
Instrukcje warunkowe........................................................................................................... 117
If-else ............................................................................................................................. 117
Switch............................................................................................................................. 120
Pętle...................................................................................................................................... 121
Pętle ograniczone i nieograniczone............................................................................... 122
While.............................................................................................................................. 122
Do-while......................................................................................................................... 123
For.................................................................................................................................. 123
Przykłady pętli................................................................................................................ 124
Break i continue ............................................................................................................. 126
Pętle nieskończone......................................................................................................... 128
Składnia alternatywna........................................................................................................... 129
Przerywanie wykonania........................................................................................................ 129
Podsumowanie...................................................................................................................... 130
Rozdział 8. Użycie i definiowanie funkcji ................................................................133
Użycie funkcji...................................................................................................................... 133
Zwracane wartości a efekty uboczne.............................................................................. 134
Dokumentacja funkcji........................................................................................................... 134
Nagłówki w dokumentacji.............................................................................................. 135
Szukanie opisu funkcji................................................................................................... 136
Definiowanie własnych funkcji............................................................................................ 136
Czym jest funkcja?......................................................................................................... 136
Składnia definicji funkcji............................................................................................... 137
Przykład definicji funkcji............................................................................................... 137
Parametry formalne i parametry aktualne....................................................................... 139
Nieprawidłowa liczba argumentów................................................................................ 139
Funkcje a zasięg zmiennych................................................................................................. 139
Zmienne globalne i lokalne ............................................................................................ 140
Zmienne statyczne.......................................................................................................... 141
Zasięg funkcji....................................................................................................................... 142
Include oraz require........................................................................................................ 142
Rekurencja...................................................................................................................... 143
Zagadnienia zaawansowane.................................................................................................. 144
Zmienna liczba argumentów .......................................................................................... 145
Wywołanie przez wartość a wywołanie przez referencję............................................... 148
Wywołanie przez referencję........................................................................................... 148
Zmienne jako nazwy funkcji.......................................................................................... 150
Bardziej skomplikowany przykład................................................................................. 150
Podsumowanie...................................................................................................................... 153
10_________________________________________________PHP 4. Biblia

Rozdział 9. Ciągi i funkcje operujące na ciągach ....................................................155


Ciągi w PHP ......................................................................................................................... 155
Znaki i indeksy ciągu ..................................................................................................... 156
Operatory dla ciągów ..................................................................................................... 156
Złączenie i przypisanie................................................................................................... 157
Funkcje operujące na ciągach............................................................................................... 157
Sprawdzanie ciągów....................................................................................................... 157
Szukanie znaków i podciągów ....................................................................................... 158
Porównywanie i przeszukiwanie .................................................................................... 159
Przeszukiwanie............................................................................................................... 160
Wycinanie podciągu....................................................................................................... 161
Funkcje porządkujące..................................................................................................... 163
Zastępowanie ciągów..................................................................................................... 163
Ciągi i kolekcje znaków ................................................................................................. 165
Funkcje analizujące........................................................................................................ 167
Funkcje zmiany wielkości liter....................................................................................... 169
Funkcje znaków sterujących........................................................................................... 170
Formatowanie danych.................................................................................................... 171
Zaawansowane własności ciągów......................................................................................... 173
Wyrażenia regularne....................................................................................................... 173
Funkcje HTML............................................................................................................... 176
Podsumowanie...................................................................................................................... 176
Rozdział 10. Matematyka........................................................................................177
Typy numeryczne................................................................................................................. 177
Operatory matematyczne...................................................................................................... 178
Operatory arytmetyczne................................................................................................. 178
Operatory arytmetyczne i typy ....................................................................................... 178
Operator inkrementacji................................................................................................... 179
Operator przypisania...................................................................................................... 180
Operatory porównania.................................................................................................... 180
Kolejność operacji i nawiasy.......................................................................................... 181
Proste funkcje matematyczne ............................................................................................... 182
Konwersja podstawy............................................................................................................. 184
Funkcje wykładnicze i logarytmy......................................................................................... 186
Trygonometria...................................................................................................................... 186
Liczby losowe....................................................................................................................... 190
Inicjowanie generatora................................................................................................... 190
Przykład: losowy wybór................................................................................................. 192
Arytmetyka o dowolnej dokładności.................................................................................... 193
Przykład użycia funkcji o dowolnej dokładności........................................................... 194
Konwersja obliczeń na dowolną dokładność.................................................................. 195
Podsumowanie...................................................................................................................... 197
Rozdział 11.Tablice i funkcje operujące na tablicach...............................................199
Użycie tablic......................................................................................................................... 199
Czym są tablice PHP?........................................................................................................... 200
Tworzenie tablic................................................................................................................... 202
Bezpośrednie przypisanie............................................................................................... 202
Konstrukcja arrayQ......................................................................................................... 203
Podawanie indeksów przy użyciu arrayQ....................................................................... 203
Funkcje zwracające tablice............................................................................................. 204
Odczytywanie wartości......................................................................................................... 204
Konstrukcja list()............................................................................................................ 205
Spis treści__________________________________________________11

Tablice wielowymiarowe...................................................................................................... 206


Informacje o tablicach .......................................................................................................... 207
Usuwanie z tablicy ............................................................................................................... 207
Iteracje.................................................................................................................................. 208
Użycie funkcji iteracyjnych............................................................................................ 208
Iteracje za pomocą currentQ i next().............................................................................. 210
Powtórne przeglądanie za pomocąreset()...................................................................... 2 1 1
Wypisywanie w odwrotnym porządku za pomocąend() i prev()................................... 212
Pobieranie wartości kluczy za pomocąkey().................................................................. 213
Wartości puste i funkcja each()...................................................................................... 213
Przeglądanie tablicy za pomocą array _walk()................................................................ 214
Stosy i kolejki....................................................................................................................... 215
Przekształcenia tablic ........................................................................................................... 218
Pobieranie kluczy i wartości........................................................................................... 218
Zamiana, odwracanie i mieszanie................................................................................... 219
Zamiana pomiędzy tablicąi zmiennymi............................................................................... 222
Sortowanie............................................................................................................................ 222
Podsumowanie...................................................................................................................... 223
Rozdział 12. Przekazywanie danych pomiędzy stronami ............................................225
HTTP jest protokołem bezstanowym.................................................................................... 225
Argumenty GET................................................................................................................... 226
Inne zastosowania adresów URL w stylu GET..................................................................... 228
Argumenty POST................................................................................................................. 230
Zarządzanie zmiennymi w PHP............................................................................................ 232
Podsumowanie...................................................................................................................... 234
Rozdział 13. Funkcje systemu operacyjnego i dostępu do plików ..............................235
Funkcje czytania i zapisywania plików ................................................................................ 236
Otwarcie pliku................................................................................................................ 236
Czytanie pliku ................................................................................................................ 238
Zapis do pliku................................................................................................................. 239
Zamknięcie pliku............................................................................................................ 241
Funkcje systemu plików i katalogów.................................................................................... 241
feof.................................................................................................................................241
file_exists....................................................................................................................... 241
filesize............................................................................................................................ 244
Funkcje sieciowe.................................................................................................................. 244
Funkcje logu systemowego ............................................................................................ 244
Funkcje DNS.................................................................................................................. 244
Funkcje gniazd............................................................................................................... 245
Funkcje daty i czasu ............................................................................................................. 245
Jeżeli nie znasz daty ani czasu........................................................................................ 246
Jeżeli już odczytałeś datę i czas...................................................................................... 247
Funkcje konwersji kalendarza .............................................................................................. 247
Podsumowanie...................................................................................................................... 249
Rozdział 14. Styl PHP..............................................................................................251
Zalety prawidłowego stylu ................................................................................................... 251
Czytelność............................................................................................................................ 252
Komentarze.................................................................................................................... 256
Nazwy zmiennych i plików............................................................................................ 257
Łatwość konserwacji............................................................................................................ 259
Unikaj „magicznych liczb"............................................................................................. 259
Funkcje........................................................................................................................... 260
12_________________________________________________PHP 4. Biblia

Pliki dołączane............................................................................................................... 260


Interfejs obiektowy......................................................................................................... 262
Solidność.............................................................................................................................. 263
Niedostępność usługi...................................................................................................... 263
Niespodziewany typ zmiennej........................................................................................ 263
Zwięzłość i wydajność.......................................................................................................... 264
Używaj właściwych algorytmów.................................................................................... 264
Poprawianie wydajności................................................................................................. 264
Zwięzłość: zmniejszanie................................................................................................. 265
Wskazówki na temat zwięzłości..................................................................................... 266
Tryb HTML, czy PHP? ........................................................................................................ 268
Oddzielanie kodu od projektu............................................................................................... 274
Funkcje........................................................................................................................... 274
Arkusze stylów w PHP................................................................................................... 274
Szablony i spójność stron............................................................................................... 275
Podsumowanie...................................................................................................................... 276
Rozdział 15. Podstawowe pułapki PHP.....................................................................277
Problemy związane z instalacją............................................................................................ 277
Źródło pliku wyświetlane w przeglądarce...................................................................... 278
Blok PHP pokazuje się jako tekst; przeglądarka chce zapisać plik ................................ 278
Nieodnaleziony serwer lub niemożliwe wyświetlenie strony......................................... 278
Problemy z wyświetlaniem................................................................................................... 279
Całkowicie pusta strona.................................................................................................. 279
Niekompletna lub nieprawidłowa strona........................................................................ 279
Kod PHP pokazuje się w przeglądarce........................................................................... 281
Niepowodzenie przy ładowaniu strony................................................................................. 282
Nieodnaleziona strona.................................................................................................... 282
Nieudane otwarcie pliku do włączenia........................................................................... 283
Błędy analizy składni............................................................................................................ 283
Komunikat błędu składni................................................................................................ 283
Brakujący średnik........................................................................................................... 284
Brak znaku $ .................................................................................................................. 284
Nieprawidłowa zmiana trybu.......................................................................................... 285
Nieoznaczone apostrofy................................................................................................. 285
Inne błędy składni.......................................................................................................... 286
Uprawnienia do plików......................................................................................................... 286
Błąd HTTP nr 403 .......................................................................................................... 286
Brak dołączanych plików ..................................................................................................... 286
Ostrzeżenie przy włączaniu pliku................................................................................... 287
Nieprzypisane zmienne......................................................................................................... 287
Zmienna nie pokazuje się w wynikowym ciągu............................................................. 287
Jak zachowują się niezainicjowane zmienne.................................................................. 288
Problemy z wielkością liter............................................................................................ 288
Problemy z zasięgiem..................................................................................................... 288
Problemy z funkcjami........................................................................................................... 289
Wywołanie niezdefiniowanej funkcji............................................................................. 289
Nie można ponownie zadeklarować funkcji................................................................... 290
Nieprawidłowa liczba argumentów................................................................................ 290
Błędy matematyczne............................................................................................................. 290
Ostrzeżenie o dzieleniu przez zero ................................................................................. 290
Niespodziewane wyniki działań..................................................................................... 291
NaN (lub NAŃ).............................................................................................................. 291
Spis treści___________________________________________________13

Przekroczenie czasu oczekiwania......................................................................................... 292


Podsumowanie...................................................................................................................... 292

Część II PHP i bazy danych ............................................................297


Rozdział 16. Wybór bazy danych dla PHP .................................................................299
Czemu używamy baz danych?.............................................................................................. 299
Unikanie redundancji..................................................................................................... 300
Unikanie nudnego programowania................................................................................. 300
Szukanie......................................................................................................................... 300
Bezpieczeństwo.............................................................................................................. 301
Architektura wielowarstwowa........................................................................................ 301
Wybór bazy danych.............................................................................................................. 302
Możesz nie mieć wyboru................................................................................................ 302
Plikowe, relacyjne i obiektowo-relacyjne bazy danych.................................................. 302
ODBC/JDBC kontra własne API................................................................................... 303
Zmiana bazy danych....................................................................................................... 304
Przegląd zaawansowanych funkcji....................................................................................... 304
GUI.................................................................................................................................304
Podzapytania.................................................................................................................. 304
Złożone złączenia........................................................................................................... 305
Wielowątkowość i blokowanie....................................................................................... 305
Transakcje...................................................................................................................... 305
Procedury i wyzwalacze................................................................................................. 306
Klucze obce i więzy integralności.................................................................................. 306
Replikacja bazy danych.................................................................................................. 306
Bazy danych obsługiwane przez PHP................................................................................... 307
Wybieramy MySQL.............................................................................................................307
Podsumowanie...................................................................................................................... 308
Rozdział 17. Samouczek SQL...................................................................................311
Standardy SQL ..................................................................................................................... 311
Podstawowe wyrażenia SQL................................................................................................ 312
SELECT......................................................................................................................... 312
INSERT.......................................................................................................................... 315
UPDATE........................................................................................................................ 316
DELETE......................................................................................................................... 316
Projekt bazy danych.............................................................................................................. 316
Użycie połączeń do bazy danych.......................................................................................... 319
Bezpieczeństwo i uprawnienia.............................................................................................. 319
Ustawianie uprawnień.................................................................................................... 320
Przechowywanie haseł w innym miejscu ....................................................................... 320
Użycie formularzy PHP do sprawdzania haseł............................................................... 321
Tworzenie kopii bezpieczeństwa.................................................................................... 322
Podsumowanie...................................................................................................................... 322
Rozdział 18. Funkcje PHP i MySQL ..........................................................................325
Łączenie z MySQL............................................................................................................... 325
Tworzenie zapytań w MySQL.............................................................................................. 326
Pobieranie wyniku................................................................................................................ 327
Pobieranie opisu danych....................................................................................................... 330
Korzystanie z wielokrotnych połączeń................................................................................. 330
Kontrola błędów................................................................................................................... 332
Tworzenie baz danych MySQL za pomocą PHP.................................................................. 332
14_________________________________________________PHP 4. Biblia

Funkcje MySQL................................................................................................................... 333


Podsumowanie...................................................................................................................... 335
Rozdział 19. Wyświetlanie zapytań w tabelach .........................................................337
Tabele HTML i tabele bazy danych...................................................................................... 338
Przekształcenie jeden w jeden........................................................................................ 338
Przykład: wyświetlanie jednej tabeli.............................................................................. 338
Przykładowe tabele......................................................................................................... 340
Ulepszanie wyświetlania................................................................................................ 341
Złożone odwzorowania......................................................................................................... 343
Wiele zapytań albo skomplikowane wyświetlanie ......................................................... 344
Użycie kilku zapytań...................................................................................................... 345
Przykład skomplikowanego wyświetlania...................................................................... 346
Tworzenie przykładowych tabel........................................................................................... 348
Podsumowanie...................................................................................................................... 350
Rozdział 20. Tworzenie formularzy z zapytań.............................................................351
Formularze HTML ............................................................................................................... 351
Samoprzetwarzanie............................................................................................................... 352
Obsługa formularzy.............................................................................................................. 353
Formularze zależne od zmiennych........................................................................................ 356
TEXT i TEXTAREA......................................................................................................356
CHECKBOX.................................................................................................................. 358
RADIO........................................................................................................................... 359
SELECT.........................................................................................................................359
Formularze zależne od zapytań............................................................................................. 361
Podsumowanie...................................................................................................................... 362
Rozdział 21. Dziennik sieciowy ................................................................................363
Dlaczego dziennik?............................................................................................................... 363
Najprostszy dziennik ............................................................................................................ 364
Wprowadzanie danych przez HTTP..................................................................................... 368
Dołączenie bazy danych....................................................................................................... 370
Możliwe rozszerzenia........................................................................................................... 375
Podsumowanie...................................................................................................................... 376
Rozdział 22. Sieciowe głosowanie............................................................................377
Zadania systemu................................................................................................................... 377
Cele systemu.................................................................................................................. 378
Struktura......................................................................................................................... 378
Obsługa bazy danych...................................................................................................... 379
Zbieranie głosów.................................................................................................................. 379
Wyświetlanie sumarycznych wyników................................................................................. 383
Nadużycia i skalowanie.................................................................................................. 387
Podsumowanie...................................................................................................................... 387
Rozdział 23. Styl i efektywność rozwiązań na podstawie PHP i bazy danych ..............389
Połączenia — ograniczanie i powtórne użycie ..................................................................... 390
Przykład nieprawidłowego użycia: jedno połączenie na wyrażenie ............................... 390
Kilka wyników nie wymaga kilku połączeń................................................................... 391
Trwałe połączenia........................................................................................................... 391
Przenoszenie pracy na serwer bazy danych.......................................................................... 392
Baza jest szybsza od ciebie............................................................................................. 392
Przykład nieprawidłowego użycia: pętla zamiast warunku ............................................ 393
Spis treści__________________________________________________15

Tworzenie pól daty i czasu............................................................................................. 394


Szukanie ostatnio wstawionego wiersza......................................................................... 395
Podsumowanie...................................................................................................................... 397
Rozdział 24. Pułapki tandemu PHP-baza danych .......................................................399
Brak połączenia.................................................................................................................... 399
Problemy z uprawnieniami................................................................................................... 402
Nieoznaczone apostrofy........................................................................................................ 403
Nieprawidłowe zapytania SQL............................................................................................. 405
Pomyłki w nazwach........................................................................................................ 407
Pomyłki przy przecinkach.............................................................................................. 407
Ciągi nieotoczone apostrofami....................................................................................... 407
Niezainicjowane zmienne............................................................................................... 407
Zbyt mało danych, zbyt dużo danych ................................................................................... 408
Kontrola poprawności........................................................................................................... 409
Podsumowanie...................................................................................................................... 409

Część III Techniki zaawansowane....................................................^!


Rozdział 25. Sesje...................................................................................................413
Czym są sesje?...................................................................................................................... 413
Co stanowi problem?...................................................................................................... 413
Dlaczego się tym zajmujemy?........................................................................................ 414
Alternatywy sesji ..................................................................................................................414
Adres IP.......................................................................................................................... 414
Ukryte zmienne.............................................................................................................. 415
Cookie............................................................................................................................ 416
Jak działają sesje w PHP....................................................................................................... 416
Uaktywnianie sesji w PHP ............................................................................................. 417
Rejestrowanie zmiennych w sesji................................................................................... 418
Gdzie są przechowywane dane?..................................................................................... 419
Funkcje obsługi sesji............................................................................................................ 420
Przykładowy kod sesji.......................................................................................................... 422
Zagadnienia konfiguracji...................................................................................................... 423
Pułapki i wykrywanie usterek............................................................................................... 423
Podsumowanie...................................................................................................................... 426
Rozdział 26. Cookie i HTTP......................................................................................427
Cookie.................................................................................................................................. 427
Funkcja setcookieQ........................................................................................................ 428
Przykłady........................................................................................................................ 428
Usuwanie cookie............................................................................................................ 430
Odczytywanie cookie..................................................................................................... 431
Zmienne GET, POST i cookie........................................................................................ 432
Pułapki cookie................................................................................................................ 435
Wysyłanie nagłówków HTTP............................................................................................... 437
Przykład: przekierowanie............................................................................................... 437
Przykład: uwierzytelnianie HTTP .................................................................................. 438
Pułapki związane z nagłówkami..................................................................................... 439
Podsumowanie...................................................................................................................... 439
Rozdział 27. PHP i JavaScript ..................................................................................441
Tworzenie kodu JavaScript w PHP....................................................................................... 441
Pojedynek obiektów....................................................................................................... 442
PHP nie analizuje wysyłanych danych........................................................................... 442
Kiedy używać JavaScript............................................................................................... 444
16_________________________________________________PHP 4. Biblia

PHP jako koło zapasowe do JavaScript................................................................................ 444


JavaScript statyczny kontra dynamiczny.............................................................................. 445
Dynamiczna generacja formularzy................................................................................. 446
Przesyłanie danych z JavaScript do PHP........................................................................ 450
Podsumowanie...................................................................................................................... 452
Rozdział 28. E-mail..................................................................................................455
Informacje na temat architektury e-mail............................................................................... 455
Model systemu e-mail.................................................................................................... 456
Pobieranie poczty za pomocą PHP ....................................................................................... 460
Tworzenie przez zaniechanie.......................................................................................... 461
Tworzenie przez przykład .............................................................................................. 461
Tworzenie przez upiększanie.......................................................................................... 461
Wysyłanie poczty za pomocą PHP ....................................................................................... 462
Konfiguracja Windows................................................................................................... 462
Konfiguracja Unixa........................................................................................................ 462
Funkcja maił................................................................................................................... 462
Więcej na temat aplikacji pocztowych.................................................................................. 464
Wysyłanie poczty z formularza...................................................................................... 464
Wysyłanie poczty przy użyciu bazy danych................................................................... 466
Własna aplikacja pocztowa w PHP ................................................................................ 466
Podsumowanie...................................................................................................................... 468
Rozdział 29. PHP i XML...........................................................................................469
Co to jest XML?...................................................................................................................469
Praca z XML......................................................................................................................... 472
Dokumenty i DTD................................................................................................................ 472
Struktura DTD................................................................................................................474
Analizatory kontrolujące i nie kontrolujące poprawności.............................................. 476
DOM kontra SAX................................................................................................................. 477
SAX................................................................................................................................ 477
DOM.............................................................................................................................. 478
Funkcje PHP dla DOM.........................................................................................................478
SAX................................................................................................................................ 480
Użycie SAX.................................................................................................................... 481
Opcje SAX.....................................................................................................................482
Funkcje PHP dla SAX .......................................................................................................... 483
Przykładowa aplikacja SAX................................................................................................. 486
Pułapki i wyszukiwanie błędów ........................................................................................... 491
Podsumowanie...................................................................................................................... 492
Rozdział 30. Programowanie obiektowe w PHP ........................................................493
Jak dobre jest programowanie obiektowe?........................................................................... 494
Terminologia programowania obiektowego................................................................... 494
Obiekty, klasy i typy w PHP .......................................................................................... 495
Atrybuty ......................................................................................................................... 496
Funkcje........................................................................................................................... 496
Konstruktory .................................................................................................................. 497
Dziedziczenie................................................................................................................. 497
Przesłanianie .................................................................................................................. 498
Przeciążanie.................................................................................................................... 499
Zasięg............................................................................................................................. 499
Przypisywanie, aliasy i referencje .................................................................................. 500
Wyświetlanie i drukowanie obiektów ............................................................................ 502
Przeglądanie................................................................................................................... 503
Spis treści ________________________________________________17

Funkcje przeglądania typów i klas........................................................................................ 503


Serializacja obiektów............................................................................................................ 507
Zewnętrzne interfejsy: COM, Java i CORBA....................................................................... 508
COM i DCOM................................................................................................................509
Przykładowa aplikacja obiektowa......................................................................................... 510
Podsumowanie...................................................................................................................... 513
Rozdział 31. Bezpieczeństwo i kryptografia ..............................................................515
Możliwe ataki....................................................................................................................... 516
Zmiana zawartości witryny ............................................................................................ 516
Dostęp do kodu źródłowego........................................................................................... 518
Odczyt dowolnego pliku................................................................................................. 519
Uruchamianie dowolnych programów ........................................................................... 521
Wirusy i inne e-robaki.................................................................................................... 523
Bezpieczeństwo poczty .................................................................................................. 523
Szyfrowanie.......................................................................................................................... 524
Szyfrowanie kluczem publicznym.................................................................................. 524
Szyfrowanie pojedynczym kluczem............................................................................... 526
Szyfrowanie cookie........................................................................................................ 527
Mieszanie ....................................................................................................................... 529
Cyfrowe podpisywanie plików....................................................................................... 530
Secure Server Layer ....................................................................................................... 531
Witryny podejmujące problematykę bezpieczeństwa........................................................... 531
Podsumowanie...................................................................................................................... 532
Rozdział 32. Konfiguracja i dostrajanie.....................................................................535
Podglądanie zmiennych środowiska..................................................................................... 535
Poznajemy konfigurację PHP............................................................................................... 535
Opcje kompilacii............................................................................................................ 536
Opcje kompilacji dla postaci CGI .................................................................................. 541
Pliki konfiguracyjne Apache .......................................................................................... 543
Plikphp.ini..................................................................................................................... 545
Poprawianie wydajności PHP............................................................................................... 550
Podsumowanie...................................................................................................................... 552

Dodatki............................................................................................553
Dodatek A PHP dla programistów C ......................................................................555
Dodatek B PHP dla programistów ASP ..................................................................561
Dodatek C PHP dla programistów HTML................................................................571
Dodatek D Zasoby Sieci na temat PHP..................................................................579
Słownik ..................................................................................................................587
Skorowidz...............................................................................................................597
JL8________________________________________________PHP 4. Biblia
O Autorach
Tim Converse pisał oprogramowanie pomagające w wyborze szalików, odpowiadające
na pytania na temat stacji kosmicznych, pobierające notowania giełdowe oraz symulu-
jące robienie kolacji. Zdobył dyplom magistra informatyki na University of Chicago.
Pracuje teraz w Excite@Home, gdzie zajmuje się wyszukiwarką internetową.

Joyce Park posiada dyplom magistra historii University of Chicago; pracowała nad kil-
koma witrynami informacyjnymi w PHP, między innymi nagrodzoną MysteryGuide.
com. Jej teksty zdobyły uznanie redaktorów oraz czytelników Slashdot, OSOpinion, Li-
nux.com i wielu innych na całym świecie. Joyce jest teraz projektantem witryn w firmie
Epinions.com.
20 PHP 4. Biblia
Przedmowa
Witamy w książce P HP 4. Biblial Mimo że jesteśmy stronniczy, wierzymy, że PHP,
skryptowy język programowania dla WWW, zajął niszę najłatwiejszego i najbardziej
elastycznego narzędzia dla serwerów WWW, pozwalając na tworzenie wspaniałych
i bardzo szybkich witryn. Mimo że miliony programistów WWW na całym świecie
zwykle mogą się mylić, w tym konkretnym przypadku nie mylą się.

PHP 4.0, udostępniony na wiosnę 2000 roku, został, w porównaniu do PHP 3, uzupeł-
niony o wiele nowych możliwości, działa dużo szybciej. W książce tej przedstawione są
główne możliwości tej wersji programu. Szczegółowe przykłady pokazują, w jaki spo-
sób tworzyć witryny WWW przy użyciu PHP.

Co to jest PHP?
PHP jest językiem skryptowym wbudowywanym w strony WWW, wykonywanym na
serwerze. Jest on zgodny z większością najważniejszych serwerów WWW (najbardziej
ze znakomitym Apache). PHP pozwala na wbudowanie fragmentów kodu w normalne
strony HTML — kodu, który jest interpretowany, gdy strony są przesyłane do użyt-
kownika. PHP spełnia rolę „kleju" ułatwiającego łączenie stron WWW z bazami da-
nych umieszczonych po stronie serwera.

Dlaczego PHP?
Odpowiedzi na to pytanie poświęcamy prawie cały rozdział l. Najkrótsza odpowiedź
to: jest darmowy, ma duże możliwości, jest niezależny, stabilny, szybki, łatwy do nauki,
dobrze zaprojektowany, dobrze współpracuje z innymi produktami. A poza tym mamy
dostęp do kodu źródłowego.
22___________________________________________________PHP 4. Biblia

Co nowego w PHP 4?
PHP 4 zawiera wszystkie funkcje PHP oraz wbudowane wsparcie dla sesji, bardziej
spójną analizę składni, nowy typ Boolean oraz wiele nowych funkcji. Rdzeniem ma-
szyny skryptowej PHP jest teraz „Zend", który został napisany na nowo, aby zachować
spójność oraz zyskać rewelacyjną szybkość działania.

PHP to żywy organizm. Oryginalną wersję książki wydrukowano na wiosnę 2000 roku,
mniej więcej w tym samym czasie, gdy PHP 4 został oficjalnie wydany. Przykłady za-
warte w niej były intensywnie testowane za pomocą PHP 3 oraz różnych wersji beta PHP
4. Mimo, że opisaliśmy większość głównych funkcji PHP 4, wciąż pojawiają się nowe.

Dla kogo jest ta książka


Książka jest przeznaczona dla każdego, kto chce tworzyć witryny WWW, bardziej zło-
żone, niż pozwala HTML. Mamy szczególnie na myśli trzy grupy:
* projektantów stron WWW, którzy znają HTML i chcą rozpocząć tworzenie
dynamicznych witryn WWW;
* zaawansowanych programistów (C, Java, Perl itp.), ale bez doświadczenia
w projektowaniu dla WWW, którzy chcą szybko nabrać biegłości w progra-
mowaniu dla serwerów WWW;
* programistów WWW, którzy używali innych technik programowania (np.: Ac-
tive Server Pages, Java Server Pages, Cold Fusion) i chcą zmienić lub po pro-
stu poznać inne narzędzie.

Zakładamy, że czytelnik zna język HTML i ma podstawową wiedzę na temat sposobu


działania Sieci, ale nie oczekujemy żadnego dodatkowego doświadczenia programi-
stycznego. Aby zaoszczędzić czas bardziej zaawansowanym programistom, dodaliśmy
wiele notatek i komentarzy porównujących PHP z innymi językami i wskazujących,
które rozdziały i fragmenty mogą być przez nich pominięte. Na koniec przejrzyj nasze
dodatki, które zawierają specyficzne porady dla programistów C, ASP oraz projektan-
tów HTML.

Książka nie jest podręcznikiem


Grupa dokumentalistów PHP przygotowała świetny podręcznik, pod adresem http://
www.php.net, udostępniany oczywiście przez PHP. Książka ta nie jest takim podręczni-
kiem, ani nawet jego namiastką. Uważamy ją za uzupełnienie podręcznika.
Przedmowa__________________________________________________23

Podręcznik jest obszerny, obejmuje wszystkie aspekty i funkcje języka, jednak pozba-
wiony jest szczegółów. My, w przeciwieństwie, mamy możliwość skupienia się na te-
matach, które są najczęściej poruszane lub najmniej zrozumiałe, możemy wyjaśniać
i dawać długie przykłady.

Jak zorganizowana jest książka


Książka jest podzielona na trzy części:

Część l Podstawy
Rozdziały od 1. do 4. stanowią wprowadzenie do PHP i opisują zagadnienia, które mu-
sisz poznać przed rozpoczęciem pracy.

Rozdziały od 5. do 13. to przewodnik traktujący o głównych aspektach PHP (oprócz


współpracy z bazami danych): składni, typach danych, podstawowych funkcjach wbu-
dowanych. Możesz opuścić ten fragment i używać go jak skorowidza.

Rozdziały 14. i 15. to podręcznik stylu PHP oraz opis najczęściej spotykanych przy
programowaniu pułapek.

Część II PHP i bazy danych


Rozdziały 16. i 17. dają ogólną orientację na temat programowania dla WWW przy
użyciu baz danych SQL. Znajdują się tutaj porady, w jaki sposób wybrać najlepszy
system baz danych.

Rozdział 18. poświęcony jest funkcjom PHP dla MySQL — systemu baz danych, który
będziemy omawiali aż do końca drugiej części książki.

Rozdziały od 19. do 22. to szczegółowe i bogate w przykłady analizy przypadków


współpracy PHP z bazami danych.

W rozdziałach 23. i 24. znajdują się wskazówki i opisy pułapek w pracy z PHP i bazami
danych.

Część III Techniki zaawansowane


W każdym z rozdziałów od 25. do 32. opisujemy bardziej zaawansowane, niezależne te-
maty. Omawiamy: nowe wbudowane funkcje obsługi sesji, użycie mechanizmu cookie,
generowanie kodu Javascript, przyłączanie PHP do programów pocztowych, wsparcie
dla XML, programowanie obiektowe, bezpieczeństwo i opcje konfiguracji.
24_________________________________________________PHP 4. Biblia

Konwencje używane w książce


Używamy czcionki o stałej szerokości do zaznaczenia literałów kodu PHP. Fragmenty
kodu zawarte w tekście wyglądają w następujący sposób, natomiast oddzielny frag-
ment kodu wygląda tak:
p r i n t ( " t a k "} ;

Jeżeli wygląd strony WWW wygenerowanej przez PHP jest ważny, zamieszczamy ry-
sunek z kopią ekranu; gdy nie jest — zamieszczamy źródło strony wygenerowanej
przez PHP przy użyciu czcionki o stałej szerokości. Jeżeli chcemy odróżnić wynik
skryptu PHP widoczny w przeglądarce od aktualnego wyniku PHP (który tłumaczy
przeglądarka), nazywamy go „wynikiem z przeglądarki".

Jeżeli zaznaczamy fragment kodu kursywą, oznacza to, że to miejsce należy odpowied-
nio zmienić, a nie traktować tekst dosłownie. Jeżeli w normalnym tekście wyróżniamy
wyraz za pomocą kursywy, oznacza to, że wyraz jest niezbyt znany i zdefiniowany
w słowniku.

Znaczenie ikon
Ikony, przedstawione poniżej, są rozrzucone w całym tekście. Ich zadaniem jest wyraź-
ne zaznaczenie wagi informacji.

Wskazówki oznaczają sztuczki lub techniki PHP, które nie są oczywiste,


a mogą pozwolić na wykonanie jakiejś czynności łatwiej i efektywniej.

Ikona notatki zwykle oznacza dodatkowe informacje lub wyjaśnienia,


które jednak można zignorować, jeżeli nie wydają się interesujące.
Notatki w tej książce są często kierowane do określonej grupy czytel-
ników, którzy znają konkretny język programowania lub technologię.

Ikona ostrzeżenia wskazuje coś, co może być niezrozumiałe lub źle


użyte, i w efekcie może sprawiać programistom kłopoty.

Ikona nowej funkcji oznacza nową możliwość w PHP 4.

Używamy tej ikony, aby skierować czytelnika do pokrewnej informacji


w innym rozdziale lub innej części książki.
Przedmowa__________________________________________________25

Witryna WWW i przykłady kodu


Wszystkie przykłady z tej książki znajdują się pod adresem:
ftp://ftp.helion.pl/przyklady/php4bi.zip
Dodatkowe materiały znajdują się na stronie WWW pod adresem:
http://w\vw. troutworks. com/phpbook
.26________________________________________ PHP 4. Biblia

,.»»*».,„„,„ „„.„-u,*,
Cześć l
Podstawy PHP
Rozdział 1.
Dlaczego PHP?
W tym rozdziale:
+ Co to jest PHP?
«• Historia PHP
*• Dlaczego kochamy PHP?
* Wyprzedzamy konkurencję z PHP

Pierwszy rozdział zawiera wprowadzenie do PHP. Spróbujemy odpowiedzieć tu na kil-


ka często stawianych pytań na temat PHP, takich jak: „Co to jest" lub „Jakie jest
w porównaniu do podobnych technologii". Większość rozdziału zajęło wymienianie
powodów, dlaczego kochamy PHP. Jeżeli jesteś inżynierem szukającym argumentów,
aby przekonać szefa lub pytających „co to jest to P-coś-tam", rozdział ten dostarczy
podstawowych odpowiedzi.

Co to jest PHP?
PHP pochodzi od Hypertext Preprocessor. Właściwie produkt ten wcześniej nazywał
się Personal Home Page Tools, jednak gdy jego zakres rozszerzył się, w drodze głoso-
wania została wybrana nowa, bardziej właściwa nazwa. Dla pliku PHP można używać
dowolnego rozszerzenia, ale zalecane są .php, ,php3 oraz .phtml. Aktualnym numerem
wersji PHP jest 4, nazywany PHP 4 lub po prostu PHP.

PHP jest językiem skryptowym wbudowywanym w HTML, wykonywanym na serwerze.


Inne produkty w tej niszy to: Active Server Pages Microsoftu, ColdFusion firmy Allaire
oraz Java Server Pages — Sun. PHP jest czasami nazywany „darmowym ASP", ponieważ
sposób jego działania jest bardzo podobny do produktu (koncepcji) Microsoftu.

W następnym rozdziale prześledzimy dokładnie koncepcję skryptów wykonywanych na


serwerze, a w tej chwili możesz myśleć o nich jak o zestawie znaczników superHTML,
30__________________________________________Część l » Podstawy PHP

które pozwalają dodać funkcje obsługi serwera do stron WWW. Możesz np. użyć PHP
do tworzenia na bieżąco skomplikowanych stron WWW lub do uruchamiania programu
do obciążania karty kredytowej, gdy klient złoży zamówienie.

Ściśle mówiąc, PHP ma niewiele wspólnego z układem strony, zdarzeniami lub czym-
kolwiek innym, co nadaje wygląd stronom WWW. Właściwie wszystko, co robi PHP, jest
niewidoczne dla użytkownika. Ten, kto ogląda stronę PHP, nie jest w stanie powiedzieć,
że strona nie została napisana w HTML, ponieważ wynikiem PHP jest kod HTML.

PHP jest oficjalnym modułem do serwera HTTP Apache, który jest wiodącym, bezpłat-
nym serwerem WWW, napędzającym około 55% serwerów sieci. Oznacza to, że ma-
szyna skryptowa PHP jest wbudowana w serwer WWW, co powoduje szybszą obsługę
stron. Podobnie jak serwer Apache PHP jest niezależny od platformy, działa w kilku ro-
dzajach systemu Unix oraz w Windows. Wszystkie projekty prowadzone przez Apache
Software Foundation — włączając w to PHP — są dostępne na zasadzie open source
(mamy dostęp do kodu źródłowego).

Różne wersje PHP zebrały przez kilka lat wiele pochwał i nagród. PHP 3 był w 1999 r.
finalistą konkursu LinuxWorld Editor's Choice Awards (w kategorii biblioteka — na-
rzędzie programistyczne), w 1998 r. wygrał z ColdFusion CNet Builder.com Product
Awards (w kategorii najlepsze narzędzie skryptowe). Kombinacja PHP 3/MySQL wy-
grała w konkursie Database of the Year na Web '98. Nieźle, jak na program bez działu
PR, bez reklam i bez agencji reklamowej.

Historia PHP
Rasmus Lerdorf— programista, członek zespołu Apache —jest twórcą i siłą napędową
PHP. Pierwszą część PHP napisał na własny użytek w 1994 roku. Był to interfejs Perl
CGI, który pomagał śledzić, kto odwiedza stronę domową. W następnym roku, w od-
powiedzi na żądania użytkowników, którzy zaangażowali się w ten projekt, skompleto-
wał pakiet nazwany Personal Home Page Tools (znany również jako PHP Construction
Kit). Niebawem została wydana wersja 2. o nazwie PHP/FI, zawierająca Form Inter-
preter, narzędzie do przetwarzania zapytań SQL.

W połowie 1997 r. około witryn na całym świecie 50 000 używało PHP. Obsługa prze-
kroczyła możliwości jednej osoby, nawet tak energicznej jak Rasmus. Niewielki zespół
programistów rozpoczął projekt na zasadzie open source, korzystając z pomocy pro-
gramistów i użytkowników z całego świata. Dwóch izraelskich programistów, Zeev Su-
raski i Andi Gutmans (autorów analizatora składni do PHP 3 oraz PHP 4), rozszerza
i uogólnia go pod szyldem Zend.com (od ich imion, Zeev i Andi).

W czwartym kwartale 1998 r. nastąpił gwałtowny rozwój PHP, który korzystając z za-
sady open source cieszył się masowym zainteresowaniem. W październiku 1998 r. około
100 000 różnych domen używało w jakiś sposób PHP. Rok później przełamana została
bariera l 000 000 domen. W czasie pisaliśmy tę książkę, liczba ta eksplodowała do
około dwóch milionów.
Rozdział 1. » Dlaczego PHP?_______________________________________31^

Dlaczego kochamy PHP?


Jest wiele powodów, dla których kochamy PHP. W tym rozdziale poznasz niektóre z nich.

PHP jest darmowy


PHP nic nie kosztuje. Nic na początku, nic w trakcie pracy aplikacji. Czy wspominaliśmy,
że zestaw Apache+PHP+MySQL działa świetnie na niezbyt mocnym, tanim sprzęcie,
w przypadku którego nawet nie możesz myśleć o instalacji IIS+ASP+SQL Server?

Dla porównania w tabeli 1.1 zamieściliśmy średnie ceny detaliczne podobnych pro-
duktów.

Tabela 1.1.
Porównanie wydatków

Pozycja ASP ColdFusion JSP PHP

Tworzenie 0 - 2 000 zł ~1 500 zł Ozł Ozł


Serwer 2 500 zł ~5 000 zł 0 - 2 200 zł Ozł
RDBMS 5 000 - 20 000 zł 0 - 4 0 000 zł 40 000 zł Ozł
Wsparcie 0 - 1 000 zł 300 zł 300 zł Ozł

Oprogramowanie open source: nie bój się taniego


W zasadzie można by mieć wątpliwości na temat jakości i żywotności bezpłatnego
oprogramowania. Prawdopodobnie do tej opinii przyczyniło się oprogramowanie, za
które nie trzeba płacić, zwane freeware, shareware lub Free Software, postrzegane jako
należące do jednej z trzech kategorii:
* programy wypełniające małe niekomercyjne nisze;
4 programy wykonujące ciężkie niskopoziomowe zadania;
+ programy dla ludzi o dziwnych poglądach socjo-politycznych.

Czas na zmianę niektórych stereotypów. Jesteśmy w trakcie zmian w przemyśle two-


rzenia programów. Wiele (jeżeli nie większość) głównych programów dla konsumen-
tów jest dziś rozprowadzanych bez opłat: programy pocztowe, przeglądarki WWW, gry,
czy nawet pełne pakiety biurowe są rozdawane tak szybko, jak tylko ich twórcy potrafią
utworzyć wersję WWW lub ustawić serwer FTP. Oprogramowanie konsumenckie jest
coraz częściej postrzegane jako źródło strat, jak kolorowy kwiatek przyciągający
pszczoły — innymi słowy, sposób na sprzedanie większej liczby serwerów, systemów
operacyjnych, połączeń, reklam lub akcji. Przez to cena programu nie odzwierciedla je-
go jakości.
32__________________________________________Część l » Podstawy PHP

W świecie serwerów idea open source oddziały wuj e jeszcze silniej. Takie produkty nie
tylko konkurują z komercyjnymi, ale wydaje się, że są poza konkurencją. Nie musisz
nam wierzyć! Jeżeli nie jesteś przekonany, więcej dowiesz się na witrynach:
http://www. opensource. org
http://www.fsf.org

Licencja PHP
Schemat licencjonowania typu open source i Free Software gwarantuje, że program jest
bezpłatny. Schemat ten jest najbardziej znany pod nazwą GPL (Gnu General Public Li-
cense) lub „copyleft". PHP był rozprowadzany na zasadach licencji GPL i własnej — do
wyboru przez każdego użytkownika. Ostatnio jednak całość programu rozprowadzana
jest na podstawie liberalnej licencji PHP 4, a Zend, jako osobny produkt, jest dystrybu-
owany na zasadach licencji Q Public license (klauzula jest stosowana, gdy Zend zosta-
nie oddzielony od PHP i ktoś będzie chciał go sprzedawać).

Dokładne objaśnienie warunków obu licencji można przeczytać pod adresami:


http://www.php. net/license
http://www. troll, no/ąpl/annotated. html

Większość użytkowników może bezpłatnie ściągnąć PHP, niektórzy zapłacą za niego,


jeżeli wchodzi w skład dystrybucji Linuksa, książki lub innego produktu. W takim wy-
padku pewnie będziesz miał mieszane uczucia na temat naszych zapewnień, że PHP nic
nie kosztuje. Możemy to wytłumaczyć: mimo że nie musisz płacić za większość pro-
gramów typu open source, zapłacisz za dostarczenie oprogramowania w bardziej wy-
godnej postaci — nagranej na dysk i dostarczonej do klienta. Możesz również zarabiać
na dostarczaniu usług biorąc ryzyko, jakiego nie podejmuje zespół programistów PHP.
Na przykład zagwarantujesz, że każda kopia będzie pozbawiona wirusów lub będzie
odpowiedniej jakości, podejmując ryzyko pozwania przez klientów, którzy dostali
uszkodzone CD-ROM-y.

Zwykle użytkownicy programów open source mogą wybrać optymalną wersję spośród
różnych propozycji: bezpłatnie i bez gwarancji, drogie, ale z świetnym serwisem lub
coś pomiędzy. Nie ma jeszcze zorganizowanego serwisu ani wsparcia dla PHP (przy-
puszczalnie będzie to jeden z dodatków do Zend).

PHP jest łatwy


PHP jest łatwy do nauczenia się, w porównaniu do innych produktów. W przeciwień-
stwie do Java Server Pages lub CGI opartego na języku C, PHP nie wymaga osiągnięcia
biegłości w podstawowych językach programowania do napisania prostych odwołań do
bazy danych. W przeciwieństwie do składni języka Perl, który jest żartobliwie nazywa-
ny językiem „tylko do zapisu", składnia PHP jest prosta do analizy i łatwa do zrozu-
mienia przez programistę. W przeciwieństwie do Active Server Pages, PHP nie zmusza
do uczenia się dwóch różnych języków!
Rozdział 1. » Dlaczego PHP?_______________________________________33

Istnieje wiele predefmiowanych, bardzo użytecznych, a specyficznych funkcji (jak


funkcje służące do łączenia się z bazami Oracle lub pobierania poczty z serwera IMAP).
Dostępnych jest również wiele gotowych skryptów, do których możesz zajrzeć w trak-
cie nauki PHP. Właściwie używanie PHP jest możliwe dzięki zmianom w istniejących
skryptach bez potrzeby pisania od początku. Oczywiście musisz znać podstawowe za-
sady, ale możesz uniknąć wielu frustrujących i zabierających czas pomyłek.

Musimy jednak ostrzec: „łatwy" może oznaczać co innego dla różnych użytkowników.
Dla niektórych projektantów WWW oznac/a środowisko graficzne, używające technik
„przenieś i upuść" lub „dostaniesz, co widzisz". Aby być naprawdę wydajnym, musisz
umieć ręcznie tworzyć strony HTML. Można oczywiście używać narzędzi WYSIWYG
do zaprojektowania witryny, sformatowania stron i dodania interakcji z użytkownikiem
przed dodaniem kodu PHP do źródeł. Są również sposoby, które opisujemy w rozdziale
3., pozwalające dodać funkcje PHP do ulubionego środowiska projektowania. Jednak
nie można w pełni korzystać z możliwości PHP bez patrzenia na kod źródłowy.

Większość zaawansowanych użytkowników PHP (włączając w to członków zespołu


tworzącego) to „zatwardziali koderzy". Twierdzą np., że ręcznie pisany kod jest piękny,
czysty i maksymalnie zgodny z przeglądarką i dlatego jest jedynie słuszny — nie wa-
hają się gwałtownie tego wyrażać. Społeczność PHP zapewnia pomoc i wymienia się
tajnikami języka przeważnie za pośrednictwem poczty elektronicznej, więc jeśli chcesz
z nich skorzystać, musisz umieć czytać czysty kod źródłowy. Niektórzy użytkownicy
narzędzi WYSIWYG czasami proszą członków listy dyskusyjnej o pomoc w rozpozna-
niu problemu, ale obejrzeli stronę zamiast kodu źródłowego.

Powtórzmy jeszcze raz, że PHP jest łatwy. Jest tylko nieco bardziej wymagający niż
HTML, ale chyba łatwiejszy niż JavaScript czy ASP i dużo mniej złożony koncepcyjnie
niż JSP.

PHP można wbudować


PHP można wbudować w HTML. Inaczej mówiąc, strony PHP są zwykłymi stronami
HTML, które „przełączają się" w tryb PHP tylko, gdy jest to konieczne. Popatrzmy na
przykład:
<HTML>
<HEAD>
<TITLE> Powitanie</TITLE>
</HEAD>
<BODY>
<P> Cześć,
<?php
/* Przełączamy się w tryb PHP.
Zamiast zmiennych, w następnych trzech liniach można łatwo
użyć danych z bazy danych*/
$firstname="Mata";
$lastname="Hari";
Stitle = "Ms.";
PRINT("$title Slastname");
// wracamy do HTML
?>.
Czy mogę nazywać cię <?php PRINT("$firstname"};?>?</P>
</BODY>
</HTML>
34__________________________________________Część l » Podstawy PHP

Gdy klient zażąda tej strony, serwer WWW przetworzy ją. Oznacza to, że przejrzy ją od
początku do końca, szukając sekcji PHP, które spróbuje przetworzyć. Analizator składni
pobierze wszystkie przypisane zmienne (oznaczone znakiem $) i spróbuje wstawić je do
późniejszych wyrażeń (w tym przypadku do funkcji print ( ) ) . Jeżeli wszystko się uda,
preprocesor zwróci normalną stronę WWW do przeglądarki klienta (rysunek 1.1).

Rysunek 1.1.
Wynik
przetworzonego PHP

Jeżeli zerkniesz do kodu źródła strony w przeglądarce, powinieneś zobaczyć coś takiego:
<HTML>
<HEAD>
<TITLE> Powitanie</TITLE>
</HEAD>
<BODY>
<P> Cześć, Ms. Hari. Czy mogę nazywać cię Mata?</P>
</BODY>
</HTML>

Gdybyś napisał to sam, byłoby identycznie. Proste!

Możliwość wbudowania PHP w HTML ma wiele pozytywnych konsekwencji:


* PHP może być szybko dodany do kodu utworzonego przez edytory WYSIWYG.
* PHP pomaga podzielić pracę pomiędzy projektantów i skryptowców.
* Nie ma potrzeby przepisywania HTML w języku programowania.
* PHP obniża koszty pracy i zwiększa wydajność.

Brak kompilacji
Prawdopodobnie najlepszą rzeczą w językach skryptowych jest to, że nie muszą być
kompilowane do postaci binarnej przed ich testowaniem i użyciem — po prostu napisz
i uruchom. PHP jest interpretowany (jak prawie wszystkie najnowsze języki programo-
wania), jednak Zend wykonuje czasami ukrytą prekompilację do zwiększenia szybkości
działania skryptów.
Rozdział 1. * Dlaczego PHP?_______________________________________35^

A może potrzebujesz kompilacji? Może być ona użyteczna, gdy chcesz rozprowadzać
kod w postaci binarnej tak, aby inni mogą go używać nie mając dostępu do źródła. Ze-
spół Zend pracuje nad kompilatorem, który na to pozwoli, usprawniając przy tym duże
i skomplikowane skrypty PHP.

PHP jest niezależny


PHP działa na różnych popularnych rodzajach Uniksa i Windows. Większość serwerów
HTTP na świecie działa na jednym z tych dwóch rodzajów systemów operacyjnych.

PHP jest zgodny z trzema wiodącymi serwerami WWW: Apache HTTP Server dla
Unix i Windows, Microsoft Internet Information Server oraz Netscape Enterprise Server
(znanym również jako iPlanet Server). Działa również z mniej znanymi serwerami, jak
np.: Alex Belits fhttpd, Microsoft Personal Web Server, AOLServer oraz Omnicentrix
Omniserver. Nie działa na platformie Macintosh. Podstawowe kombinacje OS i serwera
WWW są pokazane w tabeli 1.2.

Tabela 1.2.
Systemy operacyjne i serwery WWW dla PHP

Unix Windows

Rodzaj AIX, A/UX, BSDI, Digital UNIX/Tru64, Windows 95, Windows 98,
FreeBSD, HP-UX, IRIX, Linux, NetBSD, Windows NT, Windows 2000
OpenBSD, SCO UnixWare, Solaris, SunOS,
Ultrix, Xenix i inne
Serwer WWW Apache, fhttpd, Netscape US, PWS, Netscape, Apache, Omni

Mimo że PHP nie działa jeszcze na Macintoshu, BeOS lub innych platformach, możesz
projektować w tych systemach za pomocą ulubionego narzędzia, a następnie wrzucić
skrypt PHP na serwer Unix lub Windows. Przedyskutujemy dokładnie ten proces
w rozdziale 3.

PHP nie bazuje na znacznikach


PHP jest prawdziwym językiem programowania. W przeciwieństwie do niego ColdFusion
jest zbiorem predefiniowanych znaczników podobnych do HTML. W PHP możesz zdefi-
niować dowolną funkcję, wpisując jej nazwę i definicję. W ColdFusion musisz użyć
znaczników zdefiniowanych przez innych lub przebić się przez Custom Tag Extension.

Dowcipny członek społeczności PHP powiedział: „W ColdFusion łatwe rzeczy robi się
łatwo, średnio trudne są niemożliwe". Każdy programista może to potwierdzić; gdy po-
znasz siłę nawiasów i pętli, nigdy nie wrócisz do znaczników.
36__________________________________________Część l » Podstawy PHP

PHP jest stabilny


Słowo „stabilny" oznacza w tym kontekście, że:
1. Serwera nie trzeba często restartować.
2. Wersje programu nie zmieniają się radykalnie i są ze sobą zgodne.

W przypadku PHP zachodzą oba te przypadki.

Serwer Apache jest jednym z najbardziej stabilnych serwerów WWW, działających


długo bez przerw. Mimo że nie jest najszybszy, ani też najłatwiejszy do administrowa-
nia, po ustawieniu praktycznie nie przestaje działać. Nie wymaga również ponownego
uruchomienia po każdej zmianie parametrów (przynajmniej wersja Uniksa). PHP korzy-
sta z tej niezawodności, dodatkowo jego konstrukcja jest solidna i wydajna. W dwuipół-
miesięcznym teście przeprowadzonym w październiku 1999 r. przez laboratoria Network
Computing serwer Apache razem z PHP z łatwością pokonał US i Visual Studio oraz
Netscape Enterprise Server i Java w konkurencji stabilność środowiska.

PHP jest również stabilny w sensie stabilności funkcji. Zespół projektantów jest bar-
dziej zainteresowany jasną wizją projektu, a nie spełnianiem nieprzemyślanych za-
chcianek przypadkowych klientów. Większość pracy koncentruje się na ulepszeniach,
na przykład przyspieszeniu analizatora czy lepszej obsłudze sesji. Bardzo niewiele
funkcji zostało usuniętych przy zmianach wersji PHP.

PHP jest szybki


PHP jest bardzo szybki w działaniu, szczególnie gdy jest skompilowany jako moduł
serwera Apache działającego na Uniksie.

W chwili obecnej PHP 4 jest dużo szybszy niż jakikolwiek skrypt CGI. Niestety praw-
dziwy jest żart, że CGI to skrót od „Can't Go Instantly" (nie może działać bez przerwy).
Mimo że wiele ze skryptów CGI jest napisanych w C, jednym z najszybszych języków
programowania, szybkość jest ograniczana obsługą każdego żądania przez nowy proces
uruchamiany przez serwer http. Czas oraz zasoby potrzebne na utworzenie i zakończe-
nie procesu są znaczne, a system może ograniczać liczbę procesów działających jedno-
cześnie. Inne skrypty CGI mogą być napisane w Perl lub Tcl i dlatego stosunkowo
powolne. Większość witryn WWW odchodzi od stosowania skryptów CGI, aby polep-
szyć wydajność i zwiększyć bezpieczeństwo.

Mimo że PHP traci nieco na wydajności, ponieważ jest interpretowany, a nie kompilowa-
ny, równoważy to działaniem jako moduł serwera. Ponieważ nie ma komunikacji pomię-
dzy osobnymi aplikacjami (jak w ColdFusion), żądania są przetwarzane dużo szybciej.

Pomimo braku formalnych obszernych testów wydajnościowych, które porównywałyby


dwóch największych konkurentów, wiele eksperymentów i niewielkich testów pokazu-
je, że w większości aplikacji PHP jest co najmniej tak szybki jak ASP (więcej informa-
cji można znaleźć na serwerze Zend.com).
Rozdział 1. » Dlaczego PHP?_______________________________________37^

PHP jest otwarty


Zajmowaliśmy się już korzyściami finansowymi płynącymi z używania oprogramowa-
nia open source. Programy tego typu udostępniane są z pełnym kodem źródłowym.

Wersja PHP dla Uniksa jest dostarczana tylko jako kod źródłowy, dlatego zespół progra-
mistów PHP nie musi rozprowadzać wersji skompilowanej dla każdego rodzaju Uniksa.
Z początku nowi użytkownicy (szczególnie ci, którzy terminują w Uniksie) stwierdzają,
że kod źródłowy jest równie użyteczny co piąte koło u wozu; większość z nich woli mi-
łe i wygodne pliki rpm. Istniejąjednak idealistyczne i pragmatyczne powody zamiesz-
czania katalogów pełnych plików . c i .h.

Pierwszą z zalet takiego rozwiązania jest to, że możesz skompilować instalację PHP
zawierającą tylko te elementy, których naprawdę potrzebujesz. Podejście to pozwala na
zwiększenie szybkości działania i poziomu bezpieczeństwa. Możesz dodać połączenia
tylko do tych baz danych, których będziesz używał. Możesz kompilować PHP na nowo
tak często, jak tylko zechcesz: gdy dostaniesz uaktualnienie zabezpieczeń do serwera
Apache lub stwierdzisz, że potrzebujesz obsługi XML.

To, co odróżnia oprogramowanie open source od konkurentów, to nie tylko cena, ale
także kontrola. Większość oprogramowania użytkowego jest obecnie rozdawana pod
różnymi warunkami. Dokładne przeczytanie licencji odkryje ograniczenia sposobu, w ja-
ki program może być używany. Być może dopuszczalne będzie korzystanie z niego tyl-
ko w domu, a nie w biurze. Możliwe, że będziesz mógł zainstalować go w twoim lapto-
pie, ale złamiesz umowę, wykorzystując program do celów zawodowych. Zwykle można
używać program w dowolny sposób, ale nie można obejrzeć kodu źródłowego, a tym
bardziej go zmieniać. Istnieją nawet „licencje grupowe" nakładające na użytkowników
warunek oddawania właścicielowi programu swoich pomysłów na ulepszenie, chociaż
używanie jest płatne!

Nawet nie myśl o łamaniu licencji oprogramowania. Szczególnie dziś,


gdy bezpłatne oprogramowanie święci triumfy, nie ma powodu, aby
łamać prawo.

W tabeli 1.3 zestawiliśmy różne sposoby kontroli źródeł i opłat na dzisiejszym rynku
oprogramowania.

Tabela 1.3.
Różne metody opłat i kontroli źródel

Struktura opłat Źródła


Źródła prywatne Źródła otwarte
kontrolowane

Opłaty od wszystkich użytkowników Alaire ColdFusion


Opłaty od niektórych użytkowników Corel WorPerfect Sun Java MySQL
Brak opłat Microsoft IE Sun StarOffice Oprogramowanie GPL
38_______________________________________Część l » Podstawy PHP

Prawdziwe programy open source, jak PHP, mogą być wykorzystywane do wszelkich
zastosowań. Znamiennym dowodem tego jest możliwość wprowadzania zmian i upo-
wszechnianie ich razem z oryginalnym programem. Skrajne przypadki określane są
mianem „rozwidlania kodu".

Oznacza to, że jeżeli stworzysz zmiany niezgodne z propozycjami zespołu programi-


stów PHP, możesz zabrać cały kod napisany do tej pory i wykorzystać go jako podsta-
wę własnego produktu. Nie musisz nazywać go PHP, ale do dokumentacji musisz
dołączyć informacje o autorach.

Użytkownicy rozpoczynający przygodę z modelem open source powinni zdawać sobie


sprawę, że prawo takie przynależy również twórcom. W każdej chwili Rasmus wraz
z kolegami może opuścić społeczność i rozpocząć pracę nad komercyjnym produktem
na bazie PHP. Oczywiście kod napisany do tej chwili będzie nadal dostępny i każdy
(a powinna to być duża grupa programistów) może podjąć się kontynuacji projektu.

Sytuacja taka przywodzi na myśl inną, często zapominaną zaletę oprogramowania open
source: możesz być pewien, że oprogramowanie będzie w obiegu przez ładnych kilka
lat. W dzisiejszych czasach trudno jest wybrać produkt utrzymujący się na szczycie
przez dłuższy czas. Wielbiciele OS/2, Amigi, NeXT, Newton, Firefly, Netscape znają
uczucie osamotnienia, kiedy firma decyduje się zaprzestać wspierania technologii lub
sprzedaje ją innej firmie. My również zostaliśmy uwiedzeni i porzuceni przez inny (te-
raz przestarzały) język skryptowy. Model open source zmniejsza możliwość porzucenia
projektu i pozwala na bardziej realistyczne, długoterminowe planowanie.

PHP dobrze współpracuje z innymi produktami


PHP ułatwia komunikację z innymi programami i protokołami. Zespół PHP próbuje za-
pewnić możliwie dużą elastyczność możliwie dużej grupie użytkowników.

Łącza do baz danych są silną stroną PHP. Zapewniają funkcje obsługi około 15 popular-
nych baz danych oraz ODBC. PHP obsługuje dodatkowo kilka popularnych protokołów,
np.: POP3, IMAP oraz LDAP, a także języka Java oraz rozproszonej architektury obiek-
towej (COM i CORBA), co umożliwia tworzenie programów o wielowarstwowej archi-
tekturze

Większość produktów, których nie obsługuje PHP, ma zamkniętą architekturę. Apple


Computer i Microsoft nie zgadzają się na współpracę w projektach typu open source.
Potencjalni użytkownicy, którzy narzekają na listach dyskusyjnych na brak obsługi
komputerów Mac lub SQL Server, nie są po prostu poinformowani, gdzie leży źródło
problemów.

Popularność PHP rośnie


PHP staje się jednym z najbardziej popularnych narzędzi do tworzenia aplikacji dwu-
warstwowych (WWW i dane). Rysunek 1.2 pokazuje rozwój jego popularności od po-
czątku 1999 r.
Rozdział 1. » Dlaczego PHP?_______________________________________39

Rysunek 1.2.
Pomiar popularności
PHP (program
Netcraft)

Jak można zauważyć, pomiędzy październikiem 1999 a październikiem 2000 zanotowano


wzrost liczby domen o 400%. Witryny WWW stają się wszechobecne i wiele z nich
wykracza poza zwykłe statyczne strony HTML. Dlatego spodziewamy się szybkiego
wzrostu liczby użytkowników PHP.

Mimo że trudno jest uzyskać odpowiednie wykresy, wydaje się, że PHP stoi na mocnej
pozycji w porównaniu do podobnych produktów. Technologia ASP firmy Microsoft jest
używana w około 12% serwerów WWW, ColdFusion około 4%, natomiast z PHP ko-
rzysta 12% serwerów.

Active Server Pages i ColdFusion są bardziej widoczne, ponieważ są wybierane przez


duże witryny handlu elektronicznego. Jednak ogromna większość witryn WWW ma
charakter informacyjny, a nie handlowy i z tego powodu nie zwraca w widoczny sposób
poniesionych nakładów finansowych. PHP ma wyraźną przewagę w tej kategorii.

PHP nie jest niczyją własnością


Historia przemyski komputerowego jest kroniką standardów firmowych: prób ich usta-
nowienia, starć pomiędzy nimi, ich wad i zalet oraz sposobów, w jaki zostały zastąpione
przez inne standardy.

Od kilku lat Internet przekonuje o wygodzie bezpłatnych, zestandaryzowanych i nieza-


leżnych rozwiązań. E-mail działa tak dobrze, ponieważ korzysta z jasnego i stałego
standardu, który musi być zachowany na każdej platformie. Nowe rozwiązania, które
łamią standardy (przykładowo e-mail oparty o HTML), są uważane za odchylenie, a ich
użytkownicy muszą zmagać się z ograniczeniami nieprzemyślanej technologii.
^0__________________________________________Część l » Podstawy PHP

W chwili obecnej projektanci oprogramowania zawieszeni są pomiędzy zmiennymi


technologiami firmowymi a otwartymi standardami. Firmy chcą mieć pewność, że mo-
gą utrzymać obrót, używając otwartych standardów.

PHP jest bardzo elastyczny, ponieważ jest „afirmowy". Nie jest związany z żadnym
serwerem ani systemem operacyjnym, w przeciwieństwie do Active Server Pages. Nie
jest też związany z żadnym przenośnym standardem lub warstwą pośrednią, jak Java
Server Pages lub ColdFusion, ani też z przeglądarką, językiem programowania lub bazą
danych. PHP narzuca też warunku pracy z innym oprogramowaniem.

Społeczność PHP
PHP jest stworzony i wspierany przez światową społeczność użytkowników. Jednak
„niektóre zwierzęta (główni programiści) są równiejsze od innych" — trudno się o to
spierać, ponieważ włożyli najwięcej pracy, mieli najlepsze pomysły oraz utrzymywali
kontakty z największą liczbą innych użytkowników.

Dla nowych użytkowników główną korzyścią jest bezpłatne wsparcie techniczne, bez
granic i pokrętnych odpowiedzi. Użytkownicy, na listach dyskusyjnych przez 24 godzi-
ny na dobę i 7 dni w tygodniu, odpowiadają na pytania, pomagaj ą usunąć błędy i wysłu-
chuj ą narzekań. Członkowie społeczności mogą odesłać do podręcznika, zadać pytanie
na właściwej liście dyskusyjnej lub powstrzymać twoje jęki — nigdy nie zaproponują
sformatowania dysku C i nie zażądają pieniędzy za usługę. Mogą zajrzeć do twojego
kodu źródłowego i wskazać źródło błędu lub nawet pomóc zaprojektować aplikację od
podstaw.

Gdy lepiej poznasz PHP, będziesz mógł się odwdzięczyć. Szukanie błędów, pomoc in-
nym, wysyłanie skryptów do publicznych archiwów, tworzenie dokumentacji lub pisa-
nie kodu w C i C++, oto kilka sposobów na spłacenie długu.

Podsumowanie
Mimo że PHP posiada wiele zalet, nie jest panaceum na wszystkie problemy programi-
stów WWW. Został stworzony przez programistów dla programistów i jest wspierany
przez olbrzymią społeczność. Jego główne zalety to: lekkość, siła, pewność i łatwość
użycia. Zapewnia możliwość podłączenia do olbrzymiej ilości różnych typów serwerów
usługowych. W dodatku za darmo.
Rozdział 2.
Skrypty wykonywane
na serwerze WWW
W tym rozdziale:
* Statyczne i dynamiczne strony WWW
4 Skrypty wykonywane na kliencie i na serwerze
* Wprowadzenie do skryptów wykonywanych na serwerze

Rozdział ten zawiera informacje o skryptach wykonywanych na serwerze i ich związ-


kach z statycznymi stronami HTML oraz głównymi technologiami używanymi po stro-
nie klienta. Przekazuje wiedzę na temat oferowanych przez PHP funkcji i sposobu,
w jaki współpracuje z kodem klienta.

Statyczny HTML
Podstawowym typem stron WWW są tekstowe strony napisane od podstaw w kodzie
HTML. Przykład takiej strony jest pokazany na rysunku 2.1.

Źródło strony z rysunku 2.1.


<HTML>
<HEAD>
KTITLE>Książki o sprzęcie komputerowym</TITLE>
<META NAME-KEYWORDS CONTENT="komputer,
sprzęt, chip, business, ksia.żka">
</HEAD>
<BODY>
<CENTERXH3>Książki o przemyśle sprzętu komputerowego
</H3X/CENTER>
<H5>Apple Computer</H5>
<UL>
<LIXA H R E F = " b o o k l . h t m l " > A p p l e < / A > : C a r l t o n ,
Jim ( 1 9 9 7 )
<LIXA HREF="book2 .html">Insanely G r e a t < / A > :
Levy, Steveri (1993)
42__________________________________________Część l » Podstawy PHP

Rysunek 2.1.
Przykład
statycznego HTML

<LIXA H R E F = " b o o k 3 . h t m l " > O d y s s e y : Pepsi to


Apple</A>: Sculley, John (1997)
<LIXA HREF="book4.html">Steve Jobs and the
NeXT Big Thing</A>: Stross, Randall (1993)
</UL>
<H5>Dell Computer</H5>
<UL>
<LIXA HREF="book5.html">Direct from D e l l < / A >
: Dell, Michael (1999)
</UL>
<H5>Intel</H5>
<UL>
<LI><A HREF="book6.html">Only the Paranoid Survive</A>:
Grove, Andrew S. (1996)
<LIXA H R E F = " b o o k 7 . h t m l " > I n s i d e I n t e K / A > : Jacksvn, T i m ( 1 9 9 7 )
</UL>
<H5>Sun Microsystems</H5>
<UL>
<LIXA H R E F = " b o o k 8 . h t m l " > H i g h Noon</A>: Southwick, Karen ( 1 9 9 9 )
</UL>
</BODY>
</HTML>

Gdy komputer klienta zażąda tej strony z serwera poprzez Internet lub Intranet w spo-
sób pokazany na rysunku 2.2, serwer po prostu wyśle tekst strony do klienta.

Gdy dane wracają do komputera klienta, przeglądarka rysuje stronę interpretując kod, bio-
rąc pod uwagę ustawienia użytkownika, rozmiar monitora i inne czynniki. Zawartość pli-
ku HTML na serwerze jest dokładnie taka sama, jak źródło strony na komputerze klienta.

Czysty, statyczny kod HTML posiada następujące zalety:


* Każda przeglądarka wyświetli go poprawnie.
* Wiele urządzeń może go poprawnie wyświetlić.
* Każde żądanie jest wykonywane szybko, przy użyciu niewielkich zasobów.
Rozdział 2. » Skrypty wykonywane na serwerze WWW_______________________43

Rysunek 2.2.
Proste
żądanie HTTP

* HTML jest łatwy do nauczenia lub automatycznego wygenerowania.


* Można szybko wprowadzić małe zmiany na pojedynczych stronach.

Oczywiście statyczny kod HTML posiada również ograniczenia:


* Trudno kontrolować układ strony.
+ Nie skaluje się prawidłowo.
+ Nie jest interaktywny.
4 Utrudnia wprowadzanie znaczących metadanych.
* Nie radzi sobie z szybko zmieniającymi się danymi i personalizacją stron.
* Nie jest zbyt atrakcyjny.

Z tych powodów statyczny HTML staje się wyznacznikiem amatorszczyzny lub rygo-
rów ideologicznych.

Kilka technologii radzi sobie z tymi ograniczeniami. Należą do nich JavaScript, Casca-
ding Style Sheet i aplety Java na końcówkach klienta oraz skrypty i połączenie z bazą
danych po stronie serwera. Pojawiły się też XML i XSL, technologie, które występują
równieżjako części innych specyfikacji (XHTML, XSLT, Xpath, ICE itd.).

Poświęć trochę czasu na zrozumienie, jaki zestaw funkcji każda z nich zawiera i która
może przydać się przy tworzeniu twojej witryny. Podstawowe pytanie, które powinie-
neś sobie zadać przy tworzeniu konkretnego zadania, to: gdzie następuje przetwarzanie,
na kliencie czy na serwerze.
44___________________________________________Część l » Podstawy PHP

Technologie
wykonywane po stronie klienta
Najwięcej rozszerzeń HTML wykorzystuje się po stronie klienta. Zawierają one: rozsze-
rzenia formatowania, jak Cascading Style Sheet i Dynamie HTML, języki skryptów wy-
konywanych na kliencie i aplety Java. Obsługa tych technologii może być wbudowana
w przeglądarkę (zadania wykonywane przez te technologie opisane są w tabeli 2.1).

Tabela 2.1.
Rozszerzenia HTML po stronie klienta

Technologia Główne zastosowanie Przykład efektów

Cascading Style Sheet, Formatowanie stron: Nakładanie, różne kolory


Dynamie HTML kontrolowanie rozmiaru, koloru, i rozmiar, czcionki, warstwy
położenia, układu, czasu
pojawienia się elementów
Skrypty wykonywane na kliencie Obsługa zdarzeń: kontrola Połączenia, które zmieniają kolor
(JavaScript, VBScript) skutków definiowanych zdarzeń po najechaniu na nie myszą,
kalkulator kredytowy
Aplety Java Dostarczanie małych oddzielnych Obracające się logo, krzyżówka,
aplikacji puzzle

Strona na rysunku 2.3 ma tę samą zawartość co strona na rysunku 2. l.

Rysunek 2.3.
Przy Mad HTML
ze skryptami
wykonywanymi
po stronie klienta
Rozdział 2. » Skrypty wykonywane na serwerze WWW________________________45

Co oznacza „dynamiczny"
Często zaznacza się podział pomiędzy „statycznymi" i „dynamicznymi" stro-
nami HTML — „dynamiczny" oznacza prawie wszystko, co wykracza ponad
czysty HTML. Termin ten jest używany do opisu funkcji klienta i serwera.
W przypadku klienta mogą to być prezentacje multimedialne, przewijające
się nagłówki, automatycznie uaktualniające się strony lub elementy, które
pojawiają się i znikają. Dla serwera termin ten oznacza, że zawartość stron
jest tworzona na bieżąco.

Jak można zauważyć w źródłach, w przykładzie tym dodano arkusze stylów i skrypty
wykonywane na kliencie oraz nieco bardziej skomplikowany kod HTML.
<HTML>
<HEAD>
<TITLE>Przewodnik TechBiz: sprzęt</TITLE>
<STYLE TYPE="text/css">
< ! --
BODY (color: black; font family: verdana; font size: 10 pt}
HI (margin-top: 10; color: white; font-family: arial; font-size: 12 pt}
H2 (margin-bottom: -10; color: black; font-family:
verdana; font-size: 18 pt}
A:link (color: #000080; text-decoration: none}
.roll ( )
A.roll:hover (color: #008080}
-->
</STYLE>
<SCRIPT LANGUAGE="JavaScript">
function ListVisit(form, i)
f
// pobranie adresu URL
var site = form.elements [i] . selectedlndex;
// jeżeli to nie jest pierwsza opcja, przejdź do niej
iflsite >= 1)
(
top.location = form.elements[i].options[site].value;
(
// and then reselect the null (it functions as a label)
form.elements[i].selectedlndex = 0;
(
//-->
</SCRIPT>
</HEAD>

<BODY>
<TABLE BORDER=0 CELLPADDING=0 WIDTH=100%>
<TR>
<TD B G G O L O R = " # F O F 8 F F " ALIGN=CENTER VALIGN=TOP W I D T H = 1 7 % >
<TABLE C E L L P A D D I N G = 5 W I D T H = 1 0 0 % >
<TR ALIGN=CENTER>
<TD B G C O L O R = " D 0 0 0 0 8 0 " >
<Hl>Przewodnik ksiazkowy<BR>TechBiz</Hl>
</TDX/TRX/TABLE>
<BR>
<A H R E F = " i n d e x . p h p " > < B > P O W R O T < / B X / A > < B R X B R >
<BR>
<FORM action="">
<SELECT NAME="industries" onChange="LisLVisit(this.form, 0)">
< O P T I O N > I n n e branże
<OPTION VALUE="software.html">Oprogramowanie
<OPTION VALUE="biotech.html">Biotechnologie
<OPTION VALUE="aerospace.html">Lotnictwo
<OPTION VALUE="telephone.html">Telefonia
</SELECTX/FORMXBR>
46__________________________________________Część l » Podstawy PHP

</TD>
<TD BGCOLOR="#FFFFFF" ALIGN=LEFT VAL1GN=TOP WIDTH-83%>
<CENTERXH3>Ksiażki o przemyśle sprzętu komputerowego
</H3X/CENTER>
<H5>Apple Computer</H5>
<UL>
<LIXA H R E F = " b o o k l . h t m l " > A p p l e < / A > : Carlton,
Jim ( 1 9 9 7 )
<LIXA HREF="book2.html">Insanely Great</A>:
Levy, Steveri (1993)
<LIXA HREF="book3.html">Odyssey: Pepsi to
Apple</A>: Sculley, John (1997)
<LIXA HREF="book4.html">Steve Jobs and the
NeXT Big Thing</A>: Stress, Randall (1993)
</UL>
<H5>Dell Computer</H5>
<UL>
<LIXA H R E F = " b o o k 5 . h t m l " > D i r e c t from Dell</A>
: Dell, Michael (1999)
</UL>
<H5>InteK/H5>
<UL>
<LIXA H R E F = " b o o k 6 . h t m l " > O n l y the Paranoid Survive</A>:
Grove, Andrew S. (1996)
<LIXA H R E F = " b o o k 7 . h t m l " > I n s i d e I n t e l < / A > : Jacksvn, Tim ( 1 9 9 7 )
</UL>
<H5>Sun Microsystems</H5>
<UL>
<LIXA H R E F = " b o o k 8 . h t m l " > H i g h N o o n < / A > : Southwick, Karen ( 1 9 9 9 )
</UL>
</TD>
</TRX/TABLE>
</BODY>
</HTML>

Niestety, najlepszą cechą technologii działających po stronie klienta jest również naj-
gorszą: są one całkowicie zależne od przeglądarki. Istnieją spore różnice w możliwo-
ściach przeglądarek, a nawet pomiędzy różnymi wersjami tej samej przeglądarki. Tak
zwany Dynamie HTML to dwie różne koncepcje skryptów, którym zdarzyło się mieć tę
samą nazwę. Można, niestety, skonfigurować przeglądarkę w sposób sprawiający kło-
poty: niektórzy wyłączają JavaScript ze względów bezpieczeństwa, co powoduje nie-
możność obejrzenia stron, które używają JavaScript do nawigacji (umyślnie zrobiliśmy
to w przykładzie powyżej).
Nie ulepsza się też przeglądarek z powodu kosztów lub kłopotów technicznych. Doświad-
czeni projektanci WWW powinni również pomyśleć o przeglądarkach tekstowych, do-
stępności i globalnym zasięgu. Nieprzypadkowo duże witryny, o masowej oglądalności,
starają się trafić do możliwie dużej widowni. Chociaż Yahoo! i Amazon nie używają ar-
kuszy stylów i JavaScript i to po ponad trzech latach od wprowadzenia tych standardów.
Mimo nalegań World Wide Web Consortium wiele witryn uparcie trzyma się znaczników
FONT i atrybutu BGCOLOR jako jedynego sposobu dotarcia do klientów używających
AOL 3.0 w pięcioletnich komputerach Macintosh z 13-calowymi ekranami. Opór użyt-
kowników przed uaktualnieniem jest przekleństwem projektantów rozwiązań wykorzy-
stujących technologie klienckie. Ironicznie można podsumować to w sposób następujący:
po pięciu latach gwałtownego rozwoju Sieci jedyną rzeczą, której projektant może być
pewien jest fakt, że klient na ekranie monitora zobaczy czysty tekstowy HTML (lub raczej
podzbiór HTML, który jest powszechnie obsługiwany i przetrwał próbę czasu).
Rozdział 2. » Skrypty wykonywane na serwerze WWW_______________________47_

Aplety Java
Aplety Java, znane również pod nazwą „Java na stronie klienta", są uważane
za mniej zależne od przeglądarki niż inne technologie klienckie. Jak sugeruje
nazwa, są to małe aplikacje, które są przesyłane przez Internet. Jednak za-
miast współdziałać bezpośrednio z systemem operacyjnym, jak aplikacje
napisane w innych językach programowania, aplety Java działają na war-
stwie pośredniczącej, zwanej maszyną wirtualną Javy (JVM). Maszyna wirtu-
alna może być rozumiana jako system operacyjny działający na prawdziwym
systemie operacyjnym maszyny. Najnowsze przeglądarki posiadają maszynę
wirtualną Javy, ale także można ściągnąć ją oddzielnie. Taki podział zadań
pozwala apletom używać możliwości przeglądarki bez ograniczania przez jej
relatywnie ubogi zakres funkcji.
Aplety ucierpiały z powodu opinii o ich niewielkiej przydatności, ponieważ na
początku były używane do tworzenia trywialnych „tańczących literek": logo,
które wygląda jak zrobione z żelatyny, przewijane nagłówki, skaczące połą-
czenia i inne przyprawiające o ból głowy błahostki.

Technologie klienckie nie potrafią zrobić niczego, co wymaga podłączenia do bazowe-


go serwera. JavaScript nie może utworzyć indywidualnych list rozwijalnych na podsta-
wie danych użytkownika, zapisanych w bazie danych — gdy wymagana jest zmiana
w liście, programista musi zrobić to ręcznie (istnieje wersja JavaScript do wykonywania
na serwerze, ale nie jest zbyt rozpowszechniona). Jest to nisza, którą mogą wypełnić
skrypty wykonywane na serwerze.

Podsumowując: cokolwiek, co można zrobić z wyglądem lub zdarzeniami, dzieje się


w komputerze klienta.

Skrypty wykonywane na serwerze


Rysunek 2.4 zawiera schemat przepływu danych przy użyciu skryptów na serwerze.
Skrypty wykonywane na komputerze klienta stanowią powabną, przyciągającą wzrok
część programowania dla WWW. W przeciwieństwie do nich skrypty dla serwera są
niewidoczne dla użytkownika.
Skrypty dla serwera WWW są najczęściej wykorzystywane do połączenia witryn WWW
z bazowymi serwerami, np. serwerami baz danych. Pozwala to na dwukierunkową ko-
munikację:
* Serwer do klienta: strony WWW mogą być tworzone z wyniku działania ba-
zowego serwera.
* Klient do serwera: strony są tworzone zgodnie z danymi wprowadzonymi przez
użytkownika.

Najczęstszym przykładem interakcji klienta z serwerem są formularze i listy rozwijalne,


które są dynamicznie tworzone przez serwer.
48__________________________________________Część l » Podstawy PHP

Rysunek 2.4.
Zadania na serwerze

Skrypty dla serwera składają się z dwóch głównych elementów: języka skryptowego i ma-
szyny skryptowej (która może, ale nie musi być wbudowana w serwer WWW). Maszyna
skryptowa analizuje i interpretuje strony zapisane w znanym jej języku. Często obie części
są stworzone przez tę samą firmę lub zespół i mogą być używane tylko razem. Przykła-
dem tego jest zarówno ColdFusion, jak i PHP 3. Istnieją jednak wyjątki od tej reguły. Java
Server Pages są napisane w zwykłym języku programowania, a nie w specjalnym języku
skryptowym. Stworzono kilka maszyn skryptowych wykorzystujących ten język, np.
Allaire JRun, Apache JServ. Teoretycznie ASP pozwala na użycie dowolnego języka
skryptów i kilku z maszyn skryptowych zgodnych z ActiveX (jednak w praktyce proble-
matyczne jest użycie czegoś innego niż kombinacja NT, US, VBScript, JScript). PHP jest
również dwuczęściową technologią skryptową, ponieważ maszyna skryptowa (Zend) jest
oddzielona od języka programowania.

Rysunek 2.5 pokazuje prosty przykład skryptów dla serwera. Strona tworzona jest na
bieżąco na podstawie bazy danych i źródła interpretowanego przez serwer, wynik jest
przesyłany do klienta. W przykładzie poniżej załączyliśmy również odwołania do bazy
danych (nie zajmowaliśmy się nimi do tej pory) i pozostawiliśmy dołączane pliki, po-
nieważ zadaniem tego przykładu jest przedstawienie wyniku działania PHP, a nie goto-
wego kodu.

Tak wygląda źródło na serwerze:


<HTML>
<HEAD>
<TITLE>Przykład: Przewodnik po książkach TechBiz. Dane z serwera</TITLE>
<?php include("tbbg-style.ess"); ?>
<?php include("javascript.inc"); ?>
</HEAD>
Rozdział 2. » Skrypty wykonywane na serwerze WWW_______________________49

Rysunek 2.5.
Przyktad skryptów
dla serwera

<BODY>
<?php include{"thhg-navbar.txt"); ?>
<TD BGCOLOR="(tFFFFFF" ALIGN=LEFT VALIGN=TOP WIDTH=83%>
<TABLE CELLPADDING=5 WIDTH=100%?<TRXTD ALIGN=LEFT
VALIGN=MIDDLE>
<H2>Książki o <?php print("$co") ; //przekazany poprzez URL
?></H2XBR></TDX/TR>
<TR><TD WIDTH=50% ALIGN=LEFT>
<?php
$dbh = mysql_connect('localhost', 'Joyce') or
die("Błąd otwarcia bazy danych");
mysql_select_db("techbizbookguide") or die("Błąd otwarcia bazy danych");
S fquery = "SELECT Blurb FROM Company WHERE CompanyName = '$co'";
Sqresult = mysql_query($query) or die(mysql_error());
$blurb = mysql_fetch_array($qresult} or die(mysql_error(});
print("$blurb[0]");
?>
</TDX/TR>
<TRXTD ALIGN=LEFT>
<TABLE BORDER=1 CELLPADDING=3>
<?php
Squery2 = "SELECT ID, Title, AuthorFirst, AuthorLast FROM
bookinfo WHERE CompanyName='$co' ORDER BY AuthorLast";
$qresult2 = mysql_query{$query2} or die(mysql_error());
while($titlelist = mysql_fetch_array($qresult2))
(
$bookID = Stitlelist[0];
$title = Stitlelist[1];
$authorfirst = $titlelist[2];
Sauthorlast = $titlelist[3];
print ("<TRXTDXA HREF=\"book . php?bn=$bookID\" class=\"roll\">
Stitle</Ax/TDXTD>Sauthorfirst
Sauthorlast</TD>") ;
)
?>
</TRX/TABLE>
</TDX/TRX/TABLE>
</TDX/TRX/TABLE>
<BODYX/HTML>
_50__________________________________________Część l » Podstawy PHP

Tak wygląda źródło strony po dostarczeniu jej do klienta:


<HTML>
<HEAD>
<TITLE>Przykład: Przewodnik po książkach TechBiz. Dane z serwera</TITLE>
<SCRIPT LANGUAGE="JavaScript">
<!--
function ListVisit(form, i)
(
// Pobierz URL z opcji
var site = form.elements[i].selectedlndex;
// Jeżeli nie jest to pierwsza opcja przejdź tam
if( site >= l )
f
top.location = form.elements[i].options[site].value;
}
// Powtórnie wybierz null (Działa jak etykieta)
form.elements[i].selectedlndex = 0;
)
//-->
</SCRIPT>
</HEAD>
<BODY>
<TABLE BORDER=0 CELLPADDING=0 WIDTH=100*>
<TR>
<TD BGCOLOR="(łFOF8FF" ALIGN=CENTER VALIGN=TOP WIDTH=17%>
<TABLE CELLPADDING=5 WIDTH=100%>
<TR ALIGN=CENTER>
<TD BGCOLOR="#000080">
<Hl>Przewodnik książkowy<BR>TechBiz</Hl>
</TDX/TRX/TABLE>
<BR>
<A HREF="index.php"><B>Powrót</BX/A><BRXBR>
<B>Więcej</BXBR>
<B>firm</B><BR>
<A HREF="company.php?co=apple" class = "roll"><B>Apple</BX/A>
<BR>
<A HREF="company.php?co=dell" class="roll"XB>Dell</BX/A>
<BR>
<A HREF="company.php?co-intel" class="roll"xB>Intel</Bx/A>
<BR>
<A HREF="company.php?co=sun" class="roll "><B>Sun</Bx/A>
<BRXBR>
<FORM action="">
Oelect onChange="ListVisit ( t h i s , form, 0) ">
<OPTION>Przeglądaj według..
<OPTION VALUE="author.php">Autora
<OPTION VALUE="people.php">Ludzi
<OPTION VALUE="themes.php">Tematu
<OPTION VALUE="role.php">Roli
<OPTION VALUE="size.php">Wielkości firmy
</SELECTX/FORMXBR>
</TD>
<TD BGCOLOR="#FFFFFF" ALIGN=LEFT VALIGN=TOP W I D T H - 8 3 % >
<TABLE W I D T H = 1 0 0 % CELLPADDING=15XTRXTD ALIGN=LEFT V A L I G N = M I D D L F >
<H2>Książki o A p p l e < / H 2 X B R X / I D X / T R >
<TRXTD WIDTH=50% ALIGN=LEFT>Założone w roku 1976 przez dwóch
hobbystów, Steve Jobs i Steve W o z n i a k , w garażu. Pomogli
rozkręcić przemysł komputerów osobistych przy pomocy ich
stylowych, p r z y j a z n y c h komputerów. < / T D x / T R >
<TRXTD A L I G N = L E F T >
<TABLE BORDER=1 CELLPADDING=3>
<TR><TDXA HREF= "book.php?book=l" class="roll">Apple</Ax/TD>
<TD>Jim Carleton</TD>
<TRXTDXA HREF="book.php?book=2" class="roll">Insanely G r e a t < / A X / T D >
<TD>Steven Levy</TD>
<TRXTDXA HREF="book.php?book=3" class="roll">0dyssey:
Pepsi to Apple</AX/TDXTD>John Sculley</TD>
<TRXTDXA HREF="book.php?book=4" class = "roll">Steve Jobs
and the NeXT Big Thing</AX/TDxTD>Randall Stross</TD>
Rozdział 2. » Skrypty wykonywane na serwerze WWW_______________________51^

</TRX/TABLE>
</TDX/TRX/TABLE>
</TDX/TRX/TABLE>
</BODYX/HTML>

Przedstawiona strona nie imponuje w porównaniu ze stroną pokazaną na początku roz-


działu, jeżeli patrzymy na źródło. Jednak nadając jednej zmiennej inną wartość spowo-
dujemy, że dowolna liczba niezależnych stron będzie wyglądać inaczej. Weźmy za
przykład stronę z książkami autorów o innym, niż podano, nazwisku. Jeżeli dodamy do
bazy danych zapisy o książkach na temat innych firm, niż te które znajdują się w przy-
kładach, lista zostanie automatycznie zaktualizowana.

Jak można się zorientować z podanych przykładów, nie ma możliwości obejrzeć skryp-
tów dla serwera z końcówki klienta. Tworzenie strony jest realizowane przed wysłaniem
jej do klienta. Po wyjściu z serwera WWW strona wygląda jak zwykła strona HTML.
Oznacza to, że nie możesz powiedzieć, jakiego użyto języka skryptowego, chyba że ist-
nieje taka informacja w nagłówku. Skrypt pokazany w ostatnim przykładzie był napisa-
ny w PHP dla bazy danych MySQL. Współpraca pomiędzy tymi dwoma programami
zostanie opisana w drugiej części tej książki.

Do czego są dobre skrypty serwera


Klient dobrze wygląda, ale serwer świetnie pracuje. Skrypty dla serwera są atrakcyjne
z powodu dużej użyteczności. Większość użytkowników WWW często korzysta z wy-
ników działania skryptów serwera.

Dwa bilety do raju


Wiele czynności można zrealizować używając skryptów dla klienta lub dla
serwera. Na przykład wysyłanie poczty elektronicznej: używając skryptów
klienta (gdy użytkownik kliknie łącze MAILTO) otwieramy program pocztowy
zainstalowany na kliencie, uzupełniając odpowiednio pole adresu. Jeżeli
chcemy użyć skryptów dla serwera, tworzymy formatkę, którą formatujemy
jako e-mail i wysyłamy przez serwer SMTP. Można wybrać jedno z tych po-
dejść, aby skontrolować poprawność formatki, tworzenie list rozwijalnych
i obliczeń matematycznych. Czasami występują nieznaczne, jednak ważne
różnice przy realizacji zadania (listy tworzone przez serwer mogą być tworzo-
ne dynamicznie).
Jak wybrać właściwe rozwiązanie? Skrypty wykonywane na serwerze są zwy-
kle nieco wolniejsze z powodu dodatkowych transmisji, jednak możliwości
przeglądarki klienta ich nie ograniczają (dlatego zabiera konserwacja mniej
czasu). To dobre rozwiązanie dla witryn o masowej oglądalności lub witryn
edukacyjnych. Jeżeli jesteś pewien, że użytkownicy mają najnowsze przeglą-
darki i dużą przepustowość łącza, możesz śmiało stosować skrypty i grafikę.
Pamiętaj, że PHP może generować zarówno statyczny HTML, jak i skrypty
JavaScript. Metoda ta będzie opisana w dalszej części książki.
52__________________________________________Część l » Podstawy PHP

Istnieje jedna dziedzina zastosowań, w której serwer nie potrafi pomóc: trójwymiarowe
gry zręcznościowe. Im krótszy wymagany czas odpowiedzi, tym mniej użyteczny jest
do tego PHP. W chwili obecnej Sieć jest zbyt powolna na takie zastosowania (jednak
pionierzy szerokiego pasma starają się to zmienić).

Najbardziej użytecznymi zastosowaniami języków skryptowych, takich jak PHP, są:


* witryny informacyjne (zarówno do tworzenia, jak i oglądania);
* usługi dla grup (forum, tablica ogłoszeń itp.);
** e-mail (poczta dzięki WWW, przesyłanie);
* systemy wsparcia technicznego i usług dla klientów;
** sieci reklamowe;
* aplikacje biznesowe dostępne dzięki WWW;
+ książki telefoniczne;
* badania, ankiety, testy;
** wypełnianie i wysyłanie formularzy;
** personalizacja stron;
* oprogramowanie korporacyjne;
+ katalogi, broszury, witryny informacyjne;
* każda aplikacja, która łączy serwer usługowy (baza danych, LDAP, poczta itp.)
z WWW.

PHP zapewnia obsługę wszystkich tych zadań, a potrafi jeszcze więcej.

Wystarczy tej retoryki! Masz teraz jasny opis różnic pomiędzy technologiami dla ser-
wera i dla klienta, więc należy je zastosować w praktyce. Pokażemy teraz, jak zainsta-
lować i skonfigurować PHP (możesz też znaleźć kogoś, kto zrobi to za ciebie...).

Programowanie a pisanie skryptów


Różnica pomiędzy programowaniem a pisaniem skryptów coraz bardziej się
rozmywa. PHP używa większości podstawowych struktur stosowanych w in-
nych językach programowania. Niektóre w pełni interpretowane języki wbudo-
wywane w HTML, jak na przykład ASP, nadal są uważane za języki skryptowe,
natomiast języki dostarczające osobnych plików binarnych — jako przykład
klasycznego programowania. PHP 4 jest dynamicznie kompilowany (postać bi-
narna jest przechowywana i używana aż do zmiany w kodzie) i jest oficjalnie
„prawdziwym" językiem programowania. Zmiana ta, wraz z oszałamiającą pręd-
kością działania, przesuwa PHP do tej samej klasy w której mieści się Perl.
Rozdział 2. » Skrypty wykonywane na serwerze WWW_______________________53^

Podsumowanie
Aby wiedzieć, jakie zadania można wykonać za pomocą PHP (lub innego języka
skryptowego serwera), musisz dokładnie poznać granicę pomiędzy pracą na serwerze
i na komputerze klienta. W rozdziale tym przedstawiliśmy przykłady zwykłego statycz-
nego HTML, HTML z dodanymi rozszerzeniami CSS i kodem JavaScript, umożliwia-
jącym interakcję z klientem, a także wygenerowaną przez PHP stronę, przedstawioną
zarówno z punktu widzenia serwera, jak i klienta.

Skrypty wykonywane na kliencie pozwalają na atrakcyjną oprawę strony i działają


szybko, ale wszystkie rozszerzenia, wykraczające poza podstawowy kod HTML, zależą
od rodzaju przeglądarki. Jeżeli statyczne skrypty po stronie klienta nie są tworzone na
podstawie stale zmieniającej się składnicy danych, ich utrzymanie i zmiana wymagają
czasu. Programowanie w językach skryptowych wykonywanych na serwerze, takich jak
PHP, pozwala na podłączenie serwerów baz danych i innych serwerów usługowych do
stron WWW.

Architektura PHP 4 różni się znacznie od konstrukcji innych narzędzi do programowa-


nia na serwerze, a nawet od PHP 3. Jest on dynamicznie kompilowany, co znacznie
przyspiesza jego pracę. W PHP 4 odseparowano maszynę skryptową Zend od języka
skryptowego.
j**_____________________.____________________ Część l » Podstawy PHP
Rozdział 3.
Rozpoczynamy
pracę z PHP
W tym rozdziale:
* Decydujemy się na własny serwer lub dzierżawę
* Szukanie właściwego dostawcy Internetu
* Częste problemy z dostawcami
** Instalowanie PHP
* Wybór narzędzia programowania dla PHP

W tym rozdziale przedyskutujemy wady i zalety różnych możliwości dostępu do serwe-


ra WWW: dzierżawę, własny serwer lub możliwości pośrednie. Następnie przedstawi-
my szczegółowe wytyczne instalowania PHP. Na koniec kilka porad w sprawie wyboru
narzędzia programowania dla PHP. Po przeczytaniu tego rozdziału będziesz przygoto-
wany do napisania pierwszego skryptu.

Dzierżawa lub własny serwer


Pierwszą ważną decyzją, jaką musisz podjąć, jest wybór, gdzie będzie znajdować się twoja
witryna wykorzystująca WWW: w twoim komputerze, czy u dostawcy Internetu. Jeżeli
tę decyzję masz już za sobą, możesz przejść od razu do części o instalowaniu PHP.

Wariant z dostawcą Internetu


Uruchamianie witryn w komputerach dostawców Internetu staje się coraz bardziej
popularne wśród firm udostępniających witryny WWW. Poniżej przedstawimy wady
i zalety takiego wyjścia.
j>6__________________________________________Część l » Podstawy PHP

Zalety
Dzierżawa serwera ma wiele zalet. Dostawca będzie zarządzał (przynajmniej w teorii)
wieloma ważnymi szczegółami niezbędnymi do utrzymania witryny w ruchu, takimi jak:
* sprzęt;
* uaktualnianie oprogramowania;
* rejestracja domeny, adresowanie IP, DNS;
* serwer poczty (POP/IMAP i SMTP);
* przepustowość łącza;
* podtrzymanie napięcia;
* archiwizacja;
** bezpieczeństwo.

Hakerzy, brak prądu czy pogięte taśmy backupu nie są już twoim zmartwieniem. To
pracownik dostawcy usług internetowych dostaje informację, że coś złego dzieje się
z t woj ą witryną, i musi się zająć kłopotem.

Dzierżawa serwera jest w wielu przypadkach bardzo korzystna finansowo. PHP na Li-
nuksie lub którymś BSD jest tani i dostępny. W chwili obecnej niewielu dostawców
oferuje PHP na platformie NT (zwykle drożej).

Wady
Ale dzierżawa serwera ma też swoje wady...

Większość jest związana z kontrolą. Jeżeli korzystasz z usług dostawcy, jesteś gościem
i musisz dostosować się do jego zasad (mimo, że płacisz i wymagasz).

Głównym problemem związanym z PHP jest sposób jego pracy — moduł serwera lub
CGI. PHP działa lepiej jako moduł, jest wtedy wbudowany w serwer, a nie jest osob-
nym procesem. Prawie wszyscy wolą uruchamiać PHP jako moduł, jednak dostawcy
niekiedy uruchamiają go jako CGI, ponieważ wtedy łatwiej nim bezpiecznie admini-
strować. Są oczywiście dostawcy oferujący PHP działający jako moduł serwera, ale są
to zwykle mniejsze firmy oferujące niższą przepustowość łącza. Istnieje jeszcze kilka
innych sytuacji, w których oczekiwania użytkowników pozostają w konflikcie z wygo-
dą dzierżawy serwera.

Zwykle sprawdza się zasada: im mniej wyszukane żądania, tym bardziej możliwe i od-
powiednie jest wydzierżawienie serwera. Najgorsze jest jednak to, że na ten sam serwer
wpycha się coraz więcej witryn, czy tego chcesz, czy nie.

Istnieje kilka czynników, które mogą spowodować duże kłopoty w znalezieniu odpo-
wiedniego dostawcy.
Rozdział 3. » Rozpoczynamy pracę z PHP______________________________57

** Treści ogólnie uznane za niepożądane (przemoc, pornografia).


** Masowa poczta, znana jako spam.
* Treści atrakcyjne dla hakerów (informacje o zabezpieczeniach).
* Zawartość potencjalnie nielegalna.
* Wymagany niestandardowy sprzęt, system operacyjny lub oprogramowanie.
+ Wymagana duża przepustowość.
Jeżeli twoje wymagania należą do którejś z tych kategorii, musisz łapać każdą okazję,
jaka się nadarzy. Pewnie nie będziesz miał dużego wyboru, jeżeli jednak znajdziesz do-
stawcę, który zgodzi się na utrzymywanie twojej witryny, podpisz umowę, zanim zmie-
ni zdanie. Później będziesz mógł spokojnie szukać lepszego rozwiązania.

Musimy również wspomnieć o najważniejszym negatywnym czynniku, występującym


w przypadku usług niektórych dostawców: złości, spowodowanej awariami serwera
właśnie w momencie, gdy twoja witryna zaczęła pojawiać się w rankingach popularno-
ści. To nawet gorsze od utraty poczty, znikania DNS-a, wiszenia godzinami przy telefo-
nie w oczekiwaniu na kogoś z serwisu, braku odpowiedzi na uprzejme e-maile oraz
notorycznego zawyżania rachunków.

Jeżeli decydujesz się na dzierżawę, to na własną odpowiedzialność. Rozmawiając z do-


stawcą nie udawaj ignoranta, poświęć trochę czasu w poznanie podstawowych pojęć.
Powinieneś w szczególności nauczyć się rozróżniać problemy, które są związane z kon-
kretnym serwerem (nieprawidłowa konfiguracja), a tymi, na które nie mamy wpływu.

Szczegóły
Gdy wybierzesz już sposób, w jaki będziesz chciał udostępnić witrynę, możesz wybrać
spośród wielu ofert obecnych na rynku. Początkujący klienci powinni uważać. Słowo
ISP może dziś znaczyć prawie wszystko. W tabeli 3.1 zestawiliśmy specjalizację róż-
nych dostawców oraz ich potencjalnych klientów (firmy wymienione w tabeli są tylko
przykładami, nie popieramy tych firm, ani nie rekomendujemy ich usług).

Mimo, że znalezienie dobrego dostawcy Internetu czasami wydaje się tak samo trudne,
jak znalezienie partnera na całe życie, istniej ą witryny WWW ułatwiające to zadanie:
http://hosts.php. net
http://www. od-site. com/php
http://www. ispcheck. com
Zwróć uwagę na komentarze użytkowników, zarówno na te pozytywne, jak i negatyw-
ne. Spytaj przyjaciół i kolegów o opinie. Przeszukaj archiwa list dyskusyjnych — cza-
sami zdarzają się rekomendacje oraz opisy niedobrych doświadczeń.

Najważniejszym chyba zagadnieniem jest przepustowość łącza. Bądź ostrożny przy


zdaniu „nielimitowany ruch, pasmo, liczba odwiedzin". Przypomnij sobie pytanie mą-
drego trenera w średnim wieku, gdy stary właściciel drużyny proponował mu pracę do
J58__________________________________________Część l » Podstawy PHP

Tabela 3.1.
Różni dostawcy Internetu

Typ dostawcy Słowa kluczowe Użytkownicy PHP


Dostawca dla konsumentów Wdzwanianie, ISDN, linie DSL, Małe witryny (wizytówki)
(AOL, RoadRunner) ADSL, modemy kablowe
Bezpłatna dzierżawa witryn Bezpłatne pod pewnymi Witryny nie nastawione na zysk lub
(Linuxbox.com) warunkami witryny bezpłatnego oprogramowania
Dostawcy komercyjni Serwery wirtualne, kolokacja, Większość witryn
serwery dedykowane
Tworzenie witryn Projektowanie, promocja, Witryny, które chcą również
konsulting wynajmować projektantów
Dostawcy łącza T-l, DS.-3, DSL, Frame Relay Własne serwery

końca życia: „O jakim życiu myślisz?" Analogicznie, pasmo, jakiego nigdy nie zajmie
witryna miłośników poezji epickiej Joego, nie będzie już wystarczające dla witryny ofe-
rującej bezpłatne transmisje video. Przed zawarciem kontraktu sprawdź, czy nie wpad-
niesz w pułapkę.

W jaki sposób przewidzieć wymagane pasmo? l GB ruchu miesięcz-


nie to 100 000 obejrzeń pliku o wielkości około 10 k (włączając w to
grafikę, tekst i ogłoszenia, chyba że są ściągane z innego źródła, mie-
rzone z klienta, a nie z serwera).

Bądź szczególnie ostrożny przy szacowaniu ilości miejsca potrzebnego


dla witryny, szczególnie jeżeli zawiera ona dużo grafiki. Jeżeli przekro-
czysz limit miejsca, będziesz płacił wygórowane kwoty za każdy ułamek
dodatkowego megabajta. Zwykle odpowiedzialne za to są pliki logów.
Kasuj je lub ściągaj regularnie i przechowuj na nieco tańszym nośniku.

Własny serwer: wady i zalety


Posiadanie własnego serwera jest realne, ponieważ ceny połączeń spadają. Jest to nie-
zrównane rozwiązanie, jeżeli chodzi o konfigurację i kontrolę; zapewnia duże bezpieczeń-
stwo — jeżeli potrafisz nim właściwie zarządzać. Uruchomienie własnej konfiguracji
ułatwia rozwiązywanie problemów, ponieważ nie tracisz czasu na niekończące się roz-
mowy telefoniczne z serwisem dostawcy. Jeżeli masz niecodzienne lub budzące sprze-
ciw potrzeby, być może własny serwer będzie najlepszym rozwiązaniem.

Jednak prowadzenie serwera wymaga o wiele więcej pracy i może być nieco droższe,
szczególnie w przypadku małych i średnich witryn. Samodzielnie udostępniana witryna
świadczy o twoich umiejętnościach. Jeżeli nikt z twojego otoczenia nie zna się dobrze
na zagadnieniach bezpieczeństwa, możesz się spodziewać problemów.
Rozdział 3. » Rozpoczynamy pracę z PHP______________________________59

Mówiąc bardziej ogólnie, nie będziesz miał kogo winić, jeżeli coś pójdzie źle. Jeżeli
możesz codziennie spojrzeć w lustro i powiedzieć: „To wszystko moja zasługa i dobrze
mi z tym", masz dość pewności siebie, aby prowadzić własny serwer.

Rozwiązania pośrednie
Pomiędzy własnym serwerem a wynajmowaniem witryny rozciąga się spektrum możli-
wości. Istnieje kilka kompromisowych rozwiązań czerpiących najlepsze cechy obu po-
wyższych.

Kolokacja
Kolokacja polega na zakupieniu własnego komputera i wstawieniu go do pomieszczenia
dostawcy, gdzie zostanie wpięty do sieci i monitorowany. Jesteś odpowiedzialny za za-
kup sprzętu, licencji, zabezpieczenie, instalowanie, konfigurację oraz utrzymywanie
oprogramowania i sprzętu, oprócz UPS-a. Jeżeli serwer będzie wymagał konfiguracji,
będziesz musiał skonfigurować go sam lub zapłacić grube pieniądze za godzinę pracy
serwisanta. Jeżeli zdecydujesz się na kolokację, przygotuj się na to, że serwisant nie bę-
dzie miał żadnego doświadczenia z produktami, które zainstalowałeś na serwerze.

Serwer dedykowany
Dedykowany serwer jest dokładnie tym, co wskazuje nazwa. Dostawca kupuje maszy-
nę, konfiguruje ją według twojego życzenia (za twoje pieniądze oczywiście), włączają
do sieci i od tej pory każdy cykl procesora należy do ciebie. Dostawca zapewnia zwykle
wsparcie techniczne. Jest to bezpieczniejsze od dzierżawy i korzystne finansowo dla
średnich i dużych witryn.

Dzierżawa serwera i własne środowisko programistyczne


Opcja ta wymaga dwóch instalacji: witryny roboczej oraz identycznej instalacji na serwe-
rze przeznaczonym do jej tworzenia. Taki podział pozwala na korzystanie z najlepszych
cech obu rozwiązań: kto inny ma pager powiadamiający w środku nocy o awariach, a ty
masz własny serwer. Jeżeli mieszkasz na terenie, gdzie trudno o połączenie internetowe,
rozwiązanie takie jest jedyne z możliwych. Jest też często stosowane w dużych witry-
nach, przy których pracuje wielu programistów.

Instalowanie PHP
Jeżeli decydujesz się wydzierżawić kompletne środowisko programowania, możesz bez
obawy opuścić resztę tego rozdziału (dotyczy tylko własnego serwera).
60__________________________________________Część l » Podstawy PHP

Zanim zaczniesz
Przed rozpoczęciem instalacji PHP na jakiejkolwiek platformie będziesz potrzebował:
* komputera z wystarczającą ilością pamięci dla systemu operacyjnego;
* zainstalowanego systemu Unix lub Windows 95/98/NT/2000;
* działającego połączenia z Internetem, jeżeli jest to witryna robocza lub instala-
cja w Intranecie do tworzenia witryny. Nie potrzebujesz połączenia, jeżeli jest
to całkowicie oddzielna instalacja.

Pomoc na wstępnym etapie wykracza poza ramy tej książki. Możesz zajrzeć do nastę-
pujących witryn, aby zasięgnąć informacji:
http://164.109.153.102/idgsearchresult.asp?searchtype=2?keyword=networking
http://www. linuxdoc. org/HO WTO/HOWTO-INDEX/howtos. html

Jeżeli zamierzasz zainstalować PHP pod Windows, potrzebujesz:


* działającego serwera WWW obsługującego PHP. Za czasów PHP 3 IIS/PWS
były najlepszym rozwiązaniem, ponieważ istniał do nich moduł PHP. PHP 4
zapewnia szerszą gamę modułów dla Windows;
* zainstalowanej bazy danych obsługiwanej przez PHP (jeżeli chcesz używać
bazy danych);
* ścieżki do katalogu Windows (zwykle c:\windows dla Windows 95/98, bę-
dziesz musiał podać ten katalog przy instalacji na NT lub Windows 2000);
* uaktualnienia DCOM dla Windows 95, dostępnego bezpłatnie pod adresem:
http://do\vnload/microsoft.com/msdownloads/dcom/95/x86/en/dcom95.exe;
+ dystrybucji PHP dla Windows (www.php.net/downloads.php);
+ narzędzie do rozpakowywania plików .zip (poszukaj narzędzi kompresji pod
adresem http://download, cnet. com)
Jeżeli chcesz zainstalować PHP na Uniksie, potrzebujesz:
* dystrybucję PHP (www.php.net/downloads.php);
* ostatniej wersji serwera Apache (www.apache.org/dist/— szukaj pliku o naj-
wyższym numerze, który kończy się na tar.gz);
* zainstalowanej bazy danych obsługiwanej przez PHP (jeżeli chcesz używać
bazy danych);
** innego oprogramowania, do którego ma się podłączyć PHP (serwera poczty,
pakietu matematycznego, JDK itp.);
+ kompilatora ANSI C;
* Gnu make (od PHP 4 może być to dowolna wersja make — możesz je bezpłat-
nie skopiować z www.gnu.org/software/make);
Rozdział 3. » Rozpoczynamy pracę z PHP______________________________61

* bison i flex (wpisz „find . -name bison -print" oraz „find . -name
flex -print" z katalogu /usr, aby sprawdzić, czy masz zainstalowane te
programy, lub pozwól gcc, aby sprawdził w trakcie instalacji, czy są dostępne.
Jeżeli nie masz tych programów, możesz skopiować bison z www.gnu.org/soft-
ware/bison, a flex z ftp://ftp.ee. lbl.gov);
* ścieżki do pliku konfiguracyjnego HTTP. Do niedawna były to access, conf,
httpd.conf\ srm.conf dla serwera Apache, ale w tej chwili są połączone w jeden
plik httpd.conf. Zależy to od systemu operacyjnego, dystrybucji, serwera WWW.
Możesz użyć narzędzia Fin d, aby je odszukać;
* ścieżki do plików źródłowych Apache (zwykle /usr/local/apache_1.3.x);
* ścieżki do demona Apache znanego jako httpd (zwykle /usr/local/bin).

Teraz jesteś gotowy do instalacji.

Pamiętaj, że programy, z którymi PHP będzie się łączył, muszą być za-
instalowane przed jego kompilowaniem. Baza danych jest najbardziej
znanym typem serwera zewnętrznego. Innym przykładem jest bibliote-
ka BCmath, serwer IMAP, biblioteka mcrypt czy analizator XML Expat.

Różnica pomiędzy kompilacją dla Apache a kompilacją jako CGI jest bardzo niewielka.
Właściwie polega na ustawieniu opcji -with-apache lub -with-apxs w trakcie
konfiguracji. Wielu użytkowników dla wygody kompiluje jednocześnie wersję dla
Apache i CGI.

Procedura instalacji
Z powodu niezależności PHP można zastosować wiele specyficznych metod instalacji,
zbyt wiele, aby je tu wszystkie wymienić. Spróbujemy opisać tylko te, które uważamy
za najbardziej popularne.

Windows NT/2000 i IIS

Instalacja PHP pod Windows NT/2000 z serwerem IIS jest bardzo prosta. Należy pa-
miętać, że zasad stosowanych dla NT/2000 nie stosuje się dla Windows 95/98, i od-
wrotnie. W dalszej części rozdziału zamieściliśmy uwagi o instalacji pod 95/98, ta część
jest poświęcona Windows NT/2000.
1. Rozpakuj archiwum używając narzędzia typu unzip. Umieść pliki w katalogu
C:\PHP.
2. Skopiuj p\\kphp.ini-dist do katalogu Windows i zmień jego nazwę naphp.ini.
3. Przejdź do katalogu System32, który znajduje się w katalogu Windows. Powi-
nieneś mieć wiele plików DLL. Poszukaj pliku msvcrt.dll, jeżeli go nie ma, sko-
piuj go z katalogu PHP. Skopiuj również p\ikphp4ts.dll do katalogu System32.
j62_______________________________________Część l » Podstawy PHP

4. Uruchom Microsoft Management Console (inaczej Internet Service Manager).


Powinieneś zobaczyć graficzną reprezentację twojej witryny WWW (małe
globusy).
5. Kliknij prawym przyciskiem myszy ikonę reprezentującą witrynę, w której
chcesz uaktywnić PHP (utwórz nową, jeżeli jest to konieczne) i wybierz Prop-
erties. Kliknij zakładkę ISAP1filters. Kliknij Add, by dodać nowy filtr. Nazwij
filtr PHP, a w polu Location wpisz c: \PHP\php4isapi. dli. Musisz wykonać
tę czynność dla każdej witryny WWW osobno.
6. Kliknij zakładkę Home directory. Upewnij się, że zaznaczona jest opcja Exe-
cute permissions. Kliknij Configuration. Kliknij Add na zakładce Application
mappings. Wpisz c: \ P H P \ p h p 4 i s a p i . dli w polu Executable, w polu Exten-
sion — .php, zaznacz Script Engine, a pole Method exclusions pozostaw puste.
7. Zatrzymaj i ponownie uruchom usługę WWW. Z menu Start wybierz Settings,
Control Panel, Services. Przewiń listę, aż zobaczysz US Admin Service. Zaznacz
tę usługę i kliknij Stop. Po zatrzymaniu usługi (pokaże się stosowny komuni-
kat) zaznacz World Wide Web Publishing Service i kliknij Start. Zatrzymanie
i uruchomienie za pomocą narzędzia Internet Service Manager (kliknięciem
prawym przyciskiem myszy ikony globusa) może nie wystarczyć. Na koniec
trzeba będzie ponownie uruchomić komputer.
8. Uruchom Notepad. Wpisz <?php phpinfo ( ) ; ?>. Zapisz plik w głównym
katalogu z dokumentami serwera WWW jako info.php. Uruchom przeglądarkę
i wczytaj utworzony plik. Musisz wczytać, go podając URL (www.testdo-
main.com/info.php] lub wywołanie HTTP (http://hostname/info.php), a nie na-
zwę pliku (c:\inetpub\wwwroot\info.php). Powinieneś zobaczyć długą tabelę
z informacjami o nowej instalacji PHP 4. Gratulacje.

Niektórzy użytkownicy Windows zgłaszali, że muszą umieścić plik


php.ini w tym samym katalogu co php.exe. Nie jest to idealne rozwią-
zanie ze względów bezpieczeństwa, ponieważ lepiej usunąć ten plik
z drzewa katalogów WWW. Jednak dopóki nie będziesz miał dostępu
do źródeł tego systemu operacyjnego, nie wyeliminujesz problemu.

Unix i Apache
Za pierwszym razem gdy będziesz tworzył demona HTTP ze źródeł, możesz mieć pew-
ne obawy. Proces ten jest jasny; warto podjąć wysiłek kompilacji serwera WWW, za-
miast zależeć od pliku RPM stworzonego przez kogoś innego. Poza tym własny serwer
jest zwykle szybszy.

Tych, którzy już kompilowali wcześniejsze wersje PHP, informujemy, że procedura jest
identyczna, ale trwa dłużej.

Fragmenty kodu, przedstawione w przepisie poniżej, trzeba wpisać w wierszu poleceń.


Rozdział 3. » Rozpoczynamy pracę z PHP________________________________63^

Instalacje Red Hat lub Mandrake Linux mogą być dostarczane z Apa-
che i PHP w postaci plików RPM. Musisz usunąć je przed rozpoczę-
ciem kompilacji własnego PHP! Możesz dodatkowo mieć wersje RPM
innych serwerów, takich jak MySQL lub PostgreSQL, które instaluje
się inaczej, niż ich odpowiedniki w postaci źródeł. Jeżeli napotkasz
problemy, wyrzuć pliki RPM i zainstaluj program ze źródeł.

Załoguj się jako użytkownik root.

1. Rozpakuj programem unzip i tar dystrybucję Apache, jeśli jeszcze tego nie
zrobiłeś. Umieść je w /usr/local, jeżeli nie ma do tego przeciwwskazań.
gunzip -c apache_l.3.x.tar.gz
tar -xf apache_l.3.X.tar
cd apache_1.3.x
./configure

2. W analogiczny sposób rozpakuj i umieść dystrybucję PHP. Jeżeli Apache i PHP


nie znajdują się w tym samym katalogu, musisz poniżej zamieniać każdy ciąg
„.." na pełną ścieżkę do odpowiedniego pakietu.
cd . .
gunzip -c php-4.x.tar.gz
tar -xvf php-4.x.tar
cd php-4.x

3. Teraz należy skonfigurować tworzony pakiet (konfiguracja PHP jest obszer-


nym i skomplikowanym zagadnieniem, które nie zmieści się w tym rozdziale,
więc osoby zainteresowane powinny przejść rozdziału 31.). Najczęściej uży-
wana jest opcja tworzenia modułu Apache oraz obsługa wybranej bazy danych.
Obsługa języka Java oraz XML staje się coraz popularniejsza. Poniższy przy-
kład kompiluje PHP jako moduł Apache z obsługą MySQL i XML. Twoje
opcje mogą być inne.
. / c o n f i g u r e - - w i t h - a p a c h e - . . / a p a c h e _ l . 3 . x --with-mysql -with-xml
^--enable-track-vars

4. Utwórz i zainstaluj moduł PHP


make
make install

5. Skonfiguruj i skompiluj demona Apache. W poniższym przykładzie /etc/httpd


jest ścieżką do pliku konfiguracyjnego (np.: /conf/httpd.conf); jeżeli twój plik
znajduje się gdzieś indziej, wpisz odpowiednią ścieżkę. Jeżeli używasz naj-
nowszej wersji Apache z plikami konfiguracyjnymi w apache_1.3.x/confi nie
masz wcześniejszych plików konfiguracyjnych, możesz z poniższego przykła-
du usunąć -prefix.
cd . . /apache_l.3.x
. / c o n f i g u r e - - p r e f i x = / e t c / h t t p d --activate-module
t
3>=src/modules/php4 / I i b p h p 4 . amake

6. Zatrzymaj i zamień plik wykonywalny httpd. Istnieje kilka sposobów na za-


trzymanie i uruchomienie httpd. Jeżeli stosujesz inną metodę, nie ma powo-
64__________________________________________Część l » Podstawy PHP

dów, abyś jej nie używał. Jeżeli jest to konieczne, zamień /usr/local/bin na
właściwą ścieżkę do twojego demona HTTP. Jeżeli jesteś pewien, że nie masz
httpd, możesz po prostu wykonać make install. W środowiskach, w których
wymagana jest wysoka dostępność, możesz wstrzymać wykonanie tego etapu
aż do zakończenia konfigurowania PHP i Apache (punkty 7. i 8.), co spowo-
duje chwilowe przerwanie funkcjonowania witryny WWW.
cd src/support
./apachectl stop
cd . .
cp httpd /usrlocal/bin

7. Ustaw zawartość p\ikuphp.ini. Lista opcji zamieszczona jest w rozdziale 31.


Nowym użytkownikom polecamy ustawienie raportowania błędów na E_ALL.
cd ../../php-4.x
cp php.ini.dist /usr/local/lib/php.ini

8. Poinformuj serwer WWW, jakiego rozszerzenia chcesz używać do identyfika-


cji plików PHP (.php jest standardem, ale możesz użyć .html, .phtml lub inne-
go). Przejdź do plików konfiguracyjnych HTTP (/etc/httpd/conf lub inna
ścieżka, jaką masz w systemie) i otwórz plik httpd.conf(\ub srm.confwe wcze-
śniejszej wersji plików konfiguracyjnych Apache) za pomocą edytora. Dodaj
co najmniej jeden wiersz z definicją rozszerzenia dla PHP, tak jak pokazuje to
pierwszy wiersz przykładu. W drugim dodajemy definicję nakazującą analizę
wszystkich plików HTML.
AddType application/x-httpd-php .php
AddType application/x-httpd-php .html

9. Uruchom serwer. Za każdym razem gdy zmienisz konfigurację lub zawartość


pliku php. ini, będziesz musiał zatrzymać i uruchomić ponownie serwer. Wy-
słanie sygnału HUP nie wystarczy.
cd ../apache_l.3.x/src/support
./apachectl start

10. Nadaj uprawnienia dostępu do katalogu głównego z dokumentami wszystkim


użytkownikom. Wystarczy, że użytkownicy będą mieli prawo czytania plików
PHP. Jeżeli jest to konieczne, zamień /home/httpd/html/php na ścieżkę do twoje-
go katalogu z dokumentami.
chmod 755 /home/httpd/html/php

11. Uruchom edytor tekstu. Wpisz <?php phpinf o ( ) ; ?>. Zapisz plik w głównym
katalogu z dokumentami serwera WWW jako info.php. Uruchom przeglądarkę
i wczytaj utworzony plik. Musisz wczytać go, podając URL (www.testdomain.
com/info.php) lub wywołanie HTTP (http://localhost/info.php), a nie nazwę
pliku (/home/httpd/html/info.php). Powinieneś zobaczyć długą tabelę z infor-
macjami o nowej instalacji PHP 4. Gratulacje.

Windows 95/98 i PWS


Częstą, aczkolwiek niezbyt szczęśliwą sytuacją jest sprawdzanie działania PHP jak naj-
mniejszym kosztem (szczególnie dotyczy nowych użytkowników Windows) i instalacja
PHP pod Windows 95/98 z serwerem WWW Personal Web Server. Nie jest to najlep-
szy pomysł!
Rozdział 3. » Rozpoczynamy pracę z PHP________________________________65

Windows 95/98 nie był projektowany jako platforma dla serwerów. Aby zainstalować
serwer WWW i maszynę skryptową, należy dosyć głęboko „wkopać się" w rejestr. Je-
żeli coś pójdzie źle, system nie zadziała. Jeżeli zaś zdecydujesz się usunąć PHP z kom-
putera, nie będzie to prosta operacja usunięcia plików — należy przywrócić pierwotną
postać zapisów w rejestrze. Istnieje skrypt instalujący PHP, ale nie ma skryptu usuwają-
cego go z systemu.

Jeżeli decydujesz się na tę instalację mimo naszych ostrzeżeń, postępuj zgodnie z nastę-
pującymi wskazówkami.
1. Jeżeli pracujesz z Windows 95, zainstaluj poprawkę DCOM.
2. Rozpakuj archiwum zip i umieść jego zawartość w katalogu C:\PHP.
3. Skopiuj plik php.ini-dist z katalogu PHP do katalogu Windows i zmień jego
nazwę na php. ini. Plik musi być skopiowany przed wykonaniem następnych
czynności, inaczej instalacja się nie uda.
4. Przejdź do katalogu System, który znajduje się w katalogu Windows. Zoba-
czysz sporo plików DLL. Sprawdź, czy znajduje się tutaj plik msvcrt.dll; jeżeli
go nie ma, skopiuj go z katalogu PHP. Skopiuj również do katalogu System
p\ikphp4ts.dll.
5. Otwórz plik php.ini. Zmień ustawienie extension_dir na c:\php (tak, jak
nazwałeś katalog z PHP). Ustaw zmienną doc_root, aby wskazywała na
główny katalog dokumentów serwera WWW (c:\webroot). Usuń komentarze
przy właściwych wierszach extension=php_* .dli, aby załadować dodatko-
we moduły.
6. Wskaż PWS, gdzie znajdzie moduł ISAPI. Aby to zrobić, dodaj ścieżkę ISAPI
do pliku PWS-php4.reg, który znajdziesz w plikach dostarczanych z instalacją
PHP (czytaj podwójny backslash jak pojedynczy). Jeżeli zainstalowałeś PHP
w innym katalogu niż c:\php, musisz zmienić ścieżkę w pliku .reg
".php"=C:\\PHP\\php4isapi.dli"

7. Otwórz PWS Manager. Kliknij prawym przyciskiem myszy katalog z dokumen-


tami (na przykład c:\webroof) i wybierz Properties. Zaznacz Execute i po-
twierdź. Uruchom ponownie komputer.
8. Uruchom Notepad. Wpisz <?php phpinfo ( ) ; ?>. Zapisz plik w głównym
katalogu z dokumentami serwera WWW jako info.php. Uruchom przeglądarkę
i wczytaj utworzony plik. Musisz wczytać go, podając URL (www.testdomain.
com/info.php) lub wywołanie HTTP (http://hostname/info.php), a nie nazwę
pliku (c:\inetpub\wwwroot\info.php). Powinieneś zobaczyć długą tabelę z in-
formacjami o nowej instalacji PHP4. Gratulacje.

Nawet jeżeli jest możliwe użycie nazw katalogów zawierających spa-


cje, nie zalecamy używania ich pod Windows dla katalogów zawierają-
cych pliki PHP. Zwykle powodują problemy.
j>6__________________________________________Część l » Podstawy PHP

Inne serwery WWW


PHP działa doskonale również z innymi serwerami WWW, takimi jak Netscape Enter-
prise Server, Xitami, Zeus i thttpd. Zespół programistów zapowiedział wersję pracującą
jako moduł NSAPI (i prawdopodobnie również innych serwerów). Niestety w czasie
wydawania książki procedura takiej instalacji nie była udokumentowana lub była udo-
kumentowana słabo. Jeżeli potrzebujesz informacji o różnych instalacjach PHP, sprawdź
pod następującymi adresami.

Apache dla Windows (wersja CGI):


http://www. apache, org/docs/windows. html

fhttpd:
http://ww\v.fhttpd. org/www/install.html

Omni:
http ://www. umcsd. k!2. or. us/php/win32install. html

Narzędzia programistyczne
PHP nie posiada luksusowego graficznego środowiska programistycznego, z kreatorami,
polami wyboru czy ikonami. Jeżeli takie rzeczy są dla ciebie istotne, możesz używać
edytora typu WYSIWIG do formatowania stron, a następnie ręcznie dodawać funkcje
PHP. Wadą takiej strategii może być nieczytelność kodu napisanego przez program.

Szczególnie uważaj na Microsoft FrontPage, który sprawia użytkownikom


chyba najwięcej problemów. Musisz używać znaczników w standardzie
ASP (wybór opcji w pliku php.ini) lub stosować znaczniki w standar-
dzie JavaScript, co może być uciążliwe.

Wydaje się, że większość użytkowników PHP woli używać edytorów tekstowych. Pro-
gramy te powinny zapewniać minimum pomocy, na przykład podświetlanie składni, od-
szukiwanie nawiasów, domykanie znaczników. Większość z tych opcji pozwala na
zmniejszenie liczby pomyłek i działa jako podręczna ściągawka, nie tworząc pliku za ciebie.

Pamiętaj, że program ten nie musi znajdować się na tej samej maszynie co serwer. Jest
to szczególnie przydatne, jeżeli używamy Uniksa, w którym (parafrazując Blues
Brothers) „mamy oba rodzaje edytorów: emacs i vi". Macintosh, BE i Windows posia-
dają szeroką gamę sprytnych i przyjaznych edytorów tekstu. Patrząc z drugiej strony,
Unix ułatwia obsługę wielu systemów klienckich. Wielu programistów korzysta z zalet
obu rozwiązań.

Keith Edmunds prowadzi długą listę edytorów obsługujących PHP, wiele z nich można
uzyskać bezpłatnie albo za niewielką kwotę:
http:'//www. itworks. demon. co. uk/phpeditors. htm
Rozdział 3. « Rozpoczynamy pracę z PHP________________________________67^

Tabela 3.2.
Znane edytory PHP dla różnych platform

Platforma Produkt Opis

Macintosh BBEdit Pakiet WYSIWYG Macromedia Dreamweaver dla Maca


(www.barebones.com)
Unix emacs (www.emacs.org) Podświetlanie składni dla xemacs (może działać w emacs) jest
i xemacs (www.xemacs.org) dostępne pod adresem http://www.cs.huji.ac.il/~baryudin
/php3 _mode.html. vim (www.vim.org). Ulepszona wersja vi.
Czołowy edytor hakerów Uniksa, z podświetlaniem składni
PHP. Dostępny dla prawie wszystkich systemów operacyjnych
Windows HomeSite Ciągle popularny komercyjny edytor dla Windows. Zawiera
(www.allaire.com/homesite) Macromedia Dreamweaver. Notepad, dołączony do każdej
wersji Windows. Możesz wierzyć lub nie, ale wielu ludzi
tworzy świetne witryny przy użyciu prostych narzędzi

Słyszeliśmy plotki, że Zend pracuje nad bogatym środowiskiem IDE dla


PHP. Jednak w czasie wydawania książki nie zostały one potwierdzone.

Weź głęboki oddech. Po zainstalowaniu i skonfigurowaniu PHP będziesz gotów do na-


pisania pierwszych skryptów.

Podsumowanie
Zanim będziesz mógł używać PHP, musisz zdecydować, czy będziesz utrzymywał ser-
wer WWW, czy go wydzierżawisz, czy wreszcie wybierzesz jakieś kompromisowe
rozwiązanie, np. kolokację. Wybór determinują: koszt, rozmiar i wielkość ruchu gene-
rowanego przez witrynę, nietypowe wymagania sprzętowe lub programowe oraz rodzaj
prezentowanych stron. Najlepszym wyjściem dla małej witryny bez specjalnych wyma-
gań jest dzierżawienie serwera.

Jeżeli zdecydowałeś się na uruchomienie własnego serwera, masz już informacje o naj-
częściej używanych platformach. PHP 4 obsługuje wiele serwerów WWW, ale opis
wszystkich instalacji nie był dostępny.

Na koniec zastanawialiśmy się, jakie narzędzia programistyczne najlepiej nadają się do


PHP. W chwili obecnej nie ma jeszcze zintegrowanego środowiska dla PHP, a więk-
szość programistów używa po prostu ulubionego edytora tekstu. Można również doda-
wać PHP do stron zbudowanych przez graficzny edytor HTML, ale trzeba przygotować
się na kłopoty.
68
Podstawy PHP
Rozdział 4.
Dodajemy PHP do HTML
W tym rozdziale:
* Przełączanie się do trybu PHP
4 Wybór stylu znaczników PHP
* Piszemy w PHP program „Witaj świecie"
** Pliki dołączane i wymagane

Po tych czynnościach wstępnych możemy się wreszcie zabrać za napisanie pierwszego


skryptu PHP. W rozdziale tym opiszemy tryb PHP, znaczniki oraz sposób dołączania
innych plików i pliki wymagane. Napiszesz również pierwszy skrypt PHP.

HTML jest gotowy na PHP


PHP jest świetnie zadomowiony w HTML (musi tak być, ponieważ jest w niego wbu-
dowywany). Później zobaczysz, w jaki sposób PHP wykorzystuje sprytniejsze części
standardu HTML, takie jak formularze, do robienia różnych pożytecznych projektów.

Wszystko, co jest prawidłowym kodem HTML u klienta jest zgodne z PHP. PHP nie
przejmuje się fragmentami w JavaScript, wywołaniami dźwięków i animacji, apletów
i innych elementów działających u klienta. PHP ignoruje te fragmenty, a serwer po pro-
stu przesyła całość do klienta.

Powinno być już jasne, że możesz użyć dowolnej metody tworzenia strony WWW i do-
dawania do niej kodu PHP. Jeżeli pracujesz nad stronami w grupie, używając dużych
multimedialnych pakietów, możesz nadal tak pracować. Nie potrzebujesz radykalnej
zmiany narzędzi czy przebiegu pracy — rób to, co do tej pory, a na koniec dodaj do ko-
du skrypty serwera.
70__________________________________________Część l » Podstawy PHP

Przełączanie się z HTML do PHP


W jaki sposób wskazujemy sekcję PHP w kodzie HTML? Używa się do tego celu
znaczników PHP na początku i końcu każdej sekcji. Proces ten nazywamy przełącza-
niem z kodu HTML do PHP.

Wszystko pomiędzy tymi znacznikami jest traktowane przez moduł lub CGI jako PHP.
Tekst na zewnątrz sekcji PHP nie interesuje serwera i po prostu jest przesyłany do
klienta bez względu na to, czy jest to HTML, JavaScript lub cokolwiek innego.

Można stosować cztery rodzaje znaczników PHP, według własnego uznania i wygody,
w różny sposób uzasadniając ich użycie.

Kanoniczne znaczniki PHP


Najbardziej uniwersalnym stylem znaczników PHP jest
<?php ?>

Jeżeli używasz takich znaczników, możesz być pewien, że znaczniki te będą zawsze
poprawnie zinterpretowane. Jeżeli nie musisz stosować innego stylu znaczników, najle-
piej używać tego sposobu.

Musisz używać tych znaczników, jeżeli stosujesz XML w PHP, ponieważ XML korzy-
sta z krótkich znaczników otwierających.

Krótkie znaczniki otwierające (w stylu SGML)


Krótkie znaczniki otwierające wyglądają w następujący sposób:
<? ?>

To najkrótsza z możliwych opcji. Jeżeli często przełączasz się pomiędzy HTML i PHP,
styl ten może kusić mniejszą liczbą uderzeń w klawiaturę. Jednak cena stosowania tych
znaczników może być wysoka. Musisz wykonać jedną z trzech czynności, aby PHP
rozpoznawał takie znaczniki:
1. Wybierz opcję -enable-short-tags w trakcie konfigurowania przed kom-
pilacją PHP.
2. Uaktywnij ustawienie short_open_tag w pliku php.ini. Musisz je wyłączyć,
aby używać XML z PHP.
3. Użyj funkcji short_tags ( ) .

Krótkie znaczniki nie były obsługiwane przez wersję beta PHP 4. Zmusiło to wielu pro-
gramistów do konwersji plików do postaci zgodnej ze znacznikami kanonicznymi, być
może tylko tymczasowej.
Rozdział 4. » Dodajemy PHP do HTML__________________________________71^

Znaczniki w stylu ASP


Znaczniki w stylu ASP wyglądaj ą następująco:
<% *>

Użytkownicy FrontPage często wybierają ten rodzaj znaczników. Aby można było ich
używać, należy włączyć opcję konfiguracyjną w pliku php.ini. Oczywiście jeżeli uży-
wasz znaczników w stylu ASP i rozszerzenia .asp (np. konwertując witrynę z ASP do
PHP), musisz wyłączyć ASP na serwerze IIS.

Znaczniki skryptu HTML


Znaczniki takie wyglądająnastępująco:
<SCRIPT LANGUAGE="PHP"> </SCRIPT>

Mimo że są one efektywne i rozwiązują problemy z FrontPage, mogą być niewygodne


w niektórych sytuacjach, na przykład w krótkich sekwencjach używających zmiennych.
Bądź szczególnie ostrożny, jeżeli strona zawiera kod JavaScript, ponieważ znaczniki
zamykające skrypty są bardzo niejednoznaczne. Najlepiej używać znaczników skryptu
HTML do dosyć obszernych bloków kodu PHP.

Witaj świecie
Jesteśmy gotowi do napisania pierwszego programu w PHP. Otwórz nowy plik w ulu-
bionym edytorze i wpisz:
<HTML>
<HEAD>
<TITLE>Pierwszy program w PHP</TITLE>
</HEAD>

<BODY>
<?php print("Witaj okrutny świecie");?>
</BODY>
</HTML>

Większość przeglądarek nie wymaga niczego poza sekcją PHP. Jednak dobrym pomy-
słem jest stosowanie kompletnej struktury HTML, do której wbudowujemy PHP.

Jeżeli nie uzyskasz czegoś podobnego do rysunku 4.1, najprawdopodobniej zaistniał ja-
kiś błąd podczas konfiguracji lub instalacji.

Przeczytaj jeszcze raz rozdział 3., opisujący instalację, i rozdział 31., traktujący o opcjach
konfiguracyjnych. W rozdziale 15. rozpoznamy niektóre częste problemy i opiszemy
wskazówki na temat uruchamiania programów.
72 Część l » Podstawy PHP

Rysunek 4.1.
Pierwszy skrypt PHP

Każdy, kto programuje od jakiegoś czasu, może być znudzony takim


obowiązkowym skryptem. Jeżeli należysz do tej grupy ludzi, spróbuj
dopisać do przedstawionego powyżej skryptu wiersz z funkcją phpin-
fo(). W rozdziale 31. wyjaśnimy dokładnie zawartość tej tabeli, a w tej
chwili możesz to traktować jako testowanie instalacji.

Wejście i wyjście z trybu PHP


Skrypt PHP składa się z trybu PHP lub HTML. Nie ma stanów pośrednich. Wszystko
w środku znaczników PHP jest kodem PHP, wszystko na zewnątrz znaczników PHP
jest czystym HTML.

Możesz błyskawicznie przełączać się do trybu PHP na tak długo i tak często, jak po-
trzeba. Na przykład:
<?php Sid=l; ?>
<FORM METHOD="POST" ACTION="registration.php">
<P>Imię:
<INPUT TYPE="TEXT" NAME="firstname" SIZE 20>
<P>Nazwisko:
<INPUT TYPE="TEXT" NAME="lastname" SIZE 20>
<P>Pozycja:
<INPUT TYPE="TEXT" NAME="rank" SIZE 10>
<INPUT TYPE="HIDDEN" NAME="serial_number" VALUE="<?php
print("Sid");?>">
<INPUT TYPE="SUBMIT" VALUE="INPUT">
</FORM>

Zauważ, że wiersze wykonywane w pierwszym bloku PHP, w tym wypadku przypisa-


nie zmiennej, ciągłe obowiązują w drugim bloku. W następnym rozdziale opiszemy, co
dzieje się ze zmiennymi, gdy wchodzisz i wychodzisz z bloku PHP.
Rozdział 4. » Dodajemy PHP do HTML__________________________________73

Dołączanie plików
Innym sposobem na dodanie kodu PHP do HTML jest umieszczenie kodu w oddziel-
nym pliku i wywołanie go za pomocą funkcji include. Na przykład plik o nazwie
dziewczyna.inc zawiera tylko:
<?php $dziewczyna="Ilona";
print ("$dziewczyna") ; ?>

Plik ten jest używany na stronie WWW w następujący sposób:


<HTML>
<HEAD>
<TITLE>Wyznanie</TITLE>
</HEAD>
<BODY> «
<P>Daj mi swe usta, weź mnie w ramiona.<BR>
Niech się przekonam, ile słodyczy jest w słowie
"<?php include("dziewczyna.inc");?>"
</BODY>
</HTML>

Wynik pokazano na rysunku 4.2 (jeżeli twoje uczucia się zmienią, wystarczy zmienić
jedną zmienną w pliku dziewczyna.inc).

Rysunek 4.2.
Wynik
dołączenia pliku*

Funkcja include przekazuje po prostu zawartość pliku jako tekst. Wielu użytkowni-
ków myśli, że skoro funkcja include jest wykonywana w bloku PHP, dołączany tekst
jest również w trybie PHP. Nieprawda! Serwer przełącza się w tryb HTML przy dołą-
czaniu każdego pliku i po cichu wraca do trybu PHP na jego końcu.

Jeżeli usuniesz znaczniki PHP z pliku dziewczyna.inc w następujący sposób:


$dziewczyna="Ilona";
print ("$dziewczyna");

treść pliku pojawi się w przeglądarce (a jeśli Ilona zna PHP...).

' Fragment piosenki zespołu „Wały Jagiellońskie".


74__________________________________________Część l » Podstawy PHP

Aby upewnić się, że nie zdarzy się taka sytuacja, należy zapamiętać, że każda część do-
łączanego pliku, która ma być wykonana jako PHP, musi być opatrzona prawidłowymi
znacznikami PHP.

Niektórzy w takich sytuacjach używają konstrukcji require zamiast funkcji include,


jednak include jest w większości przypadków mniej restrykcyjne. Z drugiej strony re-
quire jest wykonywane szybciej. Konstrukcje te są opisane w rozdziale na temat funkcji.

Ponieważ include tylko przekazuje tekst, a nie kod PHP, może być stosowane do wsta-
wiania fragmentów HTML. Możesz np. umieścić informację o prawach autorskich
w pliku tekstowym i używać na każdej stronie funkcji include. Spowoduje to, że
uaktualnianie informacji o prawach autorskich jest szybsze i mniej nużące.

Podsumowanie
PHP łatwo można wbudowywać w istniejący kod HTML. Możesz użyć dowolnej ulu-
bionej metody tworzenia HTML, a następnie dodać sekcje PHP. Dodany kod PHP może
wykonywać wiele zadań, od wypisania liczby, aż do stworzenia sporego fragmentu kodu.

Każdy blok PHP, dowolnej długości, jest ograniczony znacznikami. Istnieje kilka sty-
lów znaczników PHP; początkujący użytkownicy powinni raczej używać znaczników
kanonicznych. Możesz również włączać pliki PHP używając funkcji include ( ) lub
require ( ) , lecz ich zawartość nie jest traktowana jako PHP, chyba że otoczysz ją
znacznikami PHP.
Rozdział 5.
Składnia, zmienne
i wyświetlanie
W tym rozdziale:
4 Podstawowe zasady pisania kodu PHP
•* Zapamiętywanie danych w zmiennych
+ Wyświetlanie wyników na stronie WWW

W rozdziale tym opiszemy podstawy składni PHP i zasady, jakich musi się trzymać do-
brze napisany kod PHP. Opiszemy sposób użycia zmiennych do zapamiętywania
i odczytywania informacji w czasie wykonywania skryptu. Na koniec pokażemy naj-
prostszy sposób wyświetlania tekstu w przeglądarce użytkownika.

PHP wiele wybacza


Pierwszą i najważniejszą rzeczą, jaką można powiedzieć o języku PHP, jest to, że pró-
buje wybaczać możliwie wiele. Języki programowania coraz mniej rygorystycznie pod-
chodzą do składni. Ścisłe stosowanie składni pomaga upewnić się, że napisany kod jest
dokładnie tym, o czym myślałeś. Jeżeli napisałeś program sterujący reaktorem atomo-
wym, ale zapomniałeś przypisać wartość zmiennej, to lepiej, że program zostanie odrzu-
cony. Filozofia projektu PHP zdecydowanie różni się od takiego podejścia. Ponieważ
PHP zrodził się jako narzędzie do tworzenia szybkich i prostych stron WWW, kładzie
nacisk na wygodę programisty, a nie na poprawność kodu. Zamiast zmuszać programi-
stę do dodatkowej pracy przy określaniu, co oznacza dany fragment kodu, PHP wymaga
tylko minimum i próbuje orientować się, co autor miał na myśli. Z tego powodu nie po-
trzebuje konstrukcji językowych takich, jak deklarowanie zmiennych.
76__________________________________________Część l » Podstawy PHP

Jednak PHP nie potrafi czytać w twoich myślach, zakłada minimalny zestaw zasad, któ-
re muszą być zastosowane. Jeżeli zamiast świetnej strony WWW zobaczysz w przeglą-
darce komunikat „parse error", oznacza to, że złamałeś zasady i PHP musiał się poddać
podczas analizowania twojej strony.

HTML to nie PHP


Drugą ważną zasadą jest to, że składnia PHP odnosi się tylko do fragmentów strony
zawierających PHP. Ponieważ PHP jest wbudowany w dokumenty HTML, każda część
takiego dokumentu jest interpretowana jako PHP lub HTML w zależności od tego, czy
znajduje się pomiędzy znacznikami PHP, czy nie.

Składnię PHP stosuje się tylko do PHP, więc zakładamy, że do końca tego rozdziału
mamy cały czas aktywny tryb PHP; większość fragmentów kodu należy wbudować
w stronę HTML i otoczyć odpowiednimi znacznikami.

Składnia PHP bazuje na C


Składania PHP bazuje na języku C. Jeżeli jesteś jednym z tych szczęśliwców znających
C, to dla ciebie bardzo wygodna sytuacja. Jeżeli nie jesteś pewien, jak powinno być za-
pisane jakieś wyrażenie, spróbuj najpierw napisać je w C (jeżeli nadal nie działa, zajrzyj
do podręcznika). Dalsza część przeznaczona jest dla czytelników, którzy nie znają C
(programiści C mogą przejrzeć nagłówki oraz przeczytać dodatek dla nich napisany).

PHP nie przejmuje się odstępami


Przez odstępy rozumiemy wszystkie znaki niewidoczne na ekranie, włączając spacje,
tabulacje, znaki końca wiersza. To, że PHP nie przejmuje się odstępami, nie oznacza, że
odstępy nigdy nie są znaczące (są niezbędne do oddzielania „słów" języka PHP). Moż-
na stwierdzić, że nie ma znaczenia liczba odstępów w wierszu, jeden odstęp jest tak sa-
mo dobry jak kilka.

Każde przytoczone poniżej wyrażenie przypisuje sumę 2 + 2 do zmiennej Sfour:


$four = 2 + 2 ; // pojedyncze odstępy
Sfour <tab>=<tab>2<tak»+<tab>2 ; // odstępy i tabulacje
Sfour "
2
+
2; // wiele linii

Używanie znaku końca wiersza jest bardzo wygodne, ponieważ nigdy nie trzeba się sta-
rać, aby wyrażenie zmieściło się w jednym wierszu.
^^fe.
Rozdział 5. » Składnia, zmienne i wyświetlanie__________________________ 77

PHP jest czasami wrażliwy na wielkość liter


Przeczytawszy wcześniej, że PHP nie jest wybredny, pewnie będziesz zaskoczony, że
czasami jest jednak wrażliwy na wielkość liter (różnica pomiędzy małymi i wielkimi
literami ma dla niego znaczenie). Wielkość liter ma znaczenie dla wszystkich zmien-
nych. Jeżeli wstawisz następujący kod do strony HTML:
<?php
$capital = 67;
print("Zmienna capital ma wartość $capital<BR>");
print("Zmienna CaPiTal ma wartość $CaPiTal<BR>");
?>

to oglądając wynikową stronę zobaczysz:


Zmienna capital ma wartość 67
Zmienna CaPiTal ma wartość

Ponieważ różne wielkości liter spowodowały wygenerowanie dwóch różnych zmien-


nych (standardowe ustawienia nie spowodowały wygenerowania błędu „nieprzypisana
zmienna"; więcej informacji znajdziesz w części „Nieprzypisane zmienne").

Inaczej niż w C, nazwy funkcji i podstawowe konstrukcje języka (if, then, else,
while itp.) ignoruj ą wielkość liter.

Instrukcje to wyrażenia zakończone średnikiem


Poniżej przedstawiamy typową instrukcję w PHP, w tym przypadku przypisuje ona ciąg
znaków do zmiennej o nazwie $greeting:
Sgreeting = "Witaj w P H P ! " ;

Poniżej opiszemy, w jaki sposób wyrażenia są budowane z mniejszych fragmentów i jak


interpreter PHP realizuje przetwarzanie wyrażeń (jeżeli wyrażenie i instrukcje nie przy-
sparzają ci problemu, możesz opuścić ten fragment).

Najmniejszymi fragmentami wyrażeń w PHP są niepodzielne elementy, takie jak liczby


(3.14159), ciągi ("dwa"), zmienne (Sdwa), stałe (TRUE) i słowa kluczowe składni PHP
(if, else itd.). Są one oddzielone od siebie odstępami lub innymi znakami specjalny-
mi, takimi jak nawiasy okrągłe i klamrowe.

Następny, bardziej skomplikowany składnik PHP to wyrażenie, które jest dowolną kom-
binacją elementów mających jakąś wartość. Zarówno pojedyncza liczba, jak i zmienna
są wyrażeniami. Proste wyrażenia mogą być łączone w złożone, zwykle poprzez wsta-
wienie pomiędzy nie operatora, (np.: 2 + ( 2 + 2 ) ) lub użycie funkcji (np.: power_
of (2*3, 3*2)). Operatory działające na dwóch argumentach są wstawiane pomiędzy,
natomiast funkcje pobierają dane z nawiasów następujących bezpośrednio po nazwie
funkcji. Dane te (parametry) rozdziela się przecinkami.
J78__________________________________________Część l » Podstawy PHP

Obliczanie wyrażeń
Jeżeli interpreter PHP napotka w tekście wyrażenie, natychmiast oblicza jest jego war-
tość. Oznacza to, że PHP oblicza wartości najmniejszych części wyrażenia i łączy ze
sobą wartości połączone operatorami lub wywoływanymi funkcjami, dopóki nie zosta-
nie uzyskana wartość całego wyrażenia. Kolejne kroki obliczania wartości wyrażenia
wyglądają następująco:
$wynik = 2 * 2 + 3 * 3 + 5;
( = 4 + 3 * 3 + 5 ) // kolejne kroki obliczania
(=4+9+5)
(= 13 + 5)
(= 18)

Wynik działań, liczba 18, jest zapisany do zmiennej $wynik.

Priorytety, łączenie i kolejność obliczeń


Przy wyliczaniu wyrażeń w PHP stosuje się dwa rodzaje zasad: sposób łączenia podwy-
rażeń i kolejność ich obliczania. W przykładzie, który powyżej przedstawiliśmy, mno-
żenia były wykonywane wcześniej niż dodawania, co ma wpływ na końcowy wynik.

Sposób, w jaki operatory grupują wyrażenia jest nazywany priorytetem operatorów.


Operatory o wyższym priorytecie wygrywają w „łapaniu" wyrażeń umieszczonych
obok nich. Jeżeli chcesz, możesz zapamiętać te priorytety, np. „*" ma wyższy priorytet
od „+" (opiszemy to dokładniej w dalszych rozdziałach). Jeżeli nie jesteś pewien kolej-
ności wykonania operacji, zawsze możesz użyć nawiasów do grupowania wyrażeń.

Na przykład:
$wynikl = 2 + 3 * 4 + 5 //=19
Swynik2 = (2 + 3) * (4 + 5) // = 45

Priorytety operatorów usuwają większość niejednoznaczności przy łączeniu podwyra-


żeń, jednak problemy w interpretacji kolejności obliczania wyrażeń z takim samym
priorytetem pozostają.

Na przykład:
$ile = 3 . 0 / 4 . 0 / 5 . 0 ;

Wynik może być 0,15 lub 3,75 w zależności od tego, który operator dzielenia pierwszy
„złapie" liczbę 4. W podręczniku znajduje się wyczerpująca lista zasad łączenia, jednak
najważniejszą z nich jest łączenie podwyrażeń od lewej do prawej. Zgodnie z tym nasze
wyrażenie ma wartość 0,15, ponieważ pierwszy z lewej operator dzielenia będzie wy-
konany najpierw.

Ostatnim problemem jest kolejność obliczeń, co nie jest tożsame z łączeniem wyrażeń.
Dla przykładu spójrzmy na następujące wyrażenie:
3*4 + 5*6

Wiemy, że mnożenie wykonywane jest przed dodawaniem, jednak nie wiemy, które
mnożenie zostanie wykonane pierwsze. Zwykle nie będziesz musiał przejmować się
Rozdział 5. » Składnia, zmienne i wyświetlanie_____________________________79

kolejnością, ponieważ w większości przypadków nie wpływa na wynik. Można tworzyć


dziwaczne wyrażenia, w których wynik zależy od kolejności obliczeń, zwykle wyko-
nując przypisania w podwyrażeniach.

Na przykład:
Sha = ($to = Stamto + 5) + (Stamto = Sto + 3); // ŹLE

Nie pisz w ten sposób! PHP może, ale nie musi mieć zdefiniowanej kolejności oblicza-
nia wyrażeń, jednak nie powinieneś na tym polegać (jedynym dozwolonym użyciem
obliczania od lewej do prawej jest „skracanie" wyrażeń logicznych, które opiszemy
w rozdziale 7.).

Wyrażenia i typy
Zwykle programiści są ostrożni przy dobieraniu typów wyrażeń dla operatorów lub
funkcji. Najczęściej występujące to wyrażenia matematyczne (używające operatorów
matematycznych do działań na liczbach), logiczne (obliczające wyrażenia typu prawda
lub fałsz za pomocą operatorów and i or) lub operacje na ciągach (za pomocą operato-
rów i funkcji tworzących ciągi znaków). Rozważmy poniższe wyrażenie, które umyśl-
nie miesza typy podwyrażeń:
2 + 2 * "nonsens" + TRUE

Zamiast komunikatu o błędzie pojawi się liczba 3 (możesz potraktować to jako zagad-
kę; w następnym rozdziale wyjaśnimy, co się stało).

Przypisywanie wyrażeń
Często używa się wyrażeń, w których do zmiennej przypisywany jest wynik jakiegoś
wyrażenia. Ma ono postać nazwy zmiennej (poprzedzonej znakiem S), następnie poje-
dynczego znaku równości i obliczanego wyrażenia. Na przykład:
Sosiem - 2 * (2*2}

Wyrażenie to przypisze spodziewaną wartość do zmiennej $osiem.

Należy również pamiętać, że wyrażenia przypisania same w sobie są wyrażeniami i po-


siadaj ą wartość! Wartość ta jest wartością zmiennej po wykonaniu przypisania. Możesz
więc użyć przypisania w środku bardziej skomplikowanego wyrażenia. Jeżeli wykonasz
następujące wyrażenie
$dziesiec = ($dwa = 2) 4- (Sosiem = 2 * ( 2 * 2 ) )

do każdej ze zmiennych zostanie przypisana wartość zgodna z jej nazwą.

Podsumowując, instrukcją PHP jest każde wyrażenie zakończone średnikiem. Jeżeli


wyrażenie porównamy do wyrazów, to instrukcja stanowi pełne zdanie, a średnik jest
kropką na jego końcu. Każda sekwencja prawidłowych instrukcji opatrzona znacznika-
mi PHP stanowi program.
80_______________________________________ Część l » Podstawy PHP

Powody używania wyrażeń i instrukcji

Są dwa powody pisania wyrażeń: uzyskanie ich wartości oraz efektu ubocznego. War-
tością wyrażenia jest to, co może być przekazane do bardziej skomplikowanych wyrażeń.
Efekt uboczny to wszystko, co zdarzyło się w czasie obliczania wyrażenia. Najbardziej
typowymi efektami ubocznymi to przypisanie wartości do zmiennej, zmiana wartości
zmiennej, wypisanie czegoś na ekranie lub wykonanie trwałej zmiany w środowisku
programu (np. zmiany w bazie danych).

Mimo, że instrukcje są wyrażeniami, nie są włączane w bardziej skomplikowane wyra-


żenia. Oznacza to, że jedynym powodem pisania instrukcji jest efekt uboczny! Można
więc pisać poprawne, ale całkowicie nieużyteczne instrukcje, jak na przykład druga
z poniższych:
print("Witaj"); // efektem ubocznym jest wydruk na ekranie
2*3+4; // bezużyteczne — brak efektu ubocznego
$value_num=3M + 5; // efektem ubocznym jest przypisanie
store_in_database(49.5); // efektem ubocznym jest zapis w bazie

Bloki
Mimo że instrukcji nie można łączyć tak jak wyrażeń, można zawsze umieścić sekwen-
cję instrukcji tam, gdzie spodziewana jest jedna instrukcja, poprzez otoczenie grupy in-
strukcji nawiasami klamrowymi.

Konstrukcja if w PHP ma wyrażenie warunku (w okrągłych nawiasach), po którym na-


stępuje instrukcja wykonywana, jeżeli wartością warunku jest TRUE. Jeżeli chcesz, aby
wykonane zostało kilka instrukcji, musisz wstawić w to miejsce sekwencję otoczoną
nawiasami klamrowymi. Poniższe fragmenty kodu (wypisujące uspokajające informa-
cje, że 1+2 to nadal 3) są równoważne:
if (3 == 2 + 1)
print!" Na szczęście to jeszcze działa jak myślałem<BR>") ;
if (3 == 2 + 1)
f
print(" Na szczęście to jeszcze");
print (" działa jak myślałem<BR>");
)

Możesz umieścić w bloku dowolny rodzaj instrukcji, nawet kolejną instrukcję if,
która będzie miała swój blok instrukcji. Oznacza to, że instrukcja if może zawierać
inną instrukcję if. Takie zagnieżdżanie jest możliwe bez szczególnych ograniczeń
liczby poziomów.

Komentarze
Komentarz to fragment programu przeznaczony tylko dla programistów, nie dla inter-
pretera. Pierwszą czynnością, jaką wykonuje analizator języka, jest usunięcie komenta-
rzy, więc nie mają one wpływu na działanie programu. Komentarze są nieocenioną
Rozdział 5. » Składnia, zmienne i wyświetlanie___________________________81

pomocą dla innej osoby, czytającej twój kod i starającej się zorientować, o czym my-
ślałeś, gdy pisałeś ten fragment. Często tąosobąjesteś ty sam, po kilku tygodniach bądź
miesiącach od napisania programu.

PHP czerpał inspirację z języków programowania C, Perl oraz skryptów powłoki Unix.
W efekcie PHP zapewnia kilka sposobów komentowania pochodzących z tych języków.
Style te mogą być dowolnie mieszane w kodzie PHP.

Komentarze wielowierszowe w stylu C


Wielowierszowe komentarze są identyczne jak w C. Komentarz rozpoczyna się parą
znaków / * i kończy się znakami * /. Na przykład:
/* To jest
komentarz w
PHP */

Trzeba pamiętać, że komentarzy nie można zagłębiać. Nie możesz umieścić jednego
komentarza w drugim. Jeżeli spróbujesz to zrobić, komentarz zakończy się na */,
a reszta tekstu, która miała być komentarzem, będzie interpretowana, co najprawdopo-
dobniej spowoduje błąd. Na przykład:
/* Ten komentarz spowoduje /* błąd
na ostatnim wyrazie tego */ zdania
V

Łatwo jest zrobić coś takiego nieświadomie, zwykle gdy próbujesz wyłączyć fragment
kodu przez jego zakomentowanie.

Komentarze jednowierszowe: # i //
Oprócz wielowierszowych komentarzy / * . . . * / możesz na dwa sposoby umiesz-
czać komentarze rozciągające się do końca wiersza. Jeden sposób pochodzi z C++
i Java, drugi z Perl i skryptów powłoki systemowej. Komentarze w stylu skryptów po-
włoki rozpoczynają się od znaku #, natomiast komentarze w stylu C++ od //. Oba te
sposoby komentowania powodują, że reszta bieżącego wiersza jest traktowana jako
komentarz.
tt To jest komentarz
t a to jest druga linia komentarza
// To również jest komentarz. Oba style komentują tylko
// jedna linię. Więc ostatnie słowo tego zdania spowoduje
błąd.

Uważny czytelnik może spostrzec, że jednowierszowe komentarze nie są zgodne z tym,


co powiedzieliśmy wcześniej o ignorowaniu odstępów. Jeżeli zastąpisz jedną ze spacji
w jednowierszowym komentarzu znakiem nowego wiersza, kod przestanie działać.
Bardziej precyzyjne jest określenie, że po usunięciu komentarzy kod PHP jest niewraż-
liwy na liczbę i rodzaj odstępów.
82__________________________________________Część l » Podstawy PHP

Zmienne
Główną metodą przechowywania informacji w środku programu PHP jest użycie zmien-
nych, które są sposobem na nazwanie i zapamiętanie wartości używanych później.

Poniżej przedstawimy najważniejsze informacje o zmiennych PHP (szczegółowy opis


w dalszej części).
* Wszystkie zmienne są rozpoczynane znakiem $.
* Wartością zmiennej jest wynik ostatniego przypisania.
* Wartość zmiennych przypisuje się przy użyciu operatora = ze zmienną po le-
wej stronie, a wyrażeniem po prawej.
* Zmienne nie wymagają deklaracji przed użyciem.
* Zmienne nie mają określonego typu; jest on określany typem bieżącej wartości.
* Zmienne użyte przed przypisaniem maj ą wartości domyślne.

PHP skorzystał ze stylu zmiennych Perl


Wszystkie zmienne w PHP rozpoczynają się znakiem „$" jak zmienne skalarne w języ-
ku Perl, podobnie się też zachowują (nie wymagają deklaracji typu, można się do nich
odwoływać przed przypisaniem itp.). Hakerzy Perl nie muszą nic więcej robić poza
spojrzeniem na nagłówki tej części.

Po początkowym znaku „$" zmienna może składać się z liter (małych bądź wielkich),
cyfr (O - 9) oraz podkreślenia („_")• Pierwszy znak po $ nie może być cyfrą.

Deklarowanie zmiennych
Ten podpunkt umieściliśmy w tym miejscu, ponieważ programiści piszący w innych ję-
zykach mogą go szukać. W językach takich jak C, C++ i Java programista musi zade-
klarować nazwę i typ zmiennej przed jej użyciem. Ponieważ w PHP typy są związane
z wartościami, a nie ze zmiennymi, deklaracja nie jest konieczna; pierwszym krokiem
użycia zmiennej jest przypisanie jej wartości.

Przypisywanie zmiennym wartości


Przypisanie wartości do zmiennej jest proste: napisz nazwę zmiennej, następnie poje-
dynczy znak =, a następnie wyrażenie, którego wartość chcesz przypisać do zmiennej.
Śpi = 3 + 0 . 1 4 1 5 9 ; / / w p r z y b l i ż e n i u

Zauważ, że przypisujesz wynik wyrażenia, a nie samo wyrażenie. Po wykonaniu wyra-


żenia nie ma sposobu na stwierdzenie, że wartość $pi powstała z dodania dwóch liczb.
Rozdział 5. » Składnia, zmienne i wyświetlanie___________________________83

Zmiana wartości zmiennych


Nie ma żadnej różnicy pomiędzy przypisywaniem wartości za pierwszym razem a póź-
niejszą zmianą wartości. Nie zmienia tego nawet fakt przypisywania różnych typów.
Poniższy przykład jest całkowicie prawidłowy:
$wart_num = "To powinna być liczba mam nadzieje, że będzie zmieniona";
$wart_num = 5;

Jeżeli druga instrukcja będzie wykonana bezpośrednio po pierwszej, pierwszy wiersz


nie będzie miał znaczenia.

Nieprzypisane zmienne
Wiele języków programowania sprzeciwia się używaniu zmiennej przed przypisaniem
jej wartości. Inne pozwalają na to, ale mogą odwoływać się do losowej zawartości ob-
szaru pamięci. W PHP domyślne ustawienie raportowania błędów pozwala na używanie
zmiennych bez wcześniejszego przypisania. Zmienne będą miały rozsądne domyślne
wartości.

Jeżeli chcesz dostawać ostrzeżenie o nieprzypisanych zmiennych, po-


winieneś zmienić poziom raportowania błędów na 15. z domyślnego
7. Można to zrobić przez umieszczenie na początku skryptu wyrażenia
error_reporting(i5) lub zmienić to ustawienie w pliku php.ini (patrz
rozdział 32.).

Wartości domyślne
Zmienne w PHP nie mają ustalonego typu, zmienne „nie wiedzą wcześniej", czy będą
przechowywały liczby, czy ciągi znaków. W jaki sposób znana im jest wartość domyśl-
na, skoro nie były jeszcze przypisywane?

Odpowiedź jest taka sama jak przy przypisywaniu zmiennych: typ zmiennej jest ustala-
ny w zależności od kontekstu, w którym jest użyta. W sytuacjach gdy spodziewamy się
liczby, będzie ona liczbą, w innej sytuacji będzie ciągiem znaków. Jeżeli kontekst wy-
maga uznania nieprzypisanej zmiennej za liczbę, zmienna taka będzie traktowana, jakby
miała wartość 0. W innym kontekście, w którym spodziewamy się ciągu znaków, bę-
dzie to ciąg pusty (o długości 0).

Sprawdzanie przypisania za pomocą IsSet


Ponieważ zmienne nie muszą być przypisane przed użyciem, w niektórych sytuacjach
powinieneś wiedzieć, czy zmienna była przypisana, czy nie. W PHP jest funkcja IsSet,
która sprawdza, czy zmienna ma przypisaną wartość.

Poniższy fragment kodu pokazuje, że nieprzypisana zmienna jest rozróżniana od


zmiennej, której nadano wartość domyślną:
JJ4_______________________________________Część l » Podstawy PHP

?set_var - 0; // set_var posiada wartość


// never_set nie posiada
print ("set_var posiada wartość: 5set_var<BR>");
print ("never_set posiada wartość: $never_set<BR>");
if ($set_var -= $never_set)
print("set_var jest równe never_set<BR>");
if (IsSet($set_var))
print("set_var posiada przypisana wartość<BR>");
else
print("set_var nie posiada przypisanej wartości<BR>"};
if (IsSet($never_set))
print("never_set posiada przypisana wartość<BR>");
else
print("never_set nie posiada przypisanej wartości<BR>");

Wynik wykonania tego fragmentu jest następujący:


set_var posiada wartość: O
never_set posiada wartość:
set_var jest równe never_set
set__var posiada przypisaną wartość
never_set nie posiada przypisanej wartości

Zmienna $never__set nie była nigdzie przypisana, więc gdy spodziewany był ciąg zna-
ków, dawała pusty ciąg znaków (w instrukcji print), lub zero, gdy spodziewana była
liczba (jak w porównaniu sprawdzającym, czy zmienne są takie same). Jednak funkcja
IsSet mogła określić różnicę pomiędzy $set_var i $never_set.

Przypisanie wartości do zmiennej nie jest nieodwracalne, funkcja unset {} przywraca


zmienną do postaci nieprzypisanej (np. po wykonaniu unset ($set_var) zmienna
$set_var nie będzie miała przypisanej wartości, niezależnie od wcześniejszych przy-
pisań).

Zasięg zmiennych
Zasięg jest technicznym terminem zasad określających, kiedy nazwa (zmienna lub
funkcja) ma to samo znaczenie w dwóch różnych miejscach i w jakich sytuacjach dwie
identyczne nazwy mogą odwoływać się do różnych rzeczy.

W PHP każda zmienna, która nie jest w ciele funkcji, posiada zasięg globalny i roz-
ciąga się na cały przebieg wykonania. Inaczej mówiąc, jeżeli przypiszesz zmienną na
początku pliku PHP, nazwa tej zmiennej będzie miała takie samo znaczenie i jeżeli
nie zostanie powtórnie przypisana, będzie miała taką samą wartość w całym kodzie
(oprócz ciała funkcji).

Przypisanie wartości nie wpływa na wartości zmiennych o takiej samej nazwie w in-
nych plikach PHP, a nawet na kolejne użycia tego samego pliku. Załóżmy, że mamy
dwa pliki start.php i nastepny.php, które zwykle są odwiedzane w takiej kolejności
przez użytkowników. Załóżmy również, że na początku pliku start.php znajduje się wiersz
$username = "Jan Kowalski";

który jest wykonywana w niektórych sytuacjach. Możesz założyć, że po ustawieniu tej


zmiennej w start.php będzie ustawiona również w nastepny.php, jednak tak nie jest. Za
każdym razem gdy strona jest wykonywana, zmienne są przypisywane, a na końcu stro-
ny znikają.
Rozdział 5. » Składnia, zmienne i wyświetlanie___________________________85

Funkcje i zasięg zmiennych


Poza ciałem funkcji zasięg zmiennych jest dosyć jasny: dla każdego wyko-
nania pliku PHP po prostu przypisujemy zmiennej wartość i wartość ta bę-
dzie dostępna. Jeszcze nie omawialiśmy sposobu definiowania funkcji,
jednak opiszemy zachowanie się zmiennych. Zmienne przypisane w funkcji
działają jako zmienne lokalne funkcji. Jeżeli nie zadeklarujesz funkcji w od-
powiedni sposób, nie będziesz miał dostępu do zmiennych globalnych, nawet
jeżeli były zdefiniowane w tym samym pliku (zasięg zmiennych w funkcjach
omówimy w rozdziale 11.).

Oczywiście w wielu sytuacjach będziesz chciał przechować informacje dłużej niż tylko
na czas wygenerowania jednej strony. Jest kilka sposobów na zrealizowanie tego zada-
nia, opiszemy je w dalszej części książki. Możesz przesyłać dane między stronami, uży-
wając zmiennych GET i POST (rozdział 12.), zapisywać dane w bazie danych (II część
książki), przypisać do sesji użytkownika przy użyciu nowego mechanizmu PHP obsłu-
gującego sesje (rozdział 25.) lub zapisać na dysku użytkownika używając mechanizmu
cookie (rozdział 26.).

Możesz dowolnie zmieniać tryby pracy


Jedno z naszych pierwszych pytań na temat zasięgu brzmiało: czy zmiany trybu pracy
z PHP na HTML i odwrotnie przetrwają. Jeżeli mamy plik wyglądający następująco:
<html>
<head>
<?php
$username = "Jan Kowalski";
?>
</head>
<body>
<?php
print ("$username<BR>");
?>
</body>
</html>

czy mamy oczekiwać, że przypisanie do $username przetrwa do drugiego obszaru


PHP. Odpowiedź brzmi: tak. Zmienne istnieją przez cały proces wykonania PHP (ina-
czej mówiąc, przez cały proces tworzenia strony wysyłanej do użytkownika). Jest to
objaw generalnej zasady PHP, która mówi, że jedynym celem znaczników jest wskaza-
nie maszynie PHP, czy fragment kodu należy interpretować jako tekst, czy przesłać jako
nietknięty HTML. Możesz dowolnie używać znaczników, aby przełączać się pomiędzy
trybami, gdy tego potrzebujesz.
86__________________________________________Część l « Podstawy PHP

Wyjście
Większość konstrukcji języka PHP jest wykonywana w sposób ukryty; nie piszą one nic
na wyjściu. Jedynym sposobem, aby wbudowany kod PHP wyświetlił cokolwiek
w przeglądarce użytkownika jest użycie funkcji piszącej coś do wyjścia lub użycie in-
strukcji print.

Echo i print
Dwoma podstawowymi konstrukcjami drukującymi dane na wyjściu są echo i print.
Ich status w języku jest nieco mylący, ponieważ są podstawowymi konstrukcjami języ-
ka, a nie funkcjami. Dzięki temu mogą być używane z nawiasami, albo bez nich (funk-
cje zawsze mają nazwę, po której następuje otoczona nawiasami lista parametrów).

Echo
Najprostszym sposobem użycia funkcji echo jest wypisanie ciągu podanego jako ar-
gument, np.:
echo "To będzie wyświetlone w oknie przeglądarki,";

lub
echo ("To będzie wyświetlone w oknie przeglądarki.");

Oba te wyrażenia powodują wyświetlenie podanego zdania bez cudzysłowów (uwaga


dla programistów C: myśl o połączeniu HTTP jak o „standardowym strumieniu wyj-
ściowym" dla tych funkcji).

Możesz podać wiele argumentów do wyrażenia echo bez nawiasów, rozdzielając je


przecinkami:
echo "To będzie wyświetlone", " w oknie przeglądarki.";

natomiast wersja z nawiasami nie przyjmie większej liczby argumentów:


echo ("To spowoduje ", "błąd składniowy.");

Print
Instrukcja print jest bardzo podobna do echo, ale:
•* print przyj muj e tylko j eden argument;
* print zwraca wartość wskazującą na to, czy udało się wykonanie instrukcji
print.

Wartość zwracana przez print to l, gdy drukowanie zakończyło się sukcesem, i O, gdy
się nie udało (rzadko się zdarza, że poprawna składniowo instrukcja print nie powie-
dzie się, jednak teoretycznie umożliwia sprawdzenie, czy np. przeglądarka użytkownika
zerwała połączenie). Zarówno echo, jak i print są używane zwykle z ciągami znaków
Rozdział 5. » Składnia, zmienne i wyświetlanie_____________________________87^

jako argumenty, jednak elastyczność PHP w traktowaniu typów pozwala na pobranie


dowolnego typu argumentu bez powodowania błędu. Poniższe wiersze wypisują do-
kładnie to samo:
print("3.14159"); // drukuj ciąg
print(3.14159); // drukuj liczbę

Technicznie rzecz biorąc, print w drugiej linii spodziewał się ciągu znaków jako ar-
gumentu, więc liczba zmiennoprzecinkowa została skonwertowana do ciągu, zanim
print ją dostał. Jednak głównym efektem jest to, że zarówno print, jak i echo do-
skonale drukują liczby i ciągi znaków.

Przez wzgląd na prostotę i jednolitość zwykle używamy print z nawiasami w naszych


przykładach instrukcji.

Zmienne i ciągi
Programiści C są przyzwyczajeni do używania funkcji print f, która pozwala na
wklejenie wartości i wyrażeń do specjalnie sformatowanego ciągu. PHP posiada analo-
giczną funkcję (opiszemy ją w rozdziale 9.), możemy jednak uzyskać ten sam efekt,
używając print (lub echo).

Fragment kodu:
Sanimal = "Antylopa";
$animal_heads = 1;
$animal_legs = 4;
print! "Sanimal ma $animal_heads głowę.<BR>");
print( "$animal ma $animal_legs nogi.<BR>");

da następujący wynik:
Antylopa ma l głowę.
Antylopa ma 4 nogi.

Wartości zmiennych umieszczonych w ciągu zostały wklejone do wyniku. Jest to uży-


teczna własność, która pozwala na szybkie tworzenie zawartości stron WWW, które
zależą od wartości zmiennych. I to nie dzięki własnościom print interpretacja ciągów
jest naprawdę magiczna.

Apostrofy kontra cudzysłowy


PHP wykonuje wstępne przetworzenie ciągów otaczanych cudzysłowami (ciąg o "takiej"
postaci) przed tworzeniem ich wartości. Zmienne są zamieniane na wartości (jak w po-
przednim przykładzie). Aby sprawdzić, jak naprawdę są tworzone ciągi, rozważmy taki
przykład:
$animal = "Antylopa"; // pierwsze przypisanie
$saved_string = "Zwierzę to $animal<BR>";
$animal - "Zebra"; // zmiana wartości
print("Zwierze to $animal<BR>"); // pierwsza linia wydruku
print($saved_string); // druga linia wydruku
88__________________________________________Część l » Podstawy PHP

Jaki otrzymamy wynik? Przeglądarka wyświetli następujący wynik:


Zwierzę to Zebra
Zwierzę to Antylopa

Stało się tak, ponieważ „Antylopa" została wklejona do ciągu $saved_string przed
zmianą wartości zmiennej $ an ima l.

Oprócz wklejania wartości zmiennych do ciągów PHP zamienia niektóre kilkuznakowe


sekwencje sterujące na wartości jednoznakowe. Najczęściej używanąjest sekwencja koń-
ca wiersza („\n"). W wierszu:
"Pierwsza linia \n\n\n Czwarta linia"

PHP zmieni każdy „\n" na znak końca wiersza. Jeżeli obejrzysz źródło strony HTML,
powinieneś zobaczyć kilka pustych wierszy w środku kodu (w oknie przeglądarki wszyst-
ko będzie w jednej linii; przeczytaj „HTML i końce wiersza" w dalszej części rozdziału).

Ciągi otaczane apostrofami (na przykład 'takie'} zachowują się inaczej. PHP nie wyko-
nuje wklejania zmiennych i przetwarza jedynie dwie sekwencje sterujące (możesz wsta-
wić znak ' do środka ciągu, pisząc Y oraz \, pisząc \\). Oprócz tych dwóch wyjątków PHP
interpretuje ciąg dosłownie. Jeżeli do ciągu wstawisz znak $ i wypiszesz go, w przeglą-
darce zobaczysz znak $. Takie traktowanie ciągów może być użyteczne, jeżeli chcesz na
przykład wypisać ścieżkę w stylu Windows. Wyrażenie
print('c:\newcode\php\myphp.php1);

wypisze ścieżkę tak, jak tego chcemy. Wersja z cudzysłowami zinterpretuje pierwszy
backslash jako początek sekwencji sterującej i wstawi znak końca wiersza zaraz za 'c:'.

HTML i końce wierszy


Częstym błędem popełnianym przez nowych programistów PHP (szczególnie tych
z przeszłością C) jest próba złamania wiersza w przeglądarce wstawianiem znaku końca
wiersza („\n") do ciągu. Aby zrozumieć, dlaczego to nie działa, należy zauważyć, że
wyjście programu PHP (które zwykle ma postać kodu HTML, gotowego do wysłania
przez Internet do przeglądarki) jest interpretowane przez przeglądarkę użytkownika.
Większość przeglądarek podejmuje własne decyzje, w jaki sposób złamać linie tekstu,
chyba że wymusimy to za pomocą znacznika <BR>. Znaki końca wiersza w ciągach bę-
dą umieszczone w źródle strony wysyłanej do użytkownika, ale zwykle nie dają wi-
docznego efektu w wyglądzie tekstu na stronie WWW.

Podsumowanie
Kod PHP jest zgodny z kilkoma zasadami składniowymi, w większości zapożyczonymi
z języków C i Perl. Wymagania składniowe są minimalne; PHP zwykle próbuje wy-
świetlać wynik.
Rozdział 5. » Składnia, zmienne i wyświetlanie___________________________89

Kod PHP jest niezależny od liczby odstępów, w nazwach zmiennych są rozróżniane


wielkie i małe litery. Wielkość liter nie ma znaczenia dla konstrukcji języka i nazw
funkcji. Proste wyrażenia są łączone w większe za pomocą operatorów i wywołań funk-
cji. Wyrażenia zakończone średnikiem są instrukcjami. Zmienne są odróżniane po po-
czątkowym znaku S; do przypisania używany jest operator =. Nie ma potrzeby
deklarowania typów; zmienne zawsze mają domyślne wartości w przypadku ich użycia
przed pierwszym przypisaniem. Zmienne mają zasięg globalny oprócz ciała funkcji,
w którym zmienne są lokalne, chyba że zdecydujemy inaczej.

Najprostszą metodą wysłania danych na wyjście jest użycie funkcji echo lub print,
które wypisują ciąg przekazywany jako argument. Są one dosyć użyteczne, szczególnie
w połączeniu z ciągami w cudzysłowach, które automatycznie modyfikują zmienne na
ich wartości.
90
Część l » Podstawy PHP
Rozdział 6.
Typy w PHP
W tym rozdziale:
4 Poznajemy sześć typów w PHP: integer, double, boolean, string, array i object
4 Tworzenie, czytanie, drukowanie i manipulacja obiektami różnych typów
4 Konwersja z jednego typu do innego

Wszystkie języki programowania mają jakiś rodzaj systemu typów, który definiuje różne
rodzaje wartości, jakie mogą pojawiać się w programie. Typy często korespondują
z różną reprezentacją bitową w pamięci komputera, jednak w wielu przypadkach pro-
gramiści nie muszą myśleć o reprezentacji na poziomie bitów. System typów PHP jest
prosty, wydajny, elastyczny; izoluje od niskopoziomowych szczegółów.

Rozdział ten opisuje podstawowe typy PHP (integer, double, boolean, string, array i object)
i pokazuje, w jaki sposób są one odczytywane, drukowane, przypisywane do zmiennych
konwertowane i łączone. Rozdział ten stanowi zarówno przegląd, jak i podręcznik:
zaznajomieni z programowaniem mogą go opuścić, mniej zaawansowani powinni prze-
czytać początkowe fragmenty; ale doń wracać, aby zgłębić te szczegóły, które nie wy-
dawały się ważne za pierwszym razem.

Pierwsza zasada: nie przejmuj się


PHP jest tak prosty, że nie musisz się martwić o typy zmiennych, ponieważ nie tylko
nie wymaga określania typu zmiennych, ale także wykonuje wiele typowych konwersji
za ciebie.

Brak deklaracji typów zmiennych


Jak stwierdziliśmy w poprzednim rozdziale, nie ma potrzeby deklarowania typów
zmiennych. W zamian programista może od razu przejść do przypisania i pozwolić PHP
zorientować się, jakiego typu jest przypisywane wyrażenie.
92__________________________________________Część l * Podstawy PHP

$first_number = 55.5;
$second_number = "To nie liczba";

Automatyczna konwersja typów


PHP automatycznie konwertuje typy, gdy jest to potrzebne. Jak inne nowoczesne języki
programowania, PHP dokona odpowiedniej konwersji, gdy np. wykonujemy operację
matematyczną na różnych typach numerycznych. Wynik wyrażenia
$pi = 3 + 0 . 1 4 1 5 9

będzie liczbą zmiennoprzecinkową (double), z liczbą 3 niejawnie skonwertowaną na


liczbę zmiennoprzecinkową przed wykonaniem dodawania.

Typy nadawane poprzez kontekst


PHP idzie dalej, niż większość języków programowania, w stosowaniu automatycznej
konwersji typów. Rozważmy przykład:
Ssub = s u b s t r ( 1 2 3 4 5 , 2 , 2 ) ;
p r i n t ( " C i ą g to $ s u b < B R > " ) ;

Funkcja substr jest zaprojektowana do pobierania fragmentu ciągu, od punktu startowego


i długości określanej przez ostatnie dwa parametry funkcji. Zamiast operować na ciągu
znaków, wpisaliśmy liczbę 12345. Co się stało? Nie nastąpił błąd, a w przeglądarce do-
staliśmy wynik:
Ciąg to 34

Ponieważ substr spodziewa się ciągu znaków, a nie liczby, PHP skonwertuje dla nas
liczbę 12345 do postaci ciągu znaków '12345'.

Z powodu automatycznej konwersji typów ciężko jest zmusić PHP do zwrócenia błędu
typu. Jednak programiści PHP muszą upewnić się, że konwersje niepowodujące błędów
nie dadzą nieoczekiwanych wyników.

Typy w PHP
PHP posiada tylko sześć typów danych: integer, double, boolean, string, array i object.
•* integer to liczby całkowite bez przecinka, na przykład 4 9 5 .
• Double to liczby zmiennoprzecinkowe: 3 .14159 lub 4 9 . 0 .
•* Boolean ma tylko dwie wartości: TRUE i FALSE.
•* String to ciąg znaków, np.'PHP 4.0 obsługuje operacje na ciągach'.

• Array to nazwana i zindeksowana kolekcja wartości.


Rozdział 6. » Typy w PHP_______________________________________93

•* Object to egzemplarz zdefiniowanej przez programistę klasy, która agreguje


wartości i funkcje na nich operujące.
Pierwsze pięć z nich to typy proste, natomiast ostatnie dwa są złożone. Typy
złożone mogą zawierać dowolne wartości dowolnych typów, w przeciwień-
stwie do typów prostych. W tym rozdziale nie będziemy się zagłębiać w typy
złożone, zajmiemy się nimi w oddzielnym rozdziale.

Typy proste
Proste typy danych powinny być znane dla wszystkich, który już wcześniej programowali.
Jedyną rzeczą, jaka zaskoczy programistów C, jest niewielka liczba typów.

Wiele języków programowania ma kilka różnych rozmiarów typów numerycznych,


największy z nich zapewnia duży zakres wartości, ale zajmuje dużo miejsca w pamięci.
Dla przykładu, język C posiada typ short (dla relatywnie małych liczb), long (dla du-
żych liczb) oraz int (który powinien być pośrodku, ale zwykle jest identyczny z jed-
nym z short lub long). Ten rodzaj wyboru typów miał sens w czasach, gdy rozdział
pomiędzy pamięcią i dostępnymi funkcjami był szczególnie widoczny. Projektanci PHP
podjęli dobrą decyzję o uproszczeniu typów i pozostawieniu tylko dwóch typów nume-
rycznych, odpowiadających największemu typowi całkowitemu oraz zmiennoprzecin-
kowemu w C.

Integer
Integer to najprostszy typ; odpowiada on prostym liczbom całkowitym zarówno, do-
datnim, jak i ujemnym. Dane tego typu można przypisywać do zmiennych lub używać
w wyrażeniach.
$int_var = 12345;
$inny_int = -12345 + 12345; // równe zero

Formaty zapisu
Zmienne numeryczne mogą być zapisywane w trzech postaciach: dziesiętnej, oktalnej
i szesnastkowej. Format dziesiętny jest formatem domyślnym, liczby oktalne oznaczane
są przez dodanie początkowego 'O', a liczby szesnastkowe rozpoczynają się od 'Ox'.
Każdy z tych formatów może być poprzedzony znakiem '-' w celu uzyskania liczby
ujemnej. Na przykład:
$integer_10 = 1000;
$integer_8 = -01000;
$integer_16 = 0x1000;
print("integer_10: $integer_10<BR>") ;
print("integer_8: $integer_8<BR>");
print("integer_16: $integer_16<BR>");

daje w przeglądarce:
94__________________________________________Część l » Podstawy PHP

integer_10: 1000
integer_8: -512
integer_16: 4096

Format wpływa tylko na to, w jaki sposób liczba zostanie skonwertowana w trakcie od-
czytu, wartość zapisana w zmiennej $integer_8 nie pamięta, że była liczbą ósemkową.
Wewnętrznie liczby te są zapisywane w postaci binarnej, ale widzimy je skonwertowane
do postaci dziesiętnej, ponieważ takie jest domyślne ustawienie dla konwertowania liczb
całkowitych na ciągi.

Rozmiar
Jak duże (lub małe) mogą być liczby typu integer? Ponieważ typ ten odpowiada ty-
powi long w C, który zależy od długości słowa maszyny, odpowiedź na to pytanie
jest trudna. Dla typowych maszyn największa liczba całkowita to 2 31 - l (lub 2 147
483 647), najmniejsza to -(231 - 1) (lub -2 147 483 647).

W PHP nie ma stałej (jak MAXINT w C) określającej największą liczbę typu integer.
Na końcu tego rozdziału zamieściliśmy program określający tę liczbę. Jeżeli potrzebu-
jesz naprawdę dużych liczb, PHP ma kilka funkcji o dowolnej dokładności (opisane
w części BC rozdziału 10.).

Double
Typ double to liczby zmiennoprzecinkowe, takie jak:
Sfirst_double = 123.456;
$second_double = 0.456;
Seven_double = 2.0;

Zauważ, zmienna $even_double jest liczbą „okrągłą", nie znaczy to jednak, że całko-
witą. Liczby integer i double zapisywane sąw różnych postaciach binarnych. Wynik
wyrażenia
$five = $even_double + 3;

jest liczbą double, a nie integer. W prawie wszystkich sytuacjach będziesz mógł
mieszać w wyrażeniach matematycznych dowolnie liczby double oraz integer i po-
zwalać PHP na konwersję typów.

Domyślnie liczby double są drukowane z minimalną możliwą liczbą cyfr po przecin-


ku; na przykład:
Smany = 2.2888800;
$many_2 = 2.2111200;
$few = $many + $many_2;
p r i n t ( " $ m a n y + $many_2 = $ f e w < B R > " ) ;

da w wyniku:
2 . 2 8 8 8 8 + 2.21112 = 4 . 5
Rozdział 6. » Typy w PHP_________________________________________95^

Jeżeli potrzebujesz precyzyjnego sterowania drukowaniem, zapoznaj


się z funkcją printf opisaną w rozdziale 9.

Formaty zapisu

Typowym formatem zapisu liczb double jest -x. Y, gdzie - określa liczbę ujemną, na-
tomiast x i Y są sekwencjami cyfr pomiędzy O a 9. Część x lub część Y może zostać
opuszczona. Początkowe i końcowe zera nie mają znaczenia. Wszystkie poniższe przy-
kłady są prawidłowymi liczbami double:
$sraall_positive = 0.12345;
$small_negative = -.12345;
$even_double = 2.000000;
$still_double = 2.;

Można dodatkowo używać notacji naukowej, dodając literę e i żądaną potęgę liczby 10
na końcu przedstawionego przed chwilą formatu. Na przykład 2.2e-3 oznacza
2 . 2 * 1 0 - 3 . Zmiennoprzecinkowa część tego formatu nie musi być ograniczana do liczb
z zakresu 1,0 i 10,0. Wszystkie poniższe przykłady są prawidłowe:
$small_positive = 5.5e-3;
print("small_positive = $small_positive<BR>"};
$large_positive = 2.8e+16;
print("large_positive - $large_positive<BR>");
$small_negative = -2222e-10;
print("small_negative = $small_negative<BR>") ;
$large_negative = -0.001189e6;
print("large_negative = Slarge_negative<BR>");

i daj ą następujące wyniki:


small_positive = 0.0055
large_positive = 2.8E-H6
sraall_negative = -2.222E-07
large_negative = -1890

Zauważ, że zmienna nie pamięta, czy była podana jako liczba w notacji naukowej.
W czasie drukowania liczb PHP podejmuje decyzję, czy użyć formatu naukowego do wy-
świetlania dużych liczb, ale nie ma to związku z oryginalnym formatem zapisu tej liczby.

Boolean
Typ boolean posiada tylko wartości: prawda i fałsz, które są używane do tworzenia
struktur sterujących, takich jak wyrażenia „testujące" w instrukcji if. W następnym roz-
dziale opiszemy, w jaki sposób można za pomocą operatorów łączyć wartości logiczne
w bardziej skomplikowane wyrażenia.

Prawdziwy typ boolean jest nowością w PHP 4. PHP 3 nie miał osobnego typu bo-
olean, a zamiast tego traktował określone wartości innych typów jako TRUE lub FALSE
(podejście takie jest znane hakerom Perl). Różnica ta nie jest wielka, ponieważ nadal
możesz używać innych typów w kontekście logicznym, a PHP 4 wykonuje automatycz-
ną konwersję typów (na końcu rozdziału wypisaliśmy kilka przypadków, kiedy zacho-
wanie PHP 3 i PHP 4 może się różnić).
96__________________________________________Część l » Podstawy PHP

Stałe boolean
PHP posiada parę stałych logicznych: TRUE i FALSE, które można używać w sposób na-
stępujący:
if (TRUE)
print("To będzie zawsze wydrukowane<BR>") ;
else
print("To nie będzie nigdy wydrukowanę<BR>");

Interpretowanie innych typów jako boolean


Można określić „prawdziwość" dowolnej wartości, która nie jest typem boolean we-
dług następujących zasad:
1. Jeżeli jest to liczba, zero odpowiada FALSE, każda inna wartość TRUE.
2. Jeżeli jest to ciąg znaków, pusty ciąg odpowiada FALSE, w przeciwnym wy-
padku TRUE.
3. Wartość typu złożonego (tablica lub obiekt) jest FALSE, jeżeli nie zawiera war-
tości, w przeciwnym wypadku — TRUE.

Przykłady
Każda z poniższych zmiennych, użyta jako wyrażenie logiczne, posiada wartość lo-
giczną określoną przez ich nazwę.
$true_num = 3 + 0.14159;
$true_str = "Zmęciony i prawdziwy";
$true_array[49) = "Element tablicy"; // opis w następnej części
$false_num = 999 - 999;
$false_str = ""; // ciąg o długości O

Nie używaj liczb double jako warunków logicznych


Mimo, że zasada 1. mówi, że liczba double 0,0 jest konwertowana do wartości bo-
olean — false, z powodów możliwych błędów zaokrągleń niebezpiecznie jest uży-
wać liczb double jako wyrażeń logicznych. Na przykład:
$floatbool = sqrt(2.0) * sqrt(2.0) - 2.0;
if ($floatbool)
print("Zmiennoprzecinkowe wyrażenia boolean są niebezpieczne!<BR>");
else
print("Działa ... tym razem.<BR>") ;
print ("Wartość aktualna to $floatbool<BR>");

Zmiennej $floatbool przypisany jest wynik odejmowania iloczynu pierwiastków


z dwóch od liczby dwa. Wynik powinien być równy zero, co oznacza, że $f loatbool
ma wartość logiczną false. Zamiast tego przeglądarka pokaże:
Zmiennoprzecinkowe wyrażenia boolean są niebezpieczne!
Wartość aktualna to 4.4408920985006E-16
Rozdział 6. » Typy w PHP_________________________________________97

Wartość $floatbool jest bardzo bliska 0,0, jednak nie jest to dokładnie zero i dlatego
jego wartością logicznąjest true. Liczby integer są o wiele bezpieczniejsze jako war-
tości boolean; tak długo, jak wynik działania jest liczbą całkowitą, nie trzeba obawiać
się błędów zaokrągleń.

Typ boolean w PHP 3 i PHP 4


Ponieważ PHP 3 nie ma niezależnego typu boolean, prawdziwe wartości stałych TRUE
i FALSE są innych typów. Pamiętaj, że używanie tych stałych w innym kontekście niż
w wyrażeniach logicznych prowadzi do niekompatybilności z PHP 4. Dobrą zasadą
nieporównywanie wartości do stałej logicznej za pomocą operatora =, lecz użycie samej
wartości zmiennej jako warunku logicznego. Dla przykładu załóżmy, że wynik twojej
funkcji przypisany jest do zmiennej $truth_value. Zmienna ta może być liczbą lub
ciągiem, ale zawsze ma może być skonwertowana do zmiennej logicznej według zasad
opisanych wcześniej. Jaki jest prawidłowy sposób użycia tej zmiennej? Prawidłowym
sposobem, który będzie działał zarówno w PHP 3, jak i PHP 4 jest:
if (Struth_value) // dobry styl PHP! Bezpieczny w PHP 3 i PHP 4
print("Wartością truth_value jest TRUE<BR>");

Nieprawidłowy sposób użycia:


if ($truth_value == TRUE) // niedobry styl PHP! Porównanie z boolean
print("Wartością truth_value jest TRUE<BR>");

Zachowanie się PHP 3 i PHP 4 może być różne. Jeżeli $truth_value będzie równe
np. 3, PHP 4 skonwertuje jądo wartości logicznej TRUE przed porównaniem, natomiast
PHP 3 porówna 3 z aktualną wartością stałej TRUE i test da prawdopodobnie wartość
FALSE.

String
string służy do zapamiętania sekwencji znaków:
$string_l = "Ciąg znaków w cudzysłowach.";
$string_2 = 'Nieco dłuższy ciąg znaków w apostrofach';
$string_39 = " Ciąg ma trzydzieści dziewięć znaków. ";
Sstring_0 = ""; // Ciąg pusty

Ciągi mogą być zamykane apostrofami lub cudzysłowami, które mają działają inaczej
w programie. Ciągi z apostrofami są traktowane nieomal dosłownie, natomiast w cią-
gach z cudzysłowami nazwy zmiennych są zamieniane na ich wartości i interpretowane
są sekwencje sterujące.

Ciągi w apostrofach
Oprócz pary znaków specjalnych, ciągi w apostrofach są odczytywane i przechowywa-
ne dosłownie. Poniższy przykład:
Sdoslownie = 'Ta Szmienna nie zostanie wydrukowana!\n';
print(Sdoslownie);
98__________________________________________Część l » Podstawy PHP

da w przeglądarce następujący wynik:


Ta $zmienna nie zostanie wydrukowana!\n

Ciągi w apostrofach działają zgodnie z ogólną zasadą, że apostrofy innego typu nie po-
dzielą otaczanego tekstu. To jest prawidłowy zapis:
$apostrofy = 'Ten znak " nie jest problemem';

Aby wbudować w ciąg pojedynczy apostrof, należy poprzedzić go znakiem \:


Sapostrofy = 'Ten apostrof \' również nie stanowi problemu';

Mimo że w większości przypadków backslash jest interpretowany dosłownie w ciągach


zamkniętych apostrofami, możesz użyć dwóch takich znaków (\\) jako sekwencji steru-
jącej dającej pojedynczy backslash. Jest to użyteczne w przypadku, gdy chcemy ten
znak umieścić na końcu ciągu.
$win_path = 'c: \\InetPub\\PHP\V ;
print("Ścieżka w stylu Windows: $win_path<BR>");

Przykład ten da w wyniku:


Ścieżka w stylu Windows: c:\InetPub\PHP\

Mogliśmy użyć pojedynczych znaków backslash w dwóch pierwszych


wystąpieniach, ale musieliśmy użyć sekwencji \\ w ostatnim wystą-
pieniu, aby nie została zinterpretowana sekwencja Y.

Te dwie sekwencje sterujące sąjedynym wyjątkiem od dosłownego traktowania ciągów


w apostrofach.

Ciągi w cudzysłowach
Ciągi otaczane znakami cudzysłowu (jak w "tym" przypadku) są przez PHP przetwarza-
ne według dwóch zasad:
1. Odpowiednie sekwencje sterujące rozpoczynające się znakiem \ są zamieniane
na znaki specjalne.
2. Nazwy zmiennych są zastępowane przez ich wartość, zamienioną na ciąg znaków.

Sekwencjami sterującymi są:


\n zamieniane na znak nowej linii;
\r zamieniane na znak powrotu karetki (CR);
\t zamieniane na znak tabulacji;
\$ zamieniane na znak dolara (S);
\" zamieniane na cudzysłów (");
\ \ zamieniane na \.
Rozdział 6. » Typy w PHP_________________________________________99_

Pierwsze trzy znaki umożliwiają wprowadzenie odstępów do ciągów. Sekwencja \$ po-


zwala na wstawienie do ciągu znaku $, gdy nie chcemy, aby był interpretowany jako
początek zmiennej. Sekwencja \" pozwala na wstawienie do ciągu cudzysłowu bez za-
kończenia go. Ostatnia sekwencja sterująca pozwala na wstawienie do ciągu znaku \,
który rozpoczyna wszystkie sekwencje sterujące.

Podobnie jak dla ciągów w apostrofach, terminator innego typu może być bezpiecznie
wstawiany do ciągu bez sekwencji sterującej:
$ma_apostrof = "Nie ma problemu 'z* apostrofami";

Interpretacja zmiennych
Gdziekolwiek wystąpi w ciągu znak $, PHP stara się określić, jaka zmienna znajduje się
za znakiem $ i wstawić w to miejsce wartość zmiennej. W zależności od tego, jak jest
ustawiona zmienna, może się to odbywać na kilka sposobów:
* Jeżeli do zmiennej jest przypisany ciąg znaków, jest ona wstawiana (lub wkle-
jana) do ciągu w cudzysłowach.
* Jeżeli zmienna zawiera wartość inną niż ciąg znaków, wartość ta jest konwer-
towana do postaci wklejanego ciągu.
** Jeżeli zmienna nie posiada wartości, jest zamieniana na pusty ciąg (PHP wkleja
pusty ciąg).

Na przykład:
Sto = "to";
$tamto = "tamto";
$cos_innego = 2.2000000000;
print("Sto, $nie_ustawiona, $tamto+$cos_innego<BR>");

da w wyniku:
to, ,tamto+2.2<BR>

który w przeglądarce wygląda następująco:


to, ,tamto+2.2

Prześledźmy, co dokładnie robi PHP, analizując ciąg w instrukcji print. Zauważ, że


w ciągu tym znajdują się cztery znaki $, które są interpretowane jako początek nazwy
zmiennej. Nazwy zmiennych kończą się pierwszym znakiem, który nie może wystąpić
w nazwie zmiennej. Dopuszczalnymi znakami są litery, cyfry i podkreślenie. Znaki
kończące nazwy zmiennych w ciągu z przykładu to kolejne dwa przecinki, znak plus
i lewy nawias trójkątny (<). Pierwsze dwie zmienne mają przypisane ciągi (to i tam-
to), więc są wklejone dosłownie. Następna zmienna ($nie_ustawiona) nie była
wcześniej przypisana, więc nie jest brana pod uwagę przy tworzeniu ciągu. I ostatnia
zmienna ($cos_innego), rozpoznana jako liczba zmiennoprzecinkowa. Jej wartość jest
skonwertowana do ciągu (2 .2) i ciąg ten jest wklejany do ciągu wynikowego.

Więcej informacji o konwertowaniu liczb do ciągów znajduje się w czę-


ści „Przypisania i konwersje", poniżej.
100_________________________________________Część l » Podstawy PHP

Jak wcześniej wspomnieliśmy, interpretacja ciągów zachodzi w momencie ich odczyty-


wania, a nie w momencie drukowania. Jeżeli zapiszemy przykładowy ciąg w zmiennej
i wydrukujemy go później, to będzie on odzwierciedlał wartości zmiennych z momentu
przypisania, nawet jeżeli będą zmodyfikowane.

Znaki końca wiersza w ciągach


Mimo że PHP posiada sekwencję sterującą (\n) zamienianą na znak końca wiersza, do-
brze jest wiedzieć, że identycznie traktuje złamanie wiersza w ciągu. Jest to wygodne,
gdy tworzymy ciąg HTML, ponieważ przeglądarka ignoruje znaki końca wiersza, mo-
żemy więc formatować nasz ciąg za pomocą znaków końca wiersza.
print ("<HTMLXHEADX/HEAD><BODY>Ta strona HTML jest zbyt duża
aby zmieścić się w pojedynczej linii, jednak nie oznacza to,
że potrzebujemy wielu instrukcji print !</BODYX/HTML>" );

Utworzyliśmy tę instrukcję w edytorze, naciskając Enter na końcu dwóch pierwszych


wierszy. Znaki nowego wiersza zostały wstawione do ciągu, więc pojedyncza instrukcja
print utworzy trzy osobne wiersze. Przeglądarka zignoruje je, decydując, kiedy i czy
należy złamać wiersz.

Ograniczenia
Nie ma sztucznych ograniczeń długości ciągu. W granicach dostępnej pamięci możesz
tworzyć ciągi dowolnej długości.

Tablice
Typ tablicowy pozwala na grupowanie różnych wartości i dostępu do nich dzięki warto-
ści indeksu (również dzięki nazwie, ale o tym później). Jeżeli zdarzyło ci się korzystać
ze zmiennych Szmiennal, $zmienna2, $zmienna3 itd., możesz używać w ich miej-
sce tablicy ($zmienna [1], $ z m i e n n a [ 2 ] , $ z m i e n n a [ 3 ] , ...). Do elementów tablicy
można się odwoływać dzięki indeksowi w nawiasach kwadratowych ( [ 1 ] , [ 2 ] , [3]
itd.). Do tablicy PHP można przypisywać wartości różnych typów.

Poniżej zamieściliśmy kilka przykładów, które sprawdzają wartości elementów tablicy


przed i po przypisaniu:
print{"tabl ma wartość $tabl<BR>");
print("tabl[0] ma wartość Stabl[0]<BR>");
print("tabl[5] ma wartość Stabl[5]<BR>");
$tabl[5] = "Element #6";
print("tabl ma wartość $tabl<BR>");
print("tabl[0] ma wartość Stabl[0]<BR>");
print("tabl[5] ma wartość Stabl[5]<BR>");

Wynik wykonania tego fragmentu:


tabl ma wartość
tabl[0] ma wartość
tabl[5] ma wartość
Rozdział 6. » Typy w PHP______________________________________101

tabl ma wartość Array


tabl[0] ma wartość
tabl [5] ma wartość Element (t6

Przed pierwszym przypisaniem PHP nie wiedział, że zmienna $ tab l jest przeznaczona
na tablicę; była to po prostu niezainicjowana zmienna. Została wydrukowana jako pusty
ciąg. Również odwołanie do zerowego i piątego elementu jest traktowane identycznie
jak niezainicjowana zmienna. Wynik działania pierwszych trzech instrukcji print koń-
czy się więc na wyrazie wartość.

Po wykonaniu przypisania zmienna $tabl jest teraz oficjalną tablicą, w wyniku umiesz-
czenia jej nazwy w ciągu zwrócony zostanie predefiniowany ciąg Array. Element tabli-
cy o indeksie 5 zawiera ciąg Element # 6 (jak w większości języków, elementy tablicy
są numerowane od 0) i mamy do niego dostęp poprzez podanie tego indeksu. To jest je-
dyna zmiana, element zerowy pozostaje nadal niezainicjowana zmienną.

Implementacja tablic
Tablice są jedną z najbardziej użytecznych funkcji w PHP i mimo że wyglądają tak sa-
mo jak tablice w innych językach, są implementowane w zupełnie inny sposób.

W większości języków programowania deklaruje się tablice za pomocą wyrażenia po-


dobnego do:
mt int_array[10] ; //to NIE jest PHP

Deklaracja taka rezerwuje blok pamięci na 10 liczb całkowitych, do których można od-
woływać się poprzez indeksy od O do 9.

W PHP tablice są asocjacyjne; gdy przypisujesz wartość do elementu tablicy, dodajesz


jednocześnie do tablicy miejsce na element związany z indeksem, za pomocą którego
można się odwołać do tej wartości (jest to mechanizm podobny do tablic mieszają-
cych — tablice w PHP to raczej tablice mieszające, a nie klasyczne).

Jedną z konsekwencji tego mechanizmu jest to, że nie musisz się obawiać przypisywa-
nia wartości do elementów tablicy o bardzo dużych indeksach:
Stablica[100000000] = "Nie obawiaj się"; // to jest OK

Wykonanie tej linii nie zarezerwuje 100 milionów elementów. Elementy o niższych in-
deksach prawdopodobnie jeszcze nie istnieją, więc nie zajmuj ą pamięci.

Ciągi znaków jako indeksy tablicy


Do tej pory używaliśmy liczb całkowitych jako indeksów. Możemy również korzystać
z ciągów znaków, np.:
$smakolyk["Hiszpański"] = "paella";
$smakolyk["Japoński"] - "sashimi";
Ssmakolyk["Szkocki"] = "haggis?";
102_________________________________________Część l « Podstawy PHP

Takich indeksów używa się identycznie jak indeksów numerycznych. Indeksy nume-
ryczne i ciągi znaków mogą być bez obaw mieszane w tej samej tablicy.

Czy w PHP są struktury?


Niektóre języki programowania (C, Pascal) pozwalają na używanie „struktur" bądź „re-
kordów", które umożliwiają łączenie ze sobą zmiennych różnych typów. W tych języ-
kach programowania podstawowa zasada wyboru typu złożonego brzmi: jeżeli wartości
są tych samych typów, używani tablicy, w przeciwnym przypadku używam struktury.

PHP ma typ obiektowy, który może posłużyć jako rekord lub struktura. Nawet przed
wprowadzeniem typu obiektowego PHP nie będzie potrzebował struktur, ponieważ ta-
blice nie są ograniczone do przechowywania elementów jednego typu. Jeżeli przenosisz
program z innego języka do PHP i masz w kodzie struktury, możesz użyć tablic indek-
sowanych ciągami odpowiadającymi nazwom pól struktury.

Inne własności tablic


W tym rozdziale przedstawiliśmy jedynie podstawowe własności tablic. Tablice dodatko-
wo mogą być wielowymiarowe, ich wartości mogą być przypisywane różnymi metodami.
Istnieje wiele funkcji związanych z tablicami pozwalających na łatwe sprawdzanie za-
wartości tablic i manipulowanie nimi. Wrócimy do tego w rozdziale 11.

Obiekty
Ostatnim z pięciu typów PHP jest typ obiektowy, wprowadzający PHP do modnego
świata programowania obiektowego (OOP). W rozdziale tym podamy tylko krótkie
wprowadzenie do koncepcji i składni programowania obiektowego w PHP, pełny opis
znajdziesz w rozdziale 33.

Przegląd OOP
Używając podejścia obiektowego programista definiuje nowe jednostki zwane klasami.
Każda klasa jest nowym typem danych. Po zdefiniowaniu klasy można utworzyć do-
wolną liczbę obiektów będącymi egzemplarzami bądź instancjami klasy.

Proces jest podobny do definiowania struktur lub rekordów w językach C lub Pascal.
Programowanie obiektowe idzie jednak dalej, dołączając jedną lub więcej z następują-
cych własności:
* Metody: oprócz pól danych obiekty mogą posiadać metody (funkcje definio-
wane na rzecz konkretnej klasy).
Rozdział 6. » Typy w PHP_________________________________________103

* Dziedziczenie: klasa może dziedziczyć po innej klasie, co spowoduje, że będzie


mieć wszystkie pola i metody z klasy będącej jej przodkiem. Odziedziczone
atrybuty mogą być rozszerzane (poprzez dodanie nowych pól danych i metod)
lub przesłaniane (przez ponowną definicję odziedziczonego atrybutu).
* Hermetyzacja: definicja klasy może określać, które atrybuty będą dostępne dla
kodu nie będącego kodem klasy.
* Polimorfizm: zachowanie metody może różnić się w zależności od liczby i typu
argumentów wywołania.

Jak bardzo obiektowy jest PHP?


PHP posiada ograniczoną implementację programowania obiektowego. Pozwala na de-
finicję klas i metod oraz dziedziczenie, jednak nie zezwala na dziedziczenie wielobazo-
we oraz prawdziwą hermetyzację, a w ograniczonym zakresie pozwala na polimorfizm.
Chociaż programiści znający C++, Java, Smalltalk lub Common Lisp będą tęsknić za
subtelnościami i zaawansowanymi możliwościami tych systemów, jednakże system
obiektów PHP jest niezmiernie użyteczny.

Definiowanie klas w PHP


Wydruk 6.1 zawiera przykładową definicję klasy w PHP. Klasa tworzy stos, z metoda-
mi push ( ) (położenie elementu na stos) i pop ( ) (usunięcie elementu ze stosu). Dodat-
kowo stos przechowuje tylko liczby całkowite i nie pozwala na wstawienie elementu
innego typu.

Oprócz konstrukcji obiektowych przykład ten używa kilku własności


PHP, które nie były jeszcze omawiane. Jeżeli czegoś nie rozumiesz,
opuść tę część i wróć po przeczytaniu rozdziału 30.

Wydruk 6.1. Definicja prostej klasy w PHP___________________________________


class Intstack
(
/* Stos ograniczony do liczb całkowitych */
var $the_stack;
var Scount = 0;

function push ($intvar)


{
if (is_integer($intvar})
<
$this->the_stack[$this->count] = $intvar; // wstawienie do stosu
$this->count++; // zwiększenie licznika
print! "Wstawienie Sintvar udane.<BR>");
}
else
print! "Intstack przyjmuje tylko liczby całkowite!<BR>");
}
104_________________________________________Część l « Podstawy PHP

function pop()
(
if ($this->count>0)
f
$this->count--;
Stop = $this->the_stack[$this->count];
return ($top);
)
else
print("Stos jest pusty!<BR>");
)
I

Klasa intstack ma dwie zmienne ($the_stack i $count) oraz dwie metody (push ( )
i pop ( ) ) . Zmienne zostały zadeklarowane za pomocą słowa kluczowego var, a defini-
cja metod jest podobna do definicji zwykłej funkcji, oprócz tego, że znajdują się w ciele
klasy. Metody mogą odwoływać się do zmiennych klasy dzięki specjalnej zmiennej
$this, która wskazuje na obiekt.

Tworzenie obiektów
Po zdefiniowaniu klasy można tworzyć obiekty za pomocą słowa kluczowego new po-
przedzającego nazwę klasy. Metody wywołujemy używając operatora -> na wyniko-
wym obiekcie. Poniżej przykład użycia klasy Intstack zdefiniowanej na wydruku 6.1.
$my_stack = new Intstack;
$my_stack->push(l J ;
$my_stack->push(49) ;
$my_stack->push("To nie podziała");
$pop_result = $my_stack->pop() ;
print("Na górze stosu było $pop_result<BR>");
Spop_result = $my_stack->pop();
print("Na górze stosu było $pop_result<BR>");
$pop_result = $my_stack->pop();

Wynikiem tego fragmentu kodu jest:


Wstawienie l udane.
Wstawienie 49 udane.
Intstack przyjmuje tylko liczby całkowite!
Na górze stosu było 49
Na górze stosu było l
Stos jest pusty!

Kontrola typów
Ponieważ zmienne mogą zmieniać typy po przypisaniach, czasami niezbędne jest
sprawdzenie typu zmiennej w czasie działania programu. PHP posiada zarówno ogólną
funkcję kontrolującą typ (gettype ( ) ) , jak i indywidualne funkcje logiczne dla każdego
z pięciu typów. Funkcje te wraz z ich alternatywnymi nazwami są zebrane w tabeli 6.1.
Rozdział 6. » Typy w PHP_________________________________________105

Tabela 6.1.
Funkcje kontroli typów

Funkcja Działanie

gettype ( a r g ) Zwraca ciąg określający typ argumentu: integer, double, string, array,
object lub unknown type

is_int (arg) Zwraca true, jeżeli arg jest liczbą całkowitą, false, jeżeli nie jest
is_integer(arg)
is_long(arg)
is_double ( a r g ) Zwraca true, jeżeli arg jest liczbą zmiennoprzecinkową, false, jeżeli nie jest
is_float(arg)
is_real(arg)

is_bool ( a r g ) Zwraca true, jeżeli arg jest wartością logiczną (TRUE lub FALSE),
false, jeżeli nie jest
is_string ( a r g ) Zwraca true, jeżeli arg jest ciągiem znaków, f a l s e , jeżeli nie jest
is_array ( a r g ) Zwraca true, jeżeli arg jest tablicą, false, jeżeli nie jest
is_object (arg) Zwraca true, jeżeli arg jest obiektem, false, jeżeli nie jest

Przypisania i konwersje
PHP często konwertuje wartości z jednego typu na inny, wymagany w bieżącym zasto-
sowaniu. Programista może również zażądać niektórych z takich konwersji.

Działanie konwersji typów


Zasady konwersji w PHP są wyliczone poniżej (nie ma tu obiektów, ponieważ ich kon-
wersje są mało użyteczne).
* Integer na double: tworzona jest liczba zmiennoprzecinkową odpowiadająca
całkowitej (na przykład 4 jest zamieniane na 4 . 0).
* Double na integer: odrzucana jest część ułamkowa, liczbę zaokrągla się w stro-
nę zera.
* Liczba na boolean: FALSE, jeżeli liczba jest dokładnie równa O, w przeciwnym
wypadku TRUE.

* Liczba na string: tworzony jest ciąg wyglądający dokładnie jak wydrukowana


liczba. Liczby całkowite są drukowane jako sekwencja cyfr, liczby zmienno-
przecinkowe przedstawiane są z minimalną możliwą precyzją. Bardzo duże
liczby zmiennoprzecinkowe są zamieniane na zapis naukowy.
* Boolean na liczbę: TRUE na l, FALSE na 0.
* Boolean na ciąg: TRUE na'!', FALSE na 'O'.
106_________________________________________Część l » Podstawy PHP

** String na liczbę: odpowiednik „czytania" liczby z ciągu znaków, następnie kon-


wersja do odpowiedniego typu. Jeżeli liczba nie daje się przeczytać, wartość
wynosi zero.
* String na boolean: FALSE, gdy ciąg jest pusty lub ciąg 'O', TRUE w innych
przypadkach.
* Typy proste na tablicę: odpowiednik utworzenia tablicy i przypisania wartości
typu prostego do komórki o indeksie zero.
* Tablica na liczbę: liczba l, gdy tablica zawiera wartości, w przeciwnym wy-
padku 0.
** Tablica na boolean: FALSE, jeżeli tablica jest pusta, w przeciwnym przypadku
TRUE.

* Tablica na string: 'Array'.

Jawne konwersje
PHP zezwala na trzy sposoby manipulacji typami: funkcje konwersji, rzutowanie typów
(jak w języku C) oraz wywołanie settype ( ) na zmiennej:
1. Funkcje intval (), doubleval () i strval () konwertują wartość argumentu
na odpowiednio liczbę integer lub double, albo ciąg znaków (do chwili na-
pisania książki nie było funkcji boolval).
2. Każde wyrażenie może być poprzedzone rzutowaniem typu (nazwa typu w na-
wiasach), co konwertuje wynik wyrażenia do żądanego typu.
3. Jako pierwszy argument funkcji settype ( ) może wystąpić dowolna zmienna,
która zostanie zmieniona na typ podany w drugim argumencie funkcji.

Każdy z poniższych przykładów umieszcza prawidłową liczbę dalmatyńczyków (l01)


w zmiennej $dog_count:

Podejście 1.
$dog_count = intval(strval(doubleval("101 dalmatyńczyków")));

Podejście 2.
$dog_count = (int)(string)(double) "101 dalmatyńczyków";

Podejście 3.
$dog_count = "101 dalmatyńczyków";
settype($dog_count, "double");
settype(Sdog_count, "string");
settype (Sdog_count,- "int");

Żaden z przykładów nie konwertuje w sposób bezpośredni, niepotrzeb-


nie konwertując typ double na ciąg. Wystarczy od razu skonwertować
s t r i n g na i n t e g e r .
Rozdział 6. » Typy w PHP_________________________________________107

Każdej z sześciu nazw typów (integer, double, boolean, string, array i object)
można użyć przy rzutowaniu typów oraz jako drugi argument funkcji s e t t y p e f ) .
Dodatkowo przy rzutowaniu (ale nie w funkcji settype ()) dopuszcza się kilka nazw
zastępczych: ( i n t ) zamiast ( i n t e g e r ) , ( f l o a t ) zamiast (double) oraz (bool) za-
miast ( b o o l e a n ) .

Przykłady konwersji
Na wydruku 6.2 podany jest kod, który wyświetla kilka konwersji typów w tabeli
HTML, pokazanej na rysunku 6.1 (kod ten nie jest przykładem stylu programowania
i używa kilku nieopisanych jeszcze konstrukcji).

Wydruk 6.2. Konwersje typów______________________________________________


Stype_examples[0] = 123; // liczba całkowita
$type_examples[1] = 3.14159; // liczba zmiennoprzecinkowa
$type_examples[2] = "ciąg bez liczb";
$type_examples[3] = "49.990 (rozpoczyna się liczba)";
$type_examples[4] = array(90,80,70);
print ("<TABLE BORDER=1XTR>" ) ;
print("<TH>Oryginał</<TH>");
print("<TH>(int)</<TH>");
print("<TH>(double)</<TH>");
print("<TH>(string)</<TH>");
print ( "<TH> (array) </<THX/TR>") ;
for ($index = 0; Sindex < 5; $index++)
{
print ("<TRXTD>$type_examples [$index] </TD>") ;
Sconverted_var =(int) $type_examples[$index];
print("<TD>$converted_var</TD>") ;
$converted_var = (double) $type_examples [$index] ,-
print ("<TD>$converted__var</TD>") ;
$converted_var = (string) $type_examples[$indexl;
print("<TD>Sconverted_var</TD>");
$converted_var = (array) $type_examples[$index];
print ("<TD>$converted_var</TDX/TR>") ;
(
print("</TABLE>");

Rysunek 6.1.
Przykład
konwersji typów
108_________________________________________Część l » Podstawy PHP

Inne użyteczne konwersje typów


Funkcje wymienione w tabeli 6.2 nie są funkcjami konwersji typów, ale zwracają war-
tość innego typu niż typ głównego argumentu.

Tabela 6.2.
Inne funkcje konwersji typów

Z typu \ na typ Integer String Array


Integer ord()
Double c e i l O , f loor ( ) , round ( }
String chr () explode ( )
Array implode ( )

Funkcja ceil () jako argument pobiera liczbę double i zwraca liczbę typu integer,
która jest od niej większa lub jej równa. Na przykład:
$my_double = 4.7;
$my_int = ceil($my_double) ; // $my_int = 5
$my_double = -4.7;
$my_int = f loor ($my_double) ; // $my__int = -4

Funkcja floor () jest przeciwieństwem ceil () :


Smy_int = floor(4.7); // $my_int = 4
Smy_int = floor(-4.7); // $my_int = -5

Funkcja round ( ) zwraca liczbę całkowitą najbliższą liczbie double podanej jako argu-
ment. Jeżeli część ułamkowa jest równa dokładnie 0,5, liczba jest zaokrąglana w stronę
liczby parzystej.
$my_int = round(4.7); // $my_int = 5
$my_int = round(-4.7); // $my__int = -5
$my_int = round(-4.5); // ?my_int = -4

Zauważ, że nie ma funkcji truncate ( ) (odrzucenie części ułamkowej),


ponieważ konwersja double na integer daje taki właśnie wynik.

Funkcja chr ( ) pobiera integer i zwraca ciąg składający się z jednej litery o kodzie
ASCII równym podanemu argumentowi. Funkcja ord ( ) działa odwrotnie, zwraca war-
tość ASCII pierwszego znaku ciągu.

Funkcje implode ( ) i explode ( ) pozwalają na konwersję pomiędzy ciągiem i tablicą.


implode ( ) tworzy ciąg z elementów tablicy podanej jako drugi argument, oddzielając
elementy tablicy ciągiem podanym jako pierwszy argument. Na przykład:
$slowa[0] = "Jedno";
$slowa[l] = "krótkie";
$slowa[2] = "zdanie.";
$zdanie = implode( " ", $slowa);
print("$zdanie<BR>");
Rozdział 6. » Typy w PHP______________________________________109

da w efekcie
Jedno krótkie zdanie.

Explode ( ) działa odwrotnie, tworząc tablicę z elementów ciągu.


$slowa - explode( " ", "Jedno krótkie zdanie.");

Przepełnienie liczby całkowitej


Jedną ze sprytniejszych konwersji typów wykonywanych przez PHP jest stosunkowo
często występujący przypadek, gdy wystąpi przepełnienie liczby całkowitej. Zmienna
jest konwertowana wtedy na typ double, ponieważ typ ten może przechowywać liczby
o wiele większe niż integer. Na przykład:
$za__duzo = 111;
for ($count = 0; Scount < 5; Scount++)
(
$za_duzo = 1000 * $za_duzo;
print (" Czy $za_duzo to jeszcze integer?<BR>");
)

da w wyniku:
Czy 111000 to jeszcze integer?
Czy 111000000 to jeszcze integer?
Czy 111000000000 to jeszcze integer?
Czy 1.11E+14 to jeszcze integer?
Czy 1.11E+17 to jeszcze integer?

Zmiana sposobu wyświetlania liczby na postać naukową, jest związana z zamianą typu
zmiennej $za_duzo na typ double. Oczywiście można w ten sposób utracić część in-
formacji, ponieważ double ma ograniczoną dokładność, ale PHP stara się zrobić wszyst-
ko, co jest możliwe, aby uniknąć błędu.

Szukamy największej liczby całkowitej


Jeżeli musisz dokładnie wiedzieć, jak duże liczby całkowite obsługuje PHP, a uważasz,
że nie jest to 231-1, przedstawiamy funkcję, która zwraca tą wartość.
function maxint()
{ /* funkcja określająca wielkość typu integer
funkcja zakłada, że największy integer jest w postaci 2"n-l */
$to_test = 2;
while(l)
(
Slast = $to_test;
Sto_test = 2*Sto_test;
if (($to_test < Slast) l l (!is_int($to_test)))
return (Slast + (Slast -1));
)
>
/* przykład użycia */
Smaxint = maxint O;
print{"Naj większa liczba całkowita to $maxint<BR>");
110_________________________________________Część l * Podstawy PHP

Podsumowanie
PHP 4 posiada sześć typów: integer, double, boolean, string, array i object.
Cztery z nich to typy proste: integer to liczby całkowite, double to liczby rzeczywi-
ste, boolean to wartości prawda i fałsz, natomiast string to łańcuch znaków (typ
boolean jest nowością w PHP 4). Tablice stanowią typ złożony z innych wartości PHP
indeksowanych za pomocą liczb lub ciągów. Obiekty są egzemplarzami klas definiowa-
nych przez programistę, które zawierają zarówno zmienne, jak i funkcje klasy. Klasy
mogą dziedziczyć funkcje i zmienne z innych klas.

W PHP tylko wartości posiadają typy — zmienne nie posiadają wewnętrznego typu, in-
nego niż typ ostatniego przypisanej wartości. PHP samoczynnie konwertuje typy, jeżeli
jest to wymagane przez kontekst, w jakim została użyta wartość. Programista może
również jawnie kontrolować typy zmiennych poprzez użycie konwersji lub rzutowania.
Rozdział 7.
Sterowanie
W tym rozdziale:
* Tworzenie i łączenie warunków logicznych
* Instrukcje warunkowe if i switch
+ Pętle while i for
* Przerywanie przetwarzania strony za pomocą exit i die

Trudno jest napisać interesujący program, jeżeli wykonanie programu nie zależy od
czynników zewnętrznych. Można stworzyć kod, którego wykonanie zależy tylko od
wartości zmiennych, ale jest to równie pasjonujące, co wypełnianie formularza. Pro-
gramiści chcą pisać programy, na które coś oddziałuje (świat, czas, wprowadzone dane,
zawartość bazy danych), a program na skutek tego oddziaływania robi coś innego.

Takie zachowanie programu wymaga zastosowania instrukcji sterujących, które okre-


ślają, jak w określonej sytuacji ma zachować się program. W poprzednim rozdziale uży-
liśmy konstrukcji if bez wyjaśnienia, jak ona działa, w tym rozdziale skupimy się na
instrukcjach sterujących oferowanych przez PHP.

Do doświadczonych programistów C: wszystkie instrukcje sterujące


w PHP są najbardziej zbliżone do C, wszystkie struktury tutaj użyte
działają identycznie. Możesz bezpiecznie przejść do podsumowania
na końcu tego rozdziału.

Będziemy tu mówić o dwóch rodzajach instrukcji sterujących: instrukcjach warunko-


wych i pętlach. Instrukcja warunkowa powoduje rozdzielenie ścieżki wykonania progra-
mu. W zależności od warunku program może pójść na prawo bądź na lewo, podążając
inną ścieżką aż do końca programu, lub ścieżki powtórnie się łączą. Pętla jest specjal-
nym rodzajem rozgałęzienia, w którym jedna ze ścieżek powraca na początek pętli, po-
nawiając sprawdzenie warunku i wykonanie ciała pętli.
112_________________________________________Część l » Podstawy PHP

Zanim wykorzystamy instrukcje sterujące, musimy umieć tworzyć odpowiednie warun-


ki. Rozpoczniemy od bardzo prostych warunków wykorzystujących stałe TRUE i FALSE,
a następnie będziemy tworzyć coraz bardziej skomplikowane konstrukcje.

Wyrażenia logiczne
Każda z instrukcji sterujących opisanych w tym rozdziale posiada dwie oddzielne części:
warunek (określający, która część struktury będzie wykonana) oraz sam kod (osobne
gałęzie kodu lub ciało pętli). Wyrażenia logiczne działają poprzez wyliczenie wartości
wyrażenia, którego wartość jest traktowana jako prawda lub fałsz.

Stałe logiczne
Najprostszym rodzajem wyrażenia jest po prostu wartość. Najprostszymi wartościami
logicznymi są stałe TRUE i FALSE. Możemy np. wstawić je do warunku instrukcji if-else:
if (TRUE)
print{"To będzie zawsze wydrukowane<BR>") ;
else
print("To nie będzie nigdy wydrukowane<BR>");

lub zamiennie:
if (FALSE)
printC'To nie będzie nigdy wydrukowane<BR>") ;
else
printC'To będzie zawsze wydrukowane<BR>") ;

Operatory logiczne
Operatory logiczne wiążą wartości logiczne i dają w wyniku nowe wartości logiczne.
Standardowe operatory logiczne (and, or, not i exclusive or) są dostępne w PHP.

Tabela 7.1.
Operatory logiczne

Operator Działanie

and
Daje wartość TRUE, gdy oba argumenty są TRUE
° r
Daje wartość TRUE, gdy jeden z argumentów (lub oba) jest TRUE
! TRUE, gdy jego argument jest FALSE, FALSE gdy argument jest TRUE
xor
TRUE, gdy jeden z argumentów (ale nie oba) jest TRUE
&s
To samo co 'and', ale ściślej wiąże argumenty (patrz opis kolejności wykonywania w dalszej
części rozdziału)
ll To samo co 'or', ale ściślej wiąże argumenty
Rozdział 7. » Sterowanie_________________________________________113

Operatory & & i I I są znane programistom C. Operator '!' jest czasami zwany operato-
rem not.

Dla przykładu rozważmy następujące wyrażenie:


(($instrukcja_l && Sinstrukcja_2) l !
($instrukcja_l ii !$instrukcja_2) l l
(!$instrukcja_l SS $instrukcja_2) l l
(!Sinstrukcja_l ss !$instrukcja_2))

Wyrażenie to jest tautologią, co oznacza wyrażenie zawsze prawdziwe niezależnie od


wartości zmiennych „instrukcja". Dla dwóch zmiennych istnieją cztery kombinacje war-
tości logicznych, każda reprezentowana przez jedno wyrażenie z operatorem & & . Jedno
z nich musi być prawdziwe, a ponieważ są one połączone operatorem l i , całe wyraże-
nie jest prawdziwe.

Tutaj mamy przykład innej tautologii z użyciem operatora xor:


((3instrukcja_l and $instrukcja_2 and $instrukcja3) xor
((!($instrukcja_l and $instrukcja_2)) or
(!(Sinstrukcja_l and $instrukcja_3)) or
(!($instrukcja_2 and $instrukcja_3))))

Tłumacząc na polski: „Z podanych trzech wyrażeń jedno spełnia tylko jedno z poda-
nych dwóch twierdzeń — 1) wszystkie trzy wyrażenia są prawdziwe, 2) istnieje para
wyrażeń, z których oba nie są prawdziwe".

Kolejność operatorów logicznych


Niektóre operatory logiczne mają wyższy priorytet od innych, jednak kolejność może
być zawsze zmieniona przez użycie nawiasów. Operatory logiczne wymienione w ma-
lejącym porządku priorytetów to: !, &&, I I , and, xor, or. Operatory and, xor i or
mają o wiele niższy priorytet, operator ' =' ma od nich priorytet wyższy (ale nie od & &).

Kompletna tabela kolejności operatorów znajduje się w podręczniku


dostępnym pod adresem http://tvvvw.php.net.

Skracanie operatorów logicznych


Jedną z bardzo użytecznych własności operatorów logicznych jest to, że łączą się od
lewej do prawej oraz automatycznie się skracają, co oznacza, że nie jest obliczana war-
tość drugiego argumentu, jeżeli jego wartość jest nieważna po obliczeniu wartości ar-
gumentu pierwszego. Chcesz np. znać orientacyjną wartość podzielenia jednej liczby
przez drugą, ale chcesz równocześnie uniknąć błędu dzielenia przez zero. Możesz naj-
pierw sprawdzić, czy podzielnik jest różny od zera, używając operatora ! = (różny):
if ($podz != O ss Sliczba / $podz > 2)
print! "Więcej niż dwa!");

W przypadku gdy $podz jest równy zero, operator & & powinien zwrócić FALSE bez
względu na wartość drugiego wyrażenia. Ponieważ z powodu skracania nie wykona się
114_________________________________________Część l « Podstawy PHP

drugie wyrażenie, unikniemy błędu dzielenia przez zero. W przypadku gdy $podz jest
różny od zera, operator & & ma zbyt mało informacji i musi obliczyć wartość logiczną
drugiego wyrażenia.

Do tej pory formalnie opisaliśmy stałe TRUE i FALSE i sposób ich użycia. Teraz zaj-
miemy się operatorami, które pozwolą na zbudowanie prawdziwych wyrażeń logicznych.

Operatory porównania
W tabeli 7.2 zamieszczone są operatory porównania, które mogą być używane do po-
równywania zarówno liczb, jak i ciągów (przeczytaj ostrzeżenie w dopisku).

Tabela 7.2.
Operatory porównania

Operator Nazwa Opis


— Równy Prawdziwy, jeżeli argumenty są równe, w przeciwnym przypadku fałszywy
iK Różny Fałszywy, jeżeli argumenty są równe, w przeciwnym przypadku prawdziwy
< Mniejszy Prawdziwy, jeżeli lewy argument jest mniejszy, w przeciwnym przypadku
fałszywy
> Większy Prawdziwy, jeżeli lewy argument jest większy, w przeciwnym przypadku
fałszywy
<- Mniejszy Prawdziwy, jeżeli lewy argument jest mniejszy lub równy, w przeciwnym
lub równy przypadku fałszywy
>- Większy Prawdziwy, jeżeli lewy argument jest większy lub równy, w przeciwnym
lub równy przypadku fałszywy
=s==
Identyczny Prawdziwy, jeżeli argumenty są równe i tych samych typów, w przeciwnym
przypadku fałszywy (jest to nowość w PHP 4)

Jako przykład wykonamy kilka przypisań, a następnie skonstruujemy warunek, który


jest zawsze prawdziwy:
Strzy = 3;
Scztery = 4;
Śpi = 3.14159;
if (($trzy == $trzy) and
(Scztery == $cztery) and
($trzy != Scztery) and
($trzy < Scztery) and
($trzy <= Scztery) and
(Scztery >- $trzy) and
(Strzy <= $trzy) and
(Śpi > Strzy) and
(Śpi <= Scztery))
print("Moja wiara w matematykę ocalaia!<BR>");
else
print("Na pewno się nie pomyliłeś?<BR>") ;
Rozdział 7. * Sterowanie 115

Uważaj na bardzo częsty błąd pomylenia operatora przypisania c = ' )


z operatorem porównania ( ' = = ' ) . Wyrażenie if ($trzy = Sczte-
ry) ... przypisze (niespodziewanie) wartość zmiennej Scztery do
zmiennej $trzy, a poza tym wynik będzie prawdziwy, jeżeli $cztery
jest różne od zera.

Kolejność operatorów
Mimo że duże zaufanie do reguł kolejności może być kłopotliwe dla osoby czytającej
twój kod, jednak należy zapamiętać, że operatory porównania mają wyższy priorytet niż
operatory logiczne. Oznacza to, że warunek:
if ($small_num > 2 && $small_num < 5) ...

nie wymaga żadnych dodatkowych nawiasów.

Ostrożnie z porównaniami
Mimo że operatory porównania działają zarówno z liczbami, jak i ciągami,
kryje się tutaj kilka pułapek.
Po pierwsze, mimo że porównania mniejszy lub równy, większy lub równy na
liczbach double (lub pomiędzy integer i double) zawsze są bezpieczne,
niebezpieczne jest poleganie na równości liczb double, szczególnie jeżeli są
wynikiem działań matematycznych. Problem tkwi w błędach zaokrągleń, które
mogą spowodować, że liczby teoretycznie równe nieco się od siebie różnią.
Po drugie, pomimo że operatory porównania działają równie dobrze na cią-
gach, jak i na liczbach, to automatyczna konwersja typów PHP może dopro-
wadzić do nieintuicyjnych wyników, gdy ciągi zostaną zinterpretowane jako
liczby, na przykład:
$ciag_l = "00008";
Sciag_2 = "007";
$ciag_3 - "00008-OK";
if ($ciag_2 < $ciag_l)
print ("$ciag_2 jest mniejszy od $ciag_l<BR>");
if ($ciag_3 < Sciag_2)
print ("Sciag_3 jest mniejszy od $ciag_2<BR>");
if ($ciag_l < $ciag_3)
print ("$ciag_l jest mniejszy od Sciag_3<BR>");

daje w wyniku (opatrzonym komentarzem):


007 jest mniejszy od 00008 // porównanie liczb
00008-OK jest mniejszy od 007 // porównanie ciągów
00008 jest mniejszy od 00008-OK // porównanie ciągów — sprzeczność!

PHP konwertuje ciągi na liczby, jeżeli tylko potrafi to zrobić i gdy obie strony
mogą być potraktowane w ten sposób (porównywane są wartości liczbowe,
a nie ciągi). Projektanci PHP uważają, że nie jest to błąd, to po prostu wła-
sność. Zalecamy korzystanie z funkcji strcmp ( ) , jeżeli porównywane ciągi
mają jakąkolwiek szansę być zinterpretowane jako liczby (rozdział 9.).
116_________________________________________Część l » Podstawy PHP

Porównywanie ciągów
Operatory porównania mogą być używane do porównywania ciągów tak samo jak liczb
(przeczytaj ostrzeżenie powyżej). Możemy się spodziewać, że poniższy kod wypisze
skojarzone z nim zdanie:
if (("Marek" < "Maria") and
("Maria" < "Margaryna"))
{
print("W słowniku pomiędzy Marek i Maria ");
print("znajduje się Margaryna,<BR>");
}

Porównanie zależy od wielkości liter. Przykład ten wypisze zdanie tylko dlatego, że
w słowie Margaryna zastosowaliśmy niewłaściwą wielkość liter. Z powodu nieodpo-
wiedniej wielkości liter poniższy przykład nic nie wydrukuje:
if (("głębokie morze" < "gramatyka") and
("gramatyka" < "Grażyna"))
{
print("Pomiędzy głębokie morze a Grażyna ");
print("znajduje się gramatyka.<BR>");
)

Operator trójskładnikowy
Jedną z bardziej użytecznych konstrukcji jest trójskładnikowy operator porównania,
który zajmuje miejsce pomiędzy operatorem logicznym a prawdziwą instrukcją warun-
kową. Jego działanie polega na użyciu wartości logicznej pierwszego wyrażenia do wy-
boru, które z pozostałych dwóch wyrażeń będzie wynikiem operatora. Składnia tego
operatora jest następująca:
wyrażenie-logiczne ? wyrażenie-prawda : w y r a ż e n i e - f a ł s z

Wynikiem tego wyrażenia jest wyrażenie-prawda, jeżeli wyrażenie-logiczne ma


wartość TRUE lub wyrażenie-fałsz, w przeciwnym przypadku.

Poniższe wyrażenie przypisuje np. do $max_num wartość $first_num lub $second_


num w zależności od tego, która z nich jest większa:
$max_num = Sfirst^num > $second_num ? $first_num : $second_num;

Wyrażenie to zastępuj e następuj ącą konstrukcj ę:


if ($first_num > Ssecond^num)
$max_num = $first_num;
else
$max_num = $second_num;

ale jest nieco bardziej zwięzłe.


Rozdział 7. » Sterowanie_________________________________________117

Instrukcje warunkowe
Dwoma głównymi instrukcjami sterującymi są if i switch, if to prawdopodobnie
pierwsza instrukcja warunkowa, jakiej wszyscy się uczą. Switch jest jej użyteczną od-
mianą w szczególnych przypadkach, gdy potrzebujesz kilku gałęzi opartych o tę samą
wartość, a seria instrukcji if byłaby niewygodna.

If-else
Składnia dla instrukcji if jest następująca:
if (test)
wyrażenie_l

lub z opcjonalną gałęzią else:


if (test)
wyrażenie_l
else
wyrażenie_2

Podczas wykonywania instrukcji if obliczana jest wartość wyrażenia test, jego war-
tość jest interpretowana jako wartość logiczna. Jeżeli test ma wartość TRUE, wykony-
wane jest wyrażenie_l, w przeciwnym przypadku wykonywane jest wyrażenie_2.
Jeżeli wyrażenie test ma wartość FALSE, a nie ma części else, wykonywana jest po
prostu następna instrukcja po konstrukcji if.

Zauważ, że każde „wyrażenie" może być zarówno jedną instrukcją, blokiem instrukcji
lub inną instrukcją warunkową (każda jest traktowana jako pojedyncza instrukcja). In-
strukcje warunkowe mogą być dowolnie zagnieżdżane. Wyrażenia logiczne mogą być
prawdziwymi wyrażeniami typu boolean (TRUE, FALSE lub wynik operatora logiczne-
go, albo funkcji logicznej) lub wartościami innych typów, interpretowanymi jako war-
tości logiczne.
_^X* "Ł ———————————————————————" ——"—————————————...———————————————-——— _ ___———— ____... ..———————————————————————————————————————.——__———————————————

ŁDfflfP Pełny °Pis znajduje się w rozdziale 6. W skrócie przypomnimy, że licz-


ba ( >1 cią
lakle " '°' lub Pusty c'3& "" traktowane są jako false, natomiast
inne wartości traktowane są jako true.

Poniższy przykład drukujący różnicę bezwzględną pomiędzy liczbami, pokazuje za-


gnieżdżanie instrukcji warunkowych i interpretację wyrażeń.
if ($first - Ssecond)
if (Sfirst > Ssecond)
{
$difference = $first - $second;
print("Różnica wynosi $difference.<BR>");
)
else
(
Sdifference - Ssecond - $first;
print("Różnica wynosi Sdifference.<BR>");
118_________________________________________Część l » Podstawy PHP

}
else
print("Nie ma żadnej różnicy.<BR>");

Kod ten polega na tym, że wartość O jest interpretowana jako fałsz, jeżeli różnica wyno-
si zero, drukowany jest komunikat o braku różnicy. Jeżeli liczby się różnią, wykonywa-
ne są następne instrukcje (przykład ten jest sztuczny, ponieważ warunek $ first ! =
$second załatwiłby sprawę bardziej wszechstronnie).

Dołączamy else
Programiści Pascala mogą zastanawiać się, w jaki sposób klauzula else wie, do której
instrukcji if należy. Zasada jest prosta i identyczna jak w większości języków oprócz
Pascala. Instrukcja else jest dołączana do najbliższej instrukcji if, respektując ograni-
czenia nawiasów klamrowych. Jeżeli chcesz się upewnić, że wyrażenie if pozostanie
bez części else, musisz otoczyć je klamrami.
if ($num % 2 == 0) // $num jest parzyste ?
f
if (Snum > 2)
print("Liczba nie jest liczba parzysta<BR>"};
)
else
print("Liczba jest nieparzysta<BR>");

Fragment ten wypisze „Liczba nie jest liczbą parzystą", jeżeli $num będzie parzystą
liczbą większą od 2; „Liczba jest nieparzysta", jeżeli $num jest nieparzysta; nic nie wy-
pisze, jeżeli liczba będzie wynosiła 2. Jeżeli usuniemy klamry, else zostanie przyłą-
czone do wewnętrznej instrukcji if; program nieprawidłowo wypisze „Liczba jest nie-
parzysta" jeżeli liczba będzie równa 2; nic nie wypisze, jeżeli liczba będzie nieparzysta.

^^\ W przykładach często używamy operatora „ % " , opisanego w rozdziale 10.


\^ Dla potrzeb tych przykładów powinieneś wiedzieć, że $x % $y jest ze-
ro, jeżeli $x dzieli się bez reszty przez $y.

Else-if
Można wykonać kaskadową sekwencję testów, jak w poniższej zagnieżdżonej in-
strukcji if:
if ($day == 5)
print("Pięć złotych obraczek<BR>");
else
if ($day == 4)
print("Cztery śpiewające ptaki<BR>") ;
else
if (Sday == 3)
print("Trzech muszkieterów<BR>" ) ;
else
if ($day == 2)
print("Dwie szare myszy<BR>" ) ;
else
if ($day == 1)
print("Kuropatwa na gruszy<BR>");
Rozdział 7. » Sterowanie______________________________________119

^^v Do powyższego kodu zastosowaliśmy wcięcia pokazujące właściwą


\A strukturę składni. Warto formatować w ten sposób kod, nie wszyscy
się tym przejmują i rozpoczynają każdy else w pierwszej kolumnie.

Konstrukcja ta na tyle często występuje, że wprowadzono specjalną konstrukcję else-


if do obsługiwania takich warunków. Przepiszmy jeszcze raz powyższy przykład:
if ($day — 5)
print("Pięć złotych obraczek<BR>");
elseif ($day == 4)
print("Cztery śpiewające ptaki<BR>");
elseif (Sday == 3)
print("Trzech muszkieterów<BR>");
elseif (Sday == 2)
print("Dwie szare myszy<BR>");
elseif ($day == 1)
print("Kuropatwa na gruszy<BR>");

Konstrukcja if, elseif, elseif, ... pozwala na wykonanie sekwencji testów i wykona-
nie tylko pierwszej pasującej gałęzi. Teoretycznie jest to składniowo różna instrukcja od
przedstawionej w poprzednim przykładzie (mamy jedną instrukcję z pięcioma rozgałę-
zieniami, zamiast pięciu zagnieżdżonych instrukcji), ale działanie jest identyczne. Mo-
żesz używać tej konstrukcji, która wydaje ci się wygodniejsza w konkretnej sytuacji.

Warunki i tryb HTML


Jak wcześniej mówiliśmy, możesz dowolnie przełączać się pomiędzy trybem
PHP i HTML. Jeżeli musisz włączyć duże fragmenty HTML, które nie posiada-
ją dynamicznego kodu lub interpolowanych zmiennych, prościej i bardziej
efektywnie jest przełączyć się do trybu HTML, zamiast umieszczać kod przy
UŻyciU print lub echo.

Ale nie jest oczywiste, że strategia ta działa również wewnątrz instrukcji wa-
runkowych. Można użyć PHP do wyboru, który fragment HTML należy wysłać,
a następnie „wysłać" odpowiedni fragment dzięki chwilowemu przełączeniu
się do trybu HTML.
Poniższy, niewygodny kod używa instrukcji print do stworzenia strony HTML
na podstawie spodziewanej płci użytkownika (zakładamy istnienie funkcji lo-
gicznej female o , która ją sprawdza).
<HTMLXHEAD>
<?php
if (female())
t
print ("<TITLE>Witryna tylko dla kobiet</TITLEXBR>") ;
print ("</HEADXBODY>") ;
print("Witryna ta została utworzona tylko ");
print("dla kobiet.<BR> Mężczyźni nie są tu mile widziani!"};
}
else
(
print("<TlTLE>Witryna tylko dla meżczyzn</TITLE><BR>");
print ( "</HEADXBODY>" ) ;
print{"Witryną ta została utworzona tylko "};
120______________________________________Część l » Podstawy PHP

print("dla mężczyzn.<BR> Kobiety nie są tu mile widziane!");


}
?>
</BODYX/HTML>

Zamiast używać instrukcji print, można przełączyć się do trybu HTML w każ-
dej z dwóch gałęzi:
<HTMLXHEAD>
<?php
if (female())
(
?>
<TITLE>Witryna tylko dla kobiet</TITLEXBR>
</HEADXBODY>
Witryna ta została utworzona tylko dla kobiet.<BR>
Mężczyźni nie są tu mile widziani!
<?php
)
else
{
?>
<TITLE>Witryna tylko dla mężczyzn</TITLE><BR>
</HEADXBODY>"
"Witryna ta została utworzona tylko dla mężczyzn.<BR>
Kobiety nie są tu mile widziane!"
<?php
}
?>
</BODYX/HTML>

Wersja ta jest być może nieco trudniejsza do czytania, ale jedyną różnicą
jest zamiana serii instrukcji print na blok HTML, który rozpoczyna się za-
mykającym znacznikiem PHP (?>) i kończy się otwierającym znacznikiem
PHP(<? P hp).
W przykładach umieszczonych w tej książce zwykle unikamy tego rodzaju wa-
runkowego włączania, ponieważ jest to trudniejsze do czytania dla początku-
jących programistów PHP. Jednak nie powinno to cię powstrzymywać.
Włączanie takie powoduje znaczne przyspieszenie wykonania. W trybie HTML
maszyna PHP tylko przesyła znaki, szukając otwierającego znacznika PHP.
Jest to o wiele szybsze niż analiza i wykonywanie instrukcji print, szczególnie
gdy zawierają ciągi w cudzysłowach.

Switch
Konstrukcja switch jest bardzo użyteczna dla specyficznych rodzajów rozgałęzień.
Zamiast wybierać ścieżkę obliczając wartość wyrażeń logicznych, switch bierze pod
uwagę wartość jednego wyrażenia. Składnię przedstawiamy poniżej, część nieobowiąz-
kowa otoczona jest nawiasami kwadratowymi ([]).
switch (wyrażenie)
(
case wartość_l:
instrukcja_l
instrukcja_2

[break;]
case wartość_2:
instrukcja_3
Rozdział 7. » Sterowanie______________________________________121

instrukcja_4

[break;]

[default:
instrukcja_domyślna]
}

Wyrażenie może być zmienną lub innym rodzajem wyrażenia, które można zinterpre-
tować jako wartość typu prostego (liczba lub ciąg). Konstrukcja ta oblicza wartość wy-
rażenia i sprawdza, czy wynik jest równy kolejnym wartościom podanym w klauzuli
case. Gdy zostanie znaleziona odpowiednia wartość, wykonane będą kolejne instrukcje
aż do napotkania instrukcji break lub zakończenia instrukcji switch (break może
również służyć do przerywania pętli). Część default może być umieszczona na końcu
instrukcji i będzie wykonana dla wszystkich wartości wyrażenia, dla których nie było
odpowiedników.

Przepiszmy nasz przykład dla instrukcji if:


switch ($day)
(
case 5:
print("Pięć złotych obraczek<BR>");
break;
case 4:
print("Cztery śpiewające ptaki<BR>");
break;
case 3:
print("Trzech muszkieterów<BR>"};
break;
case 2:
print("Dwie szare myszy<BR>") ;
break;
default:
print("Kuropatwa na gruszy<BR>");
)

Kod ten wypisze odpowiednią linię dla wartości 2 - 5, a dla innych wartości — „Kuro-
patwa na gruszy".

Najbardziej mylącym zachowaniem instrukcji switch jest to, że wyko-


nywane są wszystkie instrukcje po pasującej klauzuli case, chyba, że
umieścimy instrukcję break przerywającą wykonanie. W powyższym
przypadku instrukcja break pozwala na wypisanie tylko jednego wiersza
piosenki za jednym razem. Jeżeli usuniemy wszystkie instrukcje break,
dostaniemy sekwencję wierszy aż do wiersza finałowego piosenki.

Pętle
Gratulacje! Właśnie minąłeś granicę pomiędzy pisaniem skryptów a „prawdziwym pro-
gramowaniem". Struktury warunkowe, które omówiliśmy, są użyteczne, ale istnieją
granice ich samodzielnego użycia. Z drugiej strony istnieją podstawy teoretyczne mó-
wiące, że każdy język z warunkami i pętlami może wykonać wszystko, co tylko może
122_________________________________________Część l » Podstawy PHP

zrobić dowolny język programowania. Nie musisz od razu pisać kompilatora za pomocą
PHP, ale dobrze jest wiedzieć, że nie ma do tego wewnętrznych przeszkód.

Pętle ograniczone i nieograniczone


Pętla ograniczona to pętla wykonująca się stałą liczbę razy. Patrząc na kod, możesz
określić, ile razy zostanie wykonane ciało pętli, język gwarantuje liczbę przebiegów pę-
tli. Pętla nieograniczona to pętla, która wykonuje się aż do spełnienia jakiegoś warun-
ku, warunek jest zależny od kodu wykonywanego w ciele pętli. Pętle ograniczone są
przewidywalne, natomiast nieograniczone mogą być dowolnie skomplikowane.

W przeciwieństwie do niektórych języków PHP nie posiada konstrukcji przeznaczonej


tylko dla pętli ograniczonych, while, do-while oraz for są konstrukcjami nieograni-
czonymi, jednak, jak się okaże, pętle nieograniczone mogą robić to samo, co pętle ogra-
niczone.

\ oatrA Oprócz pętli PHP dostarcza funkcji do iteracji po zawartości tablicy.


takie Zostanie to opisane w rozdziale 11.

While
Najprostszą pętlą w PHP jest while, które ma następującą składnię:
while (warunek)
instrukcja

Pętla while oblicza wartość wyrażenia warunkowego, traktując je jako wyrażenie lo-
giczne. Jeżeli jest prawdziwe, wykonywana jest instrukcja ciała pętli i ponownie obli-
czana jest wartość warunku. Jeżeli warunek jest fałszywy, wykonywanie pętli while
jest zakończone. Oczywiście jak w przypadku instrukcji if, instrukcja może być jedną
instrukcją lub blokiem.

Ciało pętli nie musi zostać wykonane, jak w przykładzie:


while (FALSE)
print("To nigdy nie będzie wydrukowane.<BR>");

lub może wykonywać się „wiecznie", jak poniżej:


while (TRUE)
print("TO się nigdy nie skończy.<BR>");

albo może wykonywać się określoną liczbę razy:


Scount = l;
while ($count <=10)
(
print("Licznik: $count<BR>");
Scount = $count + 1;
(
Rozdział 7. » Sterowanie_________________________________________123

Ostatni przykład wydrukuje dokładnie dziesięć linii (inne interesujące przykłady znaj-
dziesz w „Przykładach pętli" w dalszej części rozdziału).

Do-while
Konstrukcja do-while jest podobna do while, poza tym, że warunek jest wykonywa-
ny na końcu pętli. Składnia jest następująca:
do
instrukcj a
while (wyrażenie);

Instrukcja jest wykonywana, następnie obliczana jest wartość wyrażenia. Jeżeli wyrażenie
ma wartość TRUE, instrukcja jest powtarzana, aż wyrażenie będzie miało wartość FALSE.

Jedyną praktyczną różnicą pomiędzy do-while i while jest to, że ciało pętli do-
while zostanie wykonane co najmniej raz. Na przykład:
Slicznik = 45;
do
{
print("Licznik: Scount");
$count = Scount + 1;
l
while (Slicznik <= 10);

wydrukuje tylko jedną linię:


Licznik: 45

For
Najbardziej złożoną pętlą jest pętla for. Jej składnia jest następująca:
for (wyrażenie_początkowe;
warunek_zakońcżenią;
wyrażenie_końcowe)
instrukcja

W czasie wykonywania instrukcji for, najpierw wykonywane jest wyrażenie_po-


czątkowe jeden raz; zwykle używane jest do inicjalizacji zmiennych. Następnie spraw-
dzany jest warunek_zakończenia, jeżeli ma wartość false, pętla się kończy, jeżeli
true, wykonywana jest instrukcja ciała pętli. Na koniec wykonywane jest wyraże-
nie_końcowe i cykl jest powtarzany od sprawdzenia warunku zakończenia. Jak zwy-
kle, instrukcja oznacza jedną instrukcję (zakończoną średnikiem), blok instrukcji lub
instrukcję warunkową.

Jeżeli przedstawimy konstrukcję for jako pętlę while, będzie wyglądała następująco:
wyrażenie_początkowe;
while ( w a r u n e k _ _ z a k o ń c z e n i a )
{
instrukcja;
wyrażeńie_końcowe;
}
124_________________________________________Część l » Podstawy PHP

Typowa konstrukcja for posiada jedno wyrażenie początkowe, jeden warunek zakoń-
czenia i jedno wyrażenie końcowe, jednak można usunąć każdy z tych składników.
Jeżeli zostanie usunięty warunek zakończenia, jest to traktowane tak, jak gdyby wsta-
wiona została za niego wartość TRUE. Konstrukcja:
for (;;)
instrukcja;

jest tożsama z:
while (TRUE)
instrukcja;

Można również wstawić więcej niż jeden składnik każdego typu, oddzielony przecin-
kiem. Warunek zakończenia będzie prawdziwy, jeżeli którykolwiek z podwarunków zo-
stanie spełniony, tak jak przy użyciu operatora or. Poniższa instrukcja:
for ($x = 1 , Sy = l, $z = 1; // wyrażenie_początkowe
$y < 10, $z < 10; // warunek_zakończenia
?x = $x + l, $y = $y + 2; // wyrażenie_końcowe
Sz = $z +3}
print("$x, Sy, $z<BR>");

da w wyniku:
1. i, i
2. 3, 4
3. 5, 7

Mimo że składnia for jest najbardziej skomplikowana ze wszystkich pętli, jest często
używana do tworzenia prostych pętli ograniczonych przy użyciu następującego schematu:
for ($licznik = 0; $licznik < Smaksimum; $licznik = Slicznik + l )
instrukcja;

Przykłady pętli
Prześledzimy teraz kilka przykładów użycia pętli.

Ograniczona pętla for


Na wydruku 7.1 pokazujemy typowe użycie ograniczonych pętli for. Ponieważ więk-
szość czytelników zna tabliczkę mnożenia, zamiast niej pokazujemy tabliczkę dzielenia.
Strona utworzona przez program z wydruku 7.1 pokazana jest na rysunku 7.1.

Wydruk 7.1. Tabliczka dzielenia________________________________________


<?php
Sstart_num = 1;
$end_num = 10;
?>
<HTML>
<HEAD>
<TITLE>Tabliczka dzielenia</TITLE>
</HEAD>
<BODY>
<H2>Tabliczka dzielenia</H2>
<TABLE BORDER-1>
Rozdział 7. » Sterowanie_________________________________________125

<?php
print("<TR>");
print ("<THX/TH>") ;
for ($count_l = $start_num;
$count_l <= $end_num;
Scount_l++)
print ("<TH>$count_K/TH>") ;
print("</TR>");

for (Scount_l = $start_num;


$count_l <= $end_num;
$count_l++)
(
print ("<TRXTH>$count_K/TH>") ;
for ($count_2 = $start_num;
$count_2 <= $end_num;
$count_2++)
{
Sresult = $count_l / $count_2;
printf("<TD>%.3f</TD>", Sresult); // Popatrz do rozdziału 9
)
print("</TR>\n");
)
?>
</TABLE>
</BODY>
</HTML>

Rysunek 7.1.
Tabliczka dzielenia

Główną częścią tego programu są dwie zagłębione pętle. Każda z nich wykonywana jest
dziesięć razy, co w wyniku daje tabelę 10 x 10. Każdy przebieg zewnętrznej pętli wypi-
suje wiersz tabeli, natomiast każdy przebieg pętli wewnętrznej tworzy pojedynczą ko-
mórkę. Jedyną nowością w tym przykładzie jest użycie funkcji printf do wypisywania
liczb (opisana w rozdziale 9.), która pozwala na kontrolę liczby cyfr po przecinku dru-
kowanej liczby.
126_________________________________________Część l » Podstawy PHP

Nieograniczona pętla while


Teraz zajmiemy się pętlą, która nie jest w tak oczywisty sposób ograniczona. Wyłącz-
nym przeznaczeniem kodu z wydruku 7.2 jest przybliżenie pierwiastka z 81 (przy użyciu
metody Newtona). Przybliżenie rozpoczyna się od liczby l i w kolejnych przebiegach
pętli jest poprawiane, aż do osiągnięcia zadanej dokładności. Kolejne przybliżenia
przedstawione są na rysunku 7.2.

Wydruk 7.2. Przybliżanie pierwiastka kwadratowego______________________________


<HTML>
<HEAD>
<TITLE>Przybliżanie pierwiastka kwadratowego</TITLE>
</HEAD>
<BODY>
<H3>Przybliżanie pierwiastka kwadratowego</H3>
<?php
$target = 81;
Sguess = 1.0;
Sprecision = 0.0000001;
Sguess_squared = Sguess * Sguess;
while ((Sguess_squared - Starget > Sprecision) or
{Sguess_squared - Starget < - Sprecision))
f
print{"Bieżące przybliżenie: Sguess jest pierwiastkiem
z $target<BR>");
Sguess = (Sguess + (Starget / Sguess)) / 2;
$guess_squared = Sguess * Sguess;
}
print("Sguess do kwadratu = Sguess_squared<BR>");
?>
</BODY>
</HTML>

Rysunek 7.2.
Przybliżanie
pierwiastka
kwadratowego
Rozdział 7. « Sterowanie_________________________________________127

Mimo że przykład bardzo ładnie pokazuje potencjał nieograniczonych pętli, jest jednak
dosyć sztuczny: po pierwsze, ponieważ PHP posiada świetną funkcję do obliczania
pierwiastka kwadratowego (sqrt); po drugie, liczba 81 jest na stałe wpisana do kodu.
Nie możemy użyć tej strony do obliczania pierwiastka dowolnej liczby.

W następnym rozdziale pokażemy, jak tworzyć funkcje, które pobiera-


ją jako parametr liczby. W rozdziale 12. poznasz, jak przekazywać pa-
rametry z jednej strony do drugiej.

Break i continue
Normalnie pętla kończy się, gdy warunek zakończenia ma wartość FALSE. Specjalne
instrukcje break i continue zapewniają alternatywny sposób wyjścia z każdej pętli.
* Instrukcja break kończy najbardziej wewnętrzną pętlę, której ciało zawiera
break.

* Instrukcja continue powoduje przeskoczenie do końca bieżącej iteracji w naj-


bardziej wewnętrznej pętli, zawierającej tę instrukcję.

Na przykład poniższy fragment:


for ($x - l ; Sx < 10 ; $x++)
{
// jeżeli x jest nieparzyste, przerwij pętlę
if ($x % 2 1= 0}
break;
print("$x ");
)

nic nie wydrukuje, ponieważ l jest nieparzysta. Spowoduje to natychmiastowe zakoń-


czenie pętli. Natomiast taki kod:
for ($x = l ; $x < 10 ; $x++)
(
// j e ż e l i x jest n i e p a r z y s t e , opuść ten przebieg
if ($x ł 2 !- 0)
continue;
print("$x ");
)

da w efekcie
2 4 6 8

ponieważ w efekcie działania instrukcji continue zostaną opuszczone wszystkie licz-


by nieparzyste.

Używając instrukcji break, programista nie musi korzystać z głównego warunku za-
kończenia pętli. Przeanalizujmy poniższy fragment, który drukuje listę liczb pierwszych
(niepodzielnych przez żadną inną liczbę oprócz jeden i samej siebie).
$limit = 500;
Sto_test = 2;
while(TRUE)
{
Stestdiv = 2;
128______________________________________Część l » Podstawy PHP

if (Sto_test > $limit)


break;
while (TRUE)
(
if (Stestdiv > sqrt(Sto_test) )
(
print "$to_test " ;
break;
)
// sprawdź, czy $to_test jest podzielne przez Stestdiv
if ($to_test % $testdiv == 0)
break;
$testdiv = $testdiv + 1;
)
$to_test = Sto_test + 1;
}

W kodzie tym mamy dwie zagnieżdżone pętle while, zewnętrzna przebiega przez
wszystkie liczby od l do 500, wewnętrzna pętla sprawdza prawdopodobne podzielniki.
Jeżeli wewnętrzna pętla znajdzie podzielnik, liczba nie jest pierwsza, więc pętla jest
przerywana i nic nie drukuje. Jeżeli sprawdzanie dojdzie do pierwiastka z liczby, mo-
żemy bezpiecznie założyć, że liczba jest pierwsza i wewnętrzna pętla jest przerywana
po wydrukowaniu liczby. Zewnętrzna pętla jest przerywana, gdy osiągnie granicę liczb
do sprawdzenia. Wynikiem jest lista liczb pierwszych mniejszych od 500.
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103
107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211
223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331
337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449
457 461 463 467 479 487 491 499

Zauważ, że krytyczny dla działania tego programu jest fakt, że break przerywa tylko
wewnętrzną pętlę.

Pętle nieskończone
Jeżeli kiedykolwiek programowałeś w innym języku, zdarzyło ci się prawdopodobnie
przypadkowo stworzyć pętle nieskończone (pętla, której warunek zakończenia zawsze
ma wartość TRUE, dlatego nigdy się nie kończy). Pierwszą rzeczą, jaką się wtedy robi,
jest przerwanie programu, ponieważ bez interwencji z zewnątrz będzie działał „ciągle".
Jednak w jaki sposób przerwać skrypt PHP? Czy wystarczy przycisnąć przycisk Stop
w przeglądarce?

Odpowiedź zależy od ustawień konfiguracji PHP. Możesz nakazać PHP ignorowanie


przerwania z przeglądarki (wysyłane po naciśnięciu Stop), jak również ustawić ograni-
czenie czasu wykonywania skryptu (więc „ciągle" będzie trwało nieco krócej). Do-
myślna konfiguracja PHP ignoruje przerwania z przeglądarki, ale czas wykonania
pojedynczego skryptu jest ograniczony do 30 sekund. Limit czasu zabezpiecza przed
przypadkowymi pętlami nieskończonymi, ale może nie działać w niektórych wersjach
PHP 3 i PHP 4 beta 3.

Więcej na temat konfiguracji PHP w rozdziale 31.


Rozdział 7. « Sterowanie______________________________________129

Składnia alternatywna
PHP pozwala na inny sposób rozpoczynania i kończenia ciała instrukcji if, switch,
for oraz while. Polega to na zastąpieniu początkowej klamry bloku dwukropkiem,
a zamykającej klamry specjalnym słowem kluczowym dla każdej z instrukcji (endif,
endswitch, endf or oraz endwhile).

Przykładowo taka składnia instrukcji if wygląda następująco:


if (wyrażenie):
instrukcj al;
instrukcja2;

endif;

lub
if (wyrażenie):
instrukcjal;
instrukcja2;

elseif (wyrażenie2):
instrukcja3;

else:
instrukcja4 ;

endif;

Zauważ, że ciała części elseif oraz else również rozpoczynają się dwukropkiem.
Alternatywna składnia while jest następująca
while (wyrażenie):
instrukcja;
endwhile;

Możesz używać takiej składni, jaka ci przypadnie do gustu. Alternatywna składnia jest
włączona do PHP ze względów historycznych i dla wygody ludzi używających jej od
wczesnych wersji programu. W całej książce będziemy konsekwentnie używać składni
standardowej.

Przerywanie wykonania
Czasami trzeba poddać. PHP oferuje dwie konstrukcje pozwalające na zakończenie wy-
konywania skryptu:
1. Konstrukcja exit ( ) nie posiada argumentów. Kończy przetwarzanie skryptu
w dowolnym momencie.
2. Konstrukcja die ( ) ma ciąg znaków jako argument. Kończy przetwarzanie
skryptu, wysyłając na wyjście ciąg znaków podany jako argument.
130______________________________________Część l » Podstawy PHP

Wszystko, co PHP stworzył do czasu wykonania exit { ) lub die ( ) , zostanie wysłane
do klienta.

Jaki jest cel używania exit ( ) lub die ( ) ? Jedną z możliwości jest przerwanie przetwa-
rzania strony, jeżeli skrypt zorientuje się, że nie ma już nic do wysłania, a ty nie chcesz
tworzyć skomplikowanych instrukcji warunkowych. Jednak takie podejście może utrud-
nić czytanie i uruchamianie długich skryptów.

Lepszym zastosowaniem konstrukcji die ( ) jest obsługa błędów. Dobrze jest przyzwy-
czaić się do sprawdzania nieoczekiwanych sytuacji, które mogą zepsuć wykonanie
skryptu. Jeżeli sytuacja taka zostanie obsłużona za pomocą die ( ) , zostanie wypisany
komunikat informujący o problemie. Jeżeli instrukcja wykona się prawidłowo, die ( )
nie zostanie wykonane.

Rozważmy poniższy pseudokod, który zakłada, że posiadamy funkcje łączące się z bazą
danych i używające tego połączenia:
$connection = make_database_connection();
if (!$connection)
die ("Brak połączenia z baza. danych");
use_database_connection($connection);

W tym przykładzie założyliśmy, że funkcja make_database_connection ( ) (jak


większość funkcji PHP) zwraca użyteczną wartość, jeżeli zakończy się sukcesem, lub
false, jeżeli się nie udała. Możemy napisać bardziej zwartą wersję tego przykładu, ko-
rzystając z tego, że or ma niższy priorytet niż operator przypisania.
$connection = make_database_connection()
or die("Brak połączenia z bazą danych");
use_database_connection($connection) ;

Taka konstrukcja działa, ponieważ operator or jest skracany, a instrukcja die ( ) będzie
wykonana dopiero, gdy wyrażenie $connection = make_database_connection ( )
będzie miało wartość false. Ponieważ przypisywana wartość jest wartością przypisa-
nia, fragment działa identycznie jak poprzednia wersja.

Podsumowanie
PHP posiada zestaw struktur sterujących podobny do C, które działają w zależności od
wartości wyrażenia logicznego, do zbudowania którego można użyć operatorów logicz-
nych (and, or, xor, !, & & , I I ) . Do prostego rozgałęziania kodu używamy struktur if
oraz switch, pętle budujemy przy użyciu while, do-while oraz for, exit i die są
używane do zakończenia wykonywania skryptu.

W tabeli 7.3 zebraliśmy wszystkie struktury sterujące, opisane w tym rozdziale, oprócz
alternatywnej składni instrukcji.
Rozdział 7. » Sterowanie______________________________________131

Tabela 7.3.
Struktury sterujące PHP

Nazwa Składnia Działanie

If (lub if-else) if ( w a r u n e k ) Oblicza wartość warunek i jeżeli jest true,


wyrazenie-1 wykonuje wyrażenie-1. Jeżeli warunek jest false
lub i istnieje część else, wykonywane jest
if (warunek) w y r a ż e n i e - 2 . Konstrukcja e l s e i f jest
wyrażenie-1 składniowym skrótem dla sytuacji, gdy ciałem
else
części else jest kolejna konstrukcja if.
wyrazenie-2 Wyrażeniami mogą być pojedyncze instrukcje
lub zakończone średnikiem lub blok instrukcji
if ( w a r u n e k ) otoczonych nawiasami klamrowymi
wyrażenie-1
elseif(warunek-2)
wyrażenie-2
else
wyrażenie-3
Operator wyrażenie-1 ? Wykonuje wyrażenie-1 i jego wynik interpretuje
trójskładnikowy wyrażenie-2 : jako wartość logiczną. Jeżeli ma wartość true,
wyrażenie-3 ,
wykonywane jest wyrażenie-2, w przeciwnym
wypadku wykonuje się wyrażenie-3
Switch switch ( w y r a ż e n i e ) Oblicza wartość wyrażenie i porównuje jego
' wartość z wartością każdej klauzuli case. Gdy
case wartość 1: . . . ,, ,
instrukc ' a~l zostanie znaleziona pasująca klauzula case,
instrukcja~2 rozpoczyna się wykonywanie instrukcji (włączając
... w to kolejne klauzule case), aż do napotkania
[break; ] break lub zakończenia instrukcji switch.
case wartość_2: Opcjonalna klauzula default jest wykonywana,
instru c^a jeżeli nie ma pasujących części case
instrukcja_4

[break;]

[default:
instrukcja_domyślna
}
While while ( w a r u n e k ) Wykonywany jest warunek i jego wartość jest
instrukcja interpretowana jako wartość logiczna. Jeżeli
warunek ma wartość false, wykonanie konstrukcji
while jest zakończone. Jeżeli ma wartość true,
wykonywana jest instrukcja aż do momentu, gdy
warunek będzie miał wartość false. Pętla while
jest przerywana, jeżeli napotkana zostanie instrukcja
break; jeżeli wystąpi continue, bieżąca iteracja
jest opuszczana
Do-while do
Bezwarunkowo wykonywana jest instrukcja,
instrukcja następnie jest wykonywana aż do chwili, gdy
while ( w a r u n e k ) ; i u j •
warunek będzie •miał
i ••
wartość false ,• , •
(instrukcje
break i continue działają tak samo jak dla while)
132______________________________________Część l » Podstawy PHP

Tabela 7.3.
Struktury sterujące PHP (ciąg dalszy)

Nazwa Składnia Działanie

For f°r (wyrażenie_pocz; Bezwarunkowo wykonywane jest


warunek_zakon; wyrażenie_pocz. Następnie, jeżeli
wyrazenie_ on) warunek zakon ma wartość true, wykonywana
instrukcja - , , ,
jest instrukcja i wyrażenie_kon aż do chwili, gdy
warunek_zakon będzie miał wartość false.
Można opuścić części instrukcji lub wstawić kilka
wyrażeń, oddzielając przecinkami. Brakujące
wyrażenie_kon jest traktowane jak true
(instrukcje break i continue działają tak samo
jak dla while)
Exit exit () Kończy bezwarunkowo przetwarzanie skryptu
Die die ( k o m u n i k a t ) Wysyła na wyjście komunikat i kończy
przetwarzanie skryptu
Rozdział 8.
Użycie
i definiowanie funkcji
W tym rozdziale:
* Użycie wbudowanych funkcji PHP
•* Przeszukiwanie dokumentacji funkcji
•* Definiowanie własnych funkcji
* Zagadnienia zaawansowane: zmienna liczba argumentów, przekazywanie para-
metrów przez referencję, zmienne funkcyjne

Każdy prawdziwy język programowania posiada mechanizmy abstrakcji proceduralnej,


sposobu na nazwanie fragmentów kodu i wykorzystywanie ich do tworzenia kolejnych
fragmentów. Niektóre języki skryptowe nie posiadają tych mechanizmów; z własnego
doświadczenia możemy stwierdzić, że skomplikowane skrypty szybko stają się kłopo-
tliwe w utrzymaniu.

Mechanizm PHP do zapewnienia tego rodzaju abstrakcji to funkcje. Istnieją w tym ję-
zyku dwa rodzaje funkcji, wbudowane oraz definiowane przez programistę.

W tym rozdziale wyjaśnimy sposób użycia funkcji dostarczanych przez PHP, a nieco
później omówimy sposób definiowania własnych. Na szczęście nie ma różnicy pomię-
dzy używaniem własnych i wbudowanych funkcji.

Użycie funkcji
Podstawowa składnia użycia (wywoływania) funkcji jest następująca:
nazwa_funkcji(wyrażenie_l, wyrażenie_2, ..., wyrażenie_n)
134_________________________________________Część l » Podstawy PHP

Po nazwie funkcji występuje lista wyrażeń wejściowych (w nawiasach) zwanych argu-


mentami funkcji. Funkcje mogą być wywoływane bez argumentów lub z argumentami,
w zależności od definicji. Gdy PHP napotka wywołanie funkcji, na początek oblicza
wartość każdego argumentu i używa tych wartości jako dane wejściowe funkcji. Wyni-
kiem jest wartość zwracana przez funkcję (o ile istnieje).

Wszystkie poniżej przytoczone przykłady są prawidłowymi wywołaniami wbudowa-


nych funkcji:
sqrt(9); // funkcja pierwiastek, zwraca 3
rand(10, 10+10); // liczba losowa pomiędzy 10 i 20
strlen("Tutaj znajduje się 28 znaków"); // zwraca 28
pi(); // zwraca przybliżenie liczby pi

Funkcje te były wywołane odpowiednio z l, 2 i l argumentem oraz bez argumentów.

Zwracane wartości a efekty uboczne


Każde wywołanie funkcji jest wyrażeniem PHP. Istnieją dwa powody, dla których
umieszcza się to wywołanie w kodzie: dla zwracanej wartości lub efektu ubocznego.

Wartość zwracana jest wartością, którą otrzymujemy przez wywołanie funkcji. Wartość
możesz np. przypisać do zmiennej:
?moje_pi = p i ( i ;

Można również wbudować jaw bardziej skomplikowane wyrażenie:


Sprzybl = sqrt(Sprzybl) * sqrt(Sprzybl);

Funkcje mogą być zastosowane do szerokiej gamy efektów ubocznych, takich jak: pisa-
nie do pliku, manipulowanie bazą danych, drukowanie w przeglądarce klienta. Można
również jednocześnie korzystać z efektów ubocznych i zwracanej wartości; często funk-
cja, podejmująca jakieś akcje, zwraca wartość określającą, czy zostały one zakończone
sukcesem.

Wynik funkcji może być dowolnego typu; jeżeli funkcja zwraca wiele wyników, często
korzysta się z typu tablicowego.

Dokumentacja funkcji
Architektura PHP została zaprojektowana w sposób, który ułatwia rozszerzanie jej
przez programistów. Podstawowy język PHP jest bardzo jasny i elastyczny, ale nie ofe-
ruje zbyt wielu możliwości. Większość siły PHP jest zawarta w wielu wbudowanych
funkcjach. Można łatwo rozszerzać pakiet poprzez dodawanie nowych funkcji wbudo-
wanych, nie zmieniając żadnych mechanizmów, z których mogliby korzystać inni użyt-
kownicy.
Rozdział 8. » Użycie i definiowanie funkcji_____________________________135

Mimo że książka ta opisuje wiele z wbudowanych funkcji, wyjaśniając sposób użycia


niektórych z nich bardzo szczegółowo, najbardziej wiarygodnym źródłem informacji
jest podręcznik dostępny pod adresem http://www.php.net. Tu wybraliśmy do szczegó-
łowego omówienia kilka tematów, a opisanie każdego aspektu PHP leży w gestii doku-
mentalistów. Mamy nadzieję uaktualniać książkę przed kolejnymi wydaniami, aczkolwiek
to właśnie podręcznik zawsze będzie zawierał najbardziej aktualne dane na temat ciągle
rosnącego zestawu funkcji.

Mimo że poniższe dane były prawidłowe w czasie pisania tej książki,


niektóre szczegóły mogły zostać zmienione, jeżeli przeorganizowano
podręcznik.

Aby odnaleźć podręcznik, przejdź na stronę http://www.php.net i odszukaj Documenta-


tion na lewym panelu nawigacji. Strona, do której przechodzimy za pomocą tego po-
łączenia, zawiera podręcznik w kilku rodzajach formatów. Naszym ulubionym jest
podręcznik online z komentarzami, umożliwiający użytkownikom wysyłanie komenta-
rzy do każdej strony. Należy jednak zapamiętać, że system komentowania nie służy
zadawaniu pytań! Właściwym miejscem są listy dyskusyjne, wymienione na stronie
Support, dostępnej z głównej strony http://www.php.net.

Najobszerniejszą częścią podręcznika jest lista funkcji; każda funkcja posiada własną
stronę dokumentacji. Każda ze stron rozpoczyna się nazwą funkcji i krótkim opisem.
Zawiera deklarację nagłówka w stylu języka C i nieco dłuższy opis działania. Podaje się
również przykłady użycia oraz objaśnienia i ostrzeżenia od użytkowników.

Nagłówki w dokumentacji
Dla osób nieznających języka C nagłówki funkcji, zawarte na stronach dokumentacji,
mogą wydać się nieco mylące. Format nagłówka jest następujący:
typ_zwracany nazwa_funkcj i(typl argl, typ2 ar2, ...);

Definiuje typ wartości zwracanej przez funkcję, nazwę funkcji oraz liczbę i spodziewa-
ne typy argumentów.

Typowym nagłówkiem jest:


string substr{string string, int start [, int length]);

Oznacza to, że funkcja substr zwraca ciąg znaków i oczekuje ciągu i jednego lub
dwóch liczb całkowitych jako argumentów. Nawiasy kwadratowe otaczające int
length oznaczają argument opcjonalny, więc substr może być wywoływany z cią-
giem znaków i liczbą lub ciągiem znaków i dwoma liczbami.

W przeciwieństwie do C typy argumentów w nagłówkach nie są absolutnie wymagane.


Jeżeli wywołasz funkcję substr z liczbąjako pierwszym argumentem, nie otrzymasz
komunikatu o błędzie. W zamian PHP skonwertuje pierwszy argument do postaci ciągu.
136_________________________________________Część l » Podstawy PHP

Typy argumentów w nagłówku wskazują na intencje autora; dobrze jest wywoływać


funkcję zgodnie ze specyfikacją lub wiedzieć, w jaki sposób zostanie zastosowana
konwersja typów.

W dokumentacji są używane nazwy sześciu podstawowych typów lub ich zamienni-


ków: integer (lub int), double (lub float, real), boolean, string, a r r a y i ob-
ject. Można spotkać dodatkowo typy void oraz mixed. Typ zwracanej wartości void
oznacza, że funkcja nie zwraca żadnego wyniku, natomiast mixed oznacza dowolny typ
wyniku.

Szukanie opisu funkcji


Jaka jest najlepsza metoda odszukania informacji w podręczniku? Zależy to od rodzaju
zadawanego pytania. Najczęściej zadawanymi pytaniami są:
* Chciałbym użyć funkcji X. Jak ona działa?
* Chcę zrealizować Y. Czy jest funkcja, która to wykona?

Pełna wersja podręcznika zapewnia, dla pierwszego rodzaju pytań, automatyczne szukanie
opisu funkcji na podstawie jej nazwy. Przycisk quick refna panelu nawigacji otwiera, po
przesunięciu nań kursora myszy, pole pozwalające na wpisanie nazwy funkcji. Kliknięcie
przycisku quick re/'przenosi nas do strony z alfabetyczną listą wszystkich funkcji.

Do drugiego rodzaju pytań najlepiej użyć organizacji hierarchicznej skorowidza funkcji.


W czasie pisania tej książki był on podzielony na około 62 rozdziały. Wspomniana
wcześniej funkcja substr znajduje się w części String functions. Możesz przejrzeć listę
rozdziałów i przejść do tej, która najlepiej opisuje zadania, jakie masz do wykonania.
Możesz również, jeżeli znasz nazwę funkcji, skorzystać z przycisku quick ref, aby szyb-
ko przeskoczyć do odpowiedniej części.

Definiowanie własnych funkcji


PHP nie wymaga definiowania funkcji. Możesz tworzyć interesujące i użyteczne witryny
za pomocą podstawowych konstrukcji języka i dużej liczby funkcji wbudowanych. Jednak
z czasem, gdy twój kod stanie się obszerniejszy, trudniejszy do zrozumienia i kłopotliwy
w zarządzaniu, powinieneś rozpocząć przenoszenie jego fragmentów do funkcji.

Czym jest funkcja?


Funkcja jest sposobem na wyizolowanie części kodu i nadanie mu nazwy, dzięki czemu
możliwe jest późniejsze wywołanie tego fragmentu podaniem nazwy. Funkcje są naj-
bardziej użyteczne, jeżeli używasz kodu w więcej niż jednym miejscu. Mogą być przy-
datne w przypadku jednego użycia, ponieważ zapewniają większą przejrzystość kodu.
Rozdział 8. » Użycie i definiowanie funkcji_____________________________137

Składnia definicji funkcji


Składnia definicji funkcji wygląda następująco:
function nazwa_funkcji ($arqumentl, $argument2, .,.)
{
instrukcjal;
instrukcja2;

Definicja funkcji ma cztery części:


1. Słowo kluczowe function.
2. Nazwę, jaką nadajesz funkcji.
3. Listę parametrów funkcji — zmienne oddzielone przecinkami.
4. Ciało funkcji — blok instrukcji.

Podobnie jak nazwy zmiennych, nazwy funkcji mogą składać się z liter, cyfr i podkre-
ślenia; nie mogą rozpoczynać się cyfrą. W przeciwieństwie do zmiennych, nazwy funk-
cji są konwertowane do małych liter przed ich zapamiętaniem, więc wielkość liter nie
ma znaczenia.

Gdy wywoływana jest funkcja zdefiniowana przez użytkownika, podejmowane są na-


stępujące czynności:
1. PHP na podstawie nazwy odszukuje funkcję (jeżeli nie została wcześniej zde-
finiowana, wyświetli komunikat o błędzie).
2. PHP podstawia wartości argumentów wywołania (parametrów aktualnych) do
zmiennych zdefiniowanych w liście parametrów (parametrów formalnych).
3. Wykonywane są instrukcje zawarte w ciele funkcji. Jeżeli jedną z instrukcji
jest return, wykonanie funkcji jest zatrzymane i zwracana jest podana war-
tość. Jeżeli nie ma takiej instrukcji, funkcja kończy się po wykonaniu ostatniej
instrukcji bez zwracania wartości.

Doświadczeni programiści na pewno zauważyli, że powyższy opis mó-


wi o przekazywaniu parametrów przez wartość. W ostatniej części te-
go rozdziału opiszemy różnice i sposób przekazywania parametrów
przez zmienną.

Przykład definicji funkcji


Rozważmy hipotetyczny przykład kodu, który pomaga w wyborze lepszej oferty (spo-
śród wielu butelek napojów chłodzących).
$liters_l = 1.0;
$price_l = 1.59;
$liters_2 = 1.5;
Sprice_2 = 2.09;
138______________________________________Część l » Podstawy PHP

$per_literl = Sprice_l / $liters_l;


$per_liter2 = Sprice_2 / $liters_2;
if ($per_literl < $per_liter2)
print("Pierwsza oferta jest lepsza!<BR>");
else
print("Druga oferta jest lepsza!<BR>");

Ponieważ taki rodzaj porównania występuje bardzo często, zamieńmy ten fragment na
funkcję, która może być wielokrotnie używana. Przykładem jest następujący kod:
function better_deal ($amount_l, Sprice_l,
Samount_2, $price_2)
{
$per_amount_l = $price_l / $amount_l;
$per_amount_2 = $price_2 / $amount_2;
return ($per_amount_l < $per_amount_2);
}
$liters_l = 1.0;
$price_l =1.59;
$liters_2 = 1.5;
$price_2 = 2.09;
if better_deal($liters_l, $price_l,
$liters_2, Sprice_2)
print("Pierwsza oferta jest lepsza!<BR>");
else
printC'Druga oferta jest lepsza ! <BR>" );

Funkcja better_deal wydziela trzy wiersze z poprzedniego kodu, zawierające przeli-


czenie i porównanie. Funkcja wymaga czterech argumentów i zwraca wartość logiczną.
Można jej użyć jako składnika instrukcji if. Mimo że funkcja jest dłuższa niż oryginal-
ny kod, taka zamiana przynosi następujące korzyści: można użyć tej funkcji w wielu
miejscach; jeśli zmienimy sposób przeliczania, zmiany trzeba nanieść tylko w jednym
miejscu.

Jeżeli jedynym zastosowaniem takiego porównania cen jest wypisanie lepszej oferty,
można zawrzeć drukowanie w ciele funkcji:
function print_better_deal ($amount_l, Sprice_l,
$amount_2, $price_2)
{
$per_amount_l = $price_l / $amount_l;
$per_amount_2 = $price_2 / $amount_2;
if ($per_amount_l < $per_amount_2)
print("Pierwsza oferta jest lepsza!<BR>");
else
printC'Druga oferta jest lepsza ! <BR>" ) ;
l
$liters_l = 1.0;
$price_l = 1.59;
$liters_2 = 1.5;
$price_2 = 2.09;
print_better_deal($liters_l, Sprice_l,
Sliters_2, $price_2);

Nasza pierwsza funkcja zwracała, przy użyciu instrukcji return, wartość wyrażenia
logicznego, którego użyto w warunku instrukcji if. Druga funkcja nie posiada instrukcji
. return, ponieważ używana jest tylko ze względu na jej efekt uboczny — drukowanie
tekstu w przeglądarce klienta. Gdy zostanie wykonany ostatni wiersz funkcji, PHP bę-
dzie kontynuował wykonanie programu od następnego wiersza po wywołaniu funkcji.
Rozdział 8. » Użycie i definiowanie funkcji_______________________________139

Parametry formalne i parametry aktualne


W poprzednich przykładach argumenty przekazywane do funkcji były zmiennymi (cho-
ciaż nie jest to wymagane). Parametry aktualne (to znaczy argumenty wywołania funk-
cji) mogą być dowolnymi wyrażeniami posiadającymi wartość. W przykładzie możemy
przekazać liczby zamiast zmiennych:
print_better_deal(1.0, 1.59, 1.5, 2.09);

Zauważ, że podajemy przykłady, w których parametry aktualne mają takie same nazwy
jak parametry formalne (na przykład $price_l), oraz takie, w których nazwy są różne
($liters_l). Jak się przekonamy za chwilę, parametry formalne funkcji są zupełnie
niezależne od reszty programu, nawet od wywołania funkcji.

Nieprawidłowa liczba argumentów


Co stanie się, jeżeli wywołasz funkcję ze zbyt małą bądź zbyt dużą liczbą argumentów?
Jak można się spodziewać, PHP nie zareaguje błędem przy standardowych ustawieniach
kontroli błędów. Jeżeli wywołasz funkcję z niedostateczną liczbą parametrów, PHP po-
traktuje niewypełnione parametry formalne tak samo jak niezainicjowane zmienne. Je-
żeli wstawisz zbyt dużo parametrów, zbędne zostaną po prostu zignorowane. Pod
koniec rozdziału opiszemy, jak wykorzystać tę tolerancję do tworzenia funkcji o zmien-
nej liczbie argumentów.

Funkcje a zasięg zmiennych


W rozdziale 5. opisywaliśmy zasady zasięgu zmiennych: zmienne zainicjowane w pliku
będą dostępne do końca. Zasady te nieco się komplikują przy definiowaniu zmiennych.

Podstawowa zasada rządząca zmiennymi w ciele funkcji brzmi: każda funkcja jest od-
rębną jednostką. Oznacza to, że, poza specjalnymi deklaracjami, zmienne w ciele funk-
cji nie mają nic wspólnego ze zmiennymi o takiej samej nazwie poza funkcją. Nie jest
to błąd — to bardzo pożyteczna cecha, funkcje mogą być używane wielokrotnie w wielu
miejscach programu, więc muszą być niezależne od kontekstu, w którym są wywołane.
W innym przypadku spędziłbyś wiele czasu, szukając błędów spowodowanych użyciem
zmiennej o tej samej nazwie w różnych fragmentach kodu.

Jedynymi zmiennymi, do których może odwoływać się zmienna, są zmienne parame-


trów formalnych oraz zmienne przypisane w ciele funkcji. Możesz więc używać zmien-
nych lokalnych funkcji bez obawy o jej zewnętrzne działanie. Dla przykładu rozważmy
następującą definicję funkcji i jej użycie:
function SayMyABCs ()
(
Scount = 0;
while ($count < 10)
{
print(chr(ord('A1) + $count));
140_________________________________________Część l » Podstawy PHP

$count = Scount + 1;
)
print("<BR>Znam teraz $count liter<BR>");
)
$count = 0;
SayMyABCs(};
Scount = $count + 1;
print("Teraz wykonałem $count wywołanie funkcj i.<BR>");
SayMyABCs(} ;
Scount = Scount + 1;
print("Teraz wykonałem Scount wywołanie funkcji.<BR>");

Funkcja SayMyABCs ( ) drukuje sekwencję liter (funkcje chr ( ) i ord ( ) konwertują po-
między literami a ich kodami ASCII). Wynikiem działania jest:
ABCDEFGHIJ
Znam teraz 10 liter
Teraz wykonałem l wywołanie funkcji.
ABCDEFGHIJ
Znam teraz 10 liter
Teraz wykonałem 2 wywołanie funkcji.

Zarówno definicja funkcji, jak i kod poza funkcją używają zmiennej o nazwie $ count,
jednak są to zupełnie różne, niekolidujące ze sobą zmienne.

Zmienne zainicjowane w ciele funkcji domyślnie nie mają wpływu na resztę kodu, są
tworzone na nowo przy każdym wywołaniu funkcji. Obie te własności można zmienić
za pomocą specjalnej deklaracji.

Zmienne globalne i lokalne


Zmienne definiowane w ciele funkcji domyślnie są lokalne. Używając deklaracji global,
można zdecydować, że zmienna będzie miała takie samo znaczenie jak na zewnątrz funk-
cji. Definicja taka składa się ze słowa global oraz listy zmiennych oddzielonych prze-
cinkami, zakończonej średnikiem. Aby zobaczyć efekt działania tej deklaracji, użyjmy
poprzedniego przykładu. Różnica polega na użyciu deklaracji zmiennej $count jako
zmiennej globalnej oraz usunięciu początkowego zerowania w ciele funkcji:
function SayMyABCs2 ()
(
global Scount;
while (Scount < 10)
(
print(chr(ord('A1) + Scount));
Scount = Scount + 1;
}
print("<BR>Znam teraz Scount liter<BR>");
}
Scount = 0;
SayMyABCs2();
Scount = Scount + 1;
print("Teraz wykonałem Scount wywołanie funkcji.<BR>");
SayMyABCs2();
Scount = Scount + 1;
print("Teraz wykonałem Scount wywołanie funkcji,<BR>");

Zmieniona wersja da w efekcie:


ABCDEFGHIJ
Znam teraz 10 liter
Teraz wykonałem 11 wywołanie funkcji.
r
Rozdział 8. » Użycie i definiowanie funkcji_____________________________141

Znam teraz 11 liter


Teraz wykonałem 12 wywołanie funkcji.

Zachowanie to jest nieprawidłowe z winy deklaracji globalnej zmiennej $count. Teraz


w programie istnieje tylko jedna taka zmienna, która jest zwiększana zarówno we-
wnątrz, jak i na zewnątrz funkcji. Gdy funkcja SayMyABCs2 { ) zostanie wywołana po
raz drugi, wartość tej zmiennej wyniesie 11 i program nie wykona pętli.

Mimo że przykład ten ukazuje global w złym świetle, deklaracja taka może być uży-
teczna. PHP posiada mechanizm (opisany w rozdziale 12.) przypisywania kilku zmien-
nych do każdej strony przed wykonaniem jakiegokolwiek kodu. Można łatwo uzyskać
dostęp do tych zmiennych w funkcji, bez konieczności przekazywania ich jako parametry.

Zmienne statyczne
Zmienne lokalne funkcji są tworzone na nowo za każdym wywołaniem funkcji. Aby
zmienić ten mechanizm można, użyć deklaracji static, która powoduje, że zmienna
zapamięta wartość pomiędzy kolejnymi wywołaniami tej samej funkcji. Przy użyciu tej
deklaracji zmodyfikujemy naszą wcześniejszą funkcję:
function SayMyABCs3 O
f
static $count = 0; // przypisanie tylko za pierwszym razem
Slimit = Scount + 10;
while (Scount < $limit)
(
print(chr(ord('A'} + Scount));
$count = $count + 1;
(
print("<BR>Znam teraz $count liter<BR>");
}
$count = 0;
SayMyABCs3();
Scount = Scount + 1;
print{"Teraz wykonałem Scount wywołanie funkcji.<BR>"} ;
SayMyABCsS();
$count = Scount + 1;
print("Teraz wykonałem Scount wywołanie funkcji.<BR>") ;

Wersja ta daje w wyniku:


ABCDEFGHIJ
Znam teraz 10 liter
Teraz wykonałem l wywołanie funkcji.
KLMNOPQRST
Znam teraz 20 liter
Teraz wykonałem 2 wywołanie funkcji.

Słowo kluczowe static powoduje, że pierwsze przypisanie zachodzi, jeśli funkcja nie
była wcześniej wywoływana. Za pierwszym wywołaniem SayMyABCsS ( ) lokalna wer-
sja $count jest zerowana. Za drugim wywołaniem zmienna będzie miała już wartość
z poprzedniego wywołania. Zauważ, że zmiany zmiennej $ count na zewnątrz funkcji
nie mają wpływu na lokalną wartość zmiennej.
142______________________________________Część l » Podstawy PHP

Zasięg funkcji
Mimo że zasady rządzące zasięgiem zmiennych sąjasne i proste, zasady zasięgu funkcji
są jeszcze prostsze. W PHP 4 istnieje tylko jedna zasada: funkcje mogą być zdefinio-
wane tylko raz w skrypcie, który używa tej funkcji (zajrzyj do opisu nowej funkcji, do
omówienia pomiędzy wersjami PHP). Zasięg zmiennych jest domyślnie globalny, więc
funkcja zdefiniowana w skrypcie jest dostępna we wszystkich jego miejscach. Dla
większej przejrzystości dobrze jest definiować funkcje na początku skryptu, przed uży-
wającym ich kodem.

•4^Lj. W PHP 3 funkcje mogły być używane jedynie po ich definicji. Oznaczało
t0 ze na bez iecznie
funkcja ' J P Jszą praktyką było zdefiniowanie wszystkich funkcji
' (lub włączenie ich definicji) na początku skryptu, przed ich użyciem.
PHP 4 prekompiluje skrypty przed ich uruchomieniem, w związku z tym
ich definicje są znane wcześniej. Pozwala to na umieszczenie definicji
funkcji i kodu w dowolnej kolejności, pod warunkiem jednokrotnego wy-
stąpienia definicji każdej z funkcji.

Include oraz require


Często używa się tego samego zestawu zmiennych w wielu stronach witryny WWW.
Zwykle jest to realizowane poprzez użycie include bądź require, które to instrukcje
pozwalają na zaimportowanie zawartości innego pliku do pliku wykonywanego. Użycie
jednej z powyższych instrukcji jest wskazane do klonowania definicji funkcji pomiędzy
stronami, ponieważ zmienia się definicję tylko w jednym miejscu.

Na początku pliku wstawiasz np. wiersze:


include "proste-funkcje.inc";
include "zaawansowane-funkej e.inc";
( ... kod używający prostych i zaawansowanych funkcji ... )

które importują dwa różne pliki z definicjami funkcji. Nawiasy nie są obowiązkowe za-
równo w include ( ) , jak i require ( ) . Jeżeli pliki te zawierają tylko definicje funkcji,
kolejność włączania nie ma znaczenia.

Zarówno include, jak i require pozwalają na łączenie zawartości plików w miejscu


ich wywołania. W przeciwieństwie do include, instrukcja require nie wykona się
więcej niż raz, nawet jeżeli będzie wykonywana w pętli. PHP podstawia zawartość pli-
ku tylko przy pierwszym wywołaniu.

Niestety require nie jest magicznym rozwiązaniem problemu wielokrotnej definicji


tej samej funkcji. Instrukcja ta nie potrafi skontrolować innych wywołań require
i include. Jeżeli dwa razy powtórzymy w kodzie instrukcję require, żądany plik zo-
stanie załadowany dwa razy, definiując dwukrotnie zawarte w nim funkcje. Podobna
sytuacja zachodzi, gdy pliki z prostymi i zaawansowanymi funkcjami, użyte w przykła-
dzie, korzystają z tego samego pliku z podstawowymi funkcjami za pomocą require.
Rozdział 8. » Użycie i definiowanie funkcji_____________________________143

W tym przypadku również wystąpi błąd wielokrotnego definiowania funkcji, ponieważ


podstawowe funkcje zostaną załadowane dwa razy.

Jedną z prostych technik unikania wielokrotnego ładowania funkcji jest umieszczenie


instrukcji include w instrukcji if, np.:
if (!IsSet($myfuncs^loaded))
include ("myfuncs.inc");

$myf uncs_loaded jest zmienną ustawianą w pliku myfuncs.inc.

Rekurencja
Niektóre języki kompilowane, jak np.: C i C++, nakładaj ą skomplikowane ograniczenia
kolejności definiowania funkcji. Aby skompilować funkcję, kompilator musi znać
wszystkie wywoływane funkcje, co oznacza, że muszą być one wcześniej zdefiniowane.
A jeśli funkcje się wzajemnie wywołują? Takie problemy spowodowały oddzielenie
przez projektantów C deklaracji funkcji (prototypów) od ich definicji (implementacji).
Idea tego podziału była następująca: deklaracje informują kompilator o typach argu-
mentów i typie zwracanej wartości, co wystarcza kompilatorowi na poprawne przetwo-
rzenie definicji w dowolnym porządku.

W PHP problem ten zniknął, więc nie ma prototypów funkcji. W PHP 3 funkcje mu-
siały być zdefiniowane przed ich użyciem, lecz definiowanie w jednej funkcji wywoła-
nia innej nie jest liczone jako użycie funkcji. Gdy PHP 3 napotyka definicję funkcji A,
nie oponuje, jeżeli ciało funkcji A zawiera wywołanie funkcji B, która jeszcze nie jest
zdefiniowana. Funkcja B musi być zdefiniowana, gdy wywołana jest funkcja A. W PHP 4
w ogóle problem kolejności definicji nie istnieje

Funkcje rekurencyjne (wywołujące same siebie) nie sprawiają więc trudności. Dla
przykładu zdefiniujemy funkcję rekurencyjną i wywołamy ją.
function countdown (Snum_arg)
f
if ($num_arg > 0)
{
print("Odliczamy: $num_arg<BR>");
countdown( $num_arg-l );
l
) .

countdown(10);

Uruchomienie przykładu da w wyniku:


Odliczamy: 10
Odliczamy: 9
Odliczamy: 8
Odliczamy: 7
Odliczamy: 6
Odliczamy: 5
Odliczamy: 4
Odliczamy: 3
Odliczamy: 2
Odliczamy: l
144_________________________________________Część l » Podstawy PHP

Istotne jest, czy funkcja posiada część podstawową (gałąź nierekurencyjną) i część re-
kurencyjną, a także czy część podstawowa na pewno zostanie wykonana. Jeżeli część
podstawowa nie jest nigdy wykonywana, mamy sytuację podobną do pętli while z wa-
runkiem zawsze spełnionym, nieskończoną pętlę wywołań funkcji. Z analizy przykła-
dowej funkcji wiemy, że część podstawowa zostanie wykonana, ponieważ każde
rekurencyjne wywołanie zmniejsza wartość przekazywanej liczby, która w końcu bę-
dzie równa zero. Oczywiście zakładamy, że liczba jest dodatnią liczbą całkowitą, a nie
liczbą ujemną bądź liczbą double. Zauważ, że warunek „większy od zera" zabezpiecza
przed nieskończoną rekurencją nawet w takich przypadkach, gdy warunek „różny od ze-
ra" tego zabezpieczenia nie zapewnia.

Funkcje wzajemnie rekurencyjne działają podobnie (funkcje wywołujące się wzajemnie).


function countdown_first ($num_argl
(
if ($num_arg > 0)
{
print("Odliczanie (pierwsze): $num_arg<BR>");
countdown_second($num_arg - 1);
}
}
function countdown_second (Snum_arg)
(
if ($num arg > 0)
(
print("Odliczanie (drugie): $num_arg<BR>");
countdown_first($num_arg - 1);
l
)
countdown_first(5);

W wyniku otrzymamy:
Odliczan e (pierwsze): 5
Odliczan e (drugie): 4
Odliczan e (pierwsze): 3
Odliczan e (drugie): 2
Odliczan e (pierwsze): l

Zagadnienia zaawansowane
Zajmiemy się teraz bardziej zaawansowanymi zagadnieniami dotyczącymi funkcji: spo-
sobami użycia zmiennej liczby argumentów, modyfikacji przekazanych zmiennych oraz
użyciem funkcji jako wartości zmiennych.

W zasadzie jest to część przeznaczona dla zaawansowanych progra-


mistów ze względu na złożoność zagadnień.
Rozdział 8. » Użycie i definiowanie funkcji_____________________________145

Zmienna liczba argumentów


Liczba argumentów przekazanych do zmiennej powinna zależeć od sytuacji, w której
funkcja jest wywoływana. W PHP 4 istniej ą cztery sposoby obsłużenia takiej sytuacji:
1. Definicja funkcji z argumentami domyślnymi. Jeżeli brakuje argumentu w wy-
wołaniu funkcji, branajest wartość domyślna, ostrzeżenie nie jest drukowane.
2. Użycie jako argumentu tablicy. Kod wywołujący musi wstawić potrzebne ele-
menty do tablicy, wymagana jest również ich prawidłowa interpretacja w ciele
funkcji.
3. Użycie funkcji dla zmiennej liczby argumentów (func_num_args ( ) , func_
get_arg ( ) i func_get_args ( ) ) wprowadzonych do PHP 4.

Argumenty domyślne
Aby zdefiniować funkcję z argumentami domyślnymi, należy zmienić nazwę parametru
formalnego na wyrażenie przypisania. Jeżeli wywołanie będzie miało mniejszą liczbę
argumentów niż w definicji funkcji, PHP skojarzy wszystkie parametry aktualne z for-
malnymi, brakującym parametrom formalnym przyporządkuje wartości domyślne.

Poniższa funkcja ma zdefiniowane wartości domyślne dla wszystkich parametrów:


function tour_guide(Scity = "Gotham City",
Sdesc - "rozległa metropolia",
Show_many - "przez wielu",
$of_what = "opryszków")
(
printC'Scity to Sdesc zamieszkała $how_many $of_what.<BR>");
}
tour_guide();
tour_guide{"Chicago");
tour_guide("Chicago", "wspaniałe miasto");
tour_guide("Chicago", "wspaniałe miasto",
"przez kilka milionów"};
tour_guide("Chicago", "wspaniałe miasto",
"przez kilka milionów",
"miłych ludzi");

Wynik działania tego fragmentu programu jest pokazany poniżej.


Gotham City to rozległa metropolia zamieszkała przez wielu opryszków.
Chicago to rozległa metropolia zamieszkała przez wielu opryszków.
Chicago to wspaniałe miasto zamieszkałe przez wielu opryszków.
Chicago to wspaniałe miasto zamieszkałe przez kilka milionów opryszków.
Chicago to wspaniałe miasto zamieszkałe przez kilka milionów miłych ludzi.

Głównym ograniczeniem argumentów domyślnych jest fakt, że kojarzenie parametrów


formalnych z aktualnymi bazuje na kolejności argumentów. Oznacza to, że nie ma
możliwości przypisania wartości do końcowych parametrów przy użyciu argumentów
domyślnych i wartości domyślnych parametrów początkowych.
146_________________________________________Część l » Podstawy PHP

Tablice jako substytut wielu argumentów


Jeżeli brak elastyczności wielu argumentów funkcji przeszkadza ci, możesz w zamian
użyć tablicy jako kanału komunikacyjnego.

Poniższy przykład korzysta z tej techniki. Zastosowaliśmy dodatkowo kilka sztuczek,


takich jak: operator trójskładnikowy (wprowadzony w rozdziale 7.) oraz tablica asocja-
cyjna (wspomniana w rozdziale 6. i dokładnie opisana w rozdziale 11.).
function tour_brochure(Sinfo_array)
{
Scity =
IsSet($info_array['city1 ] ) ?
$info_array['city') : "Gotham City";
$desc =
IsSet($info_array['desc'] ) ?
$info_array['desc'] : "rozległa metropolia";
$how_many =
IsSet(Sinfo_array['how_many']) ?
5info_array['how_many'] : "przez wielu";
$of_what =
IsSet($info_array['of_what']) '?
$info array['of_what'] : "opryszków";

print("Scity to Sdesc zamieszkała $how_many $of_what.<BR>"};


)

Funkcja sprawdza zawartość tablicy dla czterech indeksów, reprezentowanych przez


odpowiednie fragmenty zdania. Przy użyciu operatora trójskładnikowego przypisywane
są do zmiennych wartości, przypisane do odpowiednich komórek tablicy, lub wartości
domyślne. Teraz wywołamy funkcję na kilka różnych sposobów:
tour_brochure(array()); // pusta tablica
$tour_info =
array( 'city' => 'Cozumel',
'desc1 => 'cel ucieczki',
'of_what' => 'dobrych ludzi');
tour_brochure( Stour_info );

W przykładzie tym wywołamy funkcję tour_brochure z pustą tablicą (brak argu-


mentów) oraz z trzema z czterech możliwych wartości asocjacyjnych. Wynik działania
tego fragmentu jest następujący:
Gotham City to rozległa metropolia zamieszkała przez wielu opryszków.
Cozumel to cel ucieczki zamieszkały przez wielu dobrych ludzi.

W obu przypadkach fragment „przez wielu" pochodzi z domyślnych wartości para-


metrów, ponieważ nic nie zostało przypisane do komórki tablicy identyfikowanej przez
„how_many".

Wiele argumentów w PHP 4


PHP 4 posiada funkcje, które, użyte w ciele funkcji, pozwalają na sprawdzenie liczby
przekazanych argumentów oraz odczytanie ich wartości.
f unc_num_args ( ) Nie posiada parametrów. Zwraca liczbę parametrów
przekazanych do funkcji.
Rozdział 8. » Użycie i definiowanie funkcji_____________________________147

f unc_get_arg ( ) Jako argument pobiera liczbę całko witą n i zwraca «-ty


argument przekazany do funkcji. Argumenty są
numerowane od 0.
f unc_get_args ( ) Nie posiada argumentów. Zwraca tablicę zawierającą
wszystkie argumenty przekazane do funkcji. Pierwszy
argument znajduje się w komórce o indeksie 0.

Każda z tych funkcji, wywołana na zewnątrz, spowoduje wygenerowanie ostrzeżenia.


f unc_get_arg ( ) spowoduje ostrzeżenie, jeżeli będzie wywołana z argumentem więk-
szym od liczby argumentów przekazanych do funkcji.

PHP nie wygeneruje ostrzeżeń, jeżeli funkcja zostanie wywołana z większą liczbą pa-
rametrów, niż wynika to z jej definicji. Możesz zdefiniować funkcję bez parametrów
i używać opisanych wcześniej funkcji do pobierania wartości przekazanych parametrów.

Jako przykład podaliśmy dwie funkcje zwracające tablicę argumentów, które zostały do
nich przekazane:
function args_as_array_l ()
{
$arg_count = func_num__args () ;
$counter = 0:
$local_array = array O;
while ($counter < $arg_count)
(
$local_array[Scounter] = func_get_arg($counter);
$counter = $counter + 1;
)
return($local_array};
)
function args_as_array_2 {)
(
return(func_get_args());
}

Pierwsza dłuższa funkcja używa f unc_get_arg ( ) do odczytania każdego argumentu


w pętli ograniczonej przez wartość odczytaną przez f unc_num_args ( ) , co zapewnia
odczytanie dokładnie tylu wartości argumentów, ile podano przy wywołaniu funkcji.
Każdy argument jest indywidualnie zapamiętywany w tablicy, którajest wynikiem dzia-
łania funkcji. Taki sam schemat działania jest wykonywany przez funkcję func_get_
args ( ) , więc druga funkcja jest krótka.

Przepiszmy teraz naszą funkcję tour_guide ( ) tak, aby skorzystać z funkcji do odczy-
tywania wartości parametrów, a nie z argumentów domyślnych:
function tour_guide_2(}
{
Snum_args = func_num_args();
$city = $num_args > O ?
func_get_args(0) : "Gotham City";
$desc = $num_args > l ?
func_get_args(1) : "rozległa metropolia";
$how_many = $num_args > 2 ?
func_get_args{2) : "przez wielu";
$of_what = $num_args > 3 ?
func_get_args(3) : "opryszków";
148___________________________________________Część l » Podstawy PHP

print("$city to Sdesc zamieszkała 3how_many $of_what.<BR>");


(
tour_guide_2();

Funkcja ta działa identycznie, jak jej poprzednia wersja, i posiada te same ograniczenia.
Argumenty są kojarzone ze sobą na podstawie ich kolejności, więc nie ma sposobu na
zmianę „opryszków" na cokolwiek innego, przy domyślnej wartości „Gotham City".

Wywołanie przez wartość a wywołanie przez referencję


Standardowo PHP przekazuje parametry za pomocą techniki nazywanej „wywołaniem
przez wartość". Oznacza to, że gdy przekazujesz zmienną do funkcji, PHP tworzy kopię
wartości zmiennej i przekazuje ją do funkcji. Wynik działania funkcji nie ma wpływu
na wartość zmiennej przekazanej jako aktualny parametr funkcji. Jest to dobre zabez-
pieczenie, jeżeli używasz tylko wartości zwracanej przez funkcję, ale może być źródłem
problemów, jeżeli chcesz, by zmieniła się wartość przekazywanego parametru.

Zademonstrujemy teraz wywołanie przez wartość przy bardzo słabej i niewydajnej im-
plementacji odejmowania:
function lame_subtract ($numl, Snum2)
(
if (Snuml < Snum2)
die("Liczby ujemne nie są obsługiwane");
$return_result = 0;
while(Snuml > Snum2)
(
$numl = Snuml - l;
$return_result = $return_result + 1;
)
return{$return_result};
)
$first_op = 493;
$second_op = 355;
Sresultl = lame_subtract($first^op, $second_op);
print("Wynik 1: $resultl<BR>");
$result2 = lame_subtract($first^op, $second_op);
print("Wynik 2: $result2<BR>"); ~

Upewnijmy się, że funkcja za każdym razem da identyczny wynik (dla tych samych
danych):
Wynik It 138
Wynik 2: 138

Mimo że lame_subtract zmienia wartość parametru formalnego $numl, zmienna ta


przechowuje kopię wartości zmiennej $f irst_op, więc $f irst_op nie zmieni się.

Wywołanie przez referencję


PHP zapewnia dwa różne sposoby modyfikacji parametrów funkcji: w definicji lub
w wywołaniu.

Jeżeli chcesz zdefiniować funkcję działającą bezpośrednio na przekazywanych zmien-


nych, umieść znak & przed parametrem formalnym w definicji, np.:
Rozdział 8. » Użycie i definiowanie funkcji_____________________________149

function lame_subtract_ref (&$numl, &$num2}


{
if ($numl < $num2)
die("Liczby ujemne nie są obsługiwane");
$return_result = 0;
while ($numl > Snum2)
{
$numl = $numl - 1;
$return_result = $return_result + 1;
)
return($return_result);
)
$first_op = 493;
Ssecond_op = 355;
Sresultl = lame_subtract_ref($first_op, $second_op);
print("Wynik 1: Sresult1<BR>");
Sresult2 = lame_subtract_ref($first_op, $second_op);
print("Wynik 2: $result2<BR>");

Jeżeli wykonamy powtórnie to samo odejmowanie, otrzymamy w wyniku:


Wynik 1: 138
Wynik 2: O

Dzieje się tak, ponieważ parametr formalny $numl odwołuje się do tej samej liczby, co
parametr aktualny $f irst_op, a zmiana jednej zmiennej powoduje zmianę drugiej.

Funkcja może pobierać argumenty przez referencję. Aby to wywołać, umieść znak &
przed parametrem aktualnym. Możemy użyć oryginalnej funkcji, przekazującej para-
metry przez wartość i uzyskać efekt przekazania przez referencję:
$first_op = 493;
$second_op = 355;
$resultl = lame_subtract(s$first_op, S$second_op);
print("Wynik 1: $resultl<BR>");
$result2 = lame_subtract(&$first op, &$second_op);
print("Wynik 2:~$result2<BR>");

co w efekcie da:
Wynik 1: 138
Wynik 2: O

Można również używać referencji do wywołań funkcji i do zmiennych. Przypisanie re-


ferencji (&$nazwa) do innej zmiennej spowoduje, że te dwie zmienne są synonimami,
a nie dwiema osobnymi zmiennymi z taką samą zawartością. Na przykład:
$name_l = "Manfred von Richthofen";
$name^2 = "Percy Blakeney";
Salias_l = $name_l; // zmienne mają tę samą wartość
$alias_2 = &$name_2; // zmienne są. tym samym

$alias_l = "The Red Baron"; // nie zmienia oryginalnej zmiennej


$alias_2 = "The Scarlet Pimpernel"; // zmienia oryginalna zmienna
print ("$alias_l to $name_KBR>") ;
print ("Salias_2 to $name_2<BR>");

da w efekcie:
The Red Baron to Manfred von Richthofen
The Scarlet Pimpernel to The Scarlet Pimpernel
150_________________________________________Część l » Podstawy PHP

Zmienne jako nazwy funkcji


Jedną ze sztuczek, jaką można zastosować w PHP, jest użycie zmiennej zamiast nazwy
funkcji zdefiniowanej przez programistę. Zamiast wpisywać nazwę funkcji, umiesz-
czasz w kodzie zmienną. Uruchomiona zostanie funkcja o takiej nazwie, jaką wartość
będzie miała zmienna. Sztuczka ta jest znana zaawansowanym programistom C i użyt-
kownikom języka Lisp (na przykład Scheme lub Common Lisp).

W poniższym przykładzie wywołania funkcji są tożsame:


function customized_greeting{)
(
print ("Bądź powitany!");
)
customized_greeting{);
Smy_greeting = 'customized_greeting';
Smy_greeting();

Wykonanie tego fragmentu da w efekcie:


Bądź powitany!
Bądź powitany!

Ponieważ nazwy funkcji są po prostu ciągami, mogą być argumentem lub wynikiem
działania funkcji.

Bardziej skomplikowany przykład


Spójrzmy teraz, w jakie kłopoty możemy popaść, używając niektórych bardziej za-
awansowanych własności funkcji (np. nazw funkcji jako argumentów wywołania).

Na wydruku 8.1 zamieściliśmy przykład implementacji szyfrowania przez podstawianie


— podstawowego rodzaju, który szyfruje komunikaty, zamieniając litery jednego alfa-
betu na inny.

Kod ten jest dłuższy i bardziej skomplikowany niż wszystkie dotych-


czasowe przykłady. Możesz go opuścić, jeżeli nie chcesz zagłębiać się
w szczegóły.

Wydruk 8.1. Szyfrowanie przez podstawianie__________________ ____ ___


/* Część l - algorytm szyfrowania i funkcje usługowe*/
function add_l ($num)
(
return(($num + 1) % 26);
}

function sub_l ($num)


f
return((Snum + 25) * 26);
)

function swap_2 ($num)


{
if ($nura % 2 == 0)
Rozdział 8. » Użycie i definiowanie funkcji_______________________________151

return($num + 1);
else
return{$num - 1);
}

function swap_26 ($num)


<
return (25 - $num);
)
function lower_letter(Schar_string)
(
return ((ord($char_string) >= ord('a')) &&
(ord ($char_string) <= ord (' z ')))/'
)
function upper_letter($char_string)
(
return {(ord($char_string) >= ord('A')) &&
(ord(Schar_string) <= ord('Z')));
}
/* Część 2 - funkcje szyfrowania liter*/
function letter_cipher ($char_string, $code_func)
{
if (!(upper_letter($char_string) I I
lower_letter($char_string)})
return($char_string) ;
if (upper_letter($char_string))
3base_num = ord('A');
else
$base_num = ord('a');

$char_num = ord($char_string) - $base_num;


return(chr($base_num + ($code_func($char_num) % 26))};
)

/* Część 3 - główna funkcja szyfrowania ciągu */


function string_cipher($message, $cipher_func)
(
$coded_message = "";
Smessage_length = strlen(Smessage);
for ($index = 0;
$index < $message_length;
$index++)
$coded_message .=
letter_cipher(Smessage[$index], $cipher_func);
return($coded_message);
)

Kod pokazany wydruku 8.1 składa się z trzech części. W pierwszej zdefiniowaliśmy kilka
funkcji działających na liczbach od O do 25, które reprezentują litery od A do Z naszego
szyfru. Funkcja add_l dodaje l do podanej liczby modulo 26 (co oznacza, że liczby 26
i większe są „zawijane" i rozpoczynają znów od zera). O jest zamieniane na l, l na 2, ...,
a 25 jest zamieniane na 0. Sub_l przesuwa liczby w odwrotnym kierunku, dodając 25 (co
w arytmetyce modulo jest odpowiednikiem odjęcia 1). Swap_2 zamienia miejscami pary
liczb: O na l, l na O, 2 na 3, 3 na 2 itd. Swap_26 zamienia małe liczby na duże (25 na O, O
na 25, 24 na l, l na 24 itd.). Każda z tych funkcji jest podstawą szyfrowania. Dodatkowo
mamy kilka funkcji sprawdzających, czy znak jest wielką czy małą literą.

Część drugą stanowi jedna funkcja letter_cipher ( ) , która za pomocą funkcji z pierw-
szej części koduje pojedynczy znak. Na początek sprawdza, czy podany ciąg składa się
z liter (powinien składać się z jednej litery); jeżeli nie, zwraca go bez szyfrowania.
152_________________________________________Część l » Podstawy PHP

Jeżeli znak jest literą, zamieniany jest na liczbę za pomocą funkcji ord ( } . Odejmowa-
na jest od niego wartość ASCII liter 'A' bądź 'a', co sprowadza liczbę do przedziału O -
25. Jeśli liczba znajduje się w tym przedziale, jest szyfrowana, konwertowana z powro-
tem do postaci litery i zwracana.

Trzecia część to funkcja string_cipher ( ) , która zwraca w postaci zaszyfrowanej


ciąg podany jako parametr. Używa kilku funkcji nieopisanych do tej pory (na przykład
operatora łączenia ciągów .=, który omówimy w rozdziale 9.). Funkcja ta tworzy nowy
ciąg z ciągu podanego jako parametr, szyfrując każdą literę za pomocą funkcji $ci-
pher_func().

Teraz napiszmy program używający funkcji string_cipher ( ) :


$original = "Zaszyfrowany komunikat to ABCDEFG";
print("Ciąg oryginalny to: $original<BR>");
$coding_array = array('add_l',
1sub~l',
1swap_2',
'swap_26');
for (Scount = 0;
Scount < sizeof($coding_array);
$count++)
(
$code = $coding_array[Scount];
$coded_message =
string_cipher($original, $code);
print("Zakodowane $code to: Scoded_message<BR>");
)

W tym testowym kodzie zapamiętaliśmy w tablicy nazwy czterech predefiniowanych


funkcji, szyfrujących litery i zakodowaliśmy ciąg zawarty w zmiennej Soriginal za
pomocą pętli. Wynik działania tego fragmentu jest następujący:
Ciąg oryginalny to: Zaszyfrowany komunikat to ABCDEFG
Zakodowane add_l to: Abtazgspxboz Ipnvojlbu up BCDEFGH
Zakodowane sub_l to: Yzryxeqnvzmx jnltmhjzs sn ZABCDEF
Zakodowane swap_2 to: Ybtyzeqpxbmz Ipnvmjlbs sp BADCFEH
Zakodowane swap_26 to: Azhabuildzmb plnfmrpzg gl ZYXWVUT

Możemy napisać funkcję, która stosuje więcej niż jeden sposób szyfrowania w ciągu.
Funkcja ta korzysta z funkcji odczytywania wartości argumentów w PHP 4.
function chained_code ($message)
f
/* Pobiera ciąg oraz dowolną liczbę nazw funkcji kodujących,
zwraca wynik zastosowania w ciągu wszystkich metod */

$argc = func_num_args() ;
$coded = Smessage;
for (Scount = 1; Scount < Sargc; Scount++)
<
Sfunction_name = func_get_arg($count);
Scoded = string_cipher(Scoded, $function_name);
}
return(Scoded};
}

Pierwszym argumentem chained_code ( ) powinien być ciąg do szyfrowania, następ-


nym dowolna liczba nazw funkcji szyfrujących. Zakodowany ciąg jest wynikiem użycia
pierwszej funkcji szyfrującej, następnie drugiej funkcji — w ciągu wynikowym itd.
Możemy przetestować tę funkcję przy użyciu różnych kombinacji funkcji szyfrujących.
Rozdział 8. » Użycie i definiowanie funkcji_____________________________153

Stricky =
chained__code (Soriginal,
1 t
add_l', swap_26'/
1
add_l', 'swap_2');
print("Zakodowana wersja to $tricky<BR>");
$easy =
chained_code(Soriginal,
'add_l', 'swap_26',
1 swap_2', 'sub_l',
1 add_l', 'swap_2',
'swap_26', 'sub_l');
print("Zakodowana wersja to $easy<BR>");

Daje to w efekcie:
Zakodowana wersja to Bygbavjkcyna okmenqoyh hk YZWXUVS
Zakodowana wersja to Zaszyfrowany komunikat to ABCDEFG

Pierwsza wersja zakodowania ciągu jest kombinacją poprzednich kodowań, jednak nie
jest dokładnym odpowiednikiem żadnego z pojedynczych kodowań. Druga wersja jest
jeszcze bardziej skomplikowaną kombinacją funkcji, jednak daje niezmieniony komu-
nikat! Nie oznacza to, że nasze funkcje szyfrujące nie działają.

Morał naszej kryptograficznej opowieści jest taki: ponieważ kod szyfrujący jest nieco
skomplikowany, warto skorzystać z przekazywania nazw funkcji jako argumentów
funkcji.

Podsumowanie
Wartość PHP jest ukryta w dużej liczbie funkcji wbudowanych, dostarczonych przez
armię programistów PHP. Każda z nich jest udokumentowana (nawet zwięźle) w pod-
ręczniku, pod adresem http://www.php.net. Możesz również pisać własne funkcje, uży-
wane dokładnie w ten sam sposób, co funkcje wbudowane. Funkcje pisze się za pomocą
prostej składni w stylu C:
function moja_funkcja( $argl, $arg2, ...)
f
wyrażeniel;
wyrażenie2;

return ($wartość);
)

Funkcje mogą używać argumentów dowolnego typu, mogą również zwracać dowolnego
typu argumenty, które nie muszą być deklarowane.

Kolejność deklaracji funkcji i wywołania funkcji nie ma znaczenia, ale funkcja powinna
być zdefiniowana tylko raz. Nie ma potrzeby oddzielania deklaracji funkcji lub ich pro-
totypów. Zmienne zainicjowane w funkcji są zmiennymi lokalnymi funkcji, chyba że są
zadeklarowane jako globalne. Zmienne lokalne mogą być zadeklarowane jako statyczne,
co oznacza, że przechowuj ą wartość pomiędzy kolejnymi wywołaniami funkcji.

Domyślnym sposobem przekazywania argumentów jest wywołanie przez wartość. Ozna-


cza to, że funkcje pracują na kopiach argumentów, dlatego nie mogą zmodyfikować
wartości oryginalnych. Możesz ustawić przekazywanie przez wartość, poprzedzając pa-
154_________________________________________Część l » Podstawy PHP

rametr znakiem & w wywołaniu albo w definicji funkcji. Nazwy wywoływanych funkcji
mogą być ustalane w czasie pracy programu poprzez użycie zmiennej jako nazwy funk-
cji — pozwala to na traktowanie funkcji jak danej i przekazywanie jej jako parametru
innych funkcji.
Rozdział 9.
Ciągi i funkcje
operujące na ciągach
W tym rozdziale:
* Tworzenie ciągów i manipulowanie nimi
+ Sprawdzanie, porównywanie i przeszukiwanie ciągów
* Zaawansowane operacje na ciągach przy użyciu wyrażeń regularnych
* Funkcje obsługi kodu HTML

Mimo że rysunki, dźwięki, animacje i aplety stają się coraz ważniejszą częścią sieci
WWW, nadal bazuje ona na tekście. Podstawowym typem PHP przechowującym tekst
jest typ string.

Ciągi w PHP
Ciągi to sekwencje znaków traktowane jako odrębna jednostka. Mogą być przypisywa-
ne do zmiennych, używane jako parametry funkcji, zwracane z funkcji lub wysyłane na
wyjście i oglądane w przeglądarce klienta.

Najprostszą metodą stworzenia ciągu w PHP jest otoczenie znaków cudzysłowami (")
bądź apostrofami ('):
$ciag = 'Ciąg1;
$inny_ciag = "Inny ciąg";

Różnica pomiędzy ciągami w cudzysłowach i apostrofach leży w interpretacji ich przez


PHP. Jeżeli otoczysz ciąg apostrofami, prawie żadna interpretacja nie zostanie zastoso-
wana, jeżeli zaś otoczysz cudzysłowami, PHP wklei wartości zmiennych w miejsce ich
nazw i zamieni sekwencje sterujące na odpowiadające im znaki. Jeżeli np. wstawisz na-
stępujący kod do strony:
156_________________________________________Część l » Podstawy PHP

$wyrazenie = 'wszystko, co powiem';


$pytanie_l =
"Czy musisz brać $wyrazenie doslownie?\n<BR>";
$pytanie_2 =
'Czy musisz brać Swyrazenie dosłownie?\n<BR>';
echo Spytanie_l;
echo $pytanie_2;

powinieneś spodziewać się następującego wyniku:


Czy musisz brać wszystko, co powiem, dosłownie?
Czy musisz brać Swyrazenie dosłownie?\n

\ natrA Szczegółowy opis traktowania ciągów z apostrofami i cudzysłowami


lakffi znajduje się w części „String" w rozdziale 6.

Znaki i indeksy ciągu


W przeciwieństwie do większości języków programowania, PHP nie posiada osobnego
typu znakowego. Zwykle funkcje, które w innych językach pobierają znak, w PHP spo-
dziewają się ciągu o długości 1.

Można uzyskać znak z ciągu, traktując ciąg jak tablicę z pierwszym indeksem równym
0. Uzyskany w ten sposób znak jest jednoliterowym ciągiem. Na przykład:
$ciag - "Podwojony";
for (Sindex = O ; $index < 9; $index++ )
print("$ciag[$index]$ciąg t $index]") ;

daje w efekcie:
PPooddwwooj joonnyy

Każda litera została wypisana dwa razy (liczba 9 wpisana jest w tym przykładzie dlatego,
że nie wiemy jeszcze, jak określić długość ciągu — ponatrz r.a opis funkcji strlen ()
w części „Sprawdzanie ciągów").

Operatory dla ciągów


PHP posiada tylko jeden prawdziwy operator dla ciągów: kropkę (.) — operator złącze-
nia. Operator ten umieszczony pomiędzy ciągami tworzy nowy ciąg, będący ich skleje-
niem. Na przykład:
$zdanie_l = "Chciałbym podzielić się z wami ";
$zdanie_2 = "moimi uwagami";
print ($zdanie_l. $zdanie_2. "...");

da w efekcie:
Chciałbym podzielić się z wami moimi uwagami...

Zauważmy, że nie przekazujemy do instrukcji print wielu argumentów — przekazu-


jemy jeden argument stworzony przez połączenie trzech ciągów. Pierwszy i drugi ciąg
to zmienne, trzeci jest literałem.
Rozdział 9. » Ciągi i funkcje operujące na ciągach__________________________157

Operator złączenie nie jest tym samym co operator + w języku Java,


nie przeciąża innego operatora. Jeżeli pomylisz się i dodasz dwa ciągi
za pomocą +, zostaną zinterpretowane jako liczby. Na przykład jeden
+ dwa będzie równe O (ponieważ nie udało się przeprowadzić prawi-
dłowej konwersji ciągów).

Złączenie i przypisanie
PHP posiada operator skrócony (.=) który jest złączeniem z przypisaniem. Wyrażenie:
$ciag .= $dodatek;

jest równoznaczne z:
$ciag = Ściąg . Sdodatek;

Przy użyciu tego operatora nowy ciąg jest dodawany po prawej stronie starego. Jeżeli
chcesz zmienić kolejność, musisz użyć dłuższej formy:
$ciag = Sdodatek . Ściąg;

Funkcje operujące na ciągach


PHP zawiera wiele funkcji działających na ciągach. Jeżeli zamierzasz napisać własną
funkcję, która analizuje ciąg znak po znaku, aby stworzyć nowy ciąg, pomyśl, które z tych
zadań będą często wykonywane. Jeżeli takie wystąpią, prawdopodobnie istnieje wbudo-
wana funkcja realizująca tę czynność. W tej części zapoznamy się z podstawowymi funk-
cjami sprawdzającymi, porównującymi, modyfikującymi i drukującymi ciągi.

Dla programistów C. Powinniście znać wiele funkcji operujących na


ciągach. Należy jedynie zapamiętać, że PHP zajmuje się przydziałem
pamięci. Funkcje zwracające ciągi rezerwują pamięć na wynikowy ciąg
i nie ma potrzeby wcześniej rezerwować pamięci.

Sprawdzanie ciągów
Jakie pytania można zadać na temat ciągu? Na początek sprawdźmy przy użyciu funkcji
strlen ( ) , ile znaków zawiera.
Ściąg = "Ten ciąg zawiera 26 znaków";
print("To ma", strlen(Ściąg)."znaków");

Uruchomienie tego kodu daje następujący wynik:


To ma 26 znaków
158_________________________________________Część l « Podstawy PHP

Odczytanie długości ciągu jest użyteczne w sytuacjach, gdy chcemy za pomocą pętli
dostać się do wszystkich znaków ciągu. Bezużytecznym, ale pouczającym przykładem
może być (używamy ciągu z poprzedniego przykładu):
for (Sindex=0; $index <strlen ($ciag); $index++)
print("$ciag[$index]");

W przeglądarce pojawi się:


Ten ciąg zawiera 26 znaków

Szukanie znaków i podciągów


Następną kategorią pytań na temat ciągów może być pytanie o ich zawartość. Funkcja
strpos ( ) szuka pozycji określonego znaku w ciągu, o ile w nim występuje.
Stwister = "Król Karol kupił królowej Karolinie";
print("'k' występuje na pozycji", strpos($twister, 'k'). "<BR>");
print("'q' występuje na pozycji", strpos(Stwister, 'q'). "<BR>");

Wykonanie tego przykładu daje w efekcie:


'k' występuje na pozycji 11
'q1 występuje na pozycji

Pozycja litery 'q' pozostała niewypełniona, ponieważ funkcja strpos () zwraca FALSE
w przypadku nieznalezienia znaku, a FALSE zostało skonwertowane do pustego ciągu.

Funkcja strpos o jest jednym z przykładów, kiedy brak ścisłego okre-


ślenia typów może stanowić problem. Jeżeli nie zostanie odnaleziona
wartość, funkcja zwraca wartość FALSE. Jeżeli odnaleziony zostanie
pierwszy znak ciągu, funkcja zwróci zero (ponieważ indeksowanie rozpo-
czyna się od zera). Jeżeli użyjemy tych wartości w wyrażeniu logicznym,
obie zostaną zinterpretowane jako FALSE. Jedynym sposobem rozróż-
nienia tych wartości jest użycie operatora sprawdzającego identyczność
obiektów (===, wprowadzony w PHP 4), który jest prawdziwy, jeżeli jego
argumenty są identyczne i mają te same typy. Możesz go bezpiecznie
użyć do porównania O z FALSE. Jeżeli używasz PHP 3, musisz sprawdzić
typ zwracanej wartości, używając funkcji np. is_ integer ( ) .

Za pomocą funkcji strpos ( ) można również szukać ciągów, nie tylko pojedynczych
znaków. Możesz także podać dodatkowy parametr numeryczny określający pozycję, od
której funkcja ma rozpocząć szukanie.

Możliwe jest też szukanie wstecz przy użyciu funkcji strrpos ( ) (dodatkowe 'r' po-
chodzi od słowa „reverse", czyli w tył). Funkcja ta pobiera ciąg do szukania oraz poje-
dynczy znak, którego szukamy. W przeciwieństwie do strpos ( ) , nie można podać
podciągu do odszukania. Jeżeli użyjemy tej funkcji w naszym przykładowym zdaniu,
odszukamy inne wystąpienie litery k.:
Stwister = "Król Karol kupił królowej Karolinie";
print("'k' występuje na pozycji", strrpos(Stwister, 'k'). "<BR>");
Rozdział 9. » Ciągi i funkcje operujące na ciągach________________________159

Czy ciągi są niezmienne?


W niektórych językach programowania (np. C) normalne jest manipulowanie
ciągami przez ich bezpośrednią modyfikację, na przykład zapisywanie nowych
znaków w środku ciągu. Inne języki (np. Java) trzymają programistów z daleka
od takich operacji, tworząc klasę reprezentującą ciągi, które są niezmienne.
Można utworzyć nowy ciąg tylko poprzez stworzenie zmodyfikowanej kopii cią-
gu. Po jego utworzeniu nie można bezpośrednio zmieniać jego znaków.
W PHP ciągi mogą być zmieniane, ale w powszechnej praktyce jest trakto-
wanie ciągów, jak gdyby były niezmienne.
Można zmieniać ciąg, traktując go jako tablicę znaków i przypisywać bezpo-
średnio do tej tablicy:
Ściąg = "abcdefg";
Sciag[5] = "X";
print (Ściąg . "<BR>");

Daje to następujący wynik:


abcdeXg

Taka operacja jest jednak nieudokumentowana i nie występuje nigdzie w pod-


ręczniku, chociaż analogiczne odczytywanie znaków jest zamieszczone. Prawie
wszystkie funkcje operujące na ciągach zwracają zmienioną kopię ciągu, a nie
przeprowadzają zmian na oryginale. Projektanci PHP preferują ten styl pro-
gramowania. Radzimy nie modyfikować bezpośrednio ciągów, chyba że dzięki
temu oszczędza się pamięć.

W tym przypadku odnajdziemy pierwsze 'k' w słowie „królowej":


'k' występuje na pozycji 17

Porównywanie i przeszukiwanie
Często trzeba sprawdzać, czy dwa ciągi są identyczne. Szczególnie często w programach
pracujących na danych wprowadzonych przez użytkownika.

Najprostszą metodą porównania ciągów jest zastosowanie operatora równości (==), któ-
ry porównuje zarówno liczby, jak i ciągi.

Ciągi są takie same dla operatora ' = = ' , jeżeli zawierają dokładnie
taką samą sekwencję znaków. Operator ten nie wykonuje żadnych
dokładniejszych porównań (np. sprawdzenia obszarów pamięci).

Podstawową funkcją do porównywania ciągów jest strcmp ( ) . Posiada dwa ciągi jako
argumenty, porównuje je znak po znaku aż do znalezienia różnicy. Funkcja zwraca
wartość ujemną, jeżeli pierwszy ciąg jest „mniejszy" od drugiego, wartość dodatnią,
gdy drugi ciąg jest mniejszy, oraz O, jeżeli ciągi są identyczne.
160 Część lI »» Podstawy
Podstawy PHP
PHP

Porównanie ciągów za pomocą operatora ' = = ' (a także < i >) jest
prawidłowe tylko w przypadku, gdy oba argumenty są takimi ciągami,
nie wykonano żadnej konwersji typów (więcej na ten temat w rozdzia-
le 6.). Jedynie używając funkcji strcmpo otrzymujemy zawsze pra-
widłowe wyniki.

Funkcja strcasecmpO nie bierze pod uwagę wielkości liter podczas porównywania
ciągów. Wywołanie funkcji: strcasecmp ( " h e j ! " , " H E J ! " ) powinno zwrócić 0.

Przeszukiwanie
Funkcje porównujące ciągi sprawdzają, czy ciągi są identyczne. Aby sprawdzić, czy je-
den ciąg zawiera się w drugim, używamy funkcji strpos () (opiszemy ją później) lub
strstr () (ewentualnie jednej z jej odmian).

Funkcja strstr () oczekuje w pierwszym parametrze ciągu do przeszukania, a w drugim


ciągu do odszukania. Jeżeli operacja się powiedzie, funkcja zwróci fragment przeszuki-
wanego ciągu rozpoczynający się od pierwszego miejsca wystąpienia poszukiwanego
fragmentu. Jeżeli nic nie zostanie odnalezione, zwracana jest wartość FALSE. W poniż-
szym przykładzie zamieściliśmy jedno udane i jedno nieudane wywołanie funkcji.
$ciag_do_przeszukania = "pierwszaoladrugaola";
Sciag_do_odszukania = "ola";
print("Wynik szukania ciągu Sciag_do_odszukania: " .
strstr($ciag_do_przeszukania, Sciag_do_odszukania));
$ciag_do_odszukania = "ula";
print ("Wynik szukania ciągu Sciag__do_odszukania w " .
strstr($ciag_do_przeszukania, $ciag_do_odszukania));

Wykonanie tego fragmentu da następujący wynik:


Wynik szukania ciągu ola: oladrugaola
Wynik szukania ciągu ula:

Puste miejsce za dwukropkiem w drugim wierszu wyniku jest wynikiem próby wydru-
kowania wartości FALSE, która została skonwertowana na pusty ciąg.

Funkcja strstr () posiada alternatywną nazwę strchr (). Można używać dowolnej
z nazw tej funkcji, wynik działania będzie identyczny.

Podobnie jak strcmp (), strstr () posiada odmianę, która przy szukaniu ciągów iden-
tycznie traktuje małe i wielkie litery — stristr (). Funkcja ta działa identycznie jak
strstr (), nie rozróżnia jednak wielkości liter.

Dotychczas opisane funkcje operujące na ciągach zebraliśmy w tabeli 9.1.


Rozdział 9. » Ciągi i funkcje operujące na ciągach________________________161

Tabela 9.1.
Funkcje porównujące, przeszukujące i badające ciągi

Funkcja Opis

Strlen ( ) Zwraca długość ciągu podanego jako argument wywołania


Strpos ( ) Posiada dwa argumenty: ciąg do przeszukania i ciąg do znalezienia. Zwraca pozycję
(licząc od zera) początku pierwszego miejsca wystąpienia ciągu lub wartość FALSE,
jeżeli ciąg nie został odszukany. Można podać trzeci argument określający pozycję,
od której rozpocząć szukanie
Strrpos ( ) Jak strpos ( ) , ale przeszukuje ciąg od tyłu. Poszukiwany ciąg może składać się tylko
z jednej litery. Nie ma opcjonalnych argumentów
Strcmp ( ) Spodziewa się dwóch ciągów jako argumentów. Zwraca O, jeżeli ciągi te są identyczne.
Jeżeli funkcja znajdzie różnicę, zwraca wartość ujemną, jeżeli kod ASCII pierwszego
różniącego się znaku jest mniejszy w pierwszym ciągu, lub wartość dodatnią, jeżeli
mniejszy kod ASCII znajduje się w drugim ciągu
Strcasecmp ( ) Działa tak jak strcmp ( ) , traktując identycznie małe i wielkie litery
Strstr () Przeszukuje pierwszy ciąg, szukając w nim drugiego. Zwraca fragment pierwszego
ciągu rozpoczynający się od poszukiwanego fragmentu. Jeżeli nie ma drugiego ciągu
w pierwszym, zwraca FALSE
Strchr () Działa tak samo jak strstr ()
Stristr () Działa tak samo jak strstr (), nie rozróżniając wielkości liter

Wycinanie podciągu
Wiele z funkcji operujących na ciągach ma na celu wybranie określonego podciągu lub
modyfikację go w ciągu oryginalnym. Warto wiedzieć, że większość funkcji modyfiku-
jących ciąg nie zmienia oryginalnego ciągu, ale zwraca zmienioną kopię pozostawiając
oryginał nietknięty.

Podstawową metodą wycięcia fragmentu ciągu jest użycie funkcji substr ( ) , która
zwraca ciąg będący określonym fragmentem ciągu przekazanego jako argument. Oprócz
ciągu, na którym operuje, funkcja wymaga podania liczby określającej początek wyci-
nanego ciągu. Trzeci argument jest opcjonalny i określa długość wynikowego podciągu.
Jeżeli nie zostanie on podany, funkcja zwraca fragment od podanej pozycji (za pomocą
drugiego argumentu) do końca ciągu. Należy pamiętać, że pierwszy znak ciągu znajduje
się na pozycji 0.

Spójrzmy na następujący przykład:


echo (substr("Wycinanie ciągów w PHP jest proste", 23));

Zwróci on w wyniku ,jest proste"; wyrażenie:


echo (substr("Wycinanie ciągów w PHP jest proste", 10, 6));

wypisze słowo „ciągów" — sześcioznakowy ciąg rozpoczynający się od pozycji 10.


162_________________________________________Część l « Podstawy PHP

Oba argumenty numeryczne, pozycja początkowa i długość mogą być ujemne. Jeżeli
pozycja początkowa jest ujemna, oznacza to, że początek podciągu jest określany wzglę-
dem końca ciągu. Pozycja -l oznacza początek ciągu na ostatnim znaku, -2 na przed-
ostatnim itd.

Można oczekiwać, że podanie ujemnej długości zinterpretowane będzie analogicznie do


pozycji początkowej i podciąg będzie określony odliczając wstecz od początkowego
znaku ciągu. Nie jest to do końca prawdziwe. Znak określony przez indeks początkowy
będzie pierwszym znakiem podciągu, a jego długość określona będzie odliczeniem poda-
nej liczby znaków od końca ciągu.

Spójrz na kilka przykładów, w których użyte zostały dodatnie i ujemne wartości:


$alfabet = "abcdefghijklmnop";
print ("3:". substr( $alfabet, 3). "<BR>");
print ("-3:". substr( $alfabet, -3). "<BR>");
print ("3,5:". substrl $alfabet, 3, 5). "<BR>");
print ("3,-5:". substrl Salfabet, 3, -5). "<BR>");
print ("-3, -5:". substrl Salfabet, -3, -5). "<BR>");
print l"-3, 5:". substrl $alfabet, -3, 5). "<BR>");

W wyniku otrzymujemy:
3: defghijklmnop
-3: nop
3,5: defgh
3,-5: defghijk
-3,-5:
-3, 5: nop

•*&
pJ^fejL W przykładzie z pozycją początkową -3 i długością -5 pozycja końco-
jjggsjf wa znajduje się przed pozycją początkową, co jest sytuacją określaną
•^- jako „ciąg o ujemnej długości". W podręczniku, dostępnym pod adre-
sem http://www.php.net/manual, jest napisane, że w takiej sytuacji
funkcja substr o zwróci ciąg zawierający jeden znak znajdujący się
na pozycji początkowej. Zamiast tego widzimy, że PHP 4.0.0 zwraca
pusty ciąg. Należy szczególnie zwrócić uwagę na takie sytuacje.

Zauważmy, że pomiędzy funkcjami substr (), strstr () i strpos () występuje bli-


ski związek. Funkcja substr () wycina podciąg bazując na pozycji, strstr () wycina
podciąg na podstawie zawartości a strpos () znajduje położenie podciągu. Jeżeli jeste-
śmy pewni, że $ciag zawiera $podciag, wtedy wyrażenie:
s t r s t r ( $ c i a g , $podciag)

jest równoważne wyrażeniu:


s u b s t r ( Ś c i ą g , strpos($ciag, p o d c i ą g ) )

Funkcje porządkujące
Mimo że funkcje c h o p ( ) , l t r i m ( ) i t r i m ( ) są zwykłymi funkcjami wycinającymi
podciąg, używa się ich do porządkowania ciągów. Funkcje te wycinają znaki odstępów
z początku, końca lub początku i końca ciągu. A oto przykład:
Rozdział 9. » Ciągi i funkcje operujące na ciągach________________________163

Soryginal = " To przechodzi ludzkie pojęcie ";


$f_chop = chop($oryginal);
$f_ltrim = ltrim($oryginal);
Sf_trim = trim($oryginal);
print("Ciąg oryginalny: 'Soryginal'<BR>");
print{"Długość ciągu:", strlen($oryginal). "<BR>");
print("Po funkcji chop: '$f_chop' <BR>");
print("Długość ciągu:", strlen($f_chop). "<BR>");
print("Po funkcji Itrim: '$f_ltrim' <BR>");
print("Długość ciągu:", strlen($f_ltrim). "<BR>"1;
print("Po funkcji trim: '$f_trim' <BR>");
print("Długość ciągu:", strlen($f_trim). "<BR>"};

W przeglądarce dostajemy:
Ciąg oryginalny: ' To przechodzi ludzkie pojęcie '
Długość ciągu:33
Po funkcji chop: ' To przechodzi ludzkie pojęcie'
Długość ciągu:30
Po funkcji Itrim: 'To przechodzi ludzkie pojęcie '
Długość ciągu:32
Po funkcji trim: 'To przechodzi ludzkie pojęcie1
Długość ciągu:29

Ciąg oryginalny ciąg ma na końcu trzy spacje (usuwane przez chop ( ) i trim ( ) ) oraz
jedną na początku (usuwana przez Itrim ( ) i t r i m ( ) ) . Gdyby nazewnictwo funkcji
było spójne, funkcja chop ( ) powinna nazywać się rtrim ( ) . Dokładniej opiszemy za-
wartość okna przeglądarki po wykonaniu tego fragmentu. Powtarzające się odstępy zo-
stały przez przeglądarkę połączone w jeden. Jeżeli jednak zajrzysz do źródła strony, to
na końcu ciągu nadal znajdują się trzy odstępy.

Oprócz spacji funkcje te usuwają znaki zapisane jako sekwencje sterujące \n, \r, \t
oraz \ O (znaki końca wiersza, tabulatory oraz znak końca ciągu używany w programach
napisanych w C).

Mimo że nazwa funkcji chop ( ) (ang. rąbać) sugeruje destrukcyjne działanie, nie wpływa
ona na ciąg przekazany jako argument. Po wykonaniu funkcji ciąg nie zmienia się.

Zastępowanie ciągów
Funkcje operujące na ciągach, które omówiliśmy do tej pory, zwracały fragment ciągu
przekazanego jako argument, zamiast tworzyć całkowicie nowy ciąg. Teraz zajmiemy
się funkcjami str_replace ( ) i substr_replace ( ) .

Funkcja str_replace ( ) pozwala zamienić wszystkie miejsca wystąpienia podanego


fragmentu ciągu na inny. Funkcja oczekuje trzech argumentów: ciągu do odszukania,
ciągu, który zamieni szukany fragment oraz ciągu źródłowego. Na przykład:
$pierwszy = "Birma jest nieco podobna do Rodezji.";
Sdrugi = str_replace("Rodezji", "Zimbabwe", $pierwszy);
$trzeci = str_replace("Birma", "Panama", $drugi);
print($trzeci);
164_________________________________________Część l » Podstawy PHP

Wynikiem tych operacji jest zdanie:


Panama jest nieco podobna do Zimbabwe.

Zastępowane są wszystkie miejsca wystąpienia podanego fragmentu. Jeżeli do ciągu


PHP uda się wtłoczyć całą encyklopedię, można j ą będzie zaktualizować jednym wy-
wołaniem.

Musisz zwrócić uwagę na sytuację, gdy wiele miejsca wystąpienia ciągu nachodzi na
siebie. Fragment programu:
$ciag = "ABA jest częścią ABABA";
Swynik = str_replace("ABA", "DBF", $ciag);
print("Wynik zamiany to '$wynik'<BR>"};

wyświetli ciąg:
Wynik zamiany to 'DEF jest częścią DEFBA'

Jest to chyba najrozsądniejsze działanie.

Funkcja str_replace ( ) odszukuje fragmenty ciągu do wymiany, porównując ciąg


źródłowy z fragmentem podanym jako parametr; funkcja substr_replace ( } zamie-
nia fragment ciągu znajdujący się na określonej pozycji. Można ją wywołać maksymal-
nie z czterema argumentami: ciągiem, na którym wykonuje się operacja, ciągiem, który
jest wstawiany, początkową pozycją oraz parametrem opcjonalnym — długością frag-
mentu do wymiany. Na przykład:
print(substr_replace("ABCDEFG", "-", 2, 3);

daje w wyniku:
AB-FG

Fragment CDE został zamieniony na pojedynczy znak minus. Możemy więc zastępo-
wać fragmenty ciągu ciągami o innej długości. Jeżeli nie zostanie podana długość frag-
mentu, wymieniony zostanie fragment ciągu od pozycji startowej do końca ciągu.

Funkcja substr_replace ( ) pozwala również na używanie ujemnych wartości argu-


mentów numerycznych. Są one traktowane identycznie jak ujemne wartości argumen-
tów w funkcji substr ( ) ; opisaliśmy je w części „Wycinanie podciągu".

Mamy również kilka rzadziej używanych funkcji, które tworzą nowy ciąg na podstawie
podanego jako parametr. Funkcja strrev () zwraca ciąg podany jako argument, ale pi-
sany wspak. Funkcja str_repeat ( ) tworzy ciąg zawierający ciąg źródłowy powtórzo-
ny daną liczbę razy. Przykładowo:
print(str_repeat("witaj ", 3));

daje w wyniku:
witaj witaj witaj

Funkcje przeszukujące i zamieniające ciąg zebrane zostały w tabeli 9.2.


Rozdział 9. » Ciągi i funkcje operujące na ciągach________________________165

Tabela 9.2.
Funkcje przeszukujące i zamieniające ciąg

Funkcja Działanie

substr ( ) Zwraca fragment ciągu opisany przez drugi argument, określający pozycję
początkował opcjonalny trzeci argument, określający długość. Fragment
rozpoczyna się na pozycji startowej i ma długość podaną w trzecim argumencie,
a jeżeli nie został podany trzeci argument, obejmuje ciąg do końca. Ujemna
wartość pozycji startowej oznacza, że jest określana od końca ciągu, ujemny
parametr określający długość powoduje, że koniec podciągu jest wyznaczany
przez podaną liczbę znaków od końca ciągu
chop ( ) Zwraca ciąg podany jako argument bez końcowych znaków odstępu. Znakami
odstępu są znaki " ", \n, \r, \t i \0
itrim ( ) Zwraca ciąg podany jako argument bez początkowych znaków odstępu
trim ( ) Zwraca ciąg podany jako argument bez początkowych i końcowych znaków
odstępu
str_replace ( ) Zamienia podany fragment ciągu na inny. Funkcja ma trzy argumenty: ciąg do
odszukania, ciąg, na który jest on zamieniany oraz ciąg bazowy. Zwraca kopię
z zamienionymi na drugi argument wszystkimi miejscami wystąpienia pierwszego
argumentu
substr_replace ( ) Wstawia fragment podany jako argument na pozycję określoną przez parametry
numeryczne. Funkcja posiada cztery argumenty: ciąg bazowy, fragment
wstawiany, pozycję początkową i liczbę znaków do wymiany. Zwraca kopię ciągu
podanego jako pierwszy argument, z ciągiem do zamiany wstawionym na
określonej pozycji
Jeżeli opuszczony zostanie czwarty argument, koniec ciągu zostanie zamieniony
na fragment przekazany w argumencie. Ujemne wartości parametrów traktowane
są identycznie jak w funkcji substr ( )

Ciągi i kolekcje znaków


Oprócz poszukiwania podciągów, PHP oferuje kilka wyspecjalizowanych funkcji, które
traktuj ą ciąg jako kolekcję, a niejako sekwencję znaków.

Pierwsza z nich to funkcja strspn ( ) , za pomocą której można określić, jaka część cią-
gu składa się tylko ze znaków podanych jako argument. Przykładowo:
$twister = "Król Karol kupił królowej Karolinie";
Sznaki = "Król kupił Karolinie";
print {"Fragment zawierający '$znaki' ma" .
strspn($twister, $znaki). "znaki");

daje w wyniku:
Fragment zawierający 'Król kupił Karolinie' ma 22 znaki

Pierwszym znakiem nieodnalezionym w ciągu $ znaki jest w „w" słowie „królowej".


166_________________________________________Część l » Podstawy PHP

Funkcja strcspn ( ) działa bardzo podobnie, ale szuka znaków, które nie znajdują się
w podanym ciągu. Na przykład wywołanie:
echo (strcspn($twister, "abcd"));

spowoduje wypisanie liczby 6, ponieważ pierwszych sześć znaków ciągu nie zawiera
znaków z ciągu „abcd".

Na koniec przyjrzyjmy się fragmentowi, który wylicza statystykę liter (przykład korzy-
sta z kilku nieopisanych jeszcze własności ciągów).
$twister = "Król Karol kupił królowej Karolinie";
print ("Stwister<BR>");
$letter_array = count_chars(Stwister, 1);
while ($cell = each($letter_array))
(
$letter = chr(Scell['key']);
$frequency = $cell['value'];
print("Znak: '$letter'; liczba: $ requency<BR>");
)

Fragment ten daje w przeglądarce następujący wynik:


Król Karol kupił królowej Karolinie
Znak: liczba 4
Znak: K liczba 3
Znak: a liczba 2
Znak: e liczba 2
Znak: i liczba 3
Znak : j liczba 1
Znak: k liczba 2
Znak: 1 liczba 4
Znak : n liczba 1
Znak: o liczba 3
Znak : P liczba 1
Znak: r liczba 4
Znak: u liczba 1
Znak: w liczba 1
Znak: ł liczba 1
Znak : ó liczba 2

Funkcja count_chars ( ) zwraca w tablicy raport częstości występowania znaków


w ciągu podanym jako argument. Kluczami tablicy są wartości kodu ASCII znaków,
a wartością jest częstość występowania tych znaków w ciągu. Drugim argumentem
funkcji jest liczba oznaczająca jeden z trybów działania funkcji. W trybie O zwracana
jest tablica par klucz-wartość, w których kluczami są wszystkie wartości kodu ASCII od
O do 255, a odpowiadającymi im wartościami — częstość występowania odpowiednich
znaków. W trybach 1. i 2. w tablicy występują tylko znaki, które występują w ciągu
(tryb 1.) lub nie występują w ciągu (tryb 2.). Tryby 3. i 4. zwracają ciąg, a nie tablicę.
Ciąg ten zawiera wszystkie znaki występujące w źródłowym ciągu (tryb 3.) lub niewy-
stępujące w nim (tryb 4.).

Sposób odczytania danych z tablicy zwracanej przez funkcję count_


chars ( ) jest opisany w rozdziale 11. Funkcja chr ( ) użyta w poprzed-
nim przykładzie jest opisana w rozdziale 6.
Rozdział 9. » Ciągi i funkcje operujące na ciągach__________________________167

Tabela 9.3.
Funkcje analizujące znaki zawarte w ciągu

Funkcja Opis

count_chars ( ) Wymaga ciągu znaków oraz liczby oznaczającej tryb pracy (od O do 4). Podaje raport
częstości występowania znaków w ciągu, zwracając tablicę lub ciąg
strspn ( ) Wymaga dwóch ciągów. Zwraca długość początkowego fragmentu pierwszego ciągu,
który można utworzyć z znaków zawartych w drugim ciągu
strcspn ( ) Wymaga dwóch ciągów. Zwraca długość początkowego fragmentu pierwszego ciągu,
który można utworzyć ze znaków, których nie ma w drugim ciągu

Funkcje analizujące
Czasami trzeba podzielić ciąg na fragmenty, biorąc pod uwagę różny sposób definio-
wania tych fragmentów. Proces podziału długiego ciągu na „słowa" jest zwany analizą,
jest częścią interpretacji lub kompilacji zachodzącej przy pisaniu każdego programu,
także PHP. PHP posiada funkcję, która realizuje taką operację — strtok ( ) .

Funkcja strtok ( ) posiada dwa argumenty: ciąg przeznaczony do podziału oraz ciąg
zawierający wszystkie znaki podziału (znaki występujące pomiędzy częściami). Pod-
czas pierwszego wywołania używane są oba argumenty, a funkcja zwraca pierwszy
fragment. Aby odczytać kolejne fragmenty, należy ponownie wywołać funkcję bez po-
dawania ciągu. Ciąg jest zapamiętany jako ciąg bieżący, a funkcja zapamiętuje, w któ-
rym miejscu skończyła go analizować podczas poprzedniego wywołania. Rozpatrzmy
następujący przykład:
?token = strtok(
"klient-serwer CD-ROM baza danych", " ");
while {Stoken)
{
print(Stoken. "<BR>");
Stoken = strtok(" ");
.)

W wyniku działania tego fragmentu programu otrzymamy:


klient-serwer
CD-ROM
baza
danych

Początkowy ciąg został podzielony w miejscu każdej spacji. Zmieńmy teraz znak se-
paratora:
Stoken = strtok(
"klient-serwer CD-ROM baza danych", "-");
while ($token)
(
print(Stoken. "<BR>");
Stoken = s t r t o k ( " - " ) ;
}

Tak zmodyfikowany program da w wyniku:


168_________________________________________Część l » Podstawy PHP

klient
serwer CD
ROM baza danych

Możemy również podzielić ten ciąg we wszystkich tych miejscach, jednocześnie poda-
jąc separator w postaci" -".
Stoken = strtok(
"klient-serwer CD-ROM baza danych", " -");
while ($token)
(
print($token. "<BR>");
$token = s t r t o k t " -");
}

Ten program wypisze w oknie przeglądarki:


klient
serwer
CD
ROM
baza
danych

Zauważ, że żaden z separatorów nie występuje w fragmentach wynikowych.

Funkcja s t r t o k f ) zwraca kolejne fragmenty za następnymi wywołaniami. Możesz


użyć funkcji explode ( ) , która działa podobnie, ale zapisuje w tablicy podzielony za
jednym razem ciąg. Gdy fragmenty zostaną umieszczone w tablicy, możesz je na przy-
kład posortować.

Funkcja explode ( ) wymaga dwóch argumentów: ciągu separatora oraz ciągu do po-
dzielenia. Zwraca ona tablicę zawierającą kolejne fragmenty ciągu. Przykład wywołania
funkcji:
$wynik = explode!",", "jeden, dwa, t r z y " ) ;

Tablica $wynik po wykonaniu tego wiersza programu będzie zawierał trzy elementy:
"jeden", "dwa" i "trzy".

Ciąg separatora w funkcji explode)) ma inne znaczenie niż w funkcji s t r t o k f ) .


Wszystkie znaki separatora muszą wystąpić w ciągu źródłowym we właściwej kolejno-
ści, aby został on wykryty przez funkcję. Ciąg separatorów w funkcji s t r t o k f ) jest
zbiorem pojedynczych znaków, z których każdy służy jako separator. Taka interpretacja
separatora powoduje, że funkcja explode ( ) jest bardziej precyzyjna, ale i bardziej wy-
magająca. Jeżeli pozostawisz spację lub znak nowego wiersza, działanie funkcji może
być inne, niż się spodziewałeś.

Ponieważ cały separator jest usuwany podczas pracy explode ( ) , funkcja ta może być
podstawą wielu ciekawych zastosowań. Poniższy przykład, przytaczany w dokumenta-
cji PHP, dla wygody używa krótkich ciągów; należy pamiętać, że mogą mieć prawie
dowolną długość. Funkcja explode ( ) jest szczególnie użyteczna w przypadku długich
ciągów, kiedy analizowanie w inny sposób może być nużące. Policzymy miejsca wy-
stąpienia podanego ciągu w pliku tekstowym dzięki wczytaniu pliku do ciągu i użyciu
funkcji explode ( ) (w tym przykładzie użyliśmy kilku nieomówionych jeszcze funkcji,
ale mamy nadzieję, że ich działanie będzie jasne).
Rozdział 9. » Ciągi i funkcje operujące na ciągach__________________________169

<?php
// Wczytujemy plik tekstowy do zmiennej $filestring
Sfilename = "hardware_jDooks.html";
Sfd = fopen($filename, "r");
$filestring = fread($fd, filesize(Sfilename));
fclose(Sfd);
// Podzielimy na części, używając znacznika <TABLE> jako separatora
$pieces = explode("<TABLE", Sfilestring);
// Policzenie liczby fragmentów
$num_pieces = count(Spieces);

// Aby dostać liczbę znaczników <TABLE>, należy odjąć jeden


// od liczby fragmentów
echo ($num_pieces -l );
?>

Istnieje funkcja odwrotna do explode ( ) — implode ( ) , która wymaga dwóch argu-


mentów: ciągu „sklejającego" (analogicznie do ciągu separatora w explode ( ) ) oraz
tablicy ciągów podobnej do tej zwracanej przez explode ( ) . Zwraca ona ciąg stworzo-
ny poprzez wstawienie ciągu sklejającego pomiędzy kolejne elementy z tablicy.
Użyjemy tych dwóch funkcji, aby wymienić wszystkie miejsca wystąpienia jakiegoś
ciągu w pliku tekstowym. Należy pamiętać, że separator jest usuwany przez funkcję
explode ( ) , jeżeli chcesz, aby występował w pliku wynikowym, trzeba zamienić go
ręcznie. W kolejnym przykładzie zmienimy znaczniki czcionki na stronie WWW.
<?php
// Wczytanie pliku do zmiennej
Sfilename = "jakas_strona.html";
$fd = fopen($filename, "r");
Sfilestring = fread($fd, filesize(Sfilename));
fclose($fd);

Sparts = explode("arial, sans-serif", Sfilestring);


Swhole = implode("arial, verdana, sans-serif", Sparts);

// Zapisujemy na nowo plik


$fd = fopen(Sfilename, "w");
fwrite(Sfd, Swhole);
fclose(Sfd);
?>

Funkcje zmiany wielkości liter


Funkcje poniższe zmieniają wielkość liter z małych na wielkie, i odwrotnie. Pierwsze
dwie operują na całym ciągu, kolejne — tylko na pierwszych literach.

strtolowerO
Funkcja strtolower ( ) zwraca ciąg ze wszystkimi małymi literami. Nie ma znaczenia, czy
na początku cały ciąg był zapisany wielkimi literami, czy wielkie i małe były wymieszane.
<?php
Soryginalny = "On NIE wiE, że KRZYCZY";
Smalę = strtolower(Soryginalny);
echo Smalę;
?>

Ten fragment programu zwróci ciąg: "on nie wie, że krzyczy".


170_________________________________________Część l » Podstawy PHP

strtoupper()
Funkcja strtoupper ( ) zwraca ciąg z wszystkimi wielkimi literami. Nie ma znacze-
nia, czy na początku cały ciąg był zapisany małymi literami, czy wielkie i małe były
wymieszane.
<?php
Soryginalny = "napisz to wyraźnie";
echo("<B>".strtoupper($oryginalny)."</B>");
?>

ucfirst()
Funkcja ucf irst () zmienia pierwszą literę ciągu na wielką.
<?php
Soryginalny = "przykładowe zdanie.";
echo(ucfirst(Soryginalny)};
?>

ucwordsQ
Funkcja ucwords ( ) zamienia pierwsze litery wyrazów w ciągu na wielkie.
<?php
Soryginalny = "miasto nowy Jork";
echo( ucwords(Soryginalny));
?>

^^\ Zarówno ucwords o, jak i ucf irst o nie konwertują liter na małe.
^-A Zmieniają tylko właściwe początkowe litery na wielkie. Jeżeli w środku
ciągu wystąpią wielkie litery, nie zostaną zamienione.

Funkcje znaków sterujących


PHP działa jako język łączący strony WWW z bazami danych, serwerami LDAP, połą-
czeniami poprzez gniazda sieciowe czy protokół HTTP. Zwykle dialog taki jest realizo-
wany dwuetapowo. Najpierw tworzony jest ciąg komunikatu (na przykład zapytanie do
bazy danych), następnie jest wysyłany do programu, z którym realizowana jest komunika-
cja. Często program ten w specjalny sposób interpretuje niektóre znaki. Aby były one po-
traktowane jako część literału, a nie kod sterujący, należy je specjalnie oznaczyć.

Wielu użytkowników uaktywnia opcję magic-quotes. Istnieją też funkcje usuwające


i dodające znaki \.

Dodawanie i usuwanie znaków backslash jest użyteczne również w innym przypadku:


podczas obróbki formularzy HTML za pomocą zmiennych PHP. Mogą one zawierać
wiele apostrofów, które są częścią HTML i nie powinny być interpretowane przez PHP.
Jeżeli masz wiele takich fragmentów, możesz przyspieszyć pracę, używając funkcji za-
miast ręcznego dodawania znaków sterujących.
Rozdział 9. » Ciągi i funkcje operujące na ciągach________________________171

Funkcja addslashes ( ) oznacza apostrofy, cudzysłowy, znaki backslash oraz znaki


NULL za pomocą znaku backslash. Zwykle jest to konieczne przy wysyłaniu znaków
do bazy danych.
<?php
$escapedstring = addslashes("He said, 'I'm a dog.");
Squery = "INSERT INTO test (quote) values ('Sescapedstring')";
$result = mysql_query($query) or die(mysql_error());
?>

Zabezpiecza to przed potraktowaniem apostrofu przed "I" jak zakończenie wyrażenia


przez serwer SQL. Jeżeli odczytujesz dane z bazy, musisz użyć funkcji stripsla-
shes ( ) , aby usunąć znaki sterujące.
<?php
$query = "SELECT quote FROM test WHERE ID = 1";
Sresult = mysql_query($query) or die (mysql_error{));
$new_row = mysql_fetch_array{$result) ;
Squote = stripslashes($new_row[0]);

echo Squote;
?>

Funkcja quotemeta ( ) przetwarza wszystkie znaki specjalnego znaczenia w polece-


niach powłoki Unix: . \ + * ? [ A ] ( $ ) .

Na przykład:
$literal= 'Znaki ($, *) maja dla mnie szczególne znaczenie \n<BR>';
$qm = quotemeta($literal};
echo $qm;

Wykonanie tego fragmentu da w wyniku:


Znaki \(\$, \*\) maja dla mnie szczególne znaczenie \\n

Funkcje sterujące specyficzne dla HTML opisano na końcu tego roz-


działu.

Formatowanie danych
Podstawowymi konstrukcjami używanymi do drukowania danych są print i echo,
które zostały opisane w rozdziale 5. Zwykle wartości zmiennych są wypisywane po-
przez wbudowywanie zmiennych w ciągi otoczone cudzysłowami (i są zamieniane na
wartości) i przekazywanie takiego ciągu do instrukcji echo lub print.

Jeżeli potrzebujesz dokładniej sformatowanego tekstu, PHP udostępnia funkcje print f ()


i sprintf (), które działają analogiczne do funkcji o tych samych nazwach w C. Obie
funkcje mają takie same argumenty: specjalny ciąg formatujący (opiszemy poniżej),
a następnie dowolną liczbę argumentów, które zostaną wklejone we właściwe miejsca
ciągu formatującego.

Jedyną różnicą pomiędzy printf ( ) i sprintf ( ) jest to, że printf ( ) wysyła wynik
bezpośrednio na wyjście, a sprintf ( ) zwraca wynik jako ciąg.
172_________________________________________Część l * Podstawy PHP

Dla programistów C: funkcja sprintf o różni się od jej wersji z C


jeszcze jednym szczegółem. W C należy utworzyć ciąg wynikowy, PHP
sam utworzy ten ciąg.

Najbardziej skomplikowaną częścią tych funkcji jest ciąg formatujący. Każdy znak, jaki
umieścisz w tym ciągu, pojawi się w ciągu wynikowym, oprócz sekwencji znaków roz-
poczynających się od %. Znak % oznacza początek „specyfikacji konwersji", która
wskazuje sposób wydruku argumentu odpowiadającego tej specyfikacji.

Po znaku % można umieścić pięć elementów określających sposób konwersji (niektóre


opcjonalnie): wypełnienie, wyrównanie, minimalna szerokość, dokładność oraz typ.
1. Pojedynczy (opcjonalny) znak wypełnienia może być zerem lub spacją. Znak
ten jest używany do wypełniania nieużywanego miejsca, które zarezerwowano,
określając minimalną szerokość. Jeżeli znak ten nie zostanie podany, wolne
miejsce zostanie wypełnione spacjami.
2. Opcjonalny znak wyrównania „-". Jeżeli został umieszczony, wartość jest wy-
równana do lewej strony; jeżeli go nie ma, wartość jest wyrównana do prawej
strony.
3. Opcjonalna liczba określająca minimalną szerokość; wartość zajmuje co naj-
mniej tyle miejsca. Jeżeli będzie potrzebne więcej miejsca do wydrukowania
wartości, przekroczona zostanie szerokość pola.
4. Opcjonalne określenie szerokości części ułamkowej. Składa się z kropki i licz-
by określającej dokładność drukowania liczb rzeczywistych (dla innych warto-
ści nie ma znaczenia).
5. Pojedynczy znak określający sposób, w jaki ma być interpretowany typ zmien-
nej. Znak f oznacza drukowanie liczby rzeczywistej, s oznacza drukowanie
ciągu, reszta znaków (b, c, d, o, x, x) oznacza, że wartość jest traktowana jak
liczba całkowita i drukowana w różnych formatach. Formaty te to: format bi-
narny dla litery b, c oznacza drukowanie znaku o odpowiednim kodzie ASCII,
o oznacza liczbę oktalną, x to liczba heksagonalna (pisana małymi literami),
a x to liczba heksagonalna z wielkimi literami.

Przykładem może być drukowanie tej samej liczby rzeczywistej na różne sposoby:
<pre>
<?php
$wartosc = 3.14159;
printf("%f, %10f, %-010f, %2.2f\n", $wartosc, $wartosc, $wartosc, $wartosc);
?>
</pre>

Wynik działania tego fragmentu:


3.141590, 3.141590, 3.141590000000000, 3.14

Konstrukcja <prex/pre> w HTML wskazuje przeglądarce, aby nie formatowała tak


oznaczonego bloku (nie usuwała wielokrotnych odstępów itp.).
Rozdział 9. » Ciągi i funkcje operujące na ciągach__________________________173

Zaawansowane własności ciągów


Omówiliśmy już większość podstawowych operacji na ciągach: tworzenie, dzielenie,
porównywanie, szukanie podciągów i drukowanie. Teraz przedstawimy kilka zaawan-
sowanych funkcji.

Wyrażenia regularne
Pełny opis wyrażeń regularnych wykracza poza ramy tego rozdziału. W zamian poka-
żemy, do czego mogą się one przydać. Podamy też kilka przykładów ich użycia.

Dlaczego używamy wyrażeń regularnych?


Porównywanie ciągów i szukanie podciągów, które opisaliśmy wcześniej, działa świetnie,
ale jest ograniczone do literałów. Załóżmy, że chcesz skontrolować, czy ciąg jest szcze-
gólnym rodzajem adresu WWW: rozpoczyna się www. a kończy się .com, a pomiędzy
nimi jest jeden wyraz pisany małymi literami. Przykładowo te ciągi są akceptowane:
www. ibm. com
www.zend.com

Te ciągi powinny być odrzucone:


Java.sun.com
www.j ava.sun.com
www.php.net
www.IBM.com
www.adresy nie mogą mieć spacji.com

Po krótkim przemyśleniu staje się jasne, że nie ma wygodnego sposobu na użycie po-
równania ciągów i wycinania podciągów do stworzenia odpowiedniego testu. Możemy
sprawdzić, czy istnieje www. oraz .com, ale trudno sprawdzić to, co jest w środku. Do
tego świetnie nadają się wyrażenia regularne.

Wyrażenia regularne w PHP


Wyrażenia regularne są sposobem porównywania ciągów ze znakami specjalnymi, któ-
re zastępuj ą większe porcje ciągu docelowego. Istniajądwie klasy wyrażeń regularnych,
z którymi PHP może działać: wyrażenia w stylu POSIX i wyrażenia zgodne z Perl (w tej
książce nie będziemy zajmować się wyrażeniami zgodnymi z Perl).

Uproszczone zasady wyrażeń regularnych w stylu POSIX są następujące:


* Znaki, które nie są znakami specjalnymi, są porównywane dosłownie. Przykła-
dowo litera we wzorcu jest porównywana z literą w ciągu.
A
* Znak specjalny oznacza tylko początek ciągu, znak $ oznacza tylko koniec ciągu.
* Znak specjalny . oznacza dowolny znak. Znak specjalny * oznacza O lub więcej
miejsc wystąpienia poprzedniego wyrażenia regularnego, a znak + oznacza
jedno lub więcej miejsc wystąpienia poprzedniego wyrażenia.
174_________________________________________Część l « Podstawy PHP

* Zestaw znaków otoczony nawiasami kwadratowymi zastępuje dowolny z tych


znaków — [ab] zastępuje zarówno a, jak i b. Można również podać zakres
znaków w nawiasach, używając kreski — [a-c] zastępuje a, b lub c.
* Znaki specjalne poprzedzone znakiem \ tracą specjalne znaczenie i są porów-
nywane dosłownie.

Przy użyciu tych zasad stworzyliśmy wyrażenie, które pasuje do interesujących nas ad-
resów WWW:
"www\.[a-z]+\.com$

Na początku tego wyrażenia mamy znak A, który wskazuje, że na początku ciągu powinny
znajdować się znaki www. Następnie znajduje się kropka poprzedzona znakiem bac-
kslash, który wskazuje, że w tym miejscu naprawdę powinna być kropka, a nie specjalny
znak zastępujący dowolny znak w porównywanym ciągu. Kolejnym fragmentem wyraże-
nia jest otoczony nawiasami kwadratowymi zakres liter, obejmujący wszystkie małe lite-
ry. Co najmniej jedna z tych liter musi wystąpić tutaj jeden lub więcej razy. Na koniec
w ciągu musi wystąpić fragment .com. Znak specjalny $ wskazuje, że .com kończy ciąg.

Tak skonstruowanego wyrażenia użyjemy jako argumentu funkcji ereg ( ) , która wy-
maga ciągu zawierającego wyrażenie regularne oraz ciągu z nim porównywanego jako
argument. Napiszmy funkcję sprawdzającą, przy użyciu funkcji ereg ( ) , interesujące
nas adresy WWW.
function simple_dot_com(Surl)
(
return (ereg('"www\.[a-z]+\.com$', $url));
)

Funkcja ta zwraca TRUE lub FALSE w zależności od tego, czy udało jej się porównanie
z naszym wzorcem. Użyjmy tej funkcji do weryfikacji kilku wymienionych wcześniej
adresów (program przegląda tablicę w sposób, który dokładnie opiszemy w następnym
rozdziale).
$urls_to_test =
array{'www.ibm.com', 'www.java.sun.com', 'www.zend.com',
'java.sun.com', 'www.php.net', 'www.IBM.com',
'www.adresy WWW nie mają odstępów.com');
while ($test = array_pop($urls_to_test))
(
if (simple_dot_com($test))
print ("\"$test\"jest prostym adresem ,com<BR>");
else
print ("\"$test\"NIE jest prostym adresem .com<BR>");
}

Wynikiem działania tego programu jest:


"www.adresy WWW nie mają odstepow.com" NIE jest prostym adresem .com
"www.TBM.com" NIE jest prostym adresem .com
"www.php.net" NIE jest prostym adresem .com
"java.sun.com" NIE jest prostym adresem .com
"www.zend.com" jest prostym adresem .com
"www.java.sun.com" NIE jest prostym adresem .com
"www.ibm.com" jest prostym adresem .com

O takie właśnie rozdzielenie nam chodziło.


Rozdział 9. » Ciągi i funkcje operujące na ciągach__________________________175

W wielu komputerach działających pod Unixem wpisanie "mań 7 re-


gex" wyświetli podręcznik na temat wyrażeń regularnych POSIX. Jeżeli
nie, wpisz "man regex" i sprawdź, na której stronie znajdują się inte-
resujące cię informacje.

Funkcje korzystające z wyrażeń regularnych


Funkcje korzystające z wyrażeń regularnych zebrane są w tabeli 9.4.

Tabela 9.4.
Funkcje korzystające z wyrażeń regularnych

Funkcja Opis

ereg ( ) Wymaga dwóch argumentów i opcjonalnie trzeciego. Pierwszy ciąg jest wyrażeniem
regularnym w stylu POSIX, drugi jest porównywanym ciągiem. Funkcja zwraca
TRUE, jeżeli porównanie się powiodło. Jeżeli porównanie nie udało się, zwraca
FALSE. Jeżeli w trzecim argumencie zostanie podana tablica, a fragmenty wzorca
są otoczone nawiasami, części porównywanego ciągu, które pasują do kolejnych
fragmentów w nawiasach, są kopiowane do kolejnych komórek tablicy
ereg_replace ( ) Wymaga trzech argumentów: wyrażenia regularnego w stylu POSIX, ciągu, który
jest wstawiany, oraz ciągu, na którym przeprowadzane jest wstawianie. Funkcja
przegląda ciąg podany jako trzeci argument i zamienia fragmenty pasujące do
wzorca ciągiem podanym jako drugi argument. Zwracany jest zmodyfikowany ciąg
Jeżeli we wzorcu wy stępuj ą części otoczone nawiasami (jak w ereg ( ) ) , ciąg
wstawiany może zawierać specjalny fragment '\\cyfra' (dwa znaki backslash
i pojedyncza cyfra), zamieniany na odpowiedni fragment ciągu wynikowego
eregi < > Funkcja działa identycznie jak ereg ( ) , ale litery w wyrażeniu regularnym są
porównywane niezależnie od wielkości
eregi_replace ( ) Funkcja działa identycznie jak ereg^replace ( ) , ale litery w wyrażeniu
regularnym są porównywane niezależnie wielkości
split () Wymaga wzorca, ciągu źródłowego i opcjonalnego limitu fragmentów. Zwraca
tablicę ciągów utworzonych przez podział ciągu źródłowego na fragmenty,
rozdzielone fragmentami pasującymi do wyrażenia regularnego. Jest to funkcja
analogiczna do explode ( ) poza tym, że separator nie jest literałem, ale
wyrażeniem regularnym

Jeżeli korzystasz z funkcji korzystających z wyrażeń regularnych z wzor-


cem pozbawionym znaków specjalnych, prawdopodobnie używasz kosz-
townego narzędzia w miejscu, gdzie wystarczy mniej skomplikowane.
Jeżeli próbujesz porównywać prosty ciąg z prostym ciągiem, powinieneś
użyć jednej z prostszych i szybszych funkcji, które opisaliśmy wcześniej
w tym rozdziale.
176_________________________________________Część l « Podstawy PHP

Funkcje HTML
Na koniec przedstawimy kilka funkcji manipulujących ciągami, które są specyficzne dla
pracy ze stronami WWW.

Tabela 9.5.
Funkcje specyficzne dla pracy ze stronami WWW

Funkcja Opis

html special char s () Pobiera jako argument ciąg i zwraca ciąg z zamienionymi czterema znakami,
które mają specjalne znaczenie w kodzie HTML. Każdy z tych znaków jest
zamieniony na odpowiadającą mu jednostkę HTML, która wygląda
w przeglądarce identycznie jak oryginał. Znak & jest zamieniany na &amp,
"" (cudzysłów) jest zamieniany na &quot, < zamieniany jest na &lt a > na &gt
htmlentities ( ) Zamienia wszystkie znaki posiadające jednostki HTML na te jednostki
ge t_html_ Wymaga jednej z dwóch starych (HTML_SPECI AL_CHARS,
translation_table () HTML_ENTITIES) i zwraca tablicę konwersji, używaną przez funkcje
htmlspecialcharsO i htmlentities(). Kluczami tablicy konwersji są znaki,
a wartościami ich zamienniki
n!2br ( ) Wymaga ciągu jako argumentu. Zwraca ciąg z zamienionymi wszystkimi
miejscami wystąpienia \n na <BR>. Pomaga przy obliczaniu długości akapitów
wyświetlanych później w przeglądarce
strip_tags () Pobiera jako argument ciąg i zwraca ten ciąg usuwając znaczniki HTML i PHP

Podsumowanie
Ciągi są sekwencjami znaków. Jest to jeden z sześciu podstawowych typów danych
w PHP. W przeciwieństwie do niektórych innych języków, nie ma tu osobnego typu
znakowego, pojedynczy znak zachowuje się jak ciąg o długości l. Ciągi w kodzie są
otaczane apostrofami (') lub cudzysłowami ("). Ciągi otoczone apostrofami są interpre-
towane prawie dosłownie, a ciągi otoczone cudzysłowami interpretują kilka sekwencji
sterujących i automatycznie wstawiaj ą wartości zmiennych.

Podstawowym operatorem ciągów jest ' . ', który łączy dwa ciągi. Istnieje dodatkowo
spora gama funkcji, które umożliwiają sprawdzanie, porównywanie, szukanie, wycina-
nie, zamianę zawartości ciągu. PHP udostępnia także wyrażenia regularne, zgodne ze
standardem POSIX i Perl, do zaawansowanych zastosowań.
Rozdział 10.
Matematyka
W tym rozdziale:
* Podstawowe typy numeryczne i operacje arytmetyczne
** Funkcje wykładnicze, trygonometryczne i funkcje konwersji podstawy
* Generowanie liczb losowych
* Arytmetyka o dowolnej dokładności

Jeżeli chcesz wykonywać poważne obliczenia naukowe lub statystyczne, języki skryp-
towe nie są do tego odpowiednim narzędziem. Mimo tego PHP zawiera szeroką gamę
funkcji, które zapewniają wykonanie większości zadań matematycznych. Zapewnia aryt-
metykę o dowolnej dokładności czy dostęp do bibliotek funkcji mieszających i krypto-
graficznych.

Projektanci PHP nie próbowali dodawać nowości w tej dziedzinie. Wiele z podstawo-
wych funkcji matematycznych w PHP to proste odpowiedniki analogicznych funkcji w C
(więcej na ten temat w przypisie „Rzut oka za kurtynę" poniżej).

Typy numeryczne
PHP posiada tylko dwa typy numeryczne: integer (znany również jako long) oraz
double (float), które odpowiadają największym typom numerycznym w C. PHP
automatycznie konwertuje typy numeryczne, mogą więc być bezpiecznie mieszane
w wyrażeniach arytmetycznych; zwykle dają prawidłowy wynik. PHP potrafi również
w razie potrzeby skonwertować ciąg na liczbę.

Jeżeli chcesz, aby wartość została zinterpretowana jako wartość określonego typu nume-
rycznego, skorzystaj z rzutowania typu, poprzedzając zmienną nazwą typu w nawiasach:
(double) $zmierma
(integer) Szmienna
178_________________________________________Część l » Podstawy PHP

Możesz również użyć funkcji intval () i doubleval (), które skonwertują argument
wywołania na odpowiedni typ numeryczny.

Dokładny opis typów integer i double znajduje się w rozdziale 6.

Operatory matematyczne
Większość operacji matematycznych w PHP jest realizowana przez wywołania funkcji,
a nie przy użyciu operatorów. Oprócz operatorów porównania, opisanych w rozdziale 7.,
PHP zawiera pięć operatorów realizujących proste działania oraz kilka operatorów „skró-
towych", pozwalających na bardziej zwięzłe przypisywanie i zwiększanie wartości.

Operatory arytmetyczne
Pięć operatorów dostępnych w PHP to cztery operatory dostępne w prostym cztero-
działaniowym kalkulatorze oraz operator dzielenia modulo (%). Operatory te zostały
opisane w tabeli 10.1.

Tabela 10.1.
Operatory arytmetyczne

Operator Opis Przykład

+ Suma dwóch argumentów 4 + 9.5 = 13.5


- Jeżeli występują dwa argumenty, argument po prawej stronie jest 50 - 75 = -25
odejmowany od argumentu stojącego po lewej stronie. Jeżeli występuje - 3.9 = -3.9
tylko argument po prawej stronie, zwracana jest jego wartość
o przeciwnym znaku
* Iloczyn dwóch argumentów 3 . 1 4 * 2 = 6.28
/ Dzielenie zmiennoprzecinkowe argumentu po lewej stronie przez 5 / 2 = 2.5
argument po prawej stronie
% Całkowita reszta z dzielenia argumentu po lewej stronie przez argument 101 % 50 = l
po prawej stronie 999 % 3 = O
43 % 94 = 43
-12% 10 = -2
-12%-10 = -2

Operatory arytmetyczne i typy


W przypadku pierwszych trzech operatorów: +, -, * nastąpi konwersja do typu double
na argumentach typu integer. Jeżeli oba argumenty są typu integer, wynik również
Rozdział 10. * Matematyka_____________________________________179

będzie tego typu. Jeżeli jeden z argumentów będzie typu double, wynik również będzie
typu double. W przypadku dzielenia nastąpi też zmiana typu na double, jeżeli argu-
menty nie dzielą się bez reszty przez siebie.

,^-vZ, Jeżeli potrzebujesz wyniku dzielenia całkowitego zamiast zmiennoprze-


lrjJ3\ cinkowego, trzeba skonwertować wynik do typu integer. Na przykład
intvai(5/2j daje w wyniku liczbę całkowitą 2.

Operator modulo
Arytmetyka modulo jest czasami nazywana w szkole „arytmetyką zegara". Proces
dzielenia modulo przez liczbę jest podobny do „owijania" pierwszego argumentu do-
okoła drugiego. Wynik tej operacji jest zawsze mniejszy od drugiego argumentu.

Można to porównać do zwykłego zegara analogowego, który pokazuje czas modulo 12,
natomiast „wojskowe" zegary pokazują czas modulo 24 (nie jest to dokładnie to samo,
ponieważ dzielenie modulo daje wynik od O do n-1, a nie od l do n).

Operator modulo w PHP (%) oczekuje argumentu typu integer i jeżeli otrzyma war-
tość double, najpierw konwertuje ją do typu integer, odrzucając część ułamkową.
Wynikiem działania jest zawsze liczba całkowita.

Większość języków programowania posiada operator modulo. Zwykle różnią się obsłu-
gą ujemnych argumentów. W niektórych językach wynik działania jest zawsze dodatni
i-2 % 26 jest równe 24. W PHP-2 % 26 wynosi -2, a wyrażenie $mod = $first
% $ second jest równoważne wyrażeniu:
if ($first >=0)
$rood = $first % abs(Ssecond);
else
$mod = -(abs(Sfirst) % abs(Ssecond));

Funkcja abs ( ) zwraca wartość bezwzględną argumentu.

Operator inkrementacji
PHP korzysta z większości składni C, a wiadomo, że programiści C są dumni ze zwię-
złości. Zapożyczone z C operatory inkrementacji i dekrementacji pozwalają na zwięzły
zapis konstrukcji $count=$count+l, które wy stępuj ą bardzo często.

Operator inkrementacji (++) dodaje jeden do zmiennej, operator dekrementacji odej-


muje jeden od zmiennej. Oba te operatory występują w dwóch postaciach: „przyrost-
kowej" — umieszczanej po zmiennej, oraz „przedrostkowej" — umieszczanej przed
zmienną. Oba mają to samo działanie, ale chociaż w różny sposób obliczają wartość
wyrażeń. Operator przyrostkowy zmienia wartość zmiennej po zwróceniu przez nią
wartości, przedrostkowy zmienia jej wartość i zwraca nową. Możesz prześledzić różni-
ce, używając tych operatorów w przypisaniach podobnych do poniższych:
180_________________________________________Część l « Podstawy PHP

Scount •= 0;
$result - $count++;
print("Przyrostkowy++: count wynosi: $count, result wynosi: Sresult<BR>") ;
$count = 0;
$result = ++$count;
print("Przedrostkowy + +: count wynosi: $count, result wynosi: $result<BR>");
$count = 0;
Sresult = $count--;
print("Przyrostkowy —: count wynosi: Scount, result wynosi: Sresult<BR>");
Scount = 0;
Sresult = —Scount;
print("Przedrostkowy —: count wynosi: Scount, result wynosi: Sresult<BR>");

Operacje te dają w wyniku:


Przyrostkowy + +: count wynosi: l, result wynosi: O
Przedrostkowy ++: count wynosi: l, result wynosi: l
Przyrostkowy —: count wynosi: -l, result wynosi: O
Przedrostkowy —: count wynosi: -l, result wynosi: -l

Konstrukcja $result = $count++; jest odpowiednikiem:


Sresult = Scount;
Scount = Scount + 1;

Konstrukcja $result = ++$count; jest odpowiednikiem:


Scount - Scount + 1;
Sresult = Scount;

Operator przypisania
Operator inkrementacji ++ pozwala uniknąć pisania przy operacji dodawania jeden do
zmiennej. Nie pomoże, jeżeli chcemy dodać dowolną inną liczbę lub wykonać inną ope-
rację arytmetyczną. Na szczęście wszystkie operatory arytmetyczne posiadają odpo-
wiednie operatory przypisania (+=, -=, *=, /= i %=), które przypisują zmiennej wynik
operacji arytmetycznej na niej samej. Wyrażenie następujące:
Scount = Scount * 3;

może zostać skrócone do:


Scount *= 3;

natomiast wyrażenie:
Scount = Scount + 17;

odpowiada operacji:
Scount += 17;

Operatory porównania
PHP zawiera zwykle arytmetyczne operatory porównania, które wymagają prostych
wartości (liczb lub ciągów) jako argumentów; zwracaj ą wartość TRUE bądź FALSE:
Rozdział 10. » Matematyka_______________________________________181

* Operator < (mniejszy niż) zwraca wartość TRUE, jeżeli wartość po lewej stronie
jest mniejsza od wartości po prawej stronie. W przeciwnym przypadku zwraca
FALSE.

** Operator <= (mniejszy lub równy) zwraca wartość TRUE, jeżeli wartość po le-
wej stronie jest mniejsza bądź równa wartości po prawej stronie. W przeciw-
nym przypadku zwraca FALSE.
* Operator > (większy niż) zwraca wartość TRUE, jeżeli wartość po lewej stronie
jest większa od wartości po prawej stronie. W przeciwnym przypadku zwraca
FALSE.

* Operator >= (większy lub równy) zwraca wartość TRUE, jeżeli wartość po le-
wej stronie jest większa lub równa od wartości po prawej stronie. W przeciw-
nym przypadku zwraca FALSE.
* Operator == (równy) zwraca wartość TRUE, jeżeli argumenty są sobie równe.
W przeciwnym przypadku zwraca FALSE.
* Operator ! = (różny) zwraca wartość FALSE, jeżeli argumenty są sobie równe.
W przeciwnym przypadku zwraca TRUE.
+ Operator === (identyczny) zwraca wartość TRUE, jeżeli argumenty są sobie
równe i są tego samego typu. W przeciwnym przypadku zwraca FALSE.

Przykłady użycia operatorów porównania oraz niektóre pułapki wystę-


pujące podczas porównywania liczb double oraz ciągów opisane zo-
stały w rozdziale 7.

Operator „identyczny" (===) jest nowością w PHP 4. Jest istotny z po-


wodu automatycznej konwersji typów wykonywanej przez PHP. Żadne
z poniższych wyrażeń nie ma wartości TRUE:
2 ==- 2.0
2 === "2"
"2.0" === 2.0
O === FALSE

Nieoceniony, jeżeli mamy funkcję, która zwraca ciąg w przypadku


prawidłowego wykonania, a wartość FALSE, gdy nie uda się. Porów-
nywanie zwracanej wartości może być mylące, ponieważ FALSE jest
traktowane identycznie jak pusty ciąg; operator identyczności rozróż-
nia te wartości.

Kolejność operacji i nawiasy


Zasady kolejności wykonywania operacji decydują, który operator w wyrażeniu pierw-
szy pobierze otaczające je argumenty. Kompletną tabelę kolejności operatorów można
znaleźć w podręczniku dostępnym pod adresem http://www.php.net. Poniżej przedsta-
wiamy najważniejsze zasady kolejności dla arytmetyki:
182_________________________________________Część l » Podstawy PH

* Operatory arytmetyczne mają wyższy priorytet od operatorów porównania.


* Operatory porównania mają wyższy priorytet od operatorów przypisania.
* Operatory *, / i % maj ą ten sam priorytet.
* Operatory + i - mają ten sam priorytet.
* Operatory *, / i % mają wyższy priorytet od + i -.
* Jeżeli operatory mają ten sam priorytet, działania wykonywane są od lewej do
prawej.

Jeżeli uważasz, że priorytety operatorów są trudne do zapamiętania, używaj nawiasov


Dla przykładu, czy możesz szybko określić wartość tego wyrażenia?
l + 2 * 3 - < 5 - 5 / 4 % 3

Okazuje się, że wartością tego wyrażenia jest 2. Dużo łatwiej to stwierdzić, jeżeli di
damy nawiasy (prawdę mówiąc, niepotrzebne).
( (l + (2 * 3) - 4) - ( (5 / 4) % 3)

Proste funkcje matematyczne


Następnym krokiem w kierunku bardziej skomplikowanych zagadnień są funkcje real
żujące konwersje między dwoma typami numerycznymi (opisane w rozdziale 6.) on
zwracające minimum i maksimum ze zbioru liczb (zebrane w tabeli 10.2).

Tabela 10.2.
Proste funkcje matematyczne

Funkcja Opis

floor ( ) Wymaga jednego argumentu (zwykle rzeczywistego) i zwraca największą liczbę całkowitą,
mniejszą lub równą argumentowi
cel l ( ) Wymaga jednego argumentu (zwykle rzeczywistego) i zwraca najmniejszą liczbę całkowitą,
większą lub równą argumentowi
round ( ) Wymaga jednego argumentu (zwykle rzeczywistego) i zwraca najbliższą liczbę całkowitą.
Jeżeli część ułamkowa wynosi 0,5, zwracana jest najbliższa liczba parzysta
abs ( ) Wartość bezwzględna. Jeżeli argument jest ujemny, zwraca odpowiadającą mu wartość
dodatnią. Jeżeli argument jest dodatni, zwraca argument
m
in ( ) Pobiera dowolną liczbę argumentów numerycznych (ale co najmniej jeden) i zwraca
najmniejszy z nich
max ( ) Pobiera dowolną liczbę argumentów numerycznych (ale co najmniej jeden) i zwraca
największy z nich

Wynikiem poniższego wyrażenia jest 3, ponieważ wartością każdej z funkcji jest 3.


min(3, abs(-3), m a x ( r o u n d ( 2 . 7 ) , c e i l ( 2 . 3 ) , f l o o r ( 3 . 9 ) ) )
Rozdział 10. * Matematyka_____________________________________183

Rzut oka za kurtynę


W jaki sposób są naprawdę tworzone funkcje PHP? Może to interesować
programistów C lub tych, którzy chcą poznać wewnętrzną budową PHP. Mo-
żemy chyba zdradzić, dlaczego wiele funkcji PHP działa identycznie jak ich
odpowiedniki w C.
Poniżej aktualna definicja funkcji PHP ceil, która konwertuje liczbę double
na najmniejszą liczbę całkowitą, która jest od niej większa lub jej równa.
PHP_FUNCTION(ceil)
{
zval ** value;
if (ARG_COUNT(ht)!=1I|getParametersEx(1, svalue)==FAILURE) {
WRONG_PARAM_COUNT;
)
convert_scalar_to_number_ex (value) ;
if ((*value)->type == IS DOUBLE) (
TT l l-^ciTne] -> Abe == j DOnprEJ /
if ( R v a l u e ) - > t y p e == IS_DOUBLE) {
else if (("value)->type == IS_LONG) (
RETURN_LONG((*value)->value.Ival);
)
RETURN_FALSE;
}

Fragmenty pisane wielkimi literami (włączając w to deklarację PHP_FUNC-


TION) są makrami specyficznymi dla szkieletu kodu PHP. Większość kodu
jest napisana w prostym języku C. Kod może wydawać się zwarty i skompli-
kowany, ale wiele instrukcji jest związanych ze specjalnym traktowaniem ty-
pów w PHP. Funkcja ta działa w następujący sposób:
1. Pobierane i policzone są argumenty wywołania funkcji celi o. Jeżeli
liczba argumentów jest inna niż ±, funkcja kończy się błędem.
2. Argument jest konwertowany do liczby, jeżeli jest typem skalarnym in-
nym niż liczba. Ma zastosowanie w sytuacji wywołania funkcji z cią-
giem znaków np.: celi ( " 5 . 4 " ) .
3. Argument numeryczny jest sprawdzany, czy jest typu long (inaczej mó-
wiąc integer). Jeżeli jest to long, zwracana jest jego wartość.
4. Interesującym nas przypadkiem jest wywołanie funkcji z argumentem
double. Jeśli to się zdarzy, wywoływana jest funkcja C ceil. Jej wynik
jest konwertowany do typu long. Następnie jest umieszczana w zmien-
nej long w sensie PHP i zwracana.
Inaczej mówiąc, implementacja PHP funkcji ceil jest funkcją C ceil wzbo-
gaconą o konwersję typów i sprawdzanie argumentów. Dlatego właśnie wie-
le z funkcji PHP działa identycznie jak ich odpowiedniki w C.
184_________________________________________Część l » Podstawy PHP

Konwersja podstawy
Domyślną podstawą liczb w PHP, używaną do czytania i drukowania, jest 10. Dodat-
kowo można wczytywać liczby ósemkowe (o podstawie 8), umieszczając dodatkowe O
na początku liczby, lub liczby szesnastkowe (o podstawie 16), rozpoczynając liczbę
znakami Ox.

L Więcej o formatach wczytywania liczb, notacji oktalnej i heksadecy-


malnej, w rozdziale 6.

Po wczytaniu liczby są reprezentowane w pamięci w formacie binarnym. Wszystkie


podstawowe operacje matematyczne są realizowane na liczbach o podstawie 2. PHP po-
siada funkcje konwertujące różne podstawy. Zamieściliśmy je w tabeli 10.3.

Tabela 10.3.
Funkcje konwersji podstawy

Funkcja Opis

BinDec ( ) Wymaga ciągu zawierającego binarną reprezentację liczby jako argumentu; zwraca
ciąg zawierający reprezentację tej liczby o podstawie 10
DecBin ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 10 na 2
OctDec ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 8 na 10
DecOct ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 10 na 8
HexDec ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 16 na 10
DecHex ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 10 na 16
base_convert ( ) Jako argumentu wymaga ciągu (liczby do skonwertowania) i dwóch liczb, określających
podstawę liczby z pierwszego argumentu oraz wymaganą podstawę. Zwraca ciąg
reprezentujący liczbę o żądanej podstawie. Cyfry większe od 9 są reprezentowane
jako litery od a do z. Obie podstawy muszą zawierać się pomiędzy 2 a 36

Wszystkie funkcje konwersji podstawy mają określone przeznaczenie, konwertując jed-


ną określoną podstawę na inną. Jedynie funkcja base_convert ( ) pozwala na podanie
dowolnej podstawy początkowej i końcowej. Przypatrzmy się działaniu funkcji base_
convert().
function display_bases{ $start_string, $start_base)
{
for ($new_base - 2 ; $new_base <= 36 ; $new_base++)
{
$converted = base_convert(Sstart_string, 3start_base, $new_base);
print( "$start_string o podstawie $start_base to
$converted o podstawie $new_base<br>");
}
}

display_bases("Ijj", 20);
Rozdział 10. » Matematyka_____________________________________185

Wykonanie tego programu da w wyniku:


Ijj o podstawie 20 to 1100011111 o podstawie 2
Ijj 0 podstawie 20 to 1002121 o podstawie 3
Ijj o podstawie 20 to 30133 o podstawie 4
Ijj o podstawie 20 to 11144 o podstawie 5
Ijj o podstawie 20 to 3411 o podstawie 6
Ijj o podstawie 20 to 2221 o podstawie 7
Ijj o podstawie 20 to 1437 o podstawie 8
Ijj o podstawie 20 to 1077 o podstawie 9
Ijj o podstawie 20 to 799 o podstawie 10
Ijj 0 podstawie 20 to 667 o podstawie 11
Ijj o podstawie 20 to 567 o podstawie 12
Ijj o podstawie 20 to 496 0 podstawie 13
Ijj o podstawie 20 to 411 o podstawie 14
Ijj o podstawie 20 to 384 o podstawie 15
Ijj o podstawie 20 to 31f 0 podstawie 16
Ijj 0 podstawie 20 to 2dO o podstawie 17
Ijj o podstawie 20 to 287 o podstawie
18
Ijj o podstawie 20 to 241 o podstawie
19
Ijj 0 podstawie 20 to Ijj o podstawie
20
Ijj o podstawie 20 to Ihl o podstawie 21
Ijj o podstawie 20 to Ie7 o podstawie 22
Ijj 0 podstawie 20 to Ibh o podstawie 23
Ijj o podstawie 20 to 197 0 podstawie 24
Ijj 0 podstawie 20 to 16o o podstawie 25
Ijj o podstawie 20 to 14j 0 podstawie 26
Ijj 0 podstawie 20 to 12g o podstawie 27
Ijj o podstawie 20 to lOf 0 podstawie 28
Ijj 0 podstawie 20 to rg o podstawie 29
Ijj 0 podstawie 20 to qj o podstawie 30
Ijj o podstawie 20 to po o podstawie 31
Ijj 0 podstawie 20 to ov o podstawie 32
Ijj 0 podstawie 20 to o7 0 podstawie 33
Ijj o podstawie 20 to nh o podstawie 34
Ijj 0 podstawie 20 to mt o podstawie 35
Ijj o podstawie 20 to m7 o podstawie 36

Funkcje konwersji podstawy oczekują, że liczba przekazana jako ar-


gument jest liczbą całkowitą, a nie rzeczywistą. Oznacza to, że nie
możesz skonwertować binarnego „10.1" do dziesiętnego „2.5".
W PHP 4.0 beta 3 funkcja base_convert o dawała nieprawidłowe
wyniki, gdy otrzymała ciąg zawierający separator dziesiętny (interpre-
towała go jako 0).

Mimo że funkcje konwersji podstawy spodziewają się ciągu jako argumentu i zwracają
ciąg, możesz użyć jako argumentu liczby dziesiętnej i polegać na konwersji typów PHP
(przeczytaj ostrzeżenie poniżej). Inaczej mówiąc, oba wywołania DecBin ( " 1 2 3 4 " )
i DecBin (1234) dadzą ten sam wynik.

Nie pomyl formatu odczytu liczby z jej reprezentacją. Dla przykładu,


mimo że 10 o podstawie 16 jest równe 16 o podstawie 10, to wyra-
żenie HexDec(Oxio) daje w wyniku „22". Dlaczego? Sprawdźmy, jakie
konwersje zachodzą. Podczas odczytywania 0x10 jest konwertowany
do wewnętrznego formatu binarnego, następnie do ciągu (z postaci
binarnej do postaci 16). 16 o podstawie 16 jest zamieniane na dzie-
siętne 22. Jeżeli chcesz, aby zaszła tylko jedna konwersja, powinie-
neś zapisać to wyrażenie HexDec (" i o " ) .
186_________________________________________Część l » Podstawy PHP

Funkcje wykładnicze i logarytmy


PHP zawiera funkcje logarytmiczne i wykładnicze, bazujące na podstawie 10 i e (ta-
bela 10.4).

Tabela 10.4.
Funkcje wykładnicze

Funkcja Opis

pow ( ) Wymaga dwóch argumentów numerycznych. Zwraca pierwszy argument podniesiony do


potęgi określonej przez drugi argument. Wartością pow ( $ x , Sy) jest xy
exp O Wymaga jednego argumentu. Podnosi e do podanej potęgi. Wartością exp ( $ x ) jest e*
log ( ) Funkcja oblicza logarytm naturalny. Wymaga jednego argumentu i zwraca jego logarytm
o podstawie e. Jeżeli ey=x, wartością log ( S x ) jest y
logio { ) Wymaga jednego argumentu i zwraca jego logarytm o podstawie 10. Jeżeli 10y=x,
wartością logi O ($x) jest y

^^> Stała matematyczna e (około 2,718) nie występuje w PHP, ale jej
\A przybliżenie można uzyskać jako wynik funkcji log < i).

W przeciwieństwie do exp ( ) i podstawy e, nie ma funkcji o jednym argumencie, która


podnosi 10 do odpowiedniej potęgi. Można użyć dwuargumentowej funkcji pow ( ) , po-
dając liczbę 10 jako pierwszy argument.

Możesz sprawdzić, że funkcje wykładnicze i podnoszenie do potęgi o tej samej podsta-


wie są swoimi odwrotnościami.
$test_449 = 449.0;
$test_449 = pow(10, exp(log(loglO($test_449))));
print("test_449 wynosi $test_449<BR>");

Wykonanie tego fragmentu wyświetli w przeglądarce:


t e s t _ 4 4 9 wynosi 4 4 9

Trygonometria
Mimo że nie objaśniamy natury obliczeń wykonywanych przez omawiane funkcje, tym
razem zrobimy wyjątek (spójrz na przypis „Trygonometria w jednym akapicie"). Jeżeli
ktoś nie wie nic na temat trygonometrii, nic nie skorzysta z tego opisu.
PHP oferuje standardowy zestaw funkcji trygonometrycznych oraz stałą M_P1, która
jest przybliżeniem liczby pi, liczbą typu double i posiada wartość 3,1415926535898
(jest to chyba jedyna stała matematyczna w PHP). Stała ta może być używana wszędzie
tam, gdzie jest potrzebna liczba/?; i może być używana zamiennie z funkcją pi ( ) .
Rozdział 10. » Matematyka_______________________________________187

Oba poniższe wyrażenia maj ą taką wartość:


$liczba_pi = M_PI;
$liczba_pi = pi();

Podstawowe funkcje trygonometryczne zebrane zostały w tabeli 10.5.

Tabela 10.5.
Funkcje trygonometryczne

Funkcja Opis

pi ( ) Nie wymaga argumentów i zwraca przybliżenie liczby/?/ (3,1415926535898). Może być


używana zamiennie ze stalą M_PI
sin o Wymaga argumentu numerycznego oznaczającego kąt w radianach i zwraca wartość sinusa
podanego kąta jako liczbę double
cos ( ) Wymaga argumentu numerycznego oznaczającego kąt w radianach i zwraca wartość cosinusa
podanego kąta jako liczbę double
tan ( ) Wymaga argumentu numerycznego oznaczającego kąt w radianach i zwraca wartość tangensa
podanego kąta jako liczbę double
asin ( ) Wymaga argumentu numerycznego i zwraca arcus sinus argumentu w radianach. Wartość
argumentu może być liczbą z zakresu -1,0 do 1,0 (argument poza tym zakresem zwraca jako
wynik NaN — „nie liczba"). Wynik jest wartością z zakresu -pi/2 do pi/2
acos ( ) Wymaga argumentu numerycznego i zwraca arcus cosinus argumentu w radianach. Wartość
argumentu może być liczbą z zakresu -1,0 do 1,0 (argument poza tym zakresem zwraca jako
wynik NaN — „nie liczba"). Wynik jest wartością z zakresu od O do pi
atan ( ) Wymaga argumentu numerycznego i zwraca arcus tangens argumentu w radianach. Wynik
jest wartością z zakresu -pi/2 do pi/2
atan2 ( ) Odmiana funkcji atan(), która wymaga dwóch argumentów. atan2($y, $x) jest identyczny
z atan($y/Sx), jeżeli $x jest dodatnie, ale kwadrat wyniku atan2 zależy od znaku $y oraz $x.
Wynik funkcji jest z zakresu -pi do pi

Trygonometria w jednym akapicie


Wyobraźmy sobie okrąg o promieniu 1; jego środek znajduje się w punkcie
o współrzędnych 0,0. Rozpocznij z „prawego końca" (współrzędne 1,0), mie-
rząc odległość po okręgu przeciwnie do wskazówek zegara. Przykładowo odle-
głość 2pi pozwoli na przemieszczenie się po całym okręgu, aż do punktu
wyjścia. Dla każdej odległości funkcja sinus wskazuje wartość y współrzędnej,
do której dotarłeś. Funkcja cosinus wskazuje wartość współrzędnej x, a funk-
cja tangens jest stosunkiem współrzędnej y i x. Funkcje arcus sinus, arcus
cosinus i arcus tangens są w pewnym sensie odwrotnością funkcji podsta-
wowych — zamieniają na powrót x, y i y/x na odległość po łuku, jaką trzeba
przebyć, aby otrzymać taką wartość współrzędnej x, y lub ich stosunku. Po-
nieważ dodanie 2pi do dowolnej odległości przeniesie nas do tego samego
punktu, funkcje odwrotne mogą mieć nieskończoną liczbę rozwiązań dla okre-
ślonej danej (co zaprzecza definicji funkcji). Są ograniczone do długości poło-
wy okręgu, co powoduje, że są w ten sposób prawidłowo zdefiniowane.
188_________________________________________Część l » Podstawy PHP

Zamiast tworzyć tabelę przykładowych wyników tych funkcji, napiszmy program, który
automatycznie wyświetli wyniki w tabeli HTML. Na wydruku 10.1 zamieszczona jest
ogólna funkcja wyświetlająca zestaw wyników jednoargumentowych funkcji działających
na zbiorze argumentów. Następnie użyjemy tej funkcji do stworzenia tabeli przykłado-
wych wyników działania funkcji trygonometrycznych i odwrotnych funkcji trygonome-
trycznych. Wynik działania na rysunku 10.1.

Wydruk 10.1. Wyświetlanie wyników działania funkcji trygonometrycznych___________________


<?php
function display_func_results(Sfunc_array, $input_array)
{
/* Drukowanie nagłówka tabeli */
print ("<TABLE BORDER=1><TRXTH>DANA\\FUNKCJA</TH>" ) ;
for($y = 0;
$y < count(5func_array) ;
$y++)
print("<TH>Sfunc_array[$y]</TH>") ;
print ("</TRXTR>") ;
/* Drukowanie reszty tabeli */
for($x = 0;
$x < count($input_array) ;
Sx++)
(
/* Tworzenie kolumny dla danych */
print("<TH>".
sprintf("%.4f", Sinput_array[$x])
."</TH>");
for(Sy = 0;
$y < count($func_array);
$y++)
{
$func_name = $func_array[$y] ;
$input = Sinput_array[$x];
print("<TD>");
printf("%4.4 f", Sfunc_name(Sinput));
print("</TD>");
)
print("</TR><TR>");
)
print ("</TRX/TABLE>") ;
)
?>
<HTML>
<HEAD>
<TITLE>Rysunek 10.1</TITLE>
</HEAD>
<BODY>
<?php
/* użycie funkcji wyświetlającej */
print("<H3>Przykłady działania funkcji trygonometrycznych</H3>");
display_func_results(array{"sin", "cos", "tan"),
array(-1.25 * pi(),
-1.0 * p i l ) ,
-0.75 * pi(),
-0.5 * pil),
-0.25 * pi(),
O,
0.25 * pi() ,
0.5 * pi() ,
0.75 * pi() ,
pi() ,
1.25 - pi()));
print("<H3>Przykłady działania odwrotnych funkcji trygonometrycznych</H3>") ;
display_func_results(array("asin", "acos", "atan"),
array(-1.0, -0.5, 0.0, 0.5, 1.0));
Rozdział 10. » Matematyka_____________________________________189

?>
</BODY>
</HTML>

Rysunek 10.1.
Przykład
działania funkcji
trygonometrycznych

Na rysunku 10.1 pokazane są podstawo we funkcje trygonometryczne z zakresu danych


od -5/4 pi do 5/4 pi oraz podstawowe odwrotne funkcje trygonometryczne w zakresie
danych od —1,0 do 1,0. Duża wartość funkcji tangens jest spowodowana wartością mia-
nownika, który powinien teoretycznie być równy zero, ale z powodu błędów zaokrągle-
nia różni się od zera.

Funkcja display_func_resuits o , pokazana na wydruku 10.1, ko-


rzysta z kilku sztuczek opisanych w poprzednim rozdziale: użycia
zmiennej jako nazwy funkcji (opisanej pod koniec rozdziału 8.) oraz
łączenia ciągów za pomocą operatora konkatenacji ('.') do połączenia
ciągów w instrukcji print (opis w rozdziale 9.).

W kodzie przedstawionym na wydruku 10.1 korzystamy ze „zmiennej


funkcyjnej" wprowadzonej w PHP. Pozwala to na wywoływanie funkcji
trygonometrycznych poprzez nazwy. Poprzednia wersja PHP pozwalała
na takie wywoływanie własnych funkcji; w PHP 4 jest to nowość w od-
niesieniu do funkcji wbudowanych.
190
190_________________________________________Część l » Podstawy PHP

Liczby losowe
Funkcje generujące liczby losowe w PHP zebrane zostały w tabeli 10.6. Jeżeli potrze-
bujesz więcej wyjaśnień na temat ich losowości, przeczytaj objaśnienia w dopisku.

Tabela 10.6.
Funkcje liczb losowych

Funkcja Opis

srand () Jako argumentu wymaga dodatniej liczby całkowitej i inicjuje nią generator liczb
losowych
rand < > Wywołana bez argumentów zwraca liczbę „losową" z zakresu od O do RAND_MAX
(może zostać odczytana za pomocą funkcji getrandmax ( ) ) . Funkcja może być
wywołana z dwoma argumentami ograniczającymi zakres losowanych liczb:
pierwszy z nich jest minimum, drugi maksimum
getrandmax ( ) Zwraca największą liczbę, jaką można uzyskać za pomocą funkcji r a n d ( )
mt_srand ( ) jak srand ( ) , ale korzysta z „lepszego" generatora liczb losowych
mt_rand () j a k r a n d (), ale korzysta z „lepszego" generatora liczb losowych
mt_getrandmax ( ) Zwraca największą liczbę, jaką można uzyskać za pomocą funkcji mt_rand ( )

Istnieją dwa generatory liczb losowych, każdy z nich ma trzy takie same funkcje: ini-
cjującą, zwracającą liczbę losową oraz zwracającą największą liczbę, jaką można uzy-
skać z generatora.

Konkretna funkcja losowa może zależeć od bibliotek, z którymi został skompilowany


PHP. W odróżnieniu od tego generator mt_rand ( ) używa zawsze tej samej funkcji lo-
sującej („Mersenne Twister"). Jej autor dowodzi w dokumentacji, że jest ona szybsza
i „bardziej losowa" (w sensie kryptograficznym) od funkcji rand ( ) . Nie ma powodu
nie wierzyć autorowi, użyjemy zatem mt rand ( ) zamiast rand ( ) .

W niektórych wersjach PHP w zależności od platformy otrzymuje się


pozornie losowe liczby, jeżeli nie zainicjuje się wcześniej generatora.
Nie można na tym polegać zarówno z powodu niemożności przenie-
sienia takiego rozwiązania, jak i dlatego, że działanie niezainicjowa-
nego generatora nie ma gwarancji.

Inicjowanie generatora
Typowym sposobem inicjowania generatorów liczb pseudolosowych w PHP jest użycie
funkcji mt_srand ( ) lub srand ( ) :
mt_srand( (double)microtime()*10000000);
Rozdział 10. » Matematyka_______________________________________191

Generatory liczb pseudolosowych


Funkcje generujące liczby losowe, dostępne w PHP są tak naprawdę imple-
mentowane na bazie generatorów liczb pseudolosowych. Dzieje się tak, po-
nieważ architektura komputera bazuje na maszynie deterministycznej, która
zawsze daje te same wyniki przy tych samych danych wejściowych. Nie ma
więc miejsca na losowość (mówimy o idealnych komputerach, a nie o użyt-
kowanych codziennie). Można sobie wyobrazić podłączenie konwencjonalne-
go komputera do źródła losowych danych, jak na przykład mechanicznego
wyrzutnika monet czy urządzenia obserwującego efekty kwantowe, ale nie
wszyscy mają do nich dostęp.
Trzeba więc korzystać z generatorów liczb pseudolosowych, które produkują
deterministyczną sekwencję liczb, dostatecznie losowych do większości za-
stosowań. Ich działanie jest inicjowane poprzez podanie liczby początkowej
(ziarna) do odpowiedniej funkcji w celu wygenerowania pierwszej liczby z se-
kwencji. Kolejne liczby są wynikiem wykonania tej samej funkcji na poprzednio
uzyskanej liczbie. Sekwencja w końcu się powtórzy, ale dobra funkcja wygene-
ruje bardzo długi ciąg liczb, zanim rozpocznie powtarzanie tej sekwencji.
W jaki sposób wybrać liczbę inicjującą generator? Jeżeli na stałe zaszyjesz
w kodzie wartość ziarna, za każdym uruchomieniem programu dostaniesz tę
samą sekwencję liczb. Typowa technika inicjowania generatora polega na
użyciu szybko zmieniającej się liczby pochodzącej z zegara systemowego.
Mimo że liczba ta nie jest losowa, zmienia się tak szybko, że nawet kolejne
uruchomienia programu spowodują inną wartość inicjującą generator.

Liczba mikrosekund, które upłynęły od ostatniej „pełnej" sekundy, inicjuje generator.


Możesz używać konstrukcji, nie wnikając w szczegóły działania. Umieść taki wiersz na
dowolnej stronie PHP wykonywanej tylko raz przed odwołaniem do funkcji mt_rand ( )
lub rand ( ) . Za każdym uruchomieniem strony otrzymasz różne losowe sekwencje.

Poniższy przykład używa tej techniki inicjowania:


print ("Inicjowanie generatora<BR>");
mt_srand( (double) microtime 0*10000000);
p r i n t ( " B e z argumentów:". m t _ r a n d ( ) . " < B R > " ) ;
p r i n t ( " B e z a r g u m e n t ó w : " . m t _ r a n d ( ) . "<BR>");
print("Bez argumentów:". mt_rand(). "<BR>");
p r i n t C ' Z dwoma a r g u m e n t a m i : " . m t _ r a n d ( 2 7 , 3 1 ) . " < B R > " ) ;
p r i n t ( " Z dwoma a r g u m e n t a m i : " . m t _ r a n d ( 2 7 , 3 1 ) . " < B R > " ) ;
printC'Z dwoma argumentami:". m t _ r a n d ( 2 7 , 3 1 ) . "<BR>");

Wynik powyższego fragmentu jest następujący:


Inicjowanie generatora
Bez argumentów: 415068444
Bez argumentów: 530864282
Bez argumentów: 1823883289
Z dwoma argumentami: 29
Z dwoma argumentami: 31
Z dwoma argumentami: 30

Uruchamiając taki program, otrzymasz liczby różniące się od podanych, ponieważ ten
sposób inicjowania generatora daje odmienne wyniki.
192_________________________________________Część l » Podstawy PHP

W niektórych starych wersjach PHP 3 funkcja rand ( ) ignorowała prze-


kazane argumenty i zawsze zwracała wartość pomiędzy O i getrand-
max ( ) (podobnie wczesne implementacje dla Windows). Jeżeli twoja
wersja ma tę usterkę, możesz napisać własną ograniczoną wersję
funkcji rand ( ) :
function mo]_rand( $min, $max)
{
return (rand() % (($max - $min) +1) +$min);
)

W przeciwieństwie do rand ( ) funkcja ta zawsze wymaga argumentów


min i max.

Mimo że funkcje generatora liczb pseudolosowych zwracają tylko liczby


całkowite, łatwo możesz sprowadzić je do postaci liczb rzeczywistych
z zakresu od 0,0 do 1,0 za pomocą wyrażenia rand o / getrand-
max ( ) . Następnie możesz skalować i przesuwać otrzymaną liczbę, aby
uzyskać żądany zakres (na przykład dla liczb pomiędzy 100,0 a 120,0
należy użyć wyrażenia 100.0 + 2 0 . 0 * (rand O / getrandmax() ).

Przykład: losowy wybór


Użyjemy teraz funkcji losujących do czegoś użytecznego. Poniższe dwie funkcje po-
zwalaj ą stworzyć ciąg z losowych liter, który może być użyty jako losowe hasło lub na-
zwa użytkownika.
function random_char($string)
{
Slength = strlen(Sstring);
Sposition = mt_rand(0, Slength - 1);
return (Sstring[$position]);
}
function random_string($charset_string, Slength)
f
Sreturn_string = ""; // pusty ciąg
for ($x = O ; $x < Slength ; $x + + )
$return_string .= random_char($charset_string);
return (Sreturn^string);
l

Funkcja random_char ( ) wybiera znak (właściwie podciąg o długości 1) z ciągu wej-


ściowego. Zrealizowaliśmy to ograniczając zakres losowanych liczb do długości ciągu
i zwracając znak znajdujący się na wylosowanej pozycji. Funkcja random_string o
wywołuje random_char ( ) tyle razy, ile określiliśmy w argumencie wywołania, łącząc
wylosowane litery w ciąg o żądanej długości.

Aby zademonstrować działanie tego kodu, zainicjujemy generator i po zdefiniowaniu


liter, na których działają funkcje, wywołamy kilka razy random_string ( ) :
Rozdział 10. » Matematyka_____________________________________193

mt_srand((double)microtime()*10000000) ;
$charset = "abcdefghijklmnopqrstuvwxyz";

$random_string = random_string($oharset, 8);


print("Losowy ciąg: $random_string<BR>") ;
$random_string = random_string($charset, 8);
print("Losowy ciąg: $random_string<BR>") ;
$random_string = random_string(Scharset, 8);
print("Losowy ciąg: $random_string<BR>");

W wyniku otrzymaliśmy:
Losowy ciąg: efwwrpzd
Losowy ciąg: zwewvdck
Losowy ciąg: jynhricn

W tym przykładzie tylko raz zainicjowaliśmy generator za pomocą wartości pobranej


z zegara systemowego. Popatrzmy, co stanie się, jeżeli popełnimy błąd i będziemy wie-
lokrotnie inicjować generator tą samą wartością:
mt_srand(43);
$random_string = random_string(Scharset, 8);
print("Losowy ciąg: $random_string<BR>");

mt_srand(43);
$random_string = random_string($charset, 8);
print("Losowy ciąg: $random_string<BR>") ;

mt_srand(43);
$random_string = random_string(Scharset, 8) ;
print("Losowy ciąg: $random_string<BR>") ;

Ponieważ generowana sekwencja zależy od wartości ziarna generatora, za każdym ra-


zem dostaniemy te same wyniki:
Losowy ciąg: qgkxvurw
Losowy ciąg: qgkxvurw
Losowy ciąg: qgkxvurw

W tym przykładzie wybieraliśmy losowe litery z ciągu, lecz proces ten może zostać
uogólniony do wybierania elementów np. z tablicy (lub dowolnego losowego elementu
zbioru). Musisz zdefiniować przestrzeń elementów, sposób ich ponumerowania i wy-
brania na podstawie wylosowanej liczby. Następnie można użyć funkcji rand ( ) lub
mt_rand ( ) do wyboru losowego numeru porządkowego elementu.

Arytmetyka o dowolnej dokładności


Typy integer i double są wy starczające dla większości zadań matematycznych, które
występują przy tworzeniu witryn WWW. Każdy z tych typów jest przyporządkowany
stałej ilości pamięci komputera, więc ich rozmiar i dokładność zależą od architektury
serwera. Zwykle liczby całkowite mają zakres od -231 - l do 2 31 - l, liczby double
mają 13 lub 14 cyfr. Do zadań wymagających większej dokładności PHP zapewnia ze-
staw funkcji matematycznych o dowolnej dokładności (znanych jako funkcje „BC" od
nazwy narzędzia Unixa do obliczenia o dowolnej dokładności).
194
194_________________________________________Część l » Podstawy PHP

^^\ Aby umieścić w PHP funkcje o dowolnej dokładności, trzeba podczas


\^ kompilacji włączyć flagę —enabie-bcmath. Aby sprawdzić, czy funk-
cje są dostępne, dołącz do dowolnego skryptu wiersz bcadd("i",
"i") . Jeżeli otrzymasz błąd nieistniejącej funkcji, musisz ponownie
skonfigurować PHP.

Funkcje BC wymagają ciągu jako argumentu, a także zwracają ciągi, zamiast używać
typów numerycznych o stałej wielkości. Ponieważ ciągi w PHP są ograniczone jedynie
dostępną pamięcią, oznacza to, że liczby mogą mieć dowolną długość. Obliczenia
wykonywane przez te funkcje są realizowane na liczbach dziesiętnych. Funkcje BC są
dokładne w działaniu na liczbach całkowitych, używają tylu cyfr, ilu potrzeba. W przy-
padku działań na liczbach rzeczywistych obliczenia są realizowane z taką dokładnością,
jakiej zażądasz. Funkcje BC przedstawione sąw tabeli 10.7.

Tabela 10.7.
Funkcje matematyczne o dowolnej dokładności (BC)

Funkcja Opis

bcadd ( ) Wymaga dwóch argumentów reprezentujących liczby oraz opcjonalnego argumentu


oznaczającego dokładność. Zwraca sumę dwóch pierwszych argumentów jako ciąg,
z określoną przez argument oznaczający dokładność liczbą cyfr po przecinku. Jeżeli nie
zostanie podany trzeci argument, funkcja użyje domyślnej dokładności, którą ustawia się za
pomocą funkcji bcscale ( )
bcsub ( } Podobna do bcadd ( ) , ale zwraca różnicę pierwszego i drugiego argumentu
bcmul ( ) Podobna do bcadd ( ) , ale zwraca iloczyn pierwszego i drugiego argumentu
bcdiv ( ) Podobna do bcadd ( ) , ale zwraca wynik dzielenia pierwszego argumentu przez drugi
bcmod ( ) Zwraca resztę z dzielenia pierwszego argumentu przez drugi. Ponieważ wynik jest
całkowity, dokładność jest niepotrzebna
bcpow ( ) Podnosi pierwszy argument do potęgi określonej drugim argumentem. Jeżeli został podany
trzeci argument, określa liczbę miejsc dziesiętnych
bcsqrt ( ) Zwraca pierwiastek kwadratowy argumentu z liczbą miejsc dziesiętnych określoną przez
opcjonalny drugi argument
bcscale () Ustawia domyślną dokładność dla kolejnych wywołań funkcji BC

Większość funkcji posiada opcjonalną dokładność obliczeń jako ostatni argument, okre-
ślającą, ile liczb po przecinku znajdzie się w wyniku. Jeżeli ten argument nie zostanie
podany, przyjęta będzie domyślna dokładność, która jest ustawiana wywołaniem funkcji
bcscale ( ) . Przed wywołaniem funkcji bcscale ( ) dokładność jest ustawiona na war-
tość umieszczoną w pliku php.ini.

Przykład użycia funkcji o dowolnej dokładności


Poniżej znajduje się przykład użycia funkcji BC do obliczeń na liczbach całkowitych.
Rozdział 10. » Matematyka_____________________________________195

for( $x=l; $x < 25 ; $x+ + )


print("$x do potęgi $x wynosi". bcpowlSx, $x). "<BR>");

Wynik działania tego fragmentu jest następujący:


1 do potęgi l wynosi l
2 do potęgi 2 wynosi 4
3 do potęgi 3 wynosi 27
4 do potęgi 4 wynosi 256
5 do potęgi 5 wynosi 3125
6 do potęgi 6 wynosi 46656
7 do potęgi 7 wynosi 823543
8 do potęgi 8 wynosi 16777216
9 do potęgi 9 wynosi 387420489
10 do potęgi 10 wynosi 10000000000
11 do potęgi 11 wynosi 285311670611
12 do potęgi 12 wynosi 8916100448256
13 do potęgi 13 wynosi 302875106592253
14 do potęgi 14 wynosi 11112006825558016
15 do potęgi 15 wynosi 437893890380859375
16 do potęgi 16 wynosi 18446744073709551616
17 do potęgi 17 wynosi 827240261886336764177
18 do potęgi 18 wynosi 39346408075296537575424
19 do potęgi 19 wynosi 1978419655660313589123979
20 do potęgi 20 wynosi i04857600000000000000000000
21 do potęgi 21 wynosi 5842587018385982521381124421
22 do potęgi 22 wynosi 341427877364219557396646723584
23 do potęgi 23 wynosi 20880467999847912034355032910567
24 do potęgi 24 wynosi 1333735776850284124449081472843776

Jeżeli użyjemy do tych obliczeń zwykłego typu całkowitego, zakres liczb całkowitych
zostanie przekroczony; pod koniec pętli obliczenia będą wykonywane na przybliżonych
liczbach zmiennoprzecinkowych.

Konwersja obliczeń na dowolną dokładność


Prześledzimy teraz, jak zamienić istniejące wyrażenie matematyczne na program korzy-
stający z funkcji o dowolnej dokładności.

Kolejne przybliżenia liczby/?/:


sqrt(12 - ( 1 2 / 2 2 ) + (12/32) - (12/42) + 12/5 2 ) - ...)

Ciąg ten nie zbiega się wystarczająco szybko, ale jest bardzo prosty.
function pi_approx( $iterations, $print_frequency)
{
$squaręd_approx = 12;
$next_sign = -1;
$dęnom - 2;

for ($iter = 0; $iter < $iterations; $iter++)


t
$squared_approx += $next_sign * 127(pow($denom, 2});
$denom++;
Snext_sign - -$next_sign;
if (Sdęnom % $print_frequency == 0)
(
$estimate = sqrt($squared_approx);
print("$denom iteracja: $estimate<BR>");
196______________________________________Część l » Podstawy PHP

Program co pewien czas wypisuje bieżące przybliżenie liczby pi, więc możemy spraw-
dzić w jaki sposób jest obliczana. Wywołajmy funkcję, wypisując dla porównania war-
tość liczby pi zaszytej w PHP.
pi_approx(10000, 1000);
print ("Wartość PHP: ". pil). "<BR>");

Wynik działania jest następujący:


1000 ite acja 3.1415936094742
2000 ite acja 3.1415928924416
3000 ite acja 3.1415927597285
4000 ite acja 3.1415927132878
5000 ite acja 3.1415926917946
6000 ite acja 3.14159268012
7000 ite acja 3.141592673081
8000 ite acja 3. 1415926685124
9000 iteracja 3.1415926653804
10000 iteracja: 3.1415926631401
Wartość PHP: 3.1415926535898

Aby zamienić naszą funkcję na wersję używającą funkcji dowolnej dokładności, należy
wymienić wszystkie funkcje matematyczne i operatory na odpowiadające im funkcje BC.
function pi_approx_bc( $iterations, $print_frequency, Sscale)
(
Ssquared_approx = "12";
$next_sign = -1;
Sdenora =2;

for ($iter = 0; Siter < $iterations; $iter++)


{
$squared_approx =
bcadd($ squared_approx,
bcmul($next_sign,
bcdiv(12,
bcpow(Sdenom,
2,
Sscale),
Sscale) ,
Sscale),
$scale);
$denom++;
$next_sign = -$next_sign;
if ($denom % $print_frequency == 0}
(
$estimate = bcsqrt($squared_approx, $scale);
print("$denom iteracja: $estimate<BR>");
)
l
)

Mimo że funkcje BC wymagają ciągu jako argumentu, można zawsze użyć w ich miej-
sce zwykłych liczb, a PHP skonwertuje je do postaci ciągów. Nie używamy funkcji BC
do prostych obliczeń, które nie wymagają dużej dokładności (na przykład pozostawili-
śmy $denom++, nie zamieniliśmy go na bcadd ($denom, l)). Dodaliśmy również ar-
gument określający dokładność, z jaką realizowane są obliczenia w każdej z funkcji BC.

Niestety Autorzy nie mieli tyle cierpliwości, aby obliczyć za pomocą tego ciągu liczbę
pi z dokładnością dostępną w PHP. Poniżej kilka ostatnich wyników dla wywołania
pi_approx_bc(125000, 5000, 50):
Rozdział 10. » Matematyka_______________________________________197

5000 iteracja: 3.14159269179461818517641869197372210733965029084822


10000 iteracja: 3.14159266314004473910927237111451817630446264222245
15000 iteracja: 3.14159265783420799349912843163361809339782685610199
[...]
115000 iteracja: 3.14159265366200026774631510010908129421628048203316
120000 iteracja: 3.14159265365610835070489666690944435213685101378531
125000 iteracja: 3.14159265365090922553332186943202695005202819218913

Otrzymane wyniki różnią się od wartości pi zaszytej w PHP. Wiąże się to z wybranym
ciągiem przybliżającym, a nie z funkcjami BC. Można j ą przybliżyć za pomocą bardziej
skomplikowanych i szybszych ciągów.
print("Pierwiastek kwadratowy z dwóch wynosi:" . bcsqrt(2, 40));

daje dużo większą dokładność, niż możemy uzyskać z liczb double:


Pierwiastek kwadratowy z dwóch wynosi:
1.4142135623730950488016887242096980785696

Podsumowanie
Zagadnienia matematyki omówione w niniejszym rozdziale zostały zebrane w tabeli 10.8.

Tabela 10.8.
Zestaw operatorów i funkcji matematycznych w PHP

Zagadnienie Opis

Operatory Operatory + , - , * , / , % wykonują podstawowe operacje na liczbach całkowitych


arytmetyczne i zmiennoprzecinkowych
Operatory Operatory ++ i -- zmieniają wartość zmiennych numerycznych, odpowiednio
inkrementacji zwiększając lub zmniejszając o jeden. Wartość wyrażenia w postaci
przyrostkowej ($var++) jest równa wartości zmiennej przed zmianą wartości.
Wartość wyrażenia w postaci przedrostkowej (+ + $var) jest równa wartości
zmiennej po zmianie wartości
Operatory przypisania Każdy operator arytmetyczny (na przykład ' + ') posiada odpowiadający mu
operator przypisania ( ' + = ' ) • Wyrażenie $count += 5 jest równoważne $count
= Scount + 5

Operatory porównania <, <=, >, >=, ==, ! =. Operator === jest prawdziwy, gdy oba argumenty są równe
i mają ten sam typ
Podstawowe floor (), ceil () oraz round () konwertują liczbę double na całkowitą, min (),
funkcje matematyczne ma x ( ) wybierają największą i najmniejszą wartość podanego parametru, abs ( )
podaje wartość bezwzględną liczby
Podstawowe Funkcje wyspecjalizowane: OctDec ( ) , DecOct ( ) , BinDec ( ) , DecBin ( ) ,
funkcje konwersji HexDec ( ) , DecHex ( ) konwertuj ą odpowiednie pary podstaw; base_convert ( )
konwertuje dowolne podstawy
198_________________________________________Część l » Podstawy PHP

Tabela 10.8.
Zestaw operatorów i funkcji matematycznych w PHP (ciąg dalszy)

Zagadnienie Opis

Funkcje wykładnicze log ( ) (logarytm naturalny) logi O ( ) (logarytm dziesiętny), exp ( )


(e podniesione do odpowiedniej potęgi) oraz pow ( ) (pierwszy argument
podniesiony do potęgi określonej przez drugi)
Funkcje trygonometryczne pi (} (oraz stała M_PI), sin (), cos (), tan (), acos (), asin (}, atan ()
oraz atan2 ( ) (dwuargumentowa wersja atan ( ) )
Funkcje arytmetyki Funkcje realizujące działania arytmetyczne na ciągach dowolnej długości,
o dowolnej dokładności (BC) reprezentujących liczby całkowite oraz rzeczywiste: bcadd ( ) , bcsub ( ) ,
bcmult ( ) , bcdiv ( ) , bcmod ( ) , bcpow ( ) , bcsqrt ( ) . Większość Z nich
posiada opcjonalny argument oznaczający dokładność. Domyślna
dokładność jest ustawiana funkcją bcscale ( )
Rozdział 11.
Tablice i funkcje
operujące na tablicach
W tym rozdziale:
* Działanie tablic w PHP
* Użycie tablic wielowymiarowych
4 Imitowanie innych struktur danych za pomocą tablic
4 Sortowanie i inne transformacje

Tablice są jedną z najlepszych i najbardziej elastycznych funkcji PHP. W przeciwień-


stwie do tab li c-wektorów z innych języków programowania (C, C++, Pascal), tablice
PHP mogą przechowywać dane rozmaitych typów i automatycznie organizować je
w różny sposób.

W tym rozdziale opisaliśmy dosyć dokładnie tablice i funkcje na nich


operujące. Jeżeli potrzebujesz szybkiego wprowadzenia — przeczytaj
rozdział 6.

Uwaga! Mimo że tablice istnieją już w PHP od jakiegoś czasu i działa-


ją w ten sam sposób jak w PHP 3, niektóre z opisanych w tym roz-
dziale funkcji są wprowadzone w PHP 4.

Użycie tablic
W tym rozdziale będziemy m.in. objaśniali sposób działania tablic i omawiali wszystkie
funkcje wbudowane, przeznaczone do manipulacji tablicami. Zanim to nastąpi, wylicz-
my zastosowania tablic w kodzie stron WWW.
200_________________________________________Część l » Podstawy PHP

* Wiele wbudowanych zmiennych środowiskowych PHP to tablice (na przykład


$HTTP_COOKIE_VARS, która zawiera wszystkie zmienne i ich wartości zapa-
miętane w cookies w komputerze klienta). Jeżeli chcesz korzystać z tych zmien-
nych, powinieneś wiedzieć, w jaki sposób odwoływać się do elementów tablic.
•* Większość funkcji związanych z bazami danych przenosi informacje w postaci
tablic, tworząc poręczny pakiet danych.
•* W jednej tablicy można łatwo przekazać zbiór argumentów z formularza
HTML z jednej strony do drugiej (popatrz do następnego rozdziału).
* Tablice są dobrym narzędziem manipulacji danymi (sortowanie, zliczanie itp.).

W prawie każdej sytuacji, gdy odwołujesz się do kilku danych, które mogą zostać spa-
kowane do jednej struktury i używane w jednorodny sposób, użycie tablic będzie wła-
ściwym posunięciem.

Czym są tablice PHP?


Tablice w PHP są tablicami asocjacyjnymi, rozszerzonymi o kilka dodatkowych me-
chanizmów. Słowo „asocjacyjne" oznacza, że tablice te zapamiętują dane w połączeniu
z ich wartością kluczową, a nie w porządku liniowym. Tablice w innych językach pro-
gramowania są wektorami, nie tablicami asocjacyjnymi (więcej na ten temat w „Tablice
asocjacyjne a wektory"). Jeżeli zapisujesz do tablicy element wraz z kluczem, wartość
elementu można odczytać korzystając z wartości klucza. Zapisanie do tablicy jest bar-
dzo proste:
$ w o j e w o d z t w o [ ' G l i w i c e ' ] = 'Śląskie';

Instrukcja taka zapamiętuje element 'śląskie' w tablicy $wojewodztwo w połącze-


niu z kluczem ' Gliwice '. Po zapisaniu tego elementu można odszukać zapisaną war-
tość za pomocą wartości klucza:
Swojew = $wojewodztwo['Gliwice']; // ma wartość 'Śląskie'

To wystarczy, aby używać tablic do zapamiętywania par klucz i wartość. Jeżeli chcesz
zapamiętywać wartości w porządku numerycznym, musisz użyć liczb jako wartości
klucza:
$tablica[l] = 'Pierwszy element';
$tablica[2] = 'Drugi element';

Oprócz zapamiętywania par klucz i wartość, procedury obsługi tablic zawierają mecha-
nizmy pozwalające na traktowanie tablic jak innych struktur danych. Tablice mogą być
wielowymiarowe, co pozwala na zapisywanie wartości w połączeniu z sekwencją war-
tości kluczowych, a nie tylko z pojedynczą wartością. Tablice mogą automatycznie
utrzymywać alfabetyczny porządek zapisywanych elementów, niezależnie od wartości
ich klucza. Pozwala to na traktowanie tablic jak list. Przybliżymy te aspekty działania
tablic omawiając funkcje o specyficznych własnościach.
Rozdział 11. * Tablice i funkcje operujące na tablicach_______________________201

Tablice asocjacyjne a wektory


Jeżeli programowałeś przy użyciu języków takich jak C, C++ i Pascal, używa-
łeś prawdopodobnie innej definicji słowa „tablica", która nie pasuje do
określania tablic w PHP. Dokładniejszym określeniem tablic w C jest wektor,
a tablice w PHP to tablice asocjacyjne.
W wektorach wszystkie elementy muszą być tego samego typu; zwykle kom-
pilator musi znać rozmiar wektora. Dla przykładu: w C możesz zadeklarować
tablicę na 100 liczb zmiennoprzecinkowych o podwójnej precyzji za pomocą
wyrażenia:
double tablica[100]; // to nie jest PHP

Ograniczenie do jednego typu oraz deklaracja rozmiaru dają dodatkowe ko-


rzyści: wektory są bardzo szybkie, zarówno podczas zapisywania, jak i prze-
szukiwania. Dzieje się tak, ponieważ kompilator umieszcza taką tablicę
w ciągłym bloku pamięci, o wielkości równej wielkości jednego elementu
pomnożonej o liczbę elementów. Pozwala to na błyskawiczne odszukanie
przez język programowania odpowiedniego fragmentu pamięci. Wystarczy
znać adres początku tablicy, rozmiar elementu i indeks interesującego nas
elementu, aby bezpośrednio obliczyć adres elementu w pamięci.
W PHP tablice są asocjacyjne. Nie mają stałej liczby elementów, PHP tworzy
miejsce na nowe elementy w momencie zapisywania ich do tablicy. Nie jest
również wymagane, aby elementy tablicy były tego samego typu. Mają tę
samą własność zmiany typu, jaką mają zmienne w PHP; możesz przypisać
do komórki tablicy dowolną wartość. Ponieważ wektory umieszczają wartości
w numerycznym porządku „kluczy", używanych do przeszukiwania i zapisywa-
nia danych, muszą być one liczbami całkowitymi. Tablice PHP mogą posiadać
klucze dowolnego typu, mogą to być np. ciągi. Można wykonać kolejne przy-
pisania, np.:
Stablica[l] = 1;
$tablica['orange'] = 2;
$tablica[3] = 3;

Wynikiem wykonania tych wierszy programu jest tablica zawierająca trzy war-
tości (l, 2, 3) połączone z kluczami (l, ' o r a n g e ' , 3).
Elastyczność tablic asocjacyjnych jest okupiona sporymi kosztami, ponieważ
wykonywanie kodu do momentu obliczenia adresu interesującego nas ele-
mentu zabiera więcej czasu niż w analogicznym przypadku dla wektorów.
W większości zastosowań skryptów WWW ten dodatkowy czas nie gra zbyt
wielkiej roli.
Ponieważ liczby całkowite mogą być stosowane jako klucze w tablicy asocja-
cyjnej, można imitować działanie wektora, używając wartości całkowitych ja-
ko kluczy.

Dla programistów Perl. Tablice w PHP bardzo przypominają tablice


asocjacyjne w Perl, z niewielkimi różnicami składniowymi. Po pierwsze,
wszystkie zmienne w PHP, a nie tylko zmienne skalarne, rozpoczynają
202_________________________________________Część l * Podstawy PHP

się znakiem $. Po drugie, mimo że tablica jest asocjacyjna, indeksy


są umieszczane w nawiasach kwadratowych ([]), a nie w klamrowych
({}). Nie ma tablic indeksowanych tylko liczbami całkowitymi. Konwen-
cja, jest używanie liczb całkowitych jako indeksów asocjacyjnych, a ta-
blica utrzymuje własny porządek dla potrzeb iteracji.

Dla programistów C++. Tablice asocjacyjne pozwalają zrealizować nie-


które z zadań, do których w C++ trzeba użyć biblioteki szablonów.
Dzieje się tak, ponieważ stosowanie szablonów jest związane z unik-
nięciem podawania określonego typu danych. System typów PHP umoż-
liwia pisanie ogólnych algorytmów, które przeglądają zawartość tablicy
bez określania typu elementów.

Dla programistów znających inne języki programowania. PHP nie wyma-


ga wielu rodzajów struktur danych z powodu dużej elastyczności tablic
PHP. Wybierając odpowiedni podzbiór funkcji operujących na tablicach,
można za pomocą tablic imitować inne struktury danych, takie jak wek-
tory, struktury i rekordy, listy, tablice mieszające lub stosy i kolejki.
Takie struktury danych w innych językach programowania wymagają
własnego typu danych lub skomplikowanych funkcji języka programo-
wania: użycia wskaźników, samodzielnego zarządzania pamięcią.

Tworzenie tablic
W skrypcie PHP można utworzysz tablicę na trzy różne sposoby: przypisując wartość
do jednego klucza (tworząc ją tajnie), używając konstrukcji array ( ) oraz wywołując
funkcję zwracającą tablicę jako wartość.

Bezpośrednie przypisanie
Najprostszą metodą utworzenia tablicy jest przypisanie wartości do zmiennej mającej
być tablicą:
Stablicafl] = "Pierwszy element właśnie utworzonej tablicy";

Jeżeli $tablica była niezainicjowaną zmienną (lub zmienną nie będącą tablicą), po
wykonaniu tego wiersza będzie tablicą z jednym elementem. Jeżeli zmienna $tablica
była już tablicą, zostanie w niej zapamiętany ciąg wraz z kluczem o wartości całkowitej 1.
Jeżeli do tej pory nie był on związany z żadną wartością, zostanie utworzony nowy
element tablicy do przechowywania naszego ciągu. Jeżeli istniała wartość związana
z kluczem l, zostanie nadpisana. Można również przypisywać wartości do tablicy bez
podawania indeksu: $tablica [ ] (co zostanie opisane poniżej).
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________203

Konstrukcja array()
Innym sposobem tworzenia tablicy jest użycie konstrukcji array ( ) , która tworzy nową
tablicę, zawierającą podane jako argumenty wartości i ich klucze. Prostsza wersja kon-
strukcji array () zawiera tylko listę wartości zapisywanych do tablicy, bez określania
ich kluczy. Wynikiem jest tablica z wartościami skojarzonymi z kluczami całkowitymi,
rozpoczynającymi się od 0. Na przykład wyrażenie:
$owoce = array('jabłko', 'pomarańcza 1 , 'banan', 'gruszka'};

spowoduje zapisanie w tablicy $owoce czterech elementów ( ' j a b ł k o ' , 'pomarań-


c z a ' , ' b a n a n 1 , ' g r u s z k a ' ) , z indeksami O, l, 2 oraz 3. Tablica dodatkowo pamięta
porządek, w którym zostały zapisane elementy (patrz część o iteracji).

Przypisanie do zmiennej $owoce ma takie samo działanie, jak poniższe instrukcje:


$owoce[0] = 'jabłko';
$owoce[l] = 'pomarańcza';
$owoce(2) = 'banan';
$owoce[3] = 'gruszka';

Ten sam efekt można osiągnąć opuszczając indeksy w przypisaniu:


$owoce[] = 'jabłko';
$owoce[] = 'pomarańcza';
$owoce[] = 'banan';
$owoce[] = 'gruszka';

W tym przypadku PHP przyjmuje, że dodajesz sekwencję elementów, które powinny


mieć indeksy numeryczne rozpoczynające się od 0.

Dygresja. Domyślnie indeksy numeryczne rozpoczynają się od zera.


Jest to konwencja obowiązująca dla tablic w większości języków pro-
gramowania. Nie jesteśmy pewni, dlaczego komputerowcy rozpoczyna-
ją liczenie od O (matematycy np. od 1), ale może mieć to związek
z arytmetyką wskaźników, która oblicza adresy pamięci dla elemen-
tów wektorów. Adresy kolejnych elementów tych tablic są odszukiwa-
ne przez dodanie coraz większych wartości przesunięcia do adresu
tablicy, a pierwszy element tablicy posiada przesunięcie zero (ponie-
waż pierwszy element ma adres tablicy).

Podawanie indeksów przy użyciu array()


W konstrukcji array ( ) indeksy przypisywane do naszych elementów są liczbami cał-
kowitymi o wartościach rozpoczynających się od zera. Można jednak użyć specjalnej
składni konstrukcji array ( ) używanej do definiowania kluczy wstawianych elemen-
tów. Zamiast wartości elementów rozdzielanych przecinkami, podaje się pary klucz
i wartość rozdzielone przecinkami, a wartość jest oddzielona od klucza symbolem =>.
204___________________________________________Część l » Podstawy PHP

Przeanalizujmy następującą instrukcję:


Sowoce = a r r a y l d => ' j a b ł k o ' , l => ' p o m a r a ń c z a ' ,
2 => ' b a n a n ' , 3 => ' g r u s z k a ' ) ;

Wykonanie tego wiersza da ten sam efekt. Każdy z ciągów zostanie zapisany w tablicy
i skojarzony z indeksami O, 1,2, 3. Możesz użyć tej samej składni do zapamiętania tych
elementów z innymi kluczami:
$owoce = array('czerwony' => 'jabłko', 'pomarańczowy' => 'pomarańcza',
'żółty' => 'banan', 'zielony' => 'gruszka');

Cztery elementy, w tym samym porządku, indeksowane są kolorem, a nie liczbą. Aby
odczytać na przykład nazwę żółtego owocu, można użyć następującego wyrażenia:
Sowoce['żółty']; // jest równe 'banan1

Na koniec utwórzmy pustą tablicę, wywołując funkcję array ( ) bez parametrów:


5pusta_tablica = a r r a y ( ) ;

Może to być przydatne w przypadku funkcji, które spodziewają się tablicy jako argumentu.

Mimo że używamy konstrukcji array o do tworzenia prostych tablic,


można za jej pomocą tworzyć również tablice wielowymiarowe (opi-
szemy je w następnej części rozdziału).

Funkcje zwracające tablice


Ostatnim sposobem tworzenia tablic jest wywołanie funkcji zwracającej tablicę (funkcja
zdefiniowana przez użytkownika, w której tablica zostanie stworzona za pomocą jednej
z opisanych metod, lub jedna z funkcji wbudowanych, które tworzą tablice wewnętrz-
nymi mechanizmami PHP).

Wiele funkcji związanych z bazami danych zwraca wynik w postaci na bieżąco tworzo-
nej tablicy. Inne funkcje, tworzące tablice, są wygodnymi narzędziami dla innych funk-
cji operujących na tablicach. Jedną z nich jest funkcja rangę ( ) , która pobiera dwie
liczby i zwraca tablicę zawierającą te liczby oraz wartości spomiędzy nich:
$tablica = r a n g ę ( 1 , 5 ) ;

Powyższy wiersz jest równoznaczny:


S t a b l i c a = a r r a y ! l, 2 , 3, 4 , 5 ) ;

Odczytywanie wartości
Po zapisaniu wartości do tablicy musimy poznać sposób jej odczytania.
Rozdział 11. » Tablice i funkcje operujące na tablicach_____________________205

Odczytywanie przy użyciu indeksu


Bezpośrednią metodą odczytania wartości jest użycie jej indeksu. Jeżeli wartość została
zapisana w tablicy $tablica pod indeksem 5, wyrażenie $tablica [5] powinno mieć
jej wartość. Jeżeli zmienna $tablica nie została zainicjowana lub nic nie zostało zapi-
sane pod indeksem 5, $tablica[5] będzie się zachowywała jak niezainicjowana
zmienna.

Konstrukcja list()
Istnieje kilka sposobów odczytania wartości z tablicy bez korzystania z kluczy. Więk-
szość z nich wykorzystuje to, że tablice pamiętają porządek, w jakim były zapisywane
elementy. Opiszemy to dokładniej w części „Iteracje". Tu zamieścimy przykład użycia
konstrukcji list (), która może być używana do przypisywania kilku kolejnych ele-
mentów tablicy do zmiennych. Wykonajmy następujące dwa wiersze kodu:
$owoce = array('jabłko', 'pomarańcza1, 'banan');
list($owoc_czerw, $owoc_pom) = $owoce;

Wynikiem wykonania tego fragmentu kodu będzie przypisanie do zmiennej $owoc_


1 1
czerw ciągu ' j a b ł k o i ciągu 'pomarańcza' do zmiennej $owoc_pom. Ciąg ' b a n a n
nie zostanie przypisany zmiennej, ponieważ nie dostarczyliśmy odpowiedniej liczby
zmiennych. Zmienne będące argumentami list () są kojarzone z elementami według
kolejności ich zapisywania w tablicy. Zauważmy niecodzienny element składni: kon-
strukcja list () jest po lewej stronie operatora przypisania, gdzie normalnie znajdują
się tylko zmienne.

W pewnym sensie l i s t o jest przeciwieństwem lub odwrotnością a r r a y ( ) , która


umieszcza wartości swoich argumentów w tablicy, l i s t o dzieli tablicę na indywidu-
alne zmienne. Wykonajmy następujący wiersz kodu:
list($pierwszy, 5drugi) = array(Spierwszy, Sdrugi);

Powoduje to przypisanie wartości $pierwszy i $drugi do tych samych zmiennych, po


tymczasowym zapisaniu do tablicy.

Konsekwentnie nazywaliśmy array o i listo konstrukcjami, a nie


funkcjami, ponieważ nie są to funkcje. Są to słowa kluczowe realizu-
jące funkcje języka (jak if, while, function itp.), są interpretowane
przez język, a nie poprzez zwykłą procedurę odczytywania parametrów
wywołania funkcji. Pamiętaj, że wartości argumentu funkcji są oblicza-
ne przed jej wykonaniem, więc konstrukcje takie wymagają specjalnej
interpretacji. Trzeba prześledzić przykłady użycia zarówno array o,
jak i listo, aby zrozumieć, dlaczego potraktowanie ich jak wywoła-
nia funkcji może prowadzić do niespodziewanych wyników.
206___________________________________________Część l » Podstawy PHP

Tablice wielowymiarowe
Do tej pory zajmowaliśmy się tylko tablicami jednowymiarowymi, z jednym poziomem
kluczy w nawiasach kwadratowych. Jednak PHP potrafi obsługiwać tablice wielowy-
miarowe z dowolną liczbą kluczy. Pierwsze odwołanie do tablicy może być przypisa-
niem np.:
Stablica[l][2][3][4][5] = "skarb głęboko schowany";

Jest to tablica pięciowymiarowa, z indeksami będącymi liczbami całkowitymi.

Pamiętaj, że wartości zapisane w tablicy mogą być tablicą, liczbami lub ciągami zna-
ków. Wielowymiarowa składnia z poprzedniego przykładu może być zwięzłą formą
odwołania do (czterowymiarowej) tablicy, połączonej z kluczem o wartości l, która to
tablica zawiera tablicę (trójwymiarową) itd. Zauważ, że w takiej strukturze mogą istnieć
różne poziomy zagłębienia:
5tablica_wielowym[0] = "prosty ciąg";
$ tablica_wielowym [ l ][' zawiera '] = "głębiej zapisany ciąg";

Klucz o wartości O przechowuje ciąg znaków, klucz o wartości l — tablicę zawierającą


kolejny ciąg. Nie możesz jednak wykonać poniższego przypisania bez utraty wartości
pierwszego przypisania: "prosty ciąg". Klucz O może być użyty do przechowania
ciągu bądź tablicy, ale nie obydwu na raz.
Stablica_więlowym[0]['zawiera'] = "inny głębiej zapisany ciąg";

Jeżeli będziesz pamiętać, że tablice wielowymiarowe to zwykłe tablice zawierające ko-


lejne tablice, łatwo zrozumieć, w jaki sposób konstrukcja array ( ) uogólnia tworzenie
tablic. To pozornie skomplikowane przypisanie jest w gruncie rzeczy bardzo proste:
$tablica = array!'owoc' =>
array( czerwony1 => 'jabłko',
pomarańczowy' => 'pomarańcza',
żółty' => 'banan',
zielony' => 'gruszka'),
'kwiat =>
array( czerwony' => 'róża',
żółty' => 'słonecznik',
fioletowy' => 'irys'));

Jest to prosta tablica zawierające dwa elementy, zapamiętane wraz z ich wartościami
klucza; każdy z nich jest tablicą. Po utworzeniu tej tablicy można się do niej odwołać
w następujący sposób:
$rodzaj = 'kwiat';
$ kolor = 'fioletowy';
print("$kolor Srodzaj to". S tablica[Srodzaj ] [$kolor));

Po wykonaniu tego fragmentu w oknie przeglądarki zobaczymy:


fioletowy kwiat to irys
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________207

^^ W powyższym przykładzie użyliśmy operatora konkatenacji ciągów,


\A zamiast wbudowania odwołania $tabiica [ $ r o d z a j ] [$koior] do
drukowanego ciągu. Powodem takiego działania jest to, że PHP 3 mo-
że nieprawidłowo zanalizować wielokrotne indeksy w ciągu, więc trzeba
to wyrażenie dołączyć do ciągu za pomocą operatora. PHP 4 obsługu-
je to w lepszy sposób — możesz bezpiecznie wbudowywać tego typu
wyrażenia w ciągi, jeżeli otoczysz je nawiasami klamrowymi:
print("Skolor Srodzaj to ($tablica[$rodzaj][Skolor)(");

Zauważmy, że konsekwencje pomyłki w indeksowaniu tablic wielowymiarowych, pod-


czas odczytywania informacji, nie są duże. Jeżeli nie ma odpowiedniego klucza, wy-
rażenie będzie traktowane jak zmienna niezainicjowana. Jeżeli spróbujesz wykonać
następujące wiersze:
Srodzaj = 'owoc';
Skolor = 'fioletowy 1 ; // ratunku, nie zapisaliśmy ani jednej śliwki!
print("$kolor Srodzaj to ". $tablica[$rodzaj][$kolor]);

w wyniku otrzymasz:
fioletowy owoc to

Informacje o tablicach
Potrafimy już tworzyć tablice, zapisywać w nich wartości oraz odczytywać je. W tabeli
11.1 zebraliśmy kilka funkcji pozwalających otrzymać informacje na temat tablic.

Tabela 11.1.
Proste funkcje zwracające informacje o tablicach

Funkcja Opis

is_array ( ) Pobiera jeden argument dowolnego typu i zwraca wartość TRUE, jeżeli argument jest
tablica, a w przeciwnym wypadku zwraca FALSE
count ( ) Pobiera jako argument tablicę i zwraca liczbę pełnych elementów tablicy (dla ciągów
i liczb będzie to 1)
sizeof o Identycznie jak count()
in_array ( ) Pobiera dwa argumenty: element (który może znajdować się w tablicy) oraz tablicę (która
może zawierać element). Zwraca TRUE, jeżeli element zawiera się w tablicy

Usuwanie z tablicy
Usunięcie elementu z tablicy jest analogiczne do usuwania zmiennej. Po prostu wywo-
łaj funkcję unset ( ) , np.:
208_________________________________________Część l » Podstawy PHP

$tablica[0] = 'potrzebny';
$tablica[l] = 'niepotrzebny';
$tablica[2] = 'znowu potrzebny';
unset($tablica[l]l;

Zakładając, że zmienna $tablica była niezainicjowana, po wykonaniu tego fragmentu


otrzymamy tablicę zawierającą dwa elementy ('potrzebny', 'znowu potrzebny')
połączone z kluczami O i 2. Element ' niepotrzebny' został usunięty z tablicy.

Zauważ, że operacja ta nie jest równoważna przypisaniu do wartości pustej. Jeżeli za-
miast wywołania funkcji unset ( ) wykonamy następujący wiersz:
Stablicall] = ' ' ;

otrzymamy tablicę zawierającą trzy elementy (' potrzebny', ' ', ' znowu potrzebny')
połączone z kluczami O, l i 2.

Iteracje
Oprócz zapisywania wartości połączonych 7 kluczami, tablice PHP budują uporządko-
waną w kolejności tworzenia listę par klucz i wartość. Wspierają w ten sposób operację
przeglądania całej zawartości tablicy (iteracji). Trudno jest zbudować zwykłą pętlę na
bazie rosnącego indeksu, ponieważ indeksy tablicy nie muszą być liczbami.

W tablicach występuje ukryty mechanizm systemu wskaźników. Każda z zapisanych


par klucz i wartość wskazuje kolejną parę. Jednym z efektów dodania pierwszego ele-
mentu do tablicy jest ustawienie wskaźnika elementu bieżącego na pierwszy element
tablicy. Wskaźnik ten pozostaje, chyba że zostanie przesunięty przez jedną z funkcji ite-
racyjnych.

System list dynamicznych jest alternatywnym sposobem przeglądania i manipulowania


tablicą do systemu zapisywania i odczytywania na podstawie kluczy. Rysunek 1 1 . 1 za-
wiera ogólny schemat tych systemów w tablicach (nie oddaje dokładnie prawdziwej
implementacji).

^^> Każda tablica pamięta, która z par klucz i wartość jest „bieżąca", a funk-
\A cje iteracyjne przesuwają znacznik bieżącego elementu po wewnętrznej
liście kluczy i wartości. Mimo że nazywamy ten znacznik „wskaźnikiem
bieżącym", PHP nie zawiera wskaźników w sensie C i C++; określenie to
występuje tylko w kontekście iteracji po tablicy.

Użycie funkcji iteracyjnych


Aby prześledzić działanie funkcji iteracyjnych, utworzymy przykładową tablicę używa-
ną w kolejnych przykładach.
Rozdział 11. * Tablice i funkcje operujące na tablicach_______________________209

Rysunek 11.1.
Wewnętrzna .. t
struktura tablicy .----------- —————————————— dynamiczna
.. . indeks Wartość
przeszukiwanie _______ _________
—J*» x______«—— ?
indeks Wartość y
+—-^

. indeks
t Wartość Sy
^ - ~ ^

\v indeks Wartość J

\ *~^

L^T^—————^F^
funkcje działające
na indeksach
funkcje
iteracyjne

$stolica = arrayU;
Sstolica(0] = 'Caracas';
$stolica['Caracas'] = 'Wenezuela';
$stolica[l] = 'Paryż';
$stolica['Paryż'] = 'Francja';
$stolica[2] = 'Tokio';
Sstolica['Tokio'] = 'Japonia1;

W tablicy tej zapisaliśmy kilka nazw miast połączonych z indeksami numerycznymi.


Zapamiętaliśmy również w tej tablicy nazwy krajów, indeksując je nazwami miast.
Można to wykonać jedną instrukcją array ( ) , ale rozbicie na pojedyncze przypisania
powoduje, że struktura tablicy jest lepiej widoczna.

Teraz odczytamy dane zawarte w tej tablicy, używając systemu kluczy. Jeżeli przyj-
miemy konwencję opisaną w powyższym przykładzie (miasta zapisane z indeksami
numerycznymi, kraje indeksowane nazwą miasta), możemy napisać funkcję wypisującą
miasto i związany z nim kraj.
function city_by_number ($number__index, $city_array)
(
if (IsSet($city_array[$number_index]))

Sthe_city = $city_array[$number_index];
$the_country = Scity_array[?the_city];
print("$the_city to miasto w kraju $the_country<BR>");
}
}
city_by_number(O, $stolica);
city_by_number(l, Sstolica};
city_by_number(2, $stolica) ;
210___________________________________________Część l » Podstawy PHP

Jeżeli zawartość tablicy jest identyczna jak w poprzednim fragmencie kodu, w przeglą-
darce powinieneś zobaczyć:
Caracas to miasto w kraju Wenezuela
Paryż to miasto w kraju Francja
Tokio to miasto w kraju Japonia

Taka metoda odczytywania działa doskonale, jeżeli znamy strukturę zapisanych w ta-
beli danych i wszystkie klucze. Co zrobić, aby wypisać całą zawartość tablicy? Nie ma
dobrej metody sprawdzenia wszystkich możliwych kluczy. Jeżeliby istniała metoda,
byłaby nieefektywna.

Iteracje za pomocą current() i next()


Za pomocą funkcji iteracyjnych current ( ) i next ( ) można wypisać całą zawartość
tablicy. Zwróć uwagę, że wywołanie funkcji jest powtórzone.
function print_all_array(Scity_array)
{
// Uwaga! Nie działa najlepiej. Sprawdź funkcję each()
$current_item = current($city_array);
if ($current_item)
print("$current_item<BR>") ;
else
print("Nie ma nic do wypisania<BR>");
while ($current_item = next($city_array))
print("$current_itera<BR>") ;
)

print_all_array(Sstolica) ;
print_all_array(Sstolica); // powtórzone, aby sprawdzić jak działa

Funkcja current ( ) zwraca wartość znacznika bieżącego elementu (na rysunku 11.1
pokazano schemat wewnętrznej budowy tablic). Jeśli tablica jest tworzona za pomocą
przypisania jej elementu, element będzie wskazywany jako bieżący. Funkcja n e x t ( )
przesuwa ten wskaźnik i zwraca wartość bieżącego elementu. Jeżeli funkcja n e x t ( )
zostanie wywołana, gdy bieżący wskaźnik znajduje się na ostatnim elemencie tablicy,
zwróci wartość FALSE.
"<3
f^ilfcfe W ostatnim przykładzie znajduje się pułapka (nie widać jej w tym przy-
LjggSjl' padku) obniżająca wiarygodność tej metody poszukiwania elementów
•^"• w tablicy. Jeżeli w tablicy mamy zapisane wartości FALSE, nasza pę-
tla while nie będzie ich potrafiła odróżnić od wartości FALSE, którą
next o zwraca przy wyczerpaniu się elementów tablicy. Rozwiązaniem
tego problemu jest użycie funkcji each ( ) , która zostanie opisana w dal-
szej części tego rozdziału.

Wykonanie poprzedniego fragmentu kodu spowoduje wypisanie w przeglądarce nastę-


pującej listy:
Caracas
Wenezuela
Paryż
Francja
Tokio
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________211

Japonia
Caracas
Wenezuela
Paryż
Francja
Tokio
Japonia

To wszystkie elementy tablicy w kolejności ich zapisywania (powtórzone, ponieważ


wywołaliśmy funkcję dwukrotnie).

W jaki sposób otrzymaliśmy te same wyniki przy drugim wywołaniu funkcji print_
all_array ( ) ? W jaki sposób wskaźnik bieżącego elementu wrócił na początek, aby za
drugim razem funkcja znów przejrzała wszystkie elementy? Odpowiedź jest związana
z faktem, że przekazywanie argumentów do funkcji jest realizowane poprzez wartość,
a nie argument. Oba wywołania funkcji pobieraj ą nową kopię tablicy, na której funkcja
next ( ) nie była wykonywana.
^f—^ ———————————————————————————————————————————————————————————————————————————————————————————————————————

LDirtfP
v
Więcej na temat kopiowania argumentów przez funkcję w rozdziale 8.
Ue _____________________________
Możemy sprawdzić to wyjaśnienie, przekazując do funkcji tablicę poprzez referencję,
a nie przez wartość. Jeżeli zdefiniujemy tę samą funkcję, ale wywołamy ją za pomocą
znaków & przy argumentach:
print_all_array(&$stolica);
print_all_array(&$stolica);

otrzymamy w wyniku następujący wynik:


Caracas
Wenezuela
Paryż
Francj a
Tokio
Japonia
Nie ma nic do wypisania

Tym razem wskaźnik bieżącego elementu globalnej wersji tablicy został przesunięty
przez pierwsze wywołanie funkcji.

^^> Większość funkcji iteracyjnych posiada wartość i efekt uboczny. W przy-


^^ padku funkcji next o, p r e v o , reset o oraz end o efektem ubocz-
nym jest zmiana położenia wewnętrznego wskaźnika; zwracana jest
wartość pary klucz i wartość bieżącego elementu po zmianie położe-
nia wskaźnika

Powtórne przeglądanie za pomocą reset()


W poprzedniej części napisaliśmy funkcję wypisującą wszystkie wartości elementów
tablicy, która nie działa w przypadku, gdy wskaźnik bieżącego elementu tablicy nie
znajduje się na początku listy elementów tablicy. Funkcja reset () pozwala przesunąć
212___________________________________________Część l « Podstawy PHP

ten wskaźnik na początek. Ustawia wskaźnik na pierwszy element i zwraca jego war-
tość. Możemy użyć tej funkcji, aby ulepszyć działanie naszej funkcji drukującej, zamie-
niając wywołanie funkcji current () na reset ().
function print_all_array_reset($city_array)
(
// Uwaga! Nadal nie najlepiej. Sprawdź funkcje each()
$current_item = reset($city_array); // przesuwa i zwraca wartość
if ($current_item)
print("$current_item<BR>");
else
print("Nie ma nic do wypisania<BR>");
while ($current_item = next(Scity_array))
print("$current_item<BR>");
)

Funkcja zawsze rozpoczyna od pierwszego elementu, bez względu na położenie wskaź-


nika bieżącego elementu. Warto uzależnić od sposobu przekazywania argumentu.

Użycie funkcji reset () może wydać się niejasne. Można zamienić pierwszy wiersz
ciała funkcji na dwa następujące:
reset($city_array); // przesunięcie na pierwszy element
$current_item = current($city_array); // wartość pierwszego el.

Wypisywanie w odwrotnym porządku


za pomocą end() i prev()
Poznaliśmy już funkcje next () (przesuwająca wskaźnik do przodu) oraz reset () (prze-
suwająca na początek). Analogicznie działają funkcje prev ( ) (przesuwająca wskaźnik
wstecz) oraz end ( ) (przesuwająca wskaźnik na ostatni element w liście). Możemy ich
użyć, aby wypisać zawartość tablicy w odwrotnym porządku.
function print_all_array_backward($city_array)
{
// Uwaga! Nadal nie najlepiej. Sprawdź funkcję each()
$current_item = end($city_array); // przewija na koniec
if ($current_item)
print ( "$current_item<BR>") ;
else
print("Nie ma nic do wypisania<BR>");
while ($current_item = prev($city_array))
print("$current_item<BR>");
)
print_all_array_backward(Sstolica) ;

Jeżeli wywołamy tę funkcję z taką samą, jak w poprzednich przykładach, zawartością


tablicy $stolica, otrzymamy te same wyniki w odwrotnym porządku:
Japonia
Tokio
Francja
Paryż
Wenezuela
Caracas
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________213

Pobieranie wartości kluczy za pomocą key()


Do tej pory drukowaliśmy tylko wartości zapisane w tablicy, mimo że zapisywaliśmy
również klucze. Wartości kluczy z wewnętrznej listy można pobrać używając funkcji
key ( ) , która działa analogicznie do current ( ) , ale zwraca klucz, a nie wartość (patrz
rysunek 11.1). Aby użyć funkcji key O , zmodyfikujemy jedną z naszych wcześniej-
szych funkcji, żeby wypisywała zarówno klucze, jak i wartości.
function print_keys_and_values(Scity_array)
(
// Uwaga! Przeczytaj opis funkcji each()
reset{$city_array);
$current_item = current(Scity_array);
$current_key = key($city_array) ;
if ($current_item)
print("Klucz: $current_key; Wartość: $current_item<BR>");
else
print("Nie ma nic do wypisania<BR>");
while ($current_itern = next($city_array))
(
$current_key - key($city_array);
print("Klucz: $current_key; Wartość: $current_item<BR>");
)
}
print_keys_and_values($stolica);

W wyniku otrzymujemy:
Klucz 0; Wartość: Caracas
Klucz Caracas; Wartość: Wenezuela
Klucz 1; Wartość: Paryż
Klucz Paryż; Wartość: Francja
Klucz 2; Wartość: Tokio
Klucz Tokio; Wartość: Japonia

Wartości puste i funkcja each()


Napisaliśmy kilka funkcji drukujących zawartość tablicy, ale każda z nich ma tę samą
słabość. Każda z nich kontroluje warunek zakończenia poprzez sprawdzenie, czy funk-
cja next () zwraca wartość FALSE. Zdarza się to, gdy zostaną wyczerpane wszystkie
elementy tablicy. Może się zdarzyć, jeżeli napotkamy zapisaną w tablicy wartość FAL-
SE. Wartością FALSE może być pusty ciąg, liczba O lub wartość logiczna FALSE. Każda
z tych wartości może zostać zapisana w tablicy jako prawidłowa wartość wykorzysty-
wana do pewnych zadań.

Na ratunek przychodzi nam funkcja each ( ) , która jest nieco podobna do next ( ) , ale
zwraca wartość FALSE jedynie wtedy, gdy zabraknie elementów tablicy. Funkcja each ()
dodatkowo zwraca tablicę zawierającą zarówno wartość klucza, jak i wartość elementu.
Utrudnia to opisywanie tej funkcji, ponieważ trzeba rozróżnić dwie tablice: tablicę, któ-
rą przeglądamy, i tę, którą each ( ) zwraca za każdym wywołaniem. Zwracana tablica
ma cztery pary wartości klucz i wartość:
* klucz: O, wartość: bieżący klucz;
•* klucz: l, wartość: bieżąca wartość;
214___________________________________________Część l » Podstawy PHP

* klucz: ' k e y ' , wartość: bieżący klucz;


* klucz: ' value ', wartość: bieżąca wartość.

Bieżący klucz i bieżąca wartość to klucz i wartość bieżącego elementu przeglądanej ta-
blicy. Inaczej mówiąc, zwracana wartość jest paczką zawierającą parę klucz i wartość
z przeglądanej tablicy (oferuje indeksy numeryczne oraz będące ciągami).

Oprócz różnych typów zwracanych wartości, funkcja each o różni się


od funkcji next ( ) tym, że each ( ) zwraca wartość przed przesunięciem
wskaźnika bieżącego elementu, natomiast next o zwraca wartość po
przesunięciu elementu. Oznacza to, że jeżeli wskaźnik bieżącego ele-
mentu będzie na początku tablicy, kolejne wywołania funkcji each o
zwrócą wszystkie wartości zapisane w tablicy, a takie same wywoła-
nia funkcji next o ominą pierwszy element.

Użyjmy funkcji each ( ) , aby napisać lepszą wersję funkcji wypisującej wartości kluczy
i wartości zapisane w tablicy:
function print_keys_and_values_each($city_array)
(
// Skutecznie wypisuje całą zawartość tablicy
reset($city_array);
while ($array_cell = each($city_array))
(
$current_value = $array_cell['value'];
$current_key = $array_cell['key'];
print("Klucz: $current_key; Wartość: $current_value<BR>"};
)
}
print_keys_and_values_each(Sstolica) ;

Wykonanie tej funkcji na naszej standardowej tablicy da następujący wynik:


Klucz 0; Wartość: Caracas
Klucz Caracas; Wartość: Wenezuela
Klucz 1; Wartość: Paryż
Klucz Paryż; Wartość: Francja
Klucz 2; Wartość: Tokio
Klucz Tokio; Wartość: Japonia

Wynik ten jest identyczny z wynikiem działania poprzedniej funkcji print_keys_


and_values ( ) . Różnica polega na tym, że nowa funkcja nie zatrzyma się wcześnie,
jeżeli napotka pustą wartość lub wartość FALSE.

Przeglądanie tablicy za pomocą array_walk()


Ostatnia funkcja iteracyjna pozwala przekazać dowolną funkcję napisaną przez użyt-
kownika do wykonania na elementach tablicy. Funkcja taka może wykonać dowolne
czynności na parze klucz i wartość. Funkcja array_walk ( ) wymaga dwóch argumen-
tów: tablicy do przeglądania i nazwy funkcji wykonywanej dla każdego elementu (po-
siada również trzeci opcjonalny argument, który opiszemy później).
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________215

Funkcja przekazana do a r r a y _ w a l k ( ) powinna posiadać dwa lub trzy argumenty.


Pierwszy element jest wartością komórki tablicy, a drugi jej kluczem. Poniższa funkcja
drukuje ciąg informujący o długości ciągu zapisanego w tablicy:
function print_value_length($array_value, $array_key)
{
$the_length = strlen($array_value);
print("Ciąg $array_value ma długość: Sthe_length<BR>");
)

Funkcja ta nie korzysta z drugiego argumentu. Użyjmy teraz tej funkcji do naszej stan-
dardowej tablicy z wykorzystaniem funkcji a r r a y _ w a l k ( ) :
array_walk(Sstolica, 'print_value_length');

W efekcie zobaczymy:
Ciąg Caracas ma długość: 7
Ciąg Wenezuela ma długość: 9
Ciąg Paryż ma długość: 5
Ciąg Francja ma długość: 7
Ciąg Tokio ma długość: 5
Ciąg Japonia ma długość: 7

Funkcja array_walk() posiada trzeci, opcjonalny argument, który, przekazywany jest


do trzeciego argumentu funkcji definiowanej przez użytkownika. Argument ten jest iden-
tyczny dla wszystkich elementów tablicy, ale pozwala na dodatkowe sterowanie funkcją.

W tabeli 11.2 zamieszczone jest podsumowanie działania wszystkich funkcji iteracyj-


nych opisanych w tym rozdziale.

Stosy i kolejki
Stosy i kolejki są abstrakcyjnymi strukturami danych, często używanymi w teorii in-
formatyki. Zapewniają odpowiednią dyscyplinę w dostępie do przechowywanych ele-
mentów. Jak wcześniej wspomnieliśmy, tablice PHP pozwalają na imitację innych
struktur danych. Niezależność tablic od typu zapisywanych danych pozwala łatwo im-
plementować stosy i kolejki. PHP zawiera kilka funkcji przeznaczonych do tego celu.
Jeżeli będziesz używał tylko tych funkcji, możesz zapomnieć, że bazują na tablicach.
Stos jest kontenerem zapamiętującym wartości, zapewniającym tryb dostępu do danych
LIFO (ang. last in-first out — ostatni wchodzi, pierwszy wychodzi). Oznacza to, że stos
zapamiętuje kolejność zapisu elementów, a jedynym sposobem otrzymania wartości
elementu jest pobranie (i usunięcie) ostatnio zapisanej wartości. Zwykle porównuje się
tę strukturę do stosu tac w kawiarni w jednym z zasobników, utrzymujących stały po-
ziom górnej tacy. Można dodać nowe tace do stosu starych i można zabrać tace z góry,
ale nie da się wyjąć starej tacy bez wyjęcia nowych. Czynność dodawania do stosu na-
zywana jest „położeniem" wartości na stos, a czynność uzyskania wartości z góry jest
nazywana „zdjęciem" ze stosu. Inną analogią jest sposób, w jaki niektóre przeglądarki
zapamiętują odwiedzone strony, aby można było wrócić do nich za pomocą przycisku
Wstecz. Przejście do nowej strony powoduje położenie nowego adresu na stos, a użycie
przycisku Wstecz zdejmuje adres ze stosu.
216_________________________________________Część l » Podstawy PHP

Tabela 11.2.
Funkcje iteracyjne

Funkcja Argumenty Efekt uboczny Zwracana wartość


current () Jeden argument: tablica Brak Wartość bieżącego elementu
tablicy lub FALSE, jeżeli nie
ma już elementów
next ( ) Jeden argument: tablica Przesuwa wskaźnik Wartość bieżącego elementu
bieżącego elementu do po jego przesunięciu (lub
przodu. Jeżeli znajduje się na FALSE, jeżeli nie ma już
ostatnim elemencie, kolejne elementów)
wywołania zwracają FALSE
prev ( ) Jeden argument: tablica Przesuwa wskaźnik wstecz Wartość bieżącego elementu
o jeden. Jeżeli znajduje się po jego przesunięciu (lub
na pierwszym elemencie, FALSE, jeżeli nie ma już
przesuwa wskaźnik „przed elementów)
początek"
reset ( ) Jeden argument: tablica Przesuwa wskaźnik na Wartość pierwszego
pierwszy element lub „przed elementu lub FALSE, jeżeli
początek", jeżeli tablica jest tablica jest pusta
pusta
end() Jeden argument: tablica Przesuwa wskaźnik na Wartość ostatniego elementu
ostatni element tablicy
pos () Jeden argument: tablica Brak (funkcja identyczna Wartość bieżącego elementu
z current ( ) ) tablicy lub FALSE, jeżeli nie
ma już elementów
each() Jeden argument: tablica Przesuwa wskaźnik do Tablica zawierająca wartość
następnego elementu i klucz elementu
wskazywanego przed
przesunięciem wskaźnika
(lub FALSE, jeżeli nie ma
już elementów). Zwracana
tablica przechowuje wartości
pod indeksami 0 i 1 oraz
'key' i 'value'
array walk ( ) 1 . Tablica Funkcja wywołuje funkcję Zwraca 1
2. Nazwa o nazwie przekazanej przez jej
dwuargumentowej drugi argument dla każdego
(lub trójargumentowej) elementu tablicy. Efekt
funkcji wywoływanej uboczny zależy od efektu
dla każdego elementu ubocznego realizowanego
3. Opcjonalny argument przez przekazaną funkcję

Kolejka jest podobna do stosu, ale dostęp do danych jest organizowany na zasadzie FI-
FO (ang. First in-First out — pierwszy wchodzi, pierwszy wychodzi). Zwykle porów-
nuje się tę strukturę z kolejką osób. Ten, kto czeka dłużej w kolejce, będzie obsłużony
w następnej kolejności.
Rozdział 11. » Tablice i funkcje operujące na tablicach_____________________217

Funkcje obsługi stosu to array_push ( ) i array_pop ( ) . Funkcja array_push ( ) ocze-


kuje nazwy tablicy jako pierwszego argumentu, następnie dowolnej liczby elementów
do położenia na stos. Elementy będą wstawiane w kolejności od lewej do prawej. Funk-
cja array_pop ( ) wymaga podania tablicy i zdejmuje element ze szczytu stosu, zwra-
cając go jako wartość. Rozważmy następujący fragment:
Sstos = arrayO; // wymagany - array_push() nie stworzy tablicy
array_push(Sstos, "pierwszy", "środkowy");
array_push(?stos, "ostatni"};
while ($pobrany = array_pop(Sstos))
print("Zdejmujemy ze stosu i mamy: $pobrany<BR>");

W efekcie otrzymamy:
Zdejmujemy ze stosu i mamy: ostatni
Zdejmujemy ze stosu i mamy: środkowy
Zdejmujemy ze stosu i mamy: pierwszy

PHP 4 posiada również funkcje działające w ten sam sposób, co array_push ( ) i ar-
ray_pop ( ) , ale na drugim końcu tablicy (dodają i usuwają elementy z początku tabli-
cy). Funkcja array_unshif t ( ) jest analogiczna do array_push ( ) , a r r a y _ s h i f t ( )
działa podobnie do array_pop ( ) . Jeżeli weźmiemy jedną z funkcji z kolumny A i jed-
ną z kolumny B, otrzymamy obsługę kolejki. Zmodyfikujmy nasz poprzedni przykład,
aby położyć elementy na początek tablicy (za pomocą array_unshift ( ) ) i zdjąć je
z końca (za pomocą array_pop ( ) ) :
$kolejka = arrayO; // wymagany - array_unshif t () nie stworzy tablicy
array_unshift($kolejka, "pierwszy", "środkowy");
array_unshift(Skolejka, "ostatni");
while ($pobrany = array_pop($kolejka))
print{"Zdejmujemy ze stosu i mamy: $pobrany<BR>");

Fragment ten daje następujący wynik:


Z d e j m u j e m y ze stosu i mamy: pierwszy
Zdejmujemy ze stosu i mamy: środkowy
Z d e j m u j e m y ze stosu i mamy: ostatni
^

f^-li^fe Funkcje a r r a y _ u n s h i f t O i a r r a y _ s h i f t () różnią się od a r r a y _


Q^4Jr pusho iarray_pop() tym, że przenumerowują tablicę, jeżeli indeksy
są liczbami całkowitymi. Niektórzy użytkownicy używają indeksów nu-
merycznych, aby uporządkować zawartość tablicy. Użycie array_un-
shifto do wstawienia wartości na początek tablicy powinno przypi-
sać indeks O nowemu elementowi i zmienić numerację pozostałych.
Pobranie elementu z początku za pomocą funkcji array_shifto
powoduje zmniejszenie wartości klucza o jeden w pozostałych ele-
mentach (nie zachodzi to W przypadku array_push() i a r r a y _ p o p ( ) ,
ponieważ działają one na końcu tablicy, więc renumeracja nie jest po-
trzebna). Jeżeli używasz ciągów jako indeksów, funkcje nie zmienią
ich wartości. Zdarza się to często w funkcjach PHP operujących na
tablicach: niektóre z nich traktują indeksy jak inne indeksy asocjacyj-
ne; niektóre zakładają, że liczby te oznaczają porządek w tablicy
i zmieniają je w razie potrzeby.
218_________________________________________Część l » Podstawy PHP

Funkcje realizujące stos i kolejkę zebrane są w tabeli 11.3.

Tabela 11.3.
Funkcje stosu i kolejki

Funkcja Argumenty Efekt uboczny Zwracana wartość

array_push() Pierwszy argument to Dodaje elementy na koniec Lic/.ba elementów


tablica, następnie tablicy w tablicy po wstawieniu
dowolna liczba wartości nowych
array_pop() Jeden argument: tablica Usuwa element z końca Zwraca ostatni
tablicy (usunięty) element lub
wartość FALSE, jeżeli
tablica jest pusta
array_unshif t O Pierwszy argument to Dodaje elementy na początek Liczba elementów
tablica, następnie tablicy (ostatni argument w tablicy po wstawieniu
dowolna liczba wartości będzie pierwszym elementem nowych
tablicy)
array_shift () Jeden argument: tablica Usuwa element z początku Zwraca pierwszy
tablicy (usunięty) element lub
wartość FALSE, jeżeli
tablica jest pusta

Przekształcenia tablic
PHP 4 posiada wiele funkcji manipulujących danymi zawartymi w tablicy. Wszystkie funk-
cje przedstawione w tej części wykonuj ą określone czynności na tablicy i zwracają wynik
w postaci innej tablicy (funkcje sortujące tablice zostały wydzielone w osobnej części).

W tym rozdziale ulepszaliśmy funkcję drukującą całą zawartość tablicy; w tej części
użyjemy ostatniej wersji (print_keys_and_values_each ( ) ) do pokazania zawarto-
ści tablicy zwracanej przez opisywane funkcje.

Pobieranie kluczy i wartości


Funkcja array_keys ( ) zwraca klucze z tablicy, przekazanej jako argument, jako nową
tablicę, w które są zapisane jako wartości. Kluczami nowej tablicy są zwykle kolejne
liczby począwszy od zera. Funkcja array_values ( ) robi dokładnie to samo z warto-
ściami zapisanymi w tablicy.

Zapiszmy w tablicy następujące dane:


?pizza_requests = array('Alice' => 'pepperoni',
'Bob' => 'grzyby',
'Carl'=> 'kiełbasa',
'Dennis' => 'grzyby');
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________219

Teraz wypiszemy zawartość tablic będących wynikiem działania tych dwóch funkcji:
print("Klucze tablicy:<BR>");
print_keys_and_values_each( array_keys($pizza_requests));
print("Wartości tablicy:<BR>");
print_keys_and_values_each( array_values($pizza_requests));

W wyniku otrzymamy:
Klucze tablicy:
Klucz 0; Wartość Alice
Klucz 1; Wartość Bob
Klucz 2; Wartość Carl
Klucz 3; Wartość Dennis
Wartości tablicy:
Klucz 0; Wartość pepperoni
Klucz 1; Wartość grzyby
Klucz 2; Wartość kiełbasa
Klucz 3; Wartość grzyby

Druga z funkcji (array_values ( ) ) nie wydaje się interesująca, ponieważ na podsta-


wie starej tablicy otrzymujemy po prostu nową tablicę z kluczami zamienionymi na
kolejne liczby.

Możemy zrobić coś bardziej użytecznego (i przydatnego w porządkowaniu) za pomocą


funkcji array_count_values ( ) . Funkcja ta pobiera tablicę i zwraca nową, w której
stare wartości są kluczami, a wartością — liczba wystąpień tej wartości w oryginalnej
tablicy.
print_keys_and_values_each(array_count_values($pizza_requests)} ;

W wyniku otrzymujemy:
Klucz: pepperoni; Wartość: l
Klucz: grzyby; Wartość: 2
Klucz: kiełbasa; Wartość: l

Zamiana, odwracanie i mieszanie


Jeszcze dziwniejszą funkcją jest array_f lip { ) , która zamienia klucze tabeli na war-
tości, i na odwrót. Dla przykładu:
print_keys_and_values_each(array_flip($pizza_requests));

co daje:
Klucz: pepperoni; Wartość: Alice
Klucz: grzyby; Wartość: Dennis // co stało się z Bobem?
Klucz: kiełbasa; Wartość: Carl

Należy zapamiętać, że w tablica gwarantuje unikalność kluczy, a wartości mogą się po-
wtarzać. Z tego powodu wszystkie powtarzające się wartości z oryginalnej tablicy stają
się kluczem w nowej. Tylko jeden z oryginalnych kluczy pozostaje nową wartością.

Odwrócenie tablicy jest proste: array_reverse ( ) zwraca nową tablicę z elementami


w odwrotnym porządku. Użyjmy naszej funkcji testującej:
print_keys_and_values_each(array_reverse($pizza_requests));
220_________________________________________Część l » Podstawy PHP

W wyniku otrzymamy:
Klucz: Dennis; Wartość: grzyby
Klucz: Carl; Wartość: kiełbasa
Klucz: Bob; Wartość: grzyby
Klucz: Alice; Wartość: pepperoni

Mimo że został zmieniony wewnętrzny porządek tablicy, wszystkie pary klucz i wartość
pozostały takie same. Jednak funkcja ta (jak kilka innych PHP) traktuje indeksy nume-
ryczne w specjalny sposób. Zakłada, że porządek kluczy powinien zostać również odwró-
cony, aby można było użyć kodu pobierającego porządek elementów z indeksów, zamiast
porządku wewnętrznej listy. Funkcja array__reverse ( ) zamienia kolejność kluczy nu-
merycznych, aby nowy porządek kluczy odpowiadał porządkowi listy wewnętrznej.

Funkcja shuffle ( ) pobiera tablicę jako argument i zmienia w losowy sposób porządek
elementów tablicy. Używa funkcji randO (funkcja generująca kolejne liczby losowe),
więc przed jej wywołaniem trzeba zainicjować generator liczb losowych za pomocą
srand ( ) . Odpowiednia kolejność wywoływanych funkcji przedstawiona jest poniżej:
s r a n d ( (double) m i c r o t i m e O * 1 0 0 0 0 0 0 ) ;
/ / zainicjowanie generatora liczb losowych
shuffle(Spizza_requests);
print_keys_and_values_each($pizza_requests);

Kod ten może dać następujący wynik:


Klucz: 0; Wartość: pepperoni
Klucz: 1; Wartość: grzyby
Klucz: 2; Wartość: kiełbasa
Klucz: 3; Wartość: grzyby

Użyliśmy słowa „może", ponieważ nie można przewidzieć porządku.

VoatfP Więcej na temat generowania liczb losowych w rozdziale 10. Jeżeli za-
tskZfi rnierzasz użyć funkcji shuffle o, nie czytając rozdziału 10., na stro-
nie używającej shuffle o wywołaj funkcję srand o (tylko raz), jak
w przykładzie.

"<j
r^ltel& ^ przeciwieństwie do wielu funkcji operujących na tablicach, funkcja
[jlJs|f shuffle o jest destrukcyjna. Operuje bezpośrednio na argumentach
~ tablicy, zmieniając je, nie zwraca nowej tablicy. Oznacza to, że
$nowa_tablice = shuffle($stara_tablica); // ŹLE

jest niewłaściwym sposobem wywołania funkcji shuffle o , ponieważ


funkcja shuffieo nie zwraca wartości. Właściwym sposobem wywo-
łania jest:
shuffle($tablica); // zmienia tablicę

Powyższe funkcje, oraz kilka dodatkowych, zestawione sąw tabeli 11.4.


Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________221

Tabela 11.4.
Funkcje przekształcające tablice

Funkcja Opis

a r r a y keys O Posiada tablicę jako argument; zwraca nową tablicę zawierającą klucze
z wejściowej tablicy; nowymi kluczami są liczby rozpoczynające się od zera
array values ( ) Posiada tablicę jako argument; zwraca nową tablicę zawierającą wartości
z wejściowej tablicy; nowymi kluczami są liczby rozpoczynające się od zera
a r r a y count values ( ) Posiada tablicęjako argument; zwraca nową tablicę, w której kluczami są
wartości ze starej tablicy; wartościąjest liczba wystąpień oryginalnej tablicy
array f l i p O Posiada tablicęjako argument; zamienia klucze na wartości i odwrotnie
a r r a y reverse ( ) Posiada tablicęjako argument; zmienia wewnętrzny porządek elementów
tablicy na odwrotny. Klucze numeryczne są przy tym przenumerowywane
shuffleO Posiada tablicęjako argument; zmienia wewnętrzny porządek elementów
tablicy na przypadkowy. Zmienia wartości kluczy numerycznych tak, aby
pasowały do nowego uporządkowania. Funkcja korzysta z generatora liczb
losowych rand ( ) ; przed wywołaniem shuffle ( ) musi zostać wywołana
funkcja srand ()
a r r a y merge ( ) Dwie tablicęjako argumenty; zwraca tablicę będącą połączeniem obu tablic,
elementy pierwszej znajdują się na początku, następnie elementy drugiej.
Najbardziej użyteczne dla tablic używanych w charakterze list, ponieważ
wartości powtarzających się kluczy zostaną nadpisane. Również klucze
numeryczne są przenumerowywane dla odzwierciedlenia nowego porządku
a r r a y pad ( ) Posiada trzy argumenty: wejściową tablicę, rozmiar wypełnienia i wartość
wypełnienia. Zwraca nową tablicę wypełnioną według następujących zasad:
jeżeli rozmiar wypełnienia jest większy od długości tablicy wejściowej,
tablica jest wydłużana, za pomocą wartości wypełnienia, do rozmiaru
wypełnienia dzięki kolejnym przypisaniom typu $tablica [ ] =$wart
wypełnienia. Ujemna wartość rozmiaru wypełnienia powoduje działanie
podobne, ale wypełnianie jest realizowane od początku tablicy, a nie od
końca. Jeżeli tablica ma długość większą od podanego rozmiaru wypełnienia
(w liczbach bezwzględnych), funkcja ta nie daje żadnego efektu
array_slice ( ) Posiada trzy argumenty: tablicę wejściową, przesunięcie oraz opcjonalnie
długość. Zwraca nową tablicę, będącą wycinkiem wejściowej. Początek
i koniec wycinka określany jest przez przesunięcie i długość. Dodatnia
wartość przesunięcia oznacza, że początek jest liczony od początku tablicy;
wartość ujemna powoduje obliczenie punktu początkowego od końca tablicy.
Opcjonalny argument długość określa, ile elementów będzie zawierać nowa
tablica (jeżeli jest to wartość dodatnia) lub na którym elemencie od końca się
zakończy (w przypadku wartości ujemnej). Jeżeli argument ten nie zostanie
podany, wycinek kończy się na końcu tablicy wejściowej
array spliceO Usuwa fragment tablicy i wstawia w to miejsce fragment pochodzący z innej.
Posiada cztery argumenty: tablicę wejściową, przesunięcie, opcjonalnie
długość i opcjonalnie tablicę zawierającą wartości do wstawienia. Zwraca
nową tablicę, zawierającą fragment wycięty z wejściowej. Zasady wyliczania
początku i końca wycinka są identyczne jak w funkcji a r r a y slice ( ) .
Jeżeli nie zostanie podana tablica do wstawienia, funkcja usuwa wycinek
tablicy wejściowej i zwraca go jako wartość. Jeżeli przekazana zostanie
tablica do wstawienia, jej elementy zostaną wstawione w miejsce usuniętych
222_________________________________________Część l » Podstawy PHP

Zamiana pomiędzy tablicą i zmiennymi


PHP 4 posiada kilka rzadko spotykanych funkcji, zamieniających pary klucz i wartość
w tablicy na pary klucz i wartość zapisane w zwykłych zmiennych. Funkcja compact ( )
zamienia zmienne na tablicę, extract ( ) działa w przeciwnym kierunku. Funkcje te są
opisane w tabeli 11.5.

Tabela 11.5.
Funkcje zamiany pomiędzy zmiennymi i tablicą

Funkcja Opis

compact ( ) Pobiera podany zbiór ciągów i analizuje go w poszukiwaniu nazw zmiennych; zwraca
tablicę, w której kluczami są nazwy zmiennych, a wartościami — wartości tych zmiennych
Funkcja przyjmuje dowolną liczbę argumentów, będących ciągami lub tablicami
zawierającymi ciągi na dowolnym poziomie zagłębienia. Zbiór ciągów przekazanych do
funkcji jest analizowany w poszukiwaniu zmiennych. Ciągi niezawierające nazw
zainicjowanych zmiennych są odrzucane
extract ( ) Jako argument posiada tablicę oraz dwa argumenty opcjonalne. Importuje pary klucz
i wartość do środowiska wykonania skryptu. Klucze tablicy stają się nazwami zmiennych,
a wartości z tablicy — wartościami zmiennych. Klucze, które nie są prawidłowymi nazwami
zmiennych, są ignorowane
Argumentami opcjonalnymi są: liczba całkowita (zawierająca jedną ze stałych) oraz ciąg
zawierający przedrostek. Zadaniem tych argumentów jest obsłużenie kolizji istniejących
zmiennych z zmiennymi tworzonymi na podstawie kluczy tablicy. Dozwolonymi stałymi dla
drugiego argumentu funkcji są: 1. EXTRJDVERWRITE; 2. EXTR_SKIP; 3.
EXTR_PREFIX_SAME oraz 4. EXTR__PREFIX_ALL. Dla tych czterech wartości funkcja działa
w następujący sposób: 1. Nadpisz istniejące zmienne; 2. Opuść przypisanie mogące zamazać
istniejącą zmienną; 3. Użyj przedrostka, jeżeli przypisanie zniszczy istniejącą zmienną;
4. Użyj przedrostka dla wszystkich zmiennych tworzonych przez funkcję. Dla przykładu:
e x t r a c t ( a r r a y ( ' z m i e n n a ' => 4 ) , E X T R _ P R E F I X _ S A M E , ' d i f f _ ' ) ; spowoduje
przypisanie do zmiennej Szmienna wartości 4, jeżeli $ zmienna nie była do tej pory użyta.
W przeciwnym przypadku wartość 4 zostanie przypisana do $dif f__zmienna

Sortowanie
Na koniec przedstawimy zestaw funkcji sortujących tablice. Jak wcześniej zauważyli-
śmy, często występuje rozdźwięk pomiędzy utrzymywaniem asocjacji pary klucz
i wartość a traktowaniem kluczy numerycznych jako elementu porządkującego tablicę
(co skutkowało zmianą wartości kluczy w przypadku zmiany kolejności elementów
w tablicy). Na szczęście PHP ma odpowiednie warianty funkcji sortujących, które po-
zwalają na wybór pomiędzy sortowaniem w porządku rosnącym i malejącym oraz za-
pewniają możliwość przekazania własnej funkcji porządkującej.
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________223

Nazwy tych funkcji są bardzo zwięzłe, każda litera (poza frazą ' s o r t ' ) ma własne
znaczenie. Sposób ich rozszyfrowywania jest następujący:
* początkowe ' a ' oznacza, że funkcja sortując wartości utrzymuje asocjacje po-
między kluczami i wartościami;
* początkowe ' k ' oznacza, że funkcja sortuje klucze utrzymując asocjacje;
* brak początkowego ' a ' oraz ' k ' oznacza, że funkcja sortując wartości nie
utrzymuje asocjacji pomiędzy kluczem i wartością. Numeryczne klucze zostają
przenumerowane dla odzwierciedlenia nowego porządku elementów;
** litera ' r ' oznacza odwrotny porządek sortowania;
+ początkowe ' u ' oznacza, że funkcja spodziewa się w drugim argumencie na-
zwy funkcji, określającej porządek dowolnych dwóch sortowanych elementów
(więcej informacji w tabeli 11.6).

Tabela 11.6.
Funkcje sortujące tablice

Funkcja Opis

asort ( ) Posiada jeden argument. Sortuje elementy według ich wartości, zachowując powiązanie
pomiędzy kluczem i wartością. Dobra dla tablic asocjacyjnych
arsort ( ) Identycznie jak asort ( ) , ale sortuje w odwrotnym porządku
ksort ( ) Posiada jeden argument. Sortuje elementy według ich kluczy, zachowując powiązanie
pomiędzy kluczem i wartością
krsort ( ) Identycznie jak krsort ( ) , ale sortuje w odwrotnym porządku
sort () Posiada jeden argument. Sortuje elementy według ich wartości. Klucze mogą zostać
przenumerowane dla odzwierciedlenia nowego porządku wartości
rsort ( ) Identycznie jak rsort ( ) , ale sortuje w odwrotnym porządku
uasort ( ) Sortuje elementy używając funkcji porównującej. Jest podobna do asort ( ) , ale porządek
elementów jest ustalany przez funkcję, której nazwa jest przekazana w drugim argumencie.
Funkcja powinna zwracać wartość ujemną, jeżeli pierwszy element jest „przed" drugim,
wartość dodatnią, jeżeli pierwszy element znajduje się „za" drugim, oraz zero, jeżeli elementy
są takie same
Uksort ( ) Identycznie jak uksort ( ) , ale sortuje w odwrotnym porządku
usort ( ) Sortuje tablicę przy użyciu przekazanej funkcji porównującej. Podobna do uasort ( ) poza
tym, że nie utrzymuje powiązania klucza z wartością (jak sort ())

Podsumowanie
Tablica jest podstawowym typem PHP. Pozwala na emulację typu rekordowego oraz
wektorów znanych z innych języków programowania. Tablice w PHP są asocjacyjne, co
224_________________________________________Część l » Podstawy PHP

oznacza, że przechowują wartości w połączeniu z unikalnymi kluczami bądź indeksami.


Indeksy mogą być zarówno ciągami, jak i liczbami. Są umieszczane w nawiasach kwa-
dratowych (wyrażenie $tablica [4 ] reprezentuje wartość zapamiętaną w tablicy $ta-
blica związaną z indeksem 4; nie musi to być czwarty element tablicy).

Z powodu ulotności typów do tablicy PHP można zapisać dowolną wartość. Oznacza to
również, że tablice mogą występować jako elementy tablicy. „Tablice wielowymiaro-
we" to po prostu tablice zawierające tablice jako elementy. Do kolejnych poziomów
można się dostać za pomocą indeksów w kolejnych nawiasach (wyrażenie $ tabli-
ca [ 3 ] [ 4 ] odwołuje się do elementu tablicy — indeksowanego przez 4, który jest ele-
mentem tablicy $tablica — indeksowanym przez 3).

Tablice są podstawą funkcji zwracających dane strukturalne, więc programiści PHP


powinni nauczyć się rozpakowywać tablice, nawet jeżeli nie są zainteresowani ich two-
rzeniem. PHP dostarcza wielu funkcji manipulujących danymi zapisanymi w tablicach,
np. funkcje zwracające liczbę, sumujące i sortujące.
Rozdział 12.
Przekazywanie danych
pomiędzy stronami
W tym rozdziale:
* Dlaczego HTTP jest jak toczący się kamień?
* Argumenty GET
** Inne użycie adresów URL w stylu GET
* Argumenty POST
* Obsługa zmiennych GET/POST w PHP

W tym rozdziale zajmiemy się przekazywaniem danych, na przykład zmiennych, po-


między stronami WWW. Niektóre z tych informacji nie należą jedynie do PHP, ale są
konsekwencją interakcji PHP i HTML lub samego protokołu HTTP.

HTTP jest protokołem bezstanowym


Ważne, abyś pamiętał, że sam protokół HTTP jest protokołem bezstanowym. Oznacza
to, że każde wywołanie HTTP jest niezależne od innych, nie ma informacji na temat
stanu klienta i nie jest zapamiętywane. Każde wywołanie powoduje utworzenie osobne-
go wątku, który ma za zadanie dostarczyć jeden plik, następnie jest niszczony (uściśla-
jąc, wraca do puli dostępnych wątków).

Nawet jeżeli projekt twojej witryny zakłada nawigację tylko w jedną stronę (ze strony 1.
tylko do strony 2., a następnie do strony 3. itd.), protokół HTTP nigdy nie będzie wiedział,
czy użytkownik oglądający stronę 2. załadował wcześniej stronę 1. Nie możesz ustawić na
stronie 1. zmiennej i oczekiwać, że zostanie ona zaimportowana do strony 2. tylko przez
mechanizmy HTML. Możesz użyć HTML do wyświetlenia formularza, do którego można
wprowadzić informacje, ale jeżeli nie zrealizujesz mechanizmów przekazujących dane do
innej strony lub programu, zmienne znikną w momencie przejścia na inną stronę.
226_________________________________________Część l » Podstawy PHP

Dla takich przypadków stworzone zostały technologie przetwarzania formularzy, np.


PHP. PHP przechwytuje zmienne przenoszone z jednej strony do drugiej i pozwala na
ich późniejsze wykorzystanie. Jest niezwykle użyteczny do funkcji przenoszenia danych
i pozwala na ich łatwe zastosowanie w wielu przypadkach.

Istnieją bardziej zaawansowane sposoby zapamiętania interakcji klienta z witryną, jak


cookie oraz sesje. W tym rozdziale skupimy się tylko na podstawowych technikach
przenoszenia danych pomiędzy stronami z zastosowaniem metod GET i POST z HTML.

^^^ Programiści ASP niezmiennie twierdzą, że PHP jest gorszy, ponieważ


^-A uważają, że zmienne sesji są czymś niezwykłym. Nie daj się zakrzy-
czeć, Microsoft używa mechanizmu cookie do przechowywania zmien-
nych sesji, co może powodować wiele problemów. Możesz o tym
przeczytać w dodatku B.

Argumenty GET
Metoda GET pozwala na przesyłanie argumentów pomiędzy stronami za pomocą frag-
mentu adresu URL. W przypadku użycia tej metody do obsługi formularzu, GET powo-
duje dodanie nazw zmiennych i ich wartości do adresu URL podanego jako atrybut
ACTION. Poszczególne zmienne rozdzielone są znakiem zapytania. Kompletny adres
URL przesyłany jest do przetworzenia (w tym przypadku przez PHP).

Poniżej przedstawiamy przykładowy formularz HTML używający metody GET.


<HTML>
<HEAD>
<TITLE>Przykład GET część 1</TITLE>
</HEAD>
<BODl>
<FORM ACTION="http://localhost/php/baseball.php" METHOD="GET">
<P>Root, root, root for the:<BR>
<SELECT NAME="Team" SIZE=2>
<!— Dobrze jest używać atrybutu VALUE, nawet w przypadkach, gdy
nie jest obowiązkowy. W tym przykładzie jest on absolutnie niezbędny.
—>
<OPTION VALUE="Cubbies">Chicago Cubs (National League)
<OPTION VALUE="Pale Hose">Chicago White Sox (American League)
</SELECT>
<PXINPUT TYPE="submit">
</FORM>
</BODY>
</HTML>

Gdy użytkownik wybierze jedną z wartości i kliknie przycisk, przeglądarka sklei bez
dodatkowych odstępów następujące elementy:
•* adres URL podany w apostrofach po słowie ACTION (http://localhost/php/ba-
seball.php);
* znak zapytania (?);
Rozdział 12. » Przekazywanie danych pomiędzy stronami______________________227

* nazwę zmiennej, znak równości i jej wartość (Team=2);


* znak & oraz kolejną parę NAME=VALUE (Submit=Submit). Ta część może być
powtarzana tyle razy, na ile pozwala ograniczenie długości adresu przez serwer.

Dla naszego przykładu utworzony zostanie następujący ciąg URL:


http: //localhost/php/baseball .php?Team=Cubbies&Submit=Submit

Adres ten jest przesyłany przez przeglądarkę jako nowe żądanie. Skrypt PHP, do które-
go zostało przesłane powyższe żądanie, pobiera z adresu wartości GET i używa ich do
stworzenia strony. W naszym przypadku wkleimy wybraną wartość do tekstu.
<HTML>
<HEAD>
<TITLE>Przykład GET część 2</TITLE>
<STYLE TYPE="text/css">
<! --
BODY (font-size: 24pt;)
-->
</STYLE>
</HEAD>
<BODY)
<P>Naprzód,
<?php print("$Team"); ?> !
</BODY>
</HTML>

Końcowy wynik przedstawiony jest na rysunku 12.1.

Rysunek 12.1.
Wynik działania
formularza używającego
METHOD=GET

Obsługa formularzy za pomocą metody GET posiada jedną wielką zaletę w porównaniu
z metodą POST. Tworzy ona różne adresy, które użytkownik może zapamiętać. Z wyni-
ku działania formularza bazującego na metodzie POST nie da się zrobić zakładki.

Sposób obsługi formularzy za pomocą metody GET posiada tyle wad, że standard
HTML 4.0 odradza jego stosowanie. Wadami tymi są między innymi:
* GET nie jest odpowiedni do logowania użytkownika, ponieważ nazwa użyt-
kownika i hasło są widoczne na ekranie i mogą być zapisane w pamięci odwie-
dzonych stron w przeglądarce.
228_________________________________________Część l « Podstawy PHP

* Wysłane przez formularz dane zapamiętywane są w logach serwera WWW.


•* Ponieważ GET przypisuje dane do zmiennej środowiskowej serwera, jej dłu-
gość jest ograniczona. Próba przesłania np. 300 słów w adresie URL nie jest
najlepszym pomysłem.
"<3
r3^^Ł Oryginalna specyfikacja HTML określa maksymalną długość żądań na
|<>p-=Ęfr 244 znaki. Mimo że ograniczenie to zostało później zarzucone, używa-
••^ nie dłuższych ciągów nie jest dobrym pomysłem.

Ponieważ można zapamiętywać adresy utworzone przez metodę GET, pojawiło się wiele
głosów za zachowaniem tej metody przez W3. Tak się stało i mimo że jest to metoda
najczęściej używana do obsługi formularzy, jest zalecana tylko dla powtarzalnych za-
stosowań, czyli takich, które nie powoduj ą stałego efektu ubocznego. Najbardziej odpo-
wiednim zastosowaniem GET jest okno przeszukiwania. Dla innych formularzy lepiej
użyć metody POST.

Inne zastosowania adresów URL


w stylu GET
Mimo że w chwili obecnej obsługiwanie formularzy przez metodę GET nie jest zaleca-
ne, adresy URL w tym stylu są bardzo użyteczne do obsługi nawigacji po witrynie.
Szczególnie użyteczne jest użycie ich w witrynach generowanych dynamicznie, które są
często tworzone przy użyciu PHP, ponieważ adresy zawierające dołączone zmienne
świetnie współpracują z systemem szablonów zależnych od kontekstu.

Załóżmy, że jesteś właścicielem witryny traktującej o zwierzętach hodowanych dla wełny.


Po długiej i ciężkiej pracy nad stroną informacyjną i graficzną masz następujące strony:
alpaca.html
guanaco.html
llama.html
vicuna.html

Lecz gdy witryna zacznie się rozrastać, płaska struktura plików może wymagać dużo
pracy przy administracji, szczególnie gdy proste poprawki trzeba będzie wykonać na
każdej stronie. Jeżeli struktura stron jest podobna, możesz zastanowić się nad zastoso-
waniem systemu szablonów razem z PHP.

Można zastosować pojedynczy szablon oraz oddzielne pliki tekstowe dla każdego zwie-
rzęcia (zawierające dane, fotografie itp.):
fleecee.php
alpaca.inc
guanaco.inc
llama.inc
vicuna.inc
Rozdział 12. » Przekazywanie danych pomiędzy stronami______________________229

Można również użyć większej liczby wyspecjalizowanych szablonów:


goat.php
cashmere.inc
insect.php
silkworm.inc
llamoid.php
alpaca.inc
rabbit.php
angora.inc
sheep.php
merino.inc

W obu przypadkach plik szablonu będzie wyglądał podobnie (nie zamieściliśmy wszyst-
kich niezbędnych plików, więc przykład ten nie będzie działał):
<HTML>
<HEAD>
<TITLE>Zwierzęta hodowane dla wełny</TITLE>
<STYLE TYPE="text/css">
< ! --
BODY (font: verdana; font-size: 14pt)

</STYLE>
</HEAD>
<BODY>
<TABLE BORDER=0 CELLPADDING=0 WIDTH=100%>
<TR>
<!- Panel nawigacji z adresami w stylu GET. —>
<TD BGCOLOR="#428284" AL1GN=CENTER VALIGN=TOP WIDTH=25%>
<P>
<A HREF="f leecee .php?Name=alpaca"XB>Alpaca</BX/A>
<BR>
<A HREF=" f leecee. php?Name=guanaco"XB>Guanaco</BX/A>
<BR>
<A HREF="fleecee,php?Name=llama"><B>Llama</B></A>
<BR>
<A HREF="f leecee.php?Name=vicuna"XB>Vicu&tł241a</BX/A>
<BRXBR>
</TD>
<TD BGCOLOR«"ltFFFFFF" ALIGN=LEFT VALIGN=TOP W I D T H = 7 5 % >
<? i n c l u d e ( " $ N a m e . i n c " ) ; ?>
</TDX/TRX/TABLE>
</BODY>
</HTML>

Łącza na panelu nawigacji są obsługiwane przez przeglądarkę tak samo jak wynik
działania metody GET.

Jednak nawet w przypadku takiego rozwiązania masz jeszcze wiele ręcznej pracy: nale-
ży się upewnić, że każdy włączany plik jest prawidłowo sformatowany, dodanie nowe-
go pliku do witryny wymaga dodania nowego łącza i innych tego typu prac. Zgodnie
z zasadą oddzielania formy od treści możesz przejść na wyższy poziom abstrakcji przy
użyciu bazy danych. W tym przypadku adres typu:
fleecee.php?animalID=2

powinien spowodować sięgnięcie przez szablon do bazy danych (użycie zmiennej nu-
merycznej zamiast słowa przyspiesza interakcję z bazą danych). Taki system powinien
automatycznie umieszczać na panelu na podstawie zawartości bazy danych. Dzięki te-
mu możesz tworzyć kolejne strony WWW bez interwencji.
230_________________________________________Część l » Podstawy PHP

Argumenty POST
POST jest zalecaną metodą obsługi formularzy, szczególnie dla niepowtarzalnych zasto-
sowań (takich, które skutkują trwałymi efektami działania), takich jak dodawanie da-
nych do bazy. Zbiór danych formularza jest zawarty w ciele formularza w czasie jego
wysyłania do agenta przetwarzania (w tym przypadku PHP). Nie ma żadnych widocz-
nych zmian w adresie URL w zależności od przesyłanych danych.

Metoda POST posiada następujące zalety:


*• Jest dużo bezpieczniejsza od GET, ponieważ wprowadzone przez użytkownika
dane nie są widoczne w adresie URL, logach serwera oraz na ekranie (jeżeli są
prawidłowo obsłużone).
+ Maksymalny rozmiar przesyłanych danych jest dużo większy (kilka kilobajtów
zamiast kilku setek znaków).

Metoda ta posiada również wady:


4 Nie można utworzyć zakładki z wyniku działania.
* Metoda ta może być niezgodna z niektórymi ustawieniami firewalla, jeżeli
z powodów bezpieczeństwa usuwa on dane z formularza.

W książce tej konsekwentnie używamy POST do obsługi formularzy umieszczających


dane w systemie. GET używamy do nawigacji po witrynie oraz do obsługi przeszukiwa-
nia, czyli zastosowań wymagających odczytania danych i ich wyświetlenia.

Wiele, jeżeli nie większość zastosowań POST powoduje zainicjowanie połączenia w usłu-
gowej warstwie systemu. Przykład pokazany na wydruku 12.1 jednak tego nie wyko-
nuje (ponieważ skupiamy się na metodzie POST, usunęliśmy sprawdzanie poprawności
danych, więc formularz ten wymaga jeszcze pracy).

Wydruk 12.1. Arkusz oszczędności emerytalnych___________________________________


<HTML>
<HEAD>
<TITLE>Przykład POST, część 1</TITLE>
<STYLE TYPE="text/css">
<! —
BODY (font-size: 14pt)
.heading {font-size: 18pt; color: red}
-->
</STYLE>
</HEAD>
<?php
/* To sprawdzenie pozwala na stwierdzenie, czy formularz
jest wyświetlony po raz pierwszy (w tym przypadku wyświetla tylko
domyślny procent roczny), czy już zostały wysłane wprowadzone wartości
*/
if ( HsSet ($stage) )
SAnnGain = 7;
else
(
$Years = SRetireAge - $CurrentAge;
$YearCount = 0;
Rozdział 12. » Przekazywanie danych pomiędzy stronami____________________231

STotal = SContrib;
while(SYearCount <= SYears)
(
STotal = round(STotal * (1.0 + $AnnGain/100) + SContrib);
$YearCount = $YearCount + l;
(
)
?>
<BODY>
<DIV ALIGN=CENTER ID=Divl class=heading>
Kalkulator emerytalny
</DIV>
<P class=blurb>Wypęłnij wszystkie pola (Poza "Koszyk") i sprawdź,
ile pieniędzy zaoszczędzisz na emeryturę dla różnych przypadków!
Możesz zmieniać wartości i ponownie przeliczać dane tak często,
jak chcesz. Musisz wypełnić dwa pola wieku. Pole "Procent roczny"
posiada wartość domyślna, korygowaną o inflację
(7% = wzrost 8% minus 1% inflacja),
którą możesz zmieniać w zależności od przypadku optymistycznego i
pesymistycznego.</P>
<FORM ACTION="<?php print("$PHP_SELF"); ?>" METHOD="POST">
<P>Twój wiek: <INPUT TYPE="text" SIZE=5 NAME="CurrentAge"
VALUE="<?php print("$CurrentAge"); ?>">
<P>Planowany wiek przejścia na emeryturę: <INPUT TYPE="text"
SIZE=6 NAME="RetireAge" VALUE="<?php print("$RetireAge"); ?>">
<P>Roczna składka: <INPUT TYPE="text" SIZE=15
NAME="Contrib" VALUE="<?php print("$Contrib"); ?>">
<P>Procent roczny: <INPUT TYPE="text" SIZE=5 NAME="AnnGain"
VALUE="<?php print("$AnnGain"); ?>"> %
<BRXBR>
<PXB>Koszyk</B>: <?php print ( "STotal"); ?>
<PXINPUT TYPE="hidden" NAME="stage" VALUE=1>
<PXINPUT TYPE="submit">
</FORM>
</BODY>
</HTML>

Na rysunku 12.2 przedstawiony jest formularz z wydruku 12.1.

Rysunek 12.2.
Formularz używający
metody POST
232_________________________________________Część l » Podstawy PHP

Zarządzanie zmiennymi w PHP


PHP tak efektywnie przenosi dane, ponieważ projektanci podjęli bardzo wygodną, lecz
(w teorii) ryzykowną decyzję. PHP automatycznie i niewidocznie dla użytkownika przy-
pisuje zmienne na nowej stronie, gdy wyślesz je za pomocą GET lub POST. Większość
z jego konkurentów wymaga jawnego wykonania tych przypisań na każdej stronie. Je-
żeli o tym zapomnisz lub popełnisz błąd, dane nie będą dostępne dla agenta przetwarza-
nia. PHP jest szybszy, prostszy i w większości przypadków odporny na pomyłki.

Najprostszą metodą zilustrowania tej różnicy jest pokazanie różnych metod przetwarza-
nia danych z tego samego formularza. Formularz ten wygląda następująco:
<HTML>
<HEAD>
<TITLE>Formularz preferencji słodyczy</TITLE>
</HEAD>
<BODY>
<FORM ACTION="candy.php" METHOD="POST">
Jakie słodycze lubisz najbardziej?<BR>
<INPUT TYPE="radio" NAME="Candy" VALUE="orzeszki ziemne">Orzeszki ziemne<BR>
<INPUT TYPE="radio" NAME="Candy" VALUE="snickers">Snickers<BR>
<INPUT TYPE="radio" NAME="Candy" VALUE="krówki">Krówki<BR>
<INPUT TYPE="submit">
</FORM>
</BODY>
</HTML>

Skrypt PHP obshigujący formularz:


<HTML>
<HEAD>
<TITLE>Odpowiedż na wybór preferencji</TITLE>
</HEAD>
<BODY>
Hmmm
<?php
print("SCandy! ");
if ($Candy -~ "orzeszki ziemne")
print("Istniej e kiika świetnych rodzajów lodów zawierających małe
lub połamane SCandy.");
else
(
print("Myślę, że jeszcze nie istnieją lody zawierające $Candy, ");
if(SCandy == "snickers")
print("ale czy próbowałeś loda SCandy ?");
elseif($Candy == "krówki")
print("ale bardzo potrzebne są lody zawierające SCandy.");
(
?>
</BODY>
</HTML>

Skrypt ASP realizujący te same funkcje (aby go użyć zmień argument ACTION formula-
rza na candy .asp):
<HTML>
<HEAD>
<TITLE>Odpowiedź na wybór preferencji</TITLE>
</HEAD>
<BODY>
Hmmm
<% Candy = Request.Form("Candy") %>
Rozdział 12. » Przekazywanie danych pomiędzy stronami______________________233

<%= Response.Write (Candy)%>


<% If Candy = "orzeszki ziemne" Then %>
Istnieje kilka świetnych rodzajów lodów zawierających małe lub połamane
<% Response.Write(Candy) %>.
<% Else %>
Myślę, że jeszcze nie istnieją lody zawierające <%
Response.Write(Candy) %>,
<% End If %>
<% If Candy = "snickers" Then %>
ale czy próbowałeś loda <% Response.Write(Candy) %> '?
<% Else If Candy = "krówki" Then %>
ale bardzo potrzebne są lody zawierające <%
Response.Write(Candy) %>.
<% End If %>
<% End If %>
</BODY>
</HTML>

Wynik jest za każdym razem taki sam jak na rysunku 12.3.

Rysunek 12.3.
Wynik użycia
PHP lub ASP

Używanie GET i POST


Czy wiesz, że PHP może używać zarówno GET, jak i POST jednocześnie na
jednej stronie? Możesz rzucić się w wir dynamicznego tworzenia formularzy!
Szybko pojawia się pytanie: co się stanie, jeżeli (rozmyślnie lub nie) użyję tej
samej nazwy zmiennej zarówno w tablicach GET, jak i POST. PHP przechowuje
zmienne GET, POST oraz COOKIE w tablicach o nazwach $HTTP_GET_VARS,
$HTTP_POST_VARS oraz $HTTP_COOKIE_VARS, jak również w tablicy $GLOBALS.
Jeśli wystąpi konflikt, rozwiązywany jest przez nadpisywanie wartości zmien-
nych w kolejności ustalonej przez opcję gpc_order z pliku php.ini (należy
ustawić również opcję track_vars). Późniejszy wygrywa nad wcześniejszym,
więc jeżeli użyjesz ustawienia domyślnego GPC, cookie wygrają z POST, a te
z GET. Możesz zarządzać kolejnością nadpisywania zmiennych, zmieniając
kolejność tych trzech liter w odpowiednim wierszu pliku php.ini.
234_________________________________________Część l » Podstawy PHP

Ponieważ PHP posiada zdolność automatycznego przypisywania zmiennych, skrypt


PHP pisze się szybciej. W skrypcie PHP po prostu używamy zmiennej bez konieczności
pobierania jej wartości lub ponownej definicji. Przykład, jaki podaliśmy, jest trywialny,
ponieważ przekazywana jest tylko jedna zmienna. Jednak przy bardziej złożonych for-
mularzach z wieloma zmiennymi lub w przypadku serii formularzy współdzielących
wiele zmiennych PHP pozwala zaoszczędzić wiele czasu.

W skrypcie ASP należy odczytać każdą zmienną przysłaną za pomocą metody POST,
w PHP nie jest to konieczne. W przypadku ASP należy użyć Request. Form w przy-
padku metody POST oraz Request .QueryString w przypadku metody GET. Jeżeli
zmienisz metodę przesyłania danych, musisz wszędzie zmienić nazwę kolekcji, co mo-
że być źródłem błędów.

Automatyczne przypisywanie zmiennych w PHP również może być źródłem konfliktów


nazw zmiennych. PHP po prostu zamieni starą wartość zmiennej na nową. Możesz
sterować kolejnością zapisywania zmiennych za pomocą opcji GPC z pliku php.ini.
Najlepszą metodą jest jednak unikanie konfliktów i nadawanie dobrych, zróżnicowa-
nych nazw zmiennych. Większość użytkowników PHP uważa, że użyteczność funkcji
automatycznego przypisywania zmiennych przewyższa zagrożenie potencjalnymi kon-
fliktami nazw zmiennych.

Podsumowanie
Protokół HTTP jest protokołem bezstanowym. Oznacza to, że sam HTML nie potrafi
wymieniać danych pomiędzy stronami witryny WWW. Może zostać użyty do przeka-
zywania wartości, ale zewnętrzny program obsługi formularza musi wysilić się, aby
przekazać wartość. PHP jest prawdopodobnie najbardziej naturalnym programem ob-
sługi formularza.

Dane można przekazywać za pomocą trzech podstawowych metod: GET, POST oraz co-
okie (którymi zajmiemy się w rozdziale 26.). Metoda GET jest używana do tworzenia
złożonych adresów URL, stosowanych wraz z szablonami w dynamicznych witrynach.
Nie jest to jednak metoda zalecana do obsługi formularzy, w przeciwieństwie do POST.
Rozdział 13.
Funkcje
systemu operacyjnego
i dostępu do plików
W tym rozdziale:
* Funkcje dostępu do plików
* Funkcje operujące na katalogach
* Funkcje sieciowe
* Funkcje daty i czasu
* Funkcje konwersji kalendarza

W rozdziale tym opiszemy wiele funkcji systemowych dostępnych w PHP. Wiele z nich
pozwala na dostęp do funkcji systemowych protokołu HTTP. Najbardziej użyteczne są
funkcje do odczytu i zapisu plików oraz funkcje zwracające datę i czas.

Wykonanie wielu z funkcji pokazanych w tym rozdziale może mieć duży


wpływ na bezpieczeństwo systemu. Używając ich, musisz pomyśleć
o konsekwencjach. Nie chcemy cię przestraszyć. Po prostu nic, co po-
zwala zmienić system poprzez HTTP, nie powinno być lekceważone.

Niektóre z tych funkcji działają w systemie Unix. Windows nie zawiera


wielu narzędzi dostępnych w Uniksie. Jeżeli masz problem z urucho-
mieniem jakiejś funkcji na platformie Windows, upewnij się, że jest
ona dostępna.
236_________________________________________Część l » Podstawy PHP

Funkcje czytania i zapisywania plików


Jest to bardzo użyteczny zestaw funkcji, a szczególnie w przypadku zbiorów danych,
które są zbyt małe lub rozproszone, aby zapisywać je w bazie danych. Czytanie z pliku
jest bezpieczne, jeżeli nie umożliwiasz odczytania hasła. Zapis do pliku może być nieco
niebezpieczny.

Typowy przebieg operacji na pliku może przebiegać następująco:


1. Otwarcie pliku do czytania lub zapisu.
2. Odczyt pliku.
3. Zamknięcie pliku (może być wykonane później).
4. Przeprowadzenie operacji na pliku.
5. Zapis wyniku do pliku.

Każdy z tych etapów jest realizowany przez osobną funkcję PHP.

Poniższy przykład ilustruje niektóre subtelności składni:


$fd = fopen($filename, "r+") or
die("Nie można otworzyć pliku $filename");
$fstring = fread(Sfd, filesize($filename));
Sfout = fwrite(Sfd, $fstring);
fclose(Sfd);

Wynikiem wykonania tego kodu jest zdublowanie zawartości pliku. Mówiąc inaczej,
końcowym wynikiem działania będzie plik z zapisaną dwa razy oryginalną zawartością.
Funkcja ta nie nadpisuje zawartości pliku.

Otwarcie pliku
Wynik działania funkcji f open ( ) należy bezwzględnie przypisać do zmiennej (trady-
cyjnie $fd lub $fp, pochodzących od deskryptora lub wskaźnika pliku. Lepiej jednak
nadawać bardziej opisowe nazwy zmiennych). Jeżeli funkcja zadziała, PHP przypisze do
zmiennej wartość całkowitą, która jest potrzebna do dalszych operacji na tym pliku, takich
jak f read lub fwrite. Jeżeli funkcja się nie powiedzie, zwracana jest wartość FALSE.

System udostępnia tylko określoną liczbę deskryptorów pliku, co po-


woduje, że pliki należy zamykać tak szybko, jak tylko możliwe. Jeżeli
spodziewasz się dużego zapotrzebowania i masz dostęp do ustawień
systemowych, możesz powiększyć liczbę dostępnych plików.

Pliki można otwierać w sześciu trybach (podobnie do poziomów dostępu). Jeżeli spró-
bujesz wykonać operację niedozwoloną w danym trybie, nie zostanie ona wykonana:
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików________________237

* Tylko odczyt (r).


* Odczyt i zapis do istniejącego pliku (r+): zapis będzie realizowany na po-
czątku pliku.
* Tylko zapis (w): jeżeli plik nie istnieje, jest tworzony, zawartość istniejącego
pliku jest usuwana.
* Zapis i odczyt, nawet gdy plik nie istnieje (w+): jeżeli plik nie istnieje, jest
tworzony, zawartość istniejącego pliku jest usuwana.
* Tylko zapis na koniec istniejącego lub nieistniejącego pliku (a).
+ Odczyt i zapis na końcu pliku istniejącego lub nieistniejącego pliku (a+).

Przed użyciem trybów w lub w+ należy się upewnić, że poprzednia zawartość pliku zo-
stała zapisana. W przypadku użycia innych trybów możliwość utraty danych jest dużo
mniejsza.

Niektórzy użytkownicy mieli problemy z użyciem trybów +. Wiele z tych


problemów wynikało z niezrozumienia różnic pomiędzy trybami. Jeżeli
nie jesteś czegoś pewien, spróbuj otworzyć plik osobno w trybie od-
czytu i zapisu. Przeczytaj również fragment o zapisie plików.

Odmiany otwierania plików


Istnieją cztery podstawowe typy połączeń, za pomocą których można otwierać pliki:
HTTP, FTP, IO oraz system plików.

HTTP fopen

Funkcja fopen próbuje otworzyć połączenie HTTP 1.0 do pliku, który może zostać
przesłany przez serwer WWW (pliki typu HTML, PHP, ASP itd.). PHP „oszukuje"
serwer WWW tak, aby wydawało mu się, że żądanie dostępu do pliku zostało przesłane
przez przeglądarkę, a nie przez funkcję otwarcia pliku.

Powinieneś używać znaków ukośnika zarówno w systemach Unix, jak i Windows:


$fd = fopen("http://www.somedomain.org/openfile.html/", "r");

Musisz pamiętać o końcowym ukośniku, ponieważ funkcja ta nie obsługuje przekierowań.

Pamiętaj jednak, że nie musisz koniecznie używać HTTP do dostępu do plików HTML.
Możesz po prostu otworzyć go z systemu plików, traktując jak zwykły plik tekstowy.
Pokazana powyżej metoda jest szczególnie przydatna do zdalnych serwerów WWW.
Efekt działania jest podobny do obejrzenia strony HTML i zapisania jej źródła.

FTP fopen
Ten rodzaj funkcji fopen próbuje zestawić połączenie FTP do zdalnego serwera, udając
klienta FTP. Jest to najbardziej sprytna z podanych czterech wersji, ponieważ musisz
podać nazwę użytkownika i hasło oprócz nazwy komputera i ścieżki do pliku.
238_________________________________________Część l » Podstawy PHP

$fd = fopen{"ftp://username:password@somedomain.org/openfile.txt", "r");

Aby metoda ta prawidłowo działała, serwer FTP musi obsługiwać tryb pasywny. Dzięki
FTP pliki mogą być otwierane w trybach tylko do odczytu lub tylko do zapisu.

Fopen dla standardowego wejścia lub wyjścia


Operacje na standardowym wejściu lub wyjściu są określane poprzez php://stdin,
php: / / s t d o u t lub php: / / s t d e r r (w zależności od strumienia). Takiej operacji uży-
wa się tylko, gdy PHP jest używany w wierszu komend jako systemowy język skrypto-
wy w rodzaju języka Perl, ponieważ standardowe wejście lub wyjście jest powiązane
z oknem terminala. Zastosowanie jest na tyle rzadkie, że nie znaleźliśmy ani jednego
przykładu zastosowania.

Fopen dla systemu plików


Zwykłym sposobem użycia f open jest użycie systemu plików. Jeżeli nie wskazałeś ina-
czej, PHP próbuje otworzyć plik korzystając z systemu plików.

W systemie Windows możesz używać formatu ścieżki, ale musisz pamiętać o stosowa-
niu podwójnych znaków \:
$fp = fopen("c:\\php\\phpdocs\\navbar.inc", "r");

Pamiętaj, że pliki i katalogi muszą być dostępne do odczytu lub zapi-


su dla użytkownika zalogowanego do procesu PHP, a nie tylko dla
użytkownika systemowego. Jeżeli udostępniasz serwer, musisz pamię-
tać, aby inni użytkownicy PHP mogli czytać lub zapisywać twoje pliki.
Wiemy jednak, że nie jest to całkowicie bezpieczne.

Czytanie pliku
Funkcja f read ( ) wymaga wskaźnika pliku oraz wielkości pliku jako argumentów. Je-
żeli rozmiar pliku nie jest wystarczający do odczytania całego pliku, mogą wystąpić
problemy (chyba że podajesz mniejszą wartość, aby odczytywać plik we fragmentach).
PHP może samodzielne obliczyć wielkość pliku, używając funkcji f ilesize () z na-
zwą pliku (lub zmienną) jako argumentem.
Sfstring = fread($fd, filesize($filename));

Częstym błędem jest wpisywanie f i l e s i z e ( $ f d ) zamiast filesize ( $ f i l e n a m e ) .

Jest to bardzo użyteczna funkcja, ponieważ pozwala zamienić plik na ciąg, nad którym
można pracować za pomocą wielu funkcji działających na ciągach. Każdy ciąg można
zamienić na tablicę, używając funkcji f i l e () lub explode (), co daje nam dostęp do
wielkiego arsenału funkcji manipulujących tablicami. PHP zawiera więcej funkcji tną-
cych i szatkujących niż cały zestaw narzędzi chirurgicznych.

Jeżeli chcesz wyświetlić plik na standardowe wyjście (dla większości instalacji PHP jest
to przeglądarka), możesz użyć funkcji readf ile ( ) . Funkcja ta ma wbudowane otwarcie
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików_______________239

pliku, więc nie musisz używać do tego osobnej funkcji. Jeżeli chcesz czytać i wykony-
wać operacje na kolejnych wierszach pliku, możesz używać f gets ( ) zamiast f read ( ) .
$fd = fopen{"plikprzykladowy.inc", "r");
while (Ifeof(Sfd))
{
Ślinę = fgets($fd, 4096);
if (strcmp(Sline, $targetline) — 0)
echo "Ciąg został znaleziony!";
}
fclose ($fd);

Jeżeli chcesz odczytać plik jako tablicę, możesz użyć funkcji file (}. Tworzy ona ta-
blicę, w której każdy element jest wierszem oryginalnego pliku, wraz ze znakiem koń-
czącym wiersz. Funkcja f i l e d nie wymaga osobnego otwarcia i zamknięcia pliku.
Poniższa linia używająca f i l e (}:
$line_array = file("plik.inc");

jest odpowiednikiem:
Sfd = fopen($filename, "r+") or die("Nie mogę otworzyć pliku Sfilename");
Sfstring = fread($fd, fiłesize($filename));
$line_array = explode( "\n", Sfstring);

Jeżeli zamierzasz odczytać plik znak po znaku, możesz użyć funkcji fgetc ( ) . Zwraca
znak ze wskaźnika pliku aż do końca pliku.

Zapis do pliku
Jeżeli prawidłowo otwarłeś plik w odpowiednim trybie, zapis danych do pliku jest już
bardzo prosty. Funkcja f write ( ) pobiera wskaźnik do pliku jako argument, zapisywa-
ny ciąg znaków oraz opcjonalnie długość w bajtach. Parametr ten nie powinien być
używany oprócz niektórych specyficznych sytuacji. Funkcja zwraca liczbę zapisanych
znaków.
$fout = fwrite(Sfp, Sstring);
echo "Zapisałem do pliku Sfout znaków";

Funkcja f put s ( ) jest identyczna z fwrite ( ) . Jedynie nazwa fputs ( ) jest w stylu ję-
zyka C.

Należy zapamiętać, że zapis do pliku w trybach w lub w+ powoduje całkowite i nieod-


wracalne usunięcie całej dotychczasowej zawartości pliku. Tryby te są przeznaczone je-
dynie do całkowitego nadpisywania zawartości. Jeżeli chcesz dopisać dane na początku
lub końcu istniejącego pliku, użyj trybów r+ lub a+.

Najczęściej spotykanym błędem użycia trybów zapisu w PHP jest sposób realizowania
edytowania pliku tekstowego w przeglądarce (przy użyciu formularza HTML). Jeżeli
chcesz otworzyć plik, odczytać jego zawartość, następnie zapisać zmienioną wersję tego
pliku, nie możesz korzystać z trybów "w+". Tryby w usuwają zawartość pliku natych-
miast po jego otwarciu; możesz czytać z pliku otwartego w trybie w+ jedynie te dane,
które zostały do niego zapisane po otwarciu. Aby obejść to ograniczenie, należy raz
otworzyć plik do odczytu i raz do zapisu tak, jak zostało to pokazane w przykładzie
($f ilename zawiera nazwę pliku tekstowego).
240_________________________________________Część l » Podstawy PHP

<?php
if (IsSet($submited))
(
$fd = fopen($filename, "w+") or
die ("Nie można otworzyć pliku Sfilename");
Sfout = fwrite($fd, $newstring);
fclose ($fd);
}
Sfd = fopen (Sfilename, "r"} or
die ("Nie można otworzyć pliku Sfilename");
Sinitstring = freadISfd, filesize(Sfilename));
fclose(Sfd);
echo "<HTML>";
echo "<FORM METHOD='POST' ACTION=\"SPHP_SELF\">";
echo "<INPUT TYPE='HIDDEN' NAME=' submitted' VALUE=1>";
echo "<INPUT TYPE='SUBMIT'>";
echo "</FORM>";
echo "</HTML>";
?>

Powtórzmy jeszcze raz, że zapisywanie plików nie jest dobrym pomysłem, chyba że
możesz bardzo dokładnie konfigurować środowisko pracy programu! Dobrze zabezpie-
czony serwer Intranetu może być odpowiedni dla takich działań, ale zapisywanie na
produkcyjnej witrynie niesie ze sobą spore niebezpieczeństwo. Więcej na ten temat
znajduje się w rozdziale 31. „Bezpieczeństwo i kryptografia".

Jeżeli samodzielnie kompilowałeś PHP i znasz C, możesz zablokować możliwość zapi-


su do pliku (a właściwie zablokować możliwość otwarcia pliku w trybie zapisu — efekt
jest identyczny). Jest to dobry pomysł w przypadku, gdy twoja witryna korzysta z bazy
danych.

Aby zablokować zapis do plików, należy odszukać w głównym katalogu PHP plik o na-
zwie fopen-wrappers.c. Po otwarciu tego pliku w edytorze odszukaj definicję funkcji
php__f open_with_path ( ) . Należy dodać w odpowiednim miejscu (na przykład 10 li-
nii od początku funkcji) następujące linie:
// dopuszczam tylko tryb "r"
if (!strcmp(mode, "r"))
return NULL;

Można również zablokować jeszcze kilka opcji, ale pozostawimy w twojej gestii.

Jeżeli zablokujesz możliwość zapisu do pliku, a skrypt spróbuje to


zrobić, otrzymasz błąd analizatora składni „variable passed to file is not
an array or object".

Zamknięcie pliku
Operacja zamknięcia pliku jest bardzo prosta:
fclose($ d)

W przeciwieństwie do fopen ( ) , wynik funkcji fclose ( ) nie musi być przypisywany


do zmiennej. Zamykanie pliku wydaje się być stratą czasu, ale twój system posiada
Rozdział 13. « Funkcje systemu operacyjnego i dostępu do plików_______________241

określoną liczbę dostępnych uchwytów plików. Może więc ich zabraknąć, jeżeli nie bę-
dziesz zamykał plików.

Funkcje systemu plików i katalogów


Większość z tych funkcji będzie znana użytkownikom Uniksa, ponieważ dokładnie po-
wielają one polecenia systemowe.

Większość z funkcji opisanych poniżej niesie pewne ryzyko, ponieważ powiela funkcje,
które są i powinny być wykonywane przez lokalny komputer.

Niektóre z tych funkcji działają jedynie, gdy proces PHP jest uruchamiany z prawami
administratora. Ponieważ nie jest to domyślne ustawienie, więc tylko administratorzy,
którzy z rozmysłem ustawiaj ą taką konfigurację, strzelaj ą sobie samobójczą bramkę.

Najpierw wymienimy najczęściej wykorzystywane i bezpieczne funkcje; te rzadziej wy-


korzystywane i mniej bezpieczne znajdują się w tabeli 13.1.

feof
Funkcja feof ( ) pobiera nazwę pliku jako argument i sprawdza, czy wskaźnik pliku
znajduje się na jego końcu.

file_exists
Funkcja f ile_exists () jest prostą funkcją, której będziesz często używał korzysta-
jąc z innych funkcji systemu plików. Sprawdza ona, czy plik o podanej nazwie istnieje
w systemie.
if {!file_exists("test.php"))
<
$fd = fopen("test.php", "w+");
)

Funkcja zwraca wartość TRUE, jeżeli plik istnieje, lub FALSE, jeżeli nie został odnale-
ziony. Wynik testu jest zapisywany w pamięci podręcznej, którą można wyczyścić za
pomocą funkcji clearstatcache ( ) .

filesize
Kolejną prostą lecz użyteczną funkcją jest filesize ( ) , która zwraca i zachowuje
w pamięci podręcznej rozmiar pliku w bajtach. Użyliśmy jej w przykładach korzystają-
cych z funkcji f read ( ) . Nigdy nie przekazuj wielkości pliku za pomocą liczby, jeżeli
możesz zrobić to za pomocą funkcji f i l e s i z e f ) .
242_________________________________________Część l » Podstawy PHP

Tabela 13.1.
Funkcje systemu plików

Funkcja Opis
basename (ścieżka) Zwraca nazwę pliku z pełnej ścieżki do pliku
chgrpiplik, grupa) Zmienia grupę pliku w jedną z grup, do których należy proces PHP.
Nie działa w Windows
chmod(plik, tryb) Zmienia tryb dostępu na podany w systemie ósemkowym. Nie działa
w Windows
chown (plik, użytkownik) Jeżeli funkcja jest wykonywana przez administratora, zmienia
właściciela pliku na podanego użytkownika. Nie działa w Windows
clearstatcache ( ) Czyści pamięć podręczną danych o plikach
copytpJiAr, cel) Kopiuje plik w podane miejsce
delete (plik) Patrz unlink
dirname (ścieżka) Zwraca katalog z podanej pełnej ścieżki do pliku
diskf reespace ( "/katalog") Zwraca ilość dostępnego miejsca w podanym katalogu
f g e t c s v f f p , długość, Czyta i przetwarza linię z pliku w formacie CSV
ogranicznik)
f g e t s s ( f p , długość Czyta z pliku wiersz (zakończony znakiem nowego wiersza) i usuwa
[, dostępne znaczniki}) znaczniki HTML i PHP oprócz tych, które zostały dopuszczone
w wywołaniu funkcji
f i l e a t i m e (plik) Zwraca (i zapamiętuje w pamięci podręcznej) datę i czas ostatniego
dostępu do pliku
f i l e c t i m e (plik) Zwraca (i zapamiętuje w pamięci podręcznej) datę i czas ostatniej
zmiany i-node
f ilegroup (plik) Zwraca (i zachowuje w pamięci podręcznej) identyfikator grupy pliku.
Nazwa grupy może być odczytana za pomocaposix getgrid ( )
f ileinode (plik) Zwraca (i zachowuje w pamięci podręcznej) i-node pliku
f ilemtime (plik) Zwraca (i zachowuje w pamięci podręcznej) datę i czas ostatniej
modyfikacji pliku
fileowner (plik) Zwraca (i zachowuje w pamięci podręcznej) identyfikator właściciela
pliku. Nazwa właściciela może zostać odczytana za pomocą
posix g e t p w u i d l )
f ileperms (plik) Zwraca (i zachowuje w pamięci podręcznej) poziom dostępu do pliku
f iletype (plik) Zwraca (i zachowuje w pamięci podręcznej) jeden z typów pliku: fifo,
char, dir, block, link, file, unknown
f lock (plik, operacja) Funkcja blokowania pliku. Wartość parametru musi wynosić:
1 (współdzielony), 2 (na wyłączność), 3 (zwolnienie blokady) lub
+4 (bez blokady w czasie blokowania)
fpassthru ( f p) Przesyła na standardowe wyjście wszystkie dane z pliku określonego
przez deskryptor fp
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików________________243

Tabela 13.1.
Funkcje systemu plików (ciąg dalszy)

Funkcja Opis
fseekffp, przesuniecie, Przesuwa wskaźnik pliku o liczbę bajtów równą przesunięciu,
skąd) rozpoczynając od pozycji określonej przez parametr „skąd"
f t e l l (fp) Zwraca pozycję wskaźnika pliku
set file b u f f e r ( f p Ustawia wielkość bufora zapisu do pliku. Domyślna wartość to 8 KB
[, rozmiar bufora]
is dir (katalog) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli
istnieje podany katalog
is executable (plik) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli plik
jest plikiem wykonywalnym
is_file (plik) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli plik
jest zwykłym plikiem
is l i n k t p l i t ) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli plik
jest dowiązaniem
is readable (plik) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli plik
może być odczytany przez PHP
is w r i t a b l e (plik/katalog) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli PHP
może zapisywać do podanego pliku lub katalogu
l i n k (cel, plik) Tworzy dowiązanie. Nie działa w Windows
linkinfo (ścieżka) Potwierdza istnienie dowiązania. Nie działa w Windows
mkdir (ścieżka, tryb] Tworzy podkatalog w katalogu określonym przez ścieżkę Prawa
dostępu są określone przez parametr „tryb" w systemie ósemkowym
pclose (fp) Zamyka deskryptor pliku procesu otwarty przez popen ( )
popen ( komenda , tryb) Otwiera plik procesu
readlink ( d o w i ą z a n i e ) Zwraca obiekt zwracany przez dowiązanie. Nie działa w Windows
rename( stara nazwa, Zmienia nazwę pliku
nowa nazwa)
rewind (fp) Przesuwa wskaźnik pliku na początek pliku
rmdir (katalog) Usuwa pusty katalog
stat (plik) Zwraca wybrane informacje o pliku
Istat (plik) Zwraca wybrane informacje o pliku lub dowiązaniu
s y m l i n k ( c e J , łącze) Tworzy łącze symboliczne pomiędzy obiektem docelowym a łączem.
Nie działa w Windows
touch (plik [ , c z a s ] ) Ustawia datę modyfikacji pliku
umask (maska) Zwraca wartość umask i ustawia go na & 0777. Wywołany bez
argumentów zwraca tylko wartość umask
u n l i n k (plik) Usuwa plik
244___________________________________________Część l « Podstawy PHP

Funkcje sieciowe
Funkcje sieciowe to zbiór stosunkowo rzadko używanych funkcji umożliwiających two-
rzenie połączeń sieciowych oraz zwracających informacje. Uruchamia się je z wiersza
poleceń, chyba że tworzysz narzędzia monitorujące pracę urządzeń sieciowych.

Funkcje logu systemowego


Funkcje logu systemowego pozwalają na otwarcie go dla potrzeb programu, wygenero-
wanie komunikatu oraz zamknięcie:
* o p e n l o g f [ i d e n t ] , opcja, facil ty) jest opcjonalna, jeśli używa się jej
wraz z funkcją syslog ( ) . Wartość ident jest generowana automatycznie;
^ s y s l o g ( priorytet, komunikat);

•* closelog ( ) jest opcjonalna, jeżeli używa się jej wraz z funkcją syslog ( } .

Funkcje DNS
PHP oferuje kilka funkcji odpytujących DNS; zebraliśmy je w tabeli 13.2.

Tabela 13.2.
Funkcje DNS

Funkcja Opis

checkdnsrr ($/iost, [Styp] ) Sprawdza, czy istnieje zapis w DNS. Domyślnym typem jest MX,
inne typy to: A, ANY, CNAME, NS, SOA
gethostbyaddr ($adresip) Pobiera nazwę komputera o podanym adresie
gethostbyname ( $ n a z w a _ h o s t a ) Pobiera adres komputera o podanej nazwie
get host byname l ($nazwa_hosta) Pobiera listę adresów odpowiadających podanej nazwie komputera
getmxrr ($nazwa_hosta, Sprawdza, czy istnieją wpisy MX dla podanego komputera. Wynik
[tablica hosty_mx] , [ w a g a ] ) zostaje umieszczony w tablicy hosty mx, wypełnia również
informacje o wadze

Funkcje gniazd
Gniazdo jest rodzajem dedykowanego połączenia, umożliwiającego komunikację róż-
nych programów (mogą być uruchomione w różnych komputerach) poprzez przesyłanie
tekstu. Funkcje PHP operujące na gniazdach pozwalają na tworzenie przez skrypt połą-
czenia do serwera, który komunikuje się dzięki gniazdom. Po połączeniu dalsza komu-
nikacja odbywa się przy wykorzystaniu zwykłych funkcji zapisujących i odczytujących
pliki (fputs (), f gets () itd.).
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików_______________245

Standardową funkcją otwierającą gniazdo jest f sockopen ( ) . Funkcja p f sockopen ( )


również otwiera gniazdo, ale połączenie nie jest zrywane po zakończeniu skryptu. Blokowa-
nie połączenia przez gniazdo może być włączane za pomocą funkcji set_socket_block-
ing ( ) . Po uaktywnieniu blokowania funkcje odczytujące z gniazda zatrzymują się do
czasu odczytania danych z gniazda. Wyłączenie blokowania powoduje, że funkcje czy-
tające są natychmiast kończone, jeżeli nie ma danych do odczytania. Funkcje te są ze-
brane w tabeli 13.3.

Tabela 13.3.
Funkcje gniazd

Funkcja Opis

f sockopen ( $host, $port, [numer_blędu] , Otwiera połączenie poprzez gniazdo z podanym


[ciąg_błędu] , [czas oczekiwania]) portem komputera i zwraca wskaźnik pliku, który
może być używane w funkcjach typu fgets()
getservbyname ( $usiuga, ^protokół) Zwraca numer portu używany przez podaną usługę
getservbyport ($port, $protokól) Zwraca usługę używającą podanego portu
pf sockopen ($host, $port, [numer^błędu], Otwiera trwałe połączenie z gniazdem
[ciąg_błędu] , [czas o c z e k i w a n i a ] )
set_socket_blocking ($desJcryptor_ Ustawia tryb blokowania: TRUE zblokowaniem,
gniazda, Stryb) FALSE bez blokowania. Domyślnie — tryb bez
blokowania

Funkcje daty i czasu


Funkcje te stanowią podstawowe narzędzia do budowy własnych funkcji. Możesz użyć
ich do prostego wyświetlenia daty i czasu, do mierzenia czasu wykonania skryptu PHP
lub do uruchamiania funkcji w określonym czasie (na przykład funkcji wyświetlającej
napis „Wesołych Świąt").

Działanie tych funkcji łatwo zrozumieć, jeżeli wiesz, co to jest znacznik czasu z syste-
mu Unix. Funkcje te można podzielić na trzy rodzaje: zwracające datę lub czas, forma-
tujące datę lub czas oraz sprawdzające poprawność daty.

Znacznik czasu Uniksa to liczba sekund od początki epoki Uniksa


(północ l stycznia 1970 roku według czasu Greenwich). Funkcje te
działają również w Windows.

Jeżeli nie znasz daty ani czasu


Najszybszą metodą odczytania czasu jest użycie funkcji time ( ) . Zwraca ona znacznik
czasu Uniksa dla twojej strefy czasowej w postaci np.: „961906652". Jest to najlepszy
246_________________________________________Część l » Podstawy PHP

format, jeżeli chcesz przekazać ten znacznik do innej funkcji lub programu. Przy użyciu
funkcji opisanych w dalszej części rozdziału można przetworzyć tę wartość do postaci
czytelnej dla człowieka.

Aby uzyskać mikrosekundy i sekundy upływające od początku epoki Uniksa, należy


użyć funkcji microtime ( ) . Jest ona bardzo przydatna dla narzędzi mierzących wydaj-
ność. Zwracany wynik ma postać „0.74321900 961906846", w którym pierwsza część
to mikrosekundy, a druga to znacznik czasu Uniksa. Jeżeli chcesz (na przykład) mierzyć
wydajność różnych fragmentów strony WWW, musisz użyć mikrosekund, które mogą
być wycięte w następujący sposób:
<?php
$stamp = microtime();
$chunks = explode{ " ", $stamp );
$microseconds = chunks[O]; ^
echo Smicroseconds;
?>

Podstawową funkcją zwracającą datę jest getdate ($timestamp). Zwraca tablicę


asocjacyjną z następującymi elementami:
sekundy
minuty
godziny
Mday: dzień miesiąca 1-31
Wday: dzień tygodnia 1-7
Mon: miesiąc 1-12
Rok
Yday: dzień roku l - 356
Weekday: dzień tygodnia Sunday-Saturday
Month: January-December

Najczęściej używaną konstrukcją jest getdate (time ( ) ) , lecz można użyć getda-
te ( ) z dowolną wartością $timestamp.

Jeżeli chcesz za jednym razem pobrać czas i sformatować go, należy użyć funkcji da-
tę ( ) zamiast getdate ( ) . Jeżeli nie zostanie podany znacznik czasu, datę ( ) auto-
matycznie użyje bieżącej daty i czasu. Funkcja ta lepiej formatuje wynik (patrz dalsza
część rozdziału). Funkcja strftime ( ) także formatuje bieżący znacznik czasu (rów-
nież ją opiszemy później), jeżeli nie podamy innego.

Jeżeli już odczytałeś datę i czas


Funkcje opisane w tej części służą do precyzyjnego formatowania daty i czasu. Chcesz
np. wyświetlać daty w formacie europejskim (2000.04.20) zamiast amerykańskim
(4/20/2000).
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików________________247

Podstawową metodą formatowania znacznika czasu jest użycie datę ( $ format . . .


Sformatn [, $ times tamp] ). Przekazujesz do funkcji serię kodów określających
twoje wymagania co do formatowania oraz opcjonalnie znacznik czasu. Możesz wybrać
datę z dwucyfrowym zapisem dni, format 12- lub 24-godzinny czasu oraz skróty nazw
miesięcy (wszystkie opcje znajdują się w podręczniku PHP). Analogiczną funkcją jest
gmdate ($format . . . $formatn [ , Stimestamp] ) , która zwraca datę według
czasu Greenwich.

Funkcja strftime ($format .. . Sformatn [Stimestamp]) ma podobne działa-


nie, lecz przeznaczona jest do formatowania czasu, nie daty. Funkcja gmstrftime
($ format ... Sformatn [, $ times tamp] ) zwraca sformatowany czas Greenwich.

Funkcja m k t i m e ( ) konwertuje dowolną datę na znacznik Uniksa. Różni się nieco ko-
lejnością parametrów od komendy Uniksa o tej samej nazwie. Funkcja g m m k t i m e O
robi to samo dla czasu Greenwich.

Na koniec przedstawimy funkcję checkdate (Smiesiąc, Odzień, $rok), która


sprawdza poprawność podanej daty. Za jej pomocą można szybko sprawdzić, czy rok
jest przestępny.

Funkcje konwersji kalendarza


Funkcje konwersji kalendarza są dostępne dla tych, którzy mogą skompilować PHP
z dynamiczną biblioteką obsługi kalendarzy.

Wielu początkujących użytkowników PHP myli się uważając „funkcje


kalendarza" za funkcje daty. Funkcje te po prostu konwertują daty
pomiędzy różnymi (i w większości historycznymi) kalendarzami. Jeżeli
uważasz, że trafiłeś tu omyłkowo, przejdź do części „Funkcje daty
i czasu" we wcześniejszej części tego rozdziału.

Jeżeli zajmujesz się historią Pranej i, będziesz zadowolony, że za pomocą kilku wierszy
kodu możesz konwertować daty z kalendarza rewolucji francuskiej na kalendarz grego-
riański. Można powiedzieć: Bon Thermidor, Citoyens et Citoyennes!

Funkcje te naprawdę maj ą użytkowników — szczególnie w Internecie.

Aby użyć funkcji kalendarza, należy samemu skompilować PHP. Funkcje te znajdują
się w jeszcze niestabilnej bibliotece dynamicznej opisanej w pliku dl/README. W czasie
pisania tej książki funkcje kalendarza nie były dostępne w binarnej wersji dla Windows.

Konwersja pomiędzy kalendarzami jest możliwa, ponieważ wszystkie funkcje kalenda-


rza współdzielą uniwersalne odwołanie, tzw. „numer dnia juliańskiego" Jest to liczba
będąca liczbą dni od południa pierwszego stycznia 4713 roku p.n.e. według kalendarza
juliańskiego. To 14 stycznia w kalendarzu gregoriańskim. Tak zwana „data juliańska"
248_________________________________________Część l » Podstawy PHP

jest liczbą double, reprezentującą liczbę dni i godzin od zerowego dnia juliańskiego.
PHP nie pozwala na taki poziom dokładności. Wspomnieliśmy o tym na wypadek, gdy-
by ktoś szukał tej informacji.

Pamiętaj, że dzień juliański zmieniał się w południe, a nie o północy.

Funkcje konwersji kalendarza zamieniają datę w kalendarzu na numer dnia juliańskie-


go, i na odwrót. Aby skonwertować datę pomiędzy kalendarzami, należy użyć dwóch
funkcji: zamieniającej datę na numer dnia juliańskiego oraz zmieniającej NDJ na datę
w innym kalendarzu. W naszym przykładzie zamieniamy datę kalendarza gregoriań-
skiego na datę kalendarza żydowskiego.
$jd_no = gregoriantojd(8, 11, 1945);
Shebrew = jdtojewish($jd_no);
echo Shebrew;

Wykonanie tego fragmentu zwróci datę 2, 6 [ E l u l ] , 5705.

W chwili obecnej dostępne są kalendarze:


«• Republiki Francuskiej;
** gregoriański;
* żydowski;
* juliański;
* Uniksa.

Każdy z tych kalendarzy posiada parę funkcji j dtoX oraz xto j d.

Istnieją jeszcze dwie inne pary funkcji kalendarza. JDMonthName ( ) i JDDayofWeek ( ) .


Zwracaj ą miesiąc i dzień tygodnia dla dowolnego numeru dnia juliańskiego we wszyst-
kich obsługiwanych kalendarzach. Funkcje easter_date ( ) i easter_days ( ) obli-
czają, kiedy przypada Wielkanoc (katolicka) w dowolnym roku. Easter_date ( ) jest
prostsza, ale może być używana tylko w obrębie dat systemu Unix (1970-2037). Zwraca
ona znacznik czasu północy w Wielkanoc w podanym roku.

Podsumowanie
PHP posiada wiele wbudowanych funkcji, operujących na systemie plików oraz syste-
mowych, które mogą być użyteczne, ale niosą pewne ryzyko. Dla przykładu, istnieje
wiele funkcji powielających narzędzia systemowe Uniksa, takie jak chmod ( ) czy co-
py ( ) . PHP posiada również bardziej zaawansowane funkcje, na przykład odpytujące
DNS. Mimo że lepiej wyłączyć niektóre z nich, mogą być bardzo przydatne, w odpo-
wiednio zaprojektowanym środowisku. Funkcje PHP pozwalające na otwarcie, odczyt
i zapis plików są bardzo silnym narzędziem. Większość problemów związanych z tymi
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików_______________249

funkcjami wynika z nieprawidłowego zrozumienia trybów otwarcia plików. PHP po-


zwala również na otwarcie pliku poprzez HTTP, ftp i standardowe wejście i wyjście.

PHP posiada również wiele funkcji określających datę i czas, więc zawsze będziesz
wiedział, która jest godzina.
250 ______________________________________Część l « Podstawy PHP
Rozdział 14.
Styl PHP
W tym rozdziale:
* Jak dostać punkty za styl
* Komentowanie kodu
* Pisanie kodu łatwego do konserwacji
+ Łączenie PHP i HTML
+ Oddzielenie funkcji od układu strony

W tym rozdziale opiszemy główne punkty stylu PHP oraz sposoby polepszenia funk-
cjonalności, łatwości utrzymania i atrakcyjności kodu. Zawartość tego rozdziału ma
pomóc początkującym programistom PHP wybrać styl, w jakim będą pisali programy.
Większość z tych porad ma zastosowanie we wszystkich językach programowania.

Mamy nadzieję pomóc nowym programistom PHP rozszyfrować kod pisany przez in-
nych. Czytanie trzech różnych podręczników, które sanie ze sobą zgodne i brak w nich
wyjaśnienia przyczyn tej niezgodności, może być dla uczącego się pisać skrypty bardzo
denerwujące. Informacje zawarte w tym rozdziale powinny pomóc zidentyfikować
istotne fragmenty kodu, ukryte w stylistycznych udziwnieniach, i lepiej objaśnić anali-
zowany kod.

Zalety prawidłowego stylu


Najważniejszym celem pisania kodu jest oczywiście realizowanie założonych funkcji.
Jeżeli skrypt PHP działa, kto ma czas i ochotę patrzeć, jak on wygląda? Istnieje jednak
olbrzymia różnica pomiędzy skleceniem programu, byle działał, a pisaniem poprawnie
skonstruowanego kodu, który jest zrozumiały dla innych.

Programiści PHP stykają się z takimi samymi zagadnieniami jak inni:


252_________________________________________Część l » Podstawy PHP

4 Czytelność: na pewno rozumiesz to, co właśnie napisałeś, ale czy zrozumie to


następna osoba? A jeżeli tą następną osobą będziesz ty?
* Łatwość w utrzymaniu: co się stanie, jeżeli witryna porad medycznych w koń-
cu zmieni podawanie temperatury ze stopni Fahrenheita na Celsjusza? Niepra-
widłowa odpowiedź to: zmiana 790 wystąpień ciągu 98,6 w kodzie.
* Solidność: witryna działa świetnie, jeżeli otrzymujesz dane wejściowe, których
się spodziewasz. A jak działa dla nieprawidłowych danych?
* Zwięzłość i efektywność: szybki kod jest lepszy niż powolny; mniejszy kod
jest lepszy niż obszerny.

W tym rozdziale przeprowadzimy krótki przegląd niektórych strategii osiągania tych


celów w PHP. Następnie zajmiemy się unikalnymi sposobami organizacji kodu.

Czytelność
Podstawowa cecha skryptu to czytelność. Oko ludzkie lubi wyraźne wzory, logiczną or-
ganizację oraz znaczące powtórzenia. Czytelności sprzyja również umieszczanie naj-
ważniejszego słowa na początku wiersza, a nie ukrywanie go w środku.

Jeżeli projektujesz stronę HTML przy użyciu narzędzia wizualnego, noty za czytelność
nie będą zbyt wysokie. Programy te notorycznie generują źle sformatowany i zoriento-
wany na grafikę kod HTML, wypełniony niewidocznymi rysunkami GIF, absolutnym
skalowaniem i innymi tego typu niedociągnięciami. Wszystkie te błędy znajdują się
w poniższym przykładzie, który został wygenerowany przez powszechnie znany pro-
gram do projektowania. Jego nazwę zachowamy w tajemnicy. Należy dodać, że nie ro-
biliśmy nic, aby pogorszyć wygląd kodu.
<HTMLXHEADXTITLE>Przepis: brzoskwinie w s y r o p i e < / t i t l e x m e t a http-
equi v= "Con ten t -Type "content "text /html;
charset=iso-8859-l"x/HEADxbody bgcolor="#FFFFFF"
t e x t = " # 6 6 6 6 6 6 " link="#CC3300" vlink="#CC3300" alink-"#CC3300">
<table width="401" align="center" border="0"
CELLSPACING="1" C E L L P A D D I N G = " l " >
<tr>
<td width="50"ximg src="spacer .gif"
width="50" height="l"x/tdx/tr>
<td width="300" height="30"
colspan="5"xbxfont face="sans-serif" s i z e = " 2 "
color="#DDAODD">Brzoskwinie w syropie</fontx/bx/td>
<td width="51"ximg src="spacer.gif"
width="51" height="l"x/tdx/tr>
</tr>
<tr>
<td w i d t h = " 4 0 1 " colspan-"7"ximg src="spacer . gif'
width="300" height = "5"x/td>
</tr>
<tr>
<td width="50"ximg src="spacer. gif"
width="50" height="l"x/tdx/tr>
<td width="300" colspan="5"xfont face="sans-
serif" s i z e = " 2 " > < b x i > S k ł a d n i k i < / i x / b > < / f o n t x / t d >
<td width="51"ximg src="spacer . gif"
width="51" height = " l " x / t d x / t r >
Rozdział 14. » Styl PHP__________________________________________253

</tr>
<tr>
<td width="300" colspan="7"xfont face="sans-
s e r i f " size="l"ximg src="line.gif" width="300" h e i g h t = " l "
border="0" align="top"x/fontx/td>
</tr>
<tr>
<td width="50" align="left"xfont
size="l" face="sans-serif">&nbsp;</td>
<td w i d t h = " 1 0 0 " align="left"xfont
size="l" f a c e = " s a n s - s e r i f " > D u ż e b r z o s k w i n i e < / f o n t x / t d >
<td w i d t h = " 5 0 " a l i g n = " l e f t "
colspan="2"xfont size="l" face="sans-
serif " > 6 & n b s p ; < / f o n t x / t d >
<td align="le t" width="100">
<font size="l" face="sans-
serif"XNULL> </font>
</td>
<td width="51 "ximg src"spacer . gif "
width="51" height="l"x/tdx/tr>
</tr>
<tr>
<td width="50" align="left"xfont
size="l" face="sańs-serif">&nbsp;</td>
<td width="100" align="left"xfont
size="l" face="sans-serif">Białe w i n o < / f o n t x / t d >
<td width="50" a l i g n = " l e f t "
colspan="2"xfont size="l" f a c e = ' " s a n s - s e r i f " > 2 & n b s p < / f o n t x / t d >
<td align="left" width="100">
<font size="l" face="sans-
serif">szklanki</font>
</td>
<td width="51"ximg
src="spacer.gif" width="51" h e i g h t = " l " x / t d x / t r >
</tr>
<tr>
<td width="50" a l i g n = " l e f t " x f o n t
size="l" f a c e = " s a n s - s e r i f " > & n b s p ; < / t d >
<td width="100"
align="left"xfont size="l" f a c e = " s a n s - s e r i f " > C u k i e r
granulowany</f ontx/td>
<td width="50" align="left"
colspan="2"xfont size="l" face="sans-
serif">lsnbsp;</fontx/td>
<td align-"left" width="100">
<font size="l" face="sans-
serif">szklanka</font>
</td>
<td width="51"ximg src="spacer .gif"
width="51" height="l"x/tdx/tr>
</tr>
<tr>
<td width="50" a l i g n = " l e f t " x f o n t
size="l" f a c e = " s a n s - s e r i f " > s n b s p ; < / t d >
<td width="100"
align="left"xfont size="l" face="sans-serif'>Świeży
imbir</fontx/td>
<td width="50" align="left"
colspan="2"xfont size="l" face="sans-
serif " > 2 s n b s p ; < / f o n t x / t d >
<td align="left" width="100">
<font size="l" face="sans-
serif">plastry&nbsp </font>
</td>
<td width="51"><img src="spacer.gif"
width="51" height="l"x/tdx/tr>
</tr>
</table>
<table border="0" w i d t h = " 3 0 0 " align="center">
<tr height = "30"x/tr>
254_________________________________________Część l » Podstawy PHP

<tr>
<td width="29" align="center">i,nbsp; < / t d >
<td colspan="2"xfont face="sans-serif"
size="2"><b><i>Przygotowanie</i></bx/fontx/td>
</tr>
<tr>
<td colspan="2"xfont face="sans-serif"
size="l"ximg src="line .gif" width="300" height = "l" border="0"
align="top"x/fontx/td>
</tr>
<tr>
<td valign="top"xfont size="l"
face="sans-serif " valign="top">l. Łnbsp; < / f o n t x / t d >
<tdxfont size-"l" face="sans-
s e r i f " > W r z u ć brzoskwinie na 15 sekund do wrzącej wody, ułatwi to
obranie ze skórek. Ostrożnie p r z e k r ó j na pół, usuwając pestkę.
snbsp;</fontx/td>
</tr>
<tr>
<td valign="top"xfont size="l" face="sans-
serif " valign=" top">2 . Snbsp; </f ontx/td>
<tdxfont size = "l" face="sans-
serif">Wrzuć wszystkie składniki oprócz brzoskwiń do dużego
rondla. Gotuj aż do rozpuszczenia cukru. snbsp: </fontx/td>
</tr>
<tr>
<td valign="top"xf ont size="l"
face="sans-serif" valign="top">3 . Snbsp; < / f o n t x / t d >
<tdxfont size="l" face="sans-
serif">Gotuj brzoskwinie w syropie na wolnym ogniu przez 15 minut.
Obracaj co 5 minut. Podawaj cieple, polanę syropem
. Snbsp:</fontx/td>
</tr>
</table>
</table>
</bodyx/html>

Próba dodania PHP do takiego pliku HTML może być źródłem problemów. Jeżeli jed-
nak chcesz to robić pamiętaj, że nie jest to wina PHP i skieruj swoje żale do innego
producenta.

Spróbujemy jednak znaleźć jakąś radę dla tych, którzy nie potrafią uwolnić się od na-
rzędzi graficznych.

Prawdopodobnie najlepsze, co możesz zrobić, to przepuszczenie wygenerowanego kodu


HTML przez program, który uczyni go bardziej czytelnym dla człowieka. Nie trwa to
zbyt długo, a może wyraźnie poprawić sytuację. Jednym z lepszych narzędzi tego typu
jest HTML Tiddy dla Unixa, który można znaleźć pod adresem:
http ://www. w3. org/People/Raggett/tidy/

Program ten naprawia częste błędy w kodzie HTML, takie jak brakujące znaczniki za-
mykające. Poza tym posiada (na razie niewielkie) możliwości współpracy z PHP, jeżeli
używamy standardowych znaczników <?php ... ?>. Można więc naprawić sytuacje,
gdy „tak się śpieszyłem, że zapisałem dokument Microsoft Office jako HTML i doda-
łem kilka fragmentów w PHP". Program ten odczytuje dokumenty Microsoft Office za-
pisane jako HTML, mimo że w chwili pisania książki HTML Tiddy nie był dostępny na
platformie Windows.

Nieco bardziej pracochłonnym, ale dającym precyzyjniejszą kontrolę podejściem jest


użycie analizatora poprawności HTML. Narzędzie to (często jest dostępne w sieci)
Rozdział 14. » Styl PHP__________________________________________255

wylicza wszystkie miejsca w dokumencie, w których kod nie jest zgodny ze standardem
HTML. W przeciwieństwie do HTML Tiddy, nie poprawia kodu, trzeba samemu punkt
po punkcie wprowadzić zmiany.

Kolejną efektywną metodą zwiększenia czytelności kodu jest kupno dobrego pakietu
zawierającego zarówno dobre narzędzie wizualne, jak i dobry edytor tekstowy. Nie jest
to reklama, ale znanym programem spełniającym te warunki jest Macromedia Dream-
weaver. Działa na Macach i pod Windows, posiada obsługę PHP i może współpracować
z wieloma programami obróbki obrazu.

Nawet projektanci PHP, którzy piszą cały kod ręcznie, mogą sobie pomóc, wybierając
dobry edytor tekstowy (mówiliśmy o tym w rozdziale 3.). Po co marnować czas na za-
mykanie znaczników HTML, czy szukanie brakujących nawiasów? Większość dzisiej-
szych edytorów tekstu potrafi automatycznie domykać znaczniki i nawiasy, co pomaga
uniknąć trywialnych błędów.

Na pewno znajdziesz taki program, na odpowiednią platformę, który można dostosować


do potrzeb. Niektórzy uwielbiają wielokolorowe podświetlanie składni, inni uważają to
za zupełnie niepotrzebne. Niektórzy używają do wcięć tabulatora, a inni spacji, niektó-
rzy lubią programy, które potrafią robić wszystko oprócz kawy, inni żądają prostego,
ale posłusznego narzędzia. Możesz skonfigurować prawie każdą kombinację funkcji,
ale musisz poświęcić trochę czasu na ustawienie edytora. Ponieważ edytor dla progra-
misty jest jak koń dla rycerza, czasu nie zmarnujesz.

Upewnij się, że twój edytor jest zgodny z programami twoich współ-


pracowników. Niektóre edytory usuwają wcięcia wykonane za pomocą
tabulacji (dokumenty wyglądają brzydko), używają dziwnych apostro-
fów itp. Twoi koledzy nie będą zachwyceni, jeżeli będą musieli od no-
wa formatować każdą obejrzaną przez ciebie stronę.

Niektórzy nie potrafią zaakceptować faktu, że pamięć w komputerze nie jest bezcenna.
Spędzają mnóstwo czasu na próbach zacieśnienia tekstu programu tak, aby zajmował
możliwie mało miejsca. Praktyka ta jest niestety wspierana przez niektóre książki tech-
niczne oraz wydawców czasopism, którzy żądaj ą od autorów, aby używali różnych spo-
sobów ograniczenia długości wydruków programów (jak w poniższym przykładzie).
<?php if ($UserID Si strlen($Horse)>0) {
if($Horse=="Man O'War") print("Kasztan, białe plamki");
elseif($Horse=="Native Dancer") print("Jasnoszary");
elseif($Horse=="Seattle Slew") print("Czarny");
) else {
print{"Spróbuj jeszcze raz."); }?>

W przykładzie użyto nawiasów klamrowych w stylu Kernighana i Ritchie, wcięcia mają


tylko jedną spację na poziom, brak powtarzalnych, ale znaczących elementów pliku
(nagłówków HTML) i po prostu przestrzeni. Wszystko jest na pozór w porządku, ale
czytelność pozostawia wiele do życzenia. Porównaj ten format z poniższym i wyobraź
sobie oba fragmenty wbudowane w dłuższy i bardziej skomplikowany skrypt, który
musisz szybko zmodyfikować:
256_________________________________________Część l » Podstawy PHP

<HTMLXBODY>
<?php
if (SUserlD && strlen (SHorse) >0)
(
if($Horse=="Man 0'War")
print("Kasztan, biaie plamki");
elseif($Horse=="Native Dancer")
print{"Jasnoszary");
elseif($Horse=="Seattle Siew")
print("Czarny");
)
else
{
print("Spróbuj jeszcze raz.");
(
?>
</BODYX/HTML>

Początkujący programiści powinni zapamiętać, że kod w książkach, a szczególnie


w Internacie (gdzie miejsce naprawdę nic nie kosztuje) nie musi testować ludzkiej zdol-
ności do zrozumienia źle zapisanego tekstu.

Na koniec należy wspomnieć, że staranność również się liczy. Staje się szczególnie
ważna, gdy liczba współpracujących ze sobą programistów wzrasta i coraz ważniejsze
stają się zagadnienia łatwości konserwacji.

Komentarze
Wstawianie komentarzy do kodu jest często porównywane do czyszczenia zębów za
pomocą nitki: ważne dla zdrowia i higieny, ale zbyt często pomijane „tylko tym razem".

Problem tkwi w tym, że nie ma natychmiastowych korzyści ze stosowania komentarzy


— wszystkie ich zalety są dostrzegane po długim czasie. Spróbuj sobie przypomnieć,
kiedy słyszałeś kolegę z sąsiedniego pokoju, zachwycającego się pięknym komentowa-
niem kodu przez innego programistę. Niewiele dynamicznych witryn WWW zatrzy-
muje się na chwilę, aby programiści mogli umieścić końcowe komentarze w tekście
skryptów. Co powinno być skomentowane?
+ Wszystko, co w przyszłości może powodować pytanie: „o czym ja wtedy my-
ślałem?". Zwykle fragmenty zawikłane lub napisane niestarannie.
* Wszystkie fragmenty, które mogą nie być nieprawidłowe za jakiś czas (na
przykład używane magiczne liczby).
* Wszystko, co nieprawidłowo użyte może powodować błędy.

Idealnie byłoby, gdybyś uwzględniał przy komentowaniu następujące elementy:


* datę utworzenia pliku i nazwisko twórcy;
4 datę ostatniej zmiany i nazwisko modyfikującego oraz opis powodów zmiany;
* inne pliki i programy zależne od istnienia tego pliku;
* zakładane przeznaczenie pliku oraz jego części składowych;
4 notatki, które chciałbyś umieścić w pisanej później dokumentacji;
Rozdział 14. * Styl PHP__________________________________________257

+ powody zachowania niektórych nieużywanych fragmentów (alternatywne wer-


sje, kopie archiwalne itp.), warunki ich usunięcia lub inne plany.

Oczywiście to ty decydujesz, które elementy są naprawdę niezbędne. Jeżeli używasz


PHP do obsługi bardzo małej, osobistej witryny, prawdopodobnie komentowanie będzie
zbędne, ale im bardziej zwiększają się objętość i stopień skomplikowania witryny, tym
większa jest potrzeba opisywania pracy. Teoretycznie jest możliwe przekomentowanie
kodu, ale w praktyce niewielu programistów jest w stanie to zrobić.

W rozdziale 5. opisaliśmy kilka dostępnych stylów komentarzy w PHP.


Pamiętaj, że żaden z nich nie będzie widoczny w komputerze klienta.
Jeżeli chcesz umieścić na stronie czytelny dla klienta ukryty tekst,
musisz użyć komentarzy HTML.

Nazwy zmiennych i plików


Niektórzy uważają wymyślanie nazw zmiennych za pisanie poematu epickiego. Praco-
waliśmy kiedyś z osobą, która wydawała się niezdolna do wymyślenia nazwy, ani na-
wet przyzwoitego schematu nazwy. Pliki tworzone przez tę osobę miały nazwy
tworzone przez dodanie kolejnego numeru: filel6.html, filel7.html, filel8.html itd.
Wszystkie nazwy zmiennych na stronie WWW miały nazwy varl, var2 itd. Historia ta
byłaby zabawniejsza, gdyby zdarzyła się komuś innemu.

Ponieważ w PHP używa się o wiele więcej zmiennych niż w HTML, powinieneś opra-
cować wydajny schemat nazywania zmiennych na wszystkie okazje. W dalszej części
rozdziału przedstawimy kilka wskazówek.

Krótkie lub długie


Dłuższe nazwy są lepsze, ponieważ niosą więcej informacji. W razie potrzeby możesz
podzielić długie nazwy za pomocą znaków podkreślenia lub wielkich liter.

Nawet jeżeli większość systemów plików pozwala na używanie drugich nazw, to kata-
log taki oglądany jako ikona nie wygląda dobrze. Użytkownicy GUI mogą być świado-
mie lub nieświadomie uprzedzeni do używania długich nazw. Etykiety ikon są zwykle
krótkie i to powoduje tendencję do używania bardzo zwięzłych nazw plików. Spróbuj
nadać plikowi długą i skomplikowaną nazwę (np.: PrzepisBrzoskwinieWSyropie.php)
i umieść go na pulpicie — nie wygląda to najlepiej.

Większość plików, używanych w systemach graficznych, może mieć


nazwy ze spacjami (np.: Moje Dokumenty.doc). Unix w teorii również
na to pozwala, ale w praktyce nie jest to najlepszy pomysł. Mimo że
PHP stara się działać również z takimi plikami, w niektórych sytu-
acjach może to być niemożliwe.
258_________________________________________Część l » Podstawy PHP

Jedną z zalet stosowania PHP do dynamicznego generowania zawartości stron jest


możliwość skrócenia nazw plików, które są rozszerzane i rozróżniane przez ciągi zapy-
tań, w stylu metody GET. Witryna statyczna musi używać nazw jednoznacznie identyfi-
kujących każdą stronę, np.:
FeatureHitchcockBirds.html
MiniseriesIrvinSpy.html

Witryna dynamiczna może identyfikować te same strony za pomocą:


feature.php?ID=l
miniseries.php?ID=2

W tej sytuacji możesz korzystać z zalet obu rozwiązań: krótkich nazw plików i jedno-
znacznej identyfikacji.

PHP nie ogranicza długości nazw zmiennych, więc możesz tworzyć długie, ale niosące
dużo danych nazwy, jak np. $AdcłressOfCli v entCompanyInSasketchewan. To twoje
skrypty. Musisz jedynie ostrożnie stosować zmienne o długich nazwach jako fragmenty
formularza, obsługiwanego za pomocą metody GET.

Podkreślenia lub wielkie litery


Najczęściej stosuje się dwa sposoby dzielenia długich nazw zmiennych lub nazw pli-
ków w Uniksie. Długa nazwa podzielona podkreśleniami wygląda następująco:
$name_of_favorite_beer

Jeżeli użyjemy wielkich liter, ta sama nazwa wygląda w następujący sposób:


$NameOfFavoriteBeer

Wewnętrzne duże litery powodują, że nazwa posiada „garby", dlatego ten sposób kon-
struowania nazw jest często nazywany wielbłądzim.

Wybór sposobu należy do ciebie. PHP stosuje w predefiniowanych zmiennych podkre-


ślenia ($PHP_SELF). Pamiętaj, że nie możesz używać myślników jako znaków roz-
dzielających i uważać, stosując kropki.

W nazwach plików systemu Unix wielkie i małe litery były rozróżniane


zawsze. Nazwy plików w innych systemach operacyjnych mogą igno-
rować wielkość liter. Jeżeli będziesz przenosił pliki PHP pomiędzy sys-
temami, musisz to robić bardzo uważnie.

Najważniejszą rzeczą, o którą musisz zadbać, jest konsekwencja. Bardzo irytujące są


sytuacje, gdy usiłujesz zorientować się, dlaczego zmienna $My_Number nie została za-
inicjowana, i odkryjesz, że wcześniej przypisywałeś wartość do zmiennej o nazwie
SMyNumber.
Rozdział 14. » Styl PHP_______________________________________259

Powtórne wykorzystanie zmiennych


Istnieją sytuacje, gdy należy rozmyślnie używać tej samej nazwy, zamiast nową nazwę
zmiennej tworzyć za każdym razem. Chcesz, aby zmienna używana do określonego ty-
pu zadania miała zawsze poprawną wartość. Musisz być pewien, że nie będzie proble-
mu z rozróżnieniem, które z dwóch zapytań jest właśnie wykonywane, jeżeli używasz
tej samej nazwy dla obu (np.: $query). PHP zmieni starą wartość na nową i zmienna
będzie zawsze prawidłowa.

Łatwość konserwacji
Weterani programowania twierdzą, że łatwość konserwacji jest najważniejszą z zalet.

Niestety głównym problemem jest to, że łatwość konserwacji jest zwykle w konflikcie
z innymi celami, najczęściej z wydajnością. W czasach Internetu zdarza się, ktoś inny
konserwuje nasz kod, wydaje się więc, że widać zwycięzcę tego konfliktu.

Najważniejsze zasady, o których powinieneś pamiętać, to:


* fragmenty, które będą często zmieniane, muszą być łatwe do odszukania;
** zmiana tych fragmentów nie powinna nieść ze sobą nieprzewidzianych efektów;
* każda zmiana powinna być wykonywana tylko w jednym miejscu.

Unikaj „magicznych liczb"


Magiczną liczbą nazywamy wartość numeryczną, która pewnego dnia będzie zmienia-
na, a jest ukryta głęboko we wielu miejscach kodu. Wyobraź sobie taki fragment kodu
w witrynie WWW hipotetycznego banku:
print("Oprocentowanie lokaty może wynieść nawet 15,5 !<BR>");
$przyklad_wklad = 5000 * 1.155;
printC'Po roku możesz z inwestycji 5000 zł uzyskać $przykład_wklad!<BR>");

Gdy oprocentowanie zmieni się, np.: 15%, ktoś będzie musiał znaleźć i zmienić miej-
sca, w których ta wartość występuje. Podczas szukania w tekście wartości 15,5 bardzo
łatwo opuścić wartość 1.155 w drugim wierszu przykładu. Spowoduje to prezentację
nieprawdziwych danych.

Dla prostych witryn lepszym rozwiązaniem jest użycie zmiennej $oprocentowanie,


która jest inicjowana na samym początku skryptu — zmiana oprocentowania powoduje
jedynie zmianę wartości tej zmiennej. Bardziej skomplikowane witryny mogą tworzyć
strony, wywołując odpowiednie funkcje, ze zmienną typu $oprocentowanie jako pa-
rametrem wywołania. Zawartość niektórych witryn zapisana jest w bazie danych, więc
nie trzeba w nich zmieniać ani jednego fragmentu kodu.
260_________________________________________Część l » Podstawy PHP

Funkcje
Po próbach prowadzenia złożonej witryny WWW, która używała języka skryptowego
nieposiadającego funkcji, możemy powiedzieć, że funkcje pełnią kluczową rolę w pie-
lęgnacji oprogramowania. Sztuka stosowania abstrakcji proceduralnej przy użyciu
funkcji może być opisana w osobnej książce. Przytoczymy tylko krótkie wskazówki.
* Musi istnieć powód przeniesienia fragmentu kodu PHP do funkcji, szczególnie
w przypadku wielokrotnego użycia tego kodu.
•* Pisz krótkie definicje funkcji. Jeżeli funkcja zbytnio się rozrasta, podziel ją na
mniejsze funkcje.
•* Funkcja musi być zdefiniowana przed użyciem. Kolejność funkcji nie ma
znaczenia.

Pliki dołączane
Jedną z największych przewag dynamicznych stron WWW nad statycznym HTML jest
zdolność zmniejszania redundancji. Każdy, kto kiedykolwiek zarządzał statyczną wi-
tryną dowolnych rozmiarów, wie, jak wiele stałych fragmentów znajduje się na stronie
— zmiana każdego znaku nie jest prosta, jeżeli witryna ma 200 stron.

PHP ułatwia wstawianie dowolnych fragmentów do skryptów, od jednego znaku, do


całych oddzielnych programów, używając wbudowanych funkcji include lub requ-
ire. Składnia jest bardzo prosta:
<?php include("filename.ext"); ?>

Można również użyć zmiennych nazw plików w następujący sposób:


<?php
$LastName = "Park";
include ( "$ Las t Name . inc") ;
?>

Fragment ten spowoduje włączeniem do pliku Park.inc. Na rysunku 14.1 zamieszczony


jest przykład strony PHP używającej dołączanych plików.

Możesz używać dowolnych rozszerzeń dołączanych plików. Zwykle stosowane są


. txt, . inc lub nawet . htral.

Należy pamiętać o tym, że:


* PHP włącza cały tekst pliku do skryptu PHP w trybie HTML (wyjaśnialiśmy to
w rozdziale 4.). Jeżeli włączany plik ma być analizowany przez PHP, należy
użyć znaczników PHP na początku i końcu. Jeśli dowolny fragment włączane-
go pliku ma być przetwarzany przez PHP, część ta musi być otoczona znaczni-
kami PHP.
Rozdział 14. » Styl PHP 261

Rysunek 14.1.
Plik PHP
z włączanymi
plikami

* Przypomnij sobie różnice (szczegółowo opisane w rozdziale 5.) pomiędzy


funkcją include, a konstrukcją require (dużo szybszej w PHP 4). W PHP 4
można również stosować nową funkcję include__once ( ) , która może zmniej-
szyć czas ładowania strony, gdy pliki włączane są wiele razy.
+ Funkcja include może być używana do tworzenia skomplikowanych stron
WWW z plików tekstowych zamiast z bazy danych. W niektórych przypad-
kach działa o wiele szybciej. Jeśli zadasz sobie trud utworzenia połączenia
z bazą danych, otrzymasz doskonałe narzędzie do przechowywania dużych
bloków tekstu.

Poniższy przykład ilustruje użycie wielu funkcji include na jednej stronie:


<HTML>
<HEAD>
< !-- Jeżeli chcesz, umieść ten nagłówek w włączanym pliku. -->
<TITLE>Menu dnia</TITLE>
<STYLE TYPE="text/css">
<!--
BODY (color: #000000; font-family: verdana; font-size: 10 pt)
HI (margin-top: 10; color: #FFA500; font-family: verdana; font-size: 16
"^pt; font-weight: bold)
H2 (color: #FFA500; font-family: verdana; font-size: 10 pt}
A:link (color: łłFFEFDS; text-decoration: none)
-->
</STYLE>
</HEAD>

<BODY>
<TABLE BORDER=0 CELLPADDING=0 WIDTH=100%>
<TR>
<font c o l o r = " # O O O O B B " x ? p h p include ( "navbar . inc" ) ; ?>
</fontx/TD>
<TD BGCOLOR="#FFFFFF" ALIGN-LEFT VALIGN=TOP W I D T H = 8 0 % >
<TABLE CELLPADDING=15XTRXTD ALIGN=LEFT VALIGN=MIDDLE>
<Hl>Dzisiejsze menu</Hlx/TDX/TR>
<TRXTD ALIGN=LEFT>
262_________________________________________Część l » Podstawy PHP

<font color="łOOOOBB"><?

Stoday = date("Ymd");
include("Stoday.inc");
?>
</font> </TDX/TRX/TABLE>
</TDX/TRX/TABLE>
<BRXBR>
<!-- Nie ma szczególnego powodu umieszczania tej stopki w włączanym pliku.
Mamy po prostu taki kaprys —>
<?php include("footer.inc") ; ?>

navbar.inc
<TD BGCOLOR="#FFA500" ALIGN=CENTER VALIGN=TOP WIDTH=20%>
<H2>Bar<BR>firmowy</H2>
<BRXBR>
Wpisz <BR>
<A HREF=" suggest ion. php">propozycję</AXBRXBR>
Powiedz nam o twoich<BR>
<A HREF="preferences .php">ulubionych potrawach</AXBRXBR>
Zapisz się na specjalne <BR>
<A HREF="holidays.php">menu świąteczne</AXBRXBRX/font>

20010127.inc
<PXB>27 styczeń 2001</Bx/P>
Zupa: Pomidorowa<BR>
Kanapki: Sałatka z kurczaka<BR>
Potrawa wegetariańska: Makaron z serem<BR>
Deser: Budyń czekoladowy<BR>
<PX?php
//Możesz włączać pliki do włączanego pliku
include("always.inc"};
?>
</P>

always.inc
Bułki, ciastka, paluszki<BR>
Woda, kawa, herbata, mleko, napoje<BR>
Lody waniliowe<BR>

footer.inc
<P>Copyright 1994-<?php Stoday=date("Y"); print("Stoday"); ?>
Szef kuchni</P>
</BODYX/HTML>

Jeżeli korzystasz z modularnej konstrukcji strony, musisz zdecydować, kiedy ją zastosować,


a kiedy nie jest warta zachodu. Tylko ty możesz stwierdzić, kiedy korzyści z posiadania ela-
stycznych komponentów przewyższaj ą potencjalne problemy posiadania wielu części.

Interfejs obiektowy
Ponieważ jeszcze nie opisaliśmy systemu obiektowego PHP, wspomnimy tylko, że kon-
sekwentne użycie obiektów może ułatwić utrzymanie systemu. Dla przykładu, niektórzy
projektanci witryn PHP na podstawie bazy danych zawarli wszystkie funkcje specyficz-
ne dla bazy w metodach obiektu, więc pozostały kod nie wie nawet, z jaką bazą danych
współpracuje. Jeżeli zdecydują się przenieść bazę z MySQL na Oracle, tylko obiektowa
część kodu będzie wymagała zmian.

Na temat programowania obiektowego w PHP piszemy szczegółowo


w rozdziale 29.
Rozdział 14. » Styl PHP__________________________________________263

Solidność
1. Kod powinien wykrywać sytuacje wyjątkowe i odpowiednio je obsługiwać.
2. Program powinien informować o awaryjnym zakończeniu pracy.

Ozdobne języki, takie jak Java, posiadają obszerne systemy obsługi wyjątków, które
wymagaj ą napisania kodu określającego rangę awarii i sposoby reagowania na sytuacje
awaryjne. Niestety PHP nie ma takich konstrukcji — programiści PHP sprawdzać po-
prawność operacji i posługiwać się funkcją die ( ) .

Pisanie solidnego kodu jest z początku trudne do opanowania, ponieważ programista


musi przewidzieć wszystkie awaryjne sytuacje i próbować je obsłużyć. Przydałby się
standardowy zestaw testów, obsługujący częste przyczyny problemów. Większość za-
gadnień solidności kodu w PHP podobna jak w innych językach programowania. Ist-
nieją dwa zagadnienia wyjątkowe: problemy z zewnętrznymi usługami oraz związane
ze zmiennością typów.

Niedostępność usługi
PHP jest w części językiem zapewniającym jedno środowisko, w którym mogą być
używane różne biblioteki kodu i zewnętrzne usługi. Każda strona PHP może otworzyć
plik, połączyć się z bazą danych, odpytać serwer LDAP, wysłać nagłówek HTTP lub
wysłać pocztę przez serwer SMTP. Ważne jest aby przewidzieć, kiedy zewnętrzna usłu-
ga będzie niedostępna, przekroczony zostanie czas oczekiwania, połączenie będzie źle
działało lub zostanie przerwane w czasie pracy.

Bardzo często usługi te mają możliwość odczytania kodu błędu i wyświetlenia go, jeżeli
jedyną możliwością jest zakończenie pracy. Rozsądną konstrukcją realizującą połącze-
nie do bazy danych MySQL jest:
Sconnection = mysql_connect([argumenty]) or
die("Połączenie nieudane: $php_errormsg<BR>");

Jest to dobra metoda obsługiwania błędów. Lepiej zakończyć działanie, niż wykonywać
skrypt, zakładając, że połączenie z bazą istnieje.

Niespodziewany typ zmiennej


Mimo że zmienność typu zmiennej w PHP jest w większości przypadków zaletą, nie
możemy założyć typu zmiennej na 100%. Mimo że dokładnie znasz wszystkie zasady
konwersji typów, może się zdarzyć, że do fragmentu kodu przeznaczonego do pracy na
ciągach przekazana zostanie wartość numeryczna. Może się zdarzyć tak, ponieważ nie-
które konstrukcje PHP zakładają, że ciąg składający się wyłącznie z cyfr musi być liczbą.
Ciekawym sposobem sprawdzenia solidności kodu jest odszukanie wszystkich znaków
$ (odszukanie wszystkich zmiennych) i sprawdzenie, co się stanie, jeżeli typ zmiennej
będzie inny, niż zakładałeś.
264_________________________________________Część l » Podstawy PHP

Zwięzłość i wydajność
Zwięzłość i wydajność nie są tożsame. Zwięzły kod realizuje zadanie przy użyciu małej
liczby wierszy lub znaków, a wydajny kod realizuje to zadanie szybko, używając nie-
wiele pamięci. W tym fragmencie rozdziału podzielimy się kilkoma wskazówkami na
temat pisania zwięzłego i wydajnego kodu PHP oraz subiektywnymi opiniami na temat
sensowności takich działań.

Używaj właściwych algorytmów


W czasach, gdy pamięć komputera i jego moc obliczeniowa były bardzo cenne, warto
było wkładać wiele pracy w optymalizację kodu. Jest to nadal ważne w niektórych
działach produkcji oprogramowania (systemy operacyjne, biblioteki graficzne), lecz
w większości zadań zaoszczędzenie kilku kilobajtów pamięci nie jest warte poświęcania
innych celów. Jest to szczególnie widoczne w przypadku programowania dla sieci, pod-
czas którego zawsze występuje pewien narzut czasowy na operacje związane tylko
z Internetem. Jeżeli strona ładuje się u użytkownika pół sekundy niezależnie od sposo-
bu, w jaki sposób została utworzona, to dodatkowe pięć milisekund czasu wykonania na
serwerze zgubi się.

Jest jednak odmiana dbałości o wydajność, która prawdopodobnie zawsze będzie ważna:
wybór algorytmu lub sposobu, w jaki twój kod realizuje zadanie. Jeżeli twój kod poszu-
kuje nazwy w bazie danych poprzez pobranie wszystkich nazw i kolejne ich porówny-
wanie, szybko przekonasz się, jak ważna jest wydajność.

Poprawianie wydajności
Przedstawimy teraz kilka sposobów, jakie powinieneś stosować w trakcie programowania.

Nie odkrywaj Ameryki


Pisanie kodu, który powiela mechanizmy języka, jest zwykle niezbyt dobrym pomysłem,
chyba że robisz to dla zabawy lub nauki. Każdy programista na jakimś etapie edukacji
napisał procedury sortujące, ale nikt nie powinien tego robić w czasie normalnej pracy
(chyba że właśnie tym się zajmuje). Większość języków programowania wysokiego po-
ziomu posiada mechanizmy sortujące (część biblioteki lub języka), prawdopodobnie
lepsze od twoich. PHP nie jest wyjątkiem — istnieje kilka sposobów sortowania tablic,
również większość baz danych obsługiwanych przez PHP posiada opcje sortowania wbu-
dowane w język zapytań. Każda z tych metod jest szybsza i bardziej stabilna niż proce-
dury, które sam byś napisał.

Znajdź sedno
Lepiej od początku używać efektywnych algorytmów. Jeżeli tego nie zrobiliśmy, nie
warto optymalizować kodu, zanim nie znajdziemy miejsca, które zużywa zbyt dużo
Rozdział 14. » Styl PHP___________________________________________265

zasobów. Optymalizacja zastosowana w takim właśnie miejscu da najlepsze efekty.


Dzieje się tak, ponieważ większość istniejących programów spełnia zasadę 90/10: przez
90% czasu jest wykonywane 10% kodu. Trzeba odnaleźć i ulepszyć te 10%.

Jedną z technik używanych do odszukania tych właściwych 10% jest profilowanie.


Profiler to narzędzie śledzące wykonywany kod i zapamiętujące czas wykonania każ-
dego wywołania funkcji. Opracowuje sumaryczny raport. Niestety w chwili obecnej nie
ma narzędzia profilującego do PHP (być może zostanie dołączone do kolejnych wersji
PHP 4). Najlepsze, co teraz możemy zrobić, to wstawianie do kodu wierszy obliczają-
cych i wyświetlających czas wykonania poszczególnych fragmentów skryptu, używając
funkcji microtime ( ) . Jeżeli kod stosuje się do zasady 90/10, czas wykonania poszu-
kiwanego fragmentu będzie na pewno rzucał w oczy.

Skup się na zapytaniach bazy danych


Mimo że wybiegamy teraz nieco naprzód, do następnej części książki, powinieneś pa-
miętać, że zapytania są największym pożeraczem czasu w witrynach PHP. Jeżeli twoja
witryna na podstawie bazy danych nie wykonuje wielu innych czasochłonnych obli-
czeń, pierwszym podejrzanym są zapytania. Następnie trzeba zidentyfikować zapytanie,
które pochłania najwięcej czasu. Istnieje wiele sposobów na jego przyspieszenie; wiele
nie ma nic wspólnego z PHP.

C AjA Aby przeczytać więcej na temat optymalizacji witryn na podstawie ba-


uklfi ^ danych, przejdź do rozdziału 23.

Skup się na wewnętrznej pętli

Załóżmy, że na stronie występuj ą zagnieżdżone pętle, jak w przykładzie:


for ($x=0; Sx < 100; Sx++)
{
do_X();
for ($y=0; $y < 100; $y++)
{
do_XY();
for ($z=0; $z < 100; Sz++)
(
do_XYZ();
)
)
}

Powinieneś się skupić na optymalizacji funkcji do_XYZ ( ) (wykonywanej 1000000 razy),


a nie na pozostałych dwóch (wykonywane 10000 i 100 razy).

Zwięzłość: zmniejszanie
Zanim przedstawimy sposoby pisania bardziej zwięzłego kodu, musimy powiedzieć, że
według nas zwięzłość jest przecenianą zaletą, z poniższych powodów.
266_________________________________________Część l » Podstawy PHP

Zwięzłość rzadko oznacza wydajność


Mimo że prawdą jest, że gdzieś we wnętrzu maszyny PHP znaki kodu są czytane jeden
po drugim (teoretycznie im więcej znaków, tym więcej czasu), to jednak w praktyce
maszyna analizująca bazująca na Zend jest tak wydajna, że wielkość pliku nie stanowi
problemu (podobnie jak czas lub miejsce zajmowane przez dodatkową zmienną lub do-
datkowe wywołanie funkcji).

Zwięzłość jest w opozycji do czytelności


Pamiętaj, że każdy opuszczony przez ciebie znak może spowodować, że ktoś będzie się
zastanawiał, co miałeś na myśli w czasie pisania tego fragmentu kodu. Spójrz na poniż-
szą zwięzłą funkcję:
function sieve($n) (
for ($i = 2; $i <= sqrt($n); $i++)
for ($j = $i, Sind = $i * $j; $ind <= $n;
$j++, $ind = $i * $j)
$carray[$ind] = TRUE;
for ($i = $n, Splist = array(); $i > 1; $i--)
if (ilsSet(Scarray[Si]))

Splist = $i;
return($plist);
}

Oczywiście jest to implementacja sita Erastonesa, a $plist, jest listą wszystkich liczb
pierwszych mniejszych od $n. Oczywiście.

Dlaczego programiści zabiegają o zwięzłość? Pierwszym powodem jest oszczędzanie


czasu (ale tylko czasu kodowania). Drugim powodem jest (tylko trochę żartujemy)
obawa przed wyśmianiem (znający C wiedzą, że ten sam kod można zapisać używając
o połowę mniej miejsca).

Wskazówki na temat zwięzłości


Jeżeli musisz pisać kod zajmujący mniej miejsca, spróbuj użyć następujących technik.

Korzystaj jednocześnie ze zwracanej wartości i efektu ubocznego


Bardzo znanym sposobem jest korzystanie z faktu, że wartością przypisania jest przypi-
sana wartość, co jest przedstawione w następującym pseudokodzie:
while ( Snext = PobierzNastepny())
ZrobCos( $next );

PobierzNastepny ( ) jest funkcją zwracającą kolejne wartości. Zwraca FALSE, gdy


nie ma już nic do zwrócenia. Gdy zostanie zwrócona wartość FALSE, kończy się wyko-
nywanie pętli while.
Rozdział 14. » Styl PHP__________________________________________267

Używaj z operatorów inkrementacji i przypisania


Operatory inkrementacji (++ i --) skracają wyrażenia dodawania l lub odejmowania
l od zmiennej; połączone z operatorami przypisania (+=, *=, .= itp.) pozwalają na bar-
dzo zwięzłe zapisywanie takich przypisań.

Operatory inkrementacji i matematyczne operatory przypisania opisa-


ne są w rozdziale 10., operator połączenia ciągów ( . = ) w rozdziale 9.

Bardzo często operatory te są używane w połączeniu z poprzednią techniką:


while (Scount--)
ZrobCos( Scount );

Pętla ta (zakładając, że $count jest liczbą całkowitą dodatnią) wywoła funkcję dla każ-
dej liczby, aż do l.

Używaj funkcji wielokrotnie


W tym przypadku zwięzłość jest dobra, ponieważ korzystanie z funkcji dobrze wpływa
na styl programu. Możesz odszukać powtórzenia kodu, zmniejszyć go zamieniając
powtórzenia na wywołania funkcji. Kod będzie krótszy i prawdopodobnie łatwiejszy
w konserwacji.

Nie ma nic złego w wartościach Boolean


Początkujący programiści bardzo często mają dziwne uprzedzenia do wartości Boolean,
nie rozumiejąc, że może być przekazywana tak samo, jak wartości innych typów. Dla-
tego marnują dużo miejsca:
function PodzielnyPrzezZle(Snuml, $num2)
l
if (Snuml % Snum2 == 0)
return(TRUE);
else
return(FALSE) ;
}

/* użycie funkcji */
if (PodzielnyPrzezZleO, 3))
$divisible_result = TRUE;
else
$divisible_result = FALSE;

if ($divisible_result == TRUE)
print("Jest podzielne!<BR>") ;
else
if ($divisible result == FALSE)
print("Nie jest podzielne!<BR>");

Bardziej zwięzła wersja poprzedniego przykładu wygląda następująco:


function PodzielnyPrzezLepiej($numl, $nura2)
{
return (Snuml % Snum2 == 0);
)
268_________________________________________Część l » Podstawy PHP

/* użycie funkcji */
if (PodzielnyPrzezLepiej(9,3))
print("Jest podzielne!<BR>");
else
print("Nie jest podzielne!<BR>");

Możesz oczywiście pójść krok dalej i usunąć całą funkcję:


if (9 % 3 == 0)
print("Jest podzielne!<BR>");
else
print("Nie jest podzielne!<BR>");

Powtórzmy raz jeszcze, opisowa nazwa funkcji jest już częścią dokumentacji; każdej
funkcji można użyć w przyszłości, co oczywiście ułatwia utrzymanie oprogramowania.

Użycie skracania operatorów logicznych


Niektóre wyrażenia Boolean są niebezpieczne, jeżeli przedtem nie wykonasz kilku
sprawdzeń. Często prowadzi to do izolacji niebezpiecznego wyrażenia w konstrukcji
if. Załóżmy, że chcesz wyświetlić proporcję dwóch zmiennych, do których zostały
przypisane liczby, a ich stosunek jest większy niż 2. Poza tym chcesz uniknąć błędu
dzielenia przez 0. Można to ostrożnie zrealizować w następujący sposób:
if (IsSet($x)J
{
if (IsSet($y))
(
if (Is_Integer($x) )
l
if (Is_Integer($y) )
(
if (Sy != 0)
(
if (Sx / $y > 2)
print ("Stosunek wynosi". ($x / Sy)) ;
}
)
l
}
)

Inny ostrożny sposób:


If (IsSet($x) && IsSet($y) && Is_Integer($x) && Is_Integer($y) ss
$y != O SS $x / $y > 2)
print("Stosunek wynosi". ($x / Sy));

Sprawdzenia te zostaną przeprowadzone od lewej do prawej i jeżeli którekolwiek z nich


się nie powiedzie, kolejne testy po prawej nie będąjuż wykonywane.

Tryb HTML, czy PHP?


Istnieje ogromna liczba sposobów na połączenie PHP i HTML, dających ten sam efekt
końcowy. Wybór może zależeć od czynników zewnętrznych, na przykład od sposobu
pracy zespołu.
Rozdział 14. » Styl PHP__________________________________________269

Najprostszym sposobem pokazania różnych sposobów jest napisanie tego samego skryptu
w stylu „minimalnym PHP", „maksymalnym PHP i „średnim PHP". Pamiętaj, że wszyst-
kie są prawidło we i daj ą ten sam wynik. Wybory stylistyczne są sprawą upodobań i zwię-
złości, a czasami niewielkich różnic w możliwościach.

W poniższych przykładach adresy e-mail są nieprawdziwe. Jeżeli chcesz przetestować


ten kod na serwerze PHP obsługującym Sendmail, musisz zmienić je na prawidłowe.

Minimalny PHP (rysunki 14.2 i 14.3):

Rysunek 14.2.
Minimalny PHP,
część l

Rysunek 14.3.
Minimalny PHP,
ekran drugi
270_________________________________________Część l » Podstawy PHP

<HTML>
<HEAD>
<TITLE>MorningService.html</TITLE>
<STYLE TYPE="text/css">
<! --
BODY {color: fłOOFFOO; font-family: times; font-size: 12 pt)
HI (margin-top: 10; margin-bottom: 10; color: red; font-family: cursive;
font-size: 16 pt; font-weight: bold}
A:link {color: red)
—>
</STYLE>
</HEAD>

<BODY>
<CENTERXHl>Hotel California</HlX/CENTER>
<P>Dziękujemy za zatrzymanie się w hotelu California!<BR>
Proszę powiedzieć nam, co może sprawić, aby poranne słońce świeciło
jeszcze jaśniej.
<FORM METHOD="POST" ACTION="MorningService.php">
<P>Numer pokoju <INPUT TYPE="TEXT" NAME="Room" SIZE=10>
<P>Czy zamawia pani/pan budzenie?<BR>
Jeżeli tak, proszę wpisać godzinę <B>w formacie 24-godzinnym (GG:MM)</B>
<INPUT TYPE="TEXT" NAME="WakeupTime" SIZE=10XBR>
<P>Proszę wybrać gazetę:<SELECT NAME="Newspaper" SIZE=1>
<!-- Ponieważ całkowicie oddzielamy PHP od HTML, nie da się tworzyć
tej listy dynamicznie. Jeżeli lista gazet zmieni się,
Musisz ręcznie zmodyfikować ten formularz.
—>
<OPTION VALUE=0>Proszę dostarczyć...
<OPTION VALUE=1>USA Today
<OPTION VALUE=2>New York Times
<OPTION VALUE=3>Wall Street Journal
</SELECT>
<PXINPUT TYPE="Submit">
</FORM>
</BODYX/HTML>
<HTML>
<HEAD>
<TITLE>MorningService.php</TITLE>
</HEAD>

<BODY>
<?php if(SNewspaper == O && $WakeupTime == "") (?>
<P>Przepraszam, ale nie powiedziałeś nam, co mamy zrobić!
<?php ) elseif($Newspaper == O && SWakeupTime != "")
{
maił("deskclerk@hotelcali.com", "$Room", "Budzenie o: SWakeupTime"); ?>
<P>Budzenie zostało zarejestrowane. Śpij dobrze!
<?php } elseif($Nęwspaper > 0) {
maił{"deskclerk@hotelcali.com", "$Room", "Budzenie o:
SWakeupTime i dostarczenie SNęwspaper"); ?>
<P>Budzenie i dostarczenie gazety zostały zarejestrowane, śpij dobrze!
<?php ); ?>
</BODYX/HTML>

W stylu minimalnym oddzielamy HTML i PHP, stosując dwa osobne pliki, MorningSe-
rvice.html (formularz) oraz MorningService.php (skrypt przetwarzający formularz). Jest
to szczególnie użyteczne dla grup projektantów, które mają oddzielne zespoły zajmują-
ce się opracowaniem produktu od strony klienta i od strony serwera. Format ten po-
zwala na równoległą pracę i umożliwia szybkie wprowadzanie zmian do projektu. Styl
ten utrudnia jednak stosowanie zabiegów, takich jak pokazywanie nieco zmienionej
strony HTML w zależności od określonych warunków.

Maksymalny PHP (patrz rysunek 14.4):


Rozdział 14. » Styl PHP_______________________________________271

Rysunek 14.4.
Maksymalny PHP
po wysłaniu danych

<?php
print("<HTML>\n");
print("<HEAD>\n");
print("<TITLE>MorningService.html</TITLE>\n");
print("<STYLE TYPE=\"text/css\">\n") ;
print ("<!—\n") ;
print("BODY (color: #000000; font-family: times; font-
size: 12 pt)\n");
print("HI (margin-top: 10; margin-bottom: 10; color:
red; font-family: cursive; font-size: 16 pt; font-weight:
bold)\n");
print("A:link (color: red)\n");
print("~->\n");
print("</STYLE>\n");
print("</HEAD>\n");
print("<BODY>\n");
print ("<CENTERXHl>Hotel Calif ornia</Hlx/CENTER>\n" ) ;
print("<P>Dziękujemy za zatrzymanie się w hotelu California!<BR>\n") ;
/* Poniższy test używa wartości przycisku SUBMIT z formularza,
aby sprawdzić, czy użytkownik ogląda pierwszy raz stronę
(w tym przypadku nie wysyłamy komunikatu do recepcji)
lub wysłał/zmienia wcześniejsze zamówienie */
if(llsSet($Submit))
(
/* Poniższe puste wartości spowodują wyświetlenie pustego
formularza. */
$Room = "";
SWakeupTime = "";
$Newspaper = "";
print("Proszę powiedzieć nam, co może sprawić, aby poranne słońce świeciło
jeszcze jaśniej.\n");
)
else
{
if(SNewspaper == O S5 SWakeupTime == "")
print("<P>Właśnie usunąłeś wszystkie zamówienia porannej obsługi.\n");
elseif($Newspaper == O ss SWakeupTime != "")
(
maił("deskclerk@hotelcali.com", "$Room", "Budzenie o:
SWakeupTime");
272_________________________________________Część l » Podstawy PHP

print{"<P>Budzenie zostało zarejestrowane. Śpij dobrze!\n"};


)
elseif(SNewspaper > 0)
(
maił("deskclerk3hotelcali.com", "$Room", "Budzenie o:
$WakeupTime i dostarczenie $Newspaper");
print("<P>Budzenie i dostarczenie gazety zostały zarejestrowane.
Śpij dobrze!\n");
}
print("Możesz zmienić wartości w formularzu i ponownie je wysłać
.\n");
)
/* Zauważ, że wyświetlamy ten formularz z ustawionymi wartościami
(które mogą być pustymi ciągami). */
print("<FORM METHOD=\"POST\" ACTION=\"$PHP_SELF\">\n");
print("<P>Numer pokoju <INPUT TYPE=\"TEXT\" NAME=\"Room\"
SIZE=10 VALUE=\"5Room\">\n");
print("<P>Czy zamawia pani/pan budzenie?<BR>\n");
print ("Jeżeli tak, proszę wpisać godzinę <B>w formacie 24-godzinnym
(GG:MM)</B> <INPUT TYPE=\"TEXT\" NAME=\"WakeupTime\" SIZE=10
VALUE=\"$WakeupTime\"><BR>\n") ;
print("<P>Wybierz gazete:<SELECT NAME=\"Newspaper\" SIZE=1
VALUE=\"$Newspaper\">\n");
/* Ta lista może być dynamicznie pobierana z bazy danych */
print("<OPTION VALOE=0>Prosze dostarczyć...\n");
print("<OPTION VALUE=1>USA Today\n");
print("<OPTION VALUE=2>New York TimesXn");
print("<OPTION VALUE=3>Wall Street Journal\n");
print("</SELECT>\n");
print ("<PXINPUT TYPE=\"Submit\" NAME=\"Subrnit\"
VALUE=\"Send\">\n") ;
print ("</BODYX/HTML>\n") ;
?>

Styl ten jest faworyzowany przez programistów C, ponieważ jest bardzo podobny do
sposobu realizacji zadań w tym języku programowania. Jeżeli nie masz dużej praktyki
w pisaniu kodu tworzącego HTML, styl ten może wydawać ci się niezgrabny i mylący.
Ponieważ PHP i HTML są ze sobą połączone, możesz uzależnić wyświetlenie każdego
wiersza HTML od wielu warunków sprawdzanych w PHP. Jednak niełatwo wprowa-
dzić zmiany projektu graficznego.

Pamiętaj o poprzedzaniu znaków " i $ znakiem \. Jeżeli chcesz, aby


źródło strony było czytelne, musisz pamiętać o używaniu odpowied-
nich znaków końca wiersza (\n), powrotu (\r) i tabulatorów (\t) we-
wnątrz funkcji zwracających HTML.

Średni PHP:
<?php
function display_form()
{
?>
<CENTERXHl>Hotel California</Hl></CENTER>
<P>Dziękujemy za zatrzymanie się w hotelu California!<BR>
Proszę powiedzieć nam, co może sprawić, aby poranne słońce świeciło
jeszcze jaśniej.
<FORM METHOD="POST" ACTION="<?php echo($PHP_SELF); ?>">
<P>Numer pokoju <INPUT TYPE="TEXT" NAME="Room" SIZE=10>
<P>Czy zamawia pani/pan budzenie?<BR>
Jeżeli tak, proszę wpisać godzinę <B>w formacie 24-godzinnym (GG:MM)</B>
<INPUT TYPE="TEXT" NAME="WakeupTime" SIZE=10XBR>
<P>Proszę wybrać gazetę:<SELECT NAME="Newspaper" SIZE=1>
<OPTION VALUE=0>Proszę dostarczyć...
Rozdział 14. » Styl PHP__________________________________________273

<OPTION VALUE=1>USA Today


<OPTION VALUE=2>New York Times
<OPTION VALUE=3>Wall Street Journal
</SELECT>
< P X I N P U T TYPE="Submit" Name="Submit" Value="Send">
<?php
}

function process_form()
{
global SNewspaper, $WakeupTime, SRoom;
if($Newspaper == O ss SWakeupTime == "")
print("<P>Przepraszam, ale nie powiedziałeś nam, co mamy zrobić!");
elseif($Newspaper == O ss $WakeupTime != "")
(
maił("deskclerk@hotelcali.com", "$Room", "Budzenie o SWakeupTime"};
print("<P>Budzenie zostało zarejestrowane. Śpij dobrze!");
}
elseif($Newspaper > 0)
f
maił("deskclerk@hotelcali.com", "SRoom", "Budzenie o SWakeupTime
i dostarczenie SNewspaper");
print("<P>Budzenie i dostarczenie gazety zostały zarejestrowane.
Śpij dobrze!");
}
}
?>

<HTML>
<HEAD>
<TITLE>MorningService.php</TITLE>
<STYLE TYPE="text/css">
BODY (color: łOOOOOO; font-family: times; font-size: 12 pt)
HI {margin-top: 10; margin-bottom: 10; color: red; font-
family: cursive; font-size: 16 pt; font-weight: bold}
A:link (color: red}
</STYLE>
</HEAD>
<BODY>
<?php
if(!IsSet($Submit))
(
Sroom = "";
SWakeupTime = "";
SNewspaper = "";
display_form();
}
else
(
process_form(};
print("<P>Możesz zmienić wartości w formularzu i ponownie je wysłać
An") ;
display_form();
)
?>
</BODYX/HTML>

Jest to, jak można było przypuszczać, kompromis. Część HTML oprócz nagłówka jest
umieszczona w funkcji, również część przetwarzająca formularz jest umieszczona
w funkcji. Proste instrukcje sterujące (umieszczone w nagłówku i stopce HTML) decy-
dują, czy uruchomić pierwszą, czy obie funkcje.

Styl ten nie jest, być może, estetyczny, może nawet wydawać się czasami niejasny, ale
pozwala na pełne wykorzystanie możliwości PHP. PHP jest produktem hybrydowym
— nie jest to C, ani Perl, ani HTML, ale każdy z tych produktów ma na niego wpływ.
Styl hybrydowy jest chyba najlepszy.
274_________________________________________Część l » Podstawy PHP

Oddzielanie kodu od projektu


Przedstawiony na początku rozdziału kod strony, wygenerowany przez edytor WYSIWYG,
jest tak brzydki, ponieważ łamie zasadę oddzielania funkcji strony WWW od jej układu.
Wraz z rozwojem HTML oddzielenie funkcji i projektu staje się obowiązkowe, ponieważ
nowe ważne funkcje (na przykład XML) wymagaj ą lepiej skonstruowanego kodu.

Wiele tematów poruszonych w tym rozdziale było oczywistą konsekwencją oddzielenia


kodu i projektu. Opiszemy teraz jeszcze kilka dodatkowych technik.

Funkcje
Na przykładzie średniego PHP pokazaliśmy, że własne funkcje mogą być bardzo ela-
stycznym narzędziem formatowania strony. Jest to jedna z funkcji, które stawiają PHP
ponad językami skryptowymi bazującymi na znacznikach.

Arkusze stylów w PHP


Jak wiesz, istniej ą cztery główne sposoby stosowania stylów na stronach WWW.
* Stosowanie formatowania CSS dla poszczególnych znaczników.
** Użycie znaczników <STYLE> (opcjonalnie wewnątrz pary znaczników ko-
mentarza HTML).
* Znaczniki <LINK>.
^ Simport.

W tej książce konsekwentnie stosujemy znaczniki <STYLE>, a nie ze-


wnętrzne arkusze stylu, we wszystkich przykładach kodu. Specjalnie
dla ciebie, drogi czytelniku, abyś mógł zobaczyć deklaracje, jakich uży-
liśmy do otrzymania wyników pokazywanych na rysunkach.

W PHP możesz użyć funkcji include do stosowania stylów w sposób niestandardowy,


korzyści jednak nie są jasne. Zamiast podłączyć zewnętrzny arkusz stylów, możesz
włączyć plik tekstowy, zawierający wszystko pomiędzy znacznikami <STYLE>.

Musimy wspomnieć również o praktyce, stosowanej i deprecjonowanej tyleż samo czasu


— o używaniu przestarzałych znaczników HTML, takich jak FONT, BGCOLOR i ALINK.
PHP może ci pomóc, jeżeli z jakiegoś powodu musisz je zastosować. Na przykład:
<FONT FACE="<?php include("fontlist.txt"); ?>" SIZE=+2>

Taki wiersz pozwoli przepracowanemu projektantowi stron zmienić czcionki w całej


witrynie za pomocą modyfikacji zawartości jednego pliku.
<P STYLE="font-family: <?php include("fontlist.txt"); ?>"> Tutaj tekst</P>
Rozdział 14. » Styl PHP__________________________________________275

Szablony i spójność stron


Jak możesz sobie wyobrazić, w PHP istnieje wiele sposobów projektowania witryn, pa-
sujących do twojego stylu i organizacji pracy nad witryną. Można tak zorganizować
pracę, żeby graficy i inżynierowie nie pracowali na tych samych plikach (na wypadek,
gdyby się nie lubili). Jeżeli witryna ma wiele stron, musisz wiedzieć, że wybranie okre-
ślonej organizacji plików lub szablonów jest bardzo przydatne. Przedstawiamy poniżej
jeden uproszczony przykład szablonu używanego przez nas w witrynach www.mystery-
guide.com i www.siencebookguide.com.
<?
/* Ładowanie ogólnych funkcji */
include{"general-functions.inc");
/* Ładowanie fnukcji specyficznych dla strony*/
include("renaissance-functions.inc"};
/* zmienne dostępne na stronie */
$PageTitle = "Malarze Renesansu";
$db_connection = make_database_connection();
?>
<HTML>
<HEAD>
<TITLE>
<?php print("SPageTitle"); ?>
</TITLE>
</HEAD>
<BODY>
<H3>
<?php print("$PageTitle"); ?>
</H3>
<TABLE>
<TRXTD>
<?php lewa-strona($db_connection); ?>
</TDXTD>
<?php prawa-strona($db_connection); ?>
</TDX/TR>
</TABLE>
<?php stopka{$db_connection); ?>
</BODYXHTML>

W tym przykładzie każda strona ładuje ten sam plik funkcji użytkowych, potem plik
funkcji specyficznych dla strony, następnie definiuje zmienne globalne dla strony. Na
koniec komendy PHP umieszczane są w roboczym HTML. Zawartość strony rozmiesz-
czona jest w kolumnach. Aktualna zawartość zależy od funkcji odpowiednich stron,
które mają zawsze te same nazwy, ale zmieniające się definicje. Zmiana zawartości ko-
lumn oznacza modyfikację funkcji strony lub zmianę zawartości bazy danych. Taka
konstrukcja nie wymaga pracy programisty przy wprowadzaniu niewielkich zmian
układu, jeżeli osoba wprowadzająca zmiany będzie modyfikowała tylko HTML, zosta-
wiając PHP w spokoju.

Powyższy przykład jest tylko jednym spośród wielu sposobów podziału pracy. Wybór
strategii powinien zależeć od typu witryny i stylu pracy współpracowników.
276_________________________________________Część l « Podstawy PHP

Podsumowanie
Inne języki programowania mogą wymagać wielu elementów PHP. Powinieneś pisać
czytelny kod z odpowiednio abstrakcyjnymi funkcjami, z konsekwentnymi wcięciami
i opisowymi komentarzami. Powinieneś też unikać magicznych liczb, powielenia kodu,
nadużywania zmiennych globalnych i sztuczek. Program powinien pracować na znanych
danych i podejmować rozsądne działania w przypadku danych, których się nie spo-
dziewałeś. W przypadku awarii powinien kończyć pracę i opisać powód jej przerwania.

Część z zagadnień wyjątkowych w PHP jest związana z organizacją włączania plików,


sposobem łączenia PHP z HTML, ogólnie — z oddzieleniem projektu od kodu. Istnieje
szeroka gama stylów. Powinieneś być konsekwentny w stosowaniu wybranego stylu.

W kolejnym rozdziale przejdziemy od ogólnych pytań stylistycznych do podstawo-


wych: co robić, jeżeli kod PHP nie chce działać?
Rozdział 15.
Podstawowe pułapki PHP
W tym rozdziale:
* Problemy związane z instalacją
* Puste lub niekompletne strony
* Błędy analizatora
* Problemy z uprawnieniami
* Niezainicjowane zmienne
* Problemy z funkcjami
4 Problemy z obliczeniami

Mimo że staraliśmy się jasno tłumaczyć omawiane zagadnienia, a ty na pewno dokład-


nie się do nich stosowałeś, problemy pojawią się prędzej czy później. W tym rozdziale
skupimy się na niektórych najczęściej spotykanych symptomach i zasugerujemy roz-
wiązania.
-r~^™"\ ————^ . --- - — .

CA.A Istnieje morze pułapek związanych z połączeniami do baz danych.


W
takie rozdziale tym zajmiemy się tylko problemami związanymi z PHP. Jeżeli
napotykasz problemy z PHP i bazami danych, przejdź do rozdziału 24.
Problemy specyficzne dla niektórych zaawansowanych funkcji (sesje,
cookie, tworzenie grafiki, e-mail i XML) opisane są w odpowiednich
rozdziałach w trzeciej części książki.

Problemy związane z instalacją


Zamiast wymądrzać się na temat tych, którzy przeprowadzają w pośpiechu instalację
bez odpowiedniego zrozumienia dokumentacji, wskażemy kilka błędów często wystę-
pujących w instalacjach PHP.
278_________________________________________Część l » Podstawy PHP

Jeżeli zauważasz podobne błędy, ale jesteś pewien, że twoja instala-


cja działa stabilnie, zainteresuj się odsyłaczami do późniejszych czę-
ści książki.

Źródło pliku wyświetlane w przeglądarce


Jeżeli zamiast wynikowego HTML widzisz w przeglądarce tekst skryptu, maszyna PHP
po prostu nie została wywołana. Sprawdź, czy na pewno łączysz się z witryną przez
serwer http, nie przez dostęp do plików. Wywołuj plik przez:
http://localhost/mysite/mypage.php

a nie przez:
file://home/httpd/html/mysite/mypage.php

Blok PHP pokazuje się jako tekst;


przeglądarka chce zapisać plik
Maszyna PHP nie została prawidłowo wywołana. Jeżeli prawidłowo pobrałeś plik przez
http w sposób opisany powyżej, najczęściej powodem wystąpienia takiego błędu jest
brak określonego rozszerzenia plików, które mają być przetwarzane przez PHP. Drugim
częstym powodem jest umieszczenie p\ikuphp.ini w złym miejscu. Problemy może też
powodować nieprawidłowa dyrektywa konfiguracji.

Jeżeli widzisz kod PHP na stronie, chociaż masz stabilną instalację,


problem ten może być spowodowany brakiem znacznika PHP. Przeczy-
taj o kłopotach z wyświetlaniem w następnej części rozdziału.

Nieodnaleziony serwer
lub niemożliwe wyświetlenie strony
Jeżeli nie można odszukać serwera, może to być spowodowane problemami z DNS
(serwer nazw) lub konfiguracją serwera WWW.

Jeżeli możesz dostać się do serwera, podając zamiast nazwy jego adres IP, problem jest
najprawdopodobniej związany z DNS. Być może alias serwera nie został prawidłowo
rozesłany. Problem ten występuje, nawet jeżeli witryna długo pracuje. Problem może
też być spowodowany wyłączeniem serwera DNS lub lokalnymi kłopotami z siecią.

Jeżeli nie możesz dostać się do witryny przez adres IP, może nie został prawidłowo po-
łączony z kartą sieciową (lub httpd nie obsługuje wywołań dla określonej domeny, patrz
rozdział 4.).
Rozdział 15. » Podstawowe pułapki PHP_______________________________279

Problemy z wyświetlaniem
W tej części opiszemy problemy, kiedy PHP nie wyświetla komunikatu o błędzie, ale
brak oczekiwanych skutków działania.

Całkowicie pusta strona


Problem ten jest najczęściej związany z HTML, a nie z PHP (poza przypadkami, kiedy
PHP generuje kod HTML). Jeżeli nie używasz maksymalnego stylu PHP (inaczej mó-
wiąc, jeżeli istnieje część skryptu, która jest wyświetlana bez analizowania jej przez
PHP), problem ten prawie na pewno tkwi w HTML.

Jednym z najlepszych narzędzi do uruchamiania, w przypadku gdy


spotykamy się z zagadkowym wynikiem wyświetlenia strony, jest pod-
gląd źródła HTML. Wszystkie przeglądarki posiadają możliwość obej-
rzenia źródła strony. Internet Explorer — opcja Źródło w menu Widok.

Jeżeli tworzyłeś ten plik korzystając z edytora tekstu, sprawdź, czy nie opuściłeś np. znacz-
nika zamykającego </TABLE> lub </FORM>. Jeżeli używałeś edytora wizualnego, problem
może tkwić w dodatkowym elemencie. Plik pochodzący z takiego edytora, zamieszczony na
początku rozdziału 14., posiada o wiele więcej znaczników </TR> niż wierszy tablicy. Róż-
ne przeglądarki mogą to w rozmaity sposób obsługiwać. HTML stworzony przy użyciu
edytora tekstowego o wiele rzadziej jest narażony na takie problemy.

Możesz łatwiej zidentyfikować problem, oglądając źródło strony w komputerze klienta


(szczególnie jeżeli używasz maksymalnego stylu PHP) lub za pomocą innej przeglądarki.
Internet Explorer jest najbardziej wyrozumiałą przeglądarką, natomiast Opera i Amaya
najdokładniej przestrzegaj ą stylu HTML.

Jeżeli nie możesz znaleźć niczego złego w kodzie HTML — szczególnie jeżeli używasz
maksymalnego stylu PHP — z pewnością moduł PHP nie pracuje. Sprawdź to, ogląda-
jąc inną stronę (musi działać prawidłowo). Jeżeli nie zajmujesz się administracją witry-
ny, będziesz musiał porozumieć się z administratorem systemu.

Niekompletna lub nieprawidłowa strona


Problem ten również tkwi w części HTML i powinien być odszukany w przedstawiony
powyżej sposób. Rysunek 15.1 pokazuje interesujący przykład (zależny od przeglądar-
ki, wynik pokazujemy w IE5).

Obejrzyj źródło HTML w komputerze klienta. Czasami źródło jest złamane w dziwnym
miejscu. Jeżeli źródło nie złamie się, wstaw tymczasowe komunikaty o błędach (w try-
bie HTML) w różnych częściach skryptu, aż do miejsca załamania, jak w przykładzie:
280 Cześć l * Podstawy PHP

Rysunek 15.1.
Niekompletny wynik
HTML

<HTML>
<HEADX/HEAD>
<BODY>Czy występuje zakłócenie na pozycji 1.?
<TABLE><TRXTD>Pozycji 2.? To, co mamy tutaj, to </TDx/TR>
<?php
SProblem = "problem z komunikacja";
print("SProblem"); ?>. Albo pozycja 3.?
</BODY>
</HTML>

Wynik tego testu przedstawia rysunek 15.2, pokazując, że tymczasowe komunikaty na


pozycjach 1. i 3. zostały wyświetlone w prawidłowych miejscach w stosunku do innych
elementów. Pozycja 2. jest nie na swoim miejscu, co sygnalizuje problem (brak znacz-
nika </TABLE>) w tym wierszu.

Rysunek 15.2.
Użycie tymczasowych
komunikatów o biedach
Rozdział 15. » Podstawowe pułapki PHP_____________________________281

Strona może być niekompletna z powodu braku przetwarzania PHP, jak w następują-
cym skrypcie:
<HTML>
<HEADX/HEAD> t
<BODY>To, co mamy tutaj, to jest
<?php
SProblem = "problem z komunikacją";
print("$Problem"); ?>.
</BODY>
</HTML>

Wynik działania tego skryptu przedstawiony jest na rysunku 15.3,

Rysunek 15.3.
Awaria
przy przetwarzaniu

Inaczej mówiąc, cała zawartość części HTML została pokazana, ale nie jest wyświetla-
na część PHP, ani żaden komunikat o błędzie. Jest to objaw niedziałania skryptu PHP
lub nieuaktywnienia PHP w serwerze WWW, na którym strona jest umieszczona (pro-
szę się nie śmiać, to się czasem zdarza).

Kod PHP pokazuje się w przeglądarce


Jeżeli zamiast wygenerowanej strony w przeglądarce zobaczysz kod PHP, prawdopo-
dobnie nie umieściłeś gdzieś otwierającego znacznika PHP. Zakładamy oczywiście, że
masz skonfigurowany i uruchomiony PHP, używasz właściwych znaczników. Jeżeli
nie, przeczytaj pierwszą część tego rozdziału.

Łatwo zapomnieć, że PHP traktuje włączane pliki jako HTML, a nie PHP, chyba że
umieścisz na początku tego pliku otwierający znacznik PHP. Załóżmy, że ładujemy na-
stępujący plik PHP:
282___________________________________________Część l » Podstawy PHP

<HTML><HEADX/HEADXBODY>
<?php include("secret.php");
secret_function(} ; ?>
</BODYX/HTML>

Włącza on plik secret.php, który wygląda następująco:


function secret_function ()
{
print("Sezamie otwórz się!"); ^
)

Efekt wykonania pliku pokazano na rysunku 15.4.

Rysunek 15.4.
Włączany PUP
pokazuje się jako HTML

Można to naprawić, dodając znaczniki PHP do włączanego pliku:


<?php
function secret_function (}
t
print("Sezamie otwórz się!");
)
?>

Niepowodzenie przy ładowaniu strony


Jeżeli PHP nie odszuka żądanego pliku, zobaczysz kilka rodzajów komunikatów
o błędach.

Nieodnaleziona strona
Jeżeli przeglądarka nie może odszukać utworzonej przez ciebie strony, a ostatnio instalo-
wałeś PHP, przejrzyj pierwszą część tego rozdziału. Jeżeli wyświetla się ten komunikat,
a inne pliki PHP otwierają się bez problemu, prawdopodobnie pomyliłeś się podając
ścieżkę lub nazwę pliku.
Rozdział 15. » Podstawowe pułapki PHP_____________________________283

Nieudane otwarcie pliku do włączenia


W wersji PHP dla Windows i US czasami pojawiały się komunikaty o błędach podobne
do tego:
Warning Failed opening 'C:\InetPub\wwwroot\asdf.php' for inclusion
(include_path='') in [no active file] on line 0

Jak się okazało, jest to komunikat błędu nieodnalezienia strony w NT. PHP nie załado-
wał nawet pierwszego wiersza aktywnego pliku, ponieważ nie ma aktywnego pliku; nie
ma aktywnego pliku, ponieważ nie można odszukać pliku o podanej nazwie.

Możliwe jest również otrzymanie tego komunikatu w przypadku nieprawidłowego


ustawienia uprawnień ładowanego pliku.

Błędy analizy składni


Najczęstszą kategorią błędów są te wynikłe z pomyłek przy wpisywaniu tekstu lub nie-
prawidłowej składni kodu PHP.

Komunikat błędu składni


Mimo że może wystąpić wiele rodzajów problemów z analizą składni, objaw jest zawsze ta-
ki sam: komunikat o błędzie analizy składni, którego przykład znajduje się na rysunku 15.5.

Rysunek 15.5.
Komunikat
o biedzie skladni

Z definicji błędy te występują w trybie PHP. PHP zwraca bardziej opisowe komunikaty
o błędach niż HTML, szczególnie użyteczny jest numer wiersza, w którym wystąpił błąd.

Najczęstsze przyczyny błędów składni opisane sąw kolejnych częściach tego rozdziału.
Wszystkie z nich mają małe znaczenie i łatwo je usunąć, a PHP ułatwia ich odszukanie.
Jednak wszystkie błędy składni powodują wyświetlenie tego samego komunikatu (poza
nazwą pliku i numerem wiersza). Fragmenty HTML znajdujące się w pliku nie zostaną
wyświetlone, nawet gdy znajdują się przed wierszem powodującym błąd.
284_________________________________________Część l » Podstawy PHP

Brakujący średnik
Jeżeli każda instrukcja PHP nie będzie zakończona średnikiem, wystąpi Wad składni.
W pierwszym wierszu poniższego fragmentu kodu brakuje średnika, dlatego przypisa-
nie do zmiennej nie będzie zakończone.
Mamy tutaj
<?php
^Problem ~ "mały kłopot"
print ("SProblem"); ?>.

Jeżeli spędzasz dużo czasu na uruchamianiu kodu PHP, nieocenioną


pomocą jest edytor, który potrafi przejść do wiersza o określonym
numerze. Zauważ, że pomyłka może znajdować się w wierszu, którego
numer wskazuje PHP, lub wcześniej, ale nigdy za nim. Jeżeli instruk-
cja zajmuje kilka wierszy, brakujący średnik nie spowoduje błędu, aż
do momentu próby analizy wszystkich wierszy będących częścią takiej
instrukcji.

Brak znaku $
Innym częstym problemem jest brak znaku $ poprzedzającego zmienną. Jeżeli opusz-
czono $ w wierszu z przypisaniem wartości do zmiennej, wystąpi błąd składni:
Mamy tutaj
<?php
Problem = "mały kłopot";
print ("$Problem"); ?>.

Jeżeli opuścimy go np. podczas wyświetlania wartości zmiennej, błąd się nie pojawi:
Mamy tutaj
<?php
SProblem = "mały kłopot";
print ("Problem"); ?>.

Otrzymamy wynik pokazany na rysunku 15.6.

Rysunek 15.6.
Brak znaku $
podczas wyświetlania
zmiennej
Rozdział 15. » Podstawowe pułapki PHP_____________________________285

Nieprawidłowa zmiana trybu


Inny rodzaj błędów występuje w przypadku nieprawidłowej zmiany trybu pracy. W przy-
padku braku prawidłowego zamknięcia bloku PHP, wystąpi błąd składni:
Mamy tutaj
<?php
$Problem = "mały kłopot";
print C"SProblem");.

Ten problem jest szczególnie często spotykany w przypadku krótkich bloków PHP.
Odwrotnie, jeżeli nie otwarłeś prawidłowo bloku PHP, cała jego zawartość zostanie wy-
świetlona jako HTML.

Bardziej skomplikowane błędy są generowane przy korzystaniu z minimalnego stylu


PHP, który bazuje na zmianie trybu. Poniższy fragment (w którym brakuje znacznika
?> po pierwszym nawiasie klamrowym) spowoduje błąd składni.
<?php if (!IsSet(Sstage))
(
Mamy tutaj
<?php
$Problem = "mały kłopot";
print ("$Problem"); ?>.
<?php
} else (
print ("$Stage"); )
?>

Błędy często pojawiają się przy łączeniu krótkich bloków HTML i PHP:
<FORM>
< I N P U T T Y P E = " T E X T " SIZE=15 N A M E = " F i r s t N a m e " VALUE="<?php p r i n t ( " S F i r s t N a m e " ) ;
t?>">
<INPUT TYPE="TEXT" SIZE=15 NAME="LastName" VALUE="<?php print("$LastName"); ?>">
<INPUT TYPE="TEXT" SIZE=10 NAME="PhoneNumber" VALUE="<?php print(SPhoneNumber");
^?>"
<INPUT TYPE="SUBMIT" NAME="Submit">
</FORM>

W wierszu z polem phoneNumber brakuje apostrofu oraz zamykającego nawiasu


znacznika INPUT. Przycisk nie zostanie wyświetlony.

Ten fragment kodu pokazuje, jak łatwo można przeoczyć element na skomplikowanej
stronie z wieloma małymi, ale ważnymi elementami. Można zredukować liczbę błędów,
używając dobrego edytora tekstu, bądź testując najpierw HTML, a później dodając do
niego PHP. Można oczywiście stosować oba te sposoby.

Nieoznaczone apostrofy
Innym rodzajem błędów składni, charakterystycznym dla maksymalnego stylu PHP, są
nieoznaczone apostrofy.
<?php
print("Piotr powiedział, /"Mamy tutaj");
$Problem = "mały kłopot\"";
print ("$Problem"); ?>.
286_________________________________________Część l » Podstawy PHP

W powyższym przypadku apostrof jest nieprawidłowo oznaczony przez znak / zamiast \.

Inne błędy składni


Problemy, które wymieniliśmy, nie wyczerpują listy możliwych błędów składni (np.
niedomknięte nawiasy i ciągi, operatory bez argumentów, warunki instrukcji sterują-
cych bez nawiasów itp.).

Czasami błąd składni zawarty jest w wyrażeniu, jednak nie potrafimy sami go zlokali-
zować. Jeżeli błąd sygnalizowany jest w ostatnim wierszu pliku, zwykle apostrof, na-
wias lub klamra nie zostały zamknięte. PHP próbował odnaleźć brakujący element,
przeszukując skrypt do końca.

Uprawnienia do plików
Większość systemów operacyjnych posiada pewne mechanizmy uprawnień, które okre-
ślają dostęp użytkowników do plików i katalogów. Serwer WWW uruchamiany jest ja-
ko jeden z użytkowników systemu, więc musi posiadać prawo do czytania wszystkich
plików, z których korzysta, także HTML i PHP.

Błąd HTTP nr 403


Jeśli w przeglądarce pojawia się błąd 403 (niektóre przeglądarki nie pokazują numeru
błędu), oznacza to, że nie masz uprawnień dostępu do podanej strony WWW.

Najczęstszym powodem tego błędu jest to, że nie ustawiłeś prawa do wykonywania pli-
ków w tym katalogu (Unix) lub nie uaktywniłeś wykonywania skryptów (Windows).
Pamiętaj, że skrypt PHP będzie uruchamiany przez innego użytkownika niż twój robo-
czy użytkownik. W systemie Unix PHP zwykle korzysta z konta nobody, który jest
ograniczony tylko do usługi HTTP. W systemie Windows każde żądanie HTTP jest re-
alizowane przez anonimowego gościa.

Brak dołączanych plików


PHP musi mieć możliwość załadowania wszystkich plików wymienianych w instruk-
cjach include ( ) lub require ( ) , oprócz załadowania plików źródłowych najwyższe-
go poziomu.
Rozdział 15. » Podstawowe pułapki PHP_______________________________287

Ostrzeżenie przy włączaniu pliku


Ten rodzaj błędu pokazany jest na rysunku 15.7.

Ł o
Ostrzeżenie iS* * *" Co"™™"*" U* _ _ _ _ _ _ _
przy włączaniu pliku l 4" •*>" 3> _ft ,*. a4 <-* fi & HfSl
J Back '•<•• >'; Rebad Home Seatch Netscape Pnnt Secmiły Shop '- 1 Jjp||
§ -»t' Bookmarks .^ location: [hHp7/localhosl/php/badjndude php J-J (fJjTWhafł Related
» y : .

Warning: Failed opening 'jakisplik php' for inclusion (include_path=") in


c:\helion\php4\site/bad_include.php on line 2

g»!i(j>;'["""""" """""" fDiSri_Tie'S"Dor«""'" "" ——•—•-— .,, ^ ^ ^3 _| ^ .

Problem jest powodowany przez umieszczenie gdzieś w skrypcie pliku do włączenia,


a PHP nie potrafi go odszukać. Sprawdź, czy ścieżka do włączanego pliku jest prawi-
dłowa, czy w nazwie są błędy literowe.

Komunikat pojawi się również, gdy skrypt spróbuje włączyć plik znajdujący się w in-
nym katalogu, a użytkownik PHP nie posiada uprawnień do tego katalogu. Ogólnie
rzecz ujmując, użytkownik serwera WWW (często nobody w systemie Unix) powinien
mieć uprawnienia do czytania i wykonywania katalogu (możesz wszystkim nadać te
uprawnienia, w systemie Unix 755). Możesz nadać wszystkim uprawnienia do czytania
plików z tego katalogu (np. 744).

Nieprzypisane zmienne
PHP różni się od wielu języków programowania, w których zmienne muszą być dekla-
rowane przed użyciem. Domyślnie PHP nie sprzeciwia się używaniu zmiennych, zanim
zostaną przypisane. Jeżeli zapomnisz przypisać wartości zmiennej, błędy bezpośrednie
nie wystąpią. Przestaw poziom raportowania błędów na poziom 15 poprzez wywołanie
error_reporting (15) albo error_reporting (E_ALL), jeżeli chcesz otrzymywać
ostrzeżenia. Niektóre objawy tego typu problemów przedstawiamy poniżej.

Zmienna nie pokazuje się w wynikowym ciągu


Jeżeli wbudowałeś zmienną w ciąg otoczony apostrofami ("Jak w $ten sposób")
i wypiszesz taki ciąg używając echo lub print, wartość zmiennej powinna się poja-
wić w ciągu. Jeżeli wartość nie występuje w wynikowym ciągu ("Jak w sposób"),
zmienna najprawdopodobniej nie została zainicjowana.
288_________________________________________Część l » Podstawy PHP

Jak zachowują się niezainicjowane zmienne


PHP konwertuje typy zmiennych w zależności od kontekstu użycia i również w przy-
padku niezainicjowanych zmiennych. Zmienna niezainicjowana jest interpretowana ja-
ko O, jeżeli jest używana jako liczba, jako „", jeżeli jest używana jako ciąg, FALSE,
jeżeli spodziewana była wartość logiczna i pusta tablica w przypadku użycia jej jako ta-
blicy. Poniższy przykład ilustruje skutki niezainicjowania dwóch zmiennych ($dru-
gi_ciag oraz $trzy). Wynik pokazany jest na rysunku 15.8.

Rysunek 15.8.
Efekt niezainicjowanych
zmiennych

<?php
$pierwszy_clag = "jeden";
$trzeci_ciag = "trzy";
$j eden = 1;
Sdwa = 2;
print("Matematyka jest łatwa jak $pierwszy_ciag, $drugi_ciag,
Strzeci_ciag!<BR>");
print("$pierwszy_ciag równa się $jeden<BR>");
print("$drugi_ciag równa się $dwa<BR>");
print("$trzeci_ciag równa się $trzy<BR>");
print("$pierwszy_ciag podzielone przez $drugi_ciag wynosi".
($jeden / $dwa). "<BR>");
print("$pierwszy_ciag podzielone przez $trzeci_ciag wynosi".
(Sjeden / $trzy). "<BR>");
?>

Problemy z wielkością liter


W przypadku zmiennych w PHP rozróżnia się są małe i wielkie litery, więc użycie tej
samej nazwy z inną wielkością liter spowoduje utworzenie różnych zmiennych. Jeżeli
wartość zostanie przypisana do zmiennej $Mississippi, to zmienna $mississippi
pozostanie nieprzypisana. Nie warto używać nazw zmiennych, które są trudne do po-
wtórzenia.

Problemy z zasięgiem
Dopóki nie korzysta się z definicji funkcji, określenie zasięgu zmiennych w PHP jest
łatwe. Moment zainicjowania zmiennej udostępniają, do czasu zmiany wartości. Jedy-
Rozdział 15. » Podstawowe pułapki PHP_______________________________289

nymi zmiennymi dostępnymi w ciele funkcji są parametry formalne oraz zmienne zade-
klarowane jako globalne. Dla przykładu, w poniższym kodzie zmienna $serial_no
nie została przekazana do funkcji, ani zadeklarowana jako globalna.
Sname = "Bond/ James Bond";
Srank = "Szpieg";
$serial_no = "007";

function Answer($name)
l
global Srank;
print("Nazwisko: Sname; Stopień: Srank; numer: $serial_no<BR>");
}
Answer(Sname);

Ponieważ zmienna $serial_no nie jest przypisana wewnątrz funkcji, wynik działania
tego fragmentu wygląda następująco:
Nazwisko: Bond, James Bond; Stopień: Szpieg; numer:

PHP nie ostrzega o użyciu nieprzypisanej zmiennej przy domyślnym


ustawieniu raportowania. Ustawienie to można tymczasowo zmienić.
Jest najszybszą metodą wykrywania problemów z niezainicjowanymi
zmiennymi. Umieść następujący wiersz na początku skryptu:
error-reporting(15);

PHP będzie teraz ostrzegał o każdym użyciu nieprzypisanej zmiennej.


Po zakończeniu uruchamiania skryptu wystarczy usunąć ten wiersz,
albo powrócić do domyślnego poziomu raportów za pomocą instrukcji:
error-reporting(7) ;

Jeżeli chcesz otrzymywać ostrzeżenia, możesz zmienić na stałe poziom


raportowania błędów na 15 w pliku php.ini. Poziomy raportowania oraz
inne zagadnienia dotyczące konfiguracji opisane są w rozdziale 32.

Problemy z funkcjami
Wiele problemów związanych z wywołaniami funkcji skutkuje błędem krytycznym, co
oznacza przerwanie wykonywania skryptu.

Wywołanie niezdefiniowanej funkcji


PHP próbuje wywołać funkcję, która nie została jeszcze zdefiniowana. Może to być
spowodowane błędem w nazwie funkcji (wbudowanej lub zdefiniowanej przez użyt-
kownika) lub brakiem definicji. Jeżeli za pomocą include lub require włączasz pliki
zawierające definicje funkcji, upewnij się, że ładujesz odpowiednie pliki.
290_________________________________________Część l » Podstawy PHP

Jeżeli problem dotyczy wyspecjalizowanej funkcji wbudowanej (na przykład związanej


z XML lub obliczeniami o dowolnej dokładności), prawdopodobnie odpowiednia grupa
funkcji nie została uaktywniona podczas tworzenia PHP. Jeżeli wszystkie funkcje ma-
tematyczne o dowolnej dokładności są niezdefiniowane, należy (w systemie Unix) po-
nownie skonfigurować i skompilować PHP, aby naprawić ten problem.
Czasami PHP próbuje wywołać funkcję, nie znając jej nazwy. Zdarza się to, jeżeli
w kodzie znajdują się wywołania w postaci $moja_funkcja ( ) (nazwa funkcji jest za-
pisana w zmiennej). Jeżeli nie próbujesz świadomie nadużyć możliwości wywoływania
funkcji o nazwie zależnej od zmiennej, błąd pojawia się po przypadkowym wpisaniu S
przed wywołaniem funkcji m o j a _ f u n k c j a ( ) . Ponieważ $moja_funkcja jest nieza-
inicjowaną zmienną, PHP interpretuje ją jako pusty ciąg, który nie jest nazwą zdefinio-
wanej funkcji (w efekcie pojawia się niejasny komunikat o błędzie).

Nie można ponownie zadeklarować funkcji


Jest to prosty błąd — gdzieś w kodzie masz dwie definicje funkcji o tych samych na-
zwach, a na to nie pozwala PHP. Upewnij się, że za pomocą include nie włączasz dwa
razy tego samego pliku z definicjami funkcji.

Nieprawidłowa liczba argumentów


Funkcja, wymieniona w komunikacie błędu, jest wywołana ze zbyt małą lub zbyt dużą
liczbą argumentów i nie potrafi ich obsłużyć. Zwykle dzieje się tak w przypadku funkcji
wbudowanej, ponieważ domyślnie nie jest sprawdzana liczba jej argumentów.

Błędy matematyczne
Wymienione problemy są związane z obliczeniami i numerycznymi typami danych.

Ostrzeżenie o dzieleniu przez zero


W kodzie znajduje się operacja dzielenia z mianownikiem wartości zero. Najczęściej
powodem takiej sytuacji jest niezainicjowana zmienna.
$licznik = 5;
$wynik = $licznik / Smianownik;

W powyższym przykładzie $mianownik jest niezainicjowany. Oczywiście jest możli-


we, że obliczenia dają w wyniku zerowy mianownik. Rozwiązaniem jest śledzenie ta-
kiej sytuacji i rozsądna obsługa. Na przykład:
Sllcznik = 5;
if ($mianownik != 0)
$wynik = $licznik / Smianownik;
else
print("Przykro mi, nie mogę tego zrobić<BR>");
Rozdział 15. » Podstawowe pułapki PHP_______________________________291

Niespodziewane wyniki działań


Czasami działania po prostu nie dają prawidłowych wyników. Jeżeli zdarza ci się coś ta-
kiego, sprawdź każde wyrażenie arytmetyczne, szukając niezainicjowanych zmiennych
(będą interpretowane jako zero) oraz pomyłek w kolejności działań. Jeżeli nie jesteś pe-
wien kolejności, w jakiej wykonane będą działania, ujmij odpowiednie fragmenty wyra-
żenia w nawiasy.

NaN (lub NAŃ)


Jeżeli nie spotkałeś się z tym strasznym skrótem, wyjaśniamy, oznacza on, że jakaś
funkcja matematyczna wyszła poza zakres lub podana jest niewłaściwa wartość argu-
mentów wejściowych. „NAN" pochodzi od ,JVot a Number" (nie liczba) i ma kilka spe-
cjalnych własności. Co się stanie, jeżeli spróbujemy obliczyć arcus cosinus z 45, a arcus
cosinus jest zdefiniowany dla wartości z zakresu -l do l:
$value = acos(45);
print("Wynik acos wynosi $value<BR>");
print("Typ ". gettype(Svalue). "<BR>");
$value_2 = $value + 5;
print("Odziedziczony wynik to $value_2<BR>") ;
print("Typ ". gettype($value_2). "<BR>");
if (Svalue == Svalue)
print("Przynajmniej to ma sens<BR>");
else
print("Wartość ta nie jest nawet równa samej sobie!<BR>");

Wynik działania tego fragmentu wygląda następująco:


Wynik acos wynosi NAN
Typ double
Odziedziczony wynik to NAN
Typ double
Wartość ta nie jest nawet równa samej sobie!

Niestety NAN jest liczbą, przynajmniej w sensie typu numerycznego w PHP. W na-
szym przykładzie PHP wskazuje typ NAN jako double, a nie string. Wartość ta „za-
razi" inne wartości, jeżeli zostanie użyta w operacjach arytmetycznych. Jest to
zamierzone działanie, a nie błąd. W przypadku skomplikowanych obliczeń, które ko-
niecznie muszą być prawidłowe, lepiej całe wyrażenie oznaczyć jako niewiarygodnie,
zamiast dopuszczać do błędnej wartości jednej z części wyrażenia. Każde wyrażenie lo-
giczne, w którym bierze udział wartość NAN, jest fałszywe. NAN nie jest ani mniejsze,
ani większe, ani równe żadnej innej liczbie, włączając w to samą siebie. Jest zawsze
różne (!=) od wszystkich liczb (wartość NAN nie jest wyjątkiem PHP, jest części
standardu IEEE dotyczącego arytmetyki zmiennoprzecinkowej, który został zaimplc-
mentowany w funkcjach C, stanowiących podstawę PHP).

Taki błąd może być trudny do zlokalizowania. Najlepszym sposobem zlokalizowania


źródła problemu jest poszukiwanie wartości NAN za pomocą diagnostycznych instruk-
cji print, szczególnie że wyrażenia logiczne dają nieintuicyjne wyniki. Jeżeli chcesz
sprawdzać, czy wartość ma wartość NAN, istnieje na to sposób:
292_________________________________________Część l » Podstawy PHP

function is_nan( $value)


f
return (Svalue != $value);
l

Funkcja ta wykorzystuje do identyfikacji dziwne własności wartości NAŃ.

Przekroczenie czasu oczekiwania


Każdy proces ładowania pliku może być przerwany z powodu przekroczenia czasu
oczekiwania. Jednak nie powinno się to zdarzać zbyt często w twoim lokalnym serwe-
rze używanym do programowania!

Interesującą przyczyną przekroczenia czasu oczekiwania są nieskończone pętle, trudno


je zlokalizować, np.:
// obliczenie silni z 10
$Fact = 1;
for( SIndex = l; SIndex <= 10; $index++)
SFact *= SIndex;

Kod ten pokazuje interakcję pętli i pomyłki w wielkości liter — pisany z małej litery
$ index jest zwiększany; nie ma nic wspólnego ze zmienną $ index używaną w wa-
runku końca pętli. Pętla nigdy się nie skończy.

Podsumowanie
W tabeli 15.1 zamieściliśmy podsumowanie pułapek omówionych w tym rozdziale,
wraz z opisem objawów i powodów wystąpienia. Dodaliśmy również kilka sugestii,
w jaki sposób można poprawić najczęściej spotykane błędy.

Tabela 15.1.
Od objawów do przyczyn

Objaw Możliwe powody Sugestia

Zawartość pliku wyświetlona Nie została wywołana maszyna Upewnij się, że pobierasz plik przez
w przeglądarce (nowa PHP, być może z powodu serwer WWW, zarówno przez localhost
instalacja) otwarcia pliku poprzez system (http://localhost/[ścieżka]), gdy testujesz
plików, a nie przez żądanie do skrypt na serwerze, jak i przez pełny
serwera URL (http://www. strona, com/ [ścieżka])
Rozdział 15. » Podstawowe pułapki PHP_____________________________293

Tabela 15.1.
Od objawów do przyczyn (ciąg dalszy)

Objaw Możliwe powody Sugestia

Bloki PHP pojawiają się jako PHP jest niewłaściwie Sprawdź konfigurację serwera WWW
tekst w HTML. Przeglądarka wywoływany. Serwer WWW oraz plik konfiguracji PHP (php.ini)
proponuje zapisanie pliku może być niewłaściwie
(nowa instalacja) skonfigurowany dla przetwarzania
określonych typów plików
(np.: .php) przez PHP. Może
występować problem z położeniem
lub zawartością pliku php.ini
Serwer nie jest dostępny Często z powodu błędnej Spróbuj załadować czysty plik HTML
lub strona nie może być konfiguracji serwera WWW, z rozszerzeniem, które nie jest
wyświetlona (nowa Internetu lub DNS, bardzo rzadko skojarzone z PHP (np.: .html), aby
instalacja) z powodu PHP wykluczyć problem z PHP. Spróbuj
pobrać ten sam plik, korzystając
z adresu IP zamiast nazwy. Jeżeli
zadziała, problem jest związany z DNS
Pusta strona Zwykle źle skonstruowany kod Sprawdź źródło HTML
HTML (również tworzony w przeglądarce. Szczególnie zwróć
przez PHP) uwagę na niezamknięte znaczniki
<TABLE>, <FORM> lub formularze
<SELECT>

Niekompletna strona lub Zwykle źle skonstruowany kod Sprawdź źródło HTML
nieoczekiwana zawartość HTML (również tworzony w przeglądarce. Spróbuj odszukać
przez PHP) źródło problemów za pomocą
komunikatów diagnostycznych
(w PHP lub HTML)
Kod PHP pokazuje się Jeżeli maszyna PHP jest Sprawdź znaczniki otwierające
w przeglądarce zainstalowana i działa prawidłowo, i zamykające blok PHP i upewnij się,
jest to zwykle związane z brakiem że włączane pliki z kodem PHP mają
znacznika rozpoczynającego właściwe znaczniki otwierające
blok PHP i zamykające
Nieodnaleziona strona Zwykle oznacza to, że nazwa lub Dokładnie sprawdź nazwę i ścieżkę
ścieżka do pliku PHP są do pliku
nieprawidłowe
Nieudane otwarcie pliku do Jak wyżej, w niektórych Dokładnie sprawdź nazwę i ścieżkę
włączenia konfiguracjach NT do pliku
Komunikaty o błędach Bardzo różne przyczyny: brakujące Odszukaj wiersz z błędem w pliku
składni średniki, zmienne bez znaku $, PHP i przyczynę błędu w tym wierszu
nieoznaczone apostrofy, lub bezpośrednio go poprzedzających.
niezamknięte apostrofy, klamry Jeżeli błąd wskazywany jest
lub nawiasy; HTML w ostatnim wierszu pliku, szukaj
interpretowany jako PHP wcześniej niezamkniętego apostrofu,
nawiasu lub klamry
294_________________________________________Część l » Podstawy PHP

Tabela 15.1.
Od objawów do przyczyn (ciąg dalszy)

Objaw Możliwe powody Sugestia

Błąd HTTP numer 403 Nieprawidłowe uprawnienia Sprawdź uprawnienia do samego pliku oraz
do pliku katalogów w ścieżce
Ostrzeżenie przy Z jakiegoś powodu PHP nie Sprawdź, czy plik istnieje, poprawność nazwy
włączaniu pliku może załadować pliku oraz (w systemie Unix) wielkość liter
podanego w wyrażeniu w nazwach. Upewnij się, czy uprawnienia
include pozwalają na odczytanie pliku
Wartość zmiennej nie Zmienna nie została Sprawdź, czy zainicjowałeś zmienną przed
pojawia się zainicjowana, więc jej instrukcją print; sprawdź wielkość liter. Upewnij
w wynikowym ciągu wartość jest wstawiona się, że nie wklejasz do ciągu obiektów lub
w wynikowy ciąg jako wielowymiarowych tablic. Możesz również użyć
ciąg pusty wyrażenia e r r o r - r e p o r t i n g ( 1 5 ) , aby
włączyć ostrzeżenia przy użyciu
niezainicjowanej zmiennej
Zmienna numeryczna ma Często, jeżeli zmienna nie Patrz poprzednia porada
niespodziewanie została zainicjowana
wartość zero
Wywołanie Funkcja ( ) nie została Jeżeli próbujesz wywołać własną funkcję,
niezdefiniowanej funkcji zdefiniowana przed sprawdź, czyjej definicja (lub włączenie pliku
funkcja ( ) wywołaniem z jej definicją) znajduje się przed użyciem. Jeżeli
wywołujesz wbudowaną funkcję, sprawdź, czy
poprawnie wpisałeś jej nazwę. Jeżeli jest
prawidłowa, sprawdź, czy „rodzina" funkcji jest
włączona przy konfigurowaniu PHP
(np. wszystkie funkcje XML będą działały,
albo żadna)
Wywołanie Wywołane zostało wyrażenie Jeżeli chciałeś użyć zmiennych nazw funkcji,
niezdefiniowanej w postaci $ f u n k c j a ( } , dodaj (lub popraw) przypisanie do zmiennej
funkcji ( ) a Sfunkcja nie została $ f u n k c j a . Jeżeli chcesz wywołać funkcję
zainicjowana nazwą f u n k c j a ( ) , usuń znak $
zdefiniowanej funkcji
Nie można powtórnie F u n k c j a ( ) jest Szukaj podwójnej definicji funkcji w pliku PHP
zadeklarować funkcji zdefiniowana dwukrotnie lub dwukrotnego włączania pliku z definicją
funkcja ( ) w czasie wykonywania
skryptu
Nieprawidłowa liczba Funkcja (zwykle Porównaj wywołanie funkcji z definicją
parametrów wbudowana) jest wywołana w podręczniku (http://www.php.net)
z nieprawidłową liczbą
argumentów
Ostrzeżenie o dzieleniu Operator / po prawej stronie Zainicjuj zmienną powodującą błąd. Jeżeli
przez zero ma wartość zero. zdarzy wartość mianownika równa zero, dodaj
Niezainicjowana zmienna sprawdzenie i odpowiednio obsługuj tę sytuację
w mianowniku
Rozdział 15. » Podstawowe pułapki PHP_____________________________295

Tabela 15.1.
Od objawów do przyczyn (ciąg dalszy)

Objaw Możliwe powody Sugestia

Niespodziewane wyniki Często w przypadku użycia Szukaj niezainicjowanych zmiennych (patrz


działań arytmetycznych niezainicjowanej zmiennej poprzednia porada) i upewnij się, że nawiasy
w wyrażeniach arytmetycznych znaj duj ą się w odpowiednich miejscach
Wartość NAŃ Wbudowana funkcja Szukaj wstecz pierwszej operacji dającej
matematyczna wywołana wartość NAŃ. Sprawdź wartość obliczeń za
z wartością przekraczającą pomocą funkcji print; sprawdź, czy wartość
dopuszczalny zakres. Jeżeli nie jest równa samej sobie
wynik tej funkcji użyty zostanie
w obliczeniach, ich wynik
również będzie równy NAŃ
Strona ładuje się bardzo Opóźnienia Internetu, duże Sprawdź, czy inne witryny mają te same
długo, przeglądarka obciążenie serwera, intensywne problemy. Sprawdź pętle w twoim kodzie;
przerywa j ej ładowanie obliczenia w kodzie PHP lub sprawdź, czy występują błędy mogące
z powodu przekroczenia nieskończona pętla spowodować nieskończoną pętlę
czasu oczekiwania
296_________________________________________Część l » Podstawy PHP
Część 2
PHP i bazy danych
Rozdział 16.
Wybór bazy danych
dla PHP
W tym rozdziale:
** Dlaczego używamy baz danych?
* Wybór bazy danych
* Obsługiwane bazy danych
* Projekt bazy danych

Bazy danych i PHP są ze sobą ściśle połączone, jak bułka z masłem, Trynidad i Tobago,
Flip i Flap.

Czym właściwie jest WWW? To obszerny magazyn informacji dostępnej dla mniejszej
bądź większej publiczności. Znajdują się tutaj nie tylko małe „broszurowe" witryny, ale
również większe i częściej uaktualniane źródła danych.

Prawdopodobnie jedną z największych przewag PHP w stosunku do innych podobnych


produktów jest nieskrępowana swoboda wyboru bazy danych i łatwość podłączenia
z nią. PHP posiada własne połączenia z wieloma popularnymi bazami danych, zarówno
komercyjnymi, jak i open source. Prawie każda baza, której API opublikowano, może
być dołączona. Dla nieobsługiwanych baz danych pozostaje połączenie ODBC.

Czemu używamy baz danych?


Jeżeli zacząłeś używać PHP, prędzej czy później będziesz potrzebował również bazy
danych — prawdopodobnie prędzej. Nawet w przypadku małych projektów, na przy-
kład osobistego dziennika sieciowego, możesz pomyśleć nad zaletami zastosowania ba-
zy danych w zamian statycznych stron lub włączanych plików tekstowych.
300______________________________________Część II » PHP i bazy danych

Unikanie redundancji
Tworzenie stron na bieżąco przy użyciu PHP, szablonów i bazy danych jest bardzo
uzależniające. Jeżeli nauczysz się tego, nigdy nie wrócisz do tworzenia statycznych wi-
tryn HTML, niezależnie od rozmiaru. Możesz tworzyć nieskończenie wiele stron nakła-
dem pracy potrzebnym na oprogramowanie jednej strony. Zmieniając jedną stronę,
zmieniasz wszystkie.

Do niedawna bazy danych były zbyt kosztowne dla większości niekomercyjnych zasto-
sowań. Jednak w chwili obecnej mamy dostęp do wielu świetnych i tanich baz danych.
W odpowiedzi na tę zdrową konkurencję zmieniają się warunki licencji niektórych ko-
mercyjnych produktów.

Martwisz się o wydajność? Prawdąjest, że niektóre bazy danych są szybsze od innych.


Prawdą jest, że jeżeli potrzebujesz bazy dla tylko jednego rodzaju danych, będzie pra-
wie tak samo kosztowna jak umieszczenie wszystkiego w bazie danych. Większość cza-
su poświęconego na odczyt z bazy danych skupia się na ustanowieniu połączenia
z bazą. Jeżeli musisz to zrobić, aby uzyskać nazwisko bądź tytuł, to wczytanie dodat-
kowo kilku tysięcy słów jest nieomal niezauważalne.

Unikanie nudnego programowania


Istnieje kilka zadań, które mogą być wykonane w PHP, ale lepiej ich unikać, ponieważ
łatwo się można wplątać w ryzykowne rozwiązania programowe.

Załóżmy, że piszesz nowelę hipertekstową w odcinkach. Każdy epizod jest umieszczony


w numerowanym pliku tekstowym włączanym przez PHP do szablonu. Chcesz auto-
matycznie generować na każdej stronie łącza Następny i Poprzedni, aby można było
odczytać całość w porządku chronologicznym. Łatwo można użyć PHP do odnalezienia
poprzedniego numeru pliku, ale próba odszukania następnego może szybko doprowadzić
do nieskończonej pętli, ponieważ łatwiej jest sprawdzić, czy coś istnieje, niż czy nie ist-
nieje. Jeżeli umieścisz rozdziały noweli w bazie danych, całe zadanie stanie się trywialne.

Morał jest następujący: mimo że bazy danych mają ograniczone możliwości programo-
wania, są jednak optymalne dla niektórych zastosowań, więc należy korzystać z tych
możliwości, gdzie tylko się da.

Szukanie
Mimo że jest możliwe przeszukiwanie wielu plików tekstowych w poszukiwaniu okre-
ślonego ciągu (szczególnie w systemie Unix), nie jest to rzecz, którą programiści
WWW robią codziennie. Już w przypadku kilkuset plików proces staje się powolny
i trudny do zarządzania. Bazy danych są stworzone po to, aby ułatwić tego typu zada-
nia. Za pomocą jednej komendy możesz odszukać informację, od identyfikatora nume-
rycznego do dużego bloku tekstu lub obrazka w formacie JPG.
Rozdział 16. » Wybór bazy danych dla PHP______________________________301

W niektórych przypadkach informacja nabiera wartości, jeżeli umieścimy ją w bazie


danych. Na przykład: niewielu ludzi czyta długie listy reżyserów i ich filmów, ale może
czasami przeszukać bazę danych w poszukiwaniu takiej informacji. Musisz się zgodzić,
l< że możliwość szukania ma w tym wypadku większą wagę niż sama informacja.

Bezpieczeństwo
Baza danych tworzy dodatkowy system bezpieczeństwa, jeżeli skorzystamy z jej hasła
lub haseł.

Załóżmy, że używamy PHP do prowadzenia dziennika wypełnionego osobistymi tajemni-


cami i przemyśleniami. Ponieważ nie prowadzisz serwera WWW, chcesz przeglądać
i zmieniać zawartość dziennika poprzez HTTP, korzystając z zabezpieczenia hasłem.
Jeżeli PHP ma wprowadzać nowe wpisy do pliku tekstowego włączanego do szablonu,
musisz dać użytkownikowi serwisu HTTP (zwykle Nobody lub Everybody) prawo do
zapisu w twoim katalogu. Nie jest to dobre rozwiązanie. Jeżeli PHP będzie zapisywał
dane w bazie danych zamiast w pliku tekstowym, możesz zachować prawa tylko do od-
czytu katalogu i dodatkowo zapytać o hasło przed zmianą bazy danych.

Prześledźmy jeszcze przykład witryny z wieloma zwykłymi użytkownikami, mniejszą


liczbą osób wpisujących teksty oraz kilkoma redaktorami. Można ustawić poziom bezpie-
czeństwa dla każdej grupy tak, aby zwykli użytkownicy mogli tylko oglądać zawartość
bazy danych (sformatowaną jako strony WWW), wpisujący teksty mogą przeglądać
i zmieniać tylko swoje teksty, a redaktorzy mogą przeglądać, zmieniać i kasować wszyst-
ko w witrynie.

Architektura wielowarstwowa
Do tej pory skupialiśmy się na tak zwanej architekturze dwuwarstwowej: PHP pobierał
surowe dane z jakiegoś rodzaju systemu pamięci masowej i zmieniał je na postać
HTML. Jednym z założeń PHP było, że ma on służyć jako „klej" w architekturze trój-
warstwowej oraz wielowarstwowej. Jeżeli masz cokolwiek bardziej skomplikowanego
niż zwykła architektura dwuwarstwowa, naprawdę potrzebujesz bazy danych.

Architektura wielowarstwowa to określona liczba podsystemów programowych połą-


czonych z jednej strony do witryny WWW a z drugiej strony do jednej lub więcej baz
danych. Jednym z zastosowań architektury wielowarstwowej jest duża witryna handlu
elektronicznego, w której koszyki na zakupy są połączone z systemem zamówień, który
jest skojarzony z systemem dostaw. Poza tym mamy bazę produktów, bazę klientów,
system obciążania kart kredytowych, system odpowiedzi na pytania, automatyczny
system rekomendacji, narzędzia analizy logów, bazę wiedzy dla centrum informacji te-
lefonicznej i kto wie, co jeszcze! Z tych powodów potrzeba zaawansowanych możliwo-
ści, czym za chwilę się zajmiemy.
302______________________________________Część II » PHP i bazy danych

Wybór bazy danych


Mimo że bazy danych (również relacyjne) już bardzo długo są dostępne na rynku, to do
niedawna były bardzo drogie lub oferowały ograniczony zestaw funkcji. Dlatego nawet
wielu doświadczonych programistów nigdy nie miało okazji nauczyć się wybierać bazę
danych dla określonych zadań. Z tego powodu uważamy za słuszne opisać podstawowe
czynniki, które powinny wpływać na taką decyzję.

Możesz nie mieć wyboru


W praktyce możesz mieć niewielki wybór. Wielu ludzi szuka najpierw najszybszej
metody umieszczenia swojej firmowej bazy danych w Internecie, a nie użycia języka
skryptowego na witrynie WWW.

Co więcej, system operacyjny, serwer WWW i języki programowania mogą nie pozo-
stawić żadnego wyboru. Aplikacja napisana w języku Java na dużym komputerze
z systemem Unix nie będzie działała dobrze z Microsoft SQL Server (teoretycznie jest
to możliwe, ale w praktyce źle się to skończy i lepiej poszukać innego rozwiązania). Im
większy system dostaniesz, tym bardziej będziesz ograniczony przez czyjeś wcześniej-
sze decyzje.
Jednak mamy dobrą nowinę. PHP został wyposażony w możliwość obsługi wielu baz
danych i innych serwerów usługowych. Pozwala to na połączenie ciągle rosnącej ar-
chitektury w jeden system informacyjny. Niektóre funkcje w PHP istnieją tylko po to,
abyś mógł łatwo przenieść dane do nowocześniejszej bazy.

Plikowe, relacyjne i obiektowo-relacyjne bazy danych


Bazy danych są jak wyposażenie kuchni: im prostsze jest narzędzie, tym bardziej uta-
lentowany musi być jego użytkownik, aby osiągnął bardzo dobre wyniki. Doskonały
kucharz zrobi wyśmienity posiłek używając ostrego noża i kilku starych garnków i pa-
telni, a amatorzy muszą sięgnąć po robot kuchenny i Termomix.

Podobnie jest z bazami danych. Utyskiwania użytkowników na niedostatki jednej lub


drugiej bazy danych mogą być zabawne, ale oprogramowanie to częściej niż inne obna-
ża ich braki. Wystarczy powiedzieć, że wiele perełek oprogramowania leży w najprost-
szych tablicach mieszających, a wiele paskudnych błędów znajduje się w produktach
bazujących na najnowszej i najlepszej obiektowej bazie danych z obsługą Javy.

Za pomocą PHP możesz obsługiwać trzy główne rodzaje baz danych: plikowe, relacyj-
ne i obiektowo-relacyjne.

Plikowe lub mieszające bazy danych, takie jak Gnu DBM i Berkeley DB (tzw. Sleepy-
cat DB2) są zwykle używane przez inne programy, takie jak serwery pocztowe. Po-
zwalają na najszybsze zapisywanie i odszukiwanie danych (pary użytkownik i hasło,
przesyłki e-mail opatrzone datą).

J
Rozdział 16. » Wybór bazy danych dla PHP____________________________303

Bazy danych tego typu nie tworzą same reprezentacji bardziej skomplikowanych
związków pomiędzy danymi. Program korzystający z bazy danych musi to realizować.
Mimo to wyniki mogą być zadziwiające; wszystko zależy od umiejętności programisty.

Obecnie najczęściej stosowane są relacyjne bazy danych. Istnieją różne opinie na temat
warunków, jakie musi spełniać baza danych, aby mogła być nazwana relacyjną, więc nie
damy się wciągnąć w akademicką dyskusję i nie podamy żadnej definicji. W zamian mo-
żemy stwierdzić, że baza danych, z którą można porozumiewać się w języku SQL, jest
bazą relacyjną. Większość popularnych baz obsługiwanych przez PHP to bazy relacyjne.

Jednak relacyjność jest relatywna. Niektóre bardzo popularne komercyjne bazy danych,
takie jak Filemaker Pro czy Microsoft Access, nie były projektowane jako podstawa
witryny WWW. Mimo że posiadaj ą obsługę ODBC, dzięki czemu PHP może teoretycz-
nie pobierać z nich dane, są głównie nastawione na łatwość użycia, a nie na wydajność.
Dużo większym problemem jest to, że użytkownicy tych produktów odrzucają korzyści
płynące z relacyjnego modelu i wolą powtarzać dane w każdej pozycji, zamiast utwo-
rzyć osobną tabelę obrazującą relację. Dodatkowo bazy takie zwykle nie posiadają bar-
dziej zaawansowanych funkcji, jak wielowątkowość czy blokady. Jednak są ludzie
próbujący używać Accessa i PHP, ale nie tworzą publicznych witryn obsługujących
wielu użytkowników. Znamy również programistów, którzy używają programów Ac-
cess lub Filemaker Pro jako narzędzi programistycznych na laptopach; mogą w ten spo-
sób programować w samolocie. Są to jednak zawsze projekty przenoszone później
z tych „półrelacyjnych" baz danych do dużych systemów.

Istniej ą również obiektowe lub obiektowo-relacyjne bazy, nowe i ciągle rozwijające się
modele dostępu do danych. Obiektowe bazy danych mają na celu łatwiejsze współ-
działanie z obiektowymi językami programowania. Obiektowo-relacyjne bazy danych
to hybryda używana do typów danych (astronomicznych lub genetycznych), których
zwykłe bazy nie obsługują dobrze. PHP nie jest i prawdopodobnie nie będzie w pełni
obiektowy, więc trudno wyobrazić sobie powód wyboru PHP jako języka skryptowego
do obsługi takich systemów baz. Dobre omówienie tych typów baz znajduje się pod ad-
resem: http://www.cai.com/products/jasmine/analyst/idc/14821E.htm/.

ODBC/JDBC kontra własne API


Istnieją dwa podstawowe standardowe API, pozwalające na dostęp do baz danych:
Open Database Connectivity (ODBC) oraz Java Database Connectivity (JDBC). ODBC
jest ściśle związane z firmą Microsoft, a JDBC z Sun. Również inne firmy udostępniają
te standardy w swoich produktach jako uzupełnienie firmowych sterowników, stosowa-
nych we własnych programach.

ODBC i JDBC w mniejszym lub większym stopniu wzajemnie się wykluczają. Istnieje
wprawdzie produkt o nazwie ODBC-JDBC bridge, pozwalający programom napisanym
w Javie na dostęp do baz danych ODBC, ale jest bardzo powolny. Istnieją inne sterow-
niki, które wykonują to zadanie o wiele szybciej.
304____________________________________Część II » PHP i bazy danych

Istniej ą również bazy danych, z którymi programy klienckie komunikują się za pomocą
własnego API, a nie poprzez ODBC czy JDBC. Działa to o wiele szybciej z powodu
mniejszej liczby warstw pośredniczących. Większość baz dostępnych na warunkach
open source działa właśnie w ten sposób. Niektóre z nich posiadają sterowniki ODBC
lub JDBC. PHP może na przykład komunikować się z bazą My SQL poprzez własne
API, natomiast program w Javie poprzez JDBC. Zanim wybierzesz schemat, upewnij
się, że masz odpowiednie, pewne i wydajne sterowniki.

Zmiana bazy danych


Mimo że ODBC jest wolniejsze niż własne API, posiada ogromną zaletę: jest standar-
dem otwartym. Dzięki temu kod PHP korzystający z instrukcji ODBC będzie prawdo-
podobnie działał z każdą bazą danych obsługującą ODBC. Własność ta jest bardzo
przydatna, jeżeli musisz rozpocząć projekt na niedającej się skalować bazie danych (np.
Microsoft Access zamieniany później na dużo wydajniejszą bazę danych). Mimo że oba
produkty są dobre w swoich niszach, potrzeba wiele pracy, aby przenieść produkt
z niewielkiej bazy, np. Mini SQL (tzw. mSQL) na korporacyjny serwer (Oracle). Dobry
programista, który ma czas, napisze program w sposób umożliwiający jego łatwe prze-
niesienie na inny serwer, ale niedoświadczony lub popędzany programista nie będzie
w stanie tego zrobić.

Przegląd zaawansowanych funkcji


Zajmiemy się teraz funkcjami specyficznymi dla baz danych SQL.

GUI
Bazy danych są uzależnione od narzędzi interfejsu użytkownika. Istnieje duży wybór
rozwiązań: od prostych programów liniowych do potężnych środowisk programistycz-
nych na podstawie języka Java. Za to, co wybierzesz, zapłacisz nie tylko pieniędzmi, ale
także wydajnością, dlatego szukaj najmniejszego produktu spełniającego twoje potrzeby
(środowiska graficzne spowalniaj ą pracę programów).

Jedną z najtańszych alternatyw narzędzi interfejsu użytkownika jest użycie interfejsu


WWW. Istnieje na przykład kilka bezpłatnych narzędzi do bazy danych MySQL, na
podstawie WWW, które możesz znaleźć na serwerze Freshmeat (www.freshmeat.net).

Podzapytania
Podzapytanie jest wbudowanym wyrażeniem SELECT, np.:
SELECT * FROM tablel WHERE id IN (SELECT id FROM table2);
Rozdział 16. » Wybór bazy danych dla PHP______________________________305

lub
INSERT INTO table2[(co!2, co!3, co!17)]
SELECT lastname, firstname, state FROM tablel WHERE col5 = NULL;

Istnieją sposoby obejścia braku podzapytań, chociaż nie wszyscy ich potrzebują; mogą
zaoszczędzić nieco czasu, jeżeli konsekwentnie będziesz tworzył duże wyrażenia SE-
LECT, INSERT lub DELETE.

Złożone złączenia
Złączenie jest sposobem na szukanie danych w tablicy, z użyciem wspólnych wartości
w wielu tablicach. Najprostszą formą złączenia jest:
SELECT textfield FROM tablel, table2 WHERE tablel.id=table2.id;

Zapytanie to zwróci zawartość wszystkich wierszy, w których wartości ID są takie same


w obu tablicach. Istnieją również bardziej specyficzne i złożone typy złączeń, włączając
lewe i prawe, proste lub krzyżowe, wewnętrzne i zewnętrzne oraz naturalne.
SELECT * FROM tablel LEFT JOIN table2 ON tablel. id=table2 . id
LEFT JOIN tables ON table2.id=table3.id;

Złączenia są bardzo wygodne i oszczędzają wiele wysiłku, czasami są trzonem progra-


mu; w praktyce jednak rzadko potrzeba ich wymyślnych rodzajów. Nie należy więc od-
rzucać bazy danych z powodu braku lewego zewnętrznego złączenia.

Wielowątkowość i blokowanie
Wielowątkowość i blokowanie są bardzo ważne w wielowarstwowych witrynach, są także
przydatne w przypadku witryn dwuwarstwowych. Zapewniaj ą prawo do zapisu tylko jednej
transakcji w danym czasie, powodując, że dwa odwołania do bazy nie zderzą się ze sobą.

Przykładem, który wyraźnie pokazuje wartość blokowania, jest witryna WWW sprze-
dająca bilety na oblegany koncert (z numerowanymi miejscami). Oczywiście dwóch lu-
dzi nie może kupić biletu na to samo miejsce. Baza danych musi w jakiś sposób
rozpoznawać indywidualne żądania i pozwolić tylko jednemu użytkownikowi (lub wąt-
kowi) na wprowadzanie zmian w określonym momencie.

Jeżeli nie jesteś pewien, że w przypadku twojego projektu tylko jeden użytkownik może
modyfikować dane (na przykład dziennik sieciowy), musisz bardzo uważnie zatwier-
dzać dane w jednowątkowych bazach danych.

Transakcje
Jest to schemat operowania na danych zwiększający ich integralność.

Paradygmat transakcyjny polega na operacjach zatwierdzania i wycofywania. Mówiąc


w skrócie, transakcja, która skończyła się pomyślnie, jest zatwierdzana i zapisywana do
306____________________________________Część II » PHP i bazy danych

bazy danych. Nieudana jest usuwana, przywracany jest stan bazy sprzed transakcji. Sys-
tem handlu elektronicznego może użyć wycofania w przypadku odrzucenia karty kredy-
towej klienta, aby powrócić do stanu przed dokonaniem zakupów. Mechanizm wycofań
jest również użyteczny w przypadku uszkodzenia danych w czasie awarii serwera.

Alternatywny projekt integralności danych nazywany jest „atomowym". Zwolennicy


paradygmatu atomowego twierdzą, że jest on dużo szybszy i prawie tak samo bezpiecz-
ny, ale wielu użytkowników transakcyjnych baz danych woli pozostać przy bardziej
komfortowym rozwiązaniu.

Procedury i wyzwalacze
Procedury są zapisanymi i skompilowanymi zapytaniami lub podprogramami. Typową
procedurą będzie np. procedura wybierająca adresy e-mail wszystkich klientów, którzy
złożyli zamówienie określonego dnia. Jeżeli ciągle używasz tego samego wyrażenia
SELECT, procedura będzie wygodnym rozwiązaniem.

Wyzwalacze to procedury uruchamiane w przypadku, gdy w bazie danych wystąpi


określone zdarzenie. W zależności od bazy danych możesz napisać wyzwalacz, który
wyśle e-mailem stan konta do klientów i będzie to powtarzał np. każdej niedzieli o pół-
nocy. Innym użytecznym wyzwalaczem może być np. wysyłający e-mail do admini-
stratora w przypadku wystąpienia błędu. Stosunkowo niewiele baz danych pozwala na
tworzenie wyzwalaczy, ponieważ wymagają wydajnego języka i dodatkowych cykli
procesora na śledzenie zdarzeń.

Klucze obce i więzy integralności


Relacyjna struktura bazy danych wynika często ze sposobu, w jaki pola w jednej tabeli
odwołują się do identyfikatorów w drugiej, ale trudno zachować tę strukturę w trakcie
wprowadzania zmian. Jednym sposobem, w jaki baza danych pomaga w utrzymaniu
spójności danych, może być mechanizm usuwania kaskadowego, który kasuje wszyst-
kie rekordy zależne od usuwanego rekordu z innej tabeli (jest to czasami realizowane za
pomocą wyzwalacza). Jeżeli usuwasz rekord hospitowanego pacjenta, możesz skasować
wszystkie rekordy w tabeli jego wizyt. Innym sposobem jest zablokowanie przez bazę
możliwości usunięcia rekordu nadrzędnego, jeżeli istnieją rekordy od niego zależne.
Należy w takim przypadku najpierw usunąć zależne rekordy. To, czy ten sposób reali-
zowania integralności jest dokuczliwym ograniczeniem, czy ratuje nas od kłopotów,
zależy tylko od spójności struktury relacyjnej.

Replikacja bazy danych


Wrafe z wzrostem objętości danych musisz pomyśleć o ich skalowaniu. Można co jakiś
czas przenosić serwer bazy na szybszą maszynę z większą liczbą procesorów i potęż-
niejszymi dyskami, ale prędzej czy później baza będzie wymagała replikacji na więcej
niż jeden serwer.
Rozdział 16. » Wybór bazy danych dla PHP______________________________307

Aby zostało to wykonane, muszą zaistnieć pewne mechanizmy automatycznego utrzy-


mywania synchronizacji pomiędzy serwerami. Jest to zwykle związane z użyciem sys-
temu księgowania, często z relacją główny-podległy pomiędzy serwerami baz danych.
Jedna z baz danych jest bazą główną i wszystkie nowe dane są do niej zapisywane.
System księgowania zapamiętuje te zmiany w porządku chronologicznym. Wszystkie
serwery podległe zwracają tylko dane, nie zmieniając swojej zawartości. Co jakiś czas
odczytują dziennik głównej bazy i wprowadzają u siebie zapamiętane zmiany.

Kolejnym elementem jest mechanizm utrzymywania poprawnej pracy pomimo usterki.


Jedna z baz podległych staje się bazą główną w przypadku wyłączenia głównego serwe-
ra. Pomyśl, jak cenne muszą być twoje dane, aby stosować tak kosztowne metody
z bezpieczeń.

Bazy danych obsługiwane przez PHP


Jeżeli nigdy wcześniej nie wybierałeś bazy danych, zapewne duża liczba produktów ob-
sługiwanych przez PHP przyprawi cię o zawrót głowy. Zamieszczona poniżej tabela
zawiera przegląd różnych baz danych dostępnych użytkownikom PHP, wraz z uwagami
na temat sterowników i warunków licencji. Więcej informacji (które nie zmieściły się
w tabeli) znajduje się na witrynie http://www.troutworks.com/phpbook.

Wybieramy MySQL
Jak wiele innych zespołów, które kiedykolwiek napisały książkę na temat PHP, uwiel-
biamy MySQL i będziemy używać go we wszystkich kolejnych przykładach w części
II. MySQL jest najszybszą, najtańszą, najprostszą i najbardziej stabilną bazą danych,
która posiada większość potrzebnych funkcji. Wyróżnia się ona tym, że posiada prawie
tak samo dobre implementacje dla systemu Unix i Windows. Pomiędzy PHP i MySQL
istnieje niespotykana synergia (szczególnie na platformie Linux — Apache). Oba ze-
społy programistów postanowiły dodatkowo zacieśnić współpracę i zbliżyć do siebie te
produkty.

Ponieważ tworzymy witryny o prostej zawartości, nie potrzebujemy wielu skompliko-


wanych funkcji oferowanych przez PostgreSQL lub Oracle. Jeżeli potrzebujesz obiek-
towo-relacyjnej lub transakcyjnej bazy danych, PHP zapewni wykonanie wielu
podstawowych zadań. Jednak może nie odpowiadać wielu zaawansowanym potrzebom.

Ponieważ programiści PHP używają zwykle standaryzowanych nazw funkcji, a MySQL


obsługuje stosunkowo niewielki zestaw konstrukcji SQL, łatwo można uruchomić nasze
przykłady na innej bazie danych. Należy odszukać funkcje dostępu do bazy i zmienić je
na odpowiednie dla twojej bazy. Na przykład zamiast mysql_query wpisać mssql_
query itd.
308___________________________________Część II » PHP i bazy danych

Tabela 16.1.
Bazy danych obsługiwane przez PHP

Baza danych Typ Obsługa Platforma Licencja Uwagi

Adabas D R ODBC U, W Komercyjna Niemiecka,


(nie zalecana) rozprowadzana z SuSE
Linux
DBA/DBM Płaski plik Warstwa u Open-source Sleepy cat, Gnu DBM,
abstrakcji cdb
dBase P Tylko import w Komercyjna Bez SQL
Empress R ODBC u, w Komercyjna Korporacyjna,
dostępne JDBC
filepro P Tylko import u, w Komercyjna Nie do zadań
produkcyjnych
IBM DB2 R ODBC u, w Komercyjna Korporacyjna,
dostępne JDBC
Informix R Własna u, w Komercyjna Korporacyjna
Interbase R Własna u, w Komercyjna Korporacyjna,
dostępne JDBC
MS Access R ODBC w Komercyjna Nie do zadań
produkcyjnych
MS SQL R Własna w Komercyjna Korporacyjna
Server
mSQL R Własna u Shareware Bardzo mała
MySQL R Własna u, w Komercyjna, Kilka licencji
Shareware
Oracle O-R Własna u, w Komercyjna Korporacyjna
OracleS R Własna u, w Komercyjna Korporacyjna,
integracja z Java
PostgreSQL O-R Własna u Open-Source Wsparcie komercyjne
Solid R ODBC (nie u, w Komercyjna Wbudowana baza
zalecana) danych, firma fińska
Sybase R Własna u, w Komercyjna Korporacyjna

Podsumowanie
Największą zaletą WWW jest możliwość szybkiego i taniego opublikowania dużej ilo-
ści danych. Możliwość ta została rozszerzona dzięki zwiększeniu się liczby niedrogich
i pewnych baz.
Rozdział 16. « Wybór bazy danych dla PHP______________________________309

Ponieważ wielu programistów nigdy nie wybierało bazy danych, opisaliśmy niektóre
podstawowe zagadnienia, które należy wziąć pod uwagę przy podejmowaniu decyzji.
Do tych zagadnień zaliczamy podstawową strukturę bazy danych (plikowa, relacyjna
lub obiektowo-relacyjna), sterownik lub API, łatwość późniejszego rozwijania. Dodat-
kowe własności, takie jak transakcje lub interfejs graficzny, mogą również grać pewną
rolę przy wyborze bazy. Ponieważ PHP obsługuje wiele baz danych, masz świetną oka-
zję odnalezienia potrzebnego zestawu funkcji.
310____________________________________Część II » PHP i bazy danych
Rozdział 17.
Samouczek SQL
W tym rozdziale:
* Standardy SQL
* Podstawowe wyrażenia SQL
* Projektowanie baz danych SQL
** Użycie połączeń SQL
4 Uprawnienia i bezpieczeństwo

Rozdział ten stanowi podstawowe wprowadzenie do baz danych SQL. Omówimy tutaj
standardy, projektowania bazy danych, język manipulacji danymi (DDL) oraz wspólne
dla wszystkich baz SQL procedury zabezpieczania bazy.

Rozdział ten nie jest dokładnym opisem języka SQL ani żadnej bazy danych SQL. Aby
korzystać z bardziej zaawansowanych funkcji, musisz zapoznać się z dokumentacją do
bazy oraz sięgnąć do innych książek. Proponujemy jeden z podręczników języka SQL:
Podstawy SQL. Ćwiczenia praktyczne, Autor: Arkadiusz Jakubowski,
ISBN: 83-7197-427-2
SQL. Wydanie drugie, Autor: Martin Gruber, ISBN: 83-7197-301-2
SQL dla każdego, Autor: Rafę Coburn, ISBN: 83-7197-248-2
SQL. Księga eksperta, Autor: Hans Ladanyi, ISBN: 83-7197-120-6

Powinieneś również zapoznać się z dokumentacją używanej bazy oraz z książkami na


jej temat.

Standardy SQL
Andrew Taylor, twórca SQL twierdzi, że SQL nie pochodzi od „Structured Query Lan-
guage", ale reszta świata tak uważa. SQL reprezentuje i ściślejszą, i bardziej ogólną me-
todę przechowywania danych niż poprzedni standard plikowych baz danych.
312______________________________________Część II » PHP i bazy danych

SQL jest standardem uznawanym zarówno przez ANSI, jak i ECMA. Możesz zapoznać
się z tym standardem po zapłaceniu należności tym organizacjom:
http://www. ansi. org
http://www. ecma. org

Poza ogólnymi wytycznymi standardu istnieją bardzo istotne różnice pomiędzy produ-
ktami pochodzącymi firmowymi oraz tworzonymi na zasadach open-source. W osta-
tnich kilku latach możemy zaobserwować szybki rozwój tzw. obiektowo-relacyjnych
baz danych oraz produktów SQL ukierunkowanych na obsługę WWW.

Aby wybrać właściwą bazę danych, musisz skupić się na własnych potrzebach. Na
pewno słyszałeś opinie, że wiele zaawansowanych funkcji bazy danych (na przykład
transakcje lub połączenia krzyżowe) jest niezbędnych w każdej instalacji SQL. Nie mu-
sisz w to wierzyć. Lepiej wypisz potrzebne ci funkcje, sortując je według ważności
i sprawdź, który produkt najlepiej spełnia te wymagania.

Na szczęście SQL jest dosyć mocno zestandaryzowany. Występuje w nim kilka wrażeń,
których będziesz stale używał, niezależnie od serwera bazy, z którym będzie współpra-
cował program.

Podstawowe wyrażenia SQL


Podstawowa struktura logiczna bazy danych SQL jest bardzo prosta. Instalacja SQL
może zawierać wiele baz danych — na przykład jedną na dane klientów, a drugą na da-
ne produktów. Problem może stanowić nazewnictwo — zarówno serwer SQL, jak
i zestaw tabel, opisywane są wyrażeniem „baza danych". Każda baza danych zawiera
tabele. Każda tabela składa się z kolumn, a dodanie nowego elementu może być trakto-
wane jako dodanie wiersza.

Każdy serwer SQL posiada instrukcje manipulujące danymi. Stanowią one większą
część wszystkich operacji na relacyjnej bazie danych. Tymi instrukcjami są: SELECT,
INSERT, UPDATE oraz DELETE (twoi pracownicy i pomocnicy).

Musisz pamiętać, że instrukcje te manipulują jedynie wartościami zapisanymi w bazie


danych, a nie strukturą bazy. Możesz użyć tych wyrażeń do dopisania danych, ale nie
możesz utworzyć samej bazy. Możesz usunąć wszystkie dane, ale sama baza pozostanie
nienaruszona. Jeżeli chcesz dodawać lub usuwać kolumny, albo utworzyć bazę, musisz
użyć innych wyrażeń, np.: DROP, ALTER lub CREATE.

SELECT
SELECT jest podstawową instrukcją używaną do odczytywania danych z bazy SQL. Jej
najczęściej używana składnia jest bardzo prosta:
SELECT polel, pole2, pole3 FROM tabela WHERE warunek;
Rozdział 17. » Samouczek SQL_____________________________________313

Jest to równie nieskomplikowane jak poproszenie współpracownika o wyjęcie z szary


na akta odpowiedniej kartki.

Istnieją przypadki, gdy potrzebny jest cały wiersz zamiast poszczególnych pól danych.
Praktyka taka nie jest korzystna z wielu powodów (działa wolniej od pobierania potrzebnych
nam danych i może powodować problemy w przypadku zmiany struktury tabeli), jednak
mimo tego jest często stosowana. Cały wiersz jest zwracany po użyciu gwiazdki:
SELECT * FROM tabela WHERE data = 01-05-2000;

Złączenia
W przypadku wyrażenia SELECT istnieje tylko jeden poważniejszy problem: złączenia. Po-
nieważ są jedną z najbardziej użytecznych funkcji języka SQL, opiszemy je szczegółowo.

Wyrażenie SELECT na pojedynczej tabeli bez złączeń można porównać do pojedyncze-


go wiersza w arkuszu kalkulacyjnym.

Baza danych SQL jest jednak z definicji relacyjna. Aby zrozumieć koncepcję relacyj-
nych baz danych, musisz przypomnieć sobie sytuacje, w których musiałeś wypełnić
wiele formularzy — podczas załatwiania kredytu, podczas pierwszej wizyty u lekarza
lub załatwiania innych urzędowych spraw. Gdy po raz piętnasty trzeba było wpisać na-
zwisko, adres, datę urodzenia czy numer PESEL, pewnie pomyślałeś sobie: „Dlaczego
nie wpisuje się tego tylko raz". To jest właśnie zasada działania relacyjnej bazy danych.

Baza danych różni się od papierowego formularza głównym identyfikatorem. Ludziom


lepiej pracuje się z tekstem i wolą określać dane za pomocą identyfikatorów teksto-
wych, na przykład nazw. Jeżeliby w biurze dentysty lub sklepie z częściami samocho-
dowymi przechowywano papierowe akta w porządku numerycznym, trudno byłoby
odnaleźć dokumenty Jana Kowalskiego, jeżeli jest on następny w kolejce. Bardzo czę-
sto użytkownicy papierowych akt dodają numer PESEL jako identyfikator. Pozwala on
odróżnić różnych ludzi o tym samym imieniu i nazwisku.

Bazy danych najszybciej działają operując na liczbach całkowitych. Ponieważ liczby


całkowite są ze swojej natury unikalne, baza danych może na podstawie konkretnej
liczby jednoznacznie zidentyfikować osobę, miejsce lub rzecz, bez względu na to, ile
tabel odwołuje się do tej danej.

Zamiast kilka razy powtarzać dane, jak w poniższym przykładzie:


Nazwisko: Jan Kowalski
PESEL: 71101002505

Nazwisko: Jan Kowalski


Boi_się: koty, piątek 13, latanie

Nazwisko: Katarzyna Nowak


PESEL: 75072912346

Nazwisko: Katarzyna Nowak


Boi_się: wysokość, latanie
314 ____________________________________Część II » PHP i bazy danych

możesz zapisać za pomocą relacyjnej bazy danych każdy fragment informacji tylko
raz i odwoływać się do niego w innych częściach, w sposób pokazywany w tabelach
17.1 do 17.3.

Tabela 17.1.
Osoby

IDosoby Nazwisko PESEL

1 Jan Kowalski 71101002505


2 Katarzyna Nowak 75072912346
3 Adam Marsz 67012349856

Tabela 17.2.
Obawy

IDobawy Obawa

1 czarny kot
2 piątek 13
3 woda
4 wysokość
5 latanie

Tabela 17.3.
Osoba_Obawa

ID IDosoby IDobawy
1 1 1
2 1 2
3 1 5
4 2 4
5 2 5

Taki zapis jest lepszą i szybszą (dla bazy danych) metodą przechowywana informacji.
Jeżeli chcesz pobrać dane w postaci czytelnej dla człowieka, pojawia się problem: mu-
sisz pobrać i skorelować dane z więcej niż jednej tabeli. Jest to zadanie dla złączeń.

Aby sprawdzić, jakie obawy ma pani Nowak, musisz odszukać jej identyfikator.
SELECT IDosoby FROM Osoby WHERE Nazwisko = 'Katarzyna Nowak';

Wyrażenie to zwróci liczbę 2. Następnie możesz zastosować kolejne wyrażenie SELECT,


używając tej informacji.
Rozdział 17. » Samouczek SQL_____________________________________315

SELECT Obawa FROM Obawy WHERE Osoba_0bawa.IDosoby = 2 AND


Osoba_0bawa.IDobawy=Obawy.IDobawy;

Wyrażenie to zwróci „wysokość" i „latanie".

Możesz również stworzyć połączenie zwracające te same informacje za pomocą tylko


jednego wyrażenia SELECT:
SELECT Obawa FROM Obawy WHERE Nazwisko = 'Katarzyna Nowak'
AND Osoba.IDosoby = Osoba_0bawa.IDosoby AND Osoba_0bawa.IDobawy=Obawy.IDobawy;

Znając tylko jedną daną, za pomocą złączeń możesz pobrać z bazy wszystkie dane na
jej temat. Złączenie pozwala na traktowanie kilku tabel jako jednej w czasie poszuki-
wania informacji.

Złączenia posiadają kilka odmian. Ta pokazana w poprzednim przykładzie jest nazywa-


na złączeniem prostym (najczęściej spotykany typ). Bardziej zaawansowane złączenia
zależą od używanego serwera SQL, ponieważ nie wszystkie produkty pozwalają na sto-
sowanie każdego typu złączenia.

Podzapytania
Zanim zajmiemy się innymi wyrażeniami SQL, powinniśmy wspomnieć o podzapyta-
niach. Jest to wyrażenie:
SELECT numer_tel FROM tabela
WHERE nazwisko = 'SELECT nazwisko FROM tabela 2 WHERE ID = l 1 ;

Podzapytania powstały dla wygody. Mogą być bardzo użyteczne, jeżeli pracujesz z du-
żą ilością danych. Możesz otrzymać te same wyniki, używając dwóch prostszych wyra-
żeń SELECT (jednak będzie to działać wolniej, nawet w PHP 4).

INSERT
Poleceniem pozwalającym na wstawianie nowych danych do bazy jest INSERT. Pod-
stawowa składnia jest następująca:
INSERT INTO tabela (coll, co!2, col3) VALUES (vail, va!2, va!3);

Kolumny i wartości muszą się zgadzać, jeżeli pozamieniasz elementy tablic, będziesz
miał problem. Jeżeli nie chcesz, aby niektóre wiersze miały podane wartości w wybra-
nych polach, musisz użyć wartości NULL lub samoczynnie zwiększającej się wartości.
Przed wykonaniem operacji INSERT musisz się upewnić, że pola te mogą pozostać pu-
ste lub mają ustawione samoczynne zwiększanie się wartości. Jeżeli jest to możliwe,
możesz po prostu opuścić pola, w których chcesz pozostawić domyślne wartości w wy-
rażeniu INSERT.

Odmianą podstawowej instrukcji INSERT jest INSERT INTO ... SELECT. Pozwala ona
na wstawienie do tabeli wyników wyrażenia SELECT.
INSERT INTO klient(birthmonth, bitrhflower, birthstone)
SELECT * FROM birthday_info WHERE birthmonth = Sbirthmonth;
316_______________________________________Część II » PHP i bazy danych

Nie każda baza danych SQL posiada tę funkcję. Powinieneś być ostrożny stosując ją,
ponieważ możesz łatwo wpędzić się w kłopoty. Ogólnie rzecz biorąc, pobieranie da-
nych z tej samej tabeli, do której wstawiasz, nie jest korzystne.

UPDATE
Instrukcja UPDATE jest używana do zmieniania danych zawartych w bazie. Możesz za
jej pomocą zmienić niektóre dane w rekordzie bez potrzeby usuwania go i powtórnego
wstawiania. Składnia jest następująca:
UPDATE tabela SET polel='wartl', pole2='wart2', pole3='wart3'
WHERE warunek;

Wyrażenie warunkowe jest identyczne jak dla instrukcji SELECT, na przykład WHERE
I D = 1 5 l u b WHERE p l e c = ' K ' .

DELETE
DELETE jest używane do usuwania danych z bazy. Składnia jest następująca:
DELETE dana FROM tabela WHERE warunek;

Najistotniejszą częścią wyrażenia jest warunek. Jeżeli nie podasz żadnego warunku, zo-
stanie usunięta cała zawartość tabeli. W większości przypadków operacji nie trzeba bę-
dzie potwierdzać.

Powtórzmy jeszcze raz: musisz pamiętać o podaniu warunku w każ-


dym wyrażeniu UPDATE i DELETE. Jeżeli tego nie zrobisz, każdy wiersz
tabeli zostanie w ten sam sposób zmieniony lub zostanie usunięty.
Nawet doświadczeni programiści czasami zapominają wstawić waru-
nek. Możesz również pomyśleć o ograniczeniu uprawnień do bazy da-
nych tak, aby niewielu użytkowników mogło wykonywać operacje
niebezpieczne dla zawartości bazy.

Projekt bazy danych


Nauczenie się używania baz SQL nie jest skomplikowane. Trudniejszym zadaniem jest
projektowanie bazy danych i jej obsługa w czasie pracy.

Najczęściej spotykanymi określeniami dla podstawowego poziomu projektowania bazy są:


* jeden do jednego;
** jeden do wielu;
* wiele do wielu;
Rozdział 17. » Samouczek SQL_____________________________________317

* wiele do jednego;
* unikalny identyfikator.

Dla przykładu, dla Amerykanów relację jeden do jeden określa tzw.: Social Security
Number (w innych krajach również istnieją podobne numery). Każdy obywatel USA ma
przydzielony identyfikator, a używanie numeru innego człowieka jest przestępstwem.
Projektanci baz danych bazują na takich identyfikatorach, ponieważ prawie wszystkie
informacje o człowieku mogą się zmienić.

Wiele do jednego i jeden do wielu różnią się jedynie sposobem rozmieszczenia kolumn
w bazie danych. Przykład takich relacji, zaczerpnięty ze świata medycznego, to pacjent
i jego wizyty. Jeżeli projektujesz tabelę reprezentującą wizy ty pacjentów, zawsze będzie
wchodziła w skład relacji jeden do wielu.

Relacja wiele do wielu jest dobrze ilustrowana przez autorów i książki. Nie tylko książ-
ka może mieć wielu autorów, ale każdy z autorów może napisać sam lub w zespole róż-
ne książki. Nie jest to relacja, która może być efektywnie przedstawiona w arkuszu
kalkulacyjnym, dla tego rodzaju danych baza jest dużo lepszym rozwiązaniem.

Każda relacja pomiędzy danymi należy do jednej z powyższych kategorii. Zadaniem


projektanta bazy jest określenie, która z nich najlepiej będzie spełniała wymagania.

Wyobraź sobie, że projektujesz bazę danych przechowującą informacje o filmach.


Pierwszą decyzją, którą będziesz musiał podjąć, będzie stwierdzenie, czy „film" i „ty-
tuł" są w relacji jeden do jeden, czy dostatecznie dużo filmów posiada podtytuł (aby
dodać pole „podtytuł"), czy potrzeba utworzyć relację jeden do wielu. Nie możemy od-
powiedzieć na to pytanie. Decyzja będzie zależała od tego, jakie dokładnie dane bę-
dziesz przechowywał, jak duża będzie baza, czy warto poświęcić dodatkowe zasoby do
przechowywania precyzyjniejszej struktury danych. Warto dokładnie przemyśleć decy-
zję, gdyż może ona w przyszłości powodować poważne ograniczenia. Niektórzy mogą
być zaskoczeni tym, że przechowywanie danych jest bardziej bezlitosnym wyrzucaniem
niż dokładnym zbieraniem. Jak mawiają historycy, historia polega na zapominaniu,
a nie na pamiętaniu.

Najprostszą relacją jest jeden do jeden, ponieważ można zebrać wszystkie pola w jednej
tabeli, która może być błyskawicznie przeszukiwana. Tabela przechowująca dane
o kliencie może zawierać np. następujące pola:
ID Klienta
Nazwa Klienta
Kontakt z administracją
Kontakt z działem technicznym

W przypadku relacji jeden do jeden najtrudniej stwierdzić, czy będzie potrzebna relacja
jeden do wielu.

Gdy masz już określone relacje jeden do jeden, jeden do wielu, wiele do wielu, należy
podzielić pojedynczą tabelę na wiele tabel: przechowujących główne dane i relacje. Ta-
bele 17.4 do 17.6 zawierają przykład relacji wiele do wielu.
318_______________________________________Część
Część
II »II »PHP
PHP ii bazy danych
danych

Tabela 17.4.
Klient

ID_klienta Nazwa
1 Acme Bread
2 Baker Construction
3 Coolee Dam

Tabela 17.5.
Kontakty

IDJtontaktu Typ
1 Porada telefoniczna
2 Interwencja u klienta
3 Skarga pisemna
4 Skarga telefoniczna
5 Propozycja

Tabela 17.6.
Klienci kontakty

ID_klient_kontakt ID_klienta ID_kontaktu

1 1 1
2 3 5
3 2 4
4 2 3
5 1 2

Gdy zaprojektujesz strukturę bazy, musisz poznać szczegóły jej tworzenia. Głównymi
wyrażeniami SQL, których będziesz używał, to CREATE, ALTER i DROP.

Do utworzenia nowej bazy danych bądź tabeli używamy wyrażenia CREATE. Stworzenie
bazy danych, oprócz nadania jej nazwy, wymaga jeszcze wykonania kilku czynności.
CREATE DATABASE db_name;

Większość pracy to stworzenie tabel i ich kolumn. Najpierw trzeba podać nazwę tabeli,
a następnie szczegółowo określić typy i nazwy kolumn tabeli w wyrażeniu CREATE.
CREATE TABLE tabela (
->id_col INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
->COll TEXT NULL INDEX,
->C012 DATE NOT NULL
Rozdział 17. » Samouczek SQL_____________________________________319

Nie zawsze można łatwo przenosić typy danych i opcje definicji pomiędzy bazami, ze
względu na różnice między serwerami.

DROP jest używany do usuwania tabeli lub bazy danych wraz z wszystkimi danymi.
DROP TABLE tabela;
DROP DATBASE db_name;

Musisz być bardzo ostrożny przy stosowaniu takich wyrażeń.

Jeżeli chcesz zmienić strukturę tabeli, musisz zastosować wyrażenie ALTER. W tym wy-
rażeniu podajesz nazwę zmienianej tabeli oraz określasz zmiany. W tym przypadku
również mogą wystąpić różnice pomiędzy różnymi bazami SQL.
ALTER TABLE tabela RENAME AS nowa_tabela;
ALTER TABLE nowa_tabela ADD COLUMN Col3 VARCHAR(50);
ALTER TABLE nowa_tabela DROP COLUMN co!2;

Użycie połączeń do bazy danych


Główna zasada stosowana w połączeniach PHP-SQL brzmi: wszystkie dane muszą być
pobierane po jednym wierszu. Jest to dosyć wąskie połączenie, więc trzeba przesyłać
minimalne ilości danych. Unikaj zapytań zwracających wiele wierszy, które później
trzeba jeszcze posortować.

PHP i SQL są przeznaczone do odmiennych zadań. Na przykład alfabetyczne posorto-


wanie wielu wierszy na podstawie zawartości jednego pola jest dużo szybsze i prostsze
w SQL niż w PHP. Filtrowanie jest kolejnym obszarem, gdzie SQL jest o wiele bardziej
wydajny niż PHP. Lepiej dobrze nauczyć się używania warunków instrukcji SELECT,
niż stosować pętle PHP po pobraniu zbyt wielu danych. Serwery SQL nie dysponują
wieloma funkcjami, ale doskonale je realizują.

Więcej na temat optymalizowania połączenia PHP-SQL w rozdziale 23. „Styl i efek-


tywność rozwiązań na podstawie PHP i bazy danych".

Bezpieczeństwo i uprawnienia
Jak napisaliśmy w rozdziale 31. „Bezpieczeństwo i kryptografia", o bezpieczeństwo
w świecie WWW trzeba zadbać tak samo, jak w rzeczywistym. Każdy policjant może
potwierdzić, że nie da się całkowicie zabezpieczyć domu przed włamaniem. Bardziej
realistycznym celem jest utrudnienie potencjalnego włamania.

Użycie PHP wraz z bazą danych może być porównane do zastosowania dwóch zamków
w drzwiach wejściowych. Wyraźnie zwiększa bezpieczeństwo witryny, ale tylko wtedy,
gdy zastosuje się podstawowe zasady higieny bazy.
320____________________________________Część II » PHP i bazy danych

Ustawianie uprawnień
Podstawową zasadą zabezpieczania baz danych jest przydzielanie użytkownikowi lub
grupie minimalnych uprawnień, wymaganych do wykonywanych zadań. Nie pozwolisz
przecież obcemu na przechadzanie się po domu i czytanie pamiętnika leżącego w sy-
pialni. Ta sama zasada będzie obowiązywała w witrynie. Nieco więcej pracy wymaga
zarządzanie wieloma użytkownikami i odpowiednimi dla nich uprawnieniami, ale warto
się wysilić.

Poza groźbą uszkodzeń spowodowanych przez złośliwych użytkowników, ustawienie


właściwych uprawnień zabezpieczy cię przed samym sobą.

Typowe grupy praw w bazie danych mogą wyglądać następująco:


* użytkownik: tylko SELECT;
* współpracownik: SELECT, INSERT, być może UPDATE;
* redaktor: SELECT, INSERT, UPDATE, być może DELETE, być może GRANT;
* administrator: SELECT, INSERT, UPDATE, DELETE, GRANT, DROP.
DROP jest jak bomba atomowa, ponieważ za jej pomocą można zniszczyć całą bazę,
jedną instrukcją. Ktoś musi mieć tę możliwość, jednocześnie zdając sobie sprawę
z ogromnej odpowiedzialności.

W wielu bazach danych, w tym w MySQL, hasła są szyfrowane kilkoma sposobami za


pomocą haseł systemowych (są one zwykle przechowywane w różnych miejscach).
Nawet gdy jedno hasło zostanie złamane, drugie nadal będzie bezpieczne. Oczywiście
zakładamy, że uprawnienia są odpowiednio ustawione, hasła są prawidłowe i zastoso-
wano właściwą instrukcję do umieszczenia nazw użytkowników i ich haseł w tabeli
systemowej (nie jest to zwykłe wstawienie danych).

,§?^vz. Użytkownicy bazy danych i ich hasła powinny różnić się od użytkowni-
Z^Lx\ ków i haseł systemowych. Nigdy nie ustawiaj w bazie hasła identycz-
nego z hasłem administratora systemu. Jeżeli zdarzy się, że haker
włamie się przez Internet, dasz mu klucz do systemu.

Przechowywanie haseł w innym miejscu


Dobrym pomysłem jest oddzielenie haseł od plików, które z nich korzystają. W PHP
można użyć funkcji include lub require do łatwego dołączenia tekstu w trakcie wy-
konywania skryptu z innego pliku (na przykład z hasłami bazy danych). Należy pamię-
tać, że dołączane pliki powinny znajdować się poza katalogiem dostępnym przez
WWW! Należy umieścić taki plik poza drzewem witryny WWW.
Rozdział 17. » Samouczek SQL_____________________________________321

Umieszczenie zmiennych bazy danych poza plikami PHP jest korzystne również z in-
nego powodu. Jeżeli masz wiele skryptów PHP korzystających z tej samej bazy, mogą
używać tego samego pliku haseł. Jeżeli podejrzewasz, że hasło zostało złamane, lub re-
gularnie zmieniasz hasła, wystarczy zmodyfikować tylko jeden plik i wszystkie skrypty
zostaną uaktualnione.

Istnieje jedna wada takiego rozwiązania. Użytkownik serwera Apache musi mieć uprawnie-
nia do odczytania pliku z hasłami. Ponieważ użytkownik Apache i użytkownik bazy danych
toczęsto te same osoby, plik ten będzie dostępny dla wszystkich. To jednak bezpieczniejsze
rozwiązanie, niż przechowywanie haseł w głównym katalogu drzewa witryny WWW.

Jeżeli rzadko używasz zmiennych bazy danych, na przykład tylko w skryptach konfigu-
racyjnych, możesz trzymać go w katalogu niedostępnym dla serwera Apache i zmieniać
prawa do tego katalogu tylko wtedy, gdy konieczny jest dostęp do pliku z hasłami. Na
przykład: stosunkowo rzadko kasujemy pozycje z listy dyskusyjnej dostępnej na naszej
witrynie. Łatwiej jest przechowywać hasła w katalogu niedostępnym dla Apache i na
chwilę ustawiać uprawnienia w celu usunięcia np. obraźłiwego wpisu.

Jeżeli z jakiegoś powodu zdecydujesz się umieścić nazwę użytkownika, hasło, nazwę
komputera i bazy danych w skrypcie PHP, otrzymasz spodziewany wynik. Jeżeli demon
httpd będzie działał prawidłowo, hasła bazy danych będą tak samo bezpieczne jak każ-
dy plik witryny — nie jest to wysoki poziom bezpieczeństwa. Jeżeli demon zatrzyma
się, istnieje możliwość, że plik PHP (włączając w to wartości zmiennych bazy danych)
zostanie dostarczony użytkownikowi w czytelnej postaci. Możesz ograniczyć ryzyko,
korzystając z innego niż „.htmr rozszerzenia dla plików PHP.

Jeżeli nie uaktywniłeś „trybu cichego" w PHP 3, w przypadku braku podłączenia do ba-
zy zobaczysz w przeglądarce następujący napis:
Warning: MySQL Connection Failed: Access denied for user: 'someuser@localhost'
^(using password: NO) in /home/web/html/mysqltest,php3 on line 2

Jest to dziura w systemie bezpieczeństwa. Nazwa użytkownika bazy danych MySQL


oraz używanie hasła zostaną ujawnione. W PHP 4 komunikaty o błędach MySQL nie są
domyślnie wyświetlane. Istnieją dwie nowe funkcje mysql_errno ( ) i mysql_er-
ror ( } , które umożliwiają odczytanie kodu błędu i jego opisu, ale musisz pobrać te in-
formacje jawnie. W większości przypadków wybiera się bardziej elastyczną konstrukcję
die ( ) lub usuwa komunikaty o błędach po zakończeniu uruchamiania skryptu. Używa-
nie funkcji mysql_error na publicznym serwerze WWW nie jest dobrym pomyłem.

Użycie formularzy PHP do sprawdzania haseł


Możesz zastosować w skryptach jeszcze jeden poziom zabezpieczenia, kontrolując za
pomocą PHP nazwy użytkowników i hasła, zapisane w bazie danych. W ten sposób
ustanowisz szereg zabezpieczeń: nazwę użytkownika systemu i jego hasło, uprawnienia
użytkownika bazy danych zapisane w pliku niedostępnym z witryny WWW oraz
sprawdzanie, dzięki skryptowi PHP, zgodności użytkownika i hasła podanego w for-
mularzu z wartościami zapisanymi w bazie danych.
322____________________________________Część II » PHP i bazy danych

<HTML>
<BODY>
<FORM METHOD=POST ACTION="<?php print("form_check.php");?>">
<P>Użytkownik: <INPUT TYPE="TEXT" SIZE=20 NAME="try_user"X/P>
<P>Hasło: <INPUT TYPE="PASSWORD" SIZE=10
NAME="try_pass"x/P>
<P>Data: <INPUT TYPE="TEXT" SIZE=10 NAME="try_date"X/P>
<P>Wpis:<BR>
<TEXTAREA COLS=50 ROWS=10 NAME="try_entry"X/TEXTAREAX/P>
<PXINPUT TYPE="SUBMIT"X/P>
</BODY>
</HTML>
form_check.php
<?php
// Sprawdź użytkownika bazy danych
include("/home/Webvars.inc");
mysql_connect($hostname, Sdb_user, Spassword);
// Sprawdź użytkownika z formularza
mysql_select_db("Weblog") ;
$query = ("SELECT password FROM finalcheck WHERE user='$try_user'") ;
Sresult = mysql_query(Squery);
Spasscheck = mysql_fetch_array(Sresult);
if($passcheck[0] == Stry_pass)
{
/* Wstaw nową pozycję. */
$query = ("INSERT INTO log (ID, date, entry) VALUES
(NULL, '$try_date', '$try_entry')");
$result = mysql_query(Squery);
print("Wynik wstawienia nowej pozycji to $result."};
)
else
(
mail("security@localhost", "Ostrzeżenie",
"Ktoś z komputera $REMOTE_ADDR próbuje wejść na główną stronę dziennika.");
>
?>

Skrypt ten niczego nie zapisze w bazie danych, jeżeli nazwa użytkownika i jego hasło
nie zostaną podane. Użytkownik musi też prawidłowo załogować się do serwera.
Ostrzeżenie o próbie włamania do systemu można otrzymać e-mailem.

Tworzenie kopii bezpieczeństwa


Jednym z najważniejszych zadań strategii bezpieczeństwa jest tworzenie kopii bezpie-
czeństwa. Poświęć nieco czasu na nauczenie się najlepszej metody tworzenia kopii
używanej bazy danych (na przykład za pomocą polecenia dump w MySQL), a następnie
regularnie twórz kopie bezpieczeństwa. Jeszcze lepszą metodą jest ustawienie automa-
tycznego wykonywania kopii bazy danych przez system.

Podsumowanie
SQL to nie technologia kosmiczna. Cztery podstawowe wyrażenia używane do mani-
pulowania danymi są dostępne we właściwie wszystkich bazach SQL. SELECT pobiera
danych z bazy, INSERT umieszcza dane w bazie, UPDATE zmienia fragmenty istnieją-
cych wpisów, a DELETE usuwa wiersze tabeli.
Rozdział 17. » Samouczek SQL_____________________________________323

Najbardziej skomplikowanym zadaniem jest zaprojektowanie bazy danych. Projektant


musi wybrać najlepszą metodę reprezentacji fragmentów danych i relacji pomiędzy da-
nymi. Dobrze zaprojektowana baza danych jest łatwa do programowania, natomiast źle
zaprojektowana prowadzi do licznych problemów.

Baza danych SQL jest tworzona za pomocą wyrażeń struktury danych. Najważniejszymi
wyrażeniami tego typu są CREATE, ALTER i DROP. CREATE DATABASE tworzy nową
bazę danych, a CREATE TABLE definiuje nową tabelę w bazie. ALTER zmienia struktu-
rę tabeli. DROP usuwa całą tabelę lub bazę danych.

Dobry projekt bazy danych obejmuje również zagadnienia bezpieczeństwa. Użycie od-
powiednich zabezpieczeń w bazie wyraźnie zwiększa poziom bezpieczeństwa całego
serwera WWW. Najlepszą obroną przed włamaniami jest ustawienie regularnego two-
rzenia kopii bazy, dlatego każdy administrator SQL powinien poznać najbardziej efek-
tywne sposoby tworzenia takich kopii.
324______________________________________Część II » PHP i bazy danych l
Rozdział 18.
Funkcje PHP i MySQL
W tym rozdziale:
4 Łączenie z MySQL
* Tworzenie zapytań
** Pobieranie danych
* Pobieranie opisu danych
* Korzystanie z wielokrotnych połączeń
* Kontrola błędów
* Tworzenie baz danych

Po zaprojektowaniu i zainstalowaniu bazy danych MySQL możemy rozpocząć pisanie


skryptów operujących na bazie. Spróbujemy teraz opisać wszystkie podstawowe funk-
cje pozwalające na przesyłanie danych z witryny WWW do bazy danych i z powrotem.

Łączenie z MySQL
Podstawową funkcją pozwalającą na zainicjowanie połączenia z MySQL jest:
mysql_connect($hostname, $user, $password);

jeżeli zamiast zmiennych chcesz użyć ciągów, wywołanie tej funkcji jest następujące:
mysql_connect('localhost', 'root', 'sesame');

Hasło jest opcjonalne. Lepiej, jeżeli baza danych korzysta z hasła. Jeżeli jednak hasło
nie jest potrzebne, można pominąć ten argument. Można również podać port i gniazdo
w serwerze ($hostname: port: gniazdo), ale jeżeli nie wybrałeś innych niż standar-
dowe ustawień portu i gniazda serwera bazy, nie ma powodu, aby stosować takie wy-
wołanie.
326____________________________________Część II » PHP i bazy danych

Kolejnym krokiem jest wybór roboczej bazy danych:


mysql_select_db($database);

Jeżeli wolisz stosować jawne ciągi zamiast zmiennych, wywołaj tę funkcję następująco:
mysql_select_db('phpbook') ;

Po podłączeniu do bazy danych musisz wybrać bazę, co oznacza, że musisz to zrobić co


najmniej raz na stronie lub po każdej zmianie bazy danych. Jeżeli tego nie zrobisz, wystąpi
błąd „database not selected". Nawet jeżeli utworzysz tylko jedną bazę danych na serwe-
rze, musisz wybierać bazę, ponieważ MySQL jest instalowany z dwiema predefmiowa-
nymi bazami danych (o nazwach mysql i test), których można nie brać pod uwagę.

Wywołania tych dwóch funkcji są poprzedzone znakiem '@', na przy-


kład @mysqi_seiect_db ($database). Ten symbol włącza tryb cichy,
w którym, z powodów bezpieczeństwa, funkcja nie zwraca żadnych
komunikatów o niepowodzeniu.

Po zestawieniu połączenia jesteśmy gotowi do wykonania zapytania.

Tworzenie zapytań w MySQL


Zapytanie bazy danych z PHP jest zwykłą instrukcją MySQL, opakowaną w prostą funk-
cję PHP o nazwie mysql_query ( ) . W instrukcji tej używa się podstawowych instrukcji
SQL, omówionych w poprzednim rozdziale: SELECT, INSERT, UPDATE i DELETE. Jeżeli
chcesz przy użyciu PHP tworzyć bazę danych, możesz skorzystać z instrukcji MySQL
CREATE lub DROP TABLE (ale na całe szczęście nie DROP DATABASE).
Najprostszym możliwym sposobem uruchomienia zapytania jest:
mysql_query("SELECT Surname FROM personal_info WHERE ID<10">;

PHP spróbuje wykonać takie zapytanie. Czasem trzeba podzielić tę (i podobne) instruk-
cje na dwa wiersze i użyć dwóch dodatkowych zmiennych, tak jak w przykładzie:
$query = "INSERT INTO personal_info (ID, Surname, Name, Occupation)
VALUES (NULL, 'Adams', 'Sam', 'Pariot Brever')";
$result = mysql_query($query) ;

Podstawowym powodem jest to, że te dodatkowe zmienne daj ą nam dostęp do bardzo
użytecznych danych. Każde zapytanie MySQL w wyniku zwraca informację, czy się
powiodło, czy nie — jak bankomat, kiedy próbujesz pobrać pieniądze. Jeżeli wszystko
pójdzie dobrze, potrzebujesz tylko potwierdzenia. Jeżeli jednak coś pójdzie źle, zwraca-
na wartość podaje powód, dlaczego operacja się nie udała.

Funkcja mysql_query wymaga ciągu z zapytaniem jako parametru (który nie powi-
nien mieć średnika na końcu) i opcjonalnie identyfikatora połączenia. Jeżeli nie korzy-
stasz z wielokrotnych połączeń, identyfikator połączenia nie jest potrzebny. Funkcja
Rozdział 18. » Funkcje PHP i MySQL__________________________________327

zwraca wartość TRUE (różną od zera), jeżeli zapytanie zostało wykonane poprawnie
(nawet jeśli nie zwraca żadnych wierszy). Jeżeli zapytanie było niepoprawne lub nie zo-
stało wykonane z innych powodów, funkcja zwraca wartość FALSE.

Jeżeli zapytaniem było jedno z wyrażeń INSERT, UPDATE, DELETE, CREATE TABLE
lub DROP TABLE, wykonane poprawnie, możesz użyć funkcji mysql_affected_
rows, aby sprawdzić, ile wierszy zostało zmienionych przez zapytanie. Funkcja ta rów-
nież posiada opcjonalny parametr identyfikatora połączenia, który jest potrzebny jedy-
nie w przypadku używania wielokrotnych połączeń.
Saffected_rows = mysql_affected_rows O;

Jeżeli wykonywane zapytanie było wyrażeniem SELECT, zwracana liczba ma nieco inne
znaczenie: zamiast wartości TRUE lub FALSE zwracana jest wartość całkowita zwana
identyfikatorem wyniku. Jest to unikalny identyfikator każdej instrukcji SELECT, zwy-
kle w postaci kolejnych liczb, rozpoczynających się od l dla pierwszej instrukcji
SELECT w każdym skrypcie. Dla SELECT nie można używać funkcji mysql_af fec-
ted_rows, ponieważ wiersze nie są modyfikowane. W zamian można skorzystać z my-
sql_num_rows ( $ r e s u l t ) , aby sprawdzić, ile wierszy zwróciła instrukcja SELECT.

Zamiast używać funkcji mysqi_seiect_db i mysqi_query, można je


połączyć w jedną funkcję mysqi_db__query ($db, $query). Funkcja
zwraca wartości podobne do zwracanych przez mysql query.

Pobieranie wyniku
Pomysł pobierania danych przez PHP może wydać się dziwny początkującym użyt-
kownikom. Wydaje się logiczne założenie, że wynikiem zapytania powinny być żądane
dane. Wynikiem zapytania w PHP jest liczba określająca pomyślne lub niepomyślne
wykonanie zapytania albo identyfikator wyniku.

Funkcje mysql_query ( ) lub mysql_db_query ( ) wykonują zapytanie na serwerze


bazy danych i zwracają status operacji do PHP. W tym momencie dane znajdują się
w buforze, który nie jest bezpośrednio dostępny ani z poziomu MySQL, ani PHP. Dane
czekają na polecenie pobrania. Aby pobrać dane do PHP, należy użyć funkcji my-
sql_fetch.

Funkcje pobierające dane są następujące:


+ mysql_fetch_row: zwraca wiersz jako tablicę indeksowaną liczbami;
* mysql_fetch_object: zwraca wiersz jako obiekt;
* mysql_f etch_array: zwraca wiersz jako tablicę asocjacyjną;
4 mysql_result: zwraca jedną komórkę danych.
328 Cześć IIII »» PHP
Cześć PHP i i bazy
bazy danych
danych

Funkcje mysql_fetch_field Oraz mysql_fetch_lengths są nie-


prawidłowo nazwane. Obie zwracają informacje na temat elementów
bazy danych, a nie na temat wartości zapisanych w bazie. Niektórzy
mogą pomyśleć, że funkcja mysqi_fetch_fieid pozwala na pobra-
nie wyniku zawierającego jedno pole (identyfikator skojarzony z nazwą
użytkownika). Przeznaczenie tych funkcji jest opisane w tabeli na koń-
cu tego rozdziału; nie należy o nich myśleć jako o funkcjach zwracają-
cych wartości z bazy danych.

Różnica pomiędzy trzema podstawowymi funkcjami pobierającymi dane jest niewielka.


Najbardziej ogólna jest mysql_f etch_row, której używa się następująco:
Squery = "SELECT ID, LastName, FirstNarae FROM users WHERE Status=l";
Sresult = mysql_query(Squery) ;
while! listlSID, SLastName, SFirstName) = mysql_fetch_row(result))
print("$ID SLastName SFirstName<BR>\n");

Wykonanie powyższego kodu spowoduje wypisanie danych z bazy; każdy wiersz bę-
dzie zawierał dane skojarzone zjednostkowym identyfikatorem.

W indeksowanej liczbami tablicy liczby w nawiasach klamrowych są


nazywane przesunięciami pól. Należy pamiętać, że rozpoczynają się
zawsze od 0. Jeżeli rozpoczniesz liczenie od l, zgubisz pierwszą ko-
lumnę danych.

Funkcja mysql_fetch_object wykonuje to samo zadanie, ale wiersz jest zwracany


w postaci obiektu, a nie tablicy. Jest to wygodne dla tych użytkowników PHP, którzy
wolą używać notacji obiektowej:
$query = "SELECT ID, LastName, FirstName FROM users WHERE Status=l";
Sresult = raysql_query(Squery} ;
while! Srow = mysql_fetch_object(result))
echo Srow->ID Srow->LastName Srow->FirstName;

Najbardziej użyteczną funkcją pobierającą dane jest mysql_fetch_array, która po-


zwala na traktowanie wyniku jako tablicy asocjacyjnej lub indeksowanej liczbami, albo
równoczesne stosowanie tych reprezentacji (domyślnie). Oznacza to, że można odwo-
ływać się o nazw pól tablicy zamiast do ich numerów.
Squery = "SELECT ID, LastName, FirstName FROM users WHERE Status=l";
Sresult = mysql_query(Squery);
while( Srow = mysql_fetch_array{result})
echo Srow["ID"] Srow["LastName"] $row["FirstName"J."<BR>\n";

Pamiętaj, że mysql_f etch_array może być używana również w ten sam sposób, co
mysql_f etch_row — z identyfikatorami numerycznymi zamiast nazw pól. Używając
tej funkcji, możesz dokonać wyboru. Jeżeli chcesz używać numeru albo nazwy pola,
możesz użyć następujących wywołań:
Soffset_row = mysql_fetch_array(Sresult, MYSQL_NUM);

lub
$associative_row = mysql_fetch_array($result, MYSQL_ASSOC);
Rozdział 18. » Funkcje PHP i MySQL__________________________________329

Można również używać MYSQL_BOTH jako drugiej wartości, ale pamiętaj, że jest to
wartość domyślna (byłby to nadmiar funkcji).

We wczesnych wersjach PHP 3 mysql_fetch_row było wyraźnie szybsze niż my-


sql_fetch_object i mysql_fetch_array, ale w chwili obecnej różnice szybkości
działania są niezauważalne. Zaleca się używanie mysql_fetch_array zamiast my-
sql_f etch_row, ponieważ zapewnia większy zakres funkcji przy nieznacznym zwięk-
szeniu trudności w programowaniu lub obniżeniu wydajności.

Ostatnią i najmniej ważną funkcją jest mysql_result ( ) . Powinieneś rozważyć uży-


wanie tej funkcji tylko w sytuacjach, gdy potrzebujesz tylko jednego fragmentu danych
zwracanych przez MySQL. Na przykład:
$query = "SELECT Surname FROM personal_info WHERE ID = 255";
$db_result = mysql_query($query);
Sdatapoint = mysql_result($db_reusult, 0, 0);

Funkcja mysql_result posiada trzy parametry: identyfikator wyniku, identyfikator


wiersza i pole (opcjonalnie). Pole można określić poprzez indeks, tak jak w przykładzie,
poprzez nazwę w tablicy asocjacyjnej (surname) lub poprzez identyfikator MySQL
pole-kropka-tabela (Surname .personal_info). Jeżeli jest to możliwe, należy używać
pierwszej opcji, ponieważ jest szybsza. Jeszcze lepiej nie używać tej funkcji wcale.

Nie powinieneś nigdy używać mysqi_resuit o do pobierania danych


dostępnych przez predefiniowane funkcje PHP-MySQL. Klasycznym przy-
kładem jest wstawianie wiersza i wybór jego identyfikatora (dodatkową
wadą jest szukanie MAX(ID)). W zamian użyj mysqi_insert_id ( ) .

Istnieje specjalna funkcja MySQL używana, wraz z dowolną funkcją pobierającą dane,
do dokładnego określenia wymaganego numeru wiersza. Jest to mysql_data_seek,
która wymaga identyfikatora wyniku i numeru wiersza jako argumentu. Przesuwa ona
wewnętrzny wskaźnik wiersza w wyniku do określonego wiersza. Najczęstszym zasto-
sowaniem tej funkcji jest ponowne przeglądanie wyniku od początku — ustawienie
numeru wiersza na zero (analogicznie w funkcji dla tablic). Unika się w ten sposób ko-
lejnego kosztownego wykonywania zapytania przez serwer bazy do pobrania danych,
które już wcześniej zostały przekazane do PHP.
<?php
echo("<TABLE>\n<TRXTH>Tytuły</THX/TR>\n<TR>") ;
$query = "SELECT title, publisher FROM books";
Sresult = mysql_query(Squery);
while($book_row = mysql_fetch_array($result))
(
echo("<TD>$book_row[0]</TD>\n");
)
echo("</TRx/TABLEXBR>\n") ;
echo("<TABLE>\n<TRXTH>Wydawcy</THx/TR>\n<TR>") ;
mysql_data_seek(Sresult, 0 ) ;
while($book_row = mysql_fetch_array(Sresult))
{
echo("<TD>Sbook_row[l]</TQ>\n") ;
1
echo("</TRX/TABLEXBR>\n") ;
330______________________________________Część II » PHP i bazy danych

Pobieranie opisu danych


Aby umieścić dane w istniejącej bazie i odczytać je z powrotem, potrzeba tylko trzech
lub czterech funkcji PHP: mysql_connect, mysql_db_query (lub mysql_select_
db i mysql_query) oraz mysql_fetch_array. Większość pozostałych funkcji opi-
sywanych w tej części służy do pobierania informacji o danych umieszczanych lub po-
bieranych w bazie, albo informacji o budowie bazy. Istnieją np. funkcje pomagające
poznać nazwę tabeli, w której są umieszczone dane, określić typ danych przechowywa-
nych w określonej kolumnie, lub numer wiersza, do którego zostały wstawione dane.

Pewnie nie chcesz, aby każdy haker mógł poznać budowę i zawartość twojej bazy. Je-
żeli masz skrypty używające powyższych funkcji — na przykład narzędzi do zdalnej
administracji bazy danych — musisz szczególnie zadbać bezpieczeństwo. Upewnij się,
że skryptów może używać tylko administrator MySQL. Zaleca się stosowanie formula-
rzy z hasłami. Można również ograniczyć możliwość stosowania tych skryptów do
określonych adresów IP.

Funkcje metadanych MySQL mieszczą się w jednej z poniższych kategorii:


4 funkcje zwracające dane tylko na temat poprzedniej operacji;
* funkcje zwracające dane na temat struktury bazy danych.

Często używanym przykładem pierwszej kategorii jest m y s q l _ i n s e r t _ i d ( ) , która


zwraca wartość samoczynnie zwiększającego się identyfikatora, użytą we wstawianym
wierszu danych. Przykładem drugiej kategorii jest funkcja mysql_field_type ( ) ,
zwracająca typ określonej kolumny w tabeli (integer, varchar, text lub inny).

Większość nazw funkcji, zwracających opis danych, wyjaśnia sama siebie. Istnieje kilka
rzeczy o których należy pamiętać podczas ich używania. Po pierwsze większość tych
funkcji działa jedynie we właściwej kombinacji z innymi funkcjami — nie próbuj uży-
wać mysql_af fected_rows po zapytaniu SELECT. Po drugie, zwracaj uwagę na za-
bezpieczenie funkcji, które zwracają dane na temat struktury bazy. Poznanie nazwy
i struktury tabel będzie hakerowi bardzo przydatne. Wiele z tych funkcji stanowi połą-
czenie prostszych funkcji. Jeżeli potrzebujesz kilku fragmentów informacji z wyniku
lub z bazy danych, lepiej używać mysql__fetch_field zamiast kolejnych wywołań
mysql_field.

Korzystanie z wielokrotnych połączeń


Jeżeli nie korzystasz z wielokrotnych połączeń, wystarczy mieć jedno połączenie z bazą
na stronie. Nawet jeżeli wiele razy przełączasz się do trybu HTML, połączenie będzie
prawidłowe (zakładając, że ostatnim razem było prawidłowe). Używanie wielokrotnych
połączeń to jedna z najbardziej kosztownych i czasochłonnych operacji.
Rozdział 18. » Funkcje PHP i MySQL__________________________________331

Głównym powodem stosowania wielokrotnych połączeń jest korzystanie z dwóch lub


więcej osobnych baz danych. Jest to stosunkowo rzadki przypadek, ale PHP potrafi go
obsłużyć. Po prostu otwierasz połączenia do wszystkich potrzebnych baz i musisz jedy-
nie pamiętać, aby używać właściwego wyniku. PHP pomaga zapanować nad połącze-
niami za pomocą identyfikatorów wyniku, które zostały omówione wcześniej. Należy
przekazywać je jako argument opcjonalny do każdej funkcji MySQL. Jeżeli wykonasz
wszystkie potrzebne zapytania bez przełączania się, nie będziesz musiał przekazywać
identyfikatora połączenia. PHP automatycznie użyje ostatnio otwartego połączenia.
<?php
Ślinki = mysql_connect('hostl', 'me', 'sesame');
mysql_select_db('userdb', $linkl);
Squeryl = "SELECT ID FROM usertable WHERE username = '$username'";
Sresultl = mysql_query{Squeryl, $linkl);
$arrayl = mysql_fetch_array(Sresultl) ;
Susercount = mysql_num_rows($resultl) ;
mysql_close($linkl) ;
$today = '2000-05-01';

$link2 = mysql_connect{'host2', 'myself', 'benne');


mysql_select_db('inventorydb', Slink2);
$query2 = "SELECT sku FROM widgets WHERE ship_date = '$today'";
Sresult2 = mysql_query($query2, Slink2);
$array2 = mysql_fetch_array(Sresult2);
Swidgetcount = mysql_num__rows (Sresult2) ;
mysql_close($link2) ;
if {Susercount > 0 && Swidgetcount >0)
(
$link3 = mysql_connect('hosts', 'I', 'seed');
mysql_select_db('salesdb', Slinks);
SqueryS = "INSERT INTO saleslog (ID, date, userlD, sku)
VALUES (NOLL, 'Stoday', 'Sarrayl[0]', 'Sarray2(0]')";
SresultS = mysql_query(SqueryS, SlinkS);
SinsertID = mysql_insert_id(SresultS);
mysql_close(SlinkS);
iflSinsertID >= 1)
I
print{"Świetnie");
)
else
{
print("Uwaga Uwaga problemy!");
)
}
else
{
print("Za mało danych");
}
?>

W powyższym przykładzie rozmyślnie utrzymywaliśmy rozdzielne połączenia, ponie-


waż w ten sposób jest czytelniejszy (nawet jeżeli trzeba było zamykać każde połączenie
po zakończeniu). Bez funkcji mysql_close ( ) moglibyśmy utrzymywać równoległe
połączenia. Trzeba pamiętać, aby przekazywać połączenie z jednej funkcji do następnej.
332______________________________________Część II » PHP i bazy danych

Kontrola błędów
Ta część powinna nosić tytuł „Die", ponieważ podstawowa funkcja kontroli błędów na-
zywa się die ( ) . Ponieważ nazwa funkcji (pesymistyczna) nie pasuje do książki czyta-
nej przez początkujących programistów, zastosowaliśmy bardziej prozaiczny tytuł.

Die nie jest funkcją MySQL — podręcznik PHP wymienia ją w części „Różne funk-
cje". Funkcja ta zatrzymuje wykonywanie skryptu (lub jego części), zwracając odpo-
wiedni napis.
mysql_query("SELECT * FROM mutual_funds WHERE code = 'Ssearchstring'")
or die ("Proszę poprawić zapytanie i spróbować jeszcze raz.");

Zwróć uwagę na składnię: „or" (mógłbyś użyć również | |, ale „or DIE"' wygląda in-
trygująco...) i tylko jeden średnik.

Jeszcze do niedawna MySQL zwracał do PHP bardzo niebezpieczne i niewiele wyja-


śniające komunikaty o błędach, jeżeli miał problem z wykonaniem zapytania. Die było
często używane do przejęcia kontroli nad tym, co użytkownik widzi w przypadku wy-
stąpienia błędu.

Kolejnym narzędziem kontroli błędów są komunikaty. Stosunkowo przydatne w trakcie


tworzenia i uruchamiania skryptu, ale mogą zostać zakomentowane, gdy skrypt zaczyna
normalną pracę. Komunikaty o błędach MySQL nie pojawiają się samoczynnie. Jeżeli
potrzebujesz komunikatu, musisz zapytać o niego za pomocą funkcji mysql_errno
(zwracającej kod błędu) lub mysql_error (zwracającej komunikat tekstowy).
if (!mysql_select_db($bad_db))
print(mysql_error(}) ;

Tworzenie baz danych MySQL


za pomocą PHP
Zamiast korzystać z narzędzi klienta MySQL, możesz stworzyć bazę danych za pomocą
PHP. Praktyka taka ma pewne zalety — możesz użyć atrakcyjnego interfejsu użytkow-
nika — ale również wady, na przykład niedostateczne bezpieczeństwo.

Aby utworzyć bazę danych za pomocą PHP, użytkownik skryptu powinien posiadać
pełne uprawnienia CREATE /DROP w MySQL. Oznacza to, że każdy, kto użyje takiego
skryptu, może bardzo łatwo usunąć wszystkie twoje bazy wraz z zawartością (co oczy-
wiście jest niekorzystne).

' Ang. lub zgiń.


Rozdział 18. » Funkcje PHP i MySQL__________________________________333

Jeżeli rozważasz tworzenie baz danych za pomocą PHP, przynajmniej nie zapamiętuj
nazw użytkowników bazy danych i ich haseł w pliku tekstowym. Utwórz formularz do
logowania się do bazy i przekaż te zmienne do wszystkich skryptów. Przechowywanie
zmiennych w zewnętrznym pliku poza drzewem witryny nie jest wystarczającym za-
bezpieczeniem.

Tym, którzy lubią ryzyko, przedstawiamy listę odpowiednich funkcji:


* mysql_create_db ( ) : tworzy bazę danych o nazwie przekazanej jako argu-
ment do wskazanego komputera;
+ mysql_drop_db ( ) : usuwa podaną bazę danych;
+ mysql_query ( ) : przekazuje definicję tabeli.

Szkielet skryptu tworzącego bazę danych wygląda następująco:


<?php
$linkID = mysqł_connect('localhost', 'root', 'sesame');
mysql_create_db('new_db', $linkI D);
mysql_select_db('new_db');
$query = "CREATE TABLE new_table (id INT NOT NULL AUTO__INCREMENT PRIMARY KEY,
new_col VARCHAR(25) )";
Sresult = mysql_query($query);
$axe = mysql_drop_db (' new_db' ) ;
?>

Istnieją narzędzia realizujące te same zadania. Trzeba wypełnić formularz na stronie


WWW, a skrypt PHP tworzy bazę danych według ustawień. W większości przypadków
narzędzia te pozwalają na wykonywanie wielu funkcji administracyjnych, takich jak
sprawdzanie wielkości lub składowanie bazy. Jest to bardziej niebezpieczne, ponieważ
prawdopodobnie nie będziesz sprawdzał każdego wiersza skryptu pod kątem zabezpie-
czeń (ale stosunkowo wiele ludzi korzysta z tych narzędzi bez większych kłopotów).

Atrakcyjnym i popularnym narzędziem administracyjnym dla MySQL jest phpMy-


Admin, którego autorem jest Tobias Ratschiller. Możesz je skopiować ze strony:
www. htmlwizard. net/phpMyAdmin

Istnieje jeszcze kilka narzędzi, które najpewniej będą działać z MySQL.

Funkcje MySQL
Tabela 18.1 zawiera podsumowanie wszystkich funkcji MySQL. Argumenty w nawia-
sach kwadratowych są opcjonalne.
334______________________________________Część II » PHP i bazy danych

Tabela 18.1.
Funkcje PHP-MySQL

Nazwa funkcji Opis

mysql a f f e c t e d rows ( [ i d p o l e c z e n i a ] ) Używa się po zapytaniu INSERT, UPDATE lub


DELETE w celu sprawdzenia liczby zmienionych
wierszy
mysql change user (użytkownik, hasło, Zmienia użytkownika MySQL i otwiera połączenie
[baza], [id połączenia])
mysql close ([ id połączenia] ) Zamyka połączenia (zwykle niepotrzebne)
mysql connect ( [ h o s t ] , [ :port] , Otwiera połączenia do podanego komputera, portu,
[.•gniazdo], [ użytkownik] , [hasło]) gniazda wg podanego użytkownika i hasła. Wszystkie
parametry są opcjonalne
mysql create_db (nazwa Tworzy nową bazę MySQL na serwerze skojarzonym
[id poleczenia] ) z ostatnim otwartym połączeniem
mysql data seek (id wyniku, Przesuwa wewnętrzny wskaźnik do podanego numeru
numer wiersza) wiersza. Użyj funkcji pobierających dane do ich
odczytania
mysql db query ( zapytanie, Wybiera bazę i wysyła zapytanie przy użyciu tylko
nazwa bazy, [id p o l e c z e n i a ] ) jednej funkcji
mysql drop db (nazwa bazy [, Usuwa podaną bazę MySQL
id polacznia] )
mysql errno ( [ id poleczenia] ) Zwraca identyfikator błędu
mysql error ( [ id poleczenia] ) Zwraca komunikat o błędzie
mysql f e t c h array ( i d wyniku, Pobiera wynik i zwraca tablicę asocjacyjną. Typ
[ typ wyniku] ) wyniku: MYSQL^ASOC, MYSQL_NUM lub MYSQL_BOTH
(domyślnie)
mysql f e t c h f i e l d (id wyniku, Zwraca jako obiekt informacje na temat pól
[ n r pola] )
mysql f e t c h lengths (id wyniłru) Zwraca długość każdego pola w wyniku
mysql fetch object (id wyniku Pobiera wynik i zwraca go jako obiekt. Typ wyniku jak
[ typ wyniku] ) w mysql f e t c h a r r a y
mysql f e t c h row (id wyniku) Pobiera wynik i zwraca tablicę indeksowaną liczbami
mysql f i e l d name (id wyniJcu, nr pola) Zwraca nazwę podanego pola
mysql field seek (id wyniku, nr pola) Przesuwa wskaźnik wyniku do wybranego numeru pola.
Używa się z mysql f e t c h f i e l d
mysql f i e l d table ( i d wyniku, Zwraca nazwę tabeli podanego pola
nr pola)
mysql field type ( i d wyniku, nr pola) Zwraca typ podanego pola (np.: TINYINT, BLOB,
VARCHAR)
mysql field f lags ( i d wyniłru, Zwraca znaczniki skojarzone z polem (np.: NOT NULL,
nr pola) AUTO INCREMENT, BINARY)
Rozdział 18. » Funkcje PHP i MySQL__________________________________335

Tabela 18.1.
Funkcje PHP-MySQL (ciąg dalszy)

Nazwa funkcji Opis


raysql f i e l d len (id wyniku, nr pola) Zwraca długość podanego pola
mysql f r e e r e s u l t (id wyniku) Zwalnia pamięć używaną przez wynik
mysql insert id ( [ i d połączenia] ) Zwraca ID AUTO INCREMENT wyrażenia INSERT lub
FALSE, jeżeli ostatnim zapytaniem nie było INSERT
mysql list f i e l d s (baza, tabela, Zwraca ID wyniku. Używa się w mysql field bez
[id połączenia] ) wykonywania zapytania
mysql list d b s ( [ i d połączenia] ) Zwraca wskaźnik wyniku na mysqld. Używa się
z mysql tablename
mysql list tables (baza Zwraca wskaźnik wyniku do tablicy w bazie. Używa się
[id połączenia] ) z mysql tablename
mysql num f i e l d s (id wyniku) Zwraca liczbę pól w wyniku
mysql num rows (id wyniku) Zwraca liczbę wierszy w wyniku
mysql pconnect ( [host] , [.-port], Otwiera trwałe połączenie do bazy danych. Wszystkie
[.•gniazdo], [ użytkownik] , [hasło]) argumenty są opcjonalne. Bądź ostrożny
— mysql close i zakończenie skryptu nie zamykają
połączenia
mysql query ( zapytanie, Wysyła zapytanie do bazy danych. Pamiętaj, aby średnik
[id poleczenia] ) umieszczać poza ciągiem zapytania
mysql result (id wyniku, id wiersza, Zwraca jednopolowy wynik. Identyfikator pola może
id pola) być numerem pola (0), nazwą pola (FirstName) lub
nazwą wraz z tabelą (myf ield.mytable)
mysql select db(baza Wybiera bazę do pracy
[id połączenia] )
mysql t a b l e n a m e ( i d wyniku, Używa się z funkcjami mysqljist do zwrócenia
id_tabeli) wartości wskazywanej przez wskaźnik wyniku

Podsumowanie
Funkcje PHP do obsługi MySQL są bardzo łatwe w użyciu, mimo że czasami ich nazwy
są mylące. W celu interakcji z bazą danych musimy otworzyć połączenie z serwerem
bazy, wybrać bazę i wykonać zapytanie, które zwróci identyfikator wyniku. Wynik ten
jest rodzajem potwierdzenia, które określa, czy operacja się udała, czy nie.

Jeżeli po wykonaniu zapytania SELECT zostaną zwrócone dane, należy użyć jednej
z funkcji pobierającej wynik zapytania. Dane pobrane z bazy danych MySQL przecho-
336______________________________________Część II » PHP i bazy danych

wywane sąw buforze, z którego mogą być pobrane jedną z funkcji. Jeżeli chcesz jesz-
cze raz odczytać wynik, możesz użyć funkcji mysql_data_seek(), aby przestawić
wskaźnik wiersza do pozycji 0.

PHP posiada również wiele funkcji, zwracających informacje na temat samej bazy da-
nych lub ostatniej operacji. Dwie najczęściej używane funkcje tego typu to my-
sql_num__rows ( ) , która zwraca liczbę wierszy wyniku, oraz m y s q l _ i n s e r t _ i d ( ) ,
zwracająca identyfikator ostatniej instrukcji INSERT.

PHP sam obsługuje połączeń bez konieczności używania odpowiednich identyfikatorów


połączenia i wskaźników wyniku. Możesz jednak korzystać z wielokrotnych połączeń
do bazy danych na jednej stronie WWW. W tym przypadku możesz użyć dokładnie
tych samych funkcji i składni, ale do większości funkcji trzeba przekazywać odpowied-
nie identyfikatory połączenia.
Rozdział 19.
Wyświetlanie zapytań
w tabelach
W tym rozdziale:
+ Przekształcanie tabel bazy danych na tabele HTML
* Uniwersalne funkcje wyświetlające tabelę na podstawie instrukcji SELECT
* Wyświetlanie danych złożonych tabel relacyjnych
* Tworzenie danych relacyjnych przy użyciu INSERT

Większość funkcji realizowanych przez PHP stworzona jest po to, aby pomóc progra-
miście przenieść dane z bazy na stronę WWW. Dane te mogą być oglądane, dodawane
oraz usuwane w wyniku reakcji witryny na akcje podejmowane przez użytkownika.

W tym rozdziale ograniczymy się pokazania różnych sposobów użycia PHP do ogląda-
nia zawartości bazy danych bez jej zmieniania. Będziemy używać tylko instrukcji SQL
SELECT, a pobrane dane będziemy wyświetlać w tabelach HTML. Użyjemy jednego
przykładu do pokazania różnych technik, pisząc przy okazji kilka przydatnych funkcji,
których można użyć we własnych programach. Na koniec zajmiemy się tworzeniem
przykładowych danych przy użyciu instrukcji INSERT.

W rozdziale tym zwrócimy uwagę na dwa zagadnienia dotyczące wydajności progra-


mowania.
* Funkcje nadające się do powtórnego użycia. Problem wyświetlania tabel ciągle
przewija się przy tworzeniu witryn współpracujących z bazą danych. Jeżeli
projekt nie jest zbyt skomplikowany, możesz użyć tej samej prostej funkcji.
* Wybór pomiędzy technikami. Możesz zastosować jedno z rozwiązań alterna-
tywnych: wybrać pomiędzy wydajnością, czytelnością lub pracochłonnością.
338______________________________________Część II « PHP i bazy danych

W tym rozdziale używamy tylko bazy danych MySQL i jej funkcji, ale te
techniki wyświetlania można bezpośrednio przenieść do prawie każdej
bazy danych SQL obsługiwanej przez PHP.

Tabele HTML i tabele bazy danych


Na początek nieco terminologii. Niestety zarówno relacyjne bazy danych, jak i HTML
używają tego samego określenia „tabela", ale słowo to oznacza różne rzeczy. Tabela
bazy danych przechowuje dane w kolumnach mających zdefiniowane nazwy i typy.
Dane te mogą być później odczytane. Tabela HTML jest konstrukcją wskazującą prze-
glądarce sposób formatowania danych do postaci tablicy prostokątnej. Będziemy starali
się jasno przedstawić, o którą tabelę chodzi.

Przekształcenie jeden w jeden


Tabele HTML sąw rzeczywistości zbudowane z wierszy (konstrukcja „<TRX/TR>"),
a kolumny nie są niezależne. Każdy wiersz posiada określoną liczbę elementów tabeli
(konstrukcja „<TD> </TD>"), co powoduje, że ładny prostokątny układ osiągniemy tylko
wtedy, gdy mamy tę samą liczbę znaczników T D dla każdego znacznika TR (nie ma
znacznika <TC>, który pozwoliłby na tworzenie tabeli według kolumn). W odróżnieniu
od tego podejścia, pola (zwane kolumnami) w tabeli bazy danych są pierwotną jednost-
ką, liczba wierszy nie ma znaczenia. W tym rozdziale skupimy się na wyświetlaniu ta-
bel i zapytań w takiej postaci, aby każde pole bazy danych było wyświetlane w swojej
„kolumnie" HTML (zwykle w tabeli występuje więcej wierszy niż kolumn, a ludzie
częściej używają przewijania w pionie niż w poziomie). Jeżeli potrzebujesz odwzorować
pola bazy danych do wierszy tabeli HTML, po prostu odwróć to ćwiczenie.

Najprostszym przypadkiem jest taki, w którym struktura tabeli bazy danych lub zapyta-
nia odpowiada strukturze tabeli HTML, którą chcemy wyświetlić. Jest to sytuacja, gdy
obiekt bazy danych posiada m kolumn i n wierszy — chcemy wyświetlić prostokątną
siatkę m na n wypełnić wszystkie komórki.

Przykład: wyświetlanie jednej tabeli


Napiszmy prosty kod pobierający z bazy danych zawartość pojedynczej tabeli i wy-
świetlający wynik na ekranie. Poniżej przedstawiamy schemat działania.
* Podłączenie do bazy danych.
* Tworzenie zapytania do bazy danych.
* Wysłanie zapytania do bazy danych i zapamiętanie identyfikatora wyniku.
* Sprawdzenie liczby kolumn (pól) w każdym wierszu wyniku.
Rozdział 19. » Wyświetlanie zapytań w tabelach___________________________339

* Rozpoczęcie tabeli HTML.


4 Iteracja po wszystkich wierszach tabeli z wypisaniem par <TRX/TR> w celu
utworzenia odpowiedniego wiersza tabeli.
4 W każdym wierszu odczyt kolejnych pól i wyświetlenie ze znacznikami
<TDX/TD>.

* Zakończenie tabeli HTML.


* Zamknięcie połączenia z bazą danych.

Teraz na podstawie powyższych punktów należy napisać zgrabną funkcję, której będzie
można używać w wielu przypadkach. Nie dodamy pierwszego i ostatniego punktu: two-
rzenia i zamykania połączenia z bazą danych. Możesz użyć tej funkcji więcej niż jeden
raz na stronie i w tej sytuacji nie ma sensu za każdym razem otwierać i zamykać połą-
czenia. Założymy, że mamy otwarte wcześniej połączenie i wraz z nazwą tabeli przeka-
żemy je do funkcji jako argument.

Na wydruku 19.1 pokazujemy taką funkcję wbudowaną w kompletną stronę PHP, która
używa tej funkcji do wyświetlenia zawartości kilku tabel.

Wydruk 19.1. Wyświetlanie tabeli_______________________________________


<?php
include("/home/phpbook/phpbook-vars.inc");
$global_dbh = mysql_connect(Shostname, $username, $password);
mysql_select_db(Sdb, Sglobal_dbh);

function display_db_table($tablename, $connection)


(
$query_string = "select * from $tablename";
3result_id = mysql_query($query_string, $connection);
$column_count = mysql_num_fields($result_id);

print("<TABLE BORDER=l>\n");
while ($row = mysql_fetch_row($result_id))
(
print("<TR ALIGN=LEFT VALIGN=TOP>");
for ($column_num = 0;
$column_num < $column_count;
$column_num++)
print("<TD>$row[$column_num]</TD>\n");
print("</TR>\n");
)
print("</TABLE>\n");
)
?>
<HTML>
<HEAD>
<TITLE>Miasta i kraje</TITLE>
</HEAD>
<BODY>

<TABLEXTRXTD>
<?php display_db_table("country", $global_dbh); ?>
</TD><TD>
<?php display_db_table("city", Sglobal_dbh); ?>
</TDX/TR></TABLE></BODYX/HTML>
340____________________________________Część II » PHP i bazy danych

Kilka uwag na temat tego skryptu:


** Mimo że skrypt odwołuje się do konkretnych tabel bazy danych, funkcja di-
splay_db_table { ) jest ogólna. Możesz umieścić ją w osobnym pliku i dołą-
czać do dowolnego skryptu.
* Na samym początku skrypt ładuje plik zawierający przypisane do odpowied-
nich zmiennych: nazwę bazy danych, użytkownika bazy danych oraz jego ha-
sło. Skrypt używa tych zmiennych do przyłączenia się do MySQL i wyboru
bazy danych (umieszczenie tego pliku poza publicznie dostępnymi katalogami
drzewa witryny WWW powoduje zwiększenie poziomu bezpieczeństwa, w po-
równaniu do sytuacji, gdy umieścimy te informacje bezpośrednio w kodzie).
* Do wydruku wierszy oraz poszczególnych elementów tablicy w ciele funkcji
używamy pętli. Możemy również zastosować pętle for do pobierania wierszy,
ponieważ odczytaliśmy liczbę pobranych wierszy za pomocą funkcji mysql_
num_rows() .

* Główna pętla while korzysta z tego, że w PHP wartością przypisania jest


przypisywana wartość. Do zmiennej $row przypisywany jest wynik funkcji
mysql_f etch_row ( ) , który jest tablicą wartości wiersza lub wartością FALSE,
jeżeli odczytano już wszystkie wiersze. Jeżeli odczytamy wszystkie wiersze,
$row będzie miało wartość FALSE, a to oznacza, że całe wyrażenie jest fałszy-
we i pętla while kończy się.
* Umieszczamy znaki zmiany wiersza na końcu wierszy tabeli, dlatego źródło
HTML będzie miało czytelną strukturę, gdy będzie oglądane lub drukowane
z przeglądarki. Zauważ, że nie są to znaki zmiany wiersza w HTML (<BR>)
i nie wpływają ona na wygląd wynikowej strony WWW (jeżeli chcesz utrudnić
innym poznanie wygenerowanego kodu HTML, możesz w ogóle zrezygnować
ze znaków końca wiersza).

Przykładowe tabele
Aby zobaczyć wynik działania skryptu zamieszczonego na wydruku 19.1, popatrz na
rysunek 19.1, który pokazuje zawartość przykładowych tabel „country" oraz „city".

Tabele te mają następującą strukturę:


Country:
ID int (auto increment primary key)
Continent varchar(SO)
Countryname varchar(50)
City:
ID int (auto increment primary key)
countrylD int
cityname varchar(SO)

Możesz uważać te tabele za wstępny projekt bazy danych, będącej atlasem dostępnym
w Internecie. W tym projekcie zastosowaliśmy konwencję, według której tabela posiada
pole o nazwie ID, które stanowi jej klucz główny, i kolejne wartości numeryczne przy-
pisywane automatycznie podczas wstawiania nowego wiersza. Mimo że nie wynika to
Rozdział 19. » Wyświetlanie zapytań w tabelach 341

Rysunek 19.1.
Wyświetlanie
tabel bazy danych

jasno z opisu, tabele te posiadają wbudowaną jedną relację — pole „countrylD" w ta-
beli „city" odpowiada polu ID z tabeli „country". Reprezentuje to przynależność miasta
do kraju (jeżeli będziesz projektował prawdziwą bazę danych dla atlasu, będziesz mu-
siał podzielić tabelę „country" na tabele „country" i „continent", połączone ze sobą
analogiczną relacją).

Aby zobaczyć, w jaki sposób stworzyliśmy te tabele i wypełniliśmy je


danymi, zajrzyj do ostatniej części tego rozdziału.

Ulepszanie wyświetlania
Nasza pierwsza wersja tej funkcji ma ograniczenia: działa tylko z jedną tabelą, nie za-
pewnia kontroli błędów i wyświetla dane w bardzo prosty sposób. Zajmiemy się kolejno
tymi problemami, naprawiając je w następnej wersji (jeżeli chcesz od razu obejrzeć
ulepszoną wersję funkcji, odszukaj wydruk 19.2).

Wyświetlanie nagłówków kolumn


Pierwsza wersja naszej funkcji, wyświetlającej tabelę, pokazuje tylko komórki tabeli
bez określania, jakie jest znaczenie poszczególnych pól. W HTML istnieje konwencja,
aby używać elementów <TH> do nagłówków kolumn lub wierszy. W większości prze-
glądarek tak oznaczone elementy są wyświetlane pogrubioną czcionką. Możemy ulep-
szyć naszą funkcję i opcjonalnie wyświetlać nagłówki kolumn, bazując na nazwach pól
tabeli. Aby pobrać nazwy pól tabeli, należy użyć funkcji mysql_f ield_name ( ) .
342____________________________________Część II « PHP i bazy danych

Kontrola błędów
Pierwsza wersja naszej ftmkcji zakładała, że odpowiednio napisaliśmy zapytania, baza
danych pracuje prawidłowo —jeżeli coś pójdzie nie tak, otrzymasz błędy losowe. Czę-
ściowo można to rozwiązać poprzez dodanie wywołania die ( ) do zapytań bazy danych
— gdy wystąpi błąd, wyświetlony zostanie odpowiedni komunikat.

Kosmetyka
Nasza tabela wygląda ciągle tak samo. Najprościej jest napisać funkcję, która pozwoli
na przekazanej jako argumentu ciągu, który jest wklejany do definicji tabeli HTML.
Jest to bardzo nieeleganckie rozwiązanie, którego na pewno nie polecą zwolennicy ar-
kuszy stylów, ale pozwala na bezpośrednią kontrolę nad niektórymi elementami, odpo-
wiedzialnymi na wygląd tabeli, bez potrzeby pisania całkowicie nowej funkcji.

Wyświetlanie dowolnych zapytań


Byłoby świetnie, jeżeli istniałaby możliwość skorzystania z relacyjności bazy danych
i wyświetlenia wyniku złożonego zapytania, a nie tylko pojedynczej tabeli. Nasza funk-
cja ma obecnie zapytanie wbudowane w ciało. Jest to zawsze „select * from ta-
bela", w którym tabela jest przekazaną do funkcji nazwą tabeli. Przeróbmy naszą
funkcję wyświetlającą zawartość tabeli na funkcję wyświetlającą wynik zapytania. Na-
stępnie odtwórzmy naszą funkcję wyświetlającą zawartość tabeli jako prostą funkcję
korzystającą z funkcji wyświetlającej wynik zapytania. Dołóżmy do tych dwóch funkcji
kosmetyczne poprawki oraz lepszą kontrolę błędów. Wynik tych działań pokazany jest
na wydruku 19.2.

Wydruk 19.2. Wyświetlanie zapytania_________________________________________


<?php
include("/home/phpbook/phpbook-vars.inc");
$global_dbh = mysql_connect(Shostname, $username, $password);
if (!$global_dbh)
die("Brak połączenia z baza danych");
mysql_select_db(Sdb, $global_dbh);

function display_db_query($query_string, $connection,


$header_bool, $table_params)
(
// Wykonaj zapytanie
$result_id = mysql_query ($query__string, $connection)
or die("display_db_query:". mysql_error());

// odczytaj liczbę kolumn wyniku


$column_count = mysql_num_fields($result_id)
or die("display_db_query:". mysql_error());

// Znacznik TABLE posiada opcjonalny argument HTML


// przekazany do funkcji
print("STABLE $table_params >\n"(;

// Opcjonalnie zastosuj nagłówki kolumn tabeli


if ($header_bool)
f
print("<TR>");
for ($column_num = 0;
$column num < $column count;
Rozdział 19. » Wyświetlanie zapytań w tabelach___________________________343

$column_num++)
(
$field_name =
mysql_field_name($result_id, $column_num);
print("<TH>$field_name</TH>");
)
print("</TR>\n");
(
// Wyświetlenie tabeli
while ($row = mysql_fetch_row($result_id))
{
print("<TR ALIGN=LEFT VALIGN=TOP>");
for ($column_num = 0;
$cołumn_num < $column_count;
Scolumn_num++)
(
print("<TD>Srow[$column_num]</TD>\n");
)
print("</TR>\n");
)
print("</TABLE>\n"); )

function display_db_table($tablename, $connection,


Sheader_bool, $table_params)
(
Squery^string = "select * from $tablename";
display_db_query($query_string, $connection,
$header_bool, 5table_params);
)
?>

<HTMLXHEADXTITLE>Kraje i miasta</TITLEX/HEAD>
<BODY>
<TABLEXTRXTD>
<?php display_db_tabłe("country", $global_dbh,
TRUE, "BORDER=2"); ?>
</TDXTD>
<?php display_db_table("city", $global_dbh,
TRUE, "BORDER=2"); ?>
</TDX/TR></TABLEX/BODYX/HTML>

Wynik użycia tego kodu do tej samej, co poprzednio, zawartości bazy danych pokazany
jest na rysunku 19.2. Jedyną widoczną różnicą są nagłówki kolumn. Jednak dzięki po-
działowi funkcji na części otrzymujemy kolejną funkcję; możemy dzięki niej wyświe-
tlić w ten sam sposób dowolne zapytanie, które łączy dane z wielu tabel.

Złożone odwzorowania
Do tej pory zajmowaliśmy się tylko bardzo prostymi przypadkami zależności pomię-
dzy tabelą HTML i wynikiem zapytania — każdy wiersz wyniku odpowiadał jednemu
wierszowi w tabeli. Kod obsługujący ten przypadek składa się z dwóch zagłębionych
pętli. Czasami struktura tabeli HTML ma skomplikowaną relację z relacyjną strukturą
tabel bazy.
344 Część II » PHP i bazy danych

Rysunek 19.2.
Wyświetlanie wyniku
zapytania

Wiele zapytań albo skomplikowane wyświetlanie


Załóżmy, że zamiast wyświetlać oddzielne tabele „city" oraz „country", chcemy połą-
czyć j e w jedną tabelę.

Widoki i procedury przechowywane


Nasza funkcja wyświetlająca zapytanie zakłada podział ról pomiędzy kod
PHP i system bazy danych. Kod PHP wysyła dowolne zapytanie, które baza
danych przetwarza i zwraca wynik. W szczególności oznacza to, że system
bazy danych analizuje zapytanie i określa najlepszy sposób wygenerowania
wyniku. Jest to jeden z powodów, dlaczego bazy danych są dosyć kosztowne.
Jest to najlepsze rozwiązanie w przypadkach, kiedy nasz kod może wygene-
rować wiele zapytań. Niektóre bazy danych wcześniej konstruują zapytania,
co pozwala im zoptymalizować sposób ich wykonania. Jedną z tych kon-
strukcji jest „widok" w MS SQL Server i kilku innych systemach baz danych
— po zadeklarowaniu zapytania jako widoku może być ono traktowane jak
prawdziwa tabela. Podobnym mechanizmem są procedury przechowywane,
które działają bardzo podobnie do widoków, ale pozwalają również na prze-
kazanie argumentów wklejanych do zapytania. Jeżeli zauważasz obniżenie
wydajności zapytań, możesz sprawdzić, jakie możliwości optymalizacji ma
twoja baza.
Rozdział 19. » Wyświetlanie zapytań w tabelach___________________________345

Możemy napisać wyrażenie SELECT, które odpowiednio łączy tabele:


select country.continent, country.countryname,
city.cityname
from country, city
where city.countrylD = country.ID
order by continent, countryname, cityname

Mamy możliwość użycia naszej funkcji wyświetlającej wynik zapytania — wszystko,


co trzeba zrobić, to przekazanie powyższego wyrażenia. Zostanie wyświetlona tabela
miast wraz z kontynentami i krajami. Jednak zobaczymy osobny wiersz dla każdego
miasta, kontynent i miasto będą powtórzone kilka razy. Chcielibyśmy mieć jedną nazwę
skojarzoną z kilkoma tytułami. Jest to przypadek, kiedy wyświetlana struktura różni się
od struktury wyniku najbardziej wygodnego zapytania.

Jeżeli chcemy otrzymać bardziej skomplikowane odwzorowania, mamy wybór: może-


my przerzucić problem na zapytania bazy danych lub napisać bardziej skomplikowaną
funkcję. Spróbujmy wykonać oba te warianty (w każdym z tych przykładów odchodzi-
my od ogólnego rozwiązania, jakie wcześniej napisaliśmy, na rzecz rozwiązania szcze-
gółowego problemu wyświetlania tabeli).

Użycie kilku zapytań


Jeżeli chcemy wypisać tylko jeden wiersz HTML na kraj, możemy utworzyć zapytanie
dla krajów, a następnie wykonać kolejne zapytanie dla odpowiadających im miast, dla
każdego wiersza w tabeli „country". Funkcję realizującą tę strategię pokazano na wy-
druku 19.3.

Wydruk 19.3. Wyświetlanie przy użyciu wielu zapytań______________________________


<?php
include("/home/phpbook/phpbook-vars.inc");
/* otwarcie połączenia z bazą danych */
$global_dbh = mysql_connect($hostname, $username, Spassword);
mysql_select_db($db, Sglobal_dbh);

function display_cities($db_connection)
{
/* Wyświetlenie tabeli miast i krajów */
$country_query = "select id, continent, countryname
from country
order by continent, countryname";
$country_result =
mysql_query($country_query, $db_connection);

/* Początek tabeli, wbudowany nagłówek tabeli */


print("<TABLE BORDER=l>\n");
print ("<TRXTH>Kontynent</THXTH>Kraj</TH>
<TH>Miasta</THx/TR>") ;

/* pętla po krajach */
while ($country_row = mysql_fetch_row($country_result)}
f
/* odczytanie danych o kraju */
Scountry_id = $country_row[0];
$continent = $country_row[l];
$country_name = Scountry_row[2];

print("<TR ALIGN=LEFT VALIGN-TOP>");


346____________________________________Część II » PHP i bazy danych

print("<TD>Scontinent</TD>");
print("<TD>Scountry_name</TD>");

/* początek komórki tabeli z listą miast V


print("<TD>");
$city_query = "select cityname from city
where countrylD = $country_id
order by cityname";
$city_result =
mysql_query($city_query, $db_connection)
OR die(mysql_error());
/* pętla po miastach */
while ($city_row = mysql_fetch_row($city_result))
{
$city_name = $city_row[0];
print("$city_name<BR>");
)
/* Zamknięcie komórki miast i wiersza krajów */
print("</TD></TR>");
}
print("</TABLE>\n");
)
?>

<HTML>
<HEAD>
<TITLE>Miasta w krajach</TITLE>
</HEAD>
<BODY>
<?php
display_cities(Sglobal_dbh);
?>
</BODY>
</HTML>

Strategia jest bardzo prosta: wewnętrzna pętla używa jednego zapytania do przeglądania
wszystkich krajów, zapamiętując nazwę kraju i jego ID. Następnie pole ID jest używane
do odszukania wszystkich miast należących do kraju. Zauważ, w jaki sposób wbudo-
wujemy zmienną $countryid w wewnętrzne zapytanie — ciąg zapytania będzie nieco
inny za każdym przebiegiem pętli po krajach.

Proste? Tak. Efektywne? Prawdopodobnie nie. Funkcja ta wykonuje oddzielne zapyta-


nie o miasta dla każdego kraju. Jeżeli w bazie danych będzie 500 krajów, funkcja ta
wykona 501 zapytań (dodatkowe jedno zapytanie, aby pobrać listę krajów). Koszt tego
rozwiązania zależy od tego, jak efektywnie serwer bazy danych analizuje zapytania
i oblicza plan wykonania, ale suma wszystkich tych zapytań na pewno zabierze więcej
czasu niż proste zapytanie, od którego rozpoczęliśmy tę część rozdziału.

Przykład skomplikowanego wyświetlania


Teraz rozwiążemy ten sam problem używając innej strategii. Zamiast wielu zapytań
wykonamy tylko jedno, ale będziemy selektywnie wyświetlać jego wynik, więc każdy
wiersz tabeli HTML będzie odpowiadał więcej niż jednemu wierszowi tabeli bazy da-
nych (wydruk 19.4). Wynik widziany w przeglądarce jest identyczny jak w poprzednim
przykładzie.
Rozdział 19. » 347
Wyświetlanie zapytań w tabelach___________________________347

Wydruk 19.4. Skomplikowane wyświetlanie z jednym zapytaniem________________________


<?php
include{"/home/phpbook/phpbook-vars.inc");
/* otwarcie dla tej strony jednego połączenia z baza danych */
Sglobal_dbh = mysql_connect(Shostname, Susername, Spassword);
mysql_select_db($db, Sglobal_dbh);

function display_cities ($db_connection}


i
/* Drukuje tabele krajów i ich miast,
selektywnie drukując tylko jeden wiersz tabeli HTML
na kraj */
$query = "select country.id,
country.continent, country.countryname,
city.cityname
from country, city
where country.id = city.countrylD
order by country.continent,
country.countryname,
city.cityname";
$result_id =
mysql_query($query, $db_connection)
OR die(mysql_error($query)) ;

/* Początek tabeli, wbudowany nagłówek tabeli */


print("<TABLE BORDER=l>\n");
print("<TH>Kontynent</TH><TH>Kraj</TH>
<TH>Miasta</THX/TR>") ;

/* Inicjalizacja ID dla "poprzedniego" kraju.


Bazujemy na tym, że Country.ID jest numerowany
od l, więc poprzedni ID =0 oznacza, że bieżący kraj
jest pierwszy w tabeli */
$old_country_id = 0;

/* pętla przez wiersze wyniku (jeden na miasto) */


while (Srow_array = mysql_fetch_row($result_id))
{
$country_id = Srow_array{0];
/* jeżeli mamy nowy kraj */
if ($country_id != Sold_country_id)
{
/* odczytaj informacje o kraju */
$continent = $row_array[1];
$country_narae = Srow_array[2];

/* Jeżeli był poprzedni kraj, zamknij


komórkę miasta i wiersz kraju */
if ($old_country_id != 0)
print ( "</TDX/TR>\n" ) ;

/* Rozpocznij wiersz dla nowego kraju,


i rozpocznij komórkę dla miast */
print("<TR ALIGN=LEFT VALIGN=TOP>");
print("<TD>Scontinent</TD>");
print ("<TD>$country_name</TDXTD>") ;

/* Nowy kraj już nie jest nowy :)) */


$old_country_id = $country_id;
l
/* Za każdym razem drukujemy jedynie nazwę miasta */
$city_name = $row_array[3];
print("$city_name<BR>");
}
/* Zamknij ostatni wiersz i tabelę */
print ( "</TDX/TRX/TABLE>" ) ;
)
?>
<HTMLXHEADXTITLE>Miasta w krajach</TITLEX/HEAD>
348_____________________________________Część II » PHP i bazy danych

<BODY>
<?php d i s p l a y _ c i t i e s ( S g l o b a l ^ d b h ) ;
?>
</BODYX/HTML>

Kod ten jest dosyć sprytny — mimo że odczytuje kolejne wiersze, a wszystkie druko-
wane elementy pochodzą z bieżącego wiersza, drukuje nazwę kraju, jeżeli się zmieniła
(kontynenty powtarzają się). Zmiana kraju jest wykrywana poprzez monitorowanie
wartości pola ID, pochodzącego z tabeli „country". Zmiana kraju jest również sygnałem
do wydrukowania kodu HTML, potrzebnego do zakończenia poprzedniego wiersza
i rozpoczęcia nowego. Kod musi ponadto dodać znaczniki HTML potrzebne do rozpo-
częcia pierwszego i zakończenia ostatniego wiersza tabeli.

Tworzenie przykładowych tabel


Pokażemy teraz skrypt PHP do tworzenia tabel używanych w poprzednich przykładach
(dane takie normalnie są tworzone poprzez interakcję z My SQL, ale zdecydowaliśmy
się szanować tytuł książki i zrobić to za pomocą PHP). Kod (wydruk 19.5) jest kodem
specjalnego przeznaczenia, który może być użyty tylko raz. Nie jest to model ogólnego
stylu, ale posiada interesujący przykład wyrażenia SQL INSERT.

Wydruk 19.5. Tworzenie przykladowych tabel_____________________________________


<?php
include("/home/phpbook/phpbook-vars.inc"};
Sglobal_dbh = mysql_connect(Shostname, Susername, $password);
mysql_select_db(Sdb, Sglobal_dbh);

function add_new_country($dbh, $continent, Scountryname,


$city_array)

(
Scountry__query =
"insert into country (continent, countryname)
values ('$continent', '$countryname')";
$result_id = mysql_query($country_query)
OR die($country_query . mysql_error{});
if ($result_id)
{
ScountrylD = mysql_insert_id($dbh);
for (Scity = current (Scity__array) ;
$city;
$city = next($city_array))
{
$city_query =
"insert into city {countrylD, cityname)
values ($countryID, '$city')";
mysql_query($city_query, $dbh)
OR die($city_query. mysql_error());
)
}
)
function populate_cities_db($dbh)
(
/* Jeżeli tabele istnieją, usuń je - pozwala na wykonanie funkcji
więcej niż raz */
mysql_query("drop table city", $dbh);
Rozdział 19. » Wyświetlanie zapytań w tabelach___________________________349

mysql_query("drop table country", $dbh);

/* tworzenie tabel */
mysql_query("create table country
(ID int not null auto_increment primary key,
continent varchar(50),
countryname varchar(50))",
$dbh)
OR die(mysql_error());
mysql_query("create table city
(ID int not null auto_increment primary key,
countrylD int not null,
cityname varchar(50})",
Sdbh)
OR die(mysql_error());

/* zapis danych w tabeli */


add_new_country($dbh, 'Africa', 'Kenya',
array('Nairobi','Mombasa','Meru')) ;
add_new_country($dbh, 'South America', 'Brazil',
array('Rio de Janeiro', 'Sao Paulo',
'Salvador', 'Belo Horizonte')) ;
add_new_country{$dbh, 'North America', 'USA1,
array('Chicago', 'New York', 'Houston', 'Miami'));
add_new_country($dbh, 'North America', 'Canada',
array!'Montreal','Windsor','Winnipeg'));

print("Przykładowa baza danych utworzona!<BR>");


(
?>

<HTMLXHEAD><TITLE>Tworzenie przykładowej bazy danych</TITLEX/HEAD>


<BODY>
<?php populate_cities_db($global_dbh); ?>
</BODYX/HTML>

Za pomocą tego skryptu możesz utworzyć przykładową bazę danych w twoim kompute-
rze, zakładając oczywiście, że masz skonfigurowane PHP i MySQL. Plik phpbook-
vars.inc, zawierający nazwę użytkownika, hasło i nazwę bazy danych, znajduje się w
odpowiednim katalogu.

Kod ten wysyła zapytania (z wbudowanymi zmiennymi), ale tym razem zapytania za-
wierają instrukcję INSERT, które tworzą wiersze tabeli. Zazwyczaj wstawiane dane są
po prostu ciągami przekazanymi do funkcji, ale do przekazania dowolnej liczby miast
w krajach zastosowaliśmy tablicę.

Jedyną sztuczką użytą przy tworzeniu tych tabel jest tworzenie struktury relacyjnej.
Chcemy, aby każdy wiersz miasta miał odpowiednią wartość pola country ID, która
powinna być taka sama jak wartość ID z odpowiedniego wiersza tabeli krajów. Jednak
identyfikatory kraju są nadawane automatycznie przez MySQL i nie mamy nad nimi
kontroli. W jaki sposób poznać właściwą wartość dla countrylD? Z pomocą przycho-
dzi bardzo użyteczna funkcja mysql_insert_id ( ) , który zwraca identyfikator nadany
w ostatnim zapytaniu INSERT, wykonanym na określonym połączeniu do bazy. Wsta-
wiamy nowy kraj, odczytujemy identyfikator przydzielony do nowego wiersza i uży-
wamy go podczas wykonywania zapytania wstawiającego miasto.
350_______________________________________Część II » PHP i bazy danych

Podsumowanie
Interakcja z bazą danych jest jednym z zadań, w którym PHP naprawdę ma coś do po-
wiedzenia. Częstym zadaniem realizowanym w kodzie współdziałającym z bazą jest
atrakcyjne wyświetlanie zawartości bazy danych. Jedną z możliwości wyświetlania jest
odwzorowanie zawartości bazy danych w odpowiednich elementach tabeli HTML.

Jeżeli odwzorowanie jest dostatecznie proste, możesz stworzyć uniwersalną funkcję po-
bierającą określone nazwy tabel lub całe wyrażenia SELECT, wyświetlające wynik
w tabeli. Jeżeli potrzebujesz bardziej skomplikowanej kombinacji danych z tabel, praw-
dopodobnie będziesz musiał stworzyć wyspecjalizowaną funkcję. Powinieneś zastoso-
wać takie zapytania SQL, które zwracaj ą potrzebne dane w odpowiedniej kolejności, co
pozwoli na selektywne drukowanie niepowtarzających się danych.

Pokazaliśmy też przykład wypełniania zestawu tabel przy użyciu wyrażeń INSERT. Po-
za nim wszystkie pokazane w tym rozdziale techniki były technikami „tylko do odczy-
tu" i nie zmieniały zawartości bazy danych. W kolejnym przykładzie zobaczymy, w jaki
sposób uzyskać ściślejsze związki z bazą danych dzięki łączeniu zapytań SQL i formu-
larzy HTML.
Rozdział 20.
Tworzenie formularzy
z zapytań
W tym rozdziale:
* Statyczne formularze HTML
* Samoprzetwarzanie
* Obsługa formularzy
* Formularze zależne od zmiennych
* Formularze zależne od zapytań

Obsługa formularzy jest jedną z najlepsz ych funkcji realizowanych przez PHP. Połą-
czenie HTML do tworzenia formularzy wprowadzania danych, PHP do obsługi danych
i bazy do ich przechowywania są podstawą różnych bardzo użytecznych zastosowań
WWW.

Formularze HTML
Wiesz już, co jest potrzebne do stworzenia dobrych formularzy obsługiwanych przez
PHP i bazę danych. Poniżej przedstawiamy kilka specyficznych punktów PHP, o których
należy pamiętać.
* Zawsze używaj NAME do wszystkich elementów wprowadzania danych (INPUT,
SELECT, TEXTAREA itd.). Nawy tych atrybutów zostaną przekształcone na nazwy
zmiennych w PHP. Jeżeli twój edytor graficzny nie pozwala na ustawienie tych
nazw, musisz pamiętać o ręcznym wprowadzeniu atrybutów NAME.
* Atrybut NAME na formularzu nie musi być zgodny z nazwą pola w bazie da-
nych, ale powinien.
352____________________________________Część II » PHP i bazy danych

* Możesz (i często powinieneś) podawać wartość VALUE zamiast zezwalać PHP


na używanie wartości domyślnych. Unikaj, o ile to możliwe, podstawiania do
wartości numerycznej tekstu, ponieważ baza danych dużo wolniej porównuje
teksty niż liczby.
4 Pamiętaj, że możesz przekazać ukryte wartości pomiędzy formularzami (lub
stronami) przy użyciu elementu HIDDEN.
* Pamiętaj, że możesz przekazać wiele wartości w tablicy, ale musisz poinformo-
wać użytkownika o takiej możliwości.

Samoprzetwarzanie
Formularz HTML jest po prostu ładną otoczką graficzną, nałożoną na prostą metodę
przesyłania danych do serwera. W chwili obecnej nie potrafią one nic poza przesłaniem
danych do skryptu obsługi formularza, który musi być napisany w jakimś języku pro-
gramowania.

Jednak za pomocą PHP można połączyć w jednym skrypcie formularz i program go ob-
sługujący. Właściwie to w PHP obsługa samoprzetwarzania jest tak prosta i użyteczna,
że prawdopodobnie nie będziesz stosować oddzielnego formularza i skryptu obsługi.

Samoprzetwarzanie to proces łączenia jednego lub więcej formularzy i programów ob-


sługi w jednym skrypcie. Przy użyciu formularza HTML dane są wysyłane do skryptu
jeden lub więcej razy. Samoprzetwarzanie jest realizowane w najprostszej możliwej
formie: poprzez podanie samego siebie jako argument TARGET w znaczniku FORM:
<FORM METHOD="POST" ACTION="skrypt.php">

lub użycie zmiennej PHP:


<FORM METHOD="POST" ACTION="<?php print("$PHP_SELF"); ?>">

Mimo że zawsze możesz użyć nazwy pliku, w Uniksie zalecane jest


stosowanie zmiennej wbudowanej $PHP_SELF. W ten sposób plik bę-
dzie prawidłowo obsługiwany, jeżeli zmienisz jego nazwę lub przenie-
siesz go (oczywiście do katalogu obsługiwanego przez PHP). Pojawiły
się jednak informacje o problemach z tą zmienną w Windows.

Wspaniałą zaletą samoprzetwarzania jest możliwość wbudowywania logiki do formularzy


HTML. Możesz np. wyświetlać nieco inne formularze w zależności od tego, skąd zostały
wywołane. Formularz HTML może posiadać tylko jeden atrybut ACTION, co oznacza,
że może wysyłać dane tylko do jednego programu obsługi. Przy użyciu PHP można ob-
sługiwać formularz na różne sposoby, w zależności od upodobań użytkownika.

Rysunek 20.1 pokazuje najprostszy przykład formularza służącego do wyboru, w któ-


rym bardzo łatwo można zastosować Samoprzetwarzanie.
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________353

Aby użyć samoprzetwarzania z modyfikacją formularzy, musisz stoso-


wać styl pisania skryptów PHP określony w rozdziale 14. jako maksy-
malny lub średni. Początkujący mogą uważać to za nieco bardziej
skomplikowane od wyraźnego podziału pomiędzy HTML (wyświetlanie
formularza) i PHP (obsługa formularza).

Rysunek 20.1.
Formularz
z samoprzetwarzaniem

<HTML>
<BODY>
<P>Dziękujemy za użycie naszego systemu obsługi klienta.
Wybierz jedną z poniższych opcji:</P>
<FORM METHOD="POST" ACTION="formhandler.php">
<INPUT TYPE=RADIO NAME="userlevel" VALUE=l>Używałem już tego systemu lub wolę
mało dokumentacj i.<BR>
<INPUT TYPE=RADIO NAME="userlevel" VALUE=2>Nie używałem jeszcze tego systemu
lub wolę więcej dokumentacj i.<BR>
<INPUT TYPE=SUBMIT>
</FORM>
</BODY>
</HTML>

Używając tego formularza chciałbyś przejść do formularza dla eksperta lub dla począt-
kującego, w zależności od wybranej opcji. Możesz napisać skrypt CGI umożliwiający ci
obsługę obu opcji, ale wydaje się zbytkiem uruchamianie nowego procesu do wybrania
jednej z dwóch stron HTML. W PHP 4 samoprzetwarzanie formularza jest realizowane
prawie tak szybko jak zwykłe wywołanie HTML. Jeżeli mechanizm buforowania Zend
jest aktywny, samoprzetwarzanie będzie prawdopodobnie szybsze niż wysłanie danych
do oddzielnego skryptu obsługi formularza.

Obsługa formularzy
Jeżeli formularz i skrypt do jego obsługi są dwiema osobnymi stronami, ich obsługa za
pomocą PHP jest bardzo prosta. Rysunek 20.2 pokazuje prosty formularz do wstawiania
danych do bazy.
354______________________________________Część II » PHP i bazy danych

Rysunek 20.2.
Formularz
wprowadzania
adresu e-mail

<HTML>
<HEAD>
<TITLE>Wprowadzanie adresu e-mail</TITLE>
</HEAD>
<BODY>
<P>Proszę podać w poniższym formularzu :
płeć, imię i nazwisko oraz adres e-mail.</P>
<FORM METHOD="POST" ACTION="emailaddress.php">
Płeć:<BR>
<INPUT TYPE=RADIO NAME="Title" VALUE=l>Pan<BR>
<INPUT TYPE=RADIO NAME="Title" VALUE=2>Pani<BR>
Imię: <INPUT TYPE=TEXT NAME="GivenNeme" SI2E=25XBR>
Nazwisko: <INPUT TYPE=TEXT NAME="FamilyName" SIZE=25XBR>
Adres ę-mail: <INPUT TYPE=TEXT NAME="Email" SIZE=25><BR>
<INPUT TYPE=SUBMIT>
</FORM>
</BODY>
</HTML>

<HTML>
<HEAD>
<TITLE>Obsługa formularza wprowadzania adresu e-mail</TITLE>
</HEAD>
<BODY>
<?php
/* Otwarcie połączenia do bazy danych */
mysql_connect("localhost", "phpuser", "sesame")
or die("Nieudana próba połączenia z bazą danych");
mysql_select_db("phpbook");

/* Wstawienie wartości */
$query = "INSERT INTO addressbook
(ID, Title, GivenName, FamilyName, Email) VALUES
('NULL', '$Title', '$GivenName', 'SFamilyName', '$Email')'
$result - mysql_query($query);
if($result > 0) (
print("Dane zostały zapisane.");
) else {
print("Nieudany zapis danych.");
)
?>
</BODY>
</HTML>
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________355

Jeżeli zdecydujemy się zastosować samoprzetwarzanie, sprawa się nieco skomplikuje,


ale zyskamy większą siłę i elastyczność. Istnieje również kilka dodatkowych technik,
które na pewno będziesz chciał poznać.

Najbardziej użyteczną techniką jest ukryta zmienna stanu. Pozwala ona na zapamięta-
nie, ile razy zostały wysłane zmienne formularza. Dzięki temu wiemy, na którym etapie
procesu jesteśmy. Możesz użyć takiej zmiennej (o dowolnej nazwie, powinien być to
raczej ciąg, a nie liczba) do wyboru części formularza lub funkcji obsługi formularza,
która powinna zostać wywołana.
<HTML>
<HEAD>
<TITLE>Wprowadzanie adresu e-mail</TITLE>
</HEAD>
<BODY>
<?php
if ( HsSet (Sstage) )
f
?>
<P>Proszę podać w poniższym formularzu :
pięć, imię i nazwisko oraz adres e-mail.</P>
<FORM METHOD="POST" ACTION="<?php print("$PHP_SELF"); ?>">
PłeĆ:<BR>
•CINPUT TYPE=RADIO NAME="Title" VALUE=l>Pan<BR>
<INPUT TYP =RADIO NAME="Title" VALUE=2>Pani<BR>
Imię: <INPUT TYPE=TEXT NAME="GivenName" SIZE=25XBR>
Nazwisko: <INPUT TYPE=TEXT NAME="FamilyName" SIZE=25XBR>
Adres e-mail: <INPUT TYPE=TEXT NAME="Email" SIZE=25XBR>
<INPUT TYPE=HIDDEN NAME="stage" VALUE=1>
<INPUT TYPE=SUBMIT>
</FORM>
<?php
}
else
f
/* Otwarcie połączenia do bazy danych */
mysql_connect("localhost", "phpuser", "sesame")
or die("Nieudana próba połączenia z bazą danych");
mysql_select_db("phpbook") ;

/* Wstawienie wartości */
$query = "INSERT INTO addressbook
(ID, Title, GivenName, FamilyName, Email) VALUES
('NULL1, 'STitle', 'SGivenName', 'SFamilyName', '5Email')";
$result = mysql_query($query);
if($result > 0)
print("Dane zostały zapisane.");
else
print("Nieudany zapis danych.");
)
?>
</BODY>
</HTML>

Uważaj na tę konstrukcję! W powyższym przykładzie formularz zostanie wyświetlony


tylko raz przed ustawieniem zmiennej $ stage przy użyciu w formularzu elementu
HIDDEN. Następnie użytkownik zobaczy tylko komunikat i nie będzie miał możliwości
poprawienia wartości wysyłanych do bazy danych. Jeżeli chcesz, aby formularz był wy-
syłany przed i po wysłaniu danych, musisz skonstruować skrypt nieco inaczej.

Innym zagadnieniem, występującym przy używaniu samoprzetwarzania formularzy, jest


nawigacja. Przy używaniu tradycyjnych formularzy HTML nawigacja pomiędzy stro-
356_______________________________________Część II » PHP i bazy danych

nami przebiega tylko w jedną stronę: od formularza do skryptu obsługującego formularz


i do strony, którą określił projektant. Jednak samoprzetwarzające się formularze nie mu-
szą stosować się do tej zasady.
* Formularz może być wiele razy wysyłany przez użytkownika, zarówno w cało-
ści, jak i w częściach.
* Użytkownik decyduje się, kiedy kontynuować, lub formularz automatycznie
zarządza nawigacją.
* Dana jest przesyłana pomiędzy stronami jako otwarty tekst, albo jest ukryta.
* Decydujesz, do której strony przejdzie użytkownik, albo zostawiasz mu wybór.

Wybór pomiędzy tymi możliwościami powoduje zastosowanie określonej techniki:


użycia dodatkowego elementu formularza lub całkiem nowego formularza, zastosowa-
nia prostego hiperłącza lub wielu hiperłączy.

Niezależnie od wybory rodzaju nawigacji, pamiętaj dokładnie opisać,


co stanie się na kolejnym etapie. Ponieważ PHP daje ogromną ela-
styczność formularzy, łatwo jest doprowadzić do sytuacji, gdy oczeki-
wania nowych użytkowników w stosunku do twoich formularzy będą
niezgodne z tym, co wyprodukowałeś.

Formularze zależne od zmiennych


PHP jest znakomity w umieszczaniu zmiennych w bazie danych, ale naprawdę się wy-
różnia w przypadku formularzy wyświetlających dane z bazy. Możliwość wbudowania
go HTML, łatwe przekazywanie zmiennych i świetne połączenie z bazą danych to
w tym wypadku najlepsze cechy. Techniki te są użyteczne, ponieważ znajdziesz milion
okazji do pobrania danych z SQL, wyświetlenia ich w formularzu i uaktualnienia bazy
danych za pomocą nowych wartości zmiennych.

Przyjrzyjmy się różnym elementom formularzy HTML i sposobom ich obsługi.

TEXT i TEXTAREA
TEXT i TEXTAREA są najprostszymi elementami, ponieważ korzystają z jednoznacznej
relacji jeden do jednego, pomiędzy identyfikatorem i zawartością. Inaczej rzecz ujmu-
jąc, istnieje tylko jedna możliwa wartość dla jednej nazwy (pamiętaj, że w formularzach
HTML liczby całkowite i rzeczywiste muszą używać elementów TEXT i TEXTAREA).
Możesz po prostu pobrać zawartość pola z bazy danych i wyświetlić je w formularzu,
odwołując się do odpowiedniego pola tablicy. Przykład takiego formularza pokazany
jest na rysunku 20.3.
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________357

Rysunek 20.3.
Wyświetlanie tekstu
do edycji

<HTML>
<HEAD>
<TITLE>Edycja zapisu w dzienniku</TITLE>
</HEAD>
<BODY>
<?php
if (!IsSet($stage))
(
?>
<FORM METHOD="POST" ACTION="<?php print("SPHP_SELF"); ?>">
Data wpisu do edycji (RRRR-MM-DD):
< I N P U T T Y P E = T E X T NAME="LogDate" SIZE=10XBR>
<INPUT TYPE=HIDDEN NAME="stage" VALDE=1>
<INPUT TYPE=SOBMIT>
</FORM>
<?php
}
elseif($stage == 1)
(
/* Otwarcie połączenia z bazą danych */
mysql_connect("localhost", "root", "root") or
die("Nieudana próba połączenia z bazą danych");
mysql_select_db("phpbook");
/* Pobranie wartości */
Squery = "SELECT ID, LogText FROM weblog
WHERE LogDate = 'SLogDate'";
$result = mysql_query(Squery);
$LogRow - mysql_fetch_array{Sresult);
SLogID = $LogRow[0];
SLogText = stripslashes($LogRow[1]);
?>
<FORM METHOD="POST" ACTION="<?php print("$PHP_SELF");?>">
Data wpisu:
<INPUT TYPE=TEXT NAME="LogDate" SIZE=10 VALUE="<?php print($LogDate); ?>"><BR>
Tekst:
<TEXTAREA NAME="LogText" COLS=75 ROWS=10X?php print("SLogText");
?></TEXTAREAXBR>
<INPUT TYPE=HIDDEN NAME="LogID" VALUE="<?php echo $LogID; ?>">
<INPUT TYPE=HIDDEN NAME="stage" VALUE=2>
<INPUT TYPE=SUBMIT>
</FORM>
<?php
)
elseif(Sstage == 2);
{
358 _________________________________Część II » PHP i bazy danych

/* Otwarcie połączenia z bazą danych */


mysql_connect{"localhost", "root", "root") or
die("Nieudana próba połączenia z baza danych");
mysql_select_db("phpbook");
/* Zmiana wartości */
Squery " "UPDATE weblog SET LogDate = '$LogDate', LogText =
'LogText' WHERE ID = $LogID";
Sresult = mysql_query($query) ;
if($result == 0)
print("Mamy problem.\n");
else
print("Udana zmiana danych.\n");
?>
<FORM METHOD=POST ACTION="<?php print("$PHP_SELF");?>">
Jeżeli chcesz zmieniać następny wpis, wprowadź date (RRRR-MM-DD):
<INPUT TYPE=TEXT NAME="LogDate" SIZE=10XBR>
<INPUT TYPE=HIDDEN NAME="stage" VALUE=1>
<INPUT TYPE=SUBMIT>
</FORM>
<?php
)
?>
</BODY>
</HTML>

Jeżeli w bazie danych znajdą się wartości zawierające apostrofy lub


cudzysłowy, możesz użyć funkcji stripsiashes przed wyświetleniem
tych danych w polach TEXTAREA lub TEXT. Uważaj na nazwiska zawie-
rające apostrofy, np. 0'Malley!

CHECKBOX
Elementy typu CHECKBOX mają dwie możliwe wartości: włączony (zaznaczony) lub bez
wartości (niezaznaczony). Jednak elementy te są często stosowane grupami w celu od-
wzorowania bardziej złożonych wartości. Mimo że ich wartości rzadko są odczytywane
z bazy danych (pola wyboru są często używane do wybierania z małego zakresu warto-
ści, zwykle niezapisywanych w bazie), może być to zrealizowane w sposób pokazany
na rysunku 20.4.

Rysunek 20.4.
Pole wyboru
odczytywane
z bazy danych

<HTML>
<HEAD>
<TITLE>Formularz z polem wyboru</TITLE>
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________359

</HEAD>
<BODY>
<?php
if ( UsSet (Sstage) )
{
?>
<FORM METHOD=POST ACTION="<?php print("$PHP_SELF"); ?>">
<FONT SIZE=+2>Proszę przesyłać mi na mój adres e-mail dużo
^biuletynów!</FONT><BR>
<INPUT TYPE=CHECKBOX CHECKED NAME="OptOut" VALUE="On">
<FONT SIZE=-2>zaznaczajac pole, zgadzasz się z powyższym</FONTXBR>
<INPUT TYPE=HIDDEN NAME="stage" VALUE=1>
<INPUT TYPE=SUBMIT>
</FORM>
<?php
l
elseif(Sstage == l SS !IsSet($0pt0ut))
{
/* otwarcie połączenia z baza danych */
mysql_connect("localhost", "root", "root") or
die("Nieudana próba połączenia z baza danych");
mysql_select_db("phpbook");
/* Zmiana wartości, w tym przypadku z On na NULL */
Squery = "UPDATE checkbox SET BoxValue = NULL WHERE BoxName = 'OptOut'";
$result = mysql_query(Squery);
print("Zostałeś usunięty z naszej listy dystrybucyjnej.");
}
?>
</BODY>
</HTML>

RADIO
Element formularza RADIO (przycisk opcji) pozwala na stworzenie relacji jeden do
wielu, pomiędzy identyfikatorem i wartościami. Inaczej mówiąc, posiadają one wiele
możliwych wartości, ale tylko jeden może zostać wyświetlony lub zaznaczony. Zakres
działania przycisków opcji jest zbliżony do pól wyboru. Najczęściej używa się ich
w małych grupach opcji, zwykle poniżej 10 elementów, do opisania których wystarczy
wyraz lub dwa.

SELECT
Element SELECT jest prawdopodobnie najbardziej interesujący. Może przechowywać
największą liczbę opcji i pozwala również na wybór kilku z nich, które są przekazywa-
ne do bazy danych za pomocą tablic.

Zajrzyj do rozdziału 27. „PHP i JavaScript", aby zapoznać się z przy-


kładami zastosowania JavaScript w celu stworzenia jeszcze ciekaw-
szych formularzy z elementami SELECT.

Na rysunku 20.5 pokazaliśmy element formularza SELECT z możliwością wielokrotne-


go wyboru. W PHP realizuje się to poprzez stworzenie tablicy zaznaczonych opcji,
w celu przekazania do skryptu obsługującego formularz. Utworzysz tę tablicę dzięki
deklaracji atrybutu MULTIPLE w wyrażeniu SELECT oraz poprzez nazwanie elementu
360_______________________________________Część II » PHP i bazy danych

SELECT na przykład ,,$val[]", dodając nawiasy kwadratowe do nazwy zmiennej.


Wskaże to programowi, że nie ma do czynienia z tablicą pojedynczą. PHP stworzy ta-
blicę z wartościami zaznaczonych elementów. Gdy tablica zostanie przekazana do
skryptu obsługującego formularz, możesz pracować na tych wartościach jak na warto-
ściach dowolnej tablicy.

Rysunek 20.5.
Wypełnione pole listy
z możliwością
wielokrotnego wyboru

<HTML>
<HEAD>
<TITLE>Prezydenckie bokobrody</TITLE>
</HEAD>
<BODY>
<P>Którzy prezydenci mieli największe bokobrody? Możesz wybrać kilka
nazwisk z listy, przytrzymując klawisz "Control", "Alt", "Apple" lub "Command"
podczas klikania myszą.</P>
<FORM METHOD=POST ACTION="sideburns.php">
<SELECT NAME="PresID[] " SIZE=5 MULTIPLO

<?php
/* Otwarcie połączenia z baza danych */
mysql_connect("localhost", "root", "root") or
~ die ("<OPTION>Błąd bazy danych ! </OPTIONx/SELECT>") ;
mysql_select_db("phpbook");

/* Załadowanie listy prezydentów*/


$query = "SELECT ID,Name FROM presidents ORDER BY Name";
$result = mysql_query($query) ;
while($president = raysql_fetch_array(Sresult))
f
print("<OPTION VALUE=\"$president[0]\">Spresident[1]\n");
)
?>
</SELECT>
<P>Twój adres e-mail: <INPUT TYPE=TEXT NAME="email" SIZE=25x/P>
<INPUT TYPE=SUBMIT>
</FORM>
</BODY>
</HTML>

sideburns.php
<HTML>
<BODY>
<?php
while(list($key, $val) = each($PresID))
{
$queryl = "INSERT INTO votes (ID, PresID, email)
VALUES (NULL, 'Sval', 'Semail')";
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________361

$resultl = mysql_query($queryl);
)
print("Dziękuję za głosowanie! Wyślemy e-mail do zwycięzców naszego konkursu
w dzień wyborów prezydenckich.");
?>
</BODY>
</HTML>

Formularze zależne od zapytań


Najbardziej skomplikowanym typem formularza jest samoprzetwarzający się formularz
z kilkoma osobnymi elementami FORM, wyświetlanymi i obsługiwanymi przez jeden
skrypt. W takim przypadku trzeba napisać bardziej skomplikowany kod obejmujący
wszystkie możliwości.

Dla przykładu zamieszczamy serię formularzy do oceniania książek. Po wprowadzeniu


danych o książce można przypisać autora do książki.
<HTML>
<HEAD>
<TITLE>Przegląd książek: autorzy</TITLE>
</HEAD>
<BODY>
<?php
if ($stage == 1)
(
/* Otwarcie połączenia */
mysql_connect("localhost", "root", "root") or
die("Nieudana próba połączenia z bazą danych");
/* Połącz książki z istniejącymi autorami */
$queryl = "INSERT INTO author_book (ID, authorlD, bookID)
VALUES(NULL, $authorID, $bookID)";
Sresultl = mysql_query($queryl);
}
elseif(Sstage == 2 &6 STRLEN($firstnarae) > 0)
(
/* Otwarcie połączenia */
mysql^connect("localhost", "root", "root") or
die("Nie mogę nawiązać połączenia");
mysql_select_db("phpbook");
/* Nowi autorzy musza być najpierw wprowadzeni do bazy danych,
a następnie połączeni z książką. */
$queryl = "INSERT INTO author (ID, firstname, lastname)
VALUES(NULL, '$firstname', '$lastname')";
$resultl = mysql_query(Squeryl);
$authorID = mysql_insert_id($resultl);
$query2 = "INSERT INTO author_book (ID, authorlD, bookID)
VALUES(NULL, $authorID, $bookID)";
Sresult2 = mysql_query($query2);
)
?>
<P>Czy autor znajduje się na liście? Jeżeli tak, zaznacz go i kliknij
przycisk "Wybierz". Jeżeli nie, wypełnij formularz.</P>
<?php
/* Otwarcie połączenia */
mysql_connect("localhost", "root", "root") or
die("Nie mogę nawiązać połączenia");
mysql_select_db("phpbook");
/* Pobierz poprzedniego autora */
Squery = "SELECT ID, lastname, firstname FROM author ORDER BY lastname";
$result = mysql_query($query);
362_____________________________________Część II » PHP i bazy danych

?>
<FORM METHOD=POST ACTION="<?php print("SPHP_SELF"); ?>">
<SELECT SIZE=1 NAME="authorID">
/* Pętla wypełniająca listę autorów */
<?php
while($authorname = mysql_fetch_array(Sresult))
(
print("<OPTION VALUE=\"$authorname[0]\">$authorname[1],
$authorname[2]\n");
}
?>
</SELECT>
<INPUT TYPE=HIDDEN NAME="stage" VALUE=1>
<INPUT T Y P E = S U B M I T VALUE="Wybierz">
</FORM>
<HR ALIGN=CENTER W I D T H = 8 0 % SIZE=1>
<P>Jeżeli jesteś całkowicie pewien, że autora nią ma na liście,
wprowadź informacje o nim do formularza.</P>
<FORM METHOD=POST ACTION="<?php p r i n t ( " $ P H P _ S E L F " ) ; ?>">
I m i ę : < I N P U T TYPE=TEXT N A M E = " f i r s t n a m e " S I Z E = 2 5 X B R >
Nazwisko: <INPUT TYPE=TEXT NAME="lastname" SIZE=25xBR>
< I N P U T T Y P E = H I D D E N NAME="stage" VALUE=2>
<INPUT TYPE=SUBMIT VALUE="Dodaj autora">
</FORM>
</BODY>
</HTML>

Podsumowanie
PHP jest niezwykle wydajnym narzędziem obsługi skryptów, szczególnie we współpra-
cy z bazą danych. Możesz używać PHP do generowania wartości formularza na pod-
stawie danych zapisanych w bazie i oczywiście zapisywać do bazy dane z formularza.

Aby przygotować formularz HTML do bezproblemowej pracy z PHP, musisz przestrze-


gać kilku prostych zasad. Pierwsza i najważniejsza to nadawanie nazw każdego elementu
formularza — standard HTML nie wymaga tego, ale PHP tworzy z tych nazw zmienne
używane do obsługi formularza. Dobrym pomysłem jest nadawanie nazw elementów ta-
kich samych jak nazwy odpowiadających im pól w bazie danych. PHP ułatwia również
użycie ukrytych pól oraz list wielokrotnego wyboru, które powinny być oznaczane za
pomocą nawiasów kwadratowych (oznaczających tablicę) po nazwie elementu.

Za pomocą PHP możesz stworzyć osobno formularz HTML i skrypt obsługi w PHP, al-
bo też połączyć oba te elementy w jeden skrypt PHP. Druga opcja jest lepsza, chociaż
może być nieco bardziej kłopotliwa w programowaniu. Powinieneś ustawić zmienną
w formularzu, która wskaże, czy dane zostały już wysłane.

Możesz stworzyć nawet kilka formularzy obsługiwanych przez ten sam skrypt PHP.
Każdy formularz może być wygenerowany na podstawie danych odczytanych z bazy.
Pozwoli to elastycznie korzystać z formularzy, ale trzeba uważać na skomplikowaną,
wieloczęściową strukturę.
Rozdział 21.
Dziennik sieciowy
W tym rozdziale:
+ Dlaczego dziennik?
+ Tworzenie prostego dziennika sieciowego
* Dodanie systemu wprowadzania danych przez HTTP
* Dołączenie bazy danych
* Zmiany i uzupełnienia

Małe samodzielne aplikacje PHP, takie jak głosowanie lub tablice ogłoszeniowe, są
atrakcyjne, ale kompletne witryny są miejscem, gdzie PHP naprawdę błyszczy. W roz-
dziale tym zawarliśmy kompletną instrukcję tworzenia najprostszych samodzielnych
witryn, jakimi są dzienniki sieciowe.

Dlaczego dziennik?
Sieciowy dziennik osobisty jest najprostszym rodzajem dynamicznej witryny. Może być
uważany za dynamiczną wersję osobistej strony macierzystej: zawartość jest zorgani-
zowana chronologicznie, a nie według wymagań biznesowych lub prezentowanej dzie-
dziny. Większość dzienników nie tworzy własnej treści w sensie opisywania nowości
lub tworzenia prac plastycznych. Bazują one raczej na komentarzach na temat pracy in-
nych osób lub wydarzeń. Na najwyższym poziomie tego stylu publiczne dzienniki, jak
na przykład Slashdot, są niezwykle popularnymi miejscami gromadzącymi społeczności
sieciowe i służą im jako forum wymiany doświadczeń lub zainteresowań.

Jeżeli masz niewiele doświadczenia w pisaniu skryptów serwera, zalecamy natychmiast


rozpocząć pierwszy większy projekt: prywatny dziennik sieciowy. Nic nie pomaga tak
szybko się uczyć, jak uruchomienie własnej kompletnej witryny. Możesz tam wypró-
bować wiele nowych idei i technik programowania. Jest to szczególnie korzystne, po-
nieważ PHP i inne technologie open source rosną i zmieniają się tak szybko, że
niezbędne jest posiadanie środowiska testowego, służącego jako poletko doświadczalne.
364______________________________________Część II » PHP i bazy danych

Dzienniki są również zabawne i dzięki temu są wartościowe nawet dla tych, którzy
używają PHP do bardziej poważnych zastosowań. Nie ma większej przyjemności od
prowadzenia intelektualnej debaty, kłótni lub romansu dzięki dziennikowi sieciowemu.
Zapomnij o kinie, muzyce pop i telewizji — dzienniki sieciowe są prawdziwym me-
dium stulecia!

Najprostszy dziennik
Najprostszy dziennik jest po prostu szablonem i kilkoma dołączonymi plikami teksto-
wymi. Jest ograniczony tylko do lokalnych modyfikacji — nie możesz tworzyć wpisów
poprzez HTTP, ale zapisując plik tekstowy po załogowaniu się na serwer jako normalny
użytkownik. Nie możesz również przypisać poziomów uprawnień, więc ten typ dzien-
nika sieciowego jest najbardziej odpowiedni dla czysto prywatnej witryny prowadzonej
przez jednego autora.

Możesz więc spytać, po co to wszystko? Naszym zamiarem jest pokazanie, w jaki spo-
sób działają poszczególne części kodu PHP przed dodaniem funkcji, umożliwiających
połączenie z bazą danych, i modyfikacji zawartości witryny za pomocą danych poda-
nych w formularzach (co będzie tematem następnych dwóch części tego rozdziału). Po-
równanie podejścia, w którym dane są przechowywane w plikach, z integracją z bazą
danych efektywnie pokazuje, dlaczego to drugie wyjście jest lepsze. Możesz również
zaprojektować całkowicie prywatny pamiętnik, który można czytać w przeglądarce we
własnym komputerze i nie umieszczać go w Internecie.

Nie zalecamy umieszczania naszego najprostszego dziennika w pu-


blicznej witrynie, ponieważ stosowanie łączy w stylu GET i dołączanych
plików może być ryzykowne dla bezpieczeństwa. Dla publicznych wi-
tryn powinieneś użyć bazy do przechowywania danych.

Zastosowaliśmy tutaj najprostszy typ nawigacji, łącza tekstowe Następny i Poprzedni są


tworzone ręcznie. Daje to dużą elastyczność w decydowaniu, jak często chcesz zmie-
niać główną stronę dziennika — my robimy to codziennie, ale możesz stosować coty-
godniowe, miesięczne lub nieregularne zmiany, w zależności od tego, jak dużo tekstu
wpisujesz. Używamy również starego dobrego panelu nawigacyjnego po lewej stronie,
który zawiera łącza do stałych stron, takich jak O mnie lub Ulubione.

Gotowy dziennik sieciowy jest pokazany na rysunku 21.1.

Jest on stworzony z następujących plików:


* weblog.php: szablon strony;
* 20000101.txt, 20000102.M itd.: pozycje dziennika (zmieniane codziennie);
* notjtoday.txt: domyślny komunikat dla dni bez wpisanej pozycji;
* faves.php, links.php, aboutme.php: statyczne strony (rzadko zmieniane);
Rozdział 21. » Dziennik sieciowy__________________________________365

Rysunek 21.1.
Strona dziennika
sieciowego

+ navbar.inc: panel nawigacyjny na każdej stronie;


* style. inc. arkusz stylów.

Możesz pobrać kod zawartości tych wszystkich plików (zamieszczonych na wydrukach


21.1 do 21.6) z naszej witryny WWW http://www.troutworks.com/php4biblie/weblog/.
Musisz zmienić wartość zmiennej „$f irst_entry" w pliku weblog.php na datę two-
jego pierwszego wpisu, w przeciwnym wypadku wpadniesz w pętlę, która może zużyć
cały zasób pamięci serwera!

Wydruk 21.1. Glówny szablon dziennika (weblog.php)______________________________


<?php
/* Zmień na datę twojego pierwszego wpisu */
Sfirst_date = 20000101;

// Wewnętrzne łącza są adresami w stylu GET.


// Jeżeli nie podana została data (wywołanie strony bez argumentów),
// przyjmowana jest dzisiejsza data.
if ( IXsSet(Sdate) )
(
$date = datę("Ymd");
)
?>
<HTML>
<HEAD>
<TITLE>Dziennik sieciowy PHP4t <?php print("$date"); ?></TITLE>
<?php include{"style.inc"); ?>
</HEAD>

<BODY BGCOLOR=tFFFFFF>
<TABLE BORDER="0" CELLPADDING="5" WIDTH=100%>
<TR BGCOLOR=#822222>
<TD ALIGN=RIGHTXHl>Dziennik na dzień <?php print ("Sdate") ; ?></Hl>
366____________________________________Część II » PHP i bazy danych

</TDX/TR>
<TRXTD>
<?php include("navbar.inc"); ?>
<TD WIDTH=85%>

<?php
// Dla początkujących nieco ryzykowne jest automatyczne generowanie
// łączy "następny" i "poprzedni", ponieważ możesz łatwo można wpaść
// w nieskończoną pętlę. Dodamy te łączą ręcznie do każdej pozycji z datą.

// Dołącz pozycję dla podanej daty lub wyświetl domyślny komunikat "nie dzisia:"
// jeżeli nie ma żadnej zawartości.
if(file_exists("Sdate.txt"))
(
include("Sdate.txt") ;
)
else
(
include("not_today.txt") ;
)
?>
</TDX/TR>
</TABLE>
</TDX/TR>
</TABLE>
</BODY>
</HTML>

Wydruk 21.2. Datowana pozycja (20000101.txt)_________________________________


<CENTER>
<P>
<A HREF="weblog.php?date=19991231">Poprzedni</A>
<A HREF="weblog.php?date=20000102">Następny</A>
</P>
</CENTER>

<DIV CLASS="topic">ŚWIETO</DIV>
<P>To jest rok, w którym inżynierowie nie świętują Nowego Roku. Wszyscy śpimy
pod swoimi biurkami, upewniając akcjonariuszy, że nasz szef zwalczył pluskwę
milenijną</P>

<P>Moje postanowienia na nowy rok:


<UL>
<LI>Rok 2000 bez Win2000! Walcz z Imperium.</LI>
<LI>Wziąć udział w projekcie OSS.</LI>
<LI>Wziąć pełne 2 tygodnie urlopu (farma taty?).</LI>
<LI>Być mniej złośliwy.</LI>
</UL>
</P>

<CENTER>
<P>
<A HREF="weblog.php?date=l9991231">Poprzedni</A>
<A HREF="weblog.php?date=20000102">Następny</A>
</P>
</CENTER>

Wydruk 21.3. Komunikat domyślny (not_today.txt)_______________________________


<P>Przepraszam, dzisiaj nic nowego! Sprawdź jutro.</P>
Rozdział 21. » Dziennik sieciowy__________________________________367

Wydruk 21.4. Statyczna strona (faves.php)_______________________________________


<!-- P a m i ę t a j , aby dodać lub usunąć łącze w n a v b a r . i n c -->
<HTML>
<HEAD>
<TITLE>Moje ulubione:</TITLE>
<?php i n c l u d e { " s t y l e . i n c " } ; ?>
</HEAD>

<BODY BGCOLOR=#FFFFFF>
<TABLE BORDER="0" CELLPADDING="10" WIDTH= 100%>
<TR BGCOLOR=»822222>
<TD A L I G N = R I G H T VALIGN=BOTTOMXHl>Moje u l u b i o n e < / H l >
</TDX/TR>
<TRXTD>
<?php i n c l u d e ( " n a v b a r . i n c " } ; ?>
<TD W I D T H = 7 5 % >

< P > T u t a j mam k i l k a moich ulubionych r z e c z y , < / P >

<DIV CLASS="topic">KSIĄŻKK/DIV>
<DL>
<DT>Cryptonomicon, autor: Neal Stephenson</DT>
<DD>Majstersztyk dla technicznych — to nasze życie, wejdź w kierat ogromnych
inwestycji. Załaduj esej "Na początku byi wiersz komend" z witryny:
www.crytonomicon.com</DD>
</DL>

<DIV CLASS="topic">MUZYKA</DIV>
<DL>
<DT>When The Pawn..., Fiona Apple</DT>
<DD>Jeszcze jedna latynoska gwiazdka.</DD>
</DL>

</TDX/TR>
</TABLE>
</TDX/TR>
</TABLE>
</BODY>
</HTML>

Wydruk 21.5. Dołączany plik nawigacyjny (navbar.inc)_______________________________


•CTABLE W I D T H = 1 0 0 % CELLPADDING=10XTR>
<TD W I D T H = 1 5 % BGCOLOR=łFFFECC VALIGN=TOP>
<P CLASS=sidebarxA HREF="weblog .php">Dziś</AX/P>
<P CLASS=sidebar><A H R E F = " l i n k s .php">Łacza</AX/p>
<P CLASS=sidebarxA HREF="faves .php">Ulubione</AX/P>
<P CLASS=sidebarxA HREF="aboutme.php">O mnie</AX/P>
<P CLASS=sidebarxA H R E F = m a i l t o : m e @ l o c a l h o s t > K o n t a k t < / A X / p >
</TD>

Wydruk 21.6. Dołączany plik arkuszy stylu (style, inc)_________________________________

<STYLE TYPE="text/css">
<!-
BODY {font-family: verdana, arial, sans-serif;
font-size: lOpt; color: #000000; text-align: left)
HI (font-family: verdana, arial, sans-serif;
font-size: 14pt; color: ttFFFFFF)
.sidebar {font-family: verdana, arial, sans-serif;
font-size: 12pt; color: #822222; text-align:right; margin-top: lOpx)
.topic {font-family: verdana, arial, sans-serif;
368______________________________________Część II » PHP i bazy danych

font-size: 12pt; font-weight: bold; color: #000000; background: łłFFFECC;


text-align: left)

</STYLE>

Wprowadzanie danych przez HTTP


Prosty dziennik jest odpowiedni do wielu zastosowań, ale ma jedną wadę: nie możesz
tworzyć pozycji dziennika poprzez sieć WWW. W zamian musisz stworzyć każdą po-
zycję używając edytora tekstowego, np. Emacs czy Notepad i zapisać w katalogu do-
kumentów serwera WWW. Może to będzie problemem, jeżeli nie możesz zastosować
połączenia telnet, ssh lub ftp albo niezbyt dobrze wiesz, jak to zrobić. Dlatego następ-
nym logicznym krokiem dla wielu użytkowników jest użycie HTTP.

Za pomocą PHP możesz stworzyć dobre formularze do wprowadzania danych oraz


strony je wyświetlające. Jedynym poważnym problemem jest konieczność nadania
uprawnień do odczytu i zapisu dla użytkownika HTTP (zwykle jest to Nobody lub Eve-
rybody). Jest to niebezpieczny proces i nie polecamy go. Opisujemy tutaj skrypt wpro-
wadzania danych, więc możesz zapoznać się z nim, zanim przejdziemy do lepszego
rozwiązania, jakim jest użycie bazy danych zamiast oddzielnych plików dla każdej po-
zycji. Spróbujemy w minimalnym tylko stopniu zająć się problemami bezpieczeństwa,
używając hasła i wysyłając e-mail przy próbie nieautoryzowanego dostępu.

Ekrany wyświetlania danych są dokładnie takie same jak w tworzonej lokalnie wersji
dziennika. Dodatkowo potrzebuj emy plików zamieszczonych na wydrukach 21.7 do 21.10.
login.php
logentry.php
logentry_handler.php
password, inc

Umieśćmy plik password, inc w katalogu poza drzewem dostępnym przez WWW, na
przykład /home/weblog. Katalog ten musi byś dostępny dla wszystkich, a użytkownik
httpd (Nobody) musi mieć możliwość odczytania pliku. Jeżeli posiadasz prawa admini-
stratora, możesz zmienić właściciela pliku na użytkownika httpd. Jeżeli nie, musisz
nadać uprawnienia do odczytu wszystkim, co stanowi osłabienie systemu bezpieczeń-
stwa. Upewnij się, że hasło to nie jest hasłem użytkownika systemowego.

Wydruk 21.7. Logowanie do formularza wprowadzania danych (login.php)___________________


<HTML>
<HEAD>
<TITLE>Ekran logowania dziennika</TITLE>
</HEAD>

<PXB>Podaj użytkownika i hasło.</Bx/P>


<FORM METHOD=POST A C T I O N = " l o g e n t r y . p h p " >
< P > U Ż Y T K O W N I K : < I N P U T TYPE=TEXT NAME="test_username" S I Z E = 2 0 X / P >
<P>HASŁO:<1NPUT TYPE=PASSWORD NAME="test_password" S I Z E = 2 0 x / P >
Rozdział 21. » Dziennik sieciowy____________________________________369

<PXINPUT TYPE="SUBMIT" VALUE="WyŚli j ">


</FORM>
</BODY>
</HTML>

Wydruk 21.8. Dołączany plik haseł (password, inc)__________________________________


<?php
Susername = "logwriter" ;
Spassword = "logpass";
?>

Wydruk 21.9. Ekran wprowadzania danych (logentry.php)______________________________


<HTML>
<HEAD>
<TITLE>Ekran wprowadzania danych do dziennika</TITLE>
</HEAD>

<?php
include("/home/weblog/password.inc");
if(Stest_username == Susername && $test_password == Spassword)
{
?>
<BODY>
<FORM ACTION="logentry_handler .php" METHOD="POST">
<P>Wprowadż d z i s i e j s z y tekst do d z i e n n i k a : <BRXTEXTAREA NAME="logtext"
COLS=75 ROWS=20 WRAP="VIRTUAL"X/TEXTAREAX/P>
< I N P U T TYPE="hidden" NAME="test_username" VALUE="<?php
p r i n t ( " $ t e s t _ u s e r n a r a e " ) ; ?>">
<PXINPOT TYPE="SUBMIT" NAME="SUBMIT" VALUE="Wyśli j "></P>
</FORM>

<?php
l
else
{
mail("meSlocalhost", "Podglądanie dzinnika", "Ktoś z adresu $REMOTE_ADDR
próbuje wejść na twoją stronę wprowadzania danych do dziennika.");
)
?>
</BODY>
</HTML>

Wydruk 21.10. Skrypt obsługujący formularz (logentryjiandler.php)_____________________


<?php
Sdate = datę("Ymd") ;
// Nie zapisuj pliku, jeżeli nie podano użytkownika na poprzedniej stronie
if($test_username)
l
$fp = fopen("$date.txt", "w");
$try_entry = fwrite($fp, "Slogtext");
print("Wprowadzona pozycja dziennika na dzień Sdate zawiera Stry_entry
znaków.");
}
else
(
mail("meSlocalhost", "Podglądanie dziennika, część 2.", "Ktoś z adresu
SREMOTE_ADDR próbuje wejść na twoją stronę wprowadzania danych do
dziennika.");
}
?>
370______________________________________Część II » PHP i bazy danych

Dołączenie bazy danych


Po wypróbowaniu dopisywania pozycji za pomocą HTTP i funkcji PHP zapisującej plik
już tylko mały krok do przechowywania pozycji w bazie danych, a nie w osobnych
plikach tekstowych. Jest to bardziej staranne — ważne w rozwijających się witrynach
— i wyraźnie bezpieczniejsze. Poza tym możesz nadać różne uprawnienia różnym
użytkownikom, co pozwala na bezpieczną pracę wielu redaktorom pracującym nad za-
wartościom witryny.

Użycie bazy danych ma jeszcze dwie dodatkowe zalety. Po pierwsze, można łatwo na-
pisać skrypt do edycji i wprowadzania pozycji dziennika za pomocą formularza HTML.
Jeszcze lepsze jest programowe, a nie ręczne tworzenie łączy do nawigacji pomiędzy
pozycjami dziennika.

Mimo że mają podobne nazwy i funkcje, pliki zamieszczone na wydrukach 21.11 do


21.17 są nieco inne od poprzedniego zestawu. Potrzebujemy również skryptu tworzące-
go bazę danych (chyba że zrobisz to za pomocą wiersza komend MySQL), jeżeli chcesz
zmieniać poprzednie pozycje, musisz dodać skrypt o nazwie dbjogedit.php.

Większość z tych skryptów nie zachowuje się elegancko, jeżeli baza


danych nie zawiera danych, szczególnie nazw użytkowników i haseł
w tabeli login. Dopóki nie umieścimy jakiegoś rekordu w tej tabeli,
całość programu nie działa. Chcieliśmy skupić się na głównych funk-
cjach, a nie na kontroli błędów, ponieważ występuje tu wiele miejsc,
w których powinno się sprawdzać, czy tabela nie jest pusta.

Wydruk 21.11. Ekran logowania do bazy danych dziennika (db_login.php)_______ _______


<HTML>
<HEAD>
<TITLE>Ekran logowania do dziennika</TITLE>
</HEAD>

<PXB>Zaloguj się t u t a j , aby dopisać nową pozycję. </Bx/P>


<FORM METHOD=POST ACTION="db_logentry.php">
< P > U Ż Y T K O W N I K : < I N P U T TYPE=TEXT NAME="test_username" S I Z E = 2 0 > < / P >
<P>HASŁO:<INPUT TYPE=PASSWORD NAME="test_password" S I Z E = 2 0 X / P >
< P X I N P U T T Y P E = " S U B M I T " VALUE="Wyślij">
</FORM>

<PxB>Załoguj się t u t a j , aby zmienić poprzedna p o z y c j ę . </Bx/P>


<FORM METHOD=POST ACTION="db_logedit.php">
< P > U Ż Y T K O W N I K : < I N P U T TYPE=TEXT NAME="tęst_usęrnamę" S I Z E = 2 0 X / P >
<P>HASŁO:<INPUT TYPE=PASSWORD NAME="test_password" SIZE=20X/P>
< P > Z M I E N I A N A D A T A K I N P U T TYPE=TEXT NAME="edit_datę" S I Z E = 8 X / P >
< P X I N P U T T Y P E = " S U B M I T " VALUE="Wyśli j ">
</FORM>
</BODY>
</HTML>
Rozdział 21. » Dziennik sieciowy____________________________________371

Wydruk 21.12. Dolączany plik hasel bazy danych (db_password.inc)_______________________


<?php
$hostname = "localhost";
Suser = "dbuser";
Spassword = "dbpass";
?>

Wydruk 21.13. Ekran wprowadzania danych do bazy danych dziennika (db_logentry.php)__________


<HTML>
<HEAD>
<TITLE>Ekran wprowadzania danych</TITLE>
</HEAD>

<?php
/* Podłączenie do bazy danych, sprawdź logowanie. */
include("/home/weblog/db_password.inc"};
mysql_connect(Shostname, $user, $password);
mysql_select_db("weblogs");
Squery = "SELECT password FROM login WHERE username = '$test_username'";
$result - mysql_query{$query);
Spassword_row = mysql_fetch_array(Sresult);
Sdb_password = $password_row[0];

if(Stest_password == Sdb_password)
t
?>
<BODY>
<FORM ACTION="db_logentry_handler.php" METHOD="POST">
< P > T e k s t : < B R X T E X T A R E A NAME="logtext" COLS=75 ROWS=20
WRAP="VIRTUAL"X/TEXTAREAX/P>
< I N P U T TYPE="hidden" NAME="test_username" VALUE="<?php
p r i n t ( " S t e s t _ u s e r n a m e " ) ; ?>">
<PXINPUT TYPE="SUBMIT" NAME="SUBMIT" VALUE="Wyśli j "X/P>
</FORM>

<?php
}
else
{
mail("me@localhost", "Podglądanie dzinnika", "Ktoś z adresu SREMOTE_ADDR
próbuje wejść na twoją stronę wprowadzania danych do dziennika.");
t
?>
</BODY>
</HTML>

Wydruk 21.14. Ekran zmiany danych w bazie danych dziennika (db_logedit.php)______________


<HTML>
<HEAD>
<TITLE>Ekran edycji danych z dziennika</TITLE>
</HEAD>

<?php
/* Podłączenie do bazy danych, sprawdź logowanie. */
include("/home/weblog/db_password.inc");
mysql_connect(Shostname, Suser, Spassword);
mysql_select_db("weblogs");
$query = "SELECT password FROM login WHERE username = 'Stest_username'";
Sresult = mysql_query(Squery);
$password_row = mysql_fetch_array(Sresult);
Sdb_password = Spassword_row[0];

if ($test_password == $db_jpassword)
372______________________________________Część II « PHP i bazy danych

(
$queryl = "SELECT logtext FROM mylog WHERE date = $edit_date";
$resultl = mysql_query($queryl) ;
Sentry_row = mysql_fetch_array(Sresultl);
/* Po pobraniu tekstu t bazy SQL powinieneś usunąć znaki sterujące. */
$edit_entry = stripsląshes($entry_row[0]);
?>
<BODY>
<FORM ACTION="db_logentry_handler.php" METHOD-"POST">
< P > T e k s t : < B R X T E X T A R E A NAME="logtext" COLS=75 ROWS=20 WRAP="VIRTUAL"X?php
print ("$edit_entry") ; ?></TEXTAREAX/P>
<INPUT TYPE="hidden" NAME="edit_date" VALUE="<?php p r i n t ( " S e d i t _ d a t e " ) ; ?>">
< I N P U T TYPE="hidden" NAME="test_username" VALUE-"<?php
print ( "$test_username" ) ; ?>">
<PXINPUT TYPE="SUBMIT" NAME="SUBMIT" VALUE="Wyśli j "X/P>
</FORM>

<?php
)
else
(
mail("me@localhost", "Podglądanie dzinnika", "Ktoś z adresu $REMOTE_ADDR
Spróbuje wejść na twoja stronę zmiany danych do dziennika.");
)
?>
</BODY>
</HTML>

Wydruk 21.15. Skrypt obsługujący formularz dziennika (db JogentryJiandler .php)_______________

<?php
$date = datę("Ymd");
if($test_username)
{
include("/home/weblog/db_password.inc") ;
mysql_connect(Shostname, $user, $password);
mysql_select_db("weblogs") ;

/* Wprowadź pozycję lub zmień istniejącą. */


if(!$edit_date)
(
$query = ("INSERT INTO mylog (ID, date, logtext)
VALUES(NULL, '$date', '$logtext')");
$result = mysql_query{$query);
print("Wynik: Sresult.");
)
else
(
$query = ("UPDATE mylog SET logtext = 'Slogtext1
WHERE date = '$edit_date'");
$result = mysql_query(Squery);
print("Wynik zmiany: 5result.");
}
}
else
(
m a i l ( " m e @ l o c a l h o s t " , "Podglądanie dzinnika część 2", "Ktoś z adresu
SREMOTE_ADDR próbuje wejść na twoją stronę zmiany danych do d z i e n n i k a . " ) ;
)
?>

Wydruk 21.16. Główny szablon dziennika na podstawie bazy danych (db_weblog.php)


<?php

/* Strona ta obsługuje 5 możliwych przypadków:


Rozdział 21. » Dziennik sieciowy____________________________________373

1. Dotarłeś do strony poprzez łącze, więc znana jest data.


Nie jest to pierwsza ani ostatnia pozycja w bazie.
2. Dotarłeś do strony poprzez łącze, więc znana jest data.
Jest to pierwsza pozycja w bazie.
3. Dotarłeś do strony poprzez łącze, więc znana jest data.
Jest to ostatnia pozycja w bazie.

4. Oglądasz tą stronę bez podawania daty (wiec domyślnie jest data dzisiejsza)
i jest to nowa pozycja dzisiaj.

5. Oglądasz te stronę bez podawania daty (więc domyślnie jest data dzisiejsza)
i nie jest to nowa pozycja.
Daj domyślny komunikat z łączem do ostatniej pozycji.
*/
/* Pobierz dzisiejszą datę dla przypadków 4 i 5. */
if{llsSet(Sdate))
l
$date = datę("Ymd");
}
?>
<HTML>
<HERD>
<TITLE>Dziennik sieciowy PHP4: <?php print("$date"); ?></TITLE>
<?php include("style.inc"); ?>
</HEAD>

<BODY BGCOLOR=tłFFFFFF>
<TABLE BORDER="0" CELLPADDING="5" WIDTH=100%>
<TR BGCOLOR=#822222>
<TD ALIGN=RIGHTXHl>Mój dziennik na dzień <?php print ("$date"); ?></Hl>
</TDX/TR>
<TRXTD>
<?php include("navbar.inc"); ?>
<TD WIDTH=85%>

<?php
/* Otwórz połączenie do bazy danych. */
include("/home/weblog/db_password.inc");
mysql_connect($hostname, Suser, Spassword);
mysql_select_db("weblogs");

/* Odszukaj ostatnią pozycję dla przypadków 3, 4 i 5. */


Squery = ("SELECT MAX(ID) FROM mylog");
Sresult = mysql_query($query);
$lastID_row = mysql_fetch_array($resuit);

Slast_ID = SlastID_row[0];
/* Pierwsza gałąź tekstu wyświetla pozycje dziennika w przypadkach l - 4... */
$queryl = ("SELECT ID, logtext FROM mylog WHERE date = Sdate");
$resultl = mysql_query($queryl);
$row_test_num = mysql_num_rows(Sresultl);
if($row_test_num > 0)
f
Sentry_row = mysql_fetch_array($resultl);
$entry_ID = $entry_row[0];
$logtext = stripslashes($entry_row[1]);
/* Pobierz poprzednią datę dla przypadków l, 3 i 4. Ten test zakłada,
że baza zwiększa wartości od 1; jeżeli jest inaczej, powinieneś
zmienić liczbę poniżej. */
if($entry_ID > 1)
t
$prev_ID = $entry_ID - 1;
$query2 = ("SELECT date FROM mylog WHERE ID = $prev_ID");
$result2 = mysql_query(Squery2);
$prevdate_row = mysql_fetch_array($result2);
$prev_date = Sprevdate_row[0];
)
else
374____________________________________Część II » PHP i bazy danych

(
$prev_date = "";
)
/* Pobierz następną datę dla przypadków 1 1 2 . */
if($entry_ID != $last_ID)
{
$next_ID = $entry_ID + 1;
Squęry3 = ("SELECT date FROM mylog WHERE ID = $next_ID");
$result3 = mysql_query($query3);
Snextdate_row = mysql_fetch_array($result3);
Snext_date = Snextdate_row[0];
}
else
{
$next_date = "";
(
/* Wyświetl tekst dla... */
/* Przypadek l */
if{$next_date != "" && $prev_date != ""}
(
print ("<CENTERxpxA HREF=\"db_weblog.php?date=$next_date\">Następny</A>
<A HREF=\"db_weblog.php?date=$prev_date\">Poprzedni</A>
</Px/CENTER>\n$logtext\n
<CENTERXPXA HREF=\"db_weblog.php?date=$next_date\">Następny</A>
<A HREF=\"db_weblog.php?date=$prev_date\">Poprzedni</Ax/Px/CENTER>") ;
)
/* Przypadek 2 */
e l s e i f ( $ n e x t _ d a t e != "" & & $prev_date == "")
(
p r i n t ("<CENTERXPXA HREF=\"db_weblog .php?date=$next_date\">Następny</A>
</PX/CENTER>\n$logtext\n<CENTER>
<PXA HREF=\"db_weblog.php?date=$next_date\">
Następny</AX/PX/CENTER>") ;
)
/* P r z y p a d k i 3 1 4 . */
e l s e i f ( $ n e x t _ d a t e ••=• "" 5& $prev_date != "")
{
print ("<CENTERXP>
<A HREF=\"db_weblog.php?date=$prev_date\">Poprzedni</A>
</PX/CENTER>\n$logtext\n<CENTERXP>
<A HREF=\"db_weblog.php?date=Sprev_date\">
Poprzedni</AX/PX/CENTER>") ;
(
}
/* ... ta gałąź obsługuje przypadek 5. */
else
{
/* Pobierz datę ostatniego wpisu. */
$query2 = ("SELECT date FROM mylog WHERE ID = $last_ID");
$result2 = mysql_query($query2) ;
Slastdate_row = mysql_fetch_array(Sresult2);
$last_date = $lastdate_row[0];
print("<P>Niestety dzisiaj nic nie ma! Mój ostatni wpis jest
<A HREF=\"db_weblog.php?date=Slast_date\">tutaj</A>.</P>");
)
?>
</TDX/TR>
</TABLE>
</TDX/TR>
</TABLE>
</BODY>
</HTML>
Rozdział 21. » Dziennik sieciowy____________________________________375

Wydruk 21.17. Skrypt tworzący bazą danych (weblog_db.php)___________________________


<?php
include("/home/weblog/db_password.inc");
mysql_connect(Shostname, Suser, Spassword) or die("Nieudana próba połączenia");
$try_create = mysql_create_db("weblogs");
if($try_create > 0)
l
echo ("Udane stworzenie bazy danych.");
mysql_select_db("weblogs") ;
$query = "CREATE TABLE login (ID SMALLINT NOT NULL AUTO_INCREMENT PRIMARY
KEY, username VARCHAR(20), password VARCHAR(20))";
$result = mysql_query(Squery) ;
// Nie używamy formatu daty MySQL, zapisujemy dane jako typ integer
Squery2 = "CREATE TABLE mylog
(ID SMALLINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
date INT, logtext TEXT)";
Sresult2 = mysql_query($query2) ;
mysql_close();
if($result > 0 SS $result2 > 0)
(
echo ("Udane tworzenie tabel");
)
else
l
echo ("Nieudane stworzenie tabel.");
)
)
else
(
echo ("Nieudane stworzenie bazy danych.");
)
?>

Możliwe rozszerzenia
Możesz zmienić i dodać następujące elementy w przytoczonym kodzie:
+ Zmiana kolorów, stylów i układu.
•* Zmiana częstotliwości uaktualnień (co tydzień, co miesiąc).
•* Zmiana nawigacji na bazującą na kalendarzu, a nie na łączach Następny i Po-
przedni.
+ Zmiana nawigacji na tematyczną, a nie na podstawie daty.
+ Wstrzymanie automatycznego przeterminowania pozycji.
+ Umożliwienie edycji przyszłych pozycji w bazie danych.
• Dodanie wielu autorów, redaktorów z różnymi uprawnieniami.

Oprócz osobistego dziennika, możesz na podstawie tego kodu utworzyć dowolną prostą
chronologiczną witrynę, na przykład:
•* Rodzinny dziennik wakacyjny.
•* Historia projektu.
376____________________________________Część II » PHP i bazy danych

+ Opowieść o utracie wagi dzięki heroicznej diecie i ćwiczeniom.


•* Kronika ciąży i rozwoju dziecka.

Podsumowanie
Mimo że PHP jest użyteczny w małych oddzielnych projektach, takich jak głosowania,
najbardziej imponujące jest tworzenie kompletnej witryny sterowanej danymi. Naj-
prostszym przykładem takiej witryny jest osobisty dziennik sieciowy. Polecamy prowa-
dzenie takiej witryny każdemu użytkownikowi, ponieważ jest użyteczna do testowania
nowych pomysłów i technik.

Jeżeli chcesz, możesz przechowywać dane w zwykłych plikach tekstowych, używając


PHP do dołączenia tych plików do szablonu na podstawie jakiegoś kryterium, na przy-
kład daty. Będzie to dużo lepsze niż statyczny HTML i może zaoszczędzić trochę czasu
przy formatowaniu, kosztem nieco obniżonego bezpieczeństwa. O wiele lepiej jest jed-
nak przechowywać dane w bazie danych.

Format dziennika sieciowego jest bardzo elastyczny. Może zostać rozbudowany do po-
ważnej publicznej witryny, jak na przykład Slashdot (z wieloma współpracownikami
i fachową redakcją zawartości). Możesz również tworzyć mały sekretny pamiętnik.
Ważne jest, że po stworzeniu za pomocą PHP kompletnej witryny sterowanej danymi
nie wrócisz już do statycznych stron WWW.
Rozdział 22.
Sieciowe głosowanie
W tym rozdziale:
* Przykład zastosowania PHP i bazy danych
+ Zbieranie głosów
* Wyświetlanie głosów
* Nadużycia

Rozdział ten jest przeglądem części kodu PHP, który jest używany w popularnej witrynie
WWW. Jest to dodatkowo fragment programu, który w przeciwieństwie do prezento-
wanych do tej pory przykładów jest ciągle rozwijany. Pokazuje on dosyć skomplikowa-
ną interakcję z bazą danych, sposób zastosowania mechanizmu cookie oraz kilka
interesujących sztuczek i technik.

Chociaż do tej pory staraliśmy się zamieszczać czyste i niezależne przy-


kłady, w tym rozdziale zdecydowaliśmy się na fragment prawdziwego pro-
gramu. Ponieważ pokazujemy tylko część kodu działającego w naszej
witrynie, ten pokazany na wydrukach sam nie będzie funkcjonował

W rozdziale tym założyliśmy znajomość zawartości całej drugiej części


książki, użyliśmy technik opisanych w rozdziale 26. „Cookie i HTTP". Mo-
żesz zapoznać się najpierw z rozdziałem 26., albo przeczytać niniejszy,
wierząc nam na słowo, że to, co napisaliśmy o cookie, jest prawdą.

Zadania systemu
W „wolnym czasie" Autorzy uruchomili kilka witryn WWW z recenzjami i rekomenda-
cjami książek. Jedna z nich jest poświęcona kryminałom (www.mysteryguide.com), dru-
ga opisuje książki popularno-naukowe (www.siencebookguide.com).
378_______________________________________Część II » PHP i bazy danych

Nasi recenzenci i my wypowiedzieliśmy się na temat książek, pozwoliliśmy również


naszym czytelnikom na ocenę za pomocą not od l do 5. Mimo że jest to czysto nauko-
wy pomiar, nasi czytelnicy dobrze się przy tym bawią. W rozdziale tym zajmiemy się
kodem PHP użytym do zbierania i wyświetlania ocen wystawionych przez naszych
czytelników.

Cele systemu
Gdy rozpoczynaliśmy dodawanie tych funkcji do naszej witryny, zamierzaliśmy zreali-
zować kilka celów. Chcieliśmy, aby nasz system:
* pozwalał ocenić książkę na stronie recenzji;
* wyświetlał bieżącą ocenę książki na stronie;
* na głównej stronie wyświetlał ranking książek;
* pozwalał na łatwe jednorazowe głosowanie na książkę;
** nie pozwalał na wielokrotne głosowanie na tę samą książkę.

Ostatnie dwa punkty (łatwość użycia i zniechęcanie do zniekształcania wyników) wy-


magają krótkiego opisu. Mimo że w tym czasie nie wiedzieliśmy, że określenie to bę-
dzie częścią sporu patentowego, chcieliśmy, aby głosowanie odbywało się ,jednym
kliknięciem". Chcieliśmy, aby użytkownik mógł po prostu zagłosować za pomocą jed-
nego kliknięcia, zamiast wypełniać formularz i naciskać przycisk Wyślij.

Po drugie chcieliśmy, aby normalny użytkownik systemu głosowania miał utrudnione


wielokrotne głosowanie na swoje ulubione pozycje. Wiemy, że bardzo trudno całkowi-
cie uszczelnić system tak, aby żaden żartowniś nie mógł obejść zabezpieczeń. Zastoso-
waliśmy system bazujący na mechanizmie cookie i spowodowaliśmy, że jeden
użytkownik może tylko raz zagłosować na książkę. Dodatkowo uruchomiliśmy system
odrzucania głosów, które w sposób oczywisty są zniekształcaj ą wynik.

Struktura
Nasza witryna ma jedną stronę zarezerwowaną dla każdej omawianej książki. We wcze-
śniejszej wersji witryny każda taka strona była statyczną stroną HTML. W wersji póź-
niejszej każda strona jest dynamicznie generowana na podstawie zawartości bazy
danych MySQL.

Sam kod składa się z dwóch części: kodu obsługującego zbieranie i wyświetlanie ocen
oraz kodu obsługującego centralną stronę ocen czytelników. W rozdziale tym te frag-
menty kodu są opisane w częściach „Zbieranie głosów" oraz „Wyświetlanie zbiorczych
wyników".
Rozdział 22. » Sieciowe głosowanie___________________________________379

Obsługa bazy danych


We wszystkich przykładach zamieszczonych w tym rozdziale wykorzystujemy bazę da-
nych MySQL. Mimo że mamy wiele różnych tabel do obsługi całej witryny, w tych
przykładach używamy tylko dwóch o następującej strukturze:

Tabela 'book'
ID int (NOT NULL, PRIMARY KEY, AUTO INCREMENT)
title varchar(75)
[... wiele kolumn nie używanych w tym kodzie]

Tabela 'ReaderRatings'
ID int (NOT NULL, PRIMARY KEY, AUTO INCREMENT)
bookid int (NOT NULL)
RatingDate timestamp
ClientIP varchar(lOO)
Rating tinyint
BogusBit tinyint

Zbieranie głosów
Pierwszy fragment kodu jest odpowiedzialny za części strony związane z głosowaniem.
Jest to fragment umożliwiający głosowanie oraz ten, który zawiera wyniki głosowania
na książkę. Na rysunku 22.1 pokazano część strony umożliwiającą głosowanie — aby
zobaczyć całą stronę, możesz zajrzeć na witrynę \vww. mysteryguide.com lub www.sien-
cebookguide. com.

Rysunek 22.1.
Fragment strony
umożliwiający
głosowanie

Fragmenty strony odpowiedzialne za głosowanie wyświetlają:


380_______________________________________Część II » PHP i bazy danych

* zaproszenie do głosowania i podsumowanie głosów, jeżeli użytkownik jeszcze


nie głosował na tę książkę;
* tekst „Dziękujemy za głosowanie", jeżeli użytkownik już zagłosował;
** puste miejsce, jeżeli wykryjemy za pomocą mechanizmu cookie, że użytkow-
nik już zagłosował na książkę.

Wyświetlaniem tych fragmentów strony oraz obsługą przesłanych głosów zajmują się
funkcje pokazane na wydruku 22.1. Są one dołączone do pliku „book.php", który zaj-
muje się wyświetlaniem stron poszczególnych książek.

Wydruk 22.1. Funkcje wyświetlania strony z informacjami o książce_______________________


<?php
function HandleRatings(}
(
global $Rating, $RatingCookieName, $$RatingCookieName, $book,
$dbhost, $dbuser, $dbpass,
$REMOTE_ADDR, $REMOTE_HOST;

if (IsSet($REMOTE_HOST))
Sremote = $REMOTE_HOST;
else
Sremote = $REMOTE_ADDR;

if ($Rating && !IsSet{$$RatingCookieName))


(
SetCookie($RatingCookieName, SRating);
static $dbh;
if (!IsSet($dbh))
$dbh =
mysql_connect($dbhost, Sdbuser, Sdbpass);
mysql_select_db('scienceguide');
$query = "insert ReaderRatings
(bookid, ClientIP, Rating)
values ($book, 'Sremote', $Rating[0])";
mysql_query($query);
}
)
function DisplayRatings()
{
global $book, $global_bar_color, $global_box_color,
$dbhost, $dbuser, Sdbpass;

static $dbh;
if (ilsSet(Sdbh))
Sdbh = mysql_connect($dbhost, $dbuser, $dbpass);
mysql_select_db('scienceguide');
SresultlD = raysql_query(
"select Rating, Count(ID)
from ReaderRatings
where bookid = $book
and BogusBit <> 1
group by Rating
order by Rating desc");
print("<A NAME-ReaderRating>

<TABLE BORDER=0 CELLPADDING=5 WIDTH=100%>


<TR BGCOLOR=$global_bar_color COLSPAN=1>
<TD>
<FONT SIZE=-1
FACE=\"ARIAL, GENEVA, SANS-SERIF\" COLOR=\"#FFFFFF\">") ;
print ("<B>Reader Ratings</BX/FONT>" ) ;
print ("</TDX/TR>
Rozdział 22. » Sieciowe głosowanie__________________________________381

<TR ALIGN=LEFT BGCOLOR=\"$global_box_color\"xTD>


<FONT S I Z E = - 2 FACE=\"ARIAL, GENEVA, SANS-SERIF\">");
if (mysql_num_rows($resultID) == 0)
(print("Nikt jeszcze nie oceniał tej k s i ą ż k i . < B R > " ) ; )
else (
print ("Dotychczasowe oceny tej książki: <BRxBR>\n");
print ("<TABLE ALIGN=CENTER BORDER=lXTRXTHXFONT SIZE=-2
FACE=\"ARIAL, GENEVA, S A N S - S E R I F \ " > R a t i n g < / F O N T X / T H X T H X F O N T SIZE=-2
FACE=\"ARIAL, GENEVA, SANS-SERIF\">Number</FONTX/THX/TR>") ;
while($row_array = mysql_fetch_row($resultID))
l
p r i n t ( " < T R ALIGN=CENTER>
<TDXFONT SIZE=-2
FACE=\"ARIAL, GENEVA, SANS-SERIF\">" ) ;
if ($row_array[0) == 1)
print("l - Słaba");
else if l$row_array[0] == 2)
print("2 - Średnia");
if ($row_array[0) == 3)
print("3 - Dobra");
if ($row_array[0] == 4)
print("4 - Bardzo dobra");
if ($row_array[0] == 5)
print("5 - Świetna");
print ("</FONTX/TDXTD>
<FONT SIZE=-2
FACE=\"ARIAL, GENEVA,
SANS-SERIF\">Srow_array[l]</FONTX/TDX/TR>") ;
)
print("</TABLE>");
}

print("<BR>Zobacz, jak zostały ocenione inne książki na stonie


<A HREF=\"readerratings.html\">0ceny czytelników</A>.");
print ("</TDX/TRX/TABLE>") ;
)
function GetFeedback ()
l
global Shook, SRating,
$global_bar_color, $global_box_color;
$target_path = "book.html";
print ("<A NAME=ReaderRatingxTABLE BORDER=0 CELLPADDING=5 W1DTH=100%>
<TR BGCOLOR=\"$global_bar_color\" COLSPAN=1>
<TD>
<FONT SIZE=-1 FACE=\"ARIAL, GENEVA, SANS-SERIF\" C O L O R = \ " # F F F F F F \ " > " ) ;

print ("<B>Co TY o tym sądzisz?</Bx/FONT>") ;

print ("</TDX/TR>
<TD ALIGN=LEFT BGCOLOR=\"$global_box_color\">
<FONT SIZE=-2 FACE-\"ARIAL, GENEVA, S A N S - S E R X F \ " > " ) ;
if ($Rating)
(
print("<B>Dziekujemy za głosowanie!</B>");
)
else
(
print("<B>Czy czytałeś tę ksiązkę?</B>
Jeżeli tak, poznajmy twoją opinię o niej.
Kliknij jeden z przycisków oceny
i twój głos zostanie zarejestrowany.
<CENTER>
<FORM ACTION=\"Starget_path\">
< I N P U T TYPE=SUBMIT NAME=Rating VALUE=\"5 - Świetna\"XBRXBR>
< I N P U T TYPE=SUBMIT NAME=Rating VALUE=\"4 - Bardzo dobra\"XBRXBR>
<INPUT TYPE=SUBMIT NAME=Rating VALUE=\"3 - Dobra\"XBRXBR>
<INPUT TYPE=SUBMIT NAME=Rating VALUE=\"2 - Średnia\"XBRXBR>
382____________________________________Część II » PHP i bazy danych

< I N P U T TYPE=SUBMIT NAME=Rating VALUE=\"1 - Słaba\"XBRXBR>


< I N P U T T Y P E = H I D D E N NAME=book VALUE-$bookXBRXBR>
</FORM>
</CENTER>
<B>Czy chcesz powiedzieć nam coś więcej ?</B>
Prześlij twój <A HREF=\"post.php3\">
komentarz</A> na <A HREF=\"index.php3\">forum ScienceBookGuide</AxBR>") ;
)
print { " < / F O N T X / T D > < / T R X / T A B L E > " ) ;
}

function ReaderRating ()
f
global $book, $Rating, SRatingCookieName, SSRatingCookieName;

if (!IsSet(S$RatingCookieName))
(
GetFeedbackl);
}
DisplayRatings();
)

Funkcje te są wywoływane na stronie książki w następujący sposób:


(... ustawienie zmiennych globalnych w tym $book)
$RatingCookieName = sprint f("Rating%s", $book);
HandleRatings();
ReaderRating{);

Założyliśmy, że funkcje będą użyte na stronie informacyjnej, która zawiera m.in. zaini-
cjowaną zmienną $book (identyfikator rekordu w tabeli książek), $dbhost, $dbuser
i $dbpass (aby podłączyć się do bazy danych MySQL) oraz $global_box_color
(namiastka arkusza stylu).

Zamieszczone na wydruku funkcje realizują następujące zadania:


* DisplayRatings ( ) wyświetla obszar z dotychczasowymi ocenami książek;
•* GetFeedback ( ) wyświetla formularz do głosowania na książkę;
•* HandleRatings ( ) funkcja wywoływana po kliknięciu przycisku głosowania,
powoduje przesłanie głosu do tej samej strony. Zajmuje się zapisaniem głosu
w bazie danych;
* ReaderRating ( ) agreguje powyższe funkcje, wyświetlając formularz za-
warty w funkcji GetFeedback ( ) tylko wtedy, gdy nie ma ustawionego cookie
dla tej książki.

Teraz nieco dokładniej opiszemy niektóre z tych funkcji.

DisplayRatings()
Funkcja DisplayRatings ( ) realizuje w postaci prostej tabeli wyświetlanie wyniku
wyrażenia SELECT na tabeli ReaderRatings, w sposób opisany w rozdziale 19.
Rozdział 22. » Sieciowe głosowanie___________________________________383

GetFeedbackQ
Funkcja Get Feedback ( ) jest przeznaczona głównie do prostej prezentacji formularza
dedykowanego do przesyłania wyniku ponownie do tej samej strony book.php i zawiera
jedną niewielką sztuczkę. Jeżeli zmienna $Rating jest zainicjowana, podczas wyświe-
tlania strony po zagłosowaniu na książkę, zamiast zaproszenia do głosowania wyświe-
tlane jest podziękowanie.

HandleRatingsQ
Po spełnieniu odpowiednich warunków, w odpowiedzi na naciśnięcie przycisku myszki
przez użytkownika, funkcja HandleRatings ( ) wstawia wiersz do tabeli ReaderRa-
tings. Chcieliśmy, aby warunki te były następujące:
1. Użytkownik właśnie przesłał głos.
2. Użytkownik nie wysyłał wcześniej takiego głosu (wykrywane przez cookie).

Aby to zrealizować, zastosowaliśmy następującą sztuczkę. Ustawiamy jedno cookie dla


każdej książki, na którą można głosować (przeczytaj następną uwagę na temat takiej
praktyki), nazwa cookie zależy od identyfikatora książki. W pliku tym ustawiamy więc
SRatnigCookieName na nazwę zależną od identyfikatora. Gdy głos jest obsługiwany,
ustawiamy cookie o takiej nazwie. Na koniec sprawdzamy, czy zainicjowana została
zmienna o nazwie $$RatingCookieName (zwróć uwagę na dwa znaki $$). Ta zmienna
zmienna oznacza „zmienną, której nazwa jest ciągiem przechowywanym przez zmienną
$RatingCookieName". Pozwala to na sprawdzenie, czy mieliśmy wcześniej ustawione
cookie skojarzone z książką, na którą głosował użytkownik.

Jeżeli wszystko jest w porządku, wstawiamy nowy wiersz zawierający głos oraz adres
IP głosującego. Nie narusza to prywatności — pozwala nam łatwiej wykryć nadużycia,
jeżeli głosy takie są zgrupowane (zajrzyj do części „Nadużycia" poniżej).

Gdy pisaliśmy ten kod, byliśmy jeszcze młodzi i nie wiedzieliśmy, że


bardzo złym pomysłem jest ustawianie dużej liczby cookies w przeglą-
darce. W tym kodzie ustawiane jest jedno cookie na książkę, co zwykle
powoduje dodanie kilku w czasie sesji i wyczerpanie limitu cookies dla
serwera. Teraz, gdy jesteśmy starsi i mądrzejsi, powinniśmy przepisać
ten fragment tak, aby przechowywał wszystkie informacje o głosowaniu
w jednym cookie, lub zapamiętywać klucz pozwalający na odczytanie
tych danych z bazy.

Wyświetlanie sumarycznych wyników


Na koniec na wydruku 22.2 pokazaliśmy stronę PHP odpowiedzialną za wyświetlanie
sumarycznych danych z głosowań oraz rankingu książek. Na rysunku 22.2 przestawiona
jest taka właśnie strona.
384____________________________________Część II » PHP i bazy danych

Wydruk 22.2. Strona wyświetlania wyników___________________________________


<?php
include "globals.inc";
include("sbg-functions.txt");

Smax_rating_display = 5;
$rain_votes = 2;
$min_best_votes = 2;
$min_worst_votes = 2;
?>

<HTML>
<HEAD>
<TITLE>Oceny czytelników</TITLE>
<META NAME="description" CONTENT="Strona ocen czytelników ScienceBookGuide.com">
<META NAME="keywords" CONTENT="Oceny czytelników, głosowanie, oceny,
ScienceBookGuide.com">
<!-- Strona ocen czytelników ScienceBookGuide.com -->
</HEAD>

<BODY T E X T = " ł O O O O O O " L I N K = " ł 0 0 6 4 0 0 " VLINK="łt800080">


<FONT FACE=<?php print("Sfontlist");?> >
<TABLE BORDER=0 CELLPADDING=0 WIDTH=100%>
<TR>
<?php include "sbg-navbar.txt" ?>

<TD BGCOLOR="#FFFFFF" ALIGN=LEFT VALIGN=TOP WIDTH=83%>


<?php
$dbh = mysql_connect($dbhost, Sdbname, Sdbpass);
mysql_select_db('scienceguide');
?>
<TABLE>
<TR>
<TD ALIGN=LEFT VALIGN=MIDDLE>
<FONT FACE=<?php print("Sfontlist");?> SIZE=+2xB>
Oceny czytelników</Bx/FONT>
</TDX/TRX/TABLE>
<BR>
<TABLE ALIGN=CENTER VALIGN=TOP BORDER=1>
<TR BGCOLOR="(łFFFFFF">
<TD WIDTH=400XFONT FACE=<?php print ( "Sfontlist" );?> SIZE=-1>
Każda strona książki w ScienceBookGuide posiada przyciski
pozwalające na jej ocenę w skali 1-5 (5 to 'Świetna',
l to 'Słaba').
Tutaj znajduje się zestawienie książek, które są kochane,
znienawidzone lub co najmniej zauważone przez czytelników
ScienceBookGuide.
</FONTX/TDX/TRX/TABLE>
<BR>
<TABLE COLS=1 BORDER=1 CELLPADDING=15 WIDTH-100% ALIGN=CENTER CELLSPACING=2>
<TR VALIGN=TOP ALIGN=CENTER>
<TDXH3xFONT FACE=<?php print ("Sfontlist") ;?> >Książki z najlepszymi ocenami
<Sup>*</supX/FONTX/H3>
<TABLE WIDTH=100% CELLPADDING=1>
<?php
SresultlD = mysql_query(
"select avg(Rating) as avgrating.
Count(ReaderRatings.ID) as countrating,
book.id,
book.title
from ReaderRatings, book
where BogusBit <> 1 and
book.id = bookid
group by bookid
order by avgrating desc, countrating desc");
print ("<TR ALIGN=LEFTXTH>Książka</THxTH>Liczba</THxTH>Średnia</TH>
<FONT FACE=$fontlist SIZE—!>");
Scounter = 0;
while!(Srow array = mysql_fetch row(SresultlD)) ss
Rozdział 22. » Sieciowe głosowanie___________________________________385

(Scounter < $max_rating_display))


(
if (Srow_array[1] > $min_best_votes - 1)
{
p r i n t ("<TR ALIGN=LEFTXTD>" ) ;
$bookid = $row_array[2];
$booktitle = stripslashes($row_array[3]);
$author_string = book_to_author_string(Sbookid);
Sbookurl = sprintf("book.html?book=%d", Sbookid);
Sbookstring = sprintf("%s, %s", $booktitle, $author_string);
print("<A HREF=\"$bookurl\">$booktitle</A>, $author~string
</TDXTD> <FONT FACE=Sfontlist SIZE=-l>$row_array [ 1] </FONT>
</TDXTD> <FONT FACE=$fontllst SIZE=-1>");
printf("%3.1f",Srow_array[0]) ;
print ("</FONTX/TD></TR>") ;
$counter = $counter + 1;
)
)
?>
</TABLE>
* <FONT FACE=<?php print("$fontlist") ; ?> SIZE=-2>Musi posiadać co najmniej
<?php print("Smin_best_votes") ; ?> głosów</FONT>
</TD>
</TR>
<TR VALIGN=TOP ALIGN=CENTER>
<TDXH3XFONT FACE=<?php print ( "Sf ontlist" ) ; ?> >
Książki z najgorszymi ocenami<sup>*</supx/FONTX/H3>
<TABLE WIDTH=100* CELLPADDING-1>
<?php
$resultID = mysql_query("select avg(Rating) as avgrating,
Count(ReaderRatings.ID) as countrating, book.id, book.title from
ReaderRatings, book where BogusBit <> 1 and book.id = bookid group by
bookid order by avgrating asc, countrating desc");
print("<TR ALIGN=LEFT>
<TH>Książka</THxTH>Liczba</THXTH>Średnia</TH>
<FONT FACE=$fontlist SIZE=-1>");
Scounter = 0;
w h i l e ( ( S r o w _ a r r a y = mysql_fetch_row(SresultID)) ss
($counter < $max_rating_display))
f
if ( ( $ r o w _ a r r a y [ l ] > $rain_worst_votes - 1) SS
( 3 r o w _ a r r a y [ 4 ] != S r o w _ a r r a y [ 3 ] ) )
f
p r i n t ("<TR ALIGN=LEFTXTD>
<FONT FACE=Sfontlist SIZE=-1>");
$bookid = $row_array[2];
Sbooktitle = stripslashes($row_array[3]);
$author_string = book_to_author_string($bookid);
Sbookurl = sprintf("book.html?book=%d", $bookid);
Sbookstring = sprintf("%s, %s",
$booktitle, $author_string);
print("<A HREF=\"Sbookurl\">$booktitle</A>, $author_string
</TD> <TD> <FONT FACE=Sfontlist SIZE=-l>$row_array[I]</FONT>
</TDXTD> <FONT FACE=Sfontlist SIZE=-1>">;
print f("%3.If",Srow_array[0]);
print ("</FONTX/TD></TR>") ;
Scounter = Scounter + 1;
)
) '
?>
</TABLE>
* <FONT FACE=<?php print("$fontlist");?> SIZE=-2>Musi mieć co najmniej
<?php print("Smin_worst_votes"); ?> głosów</FONT>

</TD>
</TR>
<TR VALIGN=TOP ALIGN=CENTER>
<TD><H3><FONT FACE=<?php print("Sfontlist");
?»Najczęściej oceniane książki</FONTX/H3>
<TABLE WIDTH=100% CELLPADDING=1>
386____________________________________Część II » PHP i bazy danych

<?php
SresultlD = mysql_query("select avg(Rating) as avgrating,
Count(ReaderRatings.ID) as countrating, book.id, book.title from
ReaderRatings, book where BogusBit <> 1 and book.id = bookid group by
bookid order by countrating desc");
print("<TR ALIGN=LEFT>
<TH>Book</THXTH>Number</THxTH>Average</TH>") ;
$counter = 0;
while((Srow_array = mysql_fetch_row(SresultlD)) &&
(Scounter < $max_rating_display))
(
if ($row_array[4] != $row_array[3])
{
print ("<TR ALIGN=LEFTXTDXFONT SIZE=-1 FACE=$fontlist>");
$bookid = $row_array[2];
$booktitle = stripslashes($row_array[3]) ;
$author_string = book_to_author_string($bookid);
$bookurl = sprintf("book.html?book=%d", Sbookid);
Sbookstring = sprintf("%s, %s", $booktitle, $author_string);
print("<A HREF=\"$bookurl\">$booktitle</A>, $author_string
</TDXTD> <FONT FACE=Sfontlist SIZE=-l>$row_array [ 1) </FONT>
</TDXTD> <FONT FACE=Sfontlist SIZE=-1>"1;
print ("%3.If",$row_array[0]);
print ("</FONTX/TDX/TR>") ;
$counter = $counter + 1;
)
)
?>
</TABLE>

<BR>
</TD>
</TR>
</TABLE>
<BR>
</TDX/TRX/TABLE>
<P ALIGN=RIGHTXFONT FACE=<?php print ("Sfontlist" );?> SIZE=-2 >
&tt!69 1999 Troutworks, Inc. All rights reserved. <BR>Ciagle zmieniane</FONTX/P>
</BODYX/HTML>

Rysunek 22.2
Strona wyświetlania
wyników

Kod ten wyświetla trzy tabele typu „Złota dziesiątka", zawierając najbardziej popularne,
najmniej popularne oraz najczęściej oceniane książki. Stylistycznie rzecz ujmując, powta-
rzanie kodu dla każdej tabeli jest nieładne — właściwie to realizując, powinniśmy napisać
funkcję, która jako argument wymagałaby podanie typu tabeli i kolejności sortowania.
Niestety nie zrobiliśmy tego i pokazujemy kod w takiej postaci, w jakiej istnieje.
Rozdział 22. * Sieciowe głosowanie__________________________________387

Nadużycia i skalowanie
System naszkicowany w tym rozdziale jest wystarczający do zbierania i wyświetlania
głosów w naszych witrynach poświęconych książkom i odpowiada użytkownikom.
W dodatku bazujący na cookies system utrudniający wielokrotne głosowanie na tę samą
książkę wydaje się być wystarczający. Proszę nie mylić utrudniania z zabezpieczaniem
— na pewno nie zgodzisz się na przeprowadzanie w ten sposób wyborów prezydenc-
kich. Istnieje wiele sposobów na ominięcie tego zabezpieczenia. Zauważyliśmy, że
wielu użytkowników zaakceptowało sens użycia tej funkcji. Czasami musimy wyrzucić
część podejrzanych głosów, które są w sposób oczywisty wygenerowane przez skrypt
lub ręcznie. Nie wnosi to niczego oprócz marnowania naszego czasu.

Jeżeli chodzi o skalowanie, to tylko jedna z naszych witryn (wmv.mysteryguide.com) jest


wystarczająco często odwiedzana, aby obsłużyć odczuwalną liczbę głosów. Do tej pory
zebraliśmy około 10000 głosów. Jest to trywialne zadanie dla każdej przemysłowej bazy
danych i mamy przyjemność zakomunikować, że nasza, warta 400 dolarów, maszyna K6,
służąca za serwer WWW, zapewnia obsługę zagregowanych stron za pomocą zestawu Li-
nux, Apache, MySQL i PHP. Nie stwierdzamy żadnych zauważalnych opóźnień, mimo że
nie zastosowaliśmy żadnego buforowania ani optymalizacji bazy danych.

Podsumowanie
W tym rozdziale pokazaliśmy bardziej skomplikowany fragment kodu, który jest uży-
wany przez dosyć popularną witrynę WWW. Zbiera on głosy użytkowników na temat
jakości książek przedstawionych w witrynie, zapisuje głosy w bazie danych i wyświetla
zestawienie ogólne i dla danej książki. Witryna używa systemu cookie utrudniającego
sztuczne windowanie wyników.

W dwóch kolejnych rozdziałach zajmiemy się bardziej ogólnymi aspektami tworzenia


witryn WWW z wykorzystaniem baz danych: stylem kodu oraz unikaniem częstych
pułapek.
388____________________________________Część II » PHP i bazy danych
Rozdział 23.
Styl i efektywność
rozwiązań na podstawie
PHP i bazy danych
W tym rozdziale:
* Efektywne używanie zasobów bazy danych
4 Przenoszenie pracy na serwer bazy danych
4 Używanie pól daty i czasu w zapytaniach

Ten niewielki rozdział jest przeznaczony dla osób, tworzących witryny WWW korzy-
stające z bazy danych, które uważają, że robią coś nieodpowiednio lub nieefektywnie.
Być może nie masz doświadczenia z bazami danych, albo twoje strony ładują się zbyt
długo.

Podamy tu kilka sztuczek i sposobów pozwalających przyspieszyć działanie witryny oraz


pokażemy, w jaki sposób system bazy danych może wyeliminować pisanie niepotrzebne-
go kodu w PHP. Jak zwykle, niektóre z naszych przykładów używaj ą funkcji MySQL, ale
porady zamieszczone tutaj są ogólne i niezależne od implementacji bazy danych.

W tym rozdziale nie pomożemy uruchomić źle działających połączeń


z bazą danych. Przewodnik po częstych błędach, pułapkach i proble-
mach z bazą danych zamieszczony jest w rozdziale 24.
390_______________________________________Część II » PHP i bazy danych

Połączenia
— ograniczanie i powtórne użycie
Ważne, abyś pamiętał, że nawiązanie połączenia z bazą danych jest zawsze kosztowną
operacją. Jeżeli twój skrypt nie wykonuje intensywnych obliczeń, to interakcja z bazą
danych będzie najbardziej czasochłonną częścią kodu, pochłaniającą też najwięcej za-
sobów. Najczęściej sprawdza się twierdzenie, że nawiązanie połączenia jest najbardziej
kosztowną częścią kodu, nawet gdy połączenie jest nawiązywane na stronie tylko raz.

Istnieją tutaj dwa potencjalnie konkurencyjne cele. Z jednej strony zminimalizowanie


liczby kosztownych i czasochłonnych wywołań funkcji do otwarcia zupełnie nowego
połączenia z bazą danych. W przypadku tego podejścia optujemy za pozostawieniem
otwartego połączenia zamiast zamykania go i powtórnego otwierania. Z drugiej strony,
istnieją czasami ograniczenia liczby jednocześnie otwartych połączeń, jakie może ob-
służyć serwer bazy danych. W takim przypadku stawiamy na zamykanie połączeń tak
szybko, jak jest to możliwe, w nadziei, że krótszy czas połączenia pozwoli na jednocze-
sne wykonanie większej liczby skryptów.

Według naszych obserwacji, większość skryptów WWW nie wymaga nakładu na za-
mykanie i powtórne otwieranie połączenia z bazą danych w trakcie wykonywania jed-
nego skryptu. Jeżeli chcesz zminimalizować czas połączenia z bazą danych, otwieraj
połączenie bezpośrednio przed pierwszą operacją na bazie danych i zamykaj natych-
miast po wykonaniu ostatniej.

Przykład nieprawidłowego użycia:


jedno połączenie na wyrażenie
Nasz pierwszy przykład nieprawidłowego użycia wydaje się stylistycznie uzasadniony,
ponieważ przy użyciu tej funkcji można eliminować powtarzający się kod.
<?php
function box_query (Squery, Suser, $pass, $db)
(
$my_connection = mysql_connect('localhost', $user, $pass);
mysql_select_db { $db, $my_connection };
$result_id = mysql_query( $query, $my_connection);
print("<H3>Wynik zapytania: $query</H3>");
print("<TABLE>");
while ($tow = mysql_fetch_row($result_id))
f
print("<TR>");
$row_length = mysql_num_fields(Sresult_id) ;
for( $x=0 ; $x < $row_length ; $x++ )
(
$entry = $row[$x];
print ("<TD>?entry</TD>") ,-
)
print("</TR>");
}
print("</TABLE>");
mysql_close(Smy_connection);
Rozdział 23. » Styl i efektywność rozwiązań na podstawie PHP i bazy danych__________391

)
/* kod używający funkcji box__query{) */
?>

Funkcja ta pobiera określone zapytanie MySQL i wyświetla jego wynik w atrakcyjnej


tabeli HTML. Podstawową zaletą takiej definicji funkcji jest jej niezależność — otwiera
połączenie z bazą danych tylko dla własnych potrzeb i po zakończeniu pracy zamyka je.

Powyższy kod sprawdza się świetnie, jeżeli spodziewamy się wyświetlenia tylko jednej
tabelki na stronie. Jeżeli jednak użyjemy tej funkcji więcej niż raz na stronie, będzie
otwierała i zamykała połączenie z bazą danych za każdym jej wywołaniem, co jest
mniej efektywne niż pozostawienie otwartego połączenia. Jak wcześniej wspomnieli-
śmy, naczelną zasadą jest pozostawianie otwartego połączenia, dopóki jest ono potrzeb-
ne na bieżącej stronie. Zastosowanie tej zasady do przedstawionej funkcji powinno
spowodować jej zmodyfikowanie i pobieranie połączenia jako argumentu (lub niejawne
używanie otwartego połączenia na początku skryptu), a następnie otwarcie jednego po-
łączenia na stronę.

Kilka wyników nie wymaga kilku połączeń


Co nas w wielu bazach danych zaskoczyło, gdy zobaczyliśmy pierwszy skrypt korzy-
stający z bazy, to możliwe utrzymywanie wyniku więcej niż jednego zapytania jedno-
cześnie nawet wtedy, gdy istnieje otwarte tylko jedno połączenie. Na przykład przy
użyciu bazy danych MySQL można wykonać coś takiego:
mysql_connect('localhost', $user, $pass); // otwarcie połączenia
mysql_select_db('sienceguide') ;
$author_result = mysq_query("select ID from author")
or die(mysql_error());
while ($author_row = mysql_fetch_row{$author_result))
(
$book_result = mysql_query("select title from book
where authorlD = $author_row[0]"};
while ($book_row = mysql_fetch_row($book_result))
{
Stitle = $book_row[0];
print("Stitle<BR>");
)
}

Fragment ten powinien wypisać tytuły książek, odczytanych z tabeli książek, przy uży-
ciu identyfikatorów, odczytanych z tabeli autorów. Przykład ten pokazuje co prawda
skrajnie nieefektywną metodę pobierania danych (spójrz do części „Przenoszenie pracy
na serwer bazy danych"), ale ilustruje fakt, że dwa różne zestawy wyników (identyfi-
kowane przez zmienne $author_result i $book_result) mogą być używane w tym
samym czasie, po ich odczytaniu poprzez to samo połączenie.

Trwałe połączenia
Jeżeli przekonałeś się, że nakłady na otwieranie nowych połączeń do bazy danych za-
bijają wydajność, możesz zapoznać się z otwieraniem trwałych połączeń. W przeci-
wieństwie do zwykłych połączeń z bazą danych połączenia takie nie są automatycznie
392____________________________________Część II » PHP i bazy danych

niszczone podczas zakończenia wykonywania skryptu (lub nawet za pomocą wywołania


funkcji myql_close ( ) ) , ale są przechowywane w puli połączeń do późniejszego uży-
cia. Za pierwszym razem, gdy jeden ze skryptów otwiera połączenie, jest ono otwierane
w taki sam pochłaniający zasoby sposób jak zwykłe połączenie. Jednak następny wyko-
nywany skrypt w odpowiedzi na żądanie może dostać to samo połączenie (poprzednie
połączenie będzie użyte tylko wtedy, gdy parametry nowego żądania są identyczne
z poprzednim).

Funkcja żądania otwarcia trwałego połączenia z MySQL to mysql_pconnect ( ) , która


jest używana w dokładnie taki sam sposób jak mysql_connect ( ) . Taka konwencja
nazywania funkcji PHP wydaje się być stabilna w przypadku funkcji PHP dla innych
baz danych —jeżeli używasz funkcji „connect" określonej bazy danych, powinieneś
sprawdzić, czy istnieje odpowiednia funkcja „pconnect".

Trwałe połączenia do bazy danych istnieją tylko w przypadku instalacji


PHP jako moduł. Jeżeli zażądasz trwałego połączenia w wersji CGI, po
prostu dostaniesz zwykłe połączenie.

Oprócz zwiększania wydajności trwałe połączenie z bazą danych nie


ma żadnych dodatkowych funkcji, w porównaniu do zwykłych połą-
czeń. Nie powinieneś oczekiwać „pamięci" poprzednich zapytań lub
wartości zmiennych z wykonania poprzednich skryptów.

Przenoszenie pracy
na serwer bazy danych
Jak w przypadku pisania kodu w dowolnym języku programowania, pisanie kodu współ-
działającego z bazą danych jest ćwiczeniem odpowiedniego podziału pracy. Ludzie piszą-
cy języki programowania i bazy danych zgodzili się na automatyzację, standaryzację
i optymalizację niektórych zadań często wykonywanych w czasie programowania, więc
programiści nie muszą ciągle „wynajdywać na nowo koła" w czasie tworzenia kolejnych
aplikacji. Ogólna zasada brzmi: o ile nie chcesz poświęcać wiele energii na optymalizację
kodu dla specjalnego zastosowania, lepiej użyj mechanizmu bazy danych, zamiast wymy-
ślać własne rozwiązanie tego samego zadania.

Baza jest szybsza od ciebie


Programy baz danych są często oceniane na podstawie ich szybkości, więc programiści
baz danych poświęcają dużą część czasu na usprawnienie szybkości wykonywania za-
pytań. Każde szukanie wartości lub sortowanie zawartości bazy danych powinno być
realizowane przez bazę (o ile jest to możliwe), a nie przez twój kod.
Rozdział 23. » Styl i efektywność rozwiązań na podstawie PHP i bazy danych_________393

Przykład nieprawidłowego użycia:


pętla zamiast warunku
Dla przykładu zamieszczamy następujący fragment kodu (proszę się nie śmiać — wi-
dzieliśmy już takie kwiatki):
function print_first_name_bad( Slastname, Sdbconnection }
{
$query = "select firstname, lastname from author";
$result_id = mysql_query(Squery, Sdbconnection );
while ($row = mysql_fetch_array($result_id))
(
if ($row('lastname'] == Slastname)
print("Imie to ". $row['firstname']);
}
)

Gdy przekażemy do tej funkcji nazwisko i połączenie do bazy danych, wypisze ona
związane z nazwiskami imiona z tabeli „author", o ile takie istnieją. Dla przykładu
wywołanie print_first_name_bad( 'Sagan' , $dbconnection) może dać nastę-
pujący wynik
Imię to Carl

Jeżeli istnieje wielu autorów o takim samym nazwisku, zostanie wypisanych wiele
wierszy.

Problemem jest to, że nie potrzebujemy pobierać wszystkich danych z tabeli, „przepy-
chać przez cienką rurę", odczytywać i wybierać odpowiednie rekordy po naszej stronie
połączenia. W zamian powinniśmy użyć w zapytaniu warunku za pomocą klauzuli
WHERE:
function print_first_name_beter( Slastname, $dbconnection )
{
$query = "select firstname, lastname from author
where lastname = $łastname";
$result_id = mysql_query($query, Sdbconnection );
while ($row = mysql_fetch_array($result_id))
(
print("Imie to ". Srow['firstname']);
}
)

Klauzula WHERE zapewnia, że otrzymamy tylko interesujące nas wiersze. Nie tylko po-
zwala na ograniczenie ilości danych przesyłanych przez połączenie SQL, ale kod użyty
do odszukania właściwych wierszy w bazie danych będzie oczywiście szybszy niż twój
kod PHP.

Sortowanie i agregacja
Dokładnie tego samego argumentu powinieneś użyć, gdy będziesz chciał sam napisać
fragment sortujący wyniki zwrócone przez bazę danych, zliczający, liczący średnią (al-
bo w inny sposób agregujący wyniki). Klauzula ORDER BY w SQL pozwala na posor-
towanie pobranych wierszy według dowolnej listy kolumn w zapytaniu; sortowanie to
będzie prawdopodobnie efektywniejsze niż jakikolwiek własny kod lub funkcja PHP
394______________________________________Część II » PHP i bazy danych

sortująca tablicę. Podobnie, zamiast przeglądać wiersze bazy danych w celu zliczenia,
zsumowania lub obliczenia średniej, sprawdź, czy twoja baza danych pozwala na użycie
w SQL konstrukcji GROUP BY oraz funkcji do użycia w zapytaniach: count (), sum ()
i average ( ) . Zwykle wykonanie zapytania:
Squery = "select count(ID) from author";

jest o wydajniejszym sposobem liczenia wierszy tabeli niż pobranie ich i liczenie ich
w pętli PHP.

Tworzenie pól daty i czasu


Chcesz powiązać datę i czas z wartościami zapisanymi w wierszu tabeli? Wiersze tabeli
mogąnp. reprezentować żądania użytkowników do twojego serwera WWW.

W chwili obecnej jedynym sposobem na wstawienie lub zmianę pól daty jest podanie
ciągu reprezentującego żądaną datę w formacie czytelnym dla bazy danych. Jeżeli
chcesz np. ustawić na określoną datę pole mydate typu datetime wszystkich wierszy
tabeli, możesz to zrobić za pomocą takiego pytania:
$query = "update mytable set myquery = 'September 4, 2001'";

Następnie wysyłamy takie zapytanie do wykonania (niestety standardy formatów daty


różnią się w zależności od systemu baz danych SQL).

Poprzednie wyjście jest dobre, dopóki ciąg z datą jest prawidłowy dla twojej bazy da-
nych. Sprawy się komplikują, gdy potrzebujesz takiego ciągu na bieżąco, aby repre-
zentował datę uzależnioną od wartości zmiennych w skrypcie.

Należy jednak pamiętać, że w większości systemów baz danych nie ma potrzeby prze-
chodzenia przez te męki przy ustawianiu pola na bieżącą datę i czas. Zwykle dostępna
jest funkcja zwracająca bieżącą datę; używa się jej bezpośrednio w zapytaniu. Poprzed-
nie zapytanie w wersji dla MySQL, które ustawia odpowiednie pole daty i czasu na datę
bieżącą, wygląda następująco:
Squery = "update mytable set myquery = now()";

Zauważ, że wywołanie funkcji nów ( ) nie jest otoczone apostrofami. Analogiczne za-
pytanie dla Microsoft SQL Server wygląda następująco:
Squery = "update mytable set myquery = getdateU";

Jeżeli data, którą chcesz zapisać, nie jest datą bieżącą, są lepsze sposoby od tworzenia
w skrypcie czytelnego dla serwera ciągu z datą. Wiele wersji SQL ma, oprócz funkcji
zwracających bieżącą datę, funkcje wykonujące operacje na datach. Możesz dodać lata,
miesiące lub godziny do dowolnej daty. W MySQL takąfunkcjąjest:
^ date add(date, date-interval)
Rozdział 23. » Styl i efektywność rozwiązań na podstawie PHP i bazy danych__________395

Parametr date-interval jest ciągiem określającym liczbę jednostek czasu i rodzaj


ich. Zapytanie MySQL ustawiające wszystkie wiersze na datę o tydzień późniejszą od
dnia dzisiejszego wygląda następująco:
Squery = "update mytable set mydate = date_add(nów(), '7 days1)";

Szukanie ostatnio wstawionego wiersza


Inną pomocną funkcją, oferowaną przez niektóre systemy baz danych jest szukanie
identyfikatora ostatnio wstawionego wiersza.

Problem pojawia się, gdy próbujesz utworzyć nową pozycję w bazie danych, która jest
rozproszona w kilku tabelach (każda z tych tabel ma klucz główny o automatycznie
zwiększającej się wartości). Jako przykład weźmy tabele utworzone za pomocą nastę-
pujących wyrażeń MySQL:
create table author) ID int primary key, auto_increment,
lastname varchar(75),
firstname varchar(75));
create table book (ID int primary key, auto_increment,
authorlD int,
title varchar(100));

Jednym założeniem w przypadku tych wyrażeń jest połączenie tabeli book z tabelą au-
thor w taki sposób, że b o o k . a u t h o r l D = a u t h o r . I D . Kolejnym założeniem jest au-
tomatyczne przypisanie przez bazę danych unikalnych identyfikatorów w tych tabelach.
Niestety połączenie tych założeń prowadzi do problemów. W jaki sposób mamy napisać
kod, który wstawi połączoną parę książka-autor, jeśli zarówno książka, jak i autor są
nowi w bazie danych? Jeżeli wstawimy nowego autora, pole ID wstawionego wiersza
będzie automatycznie utworzone przez bazę danych, więc nie jest częścią twojego wy-
rażenia SQL. W jaki sposób mamy podać właściwą wartość pola authorlD w nowym
wierszu tabeli book?

Jedną z możliwych strategii pokazujemy w poniższym przykładzie (w MySQL):


Sauthor_lastname = 'Feynman';
$author_firstname = 'Richard';
$book_title = 'The Character of Physical Law 1 ;
$author_insert = "insert into author(lastname, firstname)
values! '$author_lastname', 'Sauthor_firstname')";
mysql_query($author_insert) or die(mysql_error());
Sauthor_id_query =
"select ID from author
where lastname = ' Sauthor_lastname' and
firstname = '$author_firstname'";
$author_id_result = mysql_query($author_id_query) or die(mysql_error());
if (mysql_num_rows($author_id_result) <= 0)
die ("Nie odnaleziony wstawiony autor!"};
else
$author_row = mysql_fetch_row($author_id_result);
SauthorlD = $author_row[0];
$book_insert = "insert into book (authorlD, title)
values (SauthorlD, $book_title)";
mysql_query($author_insert) or die(mysql_error());
396____________________________________ Część II » PHP i bazy danych

W kodzie tym tworzymy nowy wiersz autora i używając nazwiska oraz imienia wybie-
ramy ten nowo wstawiony wiersz, zapamiętując przypisany identyfikator nowego wier-
sza. Następnie dodajemy ten identyfikator do wyrażenia wstawiającego nowy wiersz do
tabeli book. Kod ten będzie prawdopodobnie działał w niektórych przypadkach, jeżeli
założymy, że imię i nazwisko autora są wystarczające do jednoznacznej identyfikacji
rekordu. Niestety w wielu bazach danych nie można utworzyć takiego założenia, co jest
oczywiście powodem stosowania unikalnych identyfikatorów.

Podobnym wyjściem, które jest czasami używane, jest wstawianie wiersza (na przykład
to tabeli autorów) i pobieranie wiersza o największym identyfikatorze (teoretycznie jest
wstawiony najpóźniej). Jest to niestety rozwiązanie, które działa tylko wtedy, gdy jest
testowane przez pojedynczego programistę, a przestaje działać na docelowym serwerze
bazy danych, obsługującym wiele połączeń jednocześnie. Problem wystąpi, gdy z inne-
go połączenia przyjdzie żądanie wstawienia rekordu i będzie obsłużone pomiędzy na-
szym wstawieniem a pobraniem największej wartości identyfikatora. Spowoduje to
pobranie nieprawidłowego identyfikatora, przez co nasze drugie wyrażenie wstawienia
rekordu będzie miało niewłaściwy identyfikator autora.

Najlepszym rozwiązaniem, o ile jest ono dostępne, jest pozwolenie bazie danych na pil-
nowanie ostatnio wstawionego identyfikatora i udostępnianie go w bieżącym połącze-
niu. W ten sposób nie wystąpią problemy synchronizacji, opisane w poprzednim
akapicie. PHP oferuje użytkownikom MySQL funkcję mysql_insert_id ( ) , która na
podstawie identyfikatora połączenia pobiera identyfikator ostatnio wstawionego wier-
sza. Możemy użyć tej funkcji do poprawienia poprzedniego przykładowego kodu:
Sauthor_lastname = 'Feynman1;
Sauthor_firstname = 'Richard1;
$book_title = 'The Character of Physical Law';
$author_insert = "insert into author(lastname, firstname)
values! '$author_lastname', 'Sauthor_firstname')";
mysql_query(Sauthor_insert) or die(mysql_error());
SauthorlD = mysql_insert_id{) ;
$book_insert = "insert into book (authorlD, title)
values ($authorID, $book_title)";
mysql_query($author_insert) or die(mysql_error()};

Jak w przypadku wielu funkcji PHP i MySQL, identyfikator połączenia w funkcji my-
sql_insert_id ( } jest opcjonalny i domyślnie przyjmuje ostatnio otwarte połączenie.

W niektórych systemach baz danych identyfikator ostatniej automatycznie zwiększają-


cej się wartości jest dostępny (w sesji) jako „specjalna" zmienna, której można użyć
w kolejnym zapytaniu. W Microsoft SQL Server zmienną tą jest '%%identity'; w celu
odczytania identyfikatora ostatnio wstawionego rekordu można wstawić ją do zapytania
w następujący sposób:
$query = "select %%identity";
Rozdział 23. » Styl i efektywność rozwiązań na podstawie PHP i bazy danych_________397

Podsumowanie
Ponieważ funkcje związane z bazą danych są pochłaniają najwięcej zasobów, możesz
korzystać z bardziej efektywnych technik kodowania. Jeżeli twoje skrypty sterowane
danymi są ociężałe, powinieneś nauczyć się działać razem z bazą danych, zamiast prze-
ciwko niej.

Podstawowe zasady stosowane w kodzie intensywnie korzystającym z bazy są proste.


Dużo kosztuje otwarcie połączenia z bazą, więc nie zamykajmy go niepotrzebnie. Pa-
miętaj, że powinieneś przenosić jedynie minimum danych potrzebnych na stronie. Po-
święć nieco czasu na nauczenie się funkcji oferowanych przez twoją bazę danych. SQL
jest naprawdę dobry do sortowania, filtrowania, ograniczania, zliczania i grupowania
— możesz go użyć, zamiast posługiwać się wolniejszym PHP.

W następnym rozdziale odejdziemy od wskazówek i kwestii stylistycznych. Zajmiemy


się problemami i pułapkami, które mogą popsuć kod korzystający z bazy danych lub
spowodować niespodziewane wyniki.
.39§_________________.____________________Część II » PHP i bazy danych
Rozdział 24.
Pułapki tandemu
PHP-baza danych
W tym rozdziale:
* Brak połączenia
* Problemy z uprawnieniami
* Nieoznaczone apostrofy
* Nieprawidłowe zapytania SQL
* Zbyt mało lub zbyt dużo danych

W tym rozdziale opisujemy kilka często występujących problemów z połączeniem PHP


z bazą danych. Celem tego rozdziału jest pomoc w diagnozowaniu przyczyn problemu
oraz ich szybsze rozwiązywanie. Jak zwykle, nasze przykłady kodu oraz odwołania do
funkcji mają za podstawę bazę danych MySQL, ale przedstawiony zbiór pułapek jest
całkowicie niezależny od rodzaju bazy danych.

W tym rozdziale zajmujemy się diagnozowaniem i poprawianiem kodu


PHP, który jest nieprawidłowy — to znaczy, że nie potrafi odczytać da-
nych lub powoduje generowanie błędów. Jeżeli twoje skrypty działają,
ale powoli, wróć do poprzedniego rozdziału.

Brak połączenia
Jeżeli w skrypcie PHP masz odwołanie do bazy danych, a otwarcie połączenia nie udało
się, zobaczysz jeden z dwóch przedstawionych poniżej ostrzeżeń (w zależności od
ustawionego poziomu ostrzeżeń, a także, w pewnym stopniu, od tego, co spowodowało
problem).
400______________________________________Część II » PHP i bazy danych

Pierwszy z możliwych ekranów ostrzeżenia o braku połączenia pokazany jest na rysun-


ku 24.1.

Rysunek 24.1.
Ostrzeżenie
o braku połączenia

Wskazuje to na problem z serwerem MySQL lub z ścieżką do mysqld. W ten specyficz-


ny sposób PHP próbuje nam powiedzieć, że wie on o MySQL, ale nie potrafi się z nim
połączyć.

Pierwsza wprowadzona na rynek wersja PHP 4, 4.0.0 posiadała niepra-


widłową funkcję mysqi_connect o , która nie działała, gdy otrzymała
jakikolwiek argument. Wersja funkcji dla połączenia trwałego (mysqi_
pconnecto) działała bez problemów. Błąd ten został poprawiony
w PHP 4.0.1. Jeśli posiadasz wersję 4.0.0, a chcesz skorzystać z my-
sqi_connect o , powinieneś zainstalować nowszą wersję.

Jeżeli problem leży po stronie PHP, ekran błędu będzie raczej wyglądał jak ten pokaza-
ny na rysunku 24.2.

Rysunek 24.2.
Krytyczny bląd
wywalania
niezdefiniowanej funkcji
Rozdział 24. » Pułapki tandemu PHP-baza danych__________________________401

Oznacza to, że PHP nie wie nic o MySQL.

Z tych dwóch przypadków błąd krytyczny jest prostszy do poprawienia. Jeżeli otrzymasz
błąd wywołania niezdefiniowanej funkcji, która występuje w zestawie funkcji PHP, na
pewno zapomniałeś dodać tego modułu podczas instalowania PHP. W przypadku sys-
temu Unix powinieneś skompilować ponownie PHP z opcją -with-mysql. W przypadku
Windows wszystkie dostępne opcje są już skompilowane w dostarczonym programie,
więc powinna być dostępna odpowiednia instalacja, albo możesz włączać i wyłączać
poszczególne funkcje w p\\kuphp.ini. W przypadku MySQL (lub innej obsługiwanej
bazy danych) wystarczy po prostu odkomentować wiersz zawierający php_mysql.dll
w pliku php.ini i PHP jest już gotowy do pracy (chyba że umieściłeś pliki programu
MySQL w bardzo dziwnym miejscu, chociaż nie powinieneś tego robić).

Niewinnie wyglądający błąd braku połączenia jest nieco trudniejszy do zdiagnozowa-


nia, ponieważ może wystąpić kilka różnych przyczyn błędu. Można je przypisać do
jednej z dwóch kategorii:
1. Demon MySQL nie działa.
2. MySQL używa innego gniazda, niż się spodziewa PHP.

Łatwo sprawdzić, czy program mysqld pracuje, więc możemy to zrobić na początku.
Można tutaj użyć dowolnej metody, jakiej zwykle używasz do sprawdzania pracujących
procesów. Na Windows NT oznacza to, że trzeba użyć kombinacji Ctrl+Alt+Delete, aby
wywołać Task Managera. W systemach Unix, gdzie swoboda wyboru jest ogromna,
możesz sprawdzić procesy systemu poprzez wywołanie p s lub graficznego programu
monitorowania systemu, albo nawet uruchamiając mysqladmin.

Jeżeli mysqld nie działa, prawdopodobnie po prostu zapomniałeś go uruchomić (proszę


się nie śmiać, to się zdarza). Jeżeli działał on bez przerwy 143 dni przed niespodziewa-
nym zakończeniem pracy w środku operacji, problem twój wykracza poza ramy tej
książki. Możemy jedynie skierować cię do witryny MySQL i mieć nadzieję, że serwer
był regularnie składowany.

Problemy z gniazdami zwykle występują za pierwszym razem po uruchomieniu MySQL


na nowym serwerze. Jest to raczej rzadko występujący problem w przypadku działają-
cego już serwera, jednak czasami się zdarza. Ostatnio mieliśmy przypadek w jednej
z witryn, gdy demon MySQL został przeniesiony na inny serwer bez uprzedniego po-
wiadomienia, co spowodowało, że wszystkie skrypty używające jako nazwy komputera
localhost natychmiast przestały działać.

Rozwiązanie problemów z podłączeniem do bazy danych znajduje się zwykle w pliku


php.ini. Istnieją tam sekcje zmiennych MySQL, w których należy dokładnie sprawdzić,
czy podane tam: nazwa komputera, port i gniazdo zgadzają się z tym, co podajesz
w skryptach PHP. Powinieneś się upewnić, że przypadkowo nie wskazujesz, aby PHP
szukał MySQL poprzez dziwny port lub na niewłaściwym komputerze. W systemie
Unix możesz również sprawdzić w pliku /etc/services, czy nie ma tam innych adresów
gniazd. Jeżeli nie masz innych poważnych powodów, powinieneś pozostawić te zmien-
ne puste.
402____________________________________Część II » PHP i bazy danych

Problemy z uprawnieniami
Komunikaty o błędach, powodowane przez problemy z uprawnieniami, wyglądają po-
dobnie do opisanych przed chwilą problemów z nawiązaniem połączenia.

Rysunek 24.3.
Problemy
z uprawnieniami

Kluczową różnicą jest mały fragment o nazwie użytkownika i haśle.

Ponieważ komunikaty naruszają nieco system bezpieczeństwa, najle-


piej na docelowej witrynie używać trybu „cichego". Można to zrobić
przez dodanie znaku '@' przed funkcjami mysqi_connect oraz my-
sql_select_db.

Istnieje wiele takich komunikatów, ale można je przypisać do jednej z poniższych


kategorii:
* Błędnie wpisana nazwa użytkownika lub hasło.
* Nieudane użycie hasła.
* Próba użycia nieistniejącego hasła.
* Próba użycia użytkownika i hasła systemowego zamiast użytkownika i hasła
MySQL.
* Użycie użytkownika bazy danych, który ma niewystarczające uprawnienia do
wykonania operacji.
+ Próba logowania z lokacji lub klienta; MySQL nie pozwala na pracę określo-
nych użytkowników.
* PHP nie może otworzyć pliku haseł bazy danych z powodu nieodpowiednich
uprawnień pliku (musi być to plik do odczytu w katalogu dostępnym dla
wszystkich).
* Administrator bazy danych zabrał ci uprawnienia do korzystania z bazy danych.
Rozdział 24. » Pułapki tandemu PHP-baza danych__________________________403

Nie są to problemy strukturalne, ale zwykle skutki zapomnienia ważnych dla pracy
systemu parametrów. Błędy te można łatwo naprawić w większości sytuacji.

Nieoznaczone apostrofy
Apostrofy mogą powodować wiele małych, choć dokuczliwych problemów pomiędzy
PHP i MySQL. Sednem problemów jest fakt, że PHP przetwarza ciągi w cudzysłowach
i ignoruje apostrofy, natomiast MySQL przetwarza ciągi w apostrofach i ignoruje cu-
dzysłowy. Prowadzi to do sytuacji, w których musisz dokładnie określić przeznaczenie
każdego z tych znaków. Przykładem takiego wyrażenia jest:
mysql_query("INSERT INTO book (ID, title, year, ISBN)
VALUES ('NULL 1 , '$title', 'Syear1, '$ISBN')");

W PHP zmienne w apostrofach nie są rozwijane, natomiast zmienne w cudzysłowach są


— więc to wyrażenie wygląda nieco dziwnie. Jeżeli jednak chwilę nad nim pomyślisz,
stwierdzisz, wyrażenie jest prawidłowe w obu językach. Apostrofy występują w cudzy-
słowach, więc PHP traktuje je dosłownie; zmienne znajdują się w cudzysłowach, więc
PHP podstawia na ich miejsce wartości tych zmiennych. Można o tym myśleć jako
o podziale pracy: w zapytaniu do bazy danych PHP wykonuje pracę w ciągach ograni-
czonych cudzysłowami (traktując apostrofy dosłownie), natomiast MySQL przetwarza
następnie te wyrażenia z napisami w apostrofach.

Niestety musisz być bardzo ostrożny podczas pisania takich wyrażeń. Jest to jeden z po-
wodów, dla których zaleca się podział kwerend MySQL na dwie części, jak na przykład:
Squery = "INSERT INTO book (ID, title, year, ISBN)
VALUES ('NULL1, 'Stitle', '$year', '$ISBN')";
Sresult = mysql_query($query);

Styl taki eliminuje również podwójne nawiasy, które również są częstą przyczyną błę-
dów w kodzie PHP.

Jeszcze większe problemy pojawiają się, jeżeli pracujemy z ciągami, które zawierają
w tekście apostrofy lub cudzysłowy. Pamiętaj, że apostrofy i cudzysłowy są tym samym
dla PHP i MySQL — nie ma żadnej sprytnej opcji, która rozwiązuje te problemy. Za-
pytania wstawiające dane nie będą wykonywane, jeżeli jakiekolwiek nazwisko będzie
posiadało w sobie apostrof (np.: O'Hara):
$query = "INSERT INTO employee (ID, lastname, firstname)
VALUES ('NULL', 'Slastname', 'Sfirstname')";
Sresult = mysql_query($query) ;

Inną, równie częstą przyczyną tych problemów są nazwy firm z apostrofami w nazwie,
takie jak Rosalita's Bar oraz jakiekolwiek angielskie napisy zawierające skrótowce lub
zaimki dzierżawcze (jak na przykład can't, what's lub Mike's)'.

' Ponieważ w języku polskim nie występują tego typu konstrukcje, można być nieco spokojniejszym, ale już
witryna zawierająca recenzje filmowe nie jest bezpieczna.
404_______________________________________Część II » PHP i bazy danych

Podobnym zagadnieniem po stronie PHP są ciągi zawierające w sobie cudzysłowy. Na


przykład taka konstrukcja na pewno nie zadziała, jak było zakładane:
Sstring = "Spiker powiedział: "Pani 0'Hara jest proszona na salę".";
$wyr = mysql_query("INSERT INTO diary (ID, entry)
VALUES ('NULL', '$string')");

W przypadku bardzo długich ciągów problemy z cudzysłowami mogą


spowodować wstawienie tylko części ciągu lub akceptować tylko krót-
kie ciągi.

Zajmiemy się teraz trzema sposobami obsługi ciągów z apostrofami i cudzysłowami:


1. Gdy ciąg jest umieszczony bezpośrednio w kodzie, możesz poprzedzić odpo-
wiednie znaki backslashem.
Squery = "INSERT INTO employee (ID, lastname, firstname)
VALUES ('NULL', '0\'Donnell', 'Sean')";
$result = mysql_query(Squery);

2. Gdy ciąg jest reprezentowany przez zmienną, możesz użyć funkcji addsla-
shes ( ) , która automatycznie doda niezbędne znaki.
$string -
addslashes("Spiker powiedział: 'Pani O'Hara jest proszona na salę'.");
$wyr = mysql_query("INSERT INTO diary (ID, entry)
VALUES ('NULL', 'Sstring')");

3. Możesz skompilować PHP z opcją -with-magic-quotes lub ustawić ma-


gic-quotes w plikuphp.ini. Spowoduje to automatyczne dodawanie znaków
backslash bez potrzeby wykonywania funkcji addslashes ( ) . Jeżeli twój do-
stawca Internetu nie pozwala ci zmieniać zawartości pliku php.ini, możesz
ustawić tą zmienną przy użyciu swojego pliku .htaccess.

Z niezrozumiałych dla nas przyczyn wielu użytkowników PHP nie lubi używać za każ-
dym razem funkcji addslashes () i jej partnera, funkcji stripslashes (). Raczej
wikłają się w wymienianie cudzysłowów na apostrofy, gdy naprawdę nie muszą tego
robić, jeżeli oznaczą backslashami wszystkie cudzysłowy. To praktyka w złym stylu,
a jest szczególnie niebezpieczna, gdy korzysta się z bazy danych.

Musisz dodać znaki backslash, gdy umieszczasz dane w bazie danych, a usunąć je po
odczytaniu ciągu z bazy danych (chyba że masz włączoną opcję magic-quotes).
Squery = "SELECT passphrase FROM userinfo WHERE username='Susername'";
Sresult = mysql_query(Squery) ;
$query_row = mysql_fetch_array(Sresult );
Spassphrase = stripslashes(Squery_row[0]);

Jeżeli tego nie zrobisz, za każdym wstawieniem danych do MySQL będzie dodawane co-
raz więcej znaków backslash! Jest to dosyć częste w przypadku formularzy na stronach
WWW, które wyświetlaj ą dane pobrane z bazy danych, jak pokazano na rysunku 24.4.

J
Rozdział 24. » Pułapki tandemu PHP-baza danych 405

Rysunek 24.4.
Nieusunięte z danych
znaki backslash

Nieprawidłowe zapytania SQL


Oprócz problemów z apostrofami istnieje wiele możliwości wysłania „nieprawidłowe-
go" zapytania do bazy danych. Zapytanie takie może być nieprawidłowe składniowo
lub odwoływać się do nieistniejącej tabeli, mimo że jest składniowo poprawne. Typowy
komunikat pokazany jest na rysunku 24.5.

Rysunek 24.5.
Błąd powodowany
przez nieprawidłowe
zapytanie SQL

Nieprawidłowe zapytanie SQL nie jest tym samym, co zapytanie nie-


zwracające żadnych wierszy. Można napisać idealnie prawidłowe zapy-
tanie SQL, jak na przykład:
"select ID from cust where name ='nieistniejący'"
406______________________________________Część II * PHP i bazy danych

Jeżeli wyślesz je do bazy danych, w odpowiedzi dostaniesz całkowicie


prawidłowy wynik, który zawiera dokładnie O wierszy. Oznacza to, że
procedury obsługi błędów nie pomogą wykryć przypadku, gdy wynik
nie zwraca żadnych wierszy. Istnieje funkcja mysqi_num_rows o , któ-
ra wykonana na identyfikatorze wyniku zwraca liczbę wierszy.

To, w jaki sposób będzie wyglądał ten błąd w przeglądarce, zależy od wersji PHP, wer-
sji bazy danych, ustawień obsługi błędów oraz sposobu obsługi błędów w skrypcie.
Kluczem do sukcesu jest wczesne wykrycie awarii.

Najlepszym sposobem wykonywania zapytań MySQL jest:


$result_id = mysql_query(Squery) or die(mysql_error());

Ponieważ mysqi_query () zwraca wartość FALSE w przypadku niepowo-


dzenia, część die () będzie wykonana tylko w przypadku wystąpienia
błędu. Niski priorytet operatora 'or' zapewnia, że die o nie będzie miało
żadnego znaczenia w przypisaniu —jeżeli funkcja się powiedzie, to wyra-
żenie zachowuje się tak, jakby część die () nie istniała. Niepowodzenie
powoduje zakończenie skryptu natychmiast po wypisaniu komunikatu
o błędzie, zawierającego wszystkie dane, jakie mogli dostarczyć projek-
tanci MySQL. Jeżeli w twojej bazie danych nie ma funkcji informującej
o błędzie, możesz uprościć to wywołanie do die($query). Zwykle pro-
blem stanie się oczywisty, gdy zobaczysz wysłane właśnie zapytanie.

Jeżeli nie umieściłeś procedur obsługi błędów przy wywołaniach zapytań, dostaniesz
pierwsze złe wiadomości, gdy spróbujesz użyć identyfikatora wyniku zapytania w ko-
lejnych odwołaniach do bazy danych. Typowym schematem jest:
$my_result = mysql_query{$baa_query);
// ... instrukcje przetwarzania i wyświetlania
$row = mysql_fetch_row($my_result); // tu ujawnia się błędy

Typowym komunikatem w MySQL jest „O is not a mysql result identifier in ..." (O nie
jest prawidłowym identyfikatorem wyniku mysql). Dzieje się tak dlatego, że zamiast
wykryć wartość O zwracaną przez mysql_query ( ) w przypadku błędu, próbujesz użyć
tej wartości jako prawidłowego identyfikatora wyniku.

Mimo że nieprawidłowe zapytania są najczęstszą przyczyną powsta-


wania błędu „O nie jest prawidłowym identyfikatorem wyniku", nie jest
to jedyna możliwość. Możesz otrzymać ten komunikat, gdy nieprawi-
dłowo podasz nazwę zmiennej identyfikatora wyniku (dlatego nie jest
zainicjowana) lub jeżeli zapytanie nie zostanie w ogóle wykonane (co
daje ten sam wynik), l tym razem dużo łatwiej jest wykryć te problemy,
jeżeli przechwycisz błąd możliwie najwcześniej.
Rozdział 24. » Pułapki tandemu PHP-baza danych__________________________407

Pomyłki w nazwach
Niestety, oprócz skomplikowanych błędów, tkwiących głęboko w środku programu,
istnieje mnóstwo banalnych pomyłek, które jednak bardzo łatwo usunąć.

Rozpocznijmy od najczęstszego błędu: podania nieprawidłowej nazwy tabeli, kolumny


lub nazwy zmiennej. Nie pomoże analiza wielkości liter w PHP ani MySQL. Jeżeli
użyjesz 'mytable' zamiast 'MyTable', możesz spodziewać się błędu. Żadna siła nie
zabezpieczy cię przed przypadkowym popełnieniem takiego błędu i jedynie jasne ko-
munikaty mogą pomóc. Co możemy dodać? Pamiętaj, że zdarza się to nawet doświad-
czonym programistom.

Pomyłki przy przecinkach


Pamiętaj, aby w wyrażeniach SQL zawsze umieszczać przecinki poza apostrofami. Coś
takiego nie będzie działać:
Squery = "UPDATE book SET title='Stitle,' subtitile='Ssubtitle,'
isbn='$isbn'

Natomiast takie wyrażenie zadziała świetnie:


Squery = "UPDATE book SET title='Stitle', subtitile-'$subtitle',
isbn='$isbn'";

Ciągi nieotoczone apostrofami


Każdą wartość, która powinna być traktowana przez bazę danych jako ciąg, trzeba oto-
czyć apostrofami w wyrażeniu SQL. Na przykład zapytanie to ma właściwą składnię:
$query =• "select * from author where firstname = 'Daniel'";

Dla odróżnienia, jeżeli wykonamy funkcję mysql_query ( ) z użyciem poniższego cią-


gu, możemy spodziewać się błędu:
Squery = "select * from author where firstname = Daniel";

Komunikat o błędzie zwracany przez bazę danych może być mylący — komunikat ten
mówi o nieznanej kolumnie 'Daniel'. Dzieje się tak, ponieważ ciągi nieotoczone apo-
strofami traktowane sąjako nazwy kolumn, jak w przykładzie:
$query - "select * from author where firstname = lastname";

Zapytanie takie może być użyte do wyszukiwania takich autorów, jak na przykład
„Lisa Lisa".

Niezainicjowane zmienne
Jednym ze sposobów zepsucia zapytania jest umieszczenie w nim niezainicjowanej
zmiennej.
408_______________________________________Część II » PHP i bazy danych

System automatycznego wklejania wartości zmiennych do ciągów w cudzysłowach ideal-


nie pasuje do opartego na SQL dialogu z bazą danych, o ile działa poprawnie. W twoim
kodzie można określać wartości, które na przykład mogą być użyte do ograniczania liczby
wierszy pobieranych z bazy danych, w sposób pokazany poniżej:
$customerID = find_customer_id(); // funkcja przykładowa, zwraca int
$result_id = // BŁĄD
mysql_query("select from customers where ID = 3customer_ID");
$row = mysql_fetch_row($result_id); // AWARIA

Ponieważ kod ten nie próbuje przechwycić błędu, znowu zobaczysz komunikat o tym,
że O nie jest prawidłowym identyfikatorem wyniku. Problem leży oczywiście w tym, że
wprawdzie przypisaliśmy wartość do zmiennej ($customerlD), ale użyliśmy w zapy-
taniu zupełnie innej ($customer__iD). Zmienna ta jest niezainicjowana, więc w czasie
interpretowania ciągu zachowuje się jak pusty ciąg. W wyniku tego baza danych otrzy-
ma następujące zapytanie, które oczywiście jest nieprawidłowe:
select from customers where ID =;

Ta kategoria problemów jest kolejnym powodem, dla których warto przypisywać do


zmiennych stworzone zapytania, jak na przykład:
$my_query = "select from customers where ID - $customer_ID";

Po takim przypisaniu wykonujemy osobne wywołanie mysql_query($my_query).


Jeżeli pracujesz w ten sposób, bardzo łatwo możesz uruchomić drukowanie lub dodać
instrukcje debuggera w celu wyświetlenia aktualnej postaci zapytania, wysyłanego do
bazy danych.

Zbyt mało danych, zbyt dużo danych


Na koniec zajmiemy się sytuacją, gdy twój skrypt PHP działa pozornie bezbłędnie, ale
nie wyświetla danych lub wyświetla o wiele więcej, niż powinien. Jako zasadę powi-
nieneś przyjąć, że jeżeli funkcja wykonująca zapytanie nie zwraca żadnych błędów
(a odpowiedni kod to sprawdza), podejrzenie powinno paść na samo wyrażenie SQL.
Sprawdź ponownie logikę, szczególnie klauzulę WHERE. Łatwo można na przykład na-
pisać takie zapytanie:
"select * from families where kidcount = 1 and kidcount = 2";

W tym zapytaniu tak naprawdę chciałeś użyć operatora 'or' a nie 'and', a błąd ten spo-
wodował, że zapytanie nie zwróci nigdy żadnych wierszy, niezależnie od zawartości
bazy danych.

Jeżeli skrypt przegląda w pętli wiersze tabeli i wyświetla ich o wiele zbyt dużo, problem
często leży w tym, że zapytanie ma zbyt mało połączeń. Naczelną zasadą jest, że liczba wa-
runków w klauzuli WHERE nie powinna być mniejsza od liczby łączonych tabel minus je-
den. Zapytanie poniższe posiada np. trzy tabele, ale ma tylko jeden warunek złączenia.
"select book.title, from book, author, country
where author.countrylD - country.ID";
Rozdział 24. » Pułapki tandemu PHP-baza danych__________________________409

Zapytanie to zwróci wszystkie możliwe pary książka — autor, niezależnie od tego, czy
autor napisał tę książkę, czy nie, co nie jest prawdopodobnie oczekiwanym wynikiem.

Kontrola poprawności
Jeżeli kończą ci się pomysły na szukanie błędów związanych z zapytaniami, użyteczne
jest porównywanie wyników zapytań osadzonych w PHP z tymi samymi zapytaniami
wykonanymi bezpośrednio na bazie danych. Jeżeli masz możliwość bezpośredniego
uruchomienia interpretera SQL (na przykład pisząc w wierszu poleceń mysql) oraz
przenoszenia danych pomiędzy programami, możesz wypróbować taki sposób:
1. Wstaw wyrażenie uruchomieniowe do skryptu PHP, które drukuje treść zapy-
tania bezpośrednio przed wykonaniem go w bazie danych (na przykład:
print ($query) ;).
2. Wstaw treść zapytania z przeglądarki (lub źródła HTML) do twojego interpre-
tera SQL.

Jeżeli zapytanie wygląda dobrze, ale powoduje błąd SQL i PHP, musi być w nim jakiś
błąd składniowy lub w nazwie; kod PHP w tym przypadku nic nie zawinił (chyba że
konstruuje to zapytanie). Podobnie w przypadku zbyt małej lub zbyt dużej liczby wier-
szy wyniku —jeżeli zachowanie jest identyczne w obu programach, winne jest zapytanie.
Jeżeli zaś zapytanie zachowuje się w interpreterze SQL tak, jak się tego spodziewasz,
jest prawidłowe, a podejrzenie pada na kod PHP, wysyłający zapytanie i przetwarzający
wynik.

Powinieneś bardzo uważnie czytać komunikaty o błędach, zwracając szczególnie uwagę


na określenia takie, jak „identyfikator połączenia" oraz „identyfikator wyniku". W My-
SQL pierwsze oznacza identyfikator połączenia z bazą danych, drugie — identyfikator
zestawu wierszy zwracanych przez określone zapytanie. Łatwo pomylić je ze sobą (po-
niższy fragment kodu):
$my_connect = mysql_connect('localhost', $myname, $mypass);
mysql_select_db('MyDB');
Sresult = mysql_query($my_query, Smy_connect);
while (Srow = mysql_fetch_row($my_connect)) { // ŹLE

Kod ten prawdopodobnie wygeneruje błąd zawierający zwrot „nieprawidłowy identyfi-


kator wyniku". Problem tkwi w tym, że użyliśmy identyfikatora połączenia w miejsce
identyfikatora wyniku. Wygenerowany komunikat jest uzasadniony, choć nieco niejasny.

Podsumowanie
Błędy PHP w połączeniu z bazą danych nie są zwykle zbyt głębokie, ale mogą być nie-
łatwo je odszukać. Ogólnie mówiąc, im wcześniej błąd zostanie wykryty w skrypcie,
410_______________________________________Część II » PHP i bazy danych

tym łatwiejsze będzie jego zlokalizowanie. Każde wyrażenie współpracujące z bazą da-
nych powinno posiadać dołączoną klauzulę or die ( ) , zawierającą niosący możliwie
dużo informacji komunikat o błędzie, szczególnie w trakcie uruchamiania.

Jak dotąd najczęstszym powodem problemów z połączeniem z bazą danych są niepra-


widłowe wartości argumentów przekazywanych do funkcji łączącej z bazą (nazwa
komputera, nazwa użytkownika i hasło). Najczęstszym powodem błędów przy wykona-
niu zapytań są problemy z apostrofami, niezainicjowane zmienne i proste pomyłki.

Jeżeli powtarzają się problemy z zapytaniami, na pozór zupełnie prawidłowymi, musisz


wydrukować treść podejrzanego zapytania przed wysłaniem do bazy danych. Jeżeli to
możliwe, wykonaj je bezpośrednio na bazie danych. Jeżeli problem się powtórzy, mo-
żesz bezpiecznie skupić się na szukaniu błędu w strukturze bazy danych i twoim rozu-
mieniu zapytań SQL.
Cześć 3
Techniki zaawansowane
Rozdział 25.
Sesje
W tym rozdziale:
* Do czego służą sesje
* Śledzenie sesji w PHP
+ Przykładowy program korzystający z sesji
** Ustawianie zmiennych wspomagających sesje

W innych częściach tej książki spotykałeś się czasami z ostrzeżeniem „nowa funkcja",
które zwykle oznaczało możliwość wprowadzoną w PHP 4.0. Niniejszy rozdział powinien
być w ten sposób oznaczony, ponieważ w PHP 3.x nie było wbudowanego mechanizmu
śledzenia sesji. Jeżeli chcesz korzystać z sesji użytkownika, musisz zainstalować PHP 4,
zastosować popularną bibliotekę PHPLIB lub stworzyć własne rozwiązanie.

Czym są sesje?
Co mamy na myśli, mówiąc o sesjach? Nieformalnie, sesja w przypadku przeglądania
WWW to czas, w którym dana osoba, siedząc przy jednym komputerze, przegląda okre-
śloną liczbę stron, następnie kończy ten proces. Jeżeli prowadzisz witrynę WWW, którą
przykładowy użytkownik właśnie przegląda, sesja biegnie od czasu załadowania pierw-
szej strony, aż do ostatniej. Na przykład witryna hotelu na Karaibach może korzystać
z sesji na czas przeglądania pięciu stron, ale użytkownik naprawdę rozpoczął sesję
w portalu zajmującym się podróżami, a zakończył ją w czasie rezerwacji miejsca w ho- ..
telu konkurencji. u

Co stanowi problem?
Dlaczego więc idea sesji jest na tyle skomplikowana, że zajmujemy się nią dopiero te-
raz? Jest tak dlatego, że protokół HTTP jest bezstanowy. Powoduje to, że twój serwer
WWW ma mniej pamięci długoterminowej niż twój kod. W rezultacie serwer WWW
414___________________________________Część III » Techniki zaawansowane

odpowiada na poszczególne żądania, jakie otrzymuje, i nie ma sposobu na ich połącze-


nie, nawet jeżeli żądania te są zapisywane. Jeżeli siądę przy moim komputerze w Chi-
cago, a ty przy swoim, i oboje załadujemy kolejno pierwszą i drugą stronę z witryny
hotelu na Karaibach, protokół HTTP nie zaoferuje żadnej pomocy przy rozpoznaniu, że
tych dwoje ludzi oglądało dwie kolejne strony. Od strony serwera były to cztery żądania
załadowania stron, z dodatkowymi danymi związanymi z każdym wywołaniem. Nie
tylko taka informacja nie identyfikuje cię (za pomocą nazwiska, adresu e-mail, numeru
telefonu lub innego identyfikatora), ale również nie oferuje nic, co może zidentyfikować
żądania kolejnych stron jako pochodzące od jednej osoby.

Dlaczego się tym zajmujemy?


Jeżeli twoja witryna jest przeznaczona tylko do dostarczania rozmaitych stron różnym
użytkownikom, to w zasadzie nie musisz wiedzieć, kiedy sesja się rozpoczyna i kiedy
kończy. Jednak istnieje wiele sytuacji, gdy chcemy to wiedzieć.
* Chcemy dostosowywać się do doświadczenia użytkowników, które zależy od
tego, jakie strony (lub ile) już widzieli.
* Chcemy wyświetlać reklamy, ale nie chcemy wyświetlać jednej reklamy wię-
cej niż raz w czasie sesji.
* Chcemy zbierać informacje o podejmowanych przez użytkownika działaniach
(tak jak w grach przygodowych realizuje się pamiętanie punktów lub w witry-
nach handlu elektronicznego — wózki na zakupy).
* Interesuje nas sposób, w jaki ludzie korzystają z witryny — czy przechodzą do
wewnętrznych stron korzystając z zakładek, czy przechodzą całą drogę od
strony głównej.

Dla wszystkich zastosowań powinniśmy skorelować żądania załadowania stron z kolej-


nymi częściami sesji; dla niektórych zastosowań wygodnie byłoby zapamiętywać nie-
które dane w połączeniu z sesją. Sesje PHP rozwiązują oba te problemy.

Alternatywy sesji
Zanim zajmiemy się podejściem PHP do sesji, wymienimy kilka alternatywnych sposo-
bów rozwiązania problemu. Jak się później przekonamy, podejście PHP stanowi kom-
binację kilku z tych technik.

Adres IP
Serwery WWW znają albo nazwę, albo adres IP komputera, z którego przyszło żądanie
załadowania strony. W większości konfiguracji PHP są one dostępne w zmiennych, od-
powiednio $REMOTE_HOST i $REMOTE_NAME. Wydaje się, że identyfikacja komputera
Rozdział 25. » Sesje_________________________________________415

jest jednoznaczna z użytkownikiem, co najmniej w krótkim czasie. Jeżeli w krótkim


okresie dostaniesz dwa żądania z tego samego adresu IP, kod może stwierdzić, że zo-
stały one wysłane przez tę samą osobę, która przeszła z jednej strony witryny na drugą.

Niestety adres IP twojej przeglądarki nie musi do komputera, z którego korzysta użyt-
kownik. Zarówno AOL, jak również inni dostawcy stosują serwery proxy, które działają
jako pośrednicy. Jeżeli przeglądarka zażąda załadowania URL, wysyła on to żądanie do
docelowego serwera i zwraca stronę użytkownikowi, taka konfiguracja powoduje, że
wielu użytkowników może jednocześnie przeglądać twoją witrynę pozornie z jednego
adresu IP. Adresy IP nie są więc wystarczająco jednoznaczne, aby stanowić podstawę
do śledzenia sesji.

Ukryte zmienne
Każde wywołanie HTTP jest obsługiwane niezależnie, ale za każdym razem gdy użyt-
kownik przejdzie do innej strony witryny, jest zwykle realizowane przez łącze lub
przetworzenie formularza. Jeżeli pierwsza strona, którą użytkownik odwiedza, może
wygenerować identyfikator tej wizyty, następne przejścia ze strony do strony mogą go
przenosić.

Poniższy przykładowy fragment kodu można zamieścić na początku strony:


if ( UsSet ($my_s_id) )
$my__s_id = generate_s_id () ; // to jest hipotetyczna f u n k c j a

Ten fragment kodu kontroluje, czy zmienna $my_s_id została wcześniej zainicjowana.
Jeżeli tak, zakładamy, że została przekazana do bieżącej strony i jesteśmy w środku se-
sji. Jeżeli nie, oznacza to, że jesteśmy na pierwszej stronie nowej sesji i wywołujemy
hipotetyczną funkcję, nazwaną generate_s_id ( ) , w celu utworzenia identyfikatora
(jest to trudne zadanie).

Założyliśmy, że po włączeniu poprzedniego kodu identyfikator sesji i pozostaje nam je-


dynie przekazać go do wszystkich połączonych stron. Każde łącze z naszej strony po-
winno zawierać $my_s_idjako argument GET:
<A HREF="next.php?my_s_id-<?php echo $my_s_id;?>">Następny</A>

Każdy formularz powinien zawierać ukryty argument POST:


<FORM ACTION=next.php METHOD=POST>
ciało formularza
<INPUT TYPE=HIDDEN NAME=my_s_id VALUE="<?php echo $my_s_id;?>">
</FORM>

Ta technika jest prostym sposobem odróżniania sesji (dopóki możesz generować identy-
fikatory). Jeśli mamy identyfikatory sesji, możemy zastosować wiele sposobów dołą-
czenia danych do każdej sesji. Jednym z nich jest użycie identyfikatora sesji jako klucza
tabeli bazy danych. Jednak to wyjście jest kłopotliwe — musisz upewnić się, że każde
łącze i obsługa formularza prawidłowo przenoszą opisane informacje. Jeżeli przesyłasz
informacje jako argument GET, maszyna śledzenia sesji będzie widoczna w pasku adresu
przeglądarki — argument może być zmieniony lub przechwycony przez użytkownika.
416________________________________Część III » Techniki zaawansowane

Cookie
Innym sposobem śledzenia sesji jest użycie takiego samego identyfikatora sesji, jak opi-
saliśmy przed chwilą, ale do jego przekazywania pomiędzy stronami stosuje się cookie.

Cookie jest szczególnego rodzaju plikiem, umieszczonym w komputerze z przeglądar-


ką, który może być zapisywany i odczytywany przez serwery WWW. Zamiast spraw-
dzać zmienną przekazaną za pomocą metody GET lub POST (i przypisywać nową, jeżeli
nie została odnaleziona), skrypt może sprawdzić, czy w komputerze klienta istnieje już
poprzednio utworzone cookie i zapisać do niego nowy identyfikator, jeżeli nie został
odnaleziony. Metoda ta posiada pewne zalety w porównaniu do ukrytych zmiennych:
mechanizm ten działa w sposób niewidoczny (nie pozostawia zwykle w przeglądarce
śladów aktywności), a kod, który sprawdza i ustawia cookie, może być scentralizowany
(zamiast występować w każdym łączu).

Jakie są wady? Niektóre stare przeglądarki w ogóle nie obsługują cookie, a nowe po-
zwalają użytkownikowi na zablokowanie możliwości tworzenia cookie przez serwery
WWW. Mimo że cookie dostarczają dobre rozwiązanie problemu, nie możemy zakła-
dać, że będą zawsze dostępne.

Istnieje subtelna różnica pomiędzy mechanizmem sesji realizowanym


za pomocą cookie i tym realizowanym za pomocą zmiennych GET i POST.
System na podstawie zmiennych utrzymuje identyfikatory tak długo,
jak użytkownik pozostaje w twojej witrynie, przechodząc pomiędzy
wewnętrznymi stronami. Istnieje jednak wiele sytuacji, kiedy użytkow-
nik przejdzie do innej witryny i za chwilę wróci z powrotem. Podejście
na bazie cookie potraktuje powrót z krótkiej wycieczki jako kontynu-
ację tej samej sesji, natomiast mechanizm przekazywania zmiennej
potraktuje ją jako nową wizytę.

Funkcje ustawiania i korzystania z cookie opisane są w rozdziale 26.


„Cookie i HTTP". Zapoznaj się również z uwagami, zawartymi w tym
rozdziale, na temat poszanowania prywatności. Nie umieszczaj również
w cookie żadnych ważnych informacji, zanim przeczytasz rozdział 31.
„Bezpieczeństwo i kryptografia".

Jak działają sesje w PHP


Dobrze działające sesje zajmują się dwiema rzeczami: śledzeniem sesji (to znaczy wy-
krywaniem, kiedy dwa osobne wywołania skryptów są częścią tej samej sesji) oraz za-
pamiętywaniem danych związanych z sesją. Musimy mieć pierwszą możliwość, żeby
w ogóle myśleć o drugiej.
Rozdział 25. » Sesje____________________________________________417

Zanim dodano w PHP 4 obsługę sesji, głównym zamiennikiem sesji


był pakiet PHPLIB. Obsługa sesji w PHPLIB jest koncepcyjnie podob-
na, ale różni się szczegółami. PHPLIB jest dostępny pod adresem
http://phplib.netuse.de (w czasie pisania tej książki PHPLIB nie był
zgodny z PHP 4, ale to na pewno zostało już poprawione).

Sesje PHP działają na bazie kombinacji metody ukrytych zmiennych i cookie. Dla zalet
cookie PHP będzie używał tej metody, jeżeli jest dostępna; w przypadku braku obsługi
cookie skorzysta ze zmiennych. Na szczęście funkcje obsługi sesji działają na bardziej
abstrakcyjnym poziomie i same troszczą się o obsługę cookie i inne szczegóły. Jeżeli
twoja wersja PHP 4 została odpowiednio skonfigurowana do obsługi sesji, możesz użyć
sesji bez martwienia się o to, jaka metoda została zastosowana.

Jeżeli chcesz, aby PHP przezroczyście obsługiwał przekazywanie zmien-


nych sesji w przypadku niedostępności cookie, musisz skonfigurować
PHP Z Użyciem opcji —enable-trans-sid i —enable-track-vars.
Jeżeli PHP nie zrobi tego za ciebie, musisz dodać do argumentów GET
lub POST argument session_name=session_id do wszystkich łączy
i formularzy. Gdy sesja jest aktywna, PHP dostarcza specjalną stałą
SID, która zawiera odpowiednią parę argument i wartość. Tutaj poda-
jemy przykład dodania tej stałej do łącza: <A HREF= "strona?<?php
echo(SID);?>">Następna strona</A>.

Jeśli cookies nie są dostępne, automatyczne przekazywanie identyfi-


katora (o ile jest aktywne) przenosi identyfikator sesji metodą GET dla
łączy oraz metodą POST dla formularzy. Jednak nie przekazuje tej in-
formacji przez formularze GET. W tym przypadku musimy jawnie prze-
kazać identyfikator sesji w ukrytej zmiennej (zostało to zauważone
w wersji beta i pewnie będzie poprawione w kolejnych wersjach).

Jeżeli PHP śledzi sesje, można również zachować dane przez „rejestrowanie" określo-
nych zmiennych, w celu wskazania, że ich wartości mają być zapamiętane w połączeniu
z identyfikatorem sesji.

Uaktywnianie sesji w PHP


Pierwszym krokiem skryptu, korzystającego z sesji, jest poinstruowanie PHP, że sesja
może już trwać, więc trzeba j ą przechwycić i odczytać wszystkie związane z nią dane.
Realizuje się to przez wywołanie bezargumentowej funkcji session_start (). Jeżeli
jest ona wywoływana w każdym skrypcie, możesz j ą pominąć i w zamian za to ustawić
w php.ini zmienną session.auto_start na l (jest ona zwykle ustawiona na 0).
Również każde wywołanie funkcji session_register ( ) spowoduje niejawne wy-
wołanie session s t a r t { ) .
418________________________________Część III » Techniki zaawansowane

Wynik działania session_start ( ) zależy od tego, czy PHP odszukał poprzedni


identyfikator sesji, przekazany przez argumenty HTTP lub cookie. Jeżeli został znale-
ziony, poprzednio związane z nim zmienne sesji zostaną odczytane i zamienione na
zwykłe zmienne dostępne na stronie. Jeżeli np. poprzednia strona w sesji zarejestrowała
zmienną $city i przypisała jej wartość 'Chicago', nasz skrypt może z niej skorzystać,
wywołując session_start ( ) :
session_start() ;
print! "$city<BR>" );

Jeżeli poprzednio nie została zarejestrowana taka zmienna lub zapomnieliśmy wywołać
session_start (), zmienna będzie traktowana jak każda niezainicjowana zmienna.

Odczytanie zmiennych sesji przez wywołanie session_start o spo-


woduje nadpisanie wszystkich zainicjowanych zmiennych o takich
samych nazwach, jakie istnieją w chwili wywołania funkcji w skrypcie.
Oznacza to, że jeżeli zmienne przekazane są za pomocą GET i POST
oraz mechanizmem obsługi sesji na bazie cookie, zmienne sesji „wy-
grają" w tym pojedynku (więcej o kolejności przypisywania zmiennych
GET, POST i cookie w następnym rozdziale).

Jeżeli PHP nie znajdzie poprzedniego identyfikatora sesji, wywołanie session_


start () utworzy nową sesję. Podstawowym efektem jest utworzenie nowego identyfi-
katora, który może być używany do rejestrowania zmiennych.

Rejestrowanie zmiennych w sesji


Wywołanie session_start () zajmuje się importowaniem zmiennych z sesji do bie-
żącego skryptu — teraz wyeksportujemy zmienne, aby były widoczne w kolejnych
stronach w tej samej sesji. Jest to realizowane przez funkcję session_register ( ) .
Jak się okazuje, importowanie zmiennych jest „hurtowe" (robimy to jednym wywoła-
niem funkcji), natomiast eksport jest „detaliczny" (musimy rejestrować kolejno wszyst-
kie zmienne). Należy pamiętać, że to, co zostało zaimportowane, nie jest automatycznie
eksportowane, choć tak wskazuje logika.

Załóżmy, że poprzednia strona ustawiła zmienną $city na 'Chicago'. Teraz skorzy-


stamy z tej zmiennej i przekażemy j ą (po zmianie wartości) do kolejnych stron:
session_start();
print( "$city<BR>" );
session_register('city1);
print! "$city<BR>" );
$city = 'Dallas';

Wywołanie session_start () ustawia zmienną $city na 'Chicago', więc można


użyć tej zmiennej w instrukcji print. Od tego miejsca $city będzie traktowane jak
każda inna zmienna na stronie. Wywołanie session_register ( ) umieszcza jednak
w mechanizmie sesji informację o tym, że zmienna $city ma być ponownie wyeks-
Rozdział 25. » Sesje____________________________________________419

portowana do zmiennych sesji. Na kolejnych stronach wartość jej powinna być taka sa-
ma, jak po zakończeniu tego skryptu. W naszym kodzie wartość tej zmiennej zmieniła
się na 'Dallas', więc następne strony otrzymają taką właśnie wartość zmiennej.

Nazwy zmiennych przekazywane do funkcji session_register o nie


powinny zawierać poprzedzającego je znaku '$'.

Jeżeli zapomnimy wywołać funkcję session_start O, pierwszy wiersz będzie za-


wierał puste miejsca tam, gdzie powinna być wartość zmiennej $city. Drugi drukowa-
ny wiersz będzie już wyglądał dobrze, ponieważ wywołanie session_register ( )
powoduje niejawne wywołanie session_start ( ) . Jeżeli opuścimy wywołanie ses-
sion_register ( ) , na tej stronie wszystko będzie w porządku, ale kolejne strony nie
otrzymają zmiennej sesji o nazwie $city.

Rejestracja zmiennej jest całkowicie niezależna od przypisywania do


niej wartości. Możesz przypisać wartość do zmiennej bez jej rejestro-
wania (dlatego będzie normalną zmienną strony, która nie jest prze-
noszona do innych stron) lub możesz zarejestrować zmienną bez
przypisywania do niej wartości (będzie ona występowała w kolejnych
stronach jako nieprzypisana).

Rejestrowane zmienne są super globalne


Użytecznym sposobem myślenia o zmiennych sesji jest uważanie ich za deklarowane
super globalnie. Zmienne występujące we wnętrzu definicji funkcji posiadają zasięg
ograniczony tylko do tej funkcji, chyba że zostaną zadeklarowane jako globalne. Po-
dobnie zwykłe zmienne mają zasięg ograniczony do skryptu, chyba że zarejestrujemy je
jako (super globalne) zmienne sesji.

Gdzie są przechowywane dane?


Mechanizm sesji bazuje na identyfikatorze sesji i związanych z nim wartości zmiennych.
Jak już wiemy, identyfikator sesji jest przechowywany w cookie w komputerze z prze-
glądarką lub jest wbudowywany w argumenty GET, POST, przekazywane razem z żąda-
niem pobrania strony. Nie jest nigdzie zapamiętywany — przekazywany jest jako część
żądania i zwracany do kodu HTML, do łączy i formularzy, które mogą wygenerować
kolejne żądanie. Przeglądarka i serwer przerzucają się tą krytyczną dla nas informacją
jak gorącym kartoflem. Jeżeli któraś ze stron zgubi identyfikator, nasza sesja się kończy.

Domyślnie zawartość zmiennych sesji jest przechowywana w specjalnych plikach na ser-


werze, po jednym dla każdej sesji (można przechowywć identyfikator sesji jako cookie
w komputerze klienta, ale byłoby grubiaństwem przechowywanie wartości zmiennych se-
sji na jego dysku). Zapamiętanie danych w ten sposób wymaga kodu sesji, realizującego
serializację danych. Termin ten oznacza zamianę danych w liniową sekwencję bajtów,
którą można zapisywać na dysku i odczytywać z niego w celu odtworzenia danych.
420________________________________Część III » Techniki zaawansowane

Mimo że w teorii możemy zarejestrować każdą zmienną, zawierającą do-


wolny typ PHP, jednak w PHP 4 beta ciągle nie było pewnej obsługi seria-
lizacji obiektów. Ten rodzaj obsługi może pojawić się w wersji finalnej
bądź kolejnych, ale powinieneś sprawdzić kod rejestrujący zmienne
obiektowe, aby się upewnić, czy są one prawidłowo odtwarzane.

Możliwe jest takie skonfigurowanie PHP, aby program przechowywał zmienne sesji
w bazie danych zamiast w plikach. Więcej informacji znajdziesz w części „Zagadnienia
konfiguracji" poniżej.

Funkcje obsługi sesji


Tabela 25.1 zawiera wszystkie najbardziej istotne funkcje związane z obsługą sesji,
wraz z opisem ich działania. Zauważ, że w niektórych przypadkach działanie funkcji
zależy od ustawień konfiguracji, które zostały opisane w dalszej części rozdziału.

Tabela 25.1.
Zestawienie funkcji do obsługi sesji

Funkcja Opis
session start ( ) Nie wymaga argumentu. Powoduje, że PHP odczytuje identyfikator sesji
przekazany do strony (dzięki cookie lub GET, POST); jeżeli nie może go
odszukać, tworzy nowy identyfikator sesji
Jeżeli odnaleziony został stary identyfikator sesji, PHP odtwarza
przypisania do wszystkich zarejestrowanych zmiennych i udostępnia te
wartości jako zwykłe zmienne globalne
session r e g i s t e r ! ) Wymaga ciągu jako argumentu i rejestruje zmienną o nazwie określonej
przez ciąg — na przykład session register ( ' username ' ) . Nazwa
zmiennej nie powinna zawierać początkowego znaku '$'. Można również
przekazać tablicę ciągów, aby za jednym razem zarejestrować wiele
zmiennych
Efektem rejestracji jest zapamiętanie przypisań do obiektu (po
zakończeniu skryptu zarejestrowane zmienne podlegają serializacji i są
przenoszone w sposób, który zapewnia ich odtworzenie w wywołaniu
session s t a r t ( ) )

Jeżeli nie wywołano wcześniej funkcji session start ( ) ,


session r e g i s t e r ) ) niejawnie ją wywołuje
session u n r e g i s t e r ( ) Wymaga ciągu jako argumentu i usuwa z listy zarejestrowanych
zmiennych zmienną o nazwie przekazanej jako argument. W wyniku tego
zmienna ta nie będzie podlegała serializacji i nie będzie przenoszona
pomiędzy stronami (nazwa zmiennej nie powinna zawierać początkowego
znaku '$')
Rozdział 25. » Sesje____________________________________________421

Tabela 25.1
Zestawienie funkcji do obsługi sesji (ciąg dalszy)

Funkcja Opis
session is r e g i s t e r e d ( ) Sprawdza, czy zmienna o podanej nazwie jest zarejestrowana w bieżącej
sesji, zwracając TRUE, jeżeli zmienna jest zarejestrowana, FALSE, jeżeli
nie jest
session d e s t r o y ! ) Wywołanie tej funkcji usuwa wszystkie zapamiętane dane o sesji
(identyfikator sesji nie musi się zmieniać po wywołaniu tej funkcji).
Wywołanie tej funkcji nie zmienia wartości żadnych zmiennych
w bieżącym skrypcie
session name ( ) Funkcja wywołana bez parametrów zwraca nazwę bieżącej sesji. Jest to
zwykle ' P H P S E S S I D 1
Wywołana z jednym argumentem session name ( ) ustawia nazwę
bieżącej sesji na podany ciąg. Nazwa tajest używana jako klucz do
odszukania identyfikatora sesji w cookie lub argumentach GET, POST. Aby
odnalezienie sesji się udało, nazwa sesji musi być identyczna jak nazwa
sesji, w trakcie której poddano zmienne serializacji. Zauważ, że nie ma
powodu do zmiany nazwy sesji, chyba że chcesz rozróżniać różne typy
sesji obsługiwanych jednocześnie przez serwer WWW (na przykład
w przypadku wielu witryn korzystających z sesji). Nazwa sesji jest
zmieniana na domyślną za każdym uruchomieniem skryptu, więc zmiana
nazwy sesji musi zostać wykonana w każdym skrypcie przed wywołaniem
innej funkcji obsługi sesji
session module name ( ) Jeżeli nie zostaną podane żadne argumenty, funkcja zwraca nazwę modułu
odpowiedzialnego za obsługę danych sesji. Nazwa tajest domyślnie
ustawiona na ' f ileś ' , co oznacza, że dane sesji po serializacji są
zapisywane do plików w katalogu ustawionym przez funkcję
session save path ( ) . Jeżeli podany zostanie ciąg jako argument,
nazwa modułu jest zmieniana na ten ciąg (np. ' u s e r ' dla zdefiniowanej
przez użytkownika bazy danych sesji; ale nie powinieneś zmieniać tego
parametru, jeżeli nie wiesz dokładnie, co robisz)
session save path ( ) Zwraca (lub ustawia w przypadku podania argumentu) ścieżkę do
katalogu, w którym zapisywane są wartości zmiennych sesji (zwykle jest
to /tmp w systemach Unix). Katalog ten musi mieć odpowiednio
ustawione uprawnienia, aby PHP mógł tam zapisywać pliki
session i d ( ) Nie ma argumentów. Zwraca ciąg, który jest kluczem określającym sesję.
Jeżeli zostanie przekazany argument, funkcja ta ustawi identyfikator sesji
na tę wartość
session encode ( ) Zwraca ciąg z zakodowanym stanem bieżącej sesji, który może zostać
zdekodowany przez funkcję session decode ( ) . Ciąg ten może zostać
użyty do zapamiętania sesji w celu odtworzenia jej za jakiś czas
(np. zapisanie zakodowanego ciągu w pliku lub bazie danych)
session d e c o d e ! ) Pobiera wynik działania funkcji session encoded i odtwarza stan
sesji, zmieniając zmienne sesji na zmienne strony (analogicznie do
session start ( ) )
422___________________________________Część III » Techniki zaawansowane

Przykładowy kod sesji


Kod zamieszczony na wydruku 25.1 posiada podwójne przeznaczenie: pokazuje niektóre
funkcje obsługi sesji w działaniu (szczególnie session_start (), session_regis-
ter ( ) , session_id ( ) oraz session_destroy ( ) ) , można go użyć do testowania wła-
snych implementacji sesji. Skrypt ten zawiera jedną stronę, która ma łącze do samej siebie,
oraz formularz przetwarzany przez ten sam skrypt.

Wydruk 25.1. Skrypt testujący sesje______________________________________


<?php
session_start{);
session_register('pagecount');
session_register('username'); »
if {IsSet(Spagecount))
$pagecount++;
else
$pagecount = 1;
$pagecount_limit = 5;
?>
<HTML><HEADXTITLE>Strona testowania ses j i</TITLE>
</HEADXBODY>
<H3>Testowanie sesji</H3>
Identyfikatorem sesji jest <?php print (session_id (}); ?XBR>
Może istnieć więcej innych identyfikatorów sesji, ale ten jest twój.

<P>Nie zapisuj bieżącego identyfikatora sesji. Jest on tak naprawdę tylko


identyfikatorem technicznym. PHP potrzebuje go do działania, ale ty
prawdopodobnie nie.<BR>
<?php
if (IsSet($posted_username))
$username = $posted_username;
if (IsSet($username))
{
print("<P>Znamy twoje imię! To $username<BR>");
}
?>
<P>Liczba stron odwiedzonych w tej sesji: <?php print($pagecount);?>
<BR>
<?php if {Spagecount == 1)
print("Musiałeś dopiero zajrzeć!<BR>"); ?>
Możesz obejrzeć tylko <?php print($pagecount_limit);?>
stron w czasie sesji.<BR>

<P>To jest łącze do


<A HREF="<?php echo $PHP_SELF;?>">tej samej strony</A>.<BR>
Przejście tutaj zwiększa liczbę stron <BR>
odwiedzonych w czasie sesji, ale nie robi wiele więcej.
<P>Tutaj jest formularz, w którym, jeżeli chcesz, możesz podać
nam swoje imie.<BR>
Nie sprzedamy go nikomu.<BR>
<PXFORM METHOD=POST ACTION="<?php echo S P H P _ S E L F ; ? > " >
Nazywam się:
< I N P U T TYPE=TEXT S I Z E = 2 0 NAME=posted_username><BR>
<INPUT TYPE=SUBMIT NAME=SUBMIT VALUE="Zapamiętaj m n i e ! " >
</FORM>
</BODYX/HTML>

<?php
if (Spagecount >= Spagecount__limit)
session_destroy();
?>
Rozdział 25. » Sesje____________________________________________423

Ten przykładowy kod realizuje za pomocą wywołania funkcji obsługi sesji następujące
zadania:
1. Wywołuje session_start ( ) w celu nakłonienia PHP do odszukania wcze-
śniej istniejącej sesji —jeżeli nie zostanie odnaleziona żadna sesja, tworzony
jest nowy identyfikator (chociaż kolejną funkcją jest session_register ( ) ,
która troszczy się również o rozpoczęcie sesji, jeżeli nie było wywołania
session_start ()).
2. Użyta jest funkcja session_register ( ) , aby zarejestrować zmienne 'user-
name' i 'pagecount'. Są nadal niezainicjowane po tym wywołaniu.
3. Zwiększamy wartość $pagecount — ponieważ była ona niezainicjowana, jest
interpretowana jako 0; po tej operacji będzie miała wartość 1.
4. Za pomocą funkcji session_id() drukujemy wartość identyfikatora sesji
(w przypadku produkcyjnego serwera jest to działanie niezwykłe — nie jest to
informacja przeznaczona dla użytkownika).
5. Jeżeli strona wysłała do samej siebie zmienną $posted_username (przy uży-
ciu formularza), nazwa jest zapamiętywana w zarejestrowanej zmiennej
Susername. W wyniku Susername jest przenoszone do przyszłych wywołań
skryptu, nawet gdy $posted_username nie zostanie przeniesione.
6. Na koniec ustalamy, że użytkownik może odwiedzić określoną liczbę stron,
zanim sesja zostanie rozpoczęta od nowa. Używamy funkcji session_de-
stroy ( ) , aby usunąć wszystkie dane o zmiennych sesji, co oznacza, że rozpo-
czynamy od nowa zarówno z $pagecount, jak i $username.

Rysunki 25.1, 25.2 i 25.3 pokazują wygląd okna przeglądarki na pierwszym etapie wy-
konywania skryptu z wydruku 25.1, następnie po wysłaniu nazwy przez formularz i po
przejściu przez łącze. W tym przypadku przeglądarka obsługuje cookie, więc w adresie
URL nie pokazują się żadne informacje o sesji.

Zagadnienia konfiguracji
*
Zmienne z tabeli 25.2 należy ustawiać w pliku php.ini, można je również podejrzeć za
pomocą funkcji php_inf o ( ) . W tabeli zamieściliśmy opis i „typowe" wartości (niektó-
re wartości domyślne są zależne od platformy).

Pułapki i wykrywanie usterek


Jeżeli masz kłopoty z sesjami, na początku upewnij się, że obsługa sesji istnieje. Spró-
buj załadować przykładowy kod obsługi sesji T. ftp.helion.pl/przyklady/php4bi.zip, jeżeli
nadal będzie występował błąd, szukaj wcześniej.
424___________________________________Część III * Techniki zaawansowane

Rysunek 25.1.
Strona
testowania sesji
po uruchomieniu

Rysunek 25.2.
Strona
testowania sesji
po przetworzeniu
danych z formularza
Rozdział 25. » Sesje 425

Rysunek 25.3.
Strona
testowania sesji
po przejściu
przez łącze

Tabela 25.2.
Zmienne konfiguracji sesji

Zmienna php.ini Typowa wartość Opis


session. save p a t h /tmp Ścieżka do katalogu na serwerze, w którym
w systemach Unix zapamiętywane są pliki z wartościami
zmiennych sesji
session. auto start 0 Gdy ma wartość 1 , sesje będą inicjalizowane
automatycznie po uruchomieniu skryptu. Gdy ma
wartość 0, nie będzie użyty kod sesji do czasu użycia
session s t a r t ! ) lub session r e g i s t e r ! )
session. save handler 'files' Ciąg określający metodę zapisywania zmiennych
sesji. Inne metody nie sąjeszcze dobrze
*
udokumentowane — zmienianie ich nie jest
wskazane dla początkujących użytkowników
session . cookie lifetime 0 Określa czas, po jakim cookie stanie się nieważne,
i w konsekwencji czas trwania sesji. Domyślną
wartościąjest 0, co oznacza, że sesje trwają do czasu
zamknięcia przeglądarki — każda inna liczba oznacza
liczbę sekund dopuszczalnego czasu życia sesji
session. use cookies 1 Jeżeli ma wartość 1, mechanizm sesji będzie
próbował przenosić identyfikator sesji poprzez
ustawianie i sprawdzanie cookie (jeżeli przeglądarka
odrzuci cookie, używane będą zmienne GET,
POST). Jeżeli wartość ta wynosi 0, próba użycia
cookie nie będzie podjęta
426___________________________________Część III » Techniki zaawansowane

Jeżeli sesje nie działają lub powodują błędy, sprawdź ścieżkę zwracaną przez ses-
sion_save_path ( } i upewnij się, że katalog istnieje, a PHP może w nim zapisywać
pliki. Jeżeli nie, powinieneś utworzyć go lub zmienić wartość 'session. save_path'
w pl\kuphp.ini.
Pamiętaj, że funkcje sesji, które jako argumentu wymagają nazwy zmiennej, nie ocze-
kują początkowego $ w nazwie zmiennej.

Jeżeli nagłówki HTTP nie są wysyłane, skrypt może wysyłać tekst (nawet pusty wiersz)
przed wywołaniem funkcji session_start () lub session_register (). Odszukaj
w dołączanych plikach wszystkie puste wiersze lub przesuń funkcje obsługi sesji na po-
czątek pliku.

W czasie testowania kodu związanego z sesjami pamiętaj wypróbować go zarówno


w przeglądarce obsługującej sesje, jak i w przeglądarce z zablokowaną obsługą. Jeżeli
nie zobaczysz nazwy sesji w adresie URL łącza (na przykład: PHPSESSID) w przeglą-
darce nieakceprującej cookies, sesje nie działają, albo PHP nie jest odpowiednio skonfi-
gurowany do przezroczystego przesyłania identyfikatora sesji za pomocą argumentów
GET, POST.

Podsumowanie
Sesje są użyteczne w sytuacjach, gdy chcesz śledzić działania użytkownika w przypadku
interakcji trwającej dłużej niż wykonanie jednego skryptu. Jeżeli prezentowana zawar-
tość strony zależy od działań podejmowanych na poprzedniej, kod powinien zapamię-
tywać i przenosić te informacje w sposób, który umożliwia odróżnienie poszczególnych
użytkowników. Ponieważ protokół HTTP jest protokołem bezstanowym, prowadzi to
zastosowania technik zastępczych — zwykle albo ukrytych zmiennych (które są nie-
stety kłopotliwe), albo cookies (niektóre przegładarki klientów ich nie obsługują).

Implementacja sesji w PHP hermetyzuje te skomplikowane zagadnienia. Identyfikatory


sesji są tworzone i rozprowadzane w sposób automatyczny, a mechanizm rejestracji po-
zwala programiście dołączać dowolne dane PHP do sesji. Poza początkowym dołącze-
niem się do sesji i zarejestrowaniem zmiennych, które powinny zostać rozpropagowane
poza bieżącą stronę, użycie sesji jest dla programisty przejrzyste.
Rozdział 26.
Cookie i HTTP
W tym rozdziale:
* Ustawianie i odczytywanie cookie
** Wysyłanie surowych nagłówków HTTP
* Używanie przekierowań w przeglądarkach

HyperText Transfer Protocol (HTTP) jest,językiem", którego serwery i klienci WWW


używają do komunikacji między sobą. Gdy odwiedzasz witrynę WWW, powodujesz
zainicjowanie dialogu w Internecie przy użyciu HTTP (lub jego wariantu, przykład
bezpiecznego HTTPS). Zwykły użytkownik WWW nie musi przejmować się szczegó-
łami HTTP, ale nie dotyczy to programistów. Nasz przykłady w przeważającej części
korzystały z PHP w celu utworzenia skryptu wykonywanego na maszynie klienta lub
dokumentu HTML, wyświetlanego w przeglądarce użytkownika. HTTP jest pojazdem
przeznaczonym do przesyłania wywołań do naszego skryptu, następnie transportującym
odpowiedź w postaci gotowej do wyświetlenia u użytkownika.

W tym rozdziale przedstawimy kilka technik, które zależą od innych elementów inte-
rakcji klient-serwer, takich jak zapisywanie danych w cookie w komputerze klienta lub
jawne zastosowanie nagłówków HTTP do identyfikacji użytkownika lub przekierowa-
nia na inną stronę WWW. Funkcje PHP pozwalające na korzystanie z bezpośredniej
komunikacji HTTP są bardzo proste i jest ich stosunkowo niewiele — problemy wyni-
kaj ą raczej z ograniczeń nałożonych na sam protokół.

Cookie
Cookie to mała cząstka informacji przechowywana w komputerze klienta, albo w pa-
mięci przeglądarki, albo jako mały plik zapisany na dysku. Zawiera on parę nazwa
i wartość. „Ustawianie cookie" oznacza skojarzenie wartości z nazwą i zapisanie tej pa-
ry w komputerze klienta. „Pobieranie" albo „odczytywanie" polega na użyciu nazwy
w celu uzyskania skojarzonej z nią wartości (przeczytaj część „Cookie i prywatność",
w którym opisaliśmy kontrowersje związane z używaniem cookie}.
428 Część III » Techniki zaawansowane

Jako generalną zasadę powinniśmy przyjąć, że zapisujemy dane w co-


okie po stronie klienta tylko wtedy, gdy nie można zapisać ich na ser-
werze. To nie tylko grzeczność. Występują ograniczenia w korzystaniu
przez serwer z dysku klienta (zwykle jeden serwer WWW może zapisać
do 20 cookies w komputerze klienta). Jeżeli musisz zapamiętać wiele
danych, zastanów się nad zapisaniem w cookie tylko identyfikatora
umożliwiającego odszukanie reszty danych na serwerze.

W PHP cookies są ustawiane za pomocą funkcji setcookie ( ) , czytane są nieomal


automatycznie. Oznacza to, że wszystkie cookies ustawione przez twój serwer są pobie-
rane do strony jako zwykłe zmienne (podobnie jak zmienne GET/POST).

Wiele zastosowań cookies jest związanych ze śledzeniem sesji — pa-


miętaniem jakiejś części danych w czasie nawigacji. Jeżeli chcesz użyć
cookie do takiego właśnie zastosowania i używasz PHP 4, powinieneś
pomyśleć o zastosowaniu wbudowanych funkcji obsługi sesji, które
przedstawiono w poprzednim rozdziale. Nie tylko oferują lepszy poziom
abstrakcji, ale posiadają również wbudowany mechanizm zastępczy,
uruchamiany w przypadku odrzucenia cookie. Stosowane jest wtedy
przesyłanie danych za pomocą argumentów GET/POST.

Funkcja setcookie()
Istnieje tylko jedna funkcja związana z obsługą cookie — setcookie ( ) . W tabeli 26.1
zamieszczone są argumenty funkcji. Kolejność w tabeli odpowiada kolejności w wy-
wołaniu funkcji. Wszystkie argumenty oprócz pierwszego są opcjonalne.

Szczegóły reprezentowania czasu używanego jako argument expire


zamieszczone są w rozdziale 13. „Funkcje systemu operacyjnego
i dostępu do plików". Szczególnie interesujący będzie opis funkcji
time ( ) i m k t i m e ( ) .

Wywołanie setcookie o powoduje wysłanie odpowiedniego nagłówka


HTTP. Nie da się teego wykonać, jeżeli wcześniej wysłany został nor-
malny kod strony (nawet jeżeli zawiera on tylko jedną spację lub pusty
wiersz).

Przykłady
Zamieścimy teraz kilka przykładów użycia setcookie ( ) z komentarzem.
setcookie('membername', 'timboy');
Rozdział 26. » Cookie I HTTP______________________________________429

Tabela 26.1.
Argumenty setcookief)

Nazwa Spodziewany Znaczenie


argumentu typ
name string Nazwa cookie (analogiczna do nazwy zmiennej)
value string Wartość zapisywana w cookie (analogiczna do wartości przypisywanej do
zmiennej). Jeżeli argument ten nie zostanie podany, cookie o nazwie
określonej przez pierwszy argument zostanie skasowane
expire int Określa czas, po którym cookie powinno się unieważnić. Wartość 0
(domyślna) oznacza, że cookie ma istnieć aż do zamknięcia przeglądarki.
Każda inna wartość jest interpretowana jako czas absolutny (taki, jak
zwraca funkcja mktime ( ) ), kiedy ważność cookie wygaśnie
path string Domyślnie każda strona umieszczona w głównym katalogu witryny WWW
może widzieć (i może ustawić) cookie o określonej nazwie. Ustawienie
tego parametru na podkatalog (na przykład "/mysteryguide") pozwala na
odróżnienie cookie ustawionych przez różne części tej samej witryny WWW

domain string W normalnych przypadkach nie jest sprawdzana domena wywoływana


przez klienta. Jeżeli zostanie ustawiony ten argument, domena musi się
zgadzać. Jeżeli np. ten sam serwer udostępnia mysteryguide.com
i sciencebookguide.com, to kod witryny może stwierdzić, że inna witryna
nie odczytuje (lub nie ustawia) używanego przez niącoofae dzięki
ustawieniu tego argumentu jako "mysteryguide.com"
secure int (0 lub l ) Wartością domyślnąjest 0. Jeżeli ustawimy wartość argumentu na 1,
cookie będzie wysyłane tylko przez bezpieczne połączenie (https).
Połączenie takie musi być wcześniej uruchomione

Wywołanie powyższe ustawia cookie o nazwie membername i o wartości timboy. Po-


nieważ nie ma żadnych innych parametrów oprócz pierwszych dwóch, cookie przetrwa
tylko do zamknięcia przeglądarki i będzie dostępne dla wszystkich kolejnych wywołań
z tego serwera, niezależnie od nazwy domeny i od miejsca w hierarchii katalogów ser-
wera WWW. Cookie będzie również dostępne niezależnie od tego, czy mamy bezpiecz-
ne połączenie.
setcookie f ' membername', ' troutgirl' , timeO -t- 86400,
"", "www.troutworks.com", l);

To wywołanie ustawia cookie o wartości ' t r o u t g i r l ' nadpisuje wartość z poprzed-


niego przykładu, jeżeli została ustawiona na wcześniejszej stronie. Czas, po którym
ważność cookie zakończy się, jest ustawiony na 86400 sekund (l dzień = 60 sekund *
60 minut * 24 godziny). Ścieżka nie jest podana (przekazany został pusty ciąg), więc
cookie może być czytane niezależnie od położenia strony w hierarchii katalogów. Ar-
gument host jest ustawiony na ' www. troutworks. com' co oznacza, że kolejne stro-
ny nie odczytają tego cookie, chyba że użytkownik wyśle żądanie do tego właśnie
komputera. Ostatni argument określa, że cookie może być czytane i zapisywane tylko
poprzez bezpieczne połączenie (jeżeli połączenie używane przez tę stronę nie jest bez-
pieczne, cookie nie będzie w ogóle ustawione).
430 Część III » Techniki zaawansowane

Wielokrotne wywołania setcookieo będą zwykle interpretowane w od-


wrotnej kolejności, niż znajdują się w skrypcie PHP, ale nie wszystkie
przeglądarki tak działają. Najlepszym rozwiązaniem jest nie ustawia-
nie w jednej stronie dwóch takich samych cookies (wysyłanie cookie
więcej niż raz jest zupełnie bezcelowe, ponieważ drugie wywołanie
nadpisze poprzednią wartość).

Jeżeli chcesz podać późniejsze argumenty setcookie o , pozostawia-


jąc poprzednie bez zmian, najlepiej podstawić pusty ciąg ("") w miej-
sce argumentu domain, ciąg zawierający znak ukośnika ("/") w miejsce
ścieżki oraz O w miejsce czasu upływu ważności.

Usuwanie cookie
Usuwanie cookie jest łatwe. Po prostu wywołaj setcookie (nazwa_cookie) bez
podanego drugiego argumentu, które nie przypisuje do wartości cookie pustego ciągu,
ale usuwa je.

Cookies i prywatność
Cookies zawsze budziły kontrowersje z powodu naruszania prywatności.
W czasie pisania tej książki DoubleClick (internetowa agencja reklamowa)
była krytykowana za plany skorelowania danych pochodzących z cookie
z danymi zawartymi w olbrzymiej bazie danych z nazwami konsumentów, ad-
resami i przyzwyczajeniami. Istnieje obawa, że jeżeli klient poda swoje dane
w witrynie, wypełniając formularz, i pozwoli na ustawienie cookie, wszystkie
inne witryny porównujące swoje zapisy z wcześniejszymi witrynami mogą w ten
sposób identyfikować użytkowników (i poznać w ten sposób wiele innych
danych). Jeżeli taka praktyka stałaby się powszechna, każda witryna handlu
elektronicznego, którą odwiedzasz, mogłaby poznać nie tylko twoje nazwi-
sko, adres i zakupione towary, ale również listę innych odwiedzanych stron.
Cookies są również rozsądnym obejściem bezstanowości protokołu HTTP.
Istnieje wiele powodów, żeby rozciągać interakcję pomiędzy klientem i ser-
werem na kilka kolejnych stron, zamiast pakować wszystko do jednego wy-
wołania. Jako projektant witryny możesz zdecydować się na użycie do tego
celu cookie, ponieważ nie występuje tutaj bezpośrednia ingerencja w pry-
watność użytkownika.
Wielu użytkowników tak ustawia swoje przeglądarki, aby odrzucały wszystkie
cookies (pamiętaj, że jest to związane nie tylko z prywatnością, ale również
z dostępem do ich własnych dysków). Każdy kod na serwerze powinien od-
powiednio obsługiwać odrzucenie cookies przez klienta, a witryny powinny
posiadać łatwe do odszukania opisy zasad szanowania prywatności, aby
użytkownik wiedział, czego się może spodziewać.
Rozdział 26. » Cookie I HTTP______________________________________431

Odczytywanie cookie
Cookies automatycznie pojawiają się jako zmienne globalne strony, jeżeli uda się ich
odczytanie. Na przykład ustawienie cookie:
setcookie('membername', 't imboy');

spowoduje, że na kolejnych stronach będziesz mógł łatwo wypisać tę wartość w nastę-


pujący sposób:
print("Wartość membername wynosi $membername<BR>");

Jeżeli ustawiasz cookie w skrypcie, ustawienie to zostanie wykonane


po wysłaniu całej strony do klienta zbyt późno, aby skorzystać z zalet
automatycznego odczytywania cookie na tej stronie. Oznacza to, że
odpowiednia zmienna globalna zostanie ustawiona dopiero przy na-
stępnym żądaniu strony.

Poniższy przykład nie zadziała tak, jak mógłbyś się spodziewać:


setcookie('membername', 'timboy');
print ( "Ustawiłem cookie! Teraz odczytam wartość<BR>");
// (ŹLE - wartość zmiennej $membername będzie pusta)
print("Wartość membername wynosi Smembername<BR>");

Dzieje się tak, ponieważ, jak wspomnieliśmy w ostatniej wskazówce, cookie nie zostanie
ustawione dopóki odpowiednie nagłówki HTTP nie dotrą do klienta. Ponieważ w przy-
kładzie taka sytuacja nie zaszła, zmienna $membername nie została zainicjowana i bę-
dzie zawierała prawdopodobnie pusty ciąg.

Poniższy przykład pokazuje prawidłową obsługę takiego przypadku:


$cookievalue = 'timboy';
setcookie('membername', $cookievalue);
print( "Ustawiłem cookie, aby użyć go w kolejnych stronach<BR>");
// (DOBRZE - wypisujemy wartość zainicjowanej zmiennej)
print("Wartość membername wynosi $cookievalue<BR>");

Kolejne skrypty załadowane do tej samej przeglądarki będą mogły odwoływać się do
zmiennej $membername.

Wspominaliśmy już o problemie naruszenia prywatności użytkownika


przyjmującego cookie z serwera. Trzeba zauważyć, że są to zagrożenia
również innego rodzaju. Jeżeli piszesz skrypt, który zależy od integral-
ności danych zawartych w cookie, powinieneś pamiętać, że sprytny
użytkownik może je zmienić i ustawić w nich dowolne wartości. W roz-
dziale 31. „Bezpieczeństwo i kryptografia" znajduje się opis technik
szyfrowania ważnych danych, nawet tych w cookie.
432___________________________________Część III » Techniki zaawansowane

Zmienne GET, POST i cookie


Autorzy tej książki z całego serca popieraj ą decyzję projektantów PHP, aby pozwolić na
natychmiastowy dostęp do wielu rodzajów informacji dzięki zmiennym globalnym. Ar-
gumenty GET i POST, wartości cookie i różne parametry konfiguracji oraz środowiska
po prostu są gotowe do użycia bez zbędnych wymogów formalnych. Oczywiście nie-
uniknioną wadą globalnej dostępności jest kolizja nazw — w jakimś momencie praw-
dopodobnie uwikłasz się w błędy wynikające z tego, że jakaś zmienna globalna będzie
przypisana lub jej wartość zostanie niespodziewanie zmieniona. Nie jest to problem ze
skryptem (który zwykle robi to, czego po nim oczekiwałeś w obrębie swojej przestrzeni
nazw, ponieważ żadne zwykłe przypisanie do zmiennej nie jest przenoszone do innych
skryptów), ale z inną częścią oprogramowania.

Wszystkie wartości zmiennych GET, POST oraz cookie są dostępne jako globalne
zmienne strony już na początku skryptu. Jeżeli dwa lub więcej z tych źródeł posiada
zmienne o tej samej nazwie, jedna lub więcej wartości zmiennej zostanie nadpisana.
Domyślną kolejnością nadpisywania jest: GET, POST, cookie — co oznacza, że argu-
menty POST nadpiszą wszystkie zmienne GET o takiej samej nazwie, a wartości cookie
nadpiszą wartości argumentów POST.

Przykład: kolejność nadpisywania zmiennych


Na wydruku 26.1 zamieszczony jest kod korzystający z wszystkich trzech źródeł
zmiennych globalnych. Przykład ten pokazuje mechanizm domyślnego nadpisywania
w działaniu.

Wydruk 26.1. Kolejność nadpisywania argumentów GET, POST i cookie_____ __ __ ______


<?php
if ((ilsSet($VarSource)) I I (SVarSource != "cookie"))
setcookie("VarSource", "cookie");
else
setcookie("VarSource");
?>
<HTML>
<HEAD>
<TITLE>Pokazywanie kolejności nadpisywania zmiennych</TITLE>
</HEAD>
<?php
if (IsSet(SVarSource))
print{"Źródło zmiennej: $VarSource<BR>");
else
print("Nieokreślone źródło zmiennej");
?>
<P>Formularz przetwarzany przez ten sam skrypt:
<FORM METHOD=POST
ACTION="<?php echo $PHP_SELF?>?VarSource=Get">
<INPUT TYPE=SUBMIT VALUE=WyŚlij>
<INPUT TYPE=HIDDEN NAME=VarSource VALOE=Post>
</FORM>
</BODYX/HTML>

W kodzie tym istnieją trzy możliwe sposoby ustawienia zmiennej globalnej $VarSource:
ze zmiennej POST, ze zmiennej GET oraz z cookie. Nieco pokrętny może się wydawać
sam początek kodu, który ustawia cookie 'VarSource', jeżeli zmienna $ VarSource
Rozdział 26. » Cookie I HTTP______________________________________433

nie jest ustawiona T. cookie, i usuwa cookie, jeżeli jest ustawiona. W przypadku domyśl-
nej kolejności nadpisywania powoduje to kolejne ustawianie i usuwanie cookie w trak-
cie kolejnych przeładowań strony.

Jedynym przeznaczeniem tej strony jest pokazywanie wartości zmiennej $varSource,


o ile jest ustawiona, i pokazywanie formularza zawierającego argumenty GET oraz POST
o tej samej nazwie (użycie GET i POST jednocześnie nie jest nielegalne, ale nieeleganckie).

Rysunek 26.1 pokazuje wynik działania skryptu po pierwszym załadowaniu.

Rysunek 26.1.
Pierwsze
załadowanie skryptu
pokazującego
kolejność
nadpisywania

Przy pierwszym załadowaniu strony nie ma ona żadnych argumentów GET ani POST
i mimo że zostało właśnie ustawione cookie, nie jest jeszcze dostępne w tym skrypcie.
W wyniku tego zmienna $VarSource nie została ustawiona.

Na rysunku 26.2 pokazany jest wygląd strony po naciśnięciu przycisku Wyślij za pierw-
szym razem. Tym razem zmienna $VarSource jest zainicjowana przez cookie, ponie-
waż zostało ustawione w czasie pierwszego załadowania strony. Obecnie w rym
konkretnym przypadku wszystkie źródła danych (GET, POST i cookie) dostarczaj ą war-
tości zmiennej $VarSource, nadpisywanej w takiej właśnie kolejności.

Rysunek 26.2.
Wynik dzialania
skryptu
po pierwszym
naciśnięciu Wyślij
434___________________________________Część III » Techniki zaawansowane

Jeżeli jeszcze raz przyciśniemy Wyślij, powinniśmy zobaczyć ekran pokazany na rysun-
ku 26.3. Cookie zostało usunięte w czasie drugiego załadowania, więc wartość argu-
mentu POST nie jest nadpisywana. Zauważ, że w adresie URL znajduje się zmienna
GET, ale za każdym razem jest nadpisywana.

Rysunek 26.3
Wynik dzialania
skryptu po drugim
naciśnięciu Wyślij

Ustawianie kolejności nadpisywania


Domyślną kolejnością nadpisywania wartości w czasie tworzenia zmiennych global-
nych z różnych źródeł HTTP jest GPC, skrót od GET, POST, cookie. Jeżeli posiadasz
własną instalację PHP i zdecydujesz, że kolejność ta nie jest odpowiednia, możesz ją
zmienić, edytując p\ikphp.ini i zmieniając wartość dyrektywy gpc_order z "GPC" na
cokolwiek innego. Zmienne globalne pochodzące z różnych źródeł są ustawiane w ko-
lejności liter w tym ciągu, co powoduje, że późniejsze przypisanie nadpisuje poprzed-
nie. Jeżeli opuścisz literę, odpowiadające jej źródło danych nie będzie brało udziału
w przypisywaniu wartości do zmiennych globalnych. Na przykład, jeżeli ustawisz dy-
rektywę gpc_order na PG, wartości POST będą nadpisywane przez wartości zmien-
nych GET o tej samej nazwie, a wartości zapisane w cookie nie będą brane pod uwagę
w czasie tworzenia zmiennych globalnych.

Tablice zmiennych GET, POST i COOKIE


W przypadku, gdy problemy nachodzenia na siebie przestrzeni nazw zmiennych są
trudne do opanowania, można odczytać wartości zmiennych bezpośrednio z ich źródła
dzięki użyciu tablic HTTP_GET_VARS, HTTP_POST_VARS i HTTP_COOKIE_VARS. Każ-
da z nich jest tablicą asocjacyjną, która zawiera nazwy i wartości zmiennych pochodzą-
cych z odpowiedniego źródła, niezależnie od wykonywanego później przypisania do
zmiennych globalnych. Dla przykładu, skrypt zamieszczony na wydruku 26. l pokazuje
zawartość zmiennej globalnej $VarSource, której wartość zależy od kolejności nadpi-
sywania. Jeżeli potrzebujesz wartości z określonego źródła danych HTTP, możesz użyć
wartości HTTP_GET_VARS['VarSource ' ], HTTP_POST_VARS['VarSource'] lub
HTTP COOKIE V A R S [ ' V a r S o u r c e ' ] •
Rozdział 26. « Cookie i HTTP 435

PHP przechowuje wartości zmiennych HTTP w tablicach tylko w przy-


padku, gdy zmienna konfiguracji track_vars ma wartość TRUE. Jest
ona tak ustawiana w przypadku kompilacji PHP z (domyślną) opcją --
enabie-track-vars. Można również zmienić to ustawienie, modufi-
kując wartość tej zmiennej w pliku php.ini.

Pułapki cookie
Trudno jest zrobić coś źle używając PHP. Ustawienie cookie wymaga wywołania tylko
jednej funkcji (set_cookie ( ) ) , a odczytanie cookie nie wymaga podejmowania żad-
nych działań (ponieważ wartość cookie automagicznie pojawia się jako zmienna glo-
balna). Czy można sobie wyobrazić coś łatwiejszego? Typowe problemy są związane
z samym protokołem HTTP.

Wcześniejsze wysłanie danych


Najczęstszym błędem przy obsłudze cookie jest próba ustawienia cookie po wysłaniu
jakichkolwiek danych zawartych na stronie HTML (powtórzymy to jeszcze raz w tym
rozdziale, ponieważ odnosi się to również do innych bezpośrednich manipulacji z pro-
tokołem HTTP i jest przyczyną większości problemów przy uruchamianiu).

Powodem błędnego działania jest to, że HTTP wymaga wysłania nagłówka przed za-
wartością samej strony HTML — nie mogą być wymieszane. Przed wygenerowaniem
jakiejkolwiek zawartości strony PHP musi wiedzieć o wszystkich specjalnych nagłów-
kach i wtedy wysyła je przed rozpoczęciem transmisji zawartości strony HTML. Jeżeli
zdarzy się, że cookie (lub dane o innym nagłówku) wystąpi później, zostanie wygene-
rowany błąd.

Łatwo napisać kod łamiący tę zasadę. Spójrz na następujący przykład:


<?php /* Subtelny błąd użycia cookie */
setcookie('cookie', 'wartość');
?>
<HTMLXHEAD>
<TITLE>Pozornie prosta strona ustawiająca cookie</TITLE>
</HEADXBODY>
<H3>Strona jest tak prosta, że nic złego nie może się zdarzyć</H3>
</BODYX/HTML>

Gdy załadujemy ten skrypt, otrzymamy błąd „cannot add header information" (nie
można dodać danych nagłówka). Problem powodowany jest pierwszym znakiem pliku:
spacja przed „<?php". Ponieważ pliki PHP rozpoczynaj ą pracę w trybie HTML, plik ten
spowoduje wygenerowanie jednej spacji i wysłanie jej do klienta, zanim włączony zo-
stanie tryb PHP i będzie wysłane żądanie ustawienia cookie.

Podobnym sposobem przypadkowego wysłania danych nagłówka jest zbyt wczesne


dołączenie pliku zawierającego na końcu puste wiersze po zamknięciu znacznika PHP.
Możesz również zmienić tę zasadę, wysyłając cokolwiek za pomocą echo lub print.
436___________________________________Część III » Techniki zaawansowane

Jeżeli kiedykolwiek zdarzy się ten błąd, dosyć łatwo odszukać jego przyczynę. Spróbuj
najpierw przesunąć kod związany z HTTP na początek pliku. Jeżeli mimo tego otrzy-
masz komunikaty o błędach, musisz prześledzić kod wstecz od miejsca, w którym wy-
stąpił błąd, aż do początku pliku. Gdzieś pomiędzy początkiem pliku a miejscem
wystąpienia błędu znajduje się albo kilka znaków interpretowanych w trybie HTML,
albo konstrukcja PHP wypisująca dane. Jeżeli dołączałeś jakieś pliki przed miejscem
wystąpienia błędu, sprawdź, czy występują w nich znaki przed znacznikiem startowym
PHP lub po znaczniku zamykającym blok PHP.

Nasze powtarzające się ostrzeżenia na temat kolejności danych w na-


główkach HTTP i zawartości strony są ważne dla wszystkich wersji PHP
3 oraz dla PHP 4 do wersji beta 3. Jednak projektanci PHP 4 dołożyli
opcję „buforowania wyjścia", która ma za zadanie zapamiętać fragmen-
ty danych PHP, aby wysłać je razem, a nie w momencie wygenerowa-
nia. Oznacza to, że PHP sam zajmuje się wstrzymywaniem zawartości
strong do momentu wysłania wszystkich nagłówków HTTP. Powinieneś
przetestować możliwości buforowanego wyjścia w twojej wersji PHP,
próbując uruchomić przykład naruszający reguły kolejności, zamiesz-
czony w tym rozdziale (jeżeli będzie on działał bez problemów, wyjście
jest buforowane). Możesz również obejrzeć wynik funkcji phpinfoo,
szukając wzmianki o buforowaniu — jeżeli się tam znajduje, będziesz
mógł zmienić to ustawienie w pliku php.ini.

Interpretacja w odwrotnej kolejności


Wywołania setcookieO są realizowane w odwrotnej kolejności, niż znajdują się
w skrypcie PHP. Oznacza to, że para kolejnych wywołań prawdopodobnie usunie co-
okie, ponieważ wyrażenie usuwające cookie będzie wykonane jako drugie.
setcookie("mycookie"); // usuń poprzednia wartość (ŹLE)
setcookie("mycookie", "newvalue"); // ustaw nową wartość (ŹLE)

Zwykle nie ma potrzeby usuwania cookie przed ustawieniem go na in-


ną wartość — po prostu ustaw nową wartość. Oznacza to, że myląca
odwrotna interpretacja wywołań setcookie o nie powinna zwykle
mieć znaczenia.

Odrzucenie cookie
Na koniec musimy ostrzec, że setcookie ( ) nie gwarantuje, że cookies będą przyjęte
przez przeglądarkę — setcookieO próbuje tylko wysłać odpowiedni nagłówek
HTTP. To, co stanie się później w komputerze klienta, może zależeć od tego, czy klient
ma odpowiednio nową przeglądarkę, która potrafi obsługiwać cookie, albo że użytkow-
nik z rozmysłem zablokował przyjmowanie cookie.

Funkcja setcookie ( ) nawet nie zwraca wartości wskazującej na zaakceptowanie lub


odrzucenie cookie. Jest to związane z synchronizacją pomiędzy wykonaniem skryptu
Rozdział 26. » Cookie i HTTP______________________________________437

a samym protokołem HTTP. Po pierwsze skrypt wykonuje się (włączając w to wywoła-


nie setcookie ( ) ) dając w wyniku kompletną stronę wraz z nagłówkami HTTP. Wy-
nik ten jest wysyłany do komputera klienta. W tym momencie przeglądarka decyduje,
w jaki sposób zareagować na próbę ustawienia cookie. Dlatego skrypty muszą zawsze
być napisane w sposób zapewniający sensowne działanie w przypadku niepowodzenia
funkcji setcookie ( ) . Częstą techniką jest ustawianie cookie o nazwie „CookiesOn"
i sprawdzanie na kolejnych stronach, czy zmienna $CookiesOn jest ustawiona.

Wysyłanie nagłówków HTTP


Funkcja setcookie ( ) stanowi otoczkę do użycia odpowiednich nagłówków HTTP.
PHP posiada dodatkowo funkcję header ( ) , która może być użyta do wysłania suro-
wych nagłówków HTTP. Możesz za jej pomocą napisać własną funkcję ustawiającą co-
okie. Można jej użyć do korzystania z innych funkcji kontrolowanych przez nagłówki.

Składnia funkcji header ( ) jest tak prosta: wymaga jednego argumentu, którym jest
ciąg zawierający nagłówek do wysłania.

Wszystkie ostrzeżenia na temat wysyłania HTTP przed zawartością


strony mają również zastosowanie do funkcji header o. Jeżeli nie
masz PHP 4 z włączoną funkcją buforowania wyjścia, wszystkie wywo-
łania header () muszą poprzedzać dane HTML ze skryptu, nawet jeże-
li dane te to jedna spacja bądź pusta linia.

Przykład: przekierowanie
Jednym z użytecznych nagłówków HTTP jest nagłówek „Location:", który może słu-
żyć do przekierowania. Należy podać pełny adres URL po ciągu „Location:" i prze-
glądarka rozpocznie odczytywanie danych z tego adresu. Na przykład:
<?php
if ((IsSet(Sgender) SS ($gender == 'kobieta'))
{
header(
"Location: http: //www .troutworks. com/phpbook/ch26/secret. php" ) ;
exit ;
1
?>
<HTMLXHEADXTITLE>Strona dla wszystkich</TITLEX/HEAD>
<BODY>
<H3>Witaj!</H3>
Witamy wszystkich na tej stronie, nawet mężczyzn! Opowiedzcie nam o sobie.
</BODYX/HTML>

Jeżeli wpiszemy tylko adres URL tej strony (http://www.troutworks.com/phpbook/ch26/


inclusive.php), zobaczymy stronę będącą wynikiem kodu HTML zawartego na dole
skryptu. Jeżeli jednak dodamy argument GET (http://www.troutworks.com/phpbook
/ch26/inclusive.php?gender=kobieta), zostaniemy skierowani do całkowicie innej stro-
438________________________________Część III » Techniki zaawansowane

ny. Zauważ, że różni się to od selektywnego importowania zawartości przy użyciu wy-
rażenia include — przeglądasz stronę o zupełnie innym adresie URL, niż wpisałeś
w oknie przeglądarki.
Ten rodzaj przekierowaniajest użyteczny w przypadku, gdy chcesz podzielić swoją wi-
trynę WWW na warunkowe „gałęzie" bez konieczności wybierania przez użytkownika
różnych łączy.

Przykład: uwierzytelnianie HTTP


Innym użytecznym zastosowaniem HTTP jest możliwość zażądania, aby przeglądarka
zapytała w oknie dialogowym o nazwę użytkownika i hasło. Jest to realizowane przez
nagłówek www-aut hen t i ca t e, w sposób pokazany na przykładzie:
<?php
$the__right_user = 'user1; // tylko przykład! Nie zalecane
Sthe_right_password = 'password'; //tylko przykład!

if(!isset($PHP_AUTH_USER))
{
header("WWW-Authenticate: Basic realm=\"PHP book\"");
header("HTTP/1.0 401 Unauthorized");
echo "Anulowane przez użytkownika\n";
exit;
)
else
(
if (($PHP_AUTH_USER == 'user') &&
($PHP_AUTH_PASSWD == 'password')) // patrz ostrzeżenie poniżej
print("Możesz wejść<BR>");
else
print("Nie jesteś tu mile widziany<BR>");
}
?>

Po pierwszym załadowaniu tego skryptu (z użyciem odpowiedniej przeglądarki i wersji


serwera) na ekranie pokaże się okno dialogowe. Po wprowadzeniu przez użytkownika
informacji do okna skrypt zostanie powtórnie automatycznie wywołany, z ustawionymi
wartościami zmiennych $PHP_AUTH_USER (nazwa użytkownika), $PHP_AUTH_PASSWD
(wprowadzone hasło) oraz $PHP_AUTH_TYPE (zmienna ta będzie zawierała ciąg „Ba-
sic", ponieważ w PHP dostępny jest tylko taki typ uwierzytelniania). Korzystne jest, że
zmienne te pozostaną ustawione przez przeglądarkę we wszystkich kolejnych stronach,
dlatego nie ma potrzeby używania innego mechanizmu do ich propagacji — wystarcza
tylko jedno pytanie o hasło w czasie sesji.

Poniższy kod zawiera tylko absolutne minimum potrzebne do demon-


stracji mechanizmu uwierzytelniania HTTP; nie jest modelem rzeczy-
wistego systemu zabezpieczenia za pomocą kombinacji użytkownik
i hasło. Nasz fragment kodu po prostu porównuje wartości zmiennych
dostarczone przez przeglądarkę z zapisanymi w kodzie zmiennymi.
Aby utworzyć prawdziwy system uwierzytelniania, powinieneś być mo-
że porównywać wynik zakodowania hasła z podobnie zakodowaną
wersją zapisaną w bazie danych lub pliku. Rozdział 31. zawiera więcej
informacji na temat kodowania i zagadnień bezpieczeństwa.
Rozdział 26. » Cookie i HTTP 439

Ten mechanizm uwierzytelniania działa tylko na serwerze WWW Apa-


che z modułem PHP. Nie działa z wersją CGI PHP, ani z IIS/PWS.

Oprócz przekierowań i uwierzytelniania, zdolność do wysiania dowolnych nagłówków


HTTP pozwala na precyzyjniejszą kontrolę nad wieloma aspektami relacji klient-serwer
w HTTP, które są zwykle ustawiane domyślnie. Można na przykład określić dokładnie
czas wygaśnięcia i sposób buforowania strony lub wysyłać zwrotne kody statusu, które
wskazuj ą klientowi, czy zwracana wartość powinna zostać zinterpretowana jako powo-
dzenie czy niepowodzenie. Ponieważ PHP działa tylko jako kanał komunikacyjny do
protokołu HTTP, większość z tych technik wykracza poza ramy tej książki.

Pułapki związane z nagłówkami


Funkcja header ( ) podlega takim samym ograniczeniom jak funkcja setcookie():
nie można wysłać żadnych nagłówków po przesłaniu jakiejkolwiek zawartości strony,
chyba że używasz późniejszych niż beta 3 wersji PHP 4, posiadającej opcję buforowa-
nia wyjścia.

Powinieneś również wiedzieć, że użycie nagłówków wymaga nie tylko znajomości sa-
mego protokołu HTTP, ale również wiedzy o sposobie dostosowywania się doń różnych
przeglądarek. Jeżeli piszesz skrypty przeznaczone dla szerszego grona, powinieneś wy-
konać więcej testów przeglądarek za pomocą prostych skryptów generujących HTML.

Większość przeglądarek posiada funkcję ostrzegania o próbie ustawie-


nia cookie. Chociaż podczas przeglądania witryn wykorzystujących co-
okies taka sytuacja irytuje, stanowi świetne narzędzie do uruchamiania
własnych skryptów używających cookies.

Podsumowanie
PHP oferuje kilka sposobów skorzystania z możliwości protokołu HTTP, oprócz funkcji
tworzenia stron HTML, które są przesyłane dzięki HTTP. Funkcja setcookieO po-
zwala na ustawienie lub usunięcie cookie w przeglądarce użytkownika; wartości takie
będą dostępne na kolejnych stronach jako zwykłe zmienne globalne.

Funkcja header ( ) pozwala na wysłanie dowolnego nagłówka HTTP. Może być uży-
wana na przykład do realizacji uwierzytelniania i przekierowań.

Funkcje HTTP są bardzo proste, a główną przyczyną kłopotów jest sam protokół HTTP.
Jedną z komplikacji jest konieczność wysłania wszystkich nagłówków HTTP przed wy-
słaniem zawartości strony. Jest to bardzo częste źródło błędów w PHP 3 i w wersjach
beta PHP 4. Na szczęście w finalnej wersji PHP 4 wyjście HTTP jest buforowane
w sposób umożliwiający mieszanie nagłówków z zawartością.
440________________________________Część III » Techniki zaawansowane
Rozdział 27.
PHP i JavaScript
W tym rozdziale:
** Tworzenie kodu JavaScript z PHP
* PHP jako zapas dla JavaScript
* JavaScript statyczny kontra dynamiczny
* Dynamiczna generacja formularzy

W tym rozdziale spróbujemy połączyć najlepsze cechy skryptów wykonywanych na


serwerze i na kliencie, łącząc PHP z JavaScript. Zaznaczymy granice użycia tych dwóch
języków skryptowych oraz przedstawimy wskazówki stylistyczne, które mogą być po-
mocne przy pisaniu kodu. Następnie przejdziemy do przykładów zastosowań, z których
możesz skorzystać na własnej witrynie PHP.

Jeżeli nigdy nie używałeś języka JavaScript, lektura tego rozdziału nie
wystarczy do nauczenia się go. Zajmujemy się tutaj tylko tymi aspek-
tami JavaScript, które istotnie wpływają na PHP. Jeżeli zastanawiasz
się, co to jest zdarzenie onBiur, polecamy książkę Java Script, Autor:
Arman Danesh, ISBN 83-86718-82-X.

Tworzenie kodu JavaScript w PHP


Ponieważ PHP działa po stronie serwera, a JavaScript po stronie klienta, możesz spo-
dziewać się kłopotów, stosując je razem na jednej stronie. Jednak podział ten powoduje,
że są świetnym zespołem.

Mimo że PHP potrafi tworzyć dynamicznie strony WWW, jest to jedynie język skryp-
towy serwera. Istnieje duża grupa zadań wykonywanych w witrynie WWW, które
prawdopodobnie nie potrzebuj ą potęgi serwera i najlepiej jeżeli będą wykonane szybko
— na przykład zmiana wyglądu kursora myszy w trakcie przesunięcia nad obiektem.
442___________________________________Część III » Techniki zaawansowane

JavaScript jest językiem skryptów, zorientowanym na klienta (istnieje wersja dla serwe-
ra, ale zakładamy, że już wybrałeś PHP), który może być szybko zintegrowany z PHP.

Również język skryptów klienta JavaScript (znany także jako Javascript, JScript, EC-
MAScript) posiada wiele ograniczeń. Na przykład nie potrafi komunikować się bezpo-
średnio z bazą danych, ani zmieniać się w czasie pracy. Co gorsza, nie można bazować
na technologiach po stronie klienta, ponieważ mogą być zablokowane lub niedostępne
w przeglądarkach użytkowników. Sumienni programiści skryptów muszą albo decydo-
wać o kodowaniu w najbardziej prawdopodobny sposób (i przyjmować narzekania ze
strony mniejszości) lub utrzymywać kilka wersji witryny. PHP może pomóc w obejściu
problemów niespójności technologii klienckich.

Pojedynek obiektów
Prawdopodobnie największą różnicą pomiędzy JavaScript i PHP jest różnica w mode-
lach obiektów. Są one całkowicie różne koncepcyjnie i używaj ą różnych stylów notacji.
Niektórzy mogą uważać to za zaletę, ponieważ nie ma niebezpieczeństwa pomieszania
podobnych obiektów (jest to możliwe na przykład w przypadku ASP i JavaScript).
Wielu będzie uważało to za niekompatybilność, niewygodę lub wadę projektową. Jed-
nak w każdym przypadku nie ma możliwości dostania się do tych samych obiektów za
pomocą PHP i JavaScript.

JavaScript jest konsekwentnie obiektowy. Każde wyrażenie wymaga podania obiektu


i metody lub funkcji, może również posiadać metody obsługi zdarzeń. JavaScript używa
do zapisu wyrażeń notacji z kropką (obiekt.metoda), podobnej do notacji stosowa-
nych w C, Java i Microsoft VBScript.

Wadą modelu obiektowego JavaScript jest jego słaba standaryzacja, mimo że teoretycz-
nie ECMA i W3C wprowadziły międzynarodowe standardy, praktycznie wszyscy pro-
ducenci przeglądarek łamią podstawowe zasady lub dodają nowe. Biegli programiści
JavaScript zużywają wiele energii, śledząc niekompatybilności i rozwiązania proble-
mów dla różnych przeglądarek i platform.

Jak wspominaliśmy w tej książce, klasy PHP są bardziej dodatkiem, modernizacją lub udo-
godnieniem niż istotną częścią języka. Używana notacja jest nazywana stylem „ze strzałką"
lub „wskaźnikiem", styl taki jest spotykany czasami w kodzie C++ ($this->zmienna).
Nie ma sposobu, aby używać notacji z kropką. Zmusza nas to szczerego przyznania, że kla-
sy PHP prawdopodobnie nie rozwiną się do prawdziwego modelu obiektowego.

PHP nie analizuje wysyłanych danych


Musisz wiedzieć i pamiętać, że dla PHP nie ma żadnego znaczenia, jakie dane wysyła
na wyjście. Możesz użyć PHP do wysyłania czystego tekstu, HTML, XHTML,
DHTML, JavaScript, XML, MathML, różnych formatów graficznych, CSS, XSL lub
nawet ASP. Nie ma żadnej bariery technicznej, aby PHP produkował kod w C, jednak
takie zastosowanie nie będzie prawdopodobnie zbyt szeroko rozpowszechnione.
Rozdział 27. « PHP i JavaScript_____________________________________443

Pamiętaj, że PHP nie zawsze daje w wyniku PHP — jego końcowym produktem jest
zwykle kod uruchamiany przez inną aplikację, na przykład przeglądarkę.

Istnieje kilka sposobów wysłania kodu JavaScript za pomocą PHP. Najprostszym spo-
sobem jest wyjście z trybu PHP w miejscach, w których musisz umieścić kod wykony-
wany w komputerze klienta. Jest to realizowane dokładnie tak samo, jak przejście do
trybu HTML.
<?php
echo( "Tutaj znajduje się skomplikowany kod PHP."};
?>
<SCRIPT LANGUAGE="JavaScript">
<!-- Ukryj dla przeglądarek nie obsługujących JavaScript
document.write("Oddzielenie skryptów serwera i klienta jest dobrym
^rozwiązaniem. " i
// koniec ukrywania -->
</SCRIPT>
<?php
echo("Kolejny blok PHP");
?>

Nawet ten przykład nie pokazuje pełnej separacji pomiędzy PHP a JavaScript. Wiele
definicji kodu JavaScript jest zwykle umieszczanych na stronie HTML pomiędzy
znacznikami <HEAD> i wywoływanych w części <BODY>, a PHP jest zwykle używany
w tej drugiej części.

Istnieją przypadki, kiedy nie chcemy wyłączać trybu PHP. W takich sytuacjach należy
użyć wyrażeń PHP echo lub print, aby wysłać kod JavaScript.
<?php
echo("Tutaj znajduje się kod PHP");
echo("<SCRIPT ŁANGUAGE=\"JavąScript\">\n");
echo("<!-- Ukryj dla przeglądarek nieobsługujacych JavaScript\n");
echo("document.write(V'Oddzielenie skryptów serwera i klienta jest dobrym
^rozwiązaniem.\\n\")");
echo("// koniec ukrywania -->\n");
echo("</SCRIPT>\n");
echo("Kolejny blok PHP");
?>

Styl taki jest również prawidłowy, ale jest dużo trudniejszy do zrozumienia. Jeżeli nie
jesteś doświadczonym programistą, powinieneś ograniczać stosowanie takiego stylu
tylko do sytuacji, w których wywołujesz predefmiowane funkcje JavaScript, np. zda-
rzenie onSubmit.

Możesz mieć kłopoty, stosując znaczniki w stylu JavaScript (na przy-


kład <script ianguage="PHP"» do oznaczania fragmentów kodu
PHP — analizator PHP może pogubić się przy rozpoznawaniu, który
znacznik </script> należy do którego znacznika <script>.

Pamiętaj o oznaczaniu cudzysłowów w sekcjach JavaScript tworzonych


za pomocą funkcji echo i print. Spójrz do 3. wiersza poprzedniego
przykładu kodu.
444________________________________Część III » Techniki zaawansowane

Kiedy używać JavaScript


JavaScript nie poprawi zbytnio strony, ale jest szybszy w niektórych działaniach, po-
zwala również na zrealizowanie niektórych efektów, których nie mógłbyś łatwo utwo-
rzyć za pomocą PHP. Miejsca, w których powinieneś przemyśleć zastąpienie lub
rozszerzenie PHP o JavaScript, to na przykład:
+ proste obliczenia w formularzach i kalkulatorach (na przykład podsumowanie
wartości wózka na zakupy, kalkulator oprocentowania);
* integracja z przeglądarką;
* prosta kontrola poprawności w formularzach (na przykład sprawdzenie, czy ad-
res e-mail posiada znak @);
* nawigacja po witrynie (rozwijalne menu);
** otwieranie dodatkowych okien (ostrzeżenia, okna przeszukiwania);
* obsługa zdarzeń myszy.

PHP jako koło zapasowe do JavaScript


Odwrotną stroną naszych porad „gdzie użyć JavaScript" jest fakt, że PHP może pomóc
w uszczelnieniu dziur w JavaScritpt. Czasami możesz tworzyć niezależne metody ob-
sługi niektórych zadań zarówno w postaci skryptu wykonywanego na kliencie, jak
i skryptu serwera. Jeżeli przeglądarka użytkownika posiada włączoną obsługę Java-
Script — może on skorzystać z szybszej metody. Jeżeli JavaScript nie zadziała, nie bę-
dziesz musiał wstydzić się, że witryna nie realizuje zadań.

Świetnym przykładem jest dwutorowo zrealizowane menu pozwalające na nawigację po


witrynie. JavaScript realizuje natychmiastowe przekierowanie, natomiast w przypadku
przeglądarek bez JavaScript PHP pozwala na osiągnięcie tego samego celu po nieco
dłuższym oczekiwaniu. Trik ten korzysta z metod obsługi zdarzeń w JavaScript (na przy-
kład onChange) i działania na formularzach bez potrzeby wysyłania ich do serwera.
Przycisk Wyślij został więc zarezerwowany tylko do użycia w PHP.
<HTML>
<HEAD>
<TITLE>Menu nawigacji </TITLE>
<SCRIPT LANGUAGE="JavaScript">
<! --
function Browse(form, i)
{
var site = form.elements[i].selectedlndex;
if (site>0)
{
top.location = form.elements[i].options[site].value
}
}
II —>
</SCRIPT>
</HEAD>
Rozdział 27. » PHP i JavaScript_____________________________________445

<BODY >
<FORM METHOD="post" ACTION="redirect.php">
<SELECT NAME="category" onChange="Browse(this.forra,O)">
<OPTION SELECTED VALUE=0>Wybierz rodząj</OPTION>
<OPTION VALUE="desktop.php">Komputery</OPTION>
<OPTION VALUE="laptop.php">Laptopy</OPTION>
<OPTION VALUE="monitor.php">Monitory</OPTION>
<OPTION VALUE="input.php">Urz. wejściowe</OPTION>
•cOPTION VALUE="storage.php">Pamięci masowe</OPTION>
</SELECT>
<INPUT TYPE="submit" value="Wyślij">
</FORM>
</BODY>
</HTML>

Plik o nazwie redirect.php posiada tylko jeden wiersz:


<?php header("Location: Scategory/");?>

Możesz również skorzystać z takiego podziału zadań w przypadku sprawdzania po-


prawności formularzy. Jeżeli JavaScript jest włączony, możesz za jego pomocą spraw-
dzić, czy kod pocztowy ma 5 cyfr, numery telefonu mają 10 cyfr, adresy e-mail mają
zarówno znak @, jak i '.'. Jeżeli JavaScript jest wyłączony, możesz za pomocą małego
skryptu PHP sprawdzić to samo po wysłaniu formularza przez użytkownika i zwrócić
formularz z ostrzeżeniami w przypadku odnalezienia błędnych wartości.

Kontrola formularzy przez JavaScript powinna być traktowana jako


szybkie udogodnienie, nigdy zaś jako główne narzędzie kontrolne.
Więcej szczegółów na ten temat znajduje się w rozdziale 31. „Bezpie-
czeństwo i kryptografia".

Innym rodzajem operacji na formularzach jest arytmetyka. Tutaj również możesz połą-
czyć JavaScript z PHP w celu zabezpieczenia się z obu stron.

JavaScript statyczny kontra dynamiczny


Statyczny JavaScript opisywany dotychczas w tym rozdziale jest użyteczny w wielu apli-
kacjach, ale trzeba go niestety modyfikować ręcznie. Jeżeli zdecydujesz się dodać stronę
z programami do twojej witryny, musisz pamiętać, aby dodać odpowiednią pozycję w li-
ście rozwijalnej. Możesz odpowiedzieć — nie ma problemu — ale takie błahostki stają się
pożerającymi czas kłopotami, jeżeli tworzysz dużą i często odwiedzaną witrynę.

Za pomocą PHP i bazy danych można automatycznie uaktualniać niektóre fragmenty


kodu JavaScript — można powiedzieć, dynamicznie.

Zmienimy poprzedni przykład, aby skorzystać ze ściślejszej współpracy klient-serwer:


<HTML>
<HEAD>
<TITLE>Menu nawigacji</TITLE>
<SCRIPT LANGUAGE="JavaScript">
< ! --
function Browse(form, i)
446___________________________________Część III * Techniki zaawansowane

{
var site = form.elements[i].selectedlndex;
if (site>0)
(
top.location = form.elements[i].options[site].value
1
)
// —>
</SCRIPT>
</HEAD>

<BODY >
<FORM METHOD="post" ACTION="redirect.php">
OELECT NAME="category" onChange-"Browse(this.form,0)">
<OPTION S E L E C T E D VALUE=0>Wybierz r o d z a j < / O P T I O N >
<?php
mysql_connect("localhost", "user", "password");
mysql_select_db("site_db");
Squery = "SELECT filename, ray_text FROM categories WHERE display=l";
Sresult = mysql_query(Squery);
while ( list {$f ilename, $my_text) = mysql_fetch__array (Sresult) )
{
print("<OPTION VALUE=\"$filename\">$my_text</OPTION>\n");
)
?>
</SELECT>
<INPUT TYPE="submit" value="Wyślij">
</FORM>
</BODY>
</HTML>

Niewątpliwie zorientowałeś się, że takiej techniki można użyć również w przypadku


prostego formularza JavaScript (używając zdarzenia onSubmit zamiast onChange).
Pozwala to tworzyć bardziej elastyczne funkcje JavaScript dzięki zmienianiu przez PHP
wartości zmiennych w funkcji, zanim zostanie wysłana do przeglądarki. Jeżeli chcesz,
możesz używać PHP do produkowania kodu JavaScript przy użyciu zmiennych z róż-
nych źródeł danych.

Dynamiczna generacja formularzy


Możesz jeszcze bardziej rozwinąć ten sposób programowania, tworząc serię dynamicz-
nych list rozwijalnych, które zmieniają się w zależności od poprzednio wprowadzonych
wartości. PHP pobiera dane z bazy i ładuje je do strony HTML, a JavaScript decyduje,
które dane powinny być widoczne w różnych okolicznościach.

W tym przykładzie będziemy chcieli pomóc użytkownikom odszukać dane na temat róż-
nych samochodów. Lista modeli samochodów wyprodukowanych przez wszystkich pro-
ducentów jest dosyć druga, zbyt długa nawet dla dobrze zaprojektowanej listy rozwijalnej.
Dodatkowo nazwy samochodów są fonetycznie podobne. Jedyną możliwością logicznego
zawężania wyboru jest umieszczenie listy producentów, po wybraniu określonego modelu
ograniczenie ich listy tylko do wyprodukowanych przez wybranego producenta.

Potrzebna nam tabela bazy danych wygląda następująco (nie jest to odpowiedni projekt
relacyjnej bazy danych, ale chcemy skupić się na JavaScript, a nie na bazie danych):
Rozdział 27. » PHP i JavaScript_____________________________________447

1 Producent 1 Model 1

1 Audi 1 A4 1
1 Audi A6 1
| Audi A8 l
1 Audi 1 Quattro 1
1 Chrysler I Cirrus |
1 Chrysler 1 Concorde 1
1 Chrysler 1 PT Cruiser 1
1 Toyota 1 Camry 1
| Toyota 1 Corolla !
1 Toyota 1 Rav4 1

Używając tej bazy danych i skryptów serwera w PHP będziesz ograniczony do dwóch
możliwości wyboru. Możesz stworzyć jedną dosyć dużą listę (albo w postaci listy roz-
wijalnej, albo w postaci strony) producentów i modeli, albo pokazać użytkownikowi
dwa kolejne formularze. Jeżeli dodamy do tego JavaScript, możemy na górze strony
umieścić dwie listy rozwijalne, zmieniając zawartość drugiej listy na podstawie wyboru
w pierwszej.

Nasz projekt podwójnej listy rozwijalnej bazuje na sprytnym kodzie JavaScript, którego
autorem jest Andrew King. Dostępny jest pod adresem www.webreference.com na wa-
runkach licencji Gnu.

Użyliśmy PHP, aby podłączyć się do bazy danych i przepisać dane do dwuwymiarowej
tablicy, na której pracuje kod JavaScript. JavaScript jest odpowiedzialny za zachowanie
się strony.
<HTML>
<HEAD>
<META NAME="save" CONTENT="history">
<STYLE>
.saveHistory (behavior : url ( łdef aultftsavehistory) ; (
</STYLE>

<SCRIPT LANGUAGE="JavaScript">

v=false;
//-->
</SCRIPT>

<SCRIPT LANGUAGE-"JavaScriptl . 1">


< ! --
if (typeof(Option)+"" != "undefined") v=true;
//-->
</SCRIPT>

<SCRIPT LANGUAGE="JavaScript">
< !--
// Uniwersalne menu zależne - Autor Andrew King
// Zmodyfikowane przez Joyce Park
// Dostępne na zasadach licencji Gnu
//
// Universal Related Select Menus - cascading popdown menus
// by Andrew King. vi.34 19990720
// Copyright (c) 1999 internet.com LLC. All Rights Reserved.
// Modified by Joyce Park 20000703
//
// This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later
448___________________________________Część III » Techniki zaawansowane

// version.
//
// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free
// Software Foundation, Inc., 59 Temple Place, Suite 330,
// Boston, MA 02111-1307 USA
//
// Originally published and documented at www.webreference.com
// see www.webreference.com/dev/menus/intro2.html for changelog

if(v)(a=new Array(22);)

function getFormNum (formName} {


var formNum =-1;
for (1=0; i<document. forms, length; i++) {
tempForm = document.forms[i];
if (formName == tempForm) (
formNum = i;
break;
)
}
return formNum;
)
function jmp(form, elt}
// Pierwszym parametrem jest referencja do formularza.
{
if (form != null) (
with (form.elements(elt]) {
if (0 <= selectedlndex)
location = options[selectedlndex].value;
)
)
(
var catslndex = -1;
var itemslndex;

if (v) { // Poprawka do Netscape 2


function newCat(){
catslndex++;
a[catslndex] = new Array(};
itemslndex - 0;
}
// Andrew nazwał tę funkcje "O", prawdopodobnie od słowa "Opcje"
// Nie jest to zero, zarówno tutaj, jak i w tablicy poniżej!
function O(txt,url) {
a[catslndex][itemslndex]=new myOptions(txt,url);
itemslndex++;
)
function myOptions(text,value){
this.text = text;
this.value = value;
)

// Wypełnienie tablicy
<?php
mysql_connect ("localhost", "db__user" ) ;
mysql_select_db("auto_db");
// Pobranie producentów
Smake_query = "SELECT DISTINCT make FROM cars";
$make_result = mysql_query($make_query);
\
Rozdział 27. » PHP i JavaScript_____________________________________449

$i = 0;
while ($make_row = mysql_fetch_array($make_result)) {
$make[$i] =• Smake_row[0] ;
// Wypełnianie tablicy modelami każdego producenta
echo "newCat();\n";
$model_query = "SELECT model FROM cars WHERE make = '$make[$i)' ORDER BY
^model";
$model_result = mysql_query($model_query);
while(list(Smodel) = mysql_fetch_array($model_result)) (
echo "0 ( V'SmodeiV , V'/Smodel. php\" ) \n" ;
)
echo "\n";
$i + + ;
)
?>
} // if (v)

function relate(formName,elementNum,j) {
if(v)(
var formNum = getFormNum{formName);
if (formNum>=0) (
formNum++; // Referencja do następnego formularza
with (document.forms[forraNum].elements[elementNum]) {
for(i=options.length-1;i>0;i—) options[i] = null; // zerowanie w
odwrotnej kolejności (obejście błędu)
for(i=0;i<a[j].length;i++)(
options[i] = new Option(a[j][i].text,a[j][i].value);
)
options[0].selected = true;
}
t
} else (
jmp(formName,elementNum);
)
)

// Poprawka działania klawisza Wstecz w IE4+


// Więcej komentarzy na ten temat pod adresem
// www.webreference.com
function lEsetupO{
if(!document.all) return;
IE5 = navigator.appVersion.indexOf("5.")!=-!;
if(!IE5) {
for (i = 0;i<document.forms.length;i++) {
document.forms[i).reset ();
)
}
)
window.onload = lEsetup;

// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="ttff ffff ">

<CENTER>
<TABLE BGCOLOR="#DDCCFF" BORDER="0" CELLPADDING="8" CELLSPACING="0">
<TR VALIGN="TOP">
<TD>Wybierz producenta:<BR>
<FORM NAME="fl" METHOD="POST" ACTION="redirect.php" onSubmit="return false;">
<SELECT NAME="ml" ID="ml" CLASS=saveHistory
onChange="relate(this,form,0,this.selectedlndex)">
<?php
while (list ($key, Sval) =• each ($make) ) (
echo "<OPTION VALUE=\"/Sval.php\">$val</OPTION>\n";
)
?>
</SELECT>
<INPUT TYPE=SUBMIT VALUE="Go" onClick="jmp(this.form,0);">
450___________________________________Część III » Techniki zaawansowane

</FORM>
</TD>

<TD BGCOLOR="łFFFFFF" VALIGN-MIDDLEXB>— -&gt; < / B X / T D >

<TD>Wybierz model:<BR>
<FORM NAME="f2" METHOD="POST" ACTION="redirect.php" onSubmit="return false;">
<SELECT NAME="m2" ID="m2" CLASS=saveHistory onChange="jmp(this.form,0)">
// To są wartości zajmujące miejsce przy pierwszym załadowaniu
// strony. Nie zmieniają się przy zmianie wartości w formularzu
// Jeżeli usuniesz je, formularz nadal będzie działał, ale
// druga lista rozwijalna będzie pusta do chwili zmiany.
// Wartości te mogą być generowane dynamicznie, ale chcieliśmy
// pokazać je w miejscu, w którym muszą się znajdować.
<OPTION VALUE="/A4.php">A4</OPTION>
<OPTION VALUE="/A6.php">A6</OPTION?
<OPTION VALOE="/A8.php">A8</OPTION>
<OPTION VALUE="/Quattro">Quattro</OPTION>
</SELECT>
<INPUT TYPE=SUBMIT VALUE="Go" onClick="jmp(this.form,0);">
<INPUT TYPE="hidden" NAME="baseurl" VALUE="http://localhost">
</FORM>
</TD>
</TR>
</TABLEX/CENTER>

</BODY>
</HTML>

Jeżeli zmienisz lub dodasz jakieś dane w bazie danych, automatycznie zostanie zmie-
niona część JavaScript. Dynamiczna integracja nowych danych powoduje, że jest to
świetne narzędzie pozwalające ograniczyć do minimum prace nad utrzymaniem strony.

Przesyłanie danych z JavaScript do PHP


Na koniec zamkniemy pętlę danych, przesyłając dane z powrotem do PHP z użyciem
JavaScript. Poniższy kod używa JavaScript do zaznaczenia co najmniej jednego pola
wyboru. Przesyła on dodatkowo tablicę do skryptu PHP. Zdecydowaliśmy się tutaj na
użycie ramek w celu maksymalnego przyspieszenia zmian. Dla pełnej przejrzystości
wpisaliśmy ręcznie wszystkie wartości, zamiast odczytywać je ze źródła danych.
sandwich_frames.html
<HTML>
<HEAD>
<FRAMESET ROWS="50%, 5 0 % " FRAMEBORDER="no" BORDER=0>
<FRAME SRC="main.html" NAME="main" SCROLLING="auto">
<FRAME SRO"results.php" NAME="results" SCROLLING="auto">
</FRAMESET>
</HEAD>
<BODYX/BODY>
</HTML>

main.html
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<! —

function deselectAHOthers (boxVals) (


for (var x = l; x < boxVals. length; x++) {
boxVals[x].checked=false;
}
)
Rozdział 27. » PHP i JavaScript_____________________________________451

function confirmOne(boxVals} {
var count = 0;
for (var x = 1; x < boxVals.length; x++) {
if (boxVals[x].checked == false) {
count++;
1
(
if (count == (boxVals.length - 1)) {
boxVals[0].checked = true;
} else (
boxVals[0].checked = false;
)
)
function toArray(boxVals) {
for (var x = 0; x < boxVals.length; x++) {
var valArray = boxVals[x].name+"[]";
boxVals[x].name = valArray;
)
t

// -->
</script>
</HEAD>

<BODY BGCOLOR=łFCFCFO onLoad="document.selector.submit();">

<TABLE CELLPADDING=20>
<TR>
<TD VALIGN="top">
< B > Z a m a w i a m kanapkę z . . . < / B >
<BRXBR>
<FORM N A M E = " s e l e c t o r " T A R G E T = " r e s u l t s " METHOD="post" A C T I O N = " r e s u l t S . p h p " >
< B > D o d a t k i ( z a z n a c z jeden lub wiece j ) </BXBRXBR>
< I N P U T TYPE="checkbox" n a m e = " f i l l i n g " value="wszystko" checked
o n d i c k = " d e s e l e c t A l l O t h e r s (document . s e l e c t o r , f i l l i n g ) ;
confirmOne(document.selector.filling); toArray(document.selector.filling);
s u b m i t ( ) ; " > Wszystko
<BR>
<INPUT TYPE="checkbox" name="filling" value="indyk"
onClick="confirmOne(document.selector.filling);
toArray(document.selector.filling); submit));"> Indyk
<BR>
<INPUT TYPE="checkbox" name="filling" value="pieczen"
onClick="confirmOne(document.selector.filling);
toArray(document.selector.filling); submit();"> Pieczeń
<BR>
<INPUT TYPE="checkbox" name="filling" value="wedzonka"
onClick="confirmOne(document.selector.filling);
toArray(document.selector.filling); submit(};"> Wędzonka
<BR>
<INPUT TYPE="checkbox" name="filling" value="jajko"
onclick="confirmOne(document.selector.filling);
toArray(document.selector.filling); submit();"> Jajko
<BR>
</TD>
< T D VALIGN="top"XBRXBR>
<B>Sery</B><BRxBR>
<SELECT NAME="cheese" o n C h a n g e = " s u b m i t ( ) ; " >
<OPTION VALUE="bez sera">Bez sera</OPTION>
<OPTION VALUE="cheddar">Cheddar</OPTION>
<OPTION VALUE="Swiss">Swiss</OPTION>
<OPTION VALUE="camembert">Camembert</OPTION>
<OPTION VALUE="bleu">Blue</OPTION>
<OPTION VALUE="twarożek">Twarożek</OPTION>
</SELECT>
<BRXBR>
</TD>
<TD VALIGN="top"XBRXBR>
<B>Chleb</BXBRXBR>
452___________________________________Część III » Techniki zaawansowane

<SELECT NAME="bread" onChange="submit();">


<OPTION VALUE= 'biały">Biały</OPTION>
<OPTION VALUE= 'ciemny">Ciemny</OPTION>
<OPTION VALUE= 'ryżowy">Ryżowy</OPTION>
<OPTION VALUE= 'kajzerka">Kaizerka</OPTION>
<OPTION VALUE='cebulowa">Bułka cebulowa</OPTION>
</SELECT>
</FORM>
<BRXBR>
</TD>
</TRX/TABLE>
</BODY>
</HTML>

results.php
<HTML>
<HEADX/HEAD>

<BODY BGCOLOR=#666680 TEXT=łtf f f f f f >


<TABLE CELLPADDING=30XTRXTD> •
<B>Wyniki</BXBRXBR>
<?php
if(Sfilling) (
if(is_array(Sfilling)) (
reset(Sfilling);
while(list($key, $value) = each(Sfilling)) {
echo("$value<BR>\n");
)
} else {
echo(Sfilling);
(
t
?>
<BRXBRX/TD>
<TD VALIGN=topXBRXBR>
<B>Ser</BXBRXBR>
<?php echo(Scheese); ?>
<BRXBRX/TD>
<TD VALIGN=top><BRXBR>
<B>Chleb</BXBRXBR>
<?php echo(Sbread); ?>
</TDX/TR>
</TABLE>
</BODY>
</HTML>

Formularz ten nie robi zbyt wiele, ale jego celem było pokazanie sposobu przesyłania
danych z klienta do serwera i odwrotnie. Przedstawia również inne interesujące efekty,
jakie może uzyskać programista PHP eksperymentując z JavaScript.

Podsumowanie
JavaScript jest językiem skryptowym, wykonywanym w przeglądarce klienta, który jest
bardzo efektywny w wielu zastosowaniach niewymagających interakcji z serwerem. Nie
wszyscy chcą używać JavaScript z powodu problematycznej użyteczności i zagadnień
bezpieczeństwa. Jednak ci, którzy się nań zdecydują się, połączenie języków skrypto-
wych serwera i klienta zaowocuje interesującą kombinacją funkcji.
Rozdział 27. » PHP i JavaScript_____________________________________453

PHP i JavaScript mają różne notacje obiektowe. JavaScript używa tak zwanej notacji
„z kropką", natomiast PHP używa stylu C++ „ze strzałką". JavaScript jest językiem
obiektowym, a PHP traktuje obiekty jako funkcję dodatkową. Dobrą no winą jest to, że
nigdy nie pomylisz obiektów PHP i JavaScript. Złą, że nie ma dostępu do tych samych
obiektów za pomocą obu języków.

Możliwe jest stworzenie jakiejś funkcji zarówno z wykorzystaniem serwera, jak i klienta.
Użytkownicy z przeglądarkami obsługującymi JavaScript mogą korzystać z dużej szybko-
ści i wygody, inni nie stracą dostępnych funkcji. Pozwala to na użycie JavaScript, omija-
jąc jego istotną wadę, jakąjest uniemożliwienie pracy części użytkowników.

Najcenniejszą funkcją, jaką PHP może podarować JavaScript, jest możliwość korzysta-
nia z baz danych, co w wyniku daje „dynamiczny JavaScript". JavaScript to technologia
czysto kliencka, która nie może korzystać z danych z bazy w celu wygenerowania za-
wartości strony. Bez pomocnika na serwerze, na przykład PHP, JavaScript ręcznie mo-
dyfikować przy każdej zmianie danych. Zdolność PHP do przekazania aktualnych
zmiennych powoduje, że utrzymanie kodu JavaScript jest nieco mniej pracochłonne.
454________________________________Część III » Techniki zaawansowane
Rozdział 28.
E-mail
W tym rozdziale:
+ Informacje na temat architektury e-mail
* Pobieranie poczty za pomocą PHP
* Wysyłanie poczty za pomocą PHP
4 Więcej na temat aplikacji pocztowych
* Problemy przy korzystaniu z poczty

W tym rozdziale zajmiemy się użyciem PHP (i w niektórych przypadkach bazy danych)
do wysyłania i odbierania poczty. Zakładamy jedynie podstawową znajomość systemu
e-mail i protokołów POP, IMAP oraz SMTP.

Informacje na temat architektury e-mail


E-mail jest jedną z najczęściej używanych aplikacji internetowych, istnieje już wiele lat
właściwie w niezmienionej formie. Jednak z liczby pytań pojawiających się na listach
dyskusyjnych na temat PHP wynika, że do tej pory istnieje kilka nie do końca zrozu-
miałych zagadnień. Dzięki coraz tańszym połączeniom, dostępności bezpłatnych syste-
mów operacyjnych dla serwerów oraz olbrzymiej liczbie domen korporacyjnych
pojawia się możliwość posiadania własnego serwera poczty.

Nie ma tu miejsca na wyjaśnienie wszystkich szczegółów systemu e-mail. Jeżeli po


przeczytaniu naszego streszczenia będziesz zainteresowany tym tematem, powinieneś
natychmiast przejrzeć zawartość witryny International Mail Consortium, która jest do-
stępna pod adresem http://www.imc.org. Jest tam wyjaśniona dokładnie każda informa-
cja, każdy potrzebny administratorowi szczegół na temat poczty internetowej. Zapoznaj
się z naszym krótszym wyjaśnieniem.
456________________________________Część III » Techniki zaawansowane

Model systemu e-mail


W celu przedstawienia znacznej liczby zaangażowanych w system części zbudujemy
prosty model serwera pocztowego.

W objaśnieniach tych będziemy używać w większości terminów pochodzących z sys-


temu Unix, ponieważ powstały wcześniej, niż gdy do gry weszły Microsoft i Lotus. Ex-
change Server jest nawet podobnie zbudowany — posiada nawet demon o nazwie
„sendmail" — jednak nie możemy być tego całkowicie pewni, ponieważ nie możemy
zajrzeć mu pod maskę. Dostawcy firmowego oprogramowania mają manierę tworzenia
własnej terminologii, zamiast użyć nazw, które wszyscy już dobrze znają. Praktyka ta
znacznie utrudnia porównanie tych systemów z innymi na różnych platformach. Dodat-
kowo wiele firmowych systemów pocztowych jest wbudowanych w systemy pracy gru-
powej, co jest jeszcze bardziej utrudnia porównanie. Z tych powodów dużo łatwiej
poznać serwery pocztowe dla Unixa.

Wymieniamy nazwy specyficznych produktów, abyś mógł łatwo je odszukać i dowiedzieć


się więcej na ich temat. Nie jest to z naszej strony żadna rekomendacja. Zdecydowali-
śmy się wymienić tylko te produkty, które są dostępne bez konieczności wydawania
pieniędzy.

Podstawowymi składnikami naszego modelu systemu pocztowego są:


* serwer TCP/IP;
** Maił Transfer Agent (MTA), zwany również serwerem SMTP;
* kolejka poczty;
* Mail User Agent (MUA, albo po prostu klient pocztowy);
* program pobierający pocztę (serwer POP/IMAP);
+ zarządca list wysyłkowych (MLM).

Przyjrzyjmy się tym częściom dokładniej. Aby łatwiej zapamiętać zadania poszczegól-
nych części, spróbujemy użyć metafory: rezydencja Mail Server.

Serwer TCP/IP
Dobrym sposobem na przedstawienie serwera TCP/IP jest porównanie go do odźwier-
nego, odpowiadającego na pukanie do drzwi do rezydencji Mail Server. Właściwie je-
żeli porównasz każdy port do drzwi, być może bardziej trafne było by porównanie go
do ochroniarza, który siedzi w sterowni i za pomocą zestawu kamer kontroluje wiele
alejek wejściowych i wyjściowych. Jednak prostsze i bardziej obrazowe jest porówna-
nie do staroświeckiego odźwiernego przy drzwiach wejściowych.

Odźwierny jednak nie rozmawia z gośćmi. Jego zadaniem jest tylko otwarcie drzwi, je-
żeli ktoś puka, i przywołanie właściwej osoby (nazwijmy ją lokajem), który obsłuży go-
ścia. Rolą tej osoby jest dowiedzenie się, czego gość chce.
Rozdział 28. » E-mail___________________________________________457

Otwieranie drzwi nie jest problemem — trzeba wiedzieć, że istnieją drzwi, które można
otworzyć. Serwer TCP/IP posiada listę różnych usług, za które jest odpowiedzialny,
i odpowiada na każde żądanie, wywołując odpowiedni demon — w przypadku poczty
sendmail albo serwer POP/IMAP. Pozwala to na oszczędniejszą gospodarkę zasoba-
mi, ponieważ jeden demon monitoruje wszystkie wywołania, inne demony są urucha-
miane tylko, gdy są potrzebne.

Do powszechnie znanych serwerów TCP/IP należą:


* GNU inetd, super serwer TCP/IP zwykle używany w podstawowych instala-
cjach systemów Unix (http://ww\v.gnu.org/software/inetutils/inetutils.html);
* Xinetd, bezpieczny zamiennik inetd (http://www.synack.net/xinetd/)',
* Tcpserver, którego autorem jest Dan Bernstein, często używany z qmail (http://
cr.yp. to/ucspi-tcp. html).

Maił Transfer Agent (MTA), czyli serwer SMTP


Pocztowy agent transferowy (MTA) jest sercem każdego serwera pocztowego i częścią,
której działanie najbardziej uprościliśmy w naszym opisie.

Najważniejszym zadaniem MTA jest pobieranie poczty z innego serwera SMTP i do-
starczanie jej do kolejki poczty dla właściwego adresu. Bierze w tym udział o wiele
więcej części, niż ci się wydaje, ale nie będziemy ich wszystkich tutaj opisywać. MTA
zbiera dodatkowo pocztę wychodzącą i próbuje wysłać ją do innych serwerów SMTP.

W przybliżeniu MTA pracuje na stanowisku lokaja w rezydencji Mail Server. Jest


wzywany przez odźwiernego, aby zająć się pocztą. Pyta on gościa (oczywiście odpo-
wiednio grzecznie): „Kim jesteś i czego chcesz". Jeżeli gościem jest na przykład znany
spammer, lokaj może odmówić przyjęcia jakiegokolwiek listu z tego adresu. Jeżeli gość
nie zostanie rozpoznany jako spammer, lokaj sprawdzi kopertę w celu ustalenia, czy ad-
resat mieszka w rezydencji Mail Server. Jeżeli nie, napisze na kopercie „Nieprawidłowy
adresat" i wrzuci go do worka z wychodzącą pocztą. Jeżeli adres zostanie rozpoznany,
lokaj położy list na srebrnej tacy, zaniesie do pokoju adresata i tam go pozostawi.

Protokół SMTP to język skryptu, według którego jeden serwer pyta się drugiego „Kim
jesteś i czego chcesz", wygląda podobnie jak poniższy (nazwa komputera wysyłającego
i odbierającego):
220 receipthost ESMTP
HELO
250 receipthost
MAIL From:<sender@sendhost>
250 ok
RCPT To:<receiver@receipthost>
250 ok
DATA
345 Go ahead
Body of message.

250 Message accepted for delivery


QUIT
221 receipthost closing connection
458________________________________Część III » Techniki zaawansowane

Znanymi programami typu MTA są:


Sendmail http://www.sendmail.org;
Qmail http://www.qmail.org;
Zmailer http://www.zmailer. org.

Kolejka poczty
Srebrna taca, na której lokaj przenosi listy, jest odpowiednikiem kolejki pocztowej lub
skrzynki pocztowej. Różne systemy poczty używają rozmaitych rodzajów skrzynek
pocztowych. Główną różnicą na omawianym przez nas poziomie jest to, że nowa prze-
syłka jest umieszczana na końcu bardzo długiej kolejki (jak kartka papieru z tekstem
wszystkich przesyłek, jakie dotychczas otrzymałeś, zapisanych w kolejności otrzymy-
wania) lub staje się plikiem tekstowym w katalogu.

Kilka znanych formatów skrzynek pocztowych to:


mbox Używany przez wiele klientów systemu Unix;
maildir Ściśle związany z qmail;
mbx Firmowy format firmy Microsoft.

Mail User Agent, czyli lokalny klient pocztowy


Przesyłka leży na srebrnej tacy w pokoju adresata, którego nie ma w tej chwili u siebie.
Gdy mieszkaniec rezydencji Mail Server zdecyduje się przeczytać swoją pocztę, wyśle
pokojówkę, aby przyniosła wszystkie listy zostawione przez lokaja. Pokojówka to lo-
kalny klient pocztowy.

Popularnymi programami typu MUA są:


Pine http://www. Washington, edu/pine;
Elm http://www. instinct, or g/elm;
Mutt http://www.mutt.org.

W wielu przypadkach wybór klienta poczty jest ściśle związany z wyborem MTA oraz
skrzynki pocztowej. Na przykład Mutt ułatwia korzystanie z formatu skrzynki maildir,
używanego przez qmail i dlatego wydaje się najlepszy dla użytkowników systemu Qmail.

Program pobierający pocztę (serwer POP/IMAP)


Wysyłanie pokojówki po pocztę działa świetnie w przypadku, gdy wszyscy zaintereso-
wani są w rezydencji. A jeśli mieszkaniec rezydencji Mail Server dużo podróżuje (pa-
miętaj, że podróż może stanowić również spacer do sąsiedniego biurka, jeżeli nie jest
ono obsługiwane przez ten serwer poczty)? W jaki sposób można wtedy dostać pocztę?
Rozdział28. » E-mail___________________________________________459

Wysyła się wtedy co jakiś czas żądanie do rezydencji Mail Server, aby inny lokaj prze-
słał wszystkie listy zgromadzone do tej pory na srebrnej tacy. Lokaj ten nie przyjmuje
listów —jego zadaniem jest tylko ich przesyłanie.

Ten drugi lokaj musi być bardzo sumienny. Lokaj SMTP przesyła i odbiera tylko listy
adresowane do mieszkańców, ale przecież każdy może wysłać żądanie przesłania
poczty w jakieś odległe miejsca. Dlatego lokaj numer dwa zna tajne hasła wszystkich
mieszkańców rezydencji Mail Server i nie prześle żadnego listu, jeżeli nie otrzyma wła-
ściwego hasła.

Przesyłki e-mail można czytać w zdalnych klientach pocztowych — wiele z nich jest
bazuje na witrynie WWW, istnieją także dostępne w postaci samodzielnych bezpłatnych
programów:
Microsoft Outlook Express http://www.microsoft.com/windows/IE;
Netscape Communicator http://www. netscape, com/download/index, html;
Qualcomm Eudora Light http://www.eudora.com;
Hotmail http://www.hotmail.com;
AOL mail client http://www.aol.com.

Jedną z konsekwencji stworzenia programów odczytujących pocztę jest to, że teraz na


każdej platformie można pobierać pocztę z serwera Unix. Programy typu MUA są
ograniczone tylko do klientów uniksowych (najlepiej działających lokalnie).

Istniej ą różne protokoły służące do pobierania poczty oraz olbrzymia liczba produktów
(często różniących się) korzystających z tych protokołów. Najbardziej popularnymi są
POP3 i IMA P.

Serwer POP sprawdza nazwę użytkownika, hasło i położenie kolejki poczty, porównu-
jąc odpowiedzi użytkownika z zapisanymi u siebie prawidłowymi wartościami. Dialog
z klientem wygląda następująco:
user peter
+OK
pass rabbit
+OK
data

Jeżeli istnieją nowe listy, zostają wysłane do klienta. Niektóre systemy POP mogą zo-
stać tak skonfigurowane, aby zapamiętywać kopie listów wysłanych do klienta, niektóre
pozwalają usuwać wiadomości zdalnie, ale większość zapomina o przesyłkach zaraz po
ich wysłaniu. Użytkownik może dużo czasu spędzić na czytaniu pobranych listów oraz
pisaniu odpowiedzi, potem połączyć się z serwerem SMTP i wysłać odpowiedzi.

Znanymi serwerami POP są:


Qualcomm Qpopper http://www.eudora.com/qpopper/index.html;
Qmail-pop3d http://www.qmail.org.
460___________________________________Część III » Techniki zaawansowane

Większość znanych serwerów IMAP pozwala również na korzystanie z protokołu


POP3. Funkcje POP dla IMAP działają również jako funkcje POP.

IMAP jest nowszym i lepszym protokołem pobierania poczty. Zamiast usuwać wiado-
mości po pobraniu, IMAP symuluje lokalne działanie klient-serwer. Potencjalną wadą
jest to, że każdy klient musi być podłączony do serwera IMAP w czasie, gdy użytkow-
nik czyta pocztę i odpowiada na nią, więc proces ten wymaga większych zasobów.
Serwer IMAP jest również nieco trudniejszy w instalacji, konfiguracji i utrzymaniu.

Popularnymi serwerami IMAP są:


Washington IMAP http://www.washington.edu/imap;
Curus http://ags. web. emu. edu/cynis.

Istnieje również kategoria programów nazywanych narzędziami pobierania danych.


Najbardziej znanym jest fetchmail, którego autorem jest Eric Raymond (http://www.tii-
xedo.org/~esr/fetchmaif). Jest on niezmiernie przydatny w przypadku połączeń mode-
mowych. Pozwala on na stosowanie protokołów POP, IMAP i innych.

Zarządca list wysyłkowych


Dodatkowym programem jest zarządca list wysyłkowych. Jest to pakiet oprogramowa-
nia, który pomaga w automatycznym rozsyłaniu dużej ilości korespondencji. Można
pomyśleć o tym programie jako o sekretarce w rezydencji Mail Server. Jej praca polega
na wysyłaniu np. zaproszeń na liczne bale. Musi po prostu napisać wszystkie zaprosze-
nia i wrzucić je razem do worka na pocztę. Obsługuje również odpowiedzi na standar-
dowy zestaw zapytań.

Często spotykanymi programami tego typu są:


Majordomo http://www.greatcircle. com/majordomo;
Ezmlm Dla qmail, http://www.ezmlm.org.

Mamy teraz cały model serwera poczty. Po dokładnym zdefiniowaniu terminologii mo-
żemy przejść do dalszej części rozdziału.

Pobieranie poczty za pomocą PHP


Klienci poczty bazujący na stronie WWW, pobierający pocztę z serwera POP3/IMAP
i wysyłający j ą do serwera SMTP, zajmują coraz większą część rynku. PHP jest jednym
z narzędzi pozwalających na tworzenie tych bardzo użytecznych aplikacji.

Nigdy nie myśleliśmy nawet o napisaniu takiego projektu od podstaw; każdy, kto ma
taki zamiar, na pewno nie potrzebuje naszych porad. Dlatego dyskusja o klientach
pocztowych skupi się na tym, jak zrobić to łatwo i przyjemnie (jak mówi nieśmiertelna
Tina Turner).
Rozdział 28. » E-mail___________________________________________461

Tworzenie przez zaniechanie


Najprostszym sposobem na otrzymanie świetnego klienta POP3 za pomocą PHP jest...
nietworzenie go. Jeżeli nie masz dużo wolnego czasu, przemyśl dokładnie, czy na pew-
no potrzebujesz własnego klienta POP. Bezpłatne serwisy pocztowe praktycznie płacą
za własne usługi, za ich pomocą możesz korzystać z serwerów POP położonych w każ-
dym zakątku świata. Pokazują się również bezpieczne bezpłatne aplikacje pocztowe
WWW, takie jak Hushmail. Możemy więc powierzyć prowadzenie prywatnego konta
e-mail któremuś z większych portali.

Tworzenie przez przykład


Jeżeli masz jakieś niezwykłe potrzeby, wymagające własnego klienta POP w PHP, mo-
żesz skorzystać z klas e-mail, których autorem jest Manuel Lemos. Stworzył on osobne
klasy dla obsługi POP3, SMTP oraz klasę do tworzenia przesyłek MIME. Dorzuć kilka
ładnych obrazków i jesteś już w połowie drogi. Klasy te są dostępne pod adresem:
http://phpclasses. upperdesign. com/browse, html/class/2

Jeszcze lepszym produktem jest wspaniały klient IMAP o nazwie IMP, który jest do-
stępny na warunkach licencji GPL. Autorami są ludzie skupieni w Horde.org:
http://w ww. hordę, org/imp

W chwili obecnej IMP posiada również obsługę POP3 z wykorzystaniem funkcji PHP
imap_open ( ) , ale jest to tylko dodatek. Trudno sobie wyobrazić, w jaki sposób można
ulepszyć ten program. W chwili pisania książki przenoszono go na PHP 4, ale nadal jest
dostępna wersja IMP dla PHP 3.

Tworzenie przez upiększanie


Pamiętaj również, że wiele programów typu open source (włączając wspomniane wcze-
śniej dwa produkty) pozwala na pobranie kodu, wprowadzenie zmian i upiększeń lub
zmianę projektu interfejsu. Jeżeli chcesz stworzyć stronę pocztową, która będzie wy-
glądała jak kartka papieru, ekran telewizora lub mostek USS Enterprise, po prostu uru-
chom GIMP i zabierz się do pracy.

Jest to szczególnie użyteczne dla tych, którzy chcą udostępnić firmowego klienta pocz-
towego przez WWW —jak większość dostawców Internetu. Pamiętaj jednak, że przy-
właszczenie cudzej pracę nie jest w porządku — wystarczy mała informacja w formie
logo lub adresu e-mail.
462___________________________________Część III » Techniki zaawansowane

Wysyłanie poczty za pomocą PHP


Zanim zaczniemy wysyłać pocztę za pomocą PHP, musimy zająć się niewielkim dopra-
cowaniem konfiguracji. Dopiero wtedy PHP będzie mógł poprawnie obsługiwać pocztę.

Konfiguracja Windows
Musisz ustawić dwie zmienne w pliku php. ini:
* SMTP: ciąg zawierający nazwę DNS lub adres IP serwera SMTP, który będzie
wysyłał pocztę z komputera Windows z zainstalowanym PHP. Jeżeli znajduje
się on na tym samym serwerze, należy podać 'localhost';
4 sendmail_f rom: ciąg zawierający adres e-mail domyślnego użytkownika wy-
syłającego pocztę przez PHP (na przykład info@domena.com).

IIS4 posiada wbudowany serwer SMTP. Jest on mniejszy od Exchange


Server; jeżeli nie potrzebujesz funkcji Exchange Server, użyj lepiej IIS4.

Konfiguracja Unixa
Musisz sprawdzić i być może zmienić zawartość jednej zmiennej vi php.ini: sendma-
il_path. Jest to pełna ścieżka do programu sendmail (/usr/sbin/sendmail lub /usr/lib/
sendmail) lub jego zamiennika (/var/qmail/bin/sendmail).

Jeżeli nie używasz programu sendmail i nie zmienisz opcji konfiguracji tak, aby wska-
zywała na właściwy program, przesyłanie poczty może być bardzo powolne. Serwer
będzie próbował uruchamiać kilka kolejnych alternatywnych programów, czekając jakiś
czas na odpowiedź..

W systemie Unix PHP 4 używa użytkownika root jako domyślnego użytkownika pocz-
ty. PHP 3 korzystał z użytkownika PHP (zwykle Nobody), ale wiele serwerów odrzu-
cało pocztę pochodzącą z tego konta.

We wielu systemach, na przykład qmail, nie ma konta poczty dla użyt-


kownika root. Dlatego powinieneś używać adresów „reply-to" i „bounce-
to" przy wysyłaniu każdej wiadomości e-mail.

Funkcja maił
W PHP istnieje tylko jedna funkcja do wysyłania poczty: maił ( ) . Funkcja ta (typu bo-
olean) próbuje wysłać pocztę, korzystając z danych w nawiasach, i zwraca O lub 1.
Rozdział 28. » E-mail___________________________________________463

Najprostszym użyciem tej funkcji (podany adres jest fikcyjny i nie powinieneś go uży-
wać do testowania) jest:
<?php
mail("receiver@receipthost.com", "Przykładowy temat", "Treść komunikatu\r\nz
"^wierszami oddzielonymi znakami nowego wiersza.");
?>

Jest to domyślny i minimalny format: adres i odbiorca, wiersz tematu i treść. W tym
przypadku PHP doda automatycznie linię From: root@sendhost do nagłówka każdej
wiadomości.

Mimo że e-mail istnieje w niezmienionej postaci od kilku dziesięciole-


ci, nadal nie ma uniwersalnego formatu wiadomości, który gwaranto-
wałby, że zostaną przeczytane przez wszystkie programy i serwery
pocztowe. Niektóre programy pocztowe wymagają znaku CR oraz zna-
ku nowego wiersza ("\r\n"). Niektóre nie potrafią obsłużyć wielu ad-
resów oddzielonych przecinkami. Niektóre nie dostarczają przesyłek
bez właściwej daty. Niewiele można zrobić, aby polepszyć tę sytuację.
Trzeba starać się spełnić wszystkie wymagania.

Można również użyć zmiennych zamiast wpisanych na stałe wartości:


<?php
Sadres = "swiety@mikolaj.com";
$temat = "To co chciałbym na święta";
$tresc = "Chciałbym dostać moje przednie zęby.\r\nTwoj Janek";
Smailsend = maił("$adres", "Stemat", "Streść");
print("$sendmail");
?>

Jeżeli chcesz wysłać przesyłkę do wielu adresatów, wpisz ich adresy rozdzialejąc je
przecinkami (nie wszystkie serwery mają możliwość obsługi):
<?php
$mailsend = maił("jan21@polbox.pl, j23@aol.com", "Przykładowy temat", "Treść
^komunikatu\r\nz wierszami oddzielonymi znakami nowego wiersza.");
print("$mailsend");
?>

Wiele osób chciałoby precyzyjniej kontrolować adres, wygląd i format przesyłek e-mail.
Można to zrealizować dzięki dodaniu nagłówków po trzech domyślnych nagłówkach.
<?php
Smailsend = maił("receiver@receipthost.com", "Przykładowy temat", "Treść
"^komunikatu.", "From: me@sendhost\r\nbcc: phb@sendhost\r\nContent-
"^type:text/plain\r\nX-mailer: PHP/". phpversion());
print("$mailsend");
?>

Pole „dodatkowych nagłówków" jest nieco dziwne, ponieważ zawiera kilka typów da-
nych, które normalnie powinny mieć własne pola. Naszym zadaniem nie jest zastana-
wianie się, dlaczego tak jest, tylko podanie rodzaju informacji, jaki możesz umieścić
w tym polu:
* twój adres e-mail;
* adres reply-to lub bounce-to;
464___________________________________Część III » Techniki zaawansowane

* x-mailer i jego wersja;


* wersja MIME,-
+ Content-type;
* Charset (do przypisania używa się =, a nie :);
+ Content-transfer-encoding;

** adresy cc i BCC.

Obecnie wysyłanie załączników jest nieco bardziej skomplikowane. Możesz pobrać


klasy CMailFile napisane przez Y. Lee (http://renoir.vill.edu/~ylee/mailfile.html),
użyć jednej z PHPClasses.com Manuela Lemosa lub stworzyć odpowiednik o podob-
nych możliwościach.

Istnieje jednak niewiele przypadków, w których trzeba wysyłać załączniki za pomocą


PHP. Pomyśl o umieszczeniu plików w Internecie (chronionych hasłem lub nie) i wy-
słaniu poczty z informacją, skąd można je ściągnąć.

Funkcja maił o zwraca l (TRUE) , gdy PHP uważa, że udało mu się


pomyślnie wysłać wiadomość. Nie ma to związku prawidłowym wysła-
niem lub odebraniem wiadomości. Należy myśleć o tej jedynce jako
o informacji, że PHP wykonał funkcję bez większej awarii".

Więcej na temat aplikacji pocztowych


Oprócz użycia PHP do stworzenia kompletnego klienta e-mail, częstym wykorzysta-
niem funkcji maił ( ) jest okazjonalne wysyłanie poczty w przypadku wystąpienia okre-
ślonych warunków w witrynie WWW.

Wysyłanie poczty z formularza


Wysyłanie poczty z formularza jest chyba najczęściej spotykanym zastosowaniem funkcji
PHP maił ( ) . Jest to alternatywa dla znacznika HTML mailto, który powoduje wysłanie
poczty za pomocą programu pocztowego zainstalowanego na komputerze klienta.

Dlaczego używamy serwera, zamiast skorzystać z wysyłania poczty przez klienta?


W wielu przypadkach jest to niewygodne, działa wolniej oraz wymaga więcej pracy.
Chociaż zdarzają się sytuacje, w których warto się zdecydować na takie rozwiązanie.
* Znaczna część odwiedzających korzysta z publicznych przeglądarek lub poczty
WWW.
* Chcesz zagwarantować odpowiednią postać wiadomości.
* Chcesz przy okazji umieścić kontakt w bazie danych.
Rozdział 28. » E-mail___________________________________________465

* Chcesz zmniejszyć liczbę niechcianych przesyłek i uniknąć konieczności prze-


kazywania SMTP przez inny komputer, konfigurując własny serwer pocztowy.

Tego rodzaju formularz do wysyłania poczty może być użyteczny we wielu przypad-
kach: wysyłania głosów, raportowania błędów i korzystania z pomocy technicznej.
<html>
<head>
<title>Formularz: "Co to był za dreszczowiec"</title>
</head>
<body>
<center>
<table width="550">
<tr bgcolor= # F F 9 9 3 3 x t d a l i g n = " c e n t e r " X B R x H 3 > T h e T h r i l l e r G u i d e . c o m
<BR>"Co to był za d r e s z c z o w i e c " < B R > < / H 3 > < / t d x / t r >
<trxtd>
Czy czytałeś kiedyś niezapomniany dreszczowiec, a teraz nie pamiętasz jego
tytułu? Wypełnij możliwie dużo z poniższych pól i kliknij przycisk Wyślij.
Spróbujemy przeszukać nasze zasoby i odeślemy ci odpowiedź.
</tdx/tr></table>
</center>
<FORM METHOD=post ACTION="titlehelp.php">
<P>Imię: <input type="text" size=30 name="FirstName">
<P>Nazwisko: <input type="text" size=30 name="LastName">
<P>Twój adres e-mail: <input type="text" size=30 name="Email">
<P>W którym roku dzieje się akcja? <input type="text" size=4 name="Year">
<P>Czy pamiętasz jakieś fakty? <input type="text" size=30 name="Setting">
<P>Bohaterami książki byli: <br>
<ul>
<input TYPE="radio" NAME="Gender" VALUE=l>Kobieta<br>
<input TYPE="radio" NAME="Gender" VALUE=2>Mężczyzna<br>
<input TYPE="radio" NAME="Gender" VALUE=3>Para<br>
<input TYPE="radio" NAME="Gender" VALUE=4>Dwóch mężczyzn<br>
<input TYPE="radio" NAME="Gender" VALUE=5>Dwie kobiety<br>
</ul>
<P>Gdy książka się ukazała, była: <br>
<ul>
<input TYPE="radio" NAME="Status" VALUE=l>bestselerem<br>
<input TYPE="radio" NAME="Status" VALUE=2>ulubieńcem krytyków<br>
<input TYPE="radio" NAME="Status" VALUE=3>niezauważona<br>
<input TYPE="radio" NAME="Status" VALUE=4>nie wiem<br>
</ul>
<P>Opowiedz, c o p a m i ę t a s z ( a k c j a , b o h a t e r o w i e , w e r s j a filmowa i t p . ) :
<brxtextarea NAME="Other" ROWS=6 COLS=50x/textarea>
<Pxinput type="submit" name="Submit" value="Wyślij ">
</body>
</html>

<html>
<head>
<title>ThrillerGuide: TitleHelp.php</title>
</head>
<body>
<?php
/* Jeżeli chcesz, możesz zapisać te informacje do bazy danych */
$formsent = maił("help@thrillerguide.com", "Jaki tytuł nosił dreszczowiec?",
"Pytanie od: SLastName SFirstName\r\nRok: $Year\r\nFakty:
$Setting\r\nBohaterowie: $Gender\r\nSukces: SStatus\r\nInne szczegóły
identyfikacyjne: SOther", "From: SEmail\r\nBounce-to: help3thrillerguide.com");
if($formsent == 1)
j
print("<P>Cześć, SFirstName. Dostaliśmy twoje wołanie o pomoc. Spróbujemy
odpowiedzieć w ciągu 24 godzin. Dziękujemy za wizytę w ThrillerGuide.com!");
t
else
print("Przepraszamy, wystąpił problem z formularzem. Proszę spróbować
jeszcze raz.");
466___________________________________Część III » Techniki zaawansowane

?>
</body>
</html>

Wysyłanie poczty przy użyciu bazy danych


Prawdopodobnie wysyłanie poczty seryjnej za pomocą PHP zamiast korzystania z za-
rządcy list wysyłkowych nie będzie dobrym wyjściem, szczególnie w przypadku dużej
liczby odbiorców. Jednak istnieją programiści, którym lepiej używa się z funkcji niż
specjalnego narzędzia.
<html>
<head>
<title>ThrillerGuide: Zawiadomienie o aktualizacji</title>
</head>
<body>
<?php
mysql(connect("localhost", "root");
mysql_select_db("thriller" ) ;
$query = "SELECT email FROM mailinglist");
$result = mysql_query{$query);
while ($MailArray = mysql_fetch_array(Sresult))
(
$formsent = maił(SMailArray[0], "Uaktualnienie witryny z dnia 2001.04.12",
"Informujemy, że ostatnio zmieniona została witryna
ThrillerGuide.\r\n\r\nChciałeś otrzymywać takie informacje. Jeżeli nie chcesz
ich już otrzymywać, wyślij pocztę z \"Cancel\" w temacie.", "From:
mailinglist@thrillerguide.com\r\nReply-to: help@thrillerguide.com");
print ("Wynik: Sformsent\n");
)
?>
</body>
</html>

Własna aplikacja pocztowa w PHP


Oto sytuacje, w których własna aplikacja pocztowa może się przydać:
* klient chce wysyłać spersonalizowane przesyłki e-mail do setek ludzi, a każda
z nich zawiera hasło;
* klient wprowadza ręcznie każdą nazwę i adres do formularza. Program umiesz-
cza te dane w tabeli wraz z wygenerowanym przez PHP hasłem;
* korzystając z szablonu PHP tworzy i wysyła serię przesyłek.

Poniżej zamieściliśmy przykładową aplikację spełniającą te założenia. Łączy ona moż-


liwości dwóch poprzednich przykładów i dodaje kilka innych: tablice i generator loso-
wych haseł (na podstawie przykładu z rozdziału 10.).
address_entry.php
<HTML>
<HEAD>
<TITLE>Formularz wprowadzania nazwy i adresu</TITLE>
</HEAD>

<BODY>
<CENTERXTABLE W I D T H = 5 5 0 X T R X T D >
Rozdział 28. » E-mail___________________________________________467

Wprowadź nazwy i adresy odbiorców. Nie musisz wprowadzać wszystkich 25 pozycji.


Gdy będziesz chciał wysłać adresy, kliknij przycisk Wyślij na dole
strony.<BR><BR>
</TD></TRX/TABLEX/CENTER>

<FORM METHOD=post ACTION="email_send.php">


<?php
/* Aby utrzymać możliwe z punktu widzenia obsługi rozmiary strony i wielkości
paczek e-mail, ograniczyliśmy każdą paczkę do 25 pozycji. Jeżeli coś pójdzie źle
- klient przypadkowo zamknie przeglądarkę przed wysłaniem, moduł PHP nie będzie
działał - ponownie będzie trzeba wpisać maksymalnie 25 pozycji. Aby zmienić
wielkość paczki, popraw liczbę w pętli while poniżej oraz skrypt obsługujący
formularz. */

for($batch_size = 0; $batch_size <= 25; $batch_size++)


{
print("<P>Imie: <input type=\"text\" size=30
name=\"FirstName[]\"><BR>Nazwisko: <input type=\"text\" size=30
name=\"LastName [] \"XBR>Adres e-mail: <input type=\"text\" size=30
name=\"Email[]\">"};
}
?>
<BRXBR>
< P X I N P U T T Y P E = " S u b m i t " NAME="SUBMIT" VALUE="Wyśli j ">
</FORM>
</BODY>
</HTML>

email_send.php
<html>
<head>
<title>Ekran wysyłania poczty</title>
</head>

<body>
<?php
/* Ta strona wprowadza nazwy, adresy i hasła do bazy danych
oraz wysyła e-maile. */
include("password_maker.inc");

mysql_connect("localhost", "root") ;
mysql_select_db("mailinglist");
$list_length = 0;
/* Zawiera warunek zatrzymania w przypadku wprowadzenia mniej niż 25 nazw. */
for ($list_length = 0; $list_length <= 24 &&
STRLEN(SFirstName[$list_lengthJ) > 0; ++$list_length)
(
/* Wymagamy 8 znaków w haśle. */
SPassword = random^string{Scharset, 8);
$query = "INSERT INTO recipient (FirstName, LastName, Email, Password)
VALUES('$FirstName[Slist_length]', 'SLastName[$list_length]',
'SEmail[$list_length]', $Password)";
$result = mysql_query(Squery);
$formsent = mail($Email[$list_length], "Informacja o użytkowniku i haśle",
"Twoja nazwa użykownika to: SFirstName[$list_length]
$LastName[Slist_length].\r\nUpewnij się, że jest tylko jedna spacja
pomiędzy imieniem i nazwiskiem w trakcie logowania.\r\nHaslo:
SPassword.\nPowodzenia!", "From: mailinglist@sendhost\r\nReply-to:
help@sendhost");
printC'Wynik dla Slist_length jest $f ormsent. \n") ;
(
?>
</body>
</html>

password_maker.inc
<?php
/* Wywoływana jest funkcja random_string. Używa ona random_char */
468___________________________________Część III » Techniki zaawansowane

function random_char($string)
f
$length = strlen($string);
Sposition = mt_rand(0, $length - 1);
return($string[$position]);
}

function random_string ($charset_string, Slength)


(
$return_string = r a n d o m _ c h a r ( $ c h a r s e t _ s t r i n g ) ;
for (Sx = 1; $x < Slength; $x+ + )
S r e t u r n _ s t r i n g .= r a n d o m _ c h a r ( S c h a r s e t _ s t r i n g ) ;
return($return_string);
)

// Inicjowanie generatora liczb losowych


mt_srand((double)microtime() * 1000000);

5charset = "abcdefghijklmnopqrstuvwxyz";
?>

Podsumowanie
Poczta należy do najbardziej użytecznych i atrakcyjnych aplikacji internetowych. PHP
pozwala zarówno na wysyłanie, jak i odbieranie przesyłek e-mail z poziomu strony
WWW. Jednak PHP nie jest wyspecjalizowanym programem, prawdopodobnie nie ob-
służy dużej liczby przesyłek.

E-mail stanowi jedną z najbardziej skomplikowanych usług w większości domen. Za-


wiera wiele ruchomych części, brakuje mu też standaryzacji. Nie wiń za to PHP —jest
to problem poczty.

Jednym z najczęstszych zastosowań funkcji pocztowych PHP jest wysyłanie poczty


(często do siebie samego) z zawartością generowaną za pomocą formularza WWW. Jest
to pewniejsze od łącza mai l to i pozwala na wprowadzenie ścisłej struktury danych.
Możesz również używać PHP do wysyłania małych paczek danych, korzystając z da-
nych zapisanych w bazie. Jednak do wysyłania wielu listów potrzebny jest zarządca list
wysyłkowych.
Rozdział 29.
PHPiXML
W tym rozdziale:
«• CotojestXML?
* Praca z XML
* Dokumenty i DTD
«• DOM kontra SAX
+ Funkcje PHP dla XML: DOM
+ Funkcje PHP dla XML: SAX
* Przykładowa aplikacja XML

XML to jedno z najbardziej popularnych słów w dzisiejszym przemyśle oprogramowa-


nia, ale co ono oznacza dla szeregowego programisty PHP? Może być niezbędnym
wstępem do lepszego Internetu — szybszego, bardziej interakcyjnego, mniej zaśmieco-
nego i dostępnego. Przy użyciu PHP możesz łatwo zintegrować XML z arsenałem tech-
nologii tworzenia stron WWW, gdy tylko technologia ta dojrzeje.

Co to jest XML?
XML jest skrótem od extensible Markup Language. XML jest uproszczoną formą
SGML, Standard Generalized Markup Language, nie musisz jednak nic wiedzieć na
temat SGML, aby używać XML. Definiuje on składnię dla strukturalnych dokumentów,
które są czytelne zarówno dla komputera, jak i człowieka.

Najłatwiejszym sposobem zrozumienia XML jest pomyślenie o tym, czego nie potrafi
HTML. HTML jest również językiem bazującym na znacznikach, ale dokumenty
HTML nie są strukturalne. Znaczniki HTML (technicznie nazywane elementami)
i atrybuty są prostymi znacznikami identyfikacyjnymi dla przeglądarki. Na przykład pa-
ra znaczników <H1> i < / H l > oznacza nagłówek najwyższego poziomu. Przeglądarka
interpretuje go jako tekst wyświetlany dużą, grubą i być może pochyłą czcionką. HTML
470___________________________________Część III » Techniki zaawansowane

nie wskazuje jednak, czy tekst pomiędzy tymi znacznikami jest tytułem strony, nazwi-
skiem autora, przywitaniem, obietnicą specjalnych cen sprzedaży. Jest to tylko fragment
tekstu, który ma być napisany dużymi literami.

Nasz opis XML musi być bardzo skrótowy (ponieważ jest to książka
na temat PHP, a nie XML). Osobom, które chcą dowiedzieć się więcej
na ten temat, polecamy książkę XML. Księga eksperta. Autor: Elliote
Rusty Harold, ISBN: 83-7197-275-X. Mimo że książka ta nie jest po-
radnikiem na temat tworzenia aplikacji na bazie XML, przedstawimy
podstawowe zasady i koncepcje.

Jedną z konsekwencji braku struktury HTML jest to, że wyszukiwarki dostają niewiele
wskazówek, co jest ważne na każdej stronie lub jakie są relacje między poszczególnymi
fragmentami tekstu. Aby to odgadnąć, używają różnych metod, ale żadna z nich nie jest
odporna na błędy. Nadużywa się znaczników <META>, witryny o treściach pornogra-
ficznych bardzo często umieszczają popularne, niezwiązane z prawdziwą zawartością
słowa kluczowe w nagłówkach, aby oszukać nieuważnych użytkowników WWW. Je-
żeli XML stanie się wszechobecny, może wyeliminować wiele z tych problemów.

Załóżmy, że pracujesz dla witryny informacyjnej, która właśnie podpisała strategiczne


porozumienie na temat wymiany informacji z dużym portalem. Podstawowy problem:
w jaki sposób dostarczać dane. HTML nie jest odpowiedni do tego rodzaju zastosowań.
Wiadomo, że projekt strony i technologie dostarczania danych są inne niż na twojej wi-
trynie. Druga strona nie będzie mogła włączyć twojego HTML do swoich stron. Firmy
używają różnych języków programowania, różnych baz danych, różnych edytorów
HTML, różnych arkuszy stylu. Pośrednikiem może być format wymiany danych, łatwy
do utworzenia w twoim środowisku programowym, zrozumiały dla obu stron i ich ist-
niejącego oprogramowania, łatwy do konwersji do postaci odpowiedniej dla projektu
i potrzeb kontrahenta. XML to uniwersalny format wymiany danych.

Przykłady odpowiadają na pytanie, dlaczego XML. Jest to elastyczny format wymiany


danych, który jest niezależny od oprogramowania, łatwo go analizować, pozwala dodać
informacje na temat struktury danych.

Następnym pytaniem na temat XML jest zwykle „do czego XML jest podobny". Fak-
tycznie jest podobny do HTML. Prosty plik XML, jak ten pokazany na wydruku 29.1,
jest łatwy do zrozumienia dla użytkowników HTML.

Wydruk 29.1. Prosty plik XML________________________________________


<?xml version="l.0"?>
<book>
<publisher>Helion</publisher>
<title>PHP 4 Biblia</title>
<chapter title="PHP i XML">
<section title="Co to jest XML?">
<paragraph>
Jeżeli znasz HTML, jesteś w połowie drogi do poznania XML.
</paragraph>
<paragraph>
Rozdział 29. » PHP i XML__________________________________________471

Oba są językami bazującymi na znacznikach, ale XML jest bardziej


strukturalny od HTML.
</paragraph>
</section>
</chapter>
</book>

Jak widzisz, znaczniki, atrybuty i hierarchiczna struktura, której użyliśmy, są podobne


do tych w HTML. W XML każda para znaczników (<paragraph> ... </paragraph>)
jest nazywana elementem. Tak jest też w HTML, ale większość użytkowników prefe-
ruje określenie „znacznik" (konstrukcja zaznaczająca element), a nie „element" (frag-
ment konstrukcyjny oznaczany przez znaczniki). Możesz używać dowolnego terminu.
Największą różnicą jest to, że znaczniki w XML są definiowalne i nie niosą ze sobą
żadnej informacji na temat sposobu wyświetlania ani dla przeglądarki, ani dla innych
programów wyświetlających.

Minimalne wymagania XML:


* Musi wystąpić jeden element korzenia, który zawiera wszystkie inne elementy,
podobnie do <HTML> .. . </HTML> w dokumencie HTML. Jest czasem na-
zywany elementem dokumentu.
* Elementy muszą być hierarchiczne. Oznacza to, że dozwolone jest <X><Y></
YX/X>, a <XXY><XX/Y> nie. W pierwszym przykładzie <x> zawiera ca-
łość <Y>. W drugim przykładzie <x> i <Y> zazębiają się. XML nie pozwala na
zachodzenie na siebie znaczników.
* Wszystkie elementy muszą być bezwzględnie zamknięte (w przeciwieństwie
do HTML, w którym elementy mogą pozostać niedomknięte, np. <OPTION>
lub <LI>). Można to zrealizować znacznikiem zamykającym (<title></
title>), jak w HTML, albo użyciem funkcji XML, niedostępnej w HTML,
zwanej elementami samozamykającymi się (<logo href="graphic. jpg"/>).
Elementy samozamykające się są również nazywane elementami pustymi.
* Elementy mogą zawierać elementy, tekst i inne dane. Trzeba je tylko odpo-
wiednio oznaczyć.

Znaki &, <, >, ' oraz " są znakami specjalnymi w XML. Możesz użyć
ich w danych oznaczając je za pomocą kodów, takich jak &amp;,
&it; lub umieszczając je w sekcji CDATA w sposób opisany poniżej.

Oprócz obowiązkowych warunków budowy, dokument XML powinien rozpoczynać się


identyfikującymi deklaracjami XML. Jest to deklaracja określająca typ MIME i numer
wersji, jak na przykład <?xml version="l .0"?>. Niektóre analizatory wypisują
ostrzeżenia w przypadku braku tej części.

Dokumenty XML wy stępuj ą zwykł e w postaci tekstu. Mogą zawierać dane binarne, ale
nie są dla nich przeznaczone. Jeżeli chcesz umieścić dane binarne w dokumencie XML,
musisz je najpierw zakodować i dekodować przy odczytywaniu. Zwróć uwagę, że dołą-
czanie danych binarnych może naruszyć niezależność od platformy czystego XML.
472___________________________________Część III » Techniki zaawansowane

W przypadku użycia XML deklaracja XML oraz inne instrukcje przetwa-


rzania wymagają stosowania innych znaczników PHP niż znaczniki skró-
cone. Ponieważ znaczniki są w takiej sytuacji identyczne (<? ? > ) ,
może identyfikacja, czy dana sekwencja znaków jest blokiem PHP, czy
instrukcją przetwarzania XML, jest utrudniona.

Praca z XML
Prawdopodobnie zadajesz sobie pytanie: co mogę z tym zrobić. Odpowiedź nie jest ła-
twa. Teoretycznie za pomocą XML możesz realizować trzy główne zadania: manipula-
cje i zapisywanie danych, przesyłanie danych pomiędzy aplikacjami lub pomiędzy
firmami oraz wyświetlanie stron XML w przeglądarce, z użyciem arkuszy stylów do
zdefiniowania dyrektyw wyświetlania.

W praktyce prawie nikt nie używa XML jako podstawowej składnicy danych w czasach,
gdy wszechobecny jest SQL. Możliwe jest, choć ciągle skomplikowane, manipulowanie
danymi przy użyciu XML — można edytować dokument tworząc lub zmieniając węzły
XML, zamiast zmiany tekstu. Możliwe jest (ale pracochłonne) wyświetlanie prostego do-
kumentu XML przy użyciu arkuszy stylu. W rzeczywistości niewiele witryn kor2ysta
z plików XML zamiast HTML. Więcej informacji na temat wyświetlania XML znajduje
się w dopisku „Obietnice i pułapki przy wyświetlaniu XML".

Główne zastosowanie XML to wymiana danych między aplikacjami i organizacjami.


Jest to zastosowanie, na które PHP ma największy bezpośredni wpływ. Program w C
może przeprowadzić jakieś operacje na danych z bazy i skierować wyniki do pliku XML,
który PHP może zmienić na HTML. Taki przepływ danych jest sensowny w przypadku,
gdy wymaga się wielu obliczeń, ale bez udziału dużego programu realizującego
wszystkie operacje i formatującego plik HTML.

PHP może również czytać dane z jakiegoś źródła i zapisywać je do pliku XML. Pomaga
to przy przenoszeniu zawartości witryny WWW do innej. Możesz również skorzystać
z tej funkcji, aby użytkownicy „nietechniczni" mogli tworzyć odpowiednio sformato-
wane dokumenty XML za pomocą formularzy WWW. Obecnie zapisywanie XML jest
najczęstszym zastosowaniem skryptów PHP.

Dokumenty i DTD
Jak poprzednio mówiliśmy, wymagania formatowania XML są minimalne. Ale doku-
menty XML mają dodatkowy poziom kontroli poprawności. Prawidłowy dokument XML
to taki, który spełnia wszystkie zasady znane jako definicja typu dokumentu (DTD).
Rozdział 29. » PHP i XML_________________________________________473

Obietnice i pułapki przy wyświetlaniu XML


XML próbuje zrobić coś, co w HTML jest wykonane bardzo nieprecyzyjnie:
rozdzielić zawartość i projekt. Znaczniki XML nie zawierają informacji okre-
ślającej sposób wyświetlania — element nazwany <headerx/header>
w XML nie niesie żadnej informacji o „dużym, pogrubionym tekście".
Wszystkie dane o wyświetlaniu powinny być wprowadzone przez arkusze sty-
lów (CSS), z którymi wielu projektantów HTML już się zapoznało, lub XSL
(extensible Style Language) — następną generację arkuszy stylów.
Pojedynczy dokument XML może być teoretycznie wyświetlany na kilka spo-
sobów dzięki zmianie arkusza stylu. Można wziąć jeden dokument XML i two-
rzyć wersje strony dla: bardzo dużych ekranów, telefonów komórkowych,
niedowidzących, tylko dzięki wymianie różnych szablonów XSL.
Jednak w rzeczywistości sytuacja nie jest tak korzystna. Standard XSL nadal
jest chwiejny; nie jest jeszcze zaimplementowany w głównych przeglądar-
kach (chociaż Mozilla zapowiada, że zrobi to wkrótce). CSS istnieje już od
roku 1997, chociaż obsługa w przeglądarkach jest nadal problematyczna, że
większość dużych witryn nadal używa znaczników czcionki. XML ma przed
sobą długą drogę, zanim będzie powszechnie stosowany. Użytkownicy mogą
narzekać na niedostatki HTML, ale lepsze technologie, jak na przykład XML,
są nieco trudniejsze i nie tak szybko się przyjmą.
Do tego czasu XML musi być przekształcany na HTML na serwerze. Można do
tego użyć XSL, ale niewiele witryn wybrało takie rozwiązanie. Częściej stosuje
się inny program, na przykład PHP, w celu przetłumaczenia XML na HTML.

Aby docenić wartość DTD, wyobraź sobie, że jesteś szefem projektu open source two-
rzącego książki i inne dokumenty, dostępne bezpłatnie w Internecie w postaci elektro-
nicznej. XML niezwykle cię interesuje; wydaje się, że spełnia twoje potrzeby jako
format wymiany danych, łatwy do zaadoptowania w powstających właśnie technolo-
giach wyświetlania. Członkowie twojej grupy przegłosowali przekodowanie wszystkich
książek i dokumentów projektu na postać XML oraz szybką XMLizacje wszystkich
rozpoczętych dokumentów.

Ale w chwili obejrzenia pierwszych nadchodzących przesyłek przeżywasz szok. Jedne są


w formacie pokazanym na wydruku 29.1, inne wyglądają tak, jak pokazuje wydruk 29.2.
<?xml version="l.0"?>
<book title="PHP4 Biblia">
<publisher name="Helion">
<chapter number="29">
<chapter_title>PHP i XML</chapter_title>
<p>
<sentence>Jeżeli znasz HTML, jesteś w połowie drogi
do poznania XML.</sentence>
<sentence>0ba są językami na bazie znaczników,
ale XML jest bardziej strukturalny od HTML.</sentence>
</p>
</chapter>
</book>

Oba te pliki XML korzystają z podobnej, ale nie identycznej struktury hierarchicznej,
używając podobnych znaczników. Jest to wada definiowanych przez siebie znaczników:
474___________________________________Część III » Techniki zaawansowane

losowe wahania utrudniające skojarzenie w pliku podobnych rodzajów informacji.


Szybko zorientujesz się, że musisz stworzyć jakieś zasady, według których będzie two-
rzony plik z książką, i określić relacje pomiędzy jej elementami. Potrzebny ci DTD.

Definicja typu dokumentu, albo DTD, opisuje strukturę klasy dokumentów XML. DTD
jest pewnego rodzaju formalnym ograniczeniem gwarantującym, że wszystkie doku-
menty tego typu będą spełniały założone reguły struktury i konwencje nazw. DTD po-
zwala dokładnie określić, jakie elementy są dozwolone, jaka jest ich relacja, jaki jest typ
każdego elementu i jaka jest jego nazwa. DTD pozwala również na określenie, które
elementy są obligatoryjne, które opcjonalne, jakie są ich wartości domyślne.

Możesz oczywiście zapisać te zasady w pliku tekstowym:


Głównym elementem tego dokumentu jest BOOK
BOOK posiada tylko jeden TABLE OF CONTENTS
BOOK posiada tylko jeden element TITLE
BOOK składa się z wielu elementów CHAPTER
CHAPTER posiada tylko jeden el. CHAPTERTITLE
Wszystkie CHAPTERTITLE znajdują się w TABLE OF CONTENTS
I tak dalej...

Możesz dać kopię tego pliku każdemu, kto będzie jej potrzebował. DTD jest po prostu
zwięźle zapisanym, dokładnie zdefiniowanym plikiem gramatyki. Jest użyteczne doda-
nie takiej dyscyplinującej opcji do pliku XML, gdyż jego „samodefiniowalna" natura
może wprowadzać chaos. Jeżeli grupa ludzi zgodzi się na taką postać DTD, to dobra
droga do ustanowienia standardu dla wszystkich danych określonego typu.

Wróćmy do naszego hipotetycznego przykładu. Członkowie twojej grupy mogą się


miesiącami kłócić o szczegóły pliku DTD, które perfekcyjnie opiszą powiązania między
spisem treści, rozdziałami, tytułami oraz nagłówkami, indeksami, dodatkami, częścia-
mi, akapitami, epilogami itd. Możesz oczywiście powtarzać prace nad DTD tak często,
jak to jest konieczne.

Plik XML można przepuścić przez tak zwany „analizator poprawności", który stwier-
dzi, czy dokument spełnia wymagania zapisane w pliku DTD. Zamiast sprawdzać każdą
przychodzącą książkę, możesz wrzucić plik do analizatora, który wykona kontrolę po-
prawności formalnej. Nie skontroluje on jakości zawartości dokumentu XML, ale może
zweryfikować go według wymagań.

Musimy przyznać, że PHP nie posiada (dokładnego) analizatora poprawności plików


XML. Powinieneś poznać podstawową strukturę DTD i opisanych przez nią dokumen-
tów XML, niezależnie od tego, czyje sprawdzasz ich poprawność, czy nie.

Struktura DTD
Definicja typu dokumentu jest zbiorem zasad definiujących strukturę określonej grupy
dokumentów XML. DTD może być częścią dokumentu XML (w tym przypadku mó-
wimy o wewnętrznym DTD), może też być umieszczony poza dokumentem w osobnym
pliku na serwerze, albo pod adresem URL w Internecie (w takich przypadkach mówimy
o zewnętrznym DTD).
Rozdział 29. » PHP i XML 475

DTD może istnieć wewnątrz (jako część samego dokumentu XML), ale
lepiej tworzyć zewnętrzne DTD. Wspomnieliśmy już, że DTD definiuje
klasę dokumentów. Dzięki oddzieleniu ich od XML można uniknąć
zmieniania wszystkich dokumentów XML tej klasy w przypadku póź-
niejszej zmiany pliku DTD. Łatwiej pokazać wewnętrzny DTD, użyjemy
więc obu metod w przykładach zawartych w tym rozdziale.

Rozpocznijmy od prostego dokumentu XML z wewnętrznym DTD (patrz wydruk 29.3.).

Wydruk 29.3. Dokument XML z wewnętrznym DTP (recipe.xml)__________________________


<?xml version="l.0"?>

<!DOCTYPE recipe [

<!ELEMENT recipe (ingredients, directions, servings)>


<!ATTLIST recipe name CDATfl ttREQUIRED>
<!ELEMENT ingredients (ttPCDATA)>
<!ELEMENT directions (t»PCDATA)>
<!ELEMENT servings (#PCDATA)>
]>

<recipe name ="Wołowina z burgundem">


<ingredients>Wołowina</ingredients>
<ingredients>Wino Burgund</ingredients>
<directions>
Dodaj wołowinę do burgunda. Podawać.
</directions>
<servings>12</servings>
</recipe>

Dla łatwiejszego czytania podzieliliśmy dokument XML na trzy części. Pierwszy wiersz
jest zwykłą jednowierszową deklaracją, która powinna rozpoczynać każdy dokument
XML. Druga część jest wewnętrznym DTD, oznaczonym wierszem rozpoczynającym
się od sekwencji < !. Trzecia część jest właściwym plikiem XML. Skupimy się teraz na
drugiej części, na DTD. Część tekstu położona na zewnątrz nawiasów kwadratowych
jest deklaracją typu dokumentu (nie pomyl z „definicją typu dokumentu"): < ! DOCTYPE
recipe [ . . . ] > . Deklaracja typu dokumentu wskazuje, że dokument ten będzie uży-
wał DTD. Ponieważ jest to wewnętrzny DTD, nazwaliśmy go tak samo jak główny
element (recipe) i umieściliśmy resztę deklaracji w nawiasach kwadratowych. Jeżeli
będziesz używał zewnętrznego DTD, powinieneś użyć deklaracji typu dokumentu
wskazującej typ i położenie DTD. Dwoma przykładami deklaracji typu dokumentu od-
wołujących się do zewnętrznych DTD są:
<!DOCTYPE recipe SYSTEM "recipe.dtd">
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML//EN">

W zewnętrznych deklaracjach typów dokumentów podaje się nazwę głównego ele-


mentu, typ (SYSTEM oznacza plik na serwerze, PUBLIC oznacza standardowe DTD)
oraz miejsce, gdzie znajduje się plik.

Właściwy DTD to wiersz wewnątrz nawiasów kwadratowych. Składa się on z elemen-


tów, typów elementów i atrybutów zawartych w dokumencie XML.
476___________________________________Część III « Techniki zaawansowane

Element Początkowy i końcowy znacznik np.: <k»cokolwiek</b>


lub pusty element (<br/>). Elementy majątypy, a czasami
zawartość i atrybuty.
Typ elementu Ograniczenie nałożone na zawartość i atrybuty elementu. Za
pomocą typu można określić rodzaj przechowywanych danych
i podać, jakie element może posiadać atrybuty.
Atrybut Nazwa i wartość skojarzona z elementem w postaci <element
nazwaatrybutu="wartośćatrybutu">.

W naszym przykładowym DTD, wydruk 29.3., zadeklarowaliśmy, że nasz główny


element, recipe, zawiera trzy elementy potomne — ingredients, directions
i servings, oraz jeden wymagany atrybut, name. Każdy element potomny ma typ zna-
kowy analizowany, atrybut typ znakowy.

Jeżeli podzielisz wydruk 29.3 na dokument XML i zewnętrzny DTD, w nawiasach


kwadratowych zamiast definicji będzie odwołanie do zewnętrznego pliku DTD. Wynik
pokazano na wydrukach 29.4 i 29.5.

Wydruk 29.4. Dokument XML z zewnętrznym DTP (recipejzxt.xml)____________________________


<?xml version="1.0"?>
<!DOCTYPE recipe SYSTEM "recipe.dtd">

<recipe name ="Wołowina z burgundem">


<ingredients>Woiowina</ingredients>
<ingredients>Wino Burgund</ingredients>
<directions>
Dodaj wołowinę do burgunda. Podawać.
</directions>
<servings>12</servings>
</recipe>

Wydruk 29.5. Zewnętrzny DTD (recipe.dtd)_________________________________


<!ELEMENT recipe (ingredients, directions, servings)>
<!ATTLIST recipe name CDATA #REQUIRED>
<!ELEMENT ingredients (#PCDATA)>
<!ELEMENT directions (#PCDATA)>
<!ELEMENT servings (#PCDATA)>

Jeśli XML użyty w obu przykładach jest zgodny z wewnętrznym i zewnętrznym DTD,
oba dokumenty powinny być pozytywnie zweryfikowane przez analizator poprawności.

Pokazaliśmy tylko fragment wiedzy na temat DTD i dokumentów XML, ale te podsta-
wy powinny wystarczyć do zrozumienia większości funkcji PHP dla XML.

Analizatory kontrolujące
i nie kontrolujące poprawności
Istnieją dwa rodzaje analizatorów XML: kontrolujące poprawność i je niekontrolujące.
Analizatory niekontrolujące sprawdzają tylko, czy dokument XML jest odpowiednio
Rozdział 29. » PHP i XML_________________________________________477

zbudowany (czy spełnia zasadę zamykania znaczników, apostrofów itp.). Analizatory


kontrolujące wymagają prawidłowej budowy dokumentów, dodatkowo sprawdzają do-
kument XML według DTD. Jeżeli dokument XML nie pasuje do jego DTD, analizator
wyświetla odpowiedni komunikat o błędzie.

Analizator PHP 4 SAX o nazwie expat nie jest analizatorem kontrolującym. Nie ozna-
cza to, że można zignorować DTD. Przejście przez proces tworzenia DTD dla każdego
dokumentu jest dobrą praktyką projektową. Zmusza do dokładnego przemyślenia
struktury dokumentu. Wielu ekspertów poleca przepuszczenie wszystkich dokumentów
przez analizator poprawności, nawet wtedy, gdy nie planujesz używać go ponownie.

Większość analizatorów poprawności napisano w języku Java. Ich konfiguracja i uży-


wanie są skomplikowane. Najprostszym sposobem kontroli plików jest użycie analiza-
tora dostępnego w Internecie. Jednym ze znanych analizatorów tego typu jest STG:
http ://www. stg. brown, edu/service/xmlvalid

Możliwe jest również użycie Gnome libxml do kontroli dokumentów XML. Wymaga
więcej pracy. Na witrynie libxml istnieją przykłady kontroli dokumentów z użyciem C
(www.xmlsoft.org).

DOM kontra SAX


Istnieją dwa główne API do obsługi XML i dokumentów XML: Document Object Mo-
del oraz Simple API for XML. PHP 4 posiada moduły obu tych API. Moduł SAX jest
dołączany domyślnie (jeżeli nie używasz Apache, być może będziesz musiał pobrać bi-
bliotekę Expat), moduł DOMXML jest opcjonalny.

Możesz używać dowolnego API do analizy dokumentów XML. W celu stworzenia lub
rozbudowy dokumentów XML za pomocą interfejsu PHP (bez tworzenia ręcznego) mu-
sisz skorzystać z DOM.

SAX
SAX jest mniejszy i łatwiejszy do nauki, ale traktuje XML jak przepływający strumień da-
nych. Jeżeli chcesz analizować nasz przepis, możesz skorzystać z analizatora SAX w PHP.
Jednak trudno będzie dodać nowy element lub atrybut, a zmiana wartości wybranego skład-
nika będzie pracochłonna. SAX jest doskonały do powtarzalnych zadań odnoszących się do
wszystkich elementów danego typu — na przykład zamiany znaczników odpowiedniego
elementu na znaczniki <PX/P> (krok w kierunku zamiany XML na HTML nadającego się
do wyświetlenia). SAX analizuje dokument tylko raz, od góry do dołu.

DOMXML buduje model struktury całego dokumentu XML. Pozwala na wybór odpo-
wiedniego węzła i zmianę jego wartości lub dodanie kolejnego węzła do drzewa. Jednak
proces ten wymaga dużych zasobów i dlatego nie nadaje się do obsługi długich doku-
mentów XML. Sprawdza się w nieskomplikowanych przekształceniach.
478________________________________Część III » Techniki zaawansowane

DOM
Document Object Model to kompletne API do tworzenia, edycji i analizy dokumentów
XML. Jest to obiektowe API, więc znajomość programowania obiektowego jest tu
przydatna, choć niekonieczna. DOMXML nie jest, prawdę mówiąc, analizatorem, ale
większość implementacji zawiera analizator.

DOM odczytuje plik XML i tworzy w pamięci drzewo obiektów. Rozpoczynając od


dokumentu lub jego elementu (oba nazywane w DOM węzłami), możesz pobrać węzły
potomne, rodziców lub zawartość tekstową dla każdej części drzewa. Można zapisywać
obiekty DOM do kontenerów, jak również wypisywać je w postaci tekstu.

DOM najlepiej pracuje, gdy dostępny jest cały dokument XML. Jeżeli twój XML jest
bardzo powoli przesyłany, użyj SAX. Ponieważ DOM buduje drzewo elementów
w pamięci, na wczytanie dużych dokumentów potrzeba czasu i zasobów.

Pod maską DOM API tkwi analizator gnome-libxml 2.0 (zwany Gnome xml). Jest do-
stępny na witrynie:
http://www.xmlsoft. org

Implementacja Gnomę DOM pochłania mniej pamięci. Document Object Model jest re-
komendowany przez World Wide Web Consortium. Możesz przeczytać o nim w mate-
riałach W3 dostępnych pod adresem:
http://www. w3. org/DOM/

Ostatecznym celem DOM jest umożliwienie odczytywania i edycji wszystkich doku-


mentów HTML i XML w postaci drzewa obiektów, a nie ciągów — więc zamiast pisać
<Hl>To j e s t n a g ł ó w e k < / H l > , powinieneś użyć aplikacji DOM do stworzenia no-
wego węzła nagłówka w odpowiednim miejscu drzewa. Niestety standard ten jest jesz-
cze niedojrzały (interfejs PHP do DOM także). W chwili pisania tej książki rozszerzenie
PHP DOMXML było nieużyteczne (przynajmniej dla nas) i słabo udokumentowane,
więc nie mieliśmy okazji napisać przykładów kodu. Aktualne informacje znajdziesz na
witrynie poświęconej tej książce:
http://www. trout\vorks. com/phpbook

Funkcje PHP dla DOM


API DOM jest obiektowe: funkcje wysokiego poziomu tworzą i zwracają obiekty. Mo-
żesz wywołać funkcje wysokiego poziomu z tymi obiektami jako parametrami lub sko-
rzystać z programowania obiektowego wywołując metody obiektów. Na przykład:
Sattr = node->attributes() ; "

jest tym samym co:


Sattr = domxml attributes($node);
Rozdział 29. » PHP i XML________________________________________479

Aby wywołać funkcję wysokiego poziomu, dodaj do nazwy metody przedrostek domxml
i obiekt na początku listy parametrów. Możesz dowolnie przeplatać obie metody.

Funkcja może być wywołana z każdego miejsca. Metoda jest wywoły-


wana w kontekście obiektu. W rozdziale 30. znajdziesz więcej infor-
macji na temat korzystania z klas i obiektów.

W tabeli 29.1 zamieszczono listę funkcji DOM wysokiego poziomu wraz z opisem.

W czasie pisania tego rozdziału DOM API w PHP 4 nie było jeszcze usta-
lone. Obecnie prawdopodobnie dostępnych jest więcej klas i metod.

Tabela 29.1.
Zestawienie funkcji wysokiego poziomu XML DOM

Funkcja Opis
xmldoc ( ) Wymaga ciągu zwierającego dokument XML jako argumentu. Funkcja analizuje
dokument i tworzy obiekt Document
xmldocf ile ( ) Wygodna funkcja wymagająca nazwy pliku jako argumentu; odczytuje ten plik.
Następnie zachowuje się jak xmldoc ( )
xmltree ( ) Wymaga ciągu 7. dokumentem XML jako argumentu. Tworzy drzewo obiektów PHP
i zwraca obiekt DOM
Uwaga: drzewo obiektów zwrócone przez tę funkcję jest tylko do odczytu
new xmldoc ( ) Tworzy nowy pusty obiekt XML. Zwraca obiekt Document. Musisz dodać element
główny przy użyciu metody add root ( )

Tabela 29.2 zawiera listę klas DOM API wraz z opisem sposobu użycia.

Tabela 29.2.
Zestawienie klas DOMXML

Klasa Opis
Document Klasa hermetyzująca dokument XML. Zawiera element główny i DTD, jeżeli istnieje
Node Zamyka węzeł (element). Węzeł może być elementem głównym lub dowolnym innym
elementem. Węzły mogą zawierać inne węzły, dane znakowe i atrybuty
Attribute Klasa hermetyzująca atrybut obiektu. Atrybut jest definiowaną przez użytkownika
cechą węzła

Tabela 29.3 zawiera listę metod klasy Document wraz z opisem działania.
480___________________________________Część III » Techniki zaawansowani

Tabela 29.3.
Lista metod klasy Document

Metoda Opis
root ( ) Zwraca element główny dla dokumentu DOM
add root ( ) Dodaje główny element do dokumentu i zwraca nowy węzeł. Nazwa elementu jest
przekazana przez parametr
dump mem ( ) Zapisuje całe drzewo dokumentu XML do ciągu

Tabela 29.4 zawiera metody klasy Node wraz z opisem działania.

Tabela 29.4.
Lista metod klasy Node

Metoda Opis
a t t r i b u t e s() Zwraca tablicę asocjacyjną z nazwami atrybutów i ich wartościami
g e t a t t r() Zwraca wartość atrybutu o podanej nazwie
setattr ( ) Ustawia wartość atrybutu o podanej nazwie. Jeżeli atrybut nie istnieje, zostanie
utworzony
children() Zwraca listę węzłów potomnych
lastchildt) Zwraca ostatni węzeł potomny z listy tych węzłów
parent ( ) Zwraca węzeł rodzica lub FALSE, jeżeli jest to węzeł główny
node ( ) Tworzy nowy węzeł i zwraca go
n e w child ( ) Tworzy nowy węzeł potomny do bieżącego węzła i zwraca go. Wymaga ciągu
zawierającego nazwę nowego węzła i ciągu zawierającego zawartość tego węzła jako
argumentu

Tabela 29.5 zawiera listę metod klasy Attribute wraz z opisem działania.

Tabela 29.5.
Lista metod klasy Attribute

Metoda Opis
attrname ( ) Ustawia nazwę atrybutu

SAX
Simple API for XML jest często używanym analizatorem dokumentów XML. bazuje na
zdarzeniach, co oznacza, że analizator wywołuje odpowiednie funkcje, gdy rozpozna
odpowiedni wyzwalacz w ciągu zdarzeń.
Rozdział 29. » PHP i XML__________________________________________481

SAX posiada interesującą historię, szczególnie w porównaniu z DOM. SAX API nie
jest zatwierdzone przez żaden organ standaryzujący. Tworzone jest przez programistów
grupy dyskusyjnej XML-DEV. Wielu z nich stworzyło już wcześniej analizatory XML,
nieposiadające standardowego API. Możesz się dowiedzieć więcej o twórcach SAX
z ich własnych witryn, np.:
http://www. megginson. com/SAX/index. html

SAX działa na podstawie procedur obsługi zdarzeń. W czasie przeglądania pliku XML
analizator rozpoznaje fragmenty XML, takie jak elementy, dane znakowe i jednostki
zewnętrzne. Każde z nich to zdarzenie. Jeżeli dostarczyłeś analizatorowi funkcję dla
określonego zdarzenia, w momencie napotkania takiego zdarzenia zatrzyma się i wy-
woła funkcję. Dane związane ze zdarzeniem zostaną przekazane do funkcji. Gdy funk-
cja obsługi zdarzenia zakończy się, analizator SAX będzie kontynuował analizę
dokumentu, wywołując funkcje związane ze zdarzeniami aż do końca dokumentu. Pro-
ces ten jest jednokierunkowy, od początku do końca dokumentu — analizator nie może
cofnąć się, ani wykonać pętli.

jako prosty przykład apiszemy funkcję obsługi zdarzenia związanego z elementem


XML <paragraphx/paragraph>, która doda znaczniki HTML < P > < / P > na począt-
ku i końcu danych znakowych. Jeśli nie będziesz mógł podać określonego akapitu
— funkcja będzie wykonana dla każdego miejsca wystąpienia tego zdarzenia.

Używanym przez PHP SAX analizatorem jest expat, którego autorem jest James Clark.
Expat jest często używanym i prawdopodobnie najszybszym analizatorem XML. Wię-
cej informacji na temat tego produktu na stronie WWW autora:
http://www.jclark.com/xml/

Expat jest również rozprowadzany z serwerem Apache. Jeżeli używasz innego serwera
WWW, prawdopodobnie będziesz musiał skopiować go z witryny autora.

Niestety termin „analizator XML" może określać bibliotekę, taką jak


expat, lub blok funkcji PHP obsługujących XML. Słowa „tworzyć" i „wy-
woływać" odnoszą się do drugiego znaczenia. Także każda funkcja PHP
XML używająca słowa analizator odwołuje się do drugiego znaczenia.

Użycie SAX
Sposób użycia SAX zależy od założeń, ale istniej ą części wspólne:
1. Rozpoznanie rodzajów obsługiwanych zdarzeń.
2. Napisanie funkcji obsługi dla każdego zdarzenia, np. funkcja do obsługi da-
nych znakowych oraz elementów początkowych i końcowych.
482___________________________________Część III » Techniki zaawansowane

3. Tworzenie analizatora za pomocą xml_parser_create ( ) i wywołanie go


przez x m l _ p a r s e ( ) .
4. Zwolnienie pamięci używanej przez analizator za pomocą xml_parser_
free ( ) .

Prosty przykład zamieszczony na wydruku 29.6 pokazuje użycie wszystkich podstawo-


wych funkcji XML.

Wydruk 29.6. Prosty analizator XML (simpleparser.php)______________________________


<?php
$file = "recipe.xml";

// Wywoływana na początku każdego elementu


function startElement(Sparser, Sname, $attrs) (
print "<B>Sname =></B> ";
}
// Wywoływana na końcu każdego elementu
function endElement(Sparser, $name) (
print "\n";
)
// Wywoływana dla danych znakowych
function characterData(Sparser, Svalue) {
print "$value<BR>";
(
// Definiowanie analizatora
Ssimpleparser = xml_parser_create(} ;
xml_set_element_handler(Ssimpleparser, "startElement", "endElement");
xml_set_character_data_handler(Ssimpleparser, "characterData");

// Otwarcie pliku XML do odczytu


if (!(Sfp = fopenISfile, "r"))) (
die("Nie mogę otworzyć pliku XML");
)
// Analiza
while(Sdata = freadtSfp, filesize(Sfile))) (
if (!xml_parse(Ssimpleparser, Sdata, feof(Sfp))) (
die(xml_error_string(xml_get_error_code(Ssimpleparser)));
}
}

If Zwolnienie pamięci
xml_parser_free(Ssimpleparser);
?>

Opcje SAX
Analizator XML w SAX API posiada dwie opcje konfiguracji, jedną do wyrównywania
znaków, drugą do kodowania wyniku.

Wyrównywanie znaków jest przeżytkiem, ponieważ XML reaguje na wielkość liter.


Wczesne wersje SGML i HTML ignorowały wielkość liter, dlatego stworzono funkcję
wyrównywania znaków (zamiana w czasie analizy wszystkich liter na małe bądź duże)
w celu uzyskania jednolitych wyników. W ten sposób przeglądarka łączy znacznik <p>
Rozdział 29. » PHP i XML_________________________________________483

TA znacznikiem < / P > . Wyrównywanie znaków powodowało problemy z lokalizacją,


więc po wielu debatach postanowiono, że w XML wielkość liter będzie miała znacze-
nie. Jeśli wyrównywanie jest aktywne, nazwy węzłów przekazane do funkcji obsługi
zdarzeń są zamieniane na wielkie litery. Węzeł o nazwie mynode zostaje zmieniony na
MYNODE. Jeśli wyrównywanie znaków jest wyłączone, znacznik <paragraph> nie zo-
stanie skojarzony z zamykającym go </PARAGRAPH>.

Wyrównywanie znaków jest domyślnie włączone. Jeżeli nie wyłączysz


go za pomocą xmi_parser_set_option(), funkcje obsługi zdarzeń
będą otrzymywały znaczniki zapisane WIELKIMI LITERAMI.

Funkcje obsługi zdarzeń otrzymują dane tekstowe z analizatora XML. Kodowanie tek-
stu przekazywanego do funkcji obsługi zdarzeń jest nazywane „kodowaniem wyniku".
Domyślnie kodowanie jest identyczne z kodowaniem w źródłowym dokumencie, zna-
nym jako „kodowanie źródła". Możesz zmieniać kodowanie wyniku, jeżeli chcesz ana-
lizować tekst z innym kodowaniem niż pierwotnie.

Opcje te można odczytywać i ustawiać za pomocą funkcji xml_parser_get_


option ( ) i xml_parser_set_option ( ) . Wyrównywanie znaków jest kontrolowane
przez stałą XML_OPTION_CASE_FOLDING, a kodowanie wyniku za pomocą stałej
XML_OPTION_TARGET_ENCODING.
$new_parser = xml_jparser_create ( ' US-ASCII' ) ;
Scase_folding = xml_parser_get_option(XML_OPTION_CASE_FOLDING);
$change_folding = xml__parser_set_option ($new_parser,
XML_OPTION_CASE_FOLDING, I);

$target_encoding =
xml_parser_get_option(XML_OPTION_TARGET_ENCODING);
Schange_encoding = xml_parser_set_option{$new_jparser,
XML_OPTION_TARGET_ENCODING, 'UTF-8');

Funkcje PHP dla SAX


Tabela 29.6 zawiera najważniejsze funkcje SAX wraz z opisem działania.

Tabela odwołuje się do dwóch nowych koncepcji: jednostek XML i in-


strukcji przetwarzania. Jednostka jest zapisanym fragmentem XML.
Określenie to jest używane w przypadku dokumentów XML zapisanych
jako kilka plików, ale analizowanych jako jeden dokument. Każdy do-
kument XML zawarty w jednym pliku jest również jednostką. Instrukcje
przetwarzania są instrukcjami dla serwera: <?php ?> lub <?xmi ?>.
484___________________________________Część III » Techniki zaawansowane

Tabela 29.6.
Zestawienie funkcji PHP dla SAX

Funkcja Opis

xml parser create ( ) Funkcja tworzy nową instancję analizatora XML. Możesz używać kilku
osobnych analizatorów w tym samym czasie. Zwraca analizator XML
lub FALSE w przypadku niepowodzenia
Posiada identyfikator kodowania (na przykład UTF-8) jako opcjonalny
argument. Jeżeli nie zostanie podane kodowanie, używane jest
ISO-8859- 1
xral parser f r e e ( ) Zwalnia pamięć używaną przez xmljparser_create ( )
xml parser get o p t i o n ! ) Pobiera wartość opcji analizatora, ustawianych przez
xml_parser set^option ( ) . Dostępne opcje:
XML OPTION TARGET ENCODING i XML OPTION TARGET ENCODING

xml parser set option ( ) Ustawia opcje analizatora. Dostępne opcje:


XML OPTION TARGET ENCODING i XML OPTION TARGET ENCODING

xml parse ( ) Funkcja uruchamia analizator XML. Jej argumentami są analizator


utworzony za pomocą xml parser create ( ) , ciąg zawierający XML
oraz opcjonalny znacznik zakończenia. Analizator nie zakończy pracy,
dopóki nie zostanie wywołany z ustawionym na TRUE znacznikiem
zakończenia
xml get error_code() Jeżeli analizator napotka błąd, analiza nie powiedzie się. Wywołanie tej
funkcji pozwala na odczytanie kodu błędu
xml error string ( ) Podając kod błędu, uzyskany z funkcji xml_get_error code ( ) ,
zwraca błąd w postaci tekstowej
Przykład: print xml error string ( xml get
error code ( S p a r s e r ) ) ;
xml get current line Zwraca położenie analizowanego miejsca w pliku XML. Zwracany jest
number ( ) numer linii lub FALSE w przypadku podania nieprawidłowego analizatora
xml get current column Zwraca położenie analizowanego miejsca w pliku XML. Zwracany jest
number ( ) numer kolumny lub FALSE w przypadku podania nieprawidłowego
analizatora
xml get current byte Zwraca położenie analizowanego miejsca w pliku XML. Zwracana jest
index ( ) liczba określająca, ile bajtów danych przeanalizowano, lub FALSE
w przypadku podania nieprawidłowego analizatora
xml set object ( ) Wymaga analizatora i referencji do obiektu (składnia: &$obiekt).
Po wywołaniu tej funkcji wszystkie funkcje obsługi zdarzeń stają się
metodami podanego obiektu. Nie ma możliwości odwołania tego
wywołania, więc należy tej funkcji używać rozważnie
xml set element handler ( ) Funkcja pozwala na ustawienie dwóch funkcji obsługi zdarzenia.
Pierwsza obsługuje rozpoczęcie zdarzenia i ma dostęp do nazwy
elementu oraz tablicy asocjacyjnej jego elementów. Druga obsługuje
zakończenie elementu po jego analizie. Funkcja ma dostęp do
zawartości elementu
Rozdział 29. » PHP i XML.__________________________________________485

Tabela 29.6.
Zestawienie funkcji PHP dla SAX (ciąg dalszy)

Funkcja Opis
xml set c h a r a c t e r data Ustawia funkcję obsługi danych tekstowych. Otrzymuje ciąg z danymi
handler ( ) tekstowymi z elementu jako argument
xml set e x t e r n a l e n t i t y Ustawia funkcję obsługi dla odwołania do zewnętrznej jednostki.
r e f handler ( ) Odwołania wewnętrzne są obsługiwane automatycznie. Argumentami
funkcji są: analizator, ciąg zawierający nazwę zapisu, ciąg zawierający
podstawowy URI używany do odczytywania URI dla SYSTEM lub
PUBLIC, ciąg zawierający URL SYSTEM, jeżeli istnieje, i ciąg zawierający
ciąg P U B L I C , jeżeli istnieje. Podstawowy URL jest póki co pusty
xml set p r o c e s s i n g Ustawia funkcję obsługi instrukcji przetwarzania, Funkcja otrzymuje
instruction h a n d l e r ( ) jako argumenty: analizator, cel instrukcji przetwarzania i tekst instrukcji
przetwarzania
Funkcja obsługi musi wykonać instrukcje przetwarzania. Jeżeli cel jest
znany, na przykład PHP, analizator może wykonać funkcję exal ( ) na
tekście. Jest to świetny sposób na wbudowywanie skryptów do XML
Uwaga: wykonuj tylko kod z zaufanego źródła. Musisz być
przygotowany na komunikaty typu „Undefined error: 0 in Unknown on
line 0" (niezdefiniowany błąd: 0 w nieznanym pliku w wierszu 0)
xml set u n p a r s e d e n t i t y Ustawia funkcję obsługi nieanalizowanego elementu. Gdy analizator
decl parser ( ) napotyka na element zadeklarowany jako N DAT A, nie może analizować
tego elementu, ale wywołuje funkcję jego obsługi
xml set notation decl Funkcja ustawia nazwę funkcji obsługi, wywoływaną w przypadku
handler ( ) napotkania przez analizator deklaracji < ! NOTATION. Notacjami są
nazwy i identyfikatory dołączone do nieanalizowanego elementu.
Ponieważ elementy takie nie są analizowane przez analizator XML,
ich właściwe użycie jest domeną aplikacji. Notacje są pomyślane jako
dodatkowe informacje na temat typu i obsługi nieanalizowanych
danych. Argumentami funkcji są: analizator, ciąg zawierający nazwę
notacji, ciąg zawierający podstawowy URI używany do odczytywania
URI dla SYSTEM lub PUBLIC, ciąg zawierający URL SYSTEM, jeżeli
istnieje w deklaracji < ! NOTATION, i ciąg zawierający ciąg PUBLIC,
jeżeli istnieje. Podstawowy URL jest póki co pusty
xml set d e f a u l t h a n d l e r ! ) Ustawia domyślną funkcję obsługi zdarzeń. Jeżeli nie została ustawiona
funkcja obsługi zdarzenia, wywoływana jest domyślna funkcja obsługi
Funkcja otrzymuje analizator i ciąg zawierający nieobsłużone dane jako
argument, na przykład deklarację zapisu bądź odwołanie do zewnętrznej
jednostki
u t f B decode ( ) Funkcja przekodowuje ciąg z UTF-8 na ISO-8859- 1. Wymaga ciągu
UTF-8 jako argumentu i zwraca ciąg ISO-8859- 1
u t f S encode ( ) Funkcja przekodowuje ciąg z ISO-8859- 1 na UTF-8. Wymaga ciągu
ISO-8859-1 jako argumentu i zwraca ciąg UTF-8
486________________________________Część III » Techniki zaawansowane

Przykładowa aplikacja SAX


Poniżej zamieszczona seria skryptów (wydruki 29.7 do 29.10) zapisuje do pliku XML
dane z formularza, pozwalają też zmieniać wartości w tym pliku. Część edycji pliku
XML wykorzystuje bezpłatną klasę PHP o nazwie xml-tree, której autorem jest Eric van
den Vlist, dostępną pod adresem:
hup://patches, dyomedea. com/php3/generic/xml/xmltree. inc

Wydruk 29.7. Formularz zbierający dane do pliku XML (pollform.php)______________________


<HTML>
<HEAD>
<TITLE>Tworzenie ankiety</TITLE> .
</HEAD>

<BODY>
<CENTERXH3>Tworzenie ankiety</H3x/CENTER>

<P>Zdefiniuj ankietę za pomocą tego formularza:</P>


<FORM METHOD="post" ACTION="writepoll.php">

<P>Podaj <B>krótka</B> nazwę tej ankiety, na przykład


<FONT COLOR="red">Kolory</FONT>.<BR>
<INPUT TYPE=TEXT NAME="PollNarae" SIZE=30>
</P>

<P>Ankieta powinna się <B>rozpocząć</B> tego dnia (DD.MM.RRRR):


<INPUT TYPE=TEXT Name="Poll_Startdate" SIZE=10>
</P>

<P>Ankieta powinna się <B>zakończyć</B> tego dnia (DD.MM.RRRR):


<INPUT TYPE=TEXT NAME="Poll_Enddate" SIZE=10>
</P>

<P>To jest pytanie ankiety (<FONT COLOR="blue">np. Dlaczego kurczak przeszedł


przez jezdnie?</FONT>):
<INPUT TYPE=TEXT NAME="Poll_Question", size=100>
</P>

<P>To są zdefiniowane p r z e z ciebie możliwe odpowiedzi


(<FONT COLOR="darkgreen">np. Tak, N i e , Nie w i e m ? < / F O N T > ) . Podaj t y l k o t y l e , ile
p o t r z e b u j e s z . P a m i ę t a j , że dobra a n k i e t a , to krótka a n k i e t a . < B R >
<INPUT TYPE=TEXT NAME="Raw_Poll_Option[)" SIZE=25XBR>
<INPUT T Y P E = T E X T NAME="Raw_Poll_Option [ ] " SIZE=25xBR>
<INPUT TYPE=TEXT NAME="Raw_PollJ3ption [ ] " S I Z E = 2 5 X B R >
<INPUT TYPE=TEXT NAME="Raw_Poll_Option [ ] " SIZE-25XBR>
<INPUT T Y P E = T E X T NAME="Raw_Poll_Option [ ] " SIZE=25XBR>
<INPUT TYPE=TEXT NAME="Raw_Poll_Option [ ] " SIZE=25XBR>
</P>

<INPUT TYPE="submit" NAME="Submit" VALUE="Doda] ankietę">


</FORM>

</BODY>
</HTML>

Wydruk 29.8. Skrypt zapisujący plik XML (writepoll.php)___________________________


<html>
<head>
<title>Zapis pliku XML</title>
</head>
Rozdział 29. » PHP i XML_________________________________________487

<body>
<?php

Spollfile = "poll.xml";

// Odczytanie pliku XML do ciągu


Sfd=fopen("Spollfile", "r") or die{"Nie mogę otworzyć pliku.");
Sfstr = fread(Sfd, filesize(Spollfile)) or die("Nie mogę czytać pliku. Sprawdź
uprawnienia.");
fclose(Sfd);

// Formatowanie zbioru odpowiedzi.


SPollName = str_replace("\'", "", SPollName);
$PollName = str_replace(" ", "_", $PollName);

for($r=0; $r<=5; $r++) {


if(!empty($Raw_Poll_Option[Sr]))
(
SPoll_Option[$r] = "SPollName-".str_replace("'", "", SRaw_Poll_Option[$r]);
$Poll_Option[$r) = "$PollName-".str_replace(" ", "_", SRaw_Poll_Option[Sr]);
}
else
(
$Poll_Option[Sr] = "" ;
)
SRespSet .= "\t<response resource=\"$Poll_Option[$r]\"/>\n";
$Resps .= "<response
id=\"SPoll_Option[Sr]\">\n\t<text>$Raw_Poll_Option[Sr]</text>\n</response>\n";
}
//Dodanie nowej ankiety
Sseparator = "</PollList>";
Sdivide = explode{Sseparator, Sfstr);
Sglue =
"\t<Poll name=\"SPollName\"/>
</PollList>

<Poll id=\"SPollName\">
\t<StartDate>SPoll_Startdate</StartDate>
\t<EndDate>SPoll_Enddate</EndDate>
\t<name>$PollName</name>
\t<text>SPoll_Question</text>
\t<display type=\"poll_display-Bar-Graph\"/>
\t<responseSet resource=\"SPollID-responseSet\" />
</Poll>

<responseSet id=\"$PollName-responseSet\">
SRespSet</responseSet>

SResps";

Snewxml = implode(Sglue, Sdivide);

//Zapisz do pliku
Sfd = fopen(Spollfile, "w") or die ("Nie mogę otworzyć pliku do zapisu. Sprawdź
uprawnienia.");
Swritestr - fwrite(Sfd, Snewxml);

//Komunikat
echo "zapisano Swritestr do Spollfile.";
?>

</body>
</html>
488___________________________________Część III » Techniki zaawansowane

Wydruk 29.9. Edytor XML (editpoll.php)____________________________________


<html>
<head>
<title>Edytor ankiety XML</title>
</head>

<body>
<?php

require "xmltree.inc";

$pollfile = "poll.xml";

// Wczytanie pliku rdf jako ciąg


$fd-fopen("Spollfile", "r") or die("Nie mogę otworzyć pliku.");
Sfstr = fread($fd, filesize(Spollfile)) or die("Nie mogę czytać z pliku. Sprawdź
uprawnienia.");
fclose($fd);

//Pokazanie starych wartości lub zapisanie nowych


if (!IsSet($stage)) {

// Pobieranie elementów i atrybutów


SXMLtree = new XMLtree;
if (Serr = $XMLtree->parse("Sharrisfile"))
f
die (Serr);
)

//Wyświetlenie formularza z wartościami


echo "<FORM METHOD=\"post\" ACTION=\"editpoll.php\">";

$pollname = $XMLtree->getEltByPath("/rdf(1)/poll($i)/name(1)");
echo "Nazwa ankiety: <INPUT TYPE=\"text\" SIZE=50 NAME=\"PollNarae\"
VALUE=\"$pollname\"XBR>";

$poll_startdate = $XMLtree->getEltByPath("/rdf(1)/poll($i)/startdate(1) ") ;


echo "Data startowa: <INPUT TYPE=\"text\" SIZE=10 NAME=\"Poll_Startdate\"
VALUE=\"$poll_startdate\"XBR>";

5poll_enddate = $XMLtree->getEltByPath("/rdf(1)/poll($i)/enddate(l)");
echo "Data zakończenia: <INPOT TYPE=\"text\" SIZE=50 NAME=\"Poll_Enddate\"
VALUE=\"$poll_enddate\"XBR>";

$poll_question = $XMLtree->getEltByPath("/rdf(1)/poll(Si)/text(l)");
$poll_question = stripslashes($poll_question);
echo "Pytanie ankiety: <INPUT TYPE=\"text\" SIZE=100 NftME=\"Poll_Question\"
VALUE=\"$poll_question\"XBR>";

St = l;
Slow = ( (Si-1)*6)+l;
$hi = ( (Si-l)*6)+6;
for(Sr=$low; $r<=$hi; $r++) {
$poll_option($t] = SXMLtree->getEltByPath("/rdf(1)/response($r)/text(1) ") ;
echo "Odpowiedzi: <INPUT TYPE=\"text\" SIZE=25 NAME=\"Raw_Poll_Option[St J \"
VALUE=\"Spoll_option[St]\n\"><BR>";
St + + ;
}

echo " < I N P U T T Y P E = \ " h i d d e n \ " N A M E = \ " i \ " V A L U E = S i > " ;


echo " < I N P U T T Y P E = \ " h i d d e n \ " NAME=\"stage\" VALUE=lXBR>" ;
echo "<INPUT TYPE=\"submit\" VALUE=\"Presto-chango\">";
echo "</FORM>";
) else (
//Czytanie pliku XML jako ciąg
$fd=fopen("Spollfile", "r") or die("Nie mogę otworzyć pliku.");
Sfstr = freadtSfd, filesize(Spollfile)) or die("Nie mogę czytać z pliku. Sprawdź
uprawnienia.");
Rozdział 29. « PHP i XML________________________________________489

fclose(Sfd);
// Pobranie elementów i atrybutów
SXMLtree = new XMLtree;
if ($err = $XMLtree->parse("$harrisfile"))
(
die (Serr);
)
$pollname = $XMLtree->getEltByPath("/rdf(1)/poll(Si)/name(l)");
Spoll_startdate = SXMLtree->getEltByPath("/rdf(1)/poll($i)/startdate(1)");
Spoll_enddate = $XMLtree->getEltByPath("/rdf(1)/poll(Si)/enddate(1)");
Spoll_question = SXMLtree->getEltByPath("/rdf(1)/poll(Si)/text(1)");
St = l;
Slow = ( (Si-1)*6)+l;
$hi = ( (Si-1)*6)+6;
for($r=Slow; Sr<=$hi; Sr++) (
Sraw_poll_option[$t] = SXMLtree-
>getEltByPath("/rdf(1)/response(Sr)/text(1)");
St + + ;
)
//Formatowanie zbioru odpowiedzi.
for(St-l; St<=6; $t + + ) (
if(!empty(Sraw_poll_option[$t]))
{
$poll_option[$t] = "$pollname-$raw_poll_option[$t]";
}
else
1
$poll_option(St] = "";
}
SrespSet .= "\t<response resource=\"Spoll_option[St]\"/>\n";
Sresps .- "<response
id=\"Spoll_option[St]\">\n\t<text>Sraw_poll_option[St]</text>\n</response>\n";
)

//formatowanie nowych danych


SPollName = str_replace("\'", "", SPollName);
SPollName = str_replace(" ", "_", SPollName);
for($r=l; Sr<=6; Sr++) {
if(!empty(SRaw_Poll_Option[Sr]))
f
SPoll_Option[Sr) = "SPollName-".str_replace("\'", "",
SRaw_Poll_Option[Sr]);
$Poll_Option[Sr] = "SPollName-".str_replace(" ", "_",
$Raw_Poll_Option[Sr]);
)
else
{
SPoll_Option[Sr] = "";
(
SRespSet .= "\t<response resource=\"SPoll_Option[Sr]\"/>\n";
SResps .= "<response
id=\"$Poll_Option[Sr]\">\n\t<text>$Raw_Poll_Option[Sr]</text>\n</response>\n";
}

//Edycja pozycji PollList


Sseparator = "\t<Poll name=\"Spollname\"/>\n";
Sdivide = explode(Sseparator, Sfstr);
Sglue = "\t<Poll name=\"SPollName\"/>\n";
Sfstr = implode(Sglue, Sdivide);

//Edycja głównej ankiety


Sseparator = "<Poll id=\"Spollname\">
\t<StartDate>$poll_startdate</StartDate>
\t<EndDate>$poll_enddate</EndDate>
\t<name>Spollname</name>
\t<text>Spoll_question</text>
\t<display type=\"poll__display-Bar-Graph\"/>
490___________________________________Część III » Techniki zaawansowane

\t<responseSet resource=\"$pollname-responseSet\"/>
</Poll>

<responseSet id=\"Spollname-responseSet\">
5respSet</responseSet>

Sresps";

Sdivide = e x p l o d e ( S s e p a r a t o r , S f s t r ) ;

$glue = "<Poll i d = \ " $ P o l l N a m e \ " >


\t<StartDate>$Poll_Startdate</StartDate>
\t<EndDate>$Poll_Enddate</EndDate>
\t<narae>5PollName</name>
\t<text>$Poll_Question</text>
St<display type=\"poll_display-Bar-Graph\"/>
\t<responseSet r e s o u r c e = \ " S P o l l N a m e - r e s p o n s e S e t \ " / >
</Poll>

<responseSet id=\"$PollName-responseSet\"> *
$RespSet</responseSet>

SResps";

Snewxml = implode($głue, $divide};

//Zapis do pliku
Sfd = fopen($pollfile, "w");
$writestr = fwrite($fd, $newxml);

//Komunikat
echo "Zapisano $writestr do $pollfile.";
(
?>

</body>
</html>

Wydruk 29.10. PlikXML (poll.xml)______________________________________


<PollDefs>

<PollList id="raovie-PollList">
<Poll name="Best_Summer_Filra"/>
<Poll name=""/>
</PollList>

<Poll id="">
<StartDatex/StartDate>
<EndDateX/EndDate>
<namex/name>
<textx/text>
<display type="poll_display-Bar-Graph"/>
<responseSet resource="-responseSet"/> •
</Poll>

<responseSet id="-responseSet">
<response resource=""/>
<response resource=""/>
<response resource=""/>
<response resource=""/>
<response resource=""/>
<response resource=""/>
</responseSet>

<response id-"">
<textx/text>
</response>
<response id="">
Rozdział 29. » PHP i XML_________________________________________491

<textx/text>
</response>
<response id="">
<textx/text>
</response>
<response id="">
<textx/text>
</response>
<response id="">
<textx/text>
</response>
<response id="">
<textx/text>
</response>

<Poll id="Best_Summer_Film">
<StartDate>09/01/2000</StartDate>
<EndDate>10/01/2000</EndDate>
<name>Naj lepszy film lata</name>
<text>Jaki był najlepszy film tego lata?</text>
<display type="poll_display-Bar-Graph"/>
<responseSet resource="Best_Summer_Filni-responseSet"/>
</Poll>

<responseSet id="Best_Summer__Film-responseSet">
<response resource="Best__Summer__Fil[n-Shaf t"/>
<response resource="Best_Summer_Film-Xmen"/>
<response resource="Best_Summer__Filn\-Gladiator"/>
<response resource="Best_Summer_Film-Battlefield_Earth"/>
<response resource="Best_Summer_Film-MI2"/>
</responseSet>

<response id="Best_Summer_Film-Shaft">
<text>Shaft</text>
</response>
<response id="Best_Summer_Film-Xmen">
<text>X Men</text>
</response>
<response id="Best_Summer_Film-Gladiator">
<text>Gladiator</text>
</response>
<response id="Best_Summer_Film-Battlefield_Earth">
<text>Battlefield Earth</text>
</response>
<response id="Best_Summer_Film-MI2">
<text>Mission Impossible 2</text>
</response>

</PollDefs>

Pułapki i wyszukiwanie błędów


DOM i SAX analizują tylko prawidłowo zbudowane dokumenty XML. Jeżeli analizator
odrzuci twój XML, może on zawierać błędy. Jeżeli wygląda dobrze, przepuść go przez
analizator sprawdzający lub kontroler XML, na przykład dostępny na stronie http://
www.xml.com/xml/pub/tools/ruwf/check.html.

Jeżeli nie możesz odczytać lub zapisać dokumentów XML na dysku, sprawdź, czy ser-
wer WWW ma uprawnienia do wykonania takiej operacji.
492___________________________________Część III » Techniki zaawansowane

Jeżeli API DOM zwraca błąd „fatal function not found error", oznacza to, że moduł
DOMXML nie został zainstalowany. Uruchom phpinf o ( ) i odszukaj pozycję domxml.
Jeżeli jej nie ma, musisz powtórnie skompilować PHP z modułem DOMXML.

W chwili pisania książki obsługa DOMXML dla Windows nie była jeszcze
dostępna. Biblioteka Gnome XML była już kompilowana dla Windows,
ale tylko dla odważnych programistów C, a niejako .dli dla PHP.

DOM API jest również nowością w PHP 4, więc wiele szczegółów może jeszcze nie
działać prawidłowo. Pamiętaj o tym i sprawdzaj listę błędów w przypadku problemów.
http://bugs.php. net/

Podsumowanie
XML jest niezależnym od aplikacji formatem wymiany danych, który umożliwi łatwiej-
sze i szybsze tworzenie aplikacji WWW w przyszłości. XML i HTML, spadkobiercy
SGML, są na pierwszy rzut oka podobne. Oba formaty posiadają znaczniki i atrybuty.
W XML elementy te można definiować, natomiast struktura HTML jest zdefiniowana
przez standard HTML i nie zawiera informacji o strukturze dokumentu.

XML wymaga zamykania znaczników, niezachodzenia na siebie znaczników, oznacza-


nia znaków specjalnych i głównego elementu w dokumencie. Może być również po-
prawny w sensie formalnej deklaracji struktury, zapisanej w definicji typu dokumentu,
czyli DTD. DTD może być wewnętrzne bądź zewnętrzne w stosunku do dokumentu,
można je umieścić na innym serwerze. Zawiera deklarację typów, atrybutów i nazw
elementów w pliku XML.

Istnieje obecnie niewiele narzędzi pozwalających na stworzenie, zmianę i wyświetlanie


XML. Możesz użyć jednego z dwóch API PHP do obsługi XML, SAX lub DOM do na-
pisania własnych narzędzi. API mają różne zastosowania. SAX jest bazuje na zdarze-
niach, DOMXML tworzy w pamięci drzewo obiektów.

PHP z SAX może być używany do zapisania prawidłowego pliku XML na podstawie
wartości podanych w formularzu. Jest również używany do zamiany XML na HTML
w celu łatwego wyświetlania danych w przeglądarce. Innym zadaniem jest pobieranie
danych z bazy i zapisywanie ich do pliku XML w celu wymiany z innymi organizacjami.
Rozdział 30.
Programowanie obiektowe
wPHP
W tym rozdziale:
* Co to jest programowanie obiektowe?
* Klasy, obiekty i typy w PHP
** Wyświetlanie i drukowanie obiektów
+ Kopiowanie obiektów: referencje i aliasy
+ Serializacja obiektów
* Zewnętrzne interfejsy: COM, Java i CORBA
* Przykładowa aplikacja obiektowa

Programowanie obiektowe jest zbiorem technik organizacji kodu wokół jednostek lub
obiektów reprezentowanych przez ten kod. Języki obiektowe zawierają wsparcie dla ta-
kiej organizacji.

Tradycyjne proceduralne języki programowania działają poprzez stosowanie procedur


na danych. Dane mogą być reprezentowane przez ciągi, liczby, tablice lub dowolne inne
struktury danych. Procedury są reprezentowane przez definicje procedur bądź funkcji.
Na przykład fragment programu kucharskiego może stosować do reprezentowania prze-
pisu odpowiednio stworzoną tablicę ciągów. W innej części tego programu mogą istnieć
procedury do manipulacji przepisami, jak na przykład drukowanie w ładnej formie.
Z jednej strony istnieją dane na temat przepisu, z drugiej funkcje operujące na przepi-
sach. Programista łączy je, dbając, aby funkcje przepisów były wywołane na danych
przepisów.

W przypadku programowania obiektowego obiekty danych, takie jak przepisy, mają


bardziej oficjalne znaczenie i łączą w sobie zarówno dane, jak i kod. Wersja obiektowa
naszego programu kucharskiego powinna zadeklarować nowy typ danych (klasę) dla
przepisu; typ ten będzie zawierał nie tylko dane poszczególnych części przepisu, ale
494___________________________________Część III » Techniki zaawansowane

również definicje funkcji (metod) operujących na danych. O takiej definicji klasy prze-
pisu można powiedzieć, że zawiera składniki (dane) i etapy (dane) oraz sposób, w jaki
przepis będzie wydrukowany (metoda). Ważne jest, że metody są integralną częścią
klasy — zdefiniowanie sposobu drukowania przepisu nie ma wpływu na sposób druko-
wania czegokolwiek innego.

Aby napisać program obiektowy, musisz zdefiniować klasy, a następnie stworzyć obiekty
za pomocą zdefiniowanych wcześniej klas. Zamiast tworzyć globalne funkcje i zmienne,
łączysz zmienne z funkcjami pozwalającymi na ich użycie i modyfikacje.

Jak dobre
jest programowanie obiektowe?
Programowanie obiektowe nie daje magicznej siły — nie ma programu obiektowego,
którego nie da się przepisać na wersję proceduralną. Programowanie obiektowe zmusza
do określonego zorganizowania kodu. Organizacja może ułatwiać wymyślenie sposobu
rozwiązania problemu, ponieważ prowadzi do naturalnego modelowania procesów za-
chodzących w świecie rzeczywistym.

Dodatkową korzyścią programowania obiektowego jest dziedziczenie klas. Po zdefi-


niowaniu jednej klasy możesz stworzyć nową podklasę, korzystając z oryginalnej. No-
wa podklasa będzie posiadała te same dane i metody, poza tymi, które postanowiłeś
zmienić. Można w ten sposób powtórnie użyć kod (twoje klasy dziedziczą z klas stwo-
rzonych w językach Java czy C++).

Należy powiedzieć, że większość korzyści z programowania obiektowego staje się wi-


doczna w przypadku bardzo dużych programów. Dla projektantów programu o stu ty-
siącach wierszy, tworzonego przez wielu programistów, dyscyplina wprowadzana przez
programowanie obiektowe będzie istotna, dla autorów dwustronicowego skryptu pro-
gramowanie obiektowe będzie chyba tylko rozrywką.

Terminologia programowania obiektowego


Zanim zajmiemy się specyfiką programowania obiektowego w PHP, zdefiniujmy kilka
nazw. Różnią się w one rozmaitych językach obiektowych, więc spróbujemy powie-
dzieć, co znaczą w różnych wariantach (a także co znaczą w PHP).
* Klasa jest planem, według którego język programowania tworzy typ obiektowy.
* Obiekt jest egzemplarzem klasy. Jeżeli klasa jest planem, to obiekt jest zbudo-
wany według planu. Same klasy nie są zwykle używane. W zamian na ich pod-
stawie tworzy się jeden lub więcej obiektów.
* Zmienna klasowa jest zmienną należącą do klasy (programiści baz danych mo-
gą myśleć w następujący sposób: zmienne klasy są tym dla klasy, czym pole
dla tabeli). W PHP zmienne klasy nazywa się również atrybutami.
Rozdział 30. » Programowanie obiektowe w PHP___________________________495

•* Funkcja klasowa jest funkcją należącą do klasy. Funkcje klasowe są również


nazywane metodami. W PHP funkcje klasowe nazywane są po prostu funkcjami.
4 Podklasa j est klasą, która dziedziczy lub rozszerza poprzednio zdefiniowaną klasę.
4 Nadklasajest klasą, z której dziedziczą inne klasy.

Obiekty, klasy i typy w PHP


Wszystkie zmienne w PHP 4 mają typy. Typem zmiennej może być integer, double,
string, array lub object. Jeżeli zmienna jest obiektem, posiada również klasę.

Jeżeli pisałeś kod PHP, jesteś przygotowany do pisania klas i obiektów. Łatwo stworzyć
prostą klasę:
class myClass
(
var Sattribute = "atrybut";

function show_attribute()
{
echo this->attribute. "<br>\n";
(
l

Ograniczenia w programowaniu obiektowym w PHP: czego brakuje?


Ten dopisek jest przeznaczony dla programistów znających inne języki obiek-
towe, którzy będą chcieli szybko się dowiedzieć, co potrafi, a czego nie potrafi
PHP 4. PHP nie jest obiektowym językiem skryptowym.
PHP 4 został napisany w C, a nie w C++; proces tworzenia jest procedural-
ny, a nie obiektowy. Jednak wraz ze wzrostem popularności PHP i liczby użyt-
kowników stopniowo wdrażane jest tworzenie obiektowe.
PHP 4 obsługuje większość podstawowych funkcji potrzebnych do zdefiniowa-
nia klasy i utworzenia obiektów. Przedstawiamy tutaj listę elementów, któ-
rych PHP 4 nie obsługuje w pełni:
* Dziedziczenie wielobazowe: PHP pozwala tylko na dziedziczenie
jednobazowe, ale obsługuje dziedziczenie łańcuchowe.
+ Interfejsy: brak obsługi.
* Klasy abstrakcyjne: brak obsługi.
* Przeciążanie: brak obsługi (kolejne funkcje o tej samej nazwie
przesłaniają poprzednie funkcje w definicji klasy, niezależnie od
argumentów).
* Prywatność: nie ma modyfikatorów dla składników prywatnych
i zabezpieczonych. Wszystko jest upublicznione.
+ Destruktory: brak obsługi.
4 Polimorfizm: polimorfizm generalnie działa, ale wymaga specjalnej
składni, aby dostać się do przesłoniętych funkcji klasy bazowej.
496___________________________________Część III « Techniki zaawansowane

Definicja klasy jest dosyć prosta. Jej struktura nie zmienia się:
class NAZWAKLASY [extends NAZWAKLASY_BAZOW J]
{
[atrybuty]
[funkcje]
)

Nawiasy kwadratowe oznaczaj ą opcjonalne części deklaracji. Atrybuty i funkcje nie są


obowiązkowe, ale klasa będzie bezużyteczna bez co najmniej jednego takiego składni-
ka. Kolejność atrybutów i funkcji w klasie nie jest istotna, ale zwykle umieszcza się
atrybuty na początku, a funkcje na końcu.

Atrybuty
Atrybuty są definiowane w postaci:
var Sattribute = "atrybut";

Definicja atrybutu nie może zawierać inicjalizacji niestatyczną wartością. Oznacza to,
że można ustawić początkową wartość zmiennej za pomocą ciągu, liczby całkowitej lub
zmiennoprzecinkowej, albo tablicy, ale nie można użyć do tego obiektu.

Atrybut może posiadać wartość o jednym z typów PHP 4 lub być obiektem o typie jed-
nej ze zdefiniowanych klas. Możesz deklarować ten sam atrybut wiele razy, ale będzie
miał on wartość nadaną w ostatniej inicjalizacji.

Atrybuty klasy mogą, ale nie muszą, być inicjowane. Jeżeli atrybutowi jest nadawana
wartość w deklaracji klasy, jest on inicjowany.

Nie można inicjować atrybutu obiektem.

PHP 3 pozwalał na użycie niestatycznej inicjacji dla zmiennych klas.


PHP 4 uważa to za błąd i przerywa analizę. Jest to problem dla osób
przenoszących istniejące biblioteki do PHP 4 lub korzystających z ko-
du obiektowego z wcześniejszych wersji PHP.

Funkcje
Funkcje są deklarowane w klasie podobnie jak te spoza klasy. Istnieją tylko dwie głów-
ne różnice:
* Aby dostać się do atrybutów obiektu, należy używać składni $this->attri-
bute. Nie należy używać this->$attribute, ponieważ nie działa. Forma
$this->$attribute działa tylko wtedy, gdy Sattribute jest ciągiem za-
wierającym nazwę atrybutu. Działa również w przypadku funkcji.
Rozdział 30. » Programowanie obiektowe w PHP___________________________497

var $attrl;

$attr_name = "attrl";
echo $this->$attr_name;

function functionl{) { ... }


Sfunction_name = "functionl";
$this->$function_name;

* Funkcje PHP 4, które wymagają nazw funkcji, jak na przykład array_


walk ( ) , nie obsługują funkcji obiektów (metod). Jest to znany problem, który
być może zostanie poprawiony w następnym wydaniu. Niektóre API mają od-
dzielne interfejsy dla obiektów, np. nowe API DOM XML.

W powyższym fragmencie kodu wystąpiła zmienna specjalna $this — wskaźnik bie-


żącego obiektu. Jest automatycznie dostępna we wszystkich funkcjach klasy, chyba że
funkcja jest wywołana statycznie.

Przeczytaj również część „Zasięg" poniżej.

W nazewnictwie obiektowym funkcja zdefiniowana wewnątrz klasy nazywana jest metodą.

Konstruktory
Konstruktor jest specjalizowaną funkcją zdefiniowaną w klasie. Posiada nazwę iden-
tyczną z nazwą klasy i może mieć argumenty. Klasa może posiadać tylko jeden kon-
struktor. Konstruktor jest wywoływany automatycznie w czasie tworzenia obiektu m
podstawie klasy. Używany jest do inicjalizacji atrybutów obiektu za pomocą odpowied-
nich wartości.

Dziedziczenie
Klasa w PHP 4 może dziedziczyć z innej klasy. Klasa podrzędna „dziedziczy" wszyst-
kie funkcje i atrybuty z klasy nadrzędnej, bez potrzeby ich ponownej deklaracji.
class super 9
(
function super_echo()
(
echo "SUPERCLASS. <br> \n";
)
}

class sub extends super


(
function sub_echo(}
{
echo "SUBCLASS. <br> \n";
)
(
Sobject = new sub(); // tworzenie obiektu
$object->sub_echo(); // wywołanie funkcji podklasy
$object->super echo(); // wywołanie odziedziczonej funkcji z nadklasy
498___________________________________Część III » Techniki zaawansowane

Klasę o nazwie „super" nazywamy nadklasą lub klasą bazową.

Klasa o nazwie „sub" to podklasa bądź klasa dziedzicząca. Dziedziczy (rozszerza) jedną
lub więcej klas. Klasy PHP 4 mogą dziedziczyć tylko z jednej klasy, ale klasa bazowa
może również dziedziczyć z innej klasy bazowej. Można powiedzieć, że klasy PHP
mogą mieć tylko jednego ojca, ale mogą mieć również dziadka.
class dziadek
(
}
class ojciec extends dziadek
(
)
class dziecko extends ojciec
l
)

Dziedziczenie z innej klasy jest procesem specjalizacji. Nadklasą jest przeznaczona do


zadań ogólnych, podklasa jest precyzyjnie dostosowywana do określonych potrzeb:
class Figura
{
l
class Figura_dwuwymiarowa extends Figura
l
)
class Kolo extends Figura_dwuwymiarowa
(
>

W tym przykładzie klasa Kolo dziedziczy z klasy Figura_dwuwymiarowa oraz z kla-


sy Figura, dlatego może korzystać z każdej z funkcji klas bazowych obiektu Kolo.

Przesłanianie
Podklasy automatycznie dziedziczą atrybuty i funkcje ze swoich klas nadrzędnych, ale
czasem po woduj ą konflikt nazw. Klasa nadrzędna może posiadać funkcję o takiej samej
nazwie jak funkcja w podklasie. PHP 4 musi zdecydować, którą z nich należy wywołać:
class super
{
function my_function()
{
echo "SUPERCLASS. <br> \n";
)
>
class sub extends super
{
function my_function()

echo "SUBCLASS. <br> \n";


)
I

Sobject = new sub;


Sobject->my_function() ;

Jeśli podklasa posiada funkcję o takiej samej nazwie jak nadklasą, podklasa przesiania
funkcję z nadklasy. Jeżeli przesłonięta funkcja jest wywołana w podklasie, wywoływa-
Rozdział 30. » Programowanie obiektowe w PHP_________________________499

na jest funkcja zdefiniowana w podklasie, a nie nadklasie. Funkcja zdefiniowana


w nadklasie jest nadal dostępna, ale do jej wywołania musimy zastosować inną skład-
nię. Należy użyć modyfikatora zasięgu w celu wskazania PHP 4, którą funkcję mamy
zamiar wywołać:
class sub extends super
(
function my_function()
(
super::my_function(};
}
)

W tym przykładzie funkcja podklasy wywołuje funkcję nadklasy, używając modyfika-


tora zasięgu, jakim jest operator „: :". Za jego pomocą wskazujemy PHP 4, że chcemy
wywołać funkcję pochodzącą z klasy super.

Za pomocą tego modyfikatora nie można odwoływać się do atrybutów nadklasy prze-
słoniętych w definicji podklasy.

Przeciążanie
Przeciążaniem w programowaniu obiektowym nazywamy sytuację, gdy w klasie ist-
nieje więcej niż jedna funkcja o tej samej nazwie. Interpreter lub kompilator odróżnia je
po liczbie i typach argumentów — każda z tych funkcji musi mieć unikalną sygnaturę
argumentów. PHP 4 nie obsługuje przeciążania. Poprawną sytuacją jest zdefiniowanie
w klasie więcej niż jednej funkcji o tej samej nazwie, jednak wszystkie definicje, poza
ostatnią, zostaną zignorowane.

Zasięg
Jeżeli pisałeś już funkcje, powinieneś znać pojęcie zasięgu. Zasięg to przestrzeń, w któ-
rej dostępna jest określona zmienna. W żadnej funkcji nie można używać zmiennych
globalnych bez ich wcześniejszego zadeklarowania jako globalne w ciele funkcji, albo
odwoływania się do nich poprzez tablicę GLOBALS [ ].

Zasięg w PHP 4 określa również to, która funkcja zostanie wywołana. Nie można bez-
pośrednio wywołać funkcji w klasie, nie używając modyfikatora zasięgu w celu wska-
zania, gdzie należy szukać tej funkcji.

Poniższe elementy istnieją w zasięgu i PHP 4 zawsze je odnajduje:


* wbudowane funkcje PHP 4 oraz funkcje zdefiniowane w załadowanych modułach;
* zmienne, którymi zarządza PHP, na przykład PHP_POST_VARS.

W „normalnej przestrzeni" strony PHP 4 w zasięgu znajdują się również poniższe


elementy:
500___________________________________Część III « Techniki zaawansowane

* funkcje zdefiniowane poza klasąj


** zmienne zadeklarowane poza klasą lub funkcją.

Funkcje, zmienne i definicje klas pozostają w zasięgu, nawet gdy są dołączane za po-
mocą funkcji i n c l u d e ( ) lub r e q u i r e ( ) .

Wewnątrz funkcji w zasięgu pozostają:


* zmienne zadeklarowane wewnątrz funkcji;
* zmienne przekazane do funkcji jako argumenty;

wewnątrz definicji klasy:


* funkcje w tej samej klasie, wywoływane dzięki zmiennej $this.

Atrybuty nie mogą być inicjowane wartościami zmiennych globalnych.


W definicji klasy dostęp do zmiennych globalnych jest dozwolony je-
dynie w ciele funkcji.

* Jedynymi zamiennymi spoza zasięgu, do których masz dostęp, są zmienne glo-


balne. Należy je wcześniej zdefiniować jako globalne lub odwoływać się do
nich poprzez tablicę $GLOBALS.
** W celu dostania się do funkcji spoza bieżącego zasięgu należy użyć modyfi-
katora zasięgu „: :". Pozwala to na podanie klasy, w której PHP ma szukać
funkcji:
klasa::nazwa_funkcj i(argumentl, ...)

* Jeżeli wywołujemy funkcję z nadklasy, ma ona dostęp do zmiennych i metod


obiektu.
+ Jeżeli składnia taka będzie użyta poza definicją klasy lub zostanie wskazana
klasa, z której bieżąca klasa nie dziedziczy, wywołanie to jest nazywane wy-
wołaniem funkcji statycznej. Funkcja nie posiada wtedy dostępu do wskaźnika
$this; nie można też wywoływać innych funkcji klasy bez użycia modyfi-
katora zasięgu.

Przypisywanie, aliasy i referencje


W celu uniknięcia niepotrzebnego kopiowania i zużywania pamięci PHP 4 korzysta
z techniki zwanej liczeniem referencji. Przyspiesza to działanie kodu, w którym nie wy-
stępują modyfikacje.

W przypadku, gdy tworzysz zmienną PHP lub korzystasz z istniejącej — używasz


przypisania:
Srinws yrrn >nna = ^1~ar;i ^mi^nn^f
Rozdział 30. » Programowanie obiektowe w PHP___________________________501

W poprzedniej wersji PHP przypisanie kopiowało wartość zmiennej. Jeśli $stara_


zmienna wskazywała obiekt, po przypisaniu powstawały dwa identyczne obiekty.

Jeżeli zmienisz wartość $nowa_zmienna lub $stara_zmienna, zmiany nie będą


miały wpływu na wartość drugiej ze zmiennych.

Liczenie referencji jest nową funkcją w PHP 4. Nie wpływa na działa-


nie kodu, jedynie przyspiesza go.

W rzeczywistości po przypisaniu mamy nadal jeden obiekt oraz wskazujące na niego


dwie zmienne. Co stanie się, gdy zmienimy jedną z tych zmiennych? Wówczas PHP 4
utworzy kopię zmiennej $stara_zmienna. Wprowadzenie zmian do $stara_zmienna
nie będzie miało wpływu na wartość $nowa_zmienna i odwrotnie.

Jeżeli chcesz, aby $nowa_zmienna i $stara_zmienna nadal wskazywały na tę samą


wartość, a zmiany w $nowa_zmienna wpływały na wartość Sstara_zmienna, mo-
żesz skorzystać z oferowanego przez PHP nowego mechanizmu: aliasów.

Aliasy to nowość w PHP 4.

Alias to zmienna będąca referencją do innej zmiennej. Nie posiada własnej wartości,
lecz wskazuje na wartość innej zmiennej. Może być używana jak każda inna zmienna:
możesz przypisywać jej wartość lub wywoływać funkcje, jeżeli jest obiektem.
$alias = &$zmienna;

Przytoczmy przykład pokazujący różnice pomiędzy aliasami i zwykłymi funkcjami:


$variable = "Pierwszy ciąg testowy";
$new_variable = "Pierwszy ciąg testowy";
$alias = &$variable;
echo "<BR>";
echo "Początkowe wartości:<br>";
echo "Oryginalna zmienna: ".$variable. "<br>";
echo "Przypisana zmienna: ".Snew_variable. "<br>";
echo "Alias: ".Salias. "<br>";
echo "<BR>";
echo "Zmiana przypisanej zmiennej ... <BR>";
$new_variable = "Zmieniony pierwszy ciąg testowy - zmienna";
echo "<BRXBR>";

echo "Po zmianie wartości new_variable:<BR>";


echo "Oryginalna zmienna: ".Svariable. "<br>";
echo "Przypisana zmienna: ".$new_variable. "<br>";
echo "Alias: ".Salias. "<br>";

echo "<BR>";
echo "Zmiana aliasu...<BR>";
$alias = "Zmieniony pierwszy ciąg testowy - alias";
echo "<BRXBR>";

echo "Po zmianie wartości aliasu:<BR>";


echo "Oryginalna zmienna: ".$variable. "<br>";
echo "Przypisana zmienna: ".$new_variable. "<br>";
echo "Alias: ".$alias. "<br>";
502________________________________Część III » Techniki zaawansowane

Wykonanie tego fragmentu daje następujący wynik:


Początkowe wartości:
Oryginalna zmienna: Pierwszy ciąg testowy
Przypisana zmienna: Pierwszy ciąg testowy
Alias: Pierwszy ciąg testowy

Zmiana przypisanej zmiennej...

Po zmianie wartości new_variable:


Oryginalna zmienna: Pierwszy ciąg testowy
Przypisana zmienna: Zmieniony pierwszy ciąg testowy - zmienna
Alias: Pierwszy ciąg testowy

Zmiana aliasu...

Po zmianie wartości aliasu:


Oryginalna zmienna: Zmieniony pierwszy ciąg testowy - alias
Przypisana zmienna: Zmieniony pierwszy ciąg testowy - zmienna
Alias: Zmieniony pierwszy ciąg testowy - alias

Ustawienie $variable nie ma wpływu na $new_variable, ponieważ $new_varia-


ble jest osobną kopią $variable. Jednak zmiana $alias powoduje również zmianę
$variable, ponieważ $alias nie jest osobnąkopią Svariable, a jedynie inną nazwą
tej samej zmiennej.

Aliasy mogą odwoływać się do określonych komórek w tablicy lub atrybutów w obiekcie:
$alias = &$object->attributte;
Salias = "Nowa wartość atrybutu";

echo "<br>Alias: ".$alias." jest taki sam jak ";


echo "oryginalny: " . Sobject->attributte. "<brxbr>";

Aliasów można używać z trzech powodów.


1. Zapamiętywanie wskaźników do zmiennych w wielu miejscach, takich jak
atrybuty obiektu — zmiana jednego z nich wpłynie na wartość wszystkich po-
zostałych.
2. Zabezpieczenie się przed tworzeniem przez PHP dodatkowych kopii dużych
obiektów w czasie przekazywania ich do funkcji jako argumenty (składnia:
f u n c t i o n ( & object)).
3. Skrócenie nazw zmiennych.

Jeżeli masz bardzo duże obiekty, zawsze przekazuj je jako argumenty


przy użyciu aliasu. Skróci to czas wykonania i ograniczy zużycie pamięci.

Wyświetlanie i drukowanie obiektów


PHP 4 posiada dwie wbudowane zmienne, które formatują i wyświetlają obiekt: var_
dump i print_r. Ich formaty wydruku nieco się różnią, ale wykonują to samo zadanie.
Wynikiem ich działania jest tekst, a nie HTML, więc jeżeli chcesz wyświetlić wynik na
ekranie, musisz otoczyć go znacznikami <pre>:
Rozdział 30. » Programowanie obiektowe w PHP_________________________503

echo "<pre>";
var_ciump($object) ;
echo "</pre>";

echo "<pre>";
print_r($object);
echo "</pre>";

PHP 4 oferuje inne funkcje wyświetlające wartości zmiennych: print ( ) , echoO


i strval (), ale nie mają one żadnej specjalnej obsługi obiektów.

W dalszej części rozdziału przedstawimy przykład funkcji wyświetlającej obiekt.

Przeglądanie
Obiekt jest zespołem danych i funkcji. PHP pozwala na sprawdzenie nazw, typów i war-
tości wszystkich danych w obiekcie. Obiekty mogą być traktowane jak tablice i „przeglą-
dane" w celu odczytania każdej nazwy zmiennej, typu i wartości.

PHP posiada funkcje oglądania, a nawet zmiany typów zmiennych i klas.

Funkcje przeglądania typów i klas


Tabele 30.1 do 30.3 zawierają najważniejsze funkcje PHP 4 dla typów, klas i zmiennych.

Rozdział 6. zawiera więcej informacji na temat typów i ich sprawdzania.

Tabela 30.1.
Zestawienie funkcji sprawdzających typy, zwracających wartości boolean

Funkcja Opis

is a r r a y ( $ v a r ) Zwraca TRUE, jeżeli $var jest tablicą


is int ( S v a r ) Zwraca TRUE, jeżeli $ var jest liczbą całkowitą
Zauważ, że PHP 4 jednakowo traktuje typy int, integer i long
is integer ( $ v a r ) Zwraca TRUE, jeżeli $var jest liczbą całkowitą
Zauważ, że PHP 4 jednakowo traktuje typy int, integer i long
is l o n g ( $ v a r ) Zwraca TRUE, jeżeli $var jest liczbą całkowitą
Zauważ, że PHP 4 jednakowo traktuje typy int, integer i long
is float ( $ v a r ) Zwraca TRUE, jeżeli $ var jest liczbą zmiennoprzecinkową o podwójnej precyzji
Zauważ, że PHP 4 jednakowo traktuje typy float, double i real
504___________________________________Część III » Techniki zaawansowane

Tabela 30.1.
Zestawienie funkcji sprawdzających typy, zwracających wartości boolean (ciąg dalszy)

Funkcja Opis
is real ( $ v a r ) Zwraca TRUE, jeżeli $var jest liczbą zmiennoprzecinkową o podwójnej precyzji

Zauważ, że PHP 4 jednakowo traktuje typy float, double i real


is double ( S v a r ) Zwraca TRUE, jeżeli $var jest liczbą zmiennoprzecinkową o podwójnej precyzji
Zauważ, że PHP 4 jednakowo traktuje typy float, double i real
is numeric ( $ v a r ) Zwraca TRUE, jeżeli $var jest liczbą całkowitą, liczbą zmiennoprzecinkową
bądź ciągiem zawierającym liczbę
is s t r i n g ( $ v a r ) Zwraca TRUE, jeżeli Svar jest ciągiem
is object ( $ v a r ) Zwraca TRU E, jeżeli $ va r jest obiektem
class exists ($ciag) Zwraca TRUE, jeżeli Ściąg zawiera nazwę predefiniowanej klasy lub klasy
zdefiniowanej przez użytkownika
empty ( $ v a r ) Zwraca TRUE, jeżeli Svar jest nazwą niezainicjowanej zmiennej lub posiada
wartość zero, np.: 0, "0"
IsSet ( $ v a r ) Zwraca TRUE, jeżeli $var jest nazwą niezainicjowanej bądź nieużywanej
zmiennej

Tabela 30.2.
Zestawienie funkcji typów i klas

Funkcja Opis
gettype ( $ v a r ) Zwraca ciąg reprezentujący typ wartości Svar, to znaczy string, object, double
settype ( $ v a r ) Określa typ wartości ?var na array, double, integer, object lub string. Funkcja
może dawać zaskakujące wyniki, więc przetestuj ją przed użyciem
doubleval (Svar) Zwraca liczbę typu double o wartości określanej przez wartość Svar.
Na przykład: "0.045" jest zamieniane na 0.045, 6 staje się 6.0. Funkcja nie działa
na tablicach i obiektach
intval ( $ v a r ) Zwraca liczbę typu integer o wartości określanej przez wartość Svar.
Na przykład: "45" jest zamieniane na 45, 4.0 staje się 4. Funkcja nie działa
na tablicach i obiektach
unset ( S v a l ) Usuwa podaną zmienną. Po wykonaniu funkcji zmienna nie jest dostępna
i ponowne wywołanie isset (Svar) zwróci FALSE

Zajmiemy się dokładniej trzema funkcjami:


is_object()
gettype ()
get_class ()
Rozdział 30. » Programowanie obiektowe w PHP___________________________505

Tabela 30.3.
Zestawienie funkcji wejścia i wyjścia dla klas i typów

Funkcja Opis
print r ( 5 v a r ) Drukuje ciąg będący reprezentacją zmiennej lub obiektu
var dump (Svar ) Drukuje ciąg będący reprezentacją zmiennej lub obiektu. Postać drukowanego
ciągu jest nieco inna niż print r ( )
strval ( $ v a r ) Drukuje ciąg będący reprezentacją zmiennej. Nie podaje szczegółów tablic ani
obiektów
serialize ($var) Zwraca ciąg bajtów reprezentujący zmienną lub obiekt. Ciąg ten jest odpowiedni
do zapisywania w bazie danych lub przesyłania pomiędzy stronami. Do
niektórych celów może wymagać zakodowania funkcjąbase64_encode ( )
unserialize ( $ v a r ) Zwraca zmienną stworzoną na podstawie wyniku funkcji serialize ( )

Używając dynamicznego pobierania zmiennych klasy za pomocą funkcji przeglądania


napiszemy własne funkcje drukowania obiektów. Na wydruku 30.1 zamieściliśmy dwie
funkcje: display_object ( ) oraz spaceout ( ) , pomocniczą funkcję formatującą.

Funkcja display_object ( ) przegląda podany obiekt, wyświetlając nazwy zmiennych,


typy i wartości. Jeżeli zmienna jest obiektem, funkcja wywołuje siebie samą z nowym
obiektem. Pozwala zobaczyć wszystkie zagnieżdżone obiekty, jednak stwarza ryzyko nie-
skończonej rekurencji w przypadku, gdy obiekt zawiera odwołanie do samego siebie!

Wydruk 30.1. Wyświetlanie obiektów__________________________________________


function spaceout(Sdepth)
(
for ($i = 0; $i < SdepthM; Si + +)
echo "snbsp;";
)
function display_object($obj, Sdepth = 0)
(
if (!is_object(Sobj))
return;
if ($depth == 0)
echo "<b>Drukowanie szczegółów obiektu:</b> <brxbr>";

reset($obj};
while ( list($slot) = each(Sobj) )
{
echo spaceout(Sdepth) ."<b>$slot Szczegóiy:</b> <br>";
echo spaceout($depth) ."Nazwa zmiennej: Sslot<br>";
echo spaceout($depth) ."Typ zmiennej:".gettype(Sobj->$slot}." <br>";
if (is_object($obj->$slot))
echo spaceout($depth). "zmienna klasowa:".get_class(Sobj->$slot). "<br>";

echo spaceout(Sdepth). "wartość:";

if (gettype(Sobj->$slot) == "string")
echo Sobj->$slot;
else if (gettype($obj->Sslot) == "integer")
echo $obj->$slot;
else if (gettype (Sobj->Sslot) == "double")
echo $cbj->$slot;
else if (gettype(Sobj->Sslot) == "array")
506________________________________Część III » Techniki zaawansowane

print_r(Sobj->$slot);
else if (gettype{$obj->$slot) == "object"}
{
echo spaceout (Sdepth) . "<i>0biekt klasy </ixb>" .
get_class($obj->Sslot).":</b><br>";
display_object($obj->$slot, $depth+l);
continue;
l
echo "<br>";
}
)

Poniżej znajduje się klasa zawierająca zmienne każdego typu oraz obiekt wewnętrzny:
class walkme
{
var $stringOne = "Pierwszy ciąg, wartość oryginalna";
var $stringTwo = "Pierwszy ciąg, wartość oryginalna";
var 3objectOne;
var $integerOne;
var SdoubleOne;
var $arrayOne;

function init()
ł
Sthis->stringOne = "Pierwszy ciąg, nowa wartość"; //ciąg
Sthis->objectOne = new introO; //obiekt
$this->objectOne->objectOne = new introO; //obiekt wewnętrzny
$this->integerOne = 6; //liczba całkowita
$this->doubleOne = 6.5; //liczba zmiennoprzecinkowa
$this->arrayOne = array("a", "b", "c"); // tablica
)
}

Ponieważ nie można zainicjować zmiennych wartościami niestatycznymi, takimi jak


obiekt, musimy zainicjować zmienną $objectOne po utworzeniu obiektu. Wykonamy
to dzięki wywołaniu zmiennej, która ustawi również inne zmienne. Następnie wyślemy
obiekt do funkcji wyświetlającej:
$obj = new walkme();
echo "Obiekt utworzony<br>";
$obj->init();
display_object($obj) ;

Wynik wykonania pierwszego kodu pokazany jest na poniższym wydruku.


Wydruk 30.2. Wyświetlanie zawartości obiektów___________________________________
Obiekt utworzony
Drukowanie szczegółów obiektu:

stringOne Szczegóły:
Nazwa zmiennej: stringOne
Typ zmiennej: string
wartość: Pierwszy ciąg, nowa wartość
stringTwo Szczegóły:
Nazwa zmiennej: StringTwo
Typ zmiennej: string
wartość: Pierwszy ciąg, wartość oryginalna
objectOne Szczegóły:
Nazwa zmiennej: objectOne
Typ zmiennej: object
zmienna klasowa: intro
wartość: Obiekt klasy intro:
stringOne Szczegóły:
Rozdział 30. * Programowanie obiektowe w PHP___________________________507

Nazwa zmiennej: stringOne


Typ zmiennej: string
wartość: Pierwszy ciąg, wartość oryginalna
stringTwo Szczegóły:
Nazwa zmiennej: stringTwo
Typ zmiennej: string
wartość: Pierwszy ciąg, wartość oryginalna
objectOne Szczegóły:
Nazwa zmiennej: objectOne
Typ zmiennej: object
zmienna klasowa: intro
wartość: Obiekt klasy intro:
stringOne Szczegóły:
Nazwa zmiennej: stringOne
Typ zmiennej: string
wartość: Pierwszy ciąg, wartość oryginalna
stringTwo Szczegóły:
Nazwa zmiennej: stringTwo
Typ zmiennej: string
wartość: Pierwszy ciąg, wartość oryginalna
integerOne Szczegóły:
Nazwa zmiennej: integerOne
Typ zmiennej: integer
wartość: 6
doubleOne Szczegóły:
Nazwa zmiennej: doubleOne
Typ zmiennej: double
wartość: 6.5
arrayOne Szczegóły:
Nazwa zmiennej: arrayOne
Typ zmiennej: array
wartość: Array ([01 => a [1] => b [2] => c)

Serializacja obiektów
Z kilku różnych powodów możliwość przekształcenia obiektu w liniowy ciąg bajtów
zdekodowanie jej do postaci obiektu mogą być przydatne. Używając tej możliwości
możesz zapisać obiekt na dysku, a następnie ponownie go odczytać. Można również
wysłać go do innego programu, w którym obiekt będzie zrekonstruowany. Operacja ko-
dowania i zdekodowania obiektów nazywana jest serializacją.

PHP 4 posiada wygodne funkcje realizujące to zadanie: serialize () oraz unseria-


lize ( ) . Przetwarzają one automatycznie obiekt PHP w zapisywalny ciąg, następnie
zapisany ciąg zmieniają w obiekt PHP.

Nie można serializować obiektów zewnętrznych, takich jak obiekty


COM lub Java, zwracane przez funkcje pakietów Javy i COM. Niektóre
uchwyty i typy, na przykład uchwyty połączeń do bazy danych, mogą
nie przetrwać serializacji.

Możesz zapisać serializowany obiekt do pliku lub bazy danych, przekazać go do innej
strony WWW.
if ((iisset(Sin_object)}
t
?in_object = new walkmeO;
508___________________________________Część III » Techniki zaawansowane

echo "Poniższe łącze zawiera serializowany obiekt.<BR>";


echo "<a href=\"session.php?in_object=", base64_encode(serialize(Sin_object) )
."\">Kliknij</a>";
)
else
{
echo "Dekodowanie i odtwarzanie obiektu odczytanego przez URL.<br>";
$out_obiect = unserialize(base64_decode($in_object));
echo "<pre>";
var_dump($out_object) ;
echo "<prexbr>";
)

Jedyną rzeczą, o której musisz pamiętać, jest upewnienie się, że klasa odtwarzanego po
serializacji obiektu będzie zdefiniowana w czasie odtwarzania obiektu. Jeżeli nie, zmienne
obiektowe powstałe po odtwarzaniu będą dostępne, ale wywołanie funkcji obiektu będzie
niemożliwe.

Mimo że teoretycznie można przesyłać serializowane obiekty za po-


mocą metod GET i POST, musisz wiedzieć, że istnieją ograniczenia
w ilości przesyłanych tymi metodami danych, co może spowodować
obcięcie obiektu. Jest to szczególnie ważne w przypadku GET (ograni-
czenie do 255 znaków).

Zewnętrzne interfejsy:
COM, Java i CORBA
Obecnie dostępne są moduły rozszerzeń PHP 4 do połączenia oraz manipulacji obiek-
tami COM, DCOM i Java. W wersji 4.0.0 CORBA nie była dostępna, ale trwały prace
nad co najmniej jednym modułem na bazie ORBit ORB.

PHP 4 oferuje „opakowywanie" obiektów zewnętrznych w obiekty PHP 4. Zewnętrzne


obiekty są tworzone za pomocą odwołań do modułów i mogą być używane jak zwykłe
obiekty PHP 4.

Obiekty PHP 4 tworzone przez moduły w celu opakowania obiektów


zewnętrznych nie są dokładnie zwykłymi obiektami PHP 4. Nie mogą
być na przykład serializowane.

Interfejsy COM i DCOM są dostępne tylko na platformie Windows. Interfejs do Javy


powinien byś dostępny dla większości platform.

Mimo że obsługa Javy wchodzi w skład instalacji PHP 4, nie udało się
nam tego przetestować na wersji beta PHP 4.
Rozdział 30. » Programowanie obiektowe w PHP___________________________509

COM i DCOM
Moduł COM obsługuje zarówno obiekty COM, jak i DCOM posiadające interfejs IDi-
spatch. Moduł COM jest częścią PHP 4 dla Win32. W tabeli 30.4 zamieściliśmy do-
stępne funkcje interfejsu COM.

Jeżeli w pliku php.ini podałeś bibliotekę typów, PHP 4 samoczynnie rozpozna stałe
automatyzacji. Przeczytaj plik README dołączony do modułu, w którym znajduje się
szczegółowy opis wykonania.

DCOM jest również obsługiwany, ale domyślnie jest wyłączony. W celu uaktywnienia
DCOM należy w pliku php.ini umieścić wiersz allow_dcom = 1;. Moduł automatycz-
nie skonwertuje typy pomiędzy COM i PHP, oprócz konwersji tablic i obiektów PHP.

Poniżej podaliśmy przykład użycia COM do połączenia z bazą danych.


$db = new COM("ADODB.Connection");
$database_name = "librarians";
$table_name = "librarians";

echo "Typ obiektu COM:";


echo gettype($db);
echo "<br>";
echo "Klasa obiektu COM:";
echo get_class(Sdb);
echo "<br>";
echo "<br>";

$db->0pen ($database_name, "", " " ) ;

Sresult = Sdb->Execute( "SELECT VFirst Name\", V'Last


NameV, V'StateV FROM". $table_name. " order by state");
echo "Nowy typ obiektu COM:";
echo gettype(Sresult);
echo "<br>";
echo "Nowa klasa obiektu COM:";
echo get_class(Sresult} ;
echo "<br>";
echo "<br>";

while)!Sresult->EOF)
{
echo $result->Fields["First Name"]->Value;
echo " ";
echo Sresult->Fields("Last Name"]->Value;
echo ", ";
echo $result->Fields["State"]->Value;
echo "<br>";
Sresult->MoveNext();
1

Kod ten daje następujący wynik:


Typ obiektu COM: object
Klasa obiektu COM: COM

Nowy typ obiektu COM: object


Nowa klasa obiektu COM: COM

Janet Baker, AL.


Jason Nesbit, AŻ
Scott D. Croff, AŻ
Valerie vinge, AZ
510___________________________________Część III » Techniki zaawansowane

Richard Lynn, CA
Robert G. Willis, CA
Peter Buttler, PA
Mary Steward, CA
Darvid Bergman, CA
Lisa Bates, CA
Tony Smith, NJ

Tabela 30.4.
Zestawienie funkcji interfejsu COM

Funkcja Opis
com load ( ) Parametry: (string nazwa obiektu [, string nazwa serwera])

Nazwa serwera jest wymagana tylko przy dostępie przez DCOM


com invoke ( ) Parametry: (obiekt, string nazwa funkcji [, argumentl ... argument^])
com propget ( ) Parametry: (ofcie^t, string nazwa atrybutu)
com get ( ) Identycznie jak com propget ( )
com propout ( ) Parametry: (obiekt, string nazwa atrybutu, wartość zmiennej). Nie
można przypisywać właściwości do obiektu lub tablicy
com propset ( ) Identycznie jak com propout ( )
com set ( ) Identycznie jak com propout { )

Przykładowa aplikacja obiektowa


Na wydruku 30.3 zamieściliśmy wyczerpujący przykład demonstrujący dziedziczenie
atrybutów, funkcji oraz polimorfizm.

Wydruk 30.3. Przykład abstrakcji interfejsu bazy danych______________________________


<?
session_start(};
?>
<html>
<headx/head>
<body>
<?
// *»***....*....*..»*.*.*............**....*...»..——— .
...
..
class database
(
va $server_name;
va $database_name;
va $user_name;
va Suser_password;
va $suppress_errors = 0;

function connect() { ; }
function error_string() ( ; }
function error_number(} { ; }
function execute_query($query) { ; )
function select_database() { ; }
Rozdział 30. » Programowanie obiektowe w PHP___________________________511

function commit() f ; }
function rollback() { ; }
function fetch_row_array() { ; }

function check_connect()
{
if { $this->connection == 0)
Sthis->connect();
}

function print_error($error_string)
i
if ( ! $this->suppress__errors)
echo "Niekrytyczny błąd bazy danych:".$error_string. "<br>";
}
}
class failover_database extends database
f
var $backup_server_name;

function failover_database($server__name, $backup_server_name,


$database_name, $user_name, $user_password)
(
$this->server_name = Sserver_name;
Sthis->backup_server_name = $backup_serverename;
Sthis->database_name = $database_name;
Sthis->user_name = $user_name;
Sthis->user_password = $user_password;
$this->suppress_errors = l;/7za pierwszym razem wyłącz ostrzeżenia
Sresult = $this->connect();
$this->suppress_errors = 0; // włącz z powrotem ostrzeżenia
$this->connect();
j

function connect(}
{
if ({$result == 0) && isset{$this->backup_server_name))
t
$original = $this->server_name;
$this->server_name = $this->backup__server_name;
$this->backup_server_name = Soriginal;
// spróbuj zapasowy serwer
$result = database::connect(};
}
}
}
II ***************** + + *********** + ******•*********•* + **********
class mysql_database extends failover_database
{
var Sconnection;

function mysql_database( $server_name, $backup_servername,


$database_name, $user_name, $user_password)
{
// wywołanie konstruktora klasy bazowej
failover_database::failover_database($server_name,
$backup_servername,Sdatabase_name, $user_name, $user_password);
}
function connect()
{
$this->connection = @mysql_pconnect($this->server_name,
$this->user_name, $this->user_password);
if ($this->connection == 0)
$this->print_error("Nie można połączyć się z baza danych. Sprawdź serwer,
"^nazwe użytkownika oraz hasło i spróbuj jeszcze raz.");
$this->select_database($this->database_natne);
return $this->connection;
512___________________________________Część III » Techniki zaawansowane

)
function error_string{)
(
return mysql_error();
)
function error_number()
{
return mysql_errno();
}
function execute_query(Squery)
{
Sthis->check_connect();
Sresult = Smysql_query(Squery, Sthis->connection);
if (Sresult == 0)
$this->print_error("Nie można wykonać zapytania
(".$query."). Zwracany błąd: (".$this->error_string().").
Sprawdź skiadnię oraz połączenie i spróbuj jeszcze raz."};
return ($result);
}
function select_database(Sdb_name)
(
Sthis->check_connect() ;
Sresult = @mysql_select_db(Sdb_name);
if (Sresuit == 0)
$this->print_error("Nie mogę wybrać bazy danych (".Sdb_name.").
Zwracany błąd: (".$this->error_string(}.").
Sprawdź składnie oraz połączenie i spróbuj jeszcze raz."};
l

function fetch_row_array(Squery_result)
{
$this->check_connect() ;
if ($query__result == 0)
$this->print_error(
"Nie można pobrać wyniku zapytania. Zwracany błąd:
(" .Sthis->error_string()."). Sprawdź składnie oraz połączenie
i spróbuj jeszcze raz."};
return (3mysql_fetch_array(Squery_result, MYSQL_ASSOC));
)
function commit() { ; } // Commit nie obsługiwany w MySQL
function rollback() t ; } // Rollback nie obsługiwany w MySQL
}
// *,.*,**.—— Ł**.,*.**,**..******..****.*».*.*.*„.,„,„***„*„
// Wszystkie zdefiniowane do tej pory klasy należy umieścić w pliku
// database.inc
// include("database.inc");

$server = "nazwa.serwera";
Sbackupserver = "nazwa.serwera.zapasowego";
Sdatabase = "nazwa_bazy_danych";
Susername = "nazwa_użytkownika";
Spassword = "hasło";

if (!isset(Smydb))
{
Smydb = new mysql_database(Sserver, Sbackupserver, Sdatabase,
Susername, Spassword);
echo "Tworzenie obiektu połączenia z bazą danych...<br>";
(
else
(
echo "Ponowne użycie obiektu połączenia z baza danych...<br>";
Rozdział 30. » Programowanie obiektowe w PHP___________________________513

session_register("mydb");

$query = "select name, id from people";


Sresult = $mydb->execute_query(Squery);

if (Sresult != 0)
(
while($row = $mydb->fetch_row_array(Sresult))
(
echo "Nazwa: <b>" . Srow [ "name" ] . "</bxbr>";
echo " ID : <b>" . $row [ "id"] . "</bxbr>";
echo "<br>";
I
}
?>
</body>
</html>

Podsumowanie
Programowanie obiektowe to programowanie z klasami. PHP 4 posiada podstawowe
narzędzia do tworzenia aplikacji obiektowych. Nie wszystko, do czego przyzwyczaiłeś
się przy programowaniu obiektowym, jest dostępne w PHP 4. Wielu elementów pro-
gramowania obiektowego brakuje. Jednak utrzymane są podstawowe założenia i za po-
mocą dostępnych narzędzi możesz:
** napisać bardziej elastyczną aplikację;
* połączyć funkcje z danymi, zabezpieczając dane przed zniszczeniem przez
niewłaściwe funkcje;
* zarządzać przestrzenią nazw, ukrywając tysiące funkcji w klasach (nie przesło-
nisz przypadkowo funkcji globalnej);
* pisać programy współpracujące z obiektami COM, DCOM i Java;
* poznać podstawy projektowania i programowania obiektowego.
514 Część
Część
111III »» Techniki zaawansowane
Rozdział 31.
Bezpieczeństwo
i kryptografia
W tym rozdziale:
* Możliwe ataki
* Szyfrowanie
4 Szyfrowanie kluczem publicznym
4 Szyfrowanie pojedynczym kluczem
* Mieszanie
* Secure Server Layer

Na wielu lotniskach można zauważyć tabliczkę „Bezpieczeństwo nie jest tematem do


żartów". Identyczną powinieneś powiesić niedaleko twojego serwera PHP. Każdy, kto
włącza serwer, musi zapewnić odpowiednie techniki zabezpieczeń. W innym wypadku
ryzykuje utratą danych i pieniędzy spowodowaną działaniem internetowych włamywaczy.

Hasłem przewodnim człowieka odpowiedzialnego za bezpieczeństwo systemu powinno


być „Nie ufaj Internetowi". Jeżeli obawiasz się o bezpieczeństwo witryny, wbuduj to
hasło w kod twoich stron. Każda informacja przesyłana przez sieć —jako dane związa-
ne z adresem URL lub dane przesyłane pomiędzy portami — powinna być uważana za
potencjalnie niebezpieczną. W rozdziale tym zaproponujemy kilka sposobów na „odka-
żanie" przychodzących danych.

Drugą zasadą dla bezpiecznej witryny jest: „Ograniczaj szkody". Co się stanie, jeżeli
program ma słabe punkty (mimo że stosowałeś się do zasad bezpieczeństwa przy jego
pisaniu)? Musisz zwrócić uwagę na sposoby ograniczania szkód, jakie może wyrządzić
potencjalny włamywacz, któremu uda się jednak przełamać zabezpieczenia.

Użytkownicy odwiedzający witrynę sądzą, że zawiera właściwe dane (nie są niebez-


pieczne dla nich ani ich komputerów), a dane przesłane do witryny będą prawidłowo
obsłużone. Interakcja z witryną, również rozrywkową lub informacyjną, niesie ze sobą
516___________________________________Część III » Techniki zaawansowane

jakąś dozę ryzyka. Jako projektant witryny jesteś odpowiedzialny za jego maksymalne
ograniczenie. Oprócz upewnienia się, że dane są bezpieczne na twoim serwerze, powi-
nieneś zapewnić bezpieczeństwo danych na czas ich transmisji pomiędzy komputerami
użytkowników a serwerem.

Pierwsza część tego rozdziału opisuje niektóre sposoby atakowania serwera i obrony
przed nimi. Następnie zajmiemy się użyciem technik kryptograficznych do ochrony da-
nych. Na końcu rozdziału zamieścimy listę witryn WWW zawierających bieżące infor-
macje na temat technik włamywania. Śledząc te witryny, możesz poznać słabe punkty
systemów, zanim ktoś z nich skorzysta.

Możliwe ataki
Podłączenie serwera do Internetu jest podobne do otwarcia sklepu na ruchliwej ulicy. Mimo
że masz stosunkowo niewielu klientów, możesz spodziewać się nieproszonych gości.

Zmiana zawartości witryny


Zmiana zawartości witryny jest zwykle bardziej kłopotliwa niż niebezpieczna, ponie-
waż włamywacz ma możliwość upublicznienia swojego nadużycia. Zmiana wyglądu
jest czasami wizytówką kogoś, kto włamał się do systemu.

Zmiana wyglądu źle zaprojektowanej witryny jest możliwa przy użyciu przeglądarki.
Przyjrzyj się następującemu fragmentowi programu:
<?php
if (IsSet(Svisitor))
(
$ fp ~ fopen("database" , "a");
fwrite (Sfp, "<li> SvisitorW) ;
fclose(S fp);
) ?>
<HTML>
<HEADX/HEAD>
<BODY>
<Hl>Ksiega gosci:</Hl>
<OL>
<?php
$fp = f open ("database", "r");
print(fread($fp, fileśi ze("/sciezka/do/bazy")));
fclose(Sfp); ?>
</OL>
<HR>
<FORMXINPUT T Y P E = " T E X T " NAME="visitor">
<INPUT TYPE="SUBMIT" NAME="submit" VALUE="Wpisz sie!">
</FORM>
</BODY>
</HTML>

Program ten obsługuje podstawową książkę gości. Przeglądając ten kod, powinieneś
poczuć się niepewnie. Program ten pobiera dane z formularza, spodziewając się imienia
gościa (w zmiennej $visitor), i zapisuje je w pliku tekstowym, wyświetlając później
kolejnych gości. W przypadku takich danych nie ma żadnego niebezpieczeństwa.
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________517

Przebierzmy się na moment w uniform sieciowego dowcipnisia. Pomyśl, co się stanie,


jeżeli wpisany ciąg będzie zawierał znaczniki HTML. Program ten na ślepo wstawi te
znaczniki do generowanej strony, a przeglądarka innego użytkownika zinterpretuje je
jak każde inne znaczniki. Jednym ze złośliwych znaczników jest < S C R I P T > . Włamy-
wacz, chcący zmienić zawartość strony, może skopiować wygląd strony na własnym
serwerze (www.zlawitryna.com), a następnie wpisać się do książki gości następującym
„imieniem":
<SCRIPT LANGUAGE="JavaScript">
window,location="http://www.zlawitryna.com/"</SCRIPT>

Gdy ktoś załaduje książkę gości, przeglądarka zinterpretuje ten znacznik i natychmiast
przejdzie na witrynę włamywacza. Przy odrobinie pomysłowości może on skorzystać
z zaufania, jakim gość obdarza twoją stronę, w celu otrzymania poufnych danych (ha-
sła, numery kart kredytowych).

Rozwiązaniem tego problemu jest oczyszczanie otrzymywanych danych. W tym przy-


padku powinniśmy zamienić wszystkie znaki specjalne na coś sprawiającego mniej
problemów. Na szczęście PHP posiada odpowiednią funkcję do przeprowadzenia takiej
zamiany. Funkcja htmlspecialchars ( ) konwertuje znaki '<', '>', '"' i '&' na ich od-
powiedniki w jednostkach HTML (na przykład &lt;). Zmieńmy pierwszą część nasze-
go programu tak, aby korzystał z tej funkcji:
<?php
if (IsSet($visitor))
f
Sfp = fopen("database", "a");
$clean_visitor = htmlspecialchars($visitor);
fwrite (Sfp, "<li> Sclean_visitor\n");
fclose(Sfp);
) ?>

W ten sposób załataliśmy bardzo poważną dziurę w systemie bezpieczeństwa naszej


witryny.

Crackerzy, hakerzy i inni maniacy


Termin „haker" jest często używany do określania osoby, którą bardziej do-
kładnie określa termin cracker. W społeczności internetowej crackerzy to ci,
którzy używając swoich zdolności (lub szczęścia) włamują się do systemów
komputerowych i powodują różne uszkodzenia. Haker to osoba, która potrafi
pisać bardzo efektywny kod we wielu językach programowania. Dla progra-
misty nazwanie go hakerem jest powodem do dumy; bycie crackerem powo-
dem do dumy nie jest.
Jeżeli określenie „cracker" nie jest dostatecznie uwłaczające, to młodych
crackerów, którzy używają narzędzi i skryptów znalezionych w Internecie, na-
zywa się „lamerami". Bardzo często nie zdają sobie sprawy, co właściwie
robią. Są zwykle sprawcami mało ambitnych ataków, takich jak zmiany za-
wartości witryny. Lamera można zwykle poznać po nadużywaniu dużych liter
i zamiany liter, na przykład: W3 R KOOL DOODz.
518___________________________________Część III » Techniki zaawansowane

Dostęp do kodu źródłowego


Nawet jeżeli kod źródłowy nie jest sekretem handlowym, powinieneś zabezpieczyć go
przed możliwością wyświetlenia w przeglądarce. Jeżeli włamywacz będzie mógł obej-
rzeć kod źródłowy, nie musi eksperymentować, szukając jego słabych punktów. W za-
mian może go przeanalizować, szukając pomyłek i dziur w systemie bezpieczeństwa.
Ogólnie mówiąc, im więcej informacji dasz potencjalnemu włamywaczowi, tym ła-
twiejsze będzie włamanie. Ukrywając szczegóły, takie jak kod źródłowy, nazwy katalo-
gów lub nazwy użytkowników, możesz zmniejszyć prawdopodobieństwo ataku.

W przypadku używania PHP w postaci modułu serwera WWW istnieje małe ryzyko
wyświetlenia kodu źródłowego przez serwer, ponieważ każdy plik z określonym rozsze-
rzeniem jest analizowany przez moduł PHP. Jeżeli jednak PHP jest zainstalowany jako
program CGI, sprawy mogą się skomplikować.

Jeżeli nie możesz uruchomić PHP jako modułu serwera, możesz zainstalować PHP tak,
jak inny interpreter skryptów CGI, na przykład Perl lub Python. Po umieszczeniu
wszystkich programów PHP w katalogu cgi-bin serwera lub twojego konta zmień kon-
figurację tak, aby interpreter PHP był wywoływany dla tych plików. Na Uniksie dodaj
do każdego skryptu pierwszy wiersz:
#! /usr/local/bin/php

Aby skorzystać z takiego ustawienia, musisz skompilować PHP z opcją konfiguracji


--enable-discard-path. Jednak wadą takiego rozwiązania jest to, że adresy URL
większości twoich stron będą zawierały /cgi-bin/.

Następna bezpieczna konfiguracja jest nieco skomplikowana, obecnie oczekuje na re-


komendację CERT, jednego z autorytetów w dziedzinie bezpieczeństwa sieciowego.
Umieszczamy sam interpreter PHP w katalogu cgi-bin. Zwykle nie poleca się umiesz-
czania interpretera w katalogu cgi-bin, ponieważ zasady uruchamiania programów CGI
pozwalaj ą na analizowanie przez ten program dowolnego pliku na serwerze.

Jednak PHP jest napisany w sposób, który w przypadku właściwej konfiguracji umoż-
liwia bezpieczne działanie z katalogu cgi-bin. Jeżeli zdecydujesz się na użycie takiej
konfiguracji, przeczytaj dokładnie rozdziały o bezpieczeństwie i konfiguracji w pod-
ręczniku PHP; mogą zawierać istotne informacje, które ominęliśmy w tej książce.

Konfiguracja ta bazuje na przekierowaniu adresów URL w postaci


http://serwer/program.php

na adresy w postaci
http://serwer/cgi-bin/php/program.php

Dokładne dyrektywy, powodujące takie zachowanie się serwera, zależą od jego rodzaju.
Dla Apache jest to:
Action php-script /cgi-bin/php
AddType php-script .php
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________519

Jeżeli używasz Apache, upewnij się, że PHP jest skompilowany z opcją konfiguracji
—enable-force-cgi-redirect. PHP nie potrafi odróżnić tych dwóch typów adre-
sów URL i będzie dostarczał dokumenty dowolnego typu. Pozwoli to na udostępnianie
użytkownikowi plików z pominięciem ograniczeń serwera WWW. Załóżmy na przy-
kład, że plik pod adresem http://www.secret.com/top/secret/hush.php ma zablokowane
prawa dostępu. Włamywacz może skorzystać więc z adresu URL: http://www.secret.
com/cgi-bin/php/top/secret/hush.php, aby odczytać ten plik.

W takim przypadku serwer WWW przekazuje do PHP ścieżkę /top/secret/hush.php. PHP


rozpoznaje położenie pliku, dodając do przekazanej ścieżki wartość zmiennej konfigura-
cyjnej doc_root. Zmienna ta domyślnie ma taką samą zawartość jak główny katalog
serwera WWW (katalog związany z adresem http://www.secrets.com/). Ustawienie
doc_root na inny katalog ograniczy korzystanie tylko do plików w tym katalogu i jego
podkatalogach. Jednak każdy użytkownik może dostać się do dowolnego z programów
PHP, niezależnie od ograniczeń wprowadzanych przez serwer WWW. Bądź czujny!

Odczyt dowolnego pliku


Rzadziej występującym błędem w programowaniu z użyciem PHP jest ułatwienie wła-
mywaczowi odczytanie prawie każdego pliku na serwerze. Przestudiuj następujący kod
strony:
<HTML>
<HEADX/HEAD>
<BODY>
<?php
if (IsSet($poera))
(
$fp = fopen(Spoem, "r");
print (fread($fp, filesize($poem)));
fclose($fp);
l
?>
<HRXFORM>Wybierz w i e r s z :
<SELECT NAME="poem"XOPTION VALUE=" j a b b . html">Nonsensy
<OPTION VALUE="graves.html">Kocia księżniczka</SELECT>
<INPUT TYPE="SUBMIT" VALUE="Pokaż"X/FORM>
</BODY>
</HTML>

Program ten wyświetla dostępne wiersze, dając możliwość wyboru z listy rozwijalnej.
Przypomnij sobie teraz zasadę „Nie ufaj Internetowi". Naciśnięcie przycisku Pokaż na
głównej stronie powoduje wygenerowanie adresu URL w postaci ...poezja.php?poem =
graves.html. Włamywacz może zamienić tę nazwę pliku na coś bardziej go interesują-
cego, na przykład poezja.php?poem=/etc/passwd. Program teraz posłusznie wyświetli
plik haseł Unixa, umożliwiając włamanie na przykład przez konto gościa.

Poniżej mamy rozwiązanie tego problemu:


<?php
if (IsSet($poem))
(
switch (Spoem)
(
case "jabb":
$poem_file = "jabb.html";
520___________________________________Część III » Techniki zaawansowane

break;
case "graves":
$poem_file = "graves.html";
break;
}
1
if (IsSet(Spoem_file))
(
$fp = fopen($poem_file, "r");
print (fread($fp, filesize($poem_file))};
fclose($fp);
}
?>

Zaletą tej metody jest dokładne wyliczenie wszystkich dostępnych pozycji i obsługa
niepożądanych wartości. Jeżeli umożliwiłeś dostęp do wielu wierszy, wyrażenie swi-
tch powinno być zastąpione zapytaniem o miejsce, gdzie wprowadzenie niewłaściwych
danych będzie skutkowało niepowodzeniem wykonania zapytania.

To nie jest dobre rozwiązanie:


<?php
if (IsSet(Spoem))
f
if (Istrstr($poem, "/") && !strstr($poem, "\\"))
)
$fp = fopen($poem, "r");
print (fread($fp, filesize($poem)));
fclose<$fp);
)
) ?>

Drugi warunek w tym kodzie sprawdza, czy w podanej nazwie pliku istnieją separatory
ścieżki. Program ten jawnie podaje zestaw nieakceptowanych wartości wejściowych
i uznaje wszystko inne za dopuszczalne. W tym przypadku programista założył, że ża-
den ważny plik nie będzie przechowywany w tym samym katalogu co skrypt.

A jeżeli ten plik zostanie kiedyś użyty na innym serwerze? Istnieje szansa, że z powodu
niedokładnej konfiguracji (prawdopodobnie zrobionej przez inną osobę) lub z powodu
niezauważonej dziury w systemie bezpieczeństwa udostępnimy część lub wszystkie pli-
ki serwera.

PHP pozwala podać zbiór katalogów, z których wolno otwierać pliki. Służy do tego
zmienna konfiguracyjna open_basedir. Może być użyteczna przy ograniczenia dostę-
pu do większości katalogów; jest to dobry sposób niwelowania uszkodzeń. Więcej da-
nych na temat konfigurowania PHP w rozdziale 32.

Jednak istnieje wiele plików, które muszą być otwierane przez programy PHP w trakcie
udostępniania witryny użytkownikom. Przykładem może być plik haseł. Dostęp do tego
pliku nie może być zablokowany za pomocą opcji open_basedir, ale zawarte w nim
ważne dane mogą być zaszyfrowane.

Witryny chronione hasłem muszą sprawdzać hasło podane przez użytkownika. Jednym
ze sposobów realizacji jest przechowywanie hasła w postaci zaszyfrowanej i odszyfro-
wania go w celu porównania z hasłem podanym przez użytkownika. Ale jeżeli potrafi-
my zdekodować hasło, inni również mogą to zrobić. Aby upewnić się, że nikt nie będzie
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________521

mógł zobaczyć hasła po zdekodowaniu, możemy użyć jednokierunkowej funkcji szy-


frującej, której wynik nie może być odszyfrowany. Zamiast odszyfrowywać zapamięta-
ne hasło, kodujemy ciąg podany przez użytkownika i porównujemy zaszyfrowane ciągi.
Unix używa tej samej strategii we własnym pliku haseł /etc/passwd, a PHP pozwala
użyć tej samej funkcji szyfrującej we własnych plikach haseł.

Funkcja crypt (password, salt) szyfruje podane hasło. Parametr salt powinien
być wybrany losowo podczas zapisywania hasła (PHP wybiera losową wartość w przy-
padku braku tego parametru). Funkcja zawraca złączenie wartości parametru salt i za-
szyfrowanego ciągu. Poniższa funkcja tworzy nowe hasło dla gościa:
function new_pw(Sgiven)
t
return crypt(Sgiven);
)

Porównanie hasła podanego przez użytkownika z zapisanym zaszyfrowanym hasłem


można zrealizować następująco:
function verify_pw(Sgiven, Sstored)
{
Ssalt = substr(Sstored, O, CRYPT_SALT_ŁENGTH);
Sgiven_password = crypt(Sgiven, Ssalt);
return {Sstored == $given_password);
)

Uruchamianie dowolnych programów


Jest to najgorszy koszmar administratora: serwer działa wolniej niż zwykle, przejrzenie
działających procesów ujawnia program o nazwie crack, który zużywa 98% czasu pro-
cesora. Najprawdopodobniej program ten został podrzucony przez crackera do rozko-
dowywania haseł. Administrator loguje się w celu usunięcia programu, ale jego hasło
nie działa. Jego serwer został przejęty, trudno określić, jakie szkody zostały poczynione.

Błędy ludzi
Błędy popełniane przez ludzi są często niedocenianą furtką włamania do
systemów. Czasami łatwiej wyciągnąć dane (szczególnie hasła) od ludzi niż
od komputerów:
Cracker. Cześć, tu Adam z działu IT. Kiedy ostatnio używałeś twojego konta
firmowego?
Pracownik: Hmm, wprowadziłem kilka nowych zamówień około godziny temu.
Cracker. Obawiam się, że do twojego konto ktoś się włamał. Możemy utracić
część danych, co może kosztować firmę kilka milionów dolarów, jeżeli szyb-
ko nie namierzymy włamywacza. Potrzebujemy do tego twojego hasła.
Pracownik: No dobrze, moje hasło to...
Bardzo często użytkownicy notują hasła na kawałkach papieru leżących na
ich biurkach! Zdeterminowany włamywacz może łatwo dostać posadę noc-
nego stróża i przejrzeć te notatki.
522___________________________________Część III » Techniki zaawansowane

Włamywacz musi uzyskać dostęp do serwera przez wiersz komend Uniksa bądź
MS-DOS. Oczywiście jest to najtrudniejsze do wykonania, ale wysiłek opłaca się.
„Wewnątrz" serwera może spowodować najwięcej uszkodzeń, skopiować lub zmodyfi-
kować dane, skorzystać z mocy obliczeniowej serwera do otwarcia kolejnych furtek. Co
gorsza, zaawansowany włamywacz może zatrzeć ślady, zmieniając pliki śladów i usu-
wając pliki tymczasowe.
PHP posiada kilka funkcji umożliwiających uruchamianie programów: s y s t e m ! ) ,
exec ( ) , popen ( ) , passthru (} oraz operator „"". Przykładem użycia jednej z tych
funkcji jest poniższa strona, pokazująca dane, zwracane przez polecenie Unixa finger,
dla podanego poprzez formularz HTML użytkownika:
<HTML>
<HEADX/HEAD>
<BODY>
<FORM>Pobierz dane użytkownika: <INPUT TYPE="TEXT" NAME="username">,
< I N P U T TYPE="SUBMIT" VALUE="OK"X/FORM>
<?php if (IsSet(Susername)) { ?>
<Hl>Wyniki dla <?php echo Susername; ?></Hl>
<prex?php system ( "finger ". $username) ; ?x/pre>
<?php )?>
</BODY>
</HTML>

Pobiera on nazwę użytkownika z formularza HTML i wykonuje program zwracający in-


formacje na temat użytkownika. Polecenia systemu Unix oddziela się średnikami;
wszystko, co będzie wpisane po średniku w ciągu przekazanym do funkcji s y s t e m ! ) ,
będzie traktowane jako nowe polecenie. Będzie ono wykonane na takich samych pra-
wach, jakie ma serwer WWW.

W systemie Unix polecenie rm -rf / usuwa wszystkie pliki z serwera. Wyobraź sobie
złośliwego gościa, który wpisze do formularza ; rm -rf / i naciśnie OK.

Najlepszym rozwiązaniem tego problemu jest usunięcie wszystkiego oprócz prawidło-


wych nazw użytkowników przed wywołaniem polecenia finger. Wymaga to wiedzy
na temat sposobu zapisu nazw użytkowników na serwerze, więc nie przytoczymy goto-
wego przykładu. PHP dostarcza rozwiązania, które jest prawie tak samo dobre. Funkcja
escapeshellcmd ( ) oczyszcza ciąg używany w funkcjach wywołujących zewnętrzne
programy, usuwając znaki specjalne, takie jak średniki. Zamieńmy wiersz z wywoła-
niem funkcji system ( ) w poprzednim fragmencie kodu:
<prex?php system (escapeshellcmd ("finger ". $username) ) ; ?></pre>

W ten sposób żadna wartość wprowadzona przez użytkownika nie spowoduje urucho-
mienia kolejnego programu. Nie zabezpiecza to przed podaniem niepoprawnych danych
do funkcji finger. W przypadku wprowadzenia niepoprawnych danych finger nie
spowoduje żadnych szkód w systemie, jednak programy mogą działać inaczej.

W celu zmniejszenia uszkodzeń w przypadku tego typu włamań większość serwerów


WWW działa na prawach użytkownika o bardzo ograniczonych uprawnieniach (na
systemach Unix nazywa się często nobody). Użytkownik ten posiada uprawnienia wy-
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________523

magane do uruchomienia serwera WWW (i skryptów PHP) oraz do odczytu i zapisu


niezbędnych plików. Jednak należy pamiętać, że musi mieć możliwość zmieniania bazy
danych lub plików modyfikowanych przez skrypty (dlatego są one narażone na ataki).

Wirusy i inne e-robaki


Użytkownicy ufają oprogramowaniu pochodzącemu ze znanych witryn. Jeżeli twoja
witryna umożliwia załadowanie plików przesłanych przez innych użytkowników, powi-
nieneś przypomnieć im, aby sprawdzili je programem antywirusowym przed urucho-
mieniem; regularnie skanuj pliki na serwerze. Jest to trudny do rozwiązania problem,
szczególnie w obliczu możliwości wbudowywania wirusów w pozornie bezpieczne pli-
ki, takie jak dokumenty edytora tekstu. Microsoft wypuścił CD-ROM z dokumentem
Worda zawierającym wirus Melissa.

W części „Zmiana zawartości witryny" opisaliśmy sposoby, za pomo-


cą których włamywacz może zmusić użytkownika do uruchomienia
szkodliwego programu.

Bezpieczeństwo poczty
Poczta jest najmniej bezpiecznym protokołem internetowym. W trakcie wędrówki prze-
syłka może być kolejkowana w wielu pośrednich serwerach. Jeżeli serwer jest słabo za-
bezpieczony, włamywacz może łatwo przeczytać przechodzące przesyłki. Przesyłaj
więc pocztąjak najmniej ważnych danych. To znaczy, nigdy nie przesyłaj pocztą nume-
rów kart kredytowych i unikaj wysyłania haseł, chyba że jest to absolutnie niezbędne.
Interesujące, że większość z istniejących witryn nie stosuje się do drugiej zasady.

Administratorzy systemu
Administratorzy systemu, zwani również adminami, to ludzie, którzy dbają
o działanie komputerów i Internetu. Mają w zwyczaju tak programować ser-
wery, aby zawiadamiały natychmiast o każdej niezwykłej sytuacji; podejmują
szybkie i stanowcze akcje przeciwko niebezpiecznym zjawiskom.
Wykładowca na wydziale informatyki jednej ze szkół wyższych zadał studen-
tom jako pracę domową włamanie do jego systemu Linux. Aby ułatwić pracę,
dał im tekst zaszyfrowanego hasła (przeczytaj o funkcji crypt ( ) w części
„Uruchamianie dowolnych programów"). Żadnemu ze studentów nie udało
się włamać do jego systemu. Konta kilku z nich zostały zablokowane, po-
nieważ uruchamiali obciążające system programy o nazwie crack.
Jeżeli nie jesteś administratorem systemu, ale przejmujesz się bezpieczeń-
stwem witryny, zaprzyjaźnij się z twoim lokalnym administratorem. Zasugeru-
je ci sposoby uszczelnienia witryny i będzie w stanie pomóc w odtworzeniu
systemu po awarii.
524___________________________________Część III * Techniki zaawansowane

Jeżeli twoja witryna prosi użytkownika o podanie hasła, powinieneś dokładnie wytłu-
maczyć, w jaki sposób zostanie wykorzystane i do kogo wysłane. Jeżeli na stronie
WWW znajduje się adres e-mail, powinieneś tak go zmodyfikować, aby automatyczne
wyszukiwarki adresów, wykorzystywane do tworzenia SPAMU, nie mogły go łatwo zi-
dentyfikować. Najłatwiejszym sposobem jest zamiana symbolu @ słowem „at".

Jeżeli nie jest to absolutnie niezbędne, unikaj tworzenia łączy mailto:. To świetne
źródła adresów dla spammerów; są niewygodne dla użytkowników, którzy nie używają
przeglądarek WWW do wysyłania poczty.

Szyfrowanie
Szyfrowanie jest procesem zamiany otwartego tekstu w nieczytelny tekst zaszyfrowany.
Bez odpowiedniej informacji (pewnego rodzaju klucza) trudno odtworzyć otwarty tekst
z postaci zaszyfrowanej. Jednak ktoś, kto posiada właściwy klucz, może łatwo odszy-
frować tekst zaszyfrowany, odtwarzając oryginalny tekst (jeżeli funkcja szyfrująca nie
jest jednokierunkowa).

W rozdziale tym używaliśmy już szyfrowania: hasła są przechowywane w postaci za-


szyfrowanej. Jednak szyfrowanie haseł jest z reguły jednokierunkowe. Nie istnieje
klucz, który pozwoliłby odszyfrować hasło. Szyfrowanie ma wiele zastosowań siecio-
wych, zarówno do przechowywania danych na serwerze, jak i przesyłania.

Szyfrowanie kluczem publicznym


Poznaj Alicję i Bogdana, typowy przykład kryptograficzny. Zostali wybrani przez ma-
tematyków nie z powodu ich zdolności aktorskich, ale ponieważ ich imiona zaczynają
się literami A i B. Alicja i Bogdan chcą się bezpiecznie komunikować, ale jedynym
sposobem jest skorzystanie z usług Kucyka. Każde z nich wybiera klucz publiczny
i klucz prywatny. Nazwiemy klucze Alicji Paiicja i Saiicja (publiczny i prywatny) i odpo-
wiednio klucze Bogdana Pbogdan i Sbogda„. Każde z nich zamieszcza klucz publiczny
w ogłoszeniu w gazecie, klucze prywatne zachowując w sekrecie.

Alicja ma ważną informację dla Bogdana. Wraz z kluczami Alicja dostała instrukcję szy-
frowania przesyłki. Zapiszemy tę operację jako: Phoh(M). Alicja zaszyfrowała tekst klu-
czem publicznym Bogdana i włożyła tajemniczo wyglądającą przesyłkę do torby Kucyka.

Klucze naszych przyjaciół nie zostały wybrane dowolnie. Mają również specjalną
właściwość: jeżeli przekształci się tekst jednym kluczem, a następnie wynik przekształci
się za pomocą drugiego, otrzymamy oryginalny tekst. Oznacza to: S i j (P n j (M)) =
a iC a a C a

P«iicja(Sancja(M)) =M. Nie ma innej metody odtworzenia oryginalnego tekstu. W naszym


przykładzie Bogdan przekształca otrzymaną przesyłkę, o której wie, że ma postać
Pbog<ian(M), za pomocą swojego klucza prywatnego. Shoglia„(Pbogdan(M))=M, może więc
odczytać oryginalny tekst napisany przez Alicję.
Rozdział 31. » Bezpieczeństwo i kryptografia___________________________525

Bogdan wie, że nikt inny nie mógł odczytać przesyłki, ponieważ nikt nie ma jego klu-
cza prywatnego. Jednak nie wie, czy przesyłka na pewno pochodzi od Alicji, ponieważ
każdy, kto czytał gazetę, mógł wysłać tę przesyłkę podpisując się jako Alicja.

Teraz Alicja chce wysłać inną przesyłkę do Bogdana; tym razem chce upewnić odbior-
cę, że list pochodzi od niej. Najpierw przekształca tekst za pomocą swojego klucza
prywatnego i dodaje wynik na końcu przesyłki jako sygnaturę: M+SaiiCja(M). Przesyłka
w takiej postaci zostaje wysłana do Bogdana. Po przeczytaniu tekstu przesyłki Bogdan,
stosując się do instrukcji, dekoduje sygnaturę za pomocą jej klucza publicznego:
Paiwja(S<iiicja(M))=M i otrzymuje poprawny oryginalny tekst.

Ponieważ nikt nie zna klucza prywatnego Alicji, jest ona jedyną osobą, która mogła
utworzyć taką sygnaturę, więc adresat jest pewny. Zauważ jednak, że tym razem Alicja
wysłała do Bogdana niezaszyfrowany tekst. Każdy złoczyńca mógłby go przeczytać.
Alicja może najpierw podpisać tekst, a następnie zaszyfrować tekst razem z sygnaturą,
korzystając z pierwszej metody (efektem będzie podpisana i zaszyfrowana przesyłka).

W tym schemacie istnieje jednak słaby punkt. Bez spotkania się z Alicją Bogdan nie
może być pewien, że klucz publiczny znaleziony w gazecie należy na pewno do Alicji.
A jeżeli ktoś inny wydrukuje swój klucz pod tym samym imieniem? Jeżeli Bogdan ko-
responduje z wieloma osobami, nie będzie miał czasu na sprawdzanie klucza z osobą.

Załóżmy, że jest jedna osoba, której wszyscy ufają, Tomek. Przechowuje on zestaw
kluczy i oferuje podpisywanie przesyłek za pomocą jego klucza prywatnego w przy-
padku, gdy właściciel dokumentu potwierdzi swoją tożsamość. Alicja ma swój klucz
publiczny podpisany przez Tomka. Publikuje ten klucz, nazywany certyfikatem, w ga-
zecie. Bob sprawdza sygnaturę pliku za pomocą klucza zamieszczonego w gazecie
i klucza publicznego Tomka. Wie, że Tomek podpisał tę przesyłkę, musiał też zidenty-
fikować Alicję, więc klucz w gazecie na pewno należy do Alicji.

Porzućmy już erę transportu przesyłek korzystających z usług Kucyka. Przedstawiona


wcześniej technika została zaimplementowana przez Ronalda L. Rivesta, Adi Shamira
i Leonarda M. Adlemana, którzy szybko j ą opatentowali pod nazwą RSA. System RSA
leży u podstaw prawie każdego z używanych schematów silnego szyfrowania. Każda
aplikacja jest płatna. Opłata oraz ograniczenia eksportu silnych technologii szyfrowania
z USA spowodowały rozpoczęcie prac nad bezpłatnym produktem. W czasie pisania tej
książki rozluźniono ograniczenia eksportowe w USA, a patent RSA wygasał 20 wrze-
śnia 2000 roku.

Ograniczenia eksportowe USA


Kryptografia była i nadal jest ważną składową wywiadu wojskowego. W czasie
pierwszej i drugiej wojny światowej szyfrowanie i łamanie kodów wroga było bar-
dzo ważnym zadaniem. Po wojnie technologie kryptograficzne zostaty uznane
przez Departament Stanu USA za domenę armii. Oznaczało to, że eksport tech-
nologii szyfrowania był tak samo nielegalny jak handel bronią. Wtedy nie stano-
wiło to problemu, ponieważ cywile nie używali szyfrowania. Embargo ograniczało
firmom amerykańskim dostęp do rynku międzynarodowego.
526________________________________Część III » Techniki zaawansowane

Szyfrowanie pojedynczym kluczem


Do szyfrowania i odszyfrowania używa się tego samego klucza. Zwykle działa to nieco
szybciej niż inne metody, ale jest trudniejsze, ponieważ klucz musi być w jakiś sposób
przesłany do odbiorcy. Tutaj jednak może pomóc metoda z kluczem publicznym.

Powróćmy do naszego przykładu. Wyobraź sobie, że Alicja i Bogdan chcą zabezpie-


czyć korespondencję używając szyfrowania pojedynczym kluczem. Alicja prosi Bogda-
na o jego certyfikat zawierający klucz publiczny. Teraz szyfruje tym kluczem klucz
pojedynczy i wysyła wynik do Bogdana. Za pomocą swojego klucza prywatnego może
on odszyfrować przesyłkę, otrzymując pojedynczy klucz Alicji, którego może następnie
użyć do rozpoczęcia konwersacji szyfrowanej pojedynczym kluczem.

Szyfrowanie pojedynczym kluczem nie jest objęte patentem RSA, istnieje wiele jego
odmian, które nie są objęte embargo. Wersja PHP dla Unixa posiada zestaw funkcji re-
alizujących szyfrowanie pojedynczym kluczem przy użyciu biblioteki o nazwie mcrypt.
Aby użyć tych funkcji, należy ściągnąć i zainstalować mcrypt (łącze do źródła biblio-
teki jest podane w podręczniku PHP) oraz przekompilować PHP z opcją konfiguracji
--enable-mcrypt.

W czasie pisania tej książki PHP nie był zgodny z ostatnią wersją
mcrypt. Ostatnią dobrze działającą wersją była iibmcrypt-2.2. 6.
Jednak kompilując tę wersję mcrypt należy w czasie konfiguracji włą-
czyć opcję —disabie-posix-thread. Zignorowanie powoduje awa-
rię Apache.

Mcrypt pozwala na wybór z kilku metod szyfrowania — różnych algorytmów z jednym


kluczem. Każdy z algorytmów posiada różną szybkość i siłę szyfrowania. Ogólnie rzecz bio-
rąc, DES i Blowfish to znane i dobrze wyważone pod względem siły i szybkości algorytmy.
Jeżeli potrzebujesz dużej szybkości lub silniejszego szyfrowania, powinieneś zapoznać się
z innymi algorytmami dostępnymi w twojej implementacji (wyliczone w mcrypt.h).

Mcrypt posiada również cztery tryby szyfrowania (zestawione w tabeli 31.1).

Tabela 31.1.
Tryby szyfrowania dostępne w mcrypt

Tryb Opis Wektor inicjalizacji

ECB Tłumaczy podany blok danych. Odpowiedni do małych Brak


nieprzewidywalnych bloków, na przykład innych kluczy. Nie używaj
tego trybu dla tekstu: częstotliwość występowania liter lub znaków
przestankowych może być ułatwić złamanie szyfru
CBC Silniejszy, lepszy do szyfrowania danych tekstowych Opcjonalny
CFB Świetnie się nadaje do krótkich bloków danych Tak
OFB Bardzo podobny do CFB, lepiej obsługuje błędy w danych wejściowych Tak
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________527

Ostatnie dwa tryby wymagają wektora inicjalizacji, który służy za stan początkowy dla
algorytmu szyfrowania. Różnica pomiędzy tymi trybami jest ważna dla trybu interak-
cyjnego, gdy szyfrowane są pojedyncze znaki. W takim przypadku ważne jest, aby al-
gorytm nie szyfrował 'a' w taki sam sposób za każdym razem. Jednak interfejs PHP do
mcrypt umożliwia tylko szyfrowanie ciągów, więc każdy z trybów jest akceptowalny.

W zależności od trybu, jakiego chcesz użyć, wywołuj mcrypt_ecb ( ) , mcrypt_cbc ( ) ,


mcrypt_cfb ( ) lub mcrypt_ofb ( ) w następujący sposób:
mcrypt_cbc(cipher, key, data, direction, [iv])

Parametr cipher może przyjmować wartości MCRYPT_DES, MCRYPT_BLOWFISH lub


inny, odpowiedni dla wybranego algorytmu szyfrowania (w dokumentacji PHP znajduje
się pełna lista algorytmów). Za pomocą parametrów key i data przekazuje się klucz
oraz dane do zaszyfrowania. W celu zaszyfrowania danych do parametru direction
przekazuje się MCRYPT_ENCRYPT, do odszyfrowania — MCRYPT_DECRYPT. Wektora
inicjalizacji, dla wymagających go trybów, przekazuje się parametrem iv.

Klucz musi mieć wielkość odpowiednią dla wybranego algorytmu szyfrowania. Aby
sprawdzić, jaka jest odpowiednia wielkość, należy użyć funkcji:
mcrypt_get_key_size(cipher)

Parametr cipher to wybrany algorytm szyfrowania.

W celu wygenerowania losowego klucza lub wektora inicjalizacji można użyć:


mcrypt_create_iv(size, source)

Parametr size jest rozmiarem obiektu, source to MCRYPT_RAND, MCRYPT_DEV_


RANDOM lub MCRYPT_DEVJJRANDOM określający używany generator liczb losowych,
odpowiednio: r a n d ( ) , /dev/random lub /dev/urandom. Jeżeli będziesz korzystał
z rand ( ) , pamiętaj o wcześniejszym wywołaniu srand ( ) w celu zainicjowania gene-
ratora (więcej informacji o liczbach losowych znajdziesz w rozdziale 10.). Właściwe
rozmiary dla wektora i kluczy otrzymuje się wywołaniem funkcji mcrypt_get_
block_size(cipher) oraz mcrypt_get_key_size(cipher).

Pamiętaj, że dane, na których działa mcrypt, mają postaci ciągów PHP z danymi binar-
nymi. Jeżeli chcesz wyświetlać je w postaci czytelnej lub zapamiętać w ciągu teksto-
wym, musisz je w jakiś sposób skonwertować. PHP posiada odpowiednie do takich
zadań funkcje base64_encode ( ) oraz base64_decode ( ) . Więcej informacji na ich
temat znajdziesz w podręczniku PHP.

Szyfrowanie cookie
Wysyłane przez twoją witrynę do przeglądarki cookies zawierają dane na temat użyt-
kownika. Gdy przeglądarka odsyła cookies z powrotem, witryna korzysta z zapisanych
danych do stworzenia nowej strony. Jednak cookie może być dowolnie zmodyfikowane
przez złośliwego użytkownika w celu zmylenia programu obsługi witryny. Za przykład
posłuży nam bardzo prosty program:
528___________________________________Część III » Techniki zaawansowane

<?php
$visits = Svisits + 1;
setcookie("visits", Svisits};
?>
<HTMLXHEADX/HEAD>
<BODY>
<Hl>Byłeś już u nas <?php echo Svisits; ?> razy</Hl>
</BODY>
</HTML>

Więcej informacji o cookies znajdziesz w rozdziale 26.

Mamy tutaj licznik zliczający liczbę wizyt użytkownika na witrynie. Liczba ta prze-
chowywana jest w cookie visits. Użytkownik może je zmienić, aby wskazywało na
np. 10000. Nasz program oczywiście nie wie, że w rzeczywistości użytkownik ten od-
wiedzał stronę rzadziej, i wypisze „Byłeś już u nas 10000 razy".

Korzystając z mcrypt, możemy uniemożliwić takie modyfikacje:


<?php
$key = base64_decode("NCiUrafiRByg=");
if (IsSet($visits))
(
Sencrypted = base64_decode(Svisits);
$visits = mcrypt_cbc(MCRYPT_DES, Skey, $encrypted,
MCRYPT_DECRYPT);
}
Svisits = $visits + 1;
Sencrypted = mcrypt_cbc(MCRYPT_DES, Skey, Svisits,
MCRYPT_ENCRYPT) ;
setcookie("visits", base64_encode(Sencrypted)J;
?>

Mcrypt operuje na ciągach danych binarnych, więc nie możemy ich bezpośrednio wpisać
lub przesłać do przeglądarki. W tym przypadku użyliśmy funkcji PHP base64. Zanim
uruchomimy ten program, utworzymy klucz DES za pomocą następującego programu:
<?php
$key_size = mcrypt_getKey_size(MCRYPT_DES);
$key = mcrypt_create_iv(Skey_size, MCRYPT_DEV_RANDOM);
echo base64_encode(Skey);
?>

Wstawiliśmy utworzony w ten sposób klucz (w postaci przekodowanej base64) do


pierwszego wiersza naszego programu szyfrującego cookie. Zapisujemy liczbę wizyt
w cookie o nazwie visits w postaci zaszyfrowanej i przekodowanej funkcją base64.
Jeżeli zmienna visit posiada wartość, odszyfrowujemy ją. Następnie zwiększamy
licznik i po zaszyfrowaniu oraz przekodowaniu zapisujemy w cookie. Użytkownik zo-
baczy wartość cookie w postaci IQ109yQCEgw%3D i nie będzie jej dowolnie zmieniać.

Przytoczona przez nas zakodowana wartość cookie odpowiada liczbie 7. Włamywacz


może oszukać witrynę, zamieniając bieżącą wartość cookie na zapamiętany ciąg. Jednak
nie może ustawić dowolnej wartości (w tym przypadku byłaby to duża wartość), nie ma
łatwej metody wygenerowania cookie o wcześniej wartości, która nie pojawiła się
wcześniej.
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________529

W celu lepszej obsługi wizyt powinieneś użyć sesji opisanych w roz-


dziale 25.

Przykład ten pokazuje, że należy bardzo dobrze chronić pliki źródłowe. Jeżeli włamy-
wacz podejrzy treść programu, wejdzie w posiadanie klucza używanego do szyfrowania
witryny i będzie mógł łatwo odszyfrować wartość cookie.

Mieszanie
Podpisywanie dokumentów kluczem prywatnym powoduje powstawanie sygnatur
wielkości oryginalnego dokumentu. Staje się to problemem w przypadku, gdy chcesz
podpisywać naprawdę długie pliki. Większość oprogramowania związanego z bezpie-
czeństwem systemu (na przykład mcrypt) jest podpisywana cyfrowo, więc użytkownik
wie, że ostatnia wersja była naprawdę napisana przez autora. Administratorzy obawiają
się, że włamywacze będą podrzucać programy, które instalują „tylne wejście" do systemu.

Potrzebujemy cyfrowego odcisku palca do podpisywania dużych plików. Jeżeli potrak-


tujemy dane binarne zawarte w pliku jako listę liczb całkowitych i zsumujemy je, a na-
stępnie odetniemy ostatnie 128 bitów, w wyniku otrzymamy sumę kontrolną. Autor
pliku może zaszyfrować tę sumę kluczem prywatnym i dołączyć wynik do pliku jako
sygnaturę.

Załóżmy, że włamywacz zmodyfikował plik. Następnie obliczył sumę kontrolną zmian


w pliku C i na końcu pliku umieścił liczbę pomniejszoną o C, tworząc plik mający taką
samą sumę kontrolną jak oryginał. Może dodać niezmienioną zakodowaną sumę kon-
trolnąjako sygnaturę.

Po zapisaniu tak spreparowanego pliku użytkownik może obliczyć sumę kontrolną, na-
stępnie odszyfrować sygnaturę i sprawdzić, że wyniki są identyczne. Użytkownik bę-
dzie korzystał z tego pliku, przyjmując, że stworzyła go rzeczywiście osoba podpisana.

Oczywiście kryptografowie dostarczyli nam lepsze rozwiązania. Trzeba utrudnić wpro-


wadzanie zmian do pliku, które w wyniku daj ą taką samą sumę kontrolną. Wymyślono
wiele algorytmów mieszających. Algorytmy mieszające są modyfikacjami metody szy-
frowania pojedynczym kluczem i dają w wyniku sumę kontrolną o określonej długości,
z której nie da się odtworzyć oryginalnego komunikatu.

Jak można się spodziewać PHP posiada zestaw funkcji mieszających. Bazują one na do-
stępnej publicznie bibliotece mhash. W podręczniku PHP znajduje się łącze do najnow-
szej jej wersji.

Kompiluj bibliotekę mhash z opcją konfiguracji --disabie-pthreads.


Bez tej opcji Apache nie będzie działał.
530___________________________________Część III » Techniki zaawansowane

Funkcja mhash(typ, ciąg) oblicza wynik mieszania wartości parametru ciąg przy
użyciu metody podanej w parametrze typ. Typowymi wartościami tego parametru są
MCRYPT_MD5 i MCRYPT_SHA1. Kompletna lista możliwych wartości znajduje się w pod-
ręczniku PHP.

Cyfrowe podpisywanie plików


Zaprezentujemy teraz program, który akceptuje tylko poprawnie podpisane pliki. Za-
kładamy, że nasza witryna posiada listę nazw użytkowników i ich klucze Blowfish.
Funkcja get_user_key ( u s e r n a m e ) zwraca nam te klucze. Użytkownik, przesyłający
plik, tworzy dla niego sygnaturę za pomocą algorytmu MD5 i szyfruje wynik za pomo-
cą swojego klucza Blowfish.
<HTMLXHEADX/HEAD>
<BODY>
<?php
if (empty(Sfile) I I !IsSet(Susername) I I empty($sig))
(
?>
<Hl>Przesyłanie pliku</Hl>
<FORM E N C T Y P E = " m u l t i p a r t / f o r m - d a t a " METHOD="POST">
< I N P U T T Y P E = " H I D D E N " NAME="MAX_FILE_SIZE" V A L U E = " 2 0 0 0 0 0 0 " >
Nazwa p l i k u : <INPUT NAME="file" TYPE="file"xbr>
Nazwa p l i k u sygnatury: <INPOT NAME="sig" TYPE="file"xbr>
Użytkownik <INPUT NAME="username" TYPE="text"xbr>
< I N P U T T Y P E = " S U B M I T " VALUE="Wyśli j "X/FORM>
<?
}
else
{
$fp = fopen($file, "r");
$hash = mhash(MHASH_MD5, fread($file, Sfile_size));
fclose(Sfp);
$key = get_user_key($username);
$encr_hash = mcrypt_cbc(MCRYPT_BLOWFISH, Skey, $hash,
MCRYPT_ENCRYPT);
$sfp = fopen($sig);
$sig_data = freadISsig, $sig_size);
fclose(5sfp);
if ($encr_hash !- Ssig_data)
echo "<Hl>Plik odrzucony — sygnatury się nie zgadzają</Hl>";
else
{
echo "<H1>W porzadku</Hl>";
// Dalsza obsługa przesłanego pliku
)
} ?>
</BODY>
</HTML>

Program ten wykonuje te same czynności, co użytkownik: najpierw generuje sumę


kontrolną za pomocą funkcji mieszającej, następnie szyfruje wynik za pomocą klucza
użytkownika. Jeżeli sygnatura użytkownika i wygenerowana przez witrynę są takie sa-
me, zakładamy, że plik jest autentyczny. Jeżeli się różnią, plik jest odrzucany.
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________531

Secure Server Layer


Przedstawione do tej pory zastosowania kryptografii zabezpieczają dane serwera. Z szy-
frowania pojedynczym kluczem korzystaliśmy przy kodowaniu danych zapisywanych
przez serwer w komputerze klienta (cookie), aby ich zmiana była niemożliwa. Użycie
algorytmu mieszającego pozwala serwerowi wykryć sfałszowane pliki i odrzucić je.

Teraz skupimy się na bezpieczeństwie użytkownika twojej witryny. Często przesyła on


do witryny prywatne dane. Hasła użytkownika lub numery kart kredytowych muszą być
przesyłane w dyskretny sposób.

Protokół Secure Server Layer (SSL) pozwala na zrealizowanie takiej transmisji, unie-
możliwia podsłuchiwanie. Pozwala również, aby witryna udowodniła użytkownikowi,
że jest tą, za którą się podaje. Opcjonalną możliwością jest taka sama identyfikacja
użytkownika. Nie wgłębiając się w kryptograficzne szczegóły, SSL działa na podstawie
szyfrowania kluczem publicznym w celu udowodnienia autentyczności serwera, wy-
mienia się kluczami używanymi do szyfrowania konwersacji. Następnie przełącza się
na szyfrowanie pojedynczym kluczem, ponieważ jest o wiele szybsze.

Bezpłatna implementacja SSL nie zawiera oprogramowania do klucza publicznego. Po-


nieważ RSA opatentowało szyfrowanie kluczem publicznym, mieszkańcy USA muszą
kupić jeden z licencjonowanych pakietów szyfrujących, aby używać SSL. Mieszkańcy
reszty świata mogą używać odwołującej się do RSA implementacji o nazwie RSAREF,
na którą nie nałożono ograniczeń licencyjnych.

Niezależnie od sposobu licencjonowania oprogramowania SSL, musisz kupić certyfikat


dla witryny od jednej ze znanych firm autoryzujących certyfikaty. Firmy takie dostar-
czają gwarancję autentyczności certyfikatu użytkownikowi korzystającemu z twojej
witryny (niestety nie gratis).

Porównanie serwerów SSL wykracza poza zakres tej książki. Autorzy bezpłatnego opro-
gramowania wierzą, że takie implementacje są najlepsze i najpewniejsze. Wiele z ko-
mercyjnych serwerów SSL bazuje na bezpłatnych implementacjach! Jeżeli kupisz
komercyjną implementację, będziesz miał możliwość uzyskania pomocy od producenta.

Witryny podejmujące
problematykę bezpieczeństwa
Jeżeli po przeczytaniu tego rozdziału nie możesz zasnąć — nie przejmuj się. Każdy ad-
ministrator i projektant witryny boryka się z tymi samymi problemami. Istnieje wiele
witryn poświęconych bezpieczeństwu komputerów; prawie każda z nich zawiera pełny
opis ostatnio zidentyfikowanych zagrożeń i sposobów zabezpieczenia systemu przed
atakiem. Niektóre są przeznaczone dla „służb bezpieczeństwa", inne dla włamywaczy...
Wszystkie dostarczaj ą użytecznych i interesujących informacji.
532________________________________Część III » Techniki zaawansowane

Rozpocznij przeglądanie od następujących witryn:


* CERT: Computer Emergency Response Team (http://www.cert.org/)
CERT jest jedną ze znanych składnic oficjalnych opisów nadużyć komputero-
wych. Publikuje porady, dodając bardzo przejrzyste omówienia problemów
i rozwiązań.
+ Security-focus.com (http://www.secutityfocus.com/)
Security-focus.com zawiera wiele danych o różnych aspektach bezpieczeństwa
komputerów, od prawnych i politycznych do technicznych. Prowadzi również
listę dyskusyjną o nazwie BugTraq (można do niej przejść łączem zatytułowa-
nym „Forums").
+ Rootshell (http://rootshell.com/)
Rootshell jest szanowaną witryną zawierającą rzetelne opisy techniczne wielu
słabych punktów systemów. Udostępnia opisy, w jaki sposób wykorzystać te
słabe punkty, a także jak je usunąć.
4 Insecure.org (http://insecure.org/)
Insecure.org jest witryną, która nie obawia się publikacji narzędzi do włama-
nia; omawia szczegóły wielu nadużyć. Jest niezmiernie użyteczna, jeżeli
chcesz włamać się do własnego systemu.
•* LOpht Heavy Industries (http://www. lOpht. com/index, html)
LOpht jest kolejną kontrowersyjną witryną, prowadzoną przez „recydywistów".
Zawiera również wiele interesujących opinii w sekcji „soapbox".

Podsumowanie
Dla każdej znanej witryny WWW bezpieczeństwo jest jednym z kluczowych zagadnień.
Powinieneś być niezmiernie dokładny przy zabezpieczaniu serwera i prywatnych danych
użytkowników. W czasach rozwoju biznesu sieciowego publikacja artykułu o dużych lu-
kach w systemie bezpieczeństwa witryny może podważyć zaufanie użytkowników i odej-
ście do konkurencji.

W tym rozdziale zapoznaliśmy się z trzema ważnymi zagadnieniami.

Nie ufaj Internetowi. Każdy przychodzący bajt powinien być traktowany jako poten-
cjalnie niebezpieczny. Szczególnymi restrykcjami obejmij tworzenie dostępnych da-
nych wejściowych. Listy dostępnych wartości powinny być najbezpieczniejszym
rozwiązaniem. Upewnij się, że konfiguracja serwera WWW nie pozwala na podejrzenie
kodu źródłowego lub obejście praw dostępu.

Ograniczaj uszkodzenia. Uszkodzenia spowodowane przez włamanie powinny być mi-


nimalizowane. Szyfruj ważne dane. Jeżeli masz własny serwer WWW, upewnij się, że
działa on na prawach użytkownika z bardzo szczupłym pakietem uprawnień.
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________533

Jeżeli prowadzisz własny serwer, spróbuj się do niego włamać. Jeżeli się uda, zidentyfi-
kujesz dziury, które możesz załatać, zanim skorzysta z nich ktoś inny. Jeżeli się nie uda,
odkryjesz tajniki bezpieczeństwa serwera. Jeżeli dzierżawisz serwer, spróbuj poznać
osobę, która może pomóc w zabezpieczaniu witryny.
534 Część III » Techniki zaawansowane
Rozdział 32.
Konfiguracja i dostrajanie
W tym rozdziale:
* Podglądanie zmiennych środowiska
* Konfigurowanie Apache dla PHP
* Konfigurowanie PHP
* Poprawianie wydajności

W rozdziale tym omówimy szczegółowo wiele opcji konfiguracji PHP, szczególnie te,
które są dostępne w wersji PHP będącej modułem serwera Apache. Celem rozdziału jest
to, abyś poznał plusy i minusy włączenia i wyłączenia każdej z opcji. Zapoznamy się
również ze sposobami mierzenia i polepszania wydajności skryptów PHP.

Podglądanie zmiennych środowiska


Aby zobaczyć wszystkie wymienione tu zmienne, należy użyć w skrypcie funkcji
phpinfo ( ) . Wynik działania tej funkcji rozpoczyna się krótkim zestawieniem zawie-
rającym wersję PHP, platformę, datę utworzenia i zmienne uaktywnione w czasie kom-
pilacji. Następnie metodycznie wymienione są wszystkie ustawienia PHP. Wersja dla
Apache na Uniksie zawiera również wyczerpującą listę ustawień Apache.
Plik taki jest niezmiernie cenny dla włamywaczy, więc nie należy udostępniać go na
ważnych serwerach.

Poznajemy konfigurację PHP


Jak większość najlepszego bezpłatnego oprogramowania, PHP ma wiele opcji konfigu-
racji. Każdemu użytkownikowi pozostawiono ustawienie odpowiedniej równowagi po-
między zwalczającymi się parametrami: wydajnością, elastycznością, bezpieczeństwem
i łatwością użycia.
536___________________________________Część III + Techniki zaawansowane

Trudno jest w pełni opisać konfigurację, ponieważ jest bardzo wiele możliwych kombi-
nacji parametrów — ściśle rzecz ujmując, około 25 silnia. W niektórych przypadkach
występuje oczywisty konflikt pomiędzy dyrektywami konfiguracji — musisz wybrać
jedną z nich. W innych przypadkach będziesz potrzebował kilku obejść. Spróbujemy
opisać konsekwencje działań, ile tylko się da. Jednak nikt nie może dać słowa, że prze-
testował wszystkie możliwe kombinacje.

Konfiguracja PHP różni się w zależności od platformay. Możemy porównać instalację


na Uniksie do wykwintnej kolacji zamówionej w dobrej restauracji, w Windows — do
posiłku w barze, (mamy niewielki wybór). Najgorszy jest pakiet RPM, który możemy
porównać do jedzenia na telefon — musisz zjeść to, co dostarczono — może być pysz-
ne albo paskudne.

Wersja PHP dla Windows ma niewiele opcji — szczególnie jeżeli wybierzesz firmowy
serwer WWW, taki jak Microsoft IIS. Początkujący użytkownicy PHP mogą uważać ten
brak wyboru za komfortowy; zrzucają całą odpowiedzialność na osobę, która utworzyła
program instalacyjny. Użytkownicy Windows muszą ustawić tylko zmienne dostępne
w pliku php.ini — nie wszystkie z nich mają zastosowanie w aplikacjach Windows.
Można więc bezpiecznie przejść do części rozdziału poświęconej plikowi php. ini.

Użytkownicy Unixa maj ą bogatszą paletę opcji. Aby w pełni skorzystać z tej mocy, mu-
sisz dokładnie poznać różne aspekty, pod kątem których przeanalizujesz i wyregulujesz
instalację PHP. W systemie Unix najważniejsze są:
* opcje kompilacji;
** pliki konfiguracyjne serwera WWW;
* p\\kphp.ini.

Istnieje również kilka opcji, które mogą być: sterowane opcjami ustawianymi w czasie
działania programu, ustawieniami systemu, istnieniem, brakiem i konfiguracją innych
pakietów oprogramowania.

Opcje kompilacji
W czasie konfigurowania kompilacji PHP pozwala ustawić wiele specyficznych znacz-
ników. Powoduje to dołączenie do modułu PHP odpowiednich plików.

Dobrze wiedzieć, że większość opcji kompilacji jest niezbędnym wstępem do używania


niektórych zestawów funkcji (możesz taki zestaw wyłączyć lub włączyć odpowiednim
wpisem do pliki php.ini). Procesy konfiguracji i kompilacji są ze sobą ściśle połączone.
Można rozumować w następujący sposób: muszę skompilować moduł z ustawionym
odpowiednim znacznikiem, ale nie muszę używać tego zestawu funkcji jedynie dlatego,
że skompilowałem w ten sposób moduł.
Rozdział 32. » Konfiguracja i dostrajanie________________________________537

Jeżeli zapomnisz podać odpowiednią opcję kompilacji, przy próbie


wywołania funkcji wystąpi błąd „Niezdefiniowana funkcja". Błąd ten
prawie nigdy nie występuje poza funkcjami zdefiniowanymi przez użyt-
kownika, więc powinien być on traktowany jak czerwone światło,
wskazujące na błąd w kompilacji PHP.

Pamiętaj, że wszystkie dodatkowe serwery i biblioteki oprogramowa-


nia powinny być zainstalowane przed kompilacją PHP. Oznacza to, że
serwery: WWW, bazy danych i LDAP, biblioteki: XML, szyfrujące i ma-
tematyczne muszą być na swoim miejscu, zanim skompilujesz PHP.

-with-apache[=KATALOG]
Jest to najważniejsza opcja, ponieważ powoduje skompilowanie PHP do postaci sta-
tycznego modułu Apache. Mimo że w obecnie najczęściej stosuje się PHP w postaci
modułu Apache, projektanci PHP postanowili ustawić jako domyślne kompilowanie do
postaci CGI. Jeżeli będziesz chciał otrzymać moduł Apache i zapomnisz ustawić tę
opcję, otrzymasz wersję CGI.

Warto ustawić katalog bazowy dla Apache; make ustawia go domyślnie na /usr/local/
etc/httpd. Pamiętaj, że Apache instaluje się w innym katalogu w przypadku kompilowa-
nia ze źródeł niż w przypadku instalowania RPM —jeżeli wcześniej instalowałeś Apa-
che z RPM (na przykład w trakcie instalowania Red Hat Linux), będziesz musiał
najprawdopodobniej usunąć ten pakiet, aby pozostawić wolne miejsce dla wersji, którą
właśnie kompilujesz ze źródeł.

-with-apxs
Opcja ta wskazuje, że moduł PHP będzie utworzony jako dynamiczny moduł Apache.
Oszczędza to nieco miejsca na dysku, a niektórzy uważają, że kompilacja taka jest ła-
twiejsza. Dla większości użytkowników nie ma większej różnicy pomiędzy statycznym
a dynamicznym modułem Apache.

-with-fhttpd[=KATALOG]
Opcja powoduje skompilowanie PHP do postaci modułu serwera fhttpd. Domyślnym
katalogiem jest /usr/local/src/fhttpd.

Powyższe trzy opcje wzajemnie się wykluczają. Możesz zastosować


tylko jedną znich. Szczególnie uważaj, aby nie podać —with-apache
i --with-apxs.
538________________________________Część III » Techniki zaawansowane

-with-database[=KATALOG]
Wszystkie bazy danych obsługiwane przez PHP używają podobnych opcji kompilacji.
Podanie katalogu jest potrzebne jedynie w przypadku, gdy instalujesz bazę danych
w innym katalogu niż standardowy. Więcej informacji na temat wyboru bazy danych
dla PHP w rozdziale 16.

Tabela 32.1.
Opcje kompilacji związane z bazami danych

Nazwa bazy danych Domyślny katalog Składnia opcji kompilacji


Adabas D* /usr/local/adabasd --with-adabas [=KATALOG]

Custom ODBC* /usr/Iocal --with-custom-odbc [=KATALOG]

DBase związany z bazą --with-dbase

Filepro związany z bazą --with-f ilepro

iODBC /usr/local --with-iodbc [=KATALOG]

mSql /usr/local/Hughes — with-msql [=KATALOG]

MySQL /usr/local --with-mysql [=KATALOG]

Openlink* /usr/local/openlink — with-openlink [=KATALOG]

Oracle ORACLE HOME --with-oracle [=KATALOG]

PostgreSQL /usr/local/pgsql --with-pgsql [=KATALOG]

Solid* /usr/local/solid — with-solid[=KATALOG]

Sybase /home/sybase --with-sybase [=KATALOG]

Sybase-CT /home/sybase --with-sybase-ct [=KATALOG]

Velocis* /usr/local/velocis — with-velocis [=KATALOG]

Bazy danych oznaczone w tabeli gwiazdką używaj ą komunikacji przez ODBC. Opcje te
wzajemnie się wykluczają — musisz ograniczyć się tylko do jednej. Dodatkową dy-
rektywą kompilacji, jakiej możesz użyć przy bazach danych, jest --disabie-
unif ied-odbc. Pomaga w unikaniu konfliktów pomiędzy bibliotekami ODBC.

Każda baza danych posiada nieco inne opcje konfiguracji w pliku php.ini lub innych
plikach konfiguracyjnych. Na przykład Oracle ma własne zmienne środowiska, które
omijają ustawienia PHP. Sybase używa innego stylu oznaczania apostrofów, dlatego
wymag opcji plikuphp.ini magic_quotes_sybase. MySQL pozwala podać domyślne
nazwy: komputera z bazą danych, użytkownika i hasło — nie jest to dobry pomysł,
chyba że dokładnie wiesz jakie, mogą być następstwa! Większość z tych opcji jest pro-
sta i łatwa do zrozumienia, ma niewielki wpływ na inne części PHP.
Rozdział 32. » Konfiguracja i dostrajanie________________________________539

Jeżeli intensywnie korzystasz z bazy danych, możesz ustawić opcję --enable-magic-


quotes. Opcja ta jest wbudowana — możesz ją włączyć, dodając do pliku php.ini
dyrektywę magic_quotes_runtime. Jeżeli chcesz mieć ścisłą kontrolę, możesz do-
dawać i usuwać znaki „\", nie polegając na mechanizmie obsługi automatycznej.

-enable-track-vars
Opcja ta uaktywnia predefiniowane zmienne PHP HTTP_GET_VARS, HTTP_POST_VARS
i HTTP_COOKIE_VARS. Może być pomocna w sytuacji, gdy używasz w programach
wielu metod przekazywania danych. Pozwala na upewnienie się, skąd pochodzi każda
wartość. Możesz wyłączyć tę opcję, korzystając ze zmiennej pliku php.ini track_vars.

Nowy plik php.ini-optimized wyłącza zmienną track_vars w celu po-


lepszenia wydajności. Jednak nie spodziewaj się, że będziesz miał
wszystkie zmienne w tablicy $HTTP_POST_VARS i jej braciach. Musisz
przejrzeć zawartość pliku php.ini-optimized, aby poznać konsekwencje
zastosowania wszystkich użytych tam opcji.

-with-ldap[=KATALOG]
Opcja ta pozwala PHP na podłączenie się do serwera LDAP (Lightweight Directory
Access Protocol). Domyślnym katalogiem jest /usr/local/ldap. Zanim skompilu-
jesz PHP, będziesz musiał ściągnąć i zainstalować biblioteki klienta LDAP z jednego
z tych źródeł:
http://developer.netscape.com/tech/directory/
http://www. openldap. org

LDAP jest protokołem, podobnym do bazy danych, który pomaga w zarządzaniu profi-
lami użytkownika i uprawnieniami w sieci. Można go zastosować do obsługi scentrali-
zowanej sieciowej książki adresowej, udostępniającej adresy e-mail klientom w sieci.
Pomaga również w zarządzaniu uprawnieniami — można ustawiać je w jednym miejscu
— jeżeli na przykład pracownik opuszcza firmę, możesz mu od razu odebrać wszystkie
uprawnienia.
*

Nie będziemy opisywali LDAP w tej książce, ponieważ obecnie protokół ten jest uży-
wany jedynie w wielkich sieciowych organizacjach, takich jak firmy międzynarodowe
czy rządowe laboratoria badawcze. Zapoznaj się z artykułem wprowadzającym do ko-
rzystania z LDAP, autorstwa Lincolna Steina:
http://www.webtechniques.com/archives/1999/ll/webm/

Teraz, gdy istnieje bezpłatna implementacja LDAP, coraz więcej odbiorców zaczyna
zeń korzystać. Rasmus Lerdorf, ojciec PHP, napisał krótką informację na temat użycia
LDAP w swojej sieci domowej:
http://www.webtechniques.com/archives/1999/05/junk/junk/shtml
540________________________-__________Część III » Techniki zaawansowane

-with-mcrypt[=KATALOG]
Opcja powoduje wbudowanie w PHP biblioteki mcrypt zawierającej wiele popularnych
algorytmów szyfrujących. Mcrypt jest dostępny na serwerze w Grecji:
ftp://argeas.cs-net.gr/pub/unix/mcrypt/

Brak tutaj udokumentowanego katalogu domyślnego, ale PHP prawdopodobnie znaj-


dzie jeden z katalogów wspomnianych w dokumentacji mcrypt. Więcej informacji na
temat korzystania z szyfrowania w PHP w rozdziale 31.

-with-xml
Opcja powoduje dołączenie analizatora XML expat. Jeżeli kompilujesz PHP do postaci
modułu Apache, nie potrzebujesz dodatkowej biblioteki, ponieważ expat jest wbudowa-
ny w Apache. W innym przypadku musisz ściągnąć bibliotekę z:
http://\vww.jclark. com/xml/

Aby używać XML, musisz wyłączyć skrócone znaczniki (<? ?>) za pomocą opcji
kompilacji --disable-short-tags lub dyrektywą w pliku php.ini short_open_tag.
Analizator PHP rezerwuje ten styl znaczników dla własnych potrzeb, więc PHP musi
o tym wiedzieć. Standardowe znaczniki PHP (</php ?>) to najlepszy wybór (poza
niektórymi przypadkami).

Więcej na temat XML w PHP w rozdziale 29.

-with-dom
Opcja powoduje dołączenie obsługi DOM XML przy użyciu biblioteki GNOMĘ (na-
zywanej również libxml lub gnome-xml). Biblioteka ta wraz z opisem jest dostępna pod
adresem:
http://xxx.xmlsoft. org/

-enable-bcmath
Powoduje dołączenie funkcji pozwalających na obliczenia o dowolnej dokładności.
W plikuphp. ini można ustawić domyślną liczbę miejsc po przecinku.

-with-config-file-path= KATALOG

Opcja pozwala na określenie położenia pliku php. ini. Jest potrzebna jedynie wtedy, gdy
przeniesiesz ten plik do innego katalogu niż /mr/local/lib.
Rozdział 32. » Konfiguracja i dostrajanie________________________________541

-enable-url-includes
Opcja pozwala na dołączanie plików z odległych komputerów za pomocą protokołu
HTTP lub ftp za pomocą funkcji include (http : //remotehost/include . inc).

-disable-url-fopen-wrapper
Opcja wyłącza możliwość otwierania plików z innych serwerów w następujący sposób:
include(http://remotehost/include.inc).

-enable-magic-quotes
Opcja powoduje automatyczne oznaczanie cudzysłowów w tekstach pochodzących
z zewnętrznych źródeł danych, takich jak pliki tekstowe i bazy danych. Jeżeli nie uży-
jesz tej opcji, będziesz musiał dodawać i usuwać ukośniki. Możesz włączać i wyłączać
tę opcję za pomocą dyrektywy w pliku php.ini magic_quotes_runtime. W pliku
php.ini znajdują się dodatkowo ustawienia automatycznego oznaczania cudzysłowów
w zmiennych GET, POST i cookie, oraz dla oznaczania apostrofów dla Sybase.

-enable-debugger
Opcja umożliwia przejście do wbudowanego debuggera PHP. Niestety obecnie jest on
niestabilny i niezgodny z optymal i zatorem Zend.

-disable-syntax-hl

Opcja wyłącza podświetlanie składni.

Opcje kompilacji dla postaci CGI


Wszystkie opcje kompilacji opisane do tej pory, oprócz specyficznych dla postaci
modułu (--with-apache, --with-apxs i —with-fttpd), są również dostępne
w wersji CGI.

-enable-safe-mode
Tryb bezpieczny był zaprojektowany w postaci programu CGI dla PHP. Użytkownicy
modułu serwera muszą się nieco napracować, aby zapewnić podobne działanie — mu-
szą ustawić odpowiednio dyrektywy open_basedir i include_path w pliku php.ini.

Tryb bezpieczny zapewnia:


1. Przetwarzanie plików PHP znajdujących się w określonym katalogu.
2. Zabezpieczenie przed odczytem plików należących do innego użytkownika niż
uruchamiającego PHP, nawet znajdujących się w tym określonym katalogu.
3. Uruchamianie programów tylko z określonego katalogu, np.: /usr/local/bin.
542___________________________________Część III » Techniki zaawansowane

Pamiętaj, że „użytkownik" w tych wyliczeniach określa użytkownika PHP, a nie użyt-


kownika systemu.

W rozdziale 31 opisaliśmy dokładniej tryb bezpieczny. Jednak zwiększanie bezpieczeń-


stwa kosztuje — tym razem płacimy wygodą. Dyskomfort jest prawdopodobnie naj-
częstszą przyczyną wyboru rozwiązań mniej bezpiecznych.

Dla przykładu, do pobrania pliku z repozytorium CVS zwykle używana jest prawdziwa
nazwa użytkownika. Jeżeli bezpośrednio zmieniasz pliki w katalogu, z którego korzysta
serwer WWW, również jej używasz. PHP działający w trybie bezpiecznym nie może
skorzystać z takich plików, trzeba zmienić ich właściciela na nobody. Jeżeli używasz
dużej liczby plików i katalogów, ciągłe zmienianie właściciela może być czasochłonne.

Innym przykładem mogą być pliki z hasłami, umieszczone poza drzewem katalogów
dostępnych przez WWW, na przykład w /usr/local/lib. Ich właścicielem jest określony
użytkownik, a użytkownik PHP musi mieć możliwość je odczytać. W trybie bezpiecz-
nym niestety nie będzie to możliwe — musisz umieścić plik z hasłami w katalogu do-
stępnym dla serwera WWW.

Jeżeli nie masz dostępu do hasła administratora systemu, nie będziesz mógł używać try-
bu bezpiecznego (chyba że administrator tak skonfiguruje PHP, aby uruchamiał się na
prawach określonego użytkownika, korzystając z suExec lub jego odpowiednika). Bez
uprawnień administratora nie da się zmienić właściciela pliku na użytkownika nobody.

Opcja serwera Apache, suExec, która pozwala na uruchomienie CGI


na prawach innego użytkownika niż httpd, nie jest zgodna z trybem
bezpiecznym PHP. Musisz wybrać któreś z nich, aby uniknąć wysłania
kodu binarnego PHP do przeglądarki użytkownika.

Uruchamianie programów w trybie bezpiecznym jest wprowadzone w celu ograniczenia


dostępu do narzędzi systemowych. PHP może współpracować z innymi programami, na
przykład bazą danych lub serwerem pocztowym (jest to komunikacja przez port, a nie
uruchamianie).

Podstawową dyrektywą konfiguracji Apache związaną z trybem bezpiecznym jest do-


cument root. Pamiętaj, że w trybie bezpiecznym nie możesz dołączać plików spoza
tego katalogu, więc ustaw go na odpowiednio wysokim poziomie. Możesz również
ustawić główny katalog dokumentów PHP w pliku php.ini, korzystając ze zmiennej
doc_root. Możesz zastosować drugą metodę, jeżeli tylko jedna część witryny korzysta
z PHP. Za tryb bezpieczny odpowiadają dyrektywy plikuphp.ini safe__mode=on/off
i safe_mode_exec_dir (opcja ta musi być ustawiona w przypadku, gdy zmieniasz
domyślny katalog /usr/local/bin na inny). Możesz również użyć include_path, aby
określić katalogi w drzewie katalogów serwera WWW, przeznaczone na dołączane pliki.

Trybu bezpiecznego dla katalogów nie można włączać i wyłączać za


pomocą plików Apache .htaccess. Zmiana trybu na bezpieczny musi
być wykonana w głównym pliku konfiguracyjnym Apache, httpd.conf.
Rozdział 32. » Konfiguracja i dostrajanie________________________________543

W trybie bezpiecznym nie można używać funkcji set_time_limit ( ) . Musisz pole-


gać na globalnej dyrektywie konfiguracji max_execution_time, znajdującej się
w p\\kuphp.ini.

-with-exec_dir[=KATALOG]
Kolejną opcją związaną z trybem bezpiecznym jest —with-exec-dir. Powoduje
ustawienie domyślnego katalogu z programami do uruchamiania na /usr/local/bin.
Można go zmienić za pomocą dyrektywy pliku php.ini saf e_mode_exec. Pamiętaj, że
w trybie bezpiecznym możesz uruchamiać programy jedynie z tego katalogu.

-enable-discard-path
Jeżeli chcesz umieścić PHP w wersji CGI poza drzewem katalogów WWW i urucha-
miać go tak, jak skrypty CGI Perl (umieszczając w pierwszym wierszu każdego skryptu
# ! /usr/local/bin/php), musisz skorzystać z tej opcji kompilacji. Musisz też dodać
atrybut wykonywania dla wszystkich plików ze skryptami CGI PHP.

-enable-force-cgi-redirect
Ustawienie tej opcji jest konieczne ze względów bezpieczeństwa. Zabezpiecza ona
przed bezpośrednim uruchamianiem skryptów CGI przez użytkowników, które powo-
duje obejście zabezpieczeń Apache. Ale jeżeli korzystasz z innego serwera WWW, jest
bezużyteczna.

Pliki konfiguracyjne Apache


Jeżeli PHP jest używany razem z serwerem Apache, zarówno jako moduł, jak i CGI,
większość opcji jest określana przez pliki konfiguracyjne Apache. W ostatnich wersjach
serwera Apache głównym plikiem jest httpd.conf, zawierający główne ustawienia, oraz
pliki .htaccess określające ustawienia dostępu do katalogów. W starszych wersjach plik
httpd.conf był podzielony na trzy części (access, conf, httpd.conf\ srm.conf), niektórzy
użytkownicy wolą korzystać z tego układu.

W PHP 3 istniały dyrektywy konfiguracyjne Apache, które potrafiły zastąpić prawie


każde ustawienie z pliku php.ini. Zamiast umieszczać Engine=0n w pierwszym istot-
nym wierszu, można było ulokować php3_engine on w pliku .htaccess. Wraz ze
wzrostem liczby dyrektyw konfiguracji PHP zdecydowano, że do konfiguracji Apache
wstawia się zbyt wiele przełączników. Dlatego schemat nazywania opcji został uogól-
niony i teraz wszystkie podlegają czterem podstawowym dyrektywom:
php_value nazwa wartość Ustawia wartość zmiennej
php_flag nazwa on | of f Ustawia zmienną logiczną
544___________________________________Część III » Techniki zaawansowane

php_admin_value nazwa wartość Ustawia zmienną. Może być


używana jedynie w głównym pliku
konfiguracyjnym Apache; nie można
jej używać w .htaccess
php_admin_flag nazwa on I of f Ustawia zmienną logiczną. Może
być używana jedynie w głównym
pliku konfiguracyjnym Apache; nie
można jej używać w .htaccess

Przykładem może być wyłączenie automatycznego oznaczania apostrofów w zmien-


nych GET, POST i cookie. W PHP 3 można wykonać to za pomocą wiersza w pliku
httpd.conflub .htaccess:
php3_magic_quotes_gpc O f f «

W PHP 4 skorzystamy z dyrektywy php_f lag z nazwą odpowiedniej zmiennej:


php_flag magic_quotes_gpc off

Nowe dyrektywy konfiguracji Apache stosowane sąjedynie do opcji, które można rów-
nież zmienić w p\ikuphp.ini.

Serwer Apache ma bardzo rozbudowany i dlatego nieco skomplikowany system konfi-


guracji. Więcej na ten temat znajdziesz na witrynie Apache:
http://www. apache, org

Wymienimy teraz ustawienia wpływające bezpośrednio na PHP.

Timeout
Wartość tego parametru określa liczbę sekund, zanim upłynie czas ważności wywołania
HTTP. W trybie bezpiecznym parametr ten jest ignorowany; musisz ustawiać go w pli-
ku php.ini.

DocumentRoot
Określa główny katalog dla wszystkich wywołań HTTP na tym serwerze. W systemie
Unix ustawienie to wygląda następująco:
DocumentRoot "/usr/local/apache_l.3.6/htdocs"

W systemie Windows będzie to:


DocumentRoot "C:/Program Files/Apache/htdocs"

Głównym katalogiem dokumentów może być właściwie dowolny katalog (niekoniecz-


nie katalog instalacji Apache). Możesz podać jeden z jego podkatalogów jako główny
katalog dokumentów PHP, używając ustawienia doc_root z plikuphp. ini.
Rozdział 32. » Konfiguracja i dostrajanie________________________________545

AddType
Aby pliki PHP były analizowane, musisz za pomocą tej opcji ustawić typy minie dla
PHP. Nie usuwaj pochopnie komentarza w wierszu ze źródłem PHP (.phps) tylko dlate-
go, że tak napisano w podręczniku instalacji. Pomyśl, w ilu sytuacjach będziesz chciał
ściągnąć źródło PHP z twojego serwera.

Pamiętaj, że możesz skojarzyć dowolne rozszerzenie pliku z PHP. Wielu administrato-


rów łączy z PHP pliki .php3 i .html w celu zapewnienia zgodności z poprzednimi wer-
sjami. Jeżeli chcesz, możesz również przetwarzać pliki o nazwach plik.asp lub plik.jsp.
Możesz także ustawić różne typy plików dla różnych wersji PHP. Poniżej przedstawia-
my kilka przykładowych wierszy AddType: pierwszy jest najczęściej stosowana dla
PHP 4, ale możesz dodać tyle, ile zechcesz.
AddType application/x-httpd-php4 .php
AddType application/x-httpd-php3 .php3
AddType appiication/x-httpd-php4 .htrnl
AddType application/x-httpd-php4 .php4
AddType application/x-httpd-php4 .phtml

Możesz również ustawić te typy dla określonego katalogu, z wykorzystaniem pliku


.htaccess, dodając odpowiednie wiersze do tego pliku. Pozwala to na udostępnienie pli-
ków PHP znajdujących się w jednym katalogu, na przykład w katalogu z tablicą ogło-
szeniową. Możesz w ten sposób też skonfigurować katalog zawierający zarchiwizowane
wersje plików ze starymi rozszerzeniami — Apache zanalizuje pliki z rozszerzeniem
.phtml tylko w tym katalogu.

Action
Musisz zastosować ten wiersz, korzystając z PHP w postaci CGI dla serwera Apache,
szczególnie gdy używasz Windows.
Action application/x-httpd-php4 "/php/php.exe"

ScriptAlias
Musisz zastosować ten wiersz, korzystając z PHP w postaci CGI dla serwera Apache,
szczególnie gdy używasz Windows.
ScriptAlias /php/ "c:/php/"

Plik php.ini
Plik konfiguracyjny PHP, php.ini, jest ostatnią i najszybszą metodą wpływu na zachowanie
się PHP. Od czasu wprowadzenia wersji beta PHP w strukturze tego pliku zaszły poważne
zmiany, więc jeżeli do tej pory dokładnie mu się nie przyjrzałeś, teraz jest ku temu okazja.

Plik php.ini jest zawsze odczytywany w czasie inicjalizacji PHP — czyli za każdym
ponownym uruchamianiem httpd, w przypadku stosowania modułu serwera, lub za każ-
dym wykonaniem skryptu, w przypadku wersji CGI. Jeżeli zmiany, które wprowadziłeś,
nie przynoszą skutku, zatrzymaj i wystartuj httpd. Jeżeli nadal nie ma efektu, skorzystaj
546___________________________________Część III » Techniki zaawansowane

z funkcji phpinfo ( ) , aby sprawdzić ścieżkę do p\\kuphp.ini (blisko początku). Jeżeli


jest to konieczne, możesz przekompilować PHP z opcją —with-conf ig-f ile-path
lub przenieść p\ikphp.ini w miejsce, w którym PHP się go spodziewa.

Co się stanie, gdy PHP nie znajdzie php.ini? W Windows, do chwili


powstania oficjalnej wersji PHP 4, powodowało to błąd krytyczny „nie
mogę znaleźć pliku konfiguracyjnego". W systemie Unix, i obecnie
w Windows, dzięki modułowi ISAPI błędy nie występują. — PHP roz-
pocznie pracę z domyślnymi wartościami, które są identyczne jak
w oryginalnym pliku php.ini-dist. Musisz zainstalować plik php.ini, je-
żeli chcesz wprowadzić zmiany w domyślnych wartościach.

Plik konfiguracyjny jest dokładnie i porządnie skomentowany. W nazwach kluczy roz-


różniane są wielkie i małe litery (nie w wartości klucza). Odstępy i linie rozpoczynające
się od średnika są ignorowane. Wartości logiczne są reprezentowane przez l i O, Yes
— No, On — off oraz True — False. Domyślne wartości parametrów konfiguracji
dają rozsądnie działającą witrynę, którą można później zoptymalizować.

Wymienimy teraz ustawienia w pliku php.ini, które nie są odpowiednio udokumento-


wane ani w podręczniku PHP na stronie configuration.html, ani w samym pliku.

short_open_tag = Off
Krótkie znaczniki otwierające (nazywane w PHP 3 krótkimi znacznikami) wyglądają
następująco: <? ?>. W chwili wydawania książki nie działały one w PHP 4 i dlatego
użytkownicy PHP zmuszeni są do korzystania ze standardowych znaczników PHP
(<?php ?>). Opcja ta musi być wyłączona, jeżeli chcesz użyć funkcji XML.

safe_mode = Off
Jeżeli opcja ta jest ustawiona na On, prawdopodobnie kompilowałeś PHP z opcją
—enable-safe-mode. Tryb bezpieczny jest stosowany przy korzystaniu z wersji CGI
PHP. Dokładne objaśnienie tego trybu znajduje się we wcześniejszej części tego roz-
działu „Opcje kompilacji dla postaci CGI".

saf e_mode_exec_di r= [ KATALOG ]

Opcja ta jest stosowana tylko w trybie bezpiecznym. Może być ona ustawiona również
za pomocą opcji kompilacji --with-exec_dir. W trybie bezpiecznym PHP będzie
uruchamiał jedynie programy znajdujące się w tym określonym katalogu. Domyślnym
katalogiem jest /usr/local/bin. Nie wpływa on na proces dostarczania stron HTML.

safe_mode_allowed_env_vars=[PHP_]
Opcja pozwala na wyszczególnienie zmiennych środowiska, które można zmieniać
w trybie bezpiecznym. Wartością domyślną są wszystkie zmienne rozpoczynające się od
PHP_. Jeżeli nie podamy żadnej wartości, będzie można zmieniać większość zmiennych.
Rozdział 32. « Konfiguracja i dostrajanie________________________________547

safe_mode_protected_env_vars=[LD_LIBRARY_PATH]
Opcja pozwala na podanie listy zmiennych środowiska, których nie można zmieniać
w trybie bezpiecznym, nawet gdy opcja saf e_mode_allowed_env_vars na to pozwala.

disable_functions=[funkcjal, funkcja2, ... funkcjan]


Rozszerzeniem konfiguracji PHP 4 jest możliwość wyłączania wybranych funkcji. Jest
to często stosowane z powodu bezpieczeństwa. Poprzednio, aby uzyskać taki sam wy-
nik, należało zmienić plik w C, z którego powstaje PHP. Najczęściej na tej liście znaj-
dują się funkcje operujące na plikach, systemowe i sieciowe — pozostawienie
możliwości zapisu plików i zmiany systemu przez połączenie HTTP jest niebezpieczne.

max_execution_time = 30
W trybie bezpiecznym nie działa funkcja set_time_limit ( ) , więc opcja ta jest pod-
stawowym sposobem na określenie maksymalnego czasu działania skryptu. W Win-
dows musisz określić taki warunek na podstawie ilości zużytej pamięci, a nie na
podstawie czasu. Jeżeli korzystasz z Apache, możesz również użyć analogicznej opcji
konfiguracji Apache (wpływa to na inne typy plików).

error_reporting = E_ALL & ~E_NOTICE


Do niedawna opcja ta była polem bitowym zawierającym maksymalnie 15 wartości
(i w chwili wydawania książki dokumentacja nadal tak podawała). Z powodu częstych
pomyłek konieczna była zmiana tej zmiennej 4 na ciąg. Domyślnym ustawieniem jest
raportowanie wszystkich błędów oprócz ostrzeżeń (co odpowiada starej wartości 7). Je-
żeli nie jesteś pewny, jak należy ustawić tę opcję, zalecamy ustawienie jej na E_ALL.
Wszystkie możliwe błędy i ostrzeżenia będą wyświetlane, co powinno przyspieszyć
uruchamianie skryptów i pozwolić na bardziej efektywną naukę PHP. Serwery używane
do tworzenia witryny powinny mieć tę opcję ustawioną przynajmniej na wartość do-
myślną, jedynie w przypadku serwerów roboczych można pomyśleć o ustawieniu jej na
wartość mniejszą.

error_prepend_string = ["<font color=ffOOOO>"]


Opcja ta, wraz z opcją error_append_string, pozwala na wyświetlanie komunika-
tów o błędach w innym kolorze niż reszta tekstu. Można również tak ją ustawić, aby
wyświetlała migające komunikaty o błędach, przypisując do niej wartość "<blink>"
(i oczywiście error_append_string na "</blink>"). Domyślne wartości spowo-
dują wyświetlanie błędów czerwonym kolorem. Jeżeli chcesz korzystać z tej opcji, pa-
miętaj ojej odblokowaniu, ponieważ domyślnie jest zakomentowana.

warn_plus_overloading = Off
Powoduje wyświetlenie ostrzeżenia w przypadku użycia operatora " + " dla ciągów, ta-
kich jak na przykład wartości pochodzące z formularza.
548___________________________________Część III « Techniki zaawansowane

variables_order = "EGPCS"
Ta opcja konfiguracji nadpisuje gpc_order. Pozwala na ustawienie kolejności inicjo-
wania różnych zmiennych: środowiska, GET, POST, cookie i serwera (zwanych wbudo-
wanymi). Możesz dowolnie zmieniać tę kolejność. Zmienne są inicjowane w kolejności
od lewej do prawej, więc leżąca skrajnie po prawej stronie zawsze „wygrywa". Oznacza
to, że jeżeli pozostawisz domyślne ustawienie i użyjesz tej samej nazwy zmiennych dla
zmiennej środowiska, zmiennej POST i zmiennej cookie, inicjowania zmienna będzie
miała wartość zmiennej cookie. Jednak na całe szczęście sytuacja taka nie zdarza się
zbyt często.

register_globals = On
Pozwala zadecydować, czy rejestrować zmienne EGPCS jako zmienne globalne. Jeżeli
używasz track_vars, prawdopodobnie nie będzie to konieczne.

gpc_order = G PC
Opcja niezalecana. Należy korzystać z nowej, bardziej szczegółowej opcji varia-
bles_order.

magic_quotes_gpc = On
Ustawienie to powoduje oznaczenie apostrofów w danych pochodzących ze zmiennych
GET, POST i cookie. Jeżeli używasz dużo formularzy, możesz ustawić tę dyrektywę na
On lub pamiętać o używaniu funkcji addslashes ( ) do danych tekstowych. Załóżmy,
że mamy formularz z polem, do którego użytkownicy wpisują swoje nazwiska:
<FORM METHOD="post" ACTION="formhandler.php">
<INPUT TYPE="text" NAME="surname" SIZE-25>

</FORM>

Kod obsługujący formularz jest następujący:


echo 'Witaj ' . $surnanie . ' miło jest robić z tobą interesy';

Załóżmy teraz, że formularz otworzy pan O'Donnell i wpisze swoje nazwisko. Bez
oznaczania apostrofów — wystąpi paskudny błąd składni. Z włączoną opcją oznaczania
— trzeba użyć funkcji stripslashes ( ) na zmiennej Ssurname przed jej wyświetle-
niem. Skrypt będzie działał bez błędów.

magic_quotes_runtime = Off
Opcja ta powoduje oznaczanie apostrofów w tekstach przychodzących z bazy danych.
Pamiętaj, że SQL oznacza apostrofy i cudzysłowy w czasie zapamiętywania tekstów
i nie usuwa ich podczas ich odczytywania. Jeżeli opcja ta jest ustawiona na off, musisz
skorzystać z funkcji stripslashes ( ) przy wypisywaniu danych pochodzących z bazy
danych SQL. Jeżeli opcja magic_quotes_sybase jest ustawiona na On, ta musi być
ustawiona na Of f.
Rozdział 32. » Konfiguracja i dostrajanie________________________________549

magic_quotes_sybase = Off
Opcja ta powoduje oznaczanie apostrofów w ciągach pochodzących z bazy danych za
pomocą ukośników stosowanych przez Sybase (zamiast lewych ukośników). Jeżeli
opcja magic_quotes_runtime jest ustawiona na On, ta musi być ustawiona na Off.

auto-prepend-file = [scieżka_do_pliku]
Jeżeli podamy tutaj ścieżkę, PHP będzie automatycznie dołączał ją na początku każdego
pliku PHP. Istnieją ograniczenia w dołączaniu plików. Pamiętaj o tym, że nie możesz
dwa razy wysyłać nagłówka HTTP.

auto-append-file = [scieżka_do_pliku]
Jeżeli podamy tutaj ścieżkę, PHP będzie automatycznie dołączał ją na końcu każdego
pliku PHP, chyba że wcześniej użyjesz funkcji exit ().

include_path = [KATALOG]
Jeżeli ustawisz wartość tego parametru, będziesz mógł dołączać jedynie pliki znajdujące
się w tych katalogach. Katalog plików dołączanych znajduje się zwykle w drzewie ka-
talogów WWW — to wymóg pracy w trybie bezpiecznym. Ustawienie tej opcji na . /
spowoduje dołączenie plików jedynie z katalogu, w którym znajduje się skrypt.

doc_root = [KATALOG]
Jeżeli używasz Apache, na pewno ustawiłeś katalog dokumentów serwera w pliku
httpd.conf. Ustawienie tej opcji jest potrzebne w przypadku pracy w trybie bezpiecz-
nym, bądź jeżeli chcesz włączyć obsługę PHP jedynie we fragmencie witryny (na przy-
kład w jednym podkatalogu drzewa WWW).

upload_tmp_dir = [KATALOG]
Nie zmieniaj tego wiersza, zanim nie poznasz wszystkich konsekwencji ładowania pli-
ków na serwer przez HTTP!

session.save-handler = files
Opcja opisana w rozdziale „Sesje".

ignore_user_abort = [On/Off]
Opcja pozwala na kontrolowanie zachowania PHP, gdy użytkownik kliknie przycisk
Stop w przeglądarce. Domyślną wartością parametru jest On, co oznacza, że skrypt bę-
dzie działał nadal, do zakończenia lub przekroczenia dopuszczalnego czasu. Jeżeli
ustawisz opcję na Off, skrypt zostanie przerwany. Opcja ta działa jedynie w przypadku
pracy PHP jako modułu serwera.
550___________________________________Część III » Techniki zaawansowane

Poprawianie wydajności PHP


Istnieją dwie szkoły myślenia o wydajności witryny WWW. Pierwsza mówi, że wydaj-
ność skryptów PHP, teoretyczna wydajność serwera WWW, częstotliwość taktowania
procesora, ilość pamięci serwera i prawie wszystkie inne parametry są nieistotne, a liczy
się jedynie przepustowość łącza. Druga mówi, że nie ma nic lepszego, niż urwanie kilku
mikrosekund czasu wykonania skryptu. Ten fragment książki jest raczej bezużyteczny
uznającym jedynie pierwszy z przedstawionych punktów widzenia.

Zanim zaczniemy poprawiać wydajność, musimy umieć ją mierzyć. Zend pracuje nad
komercyjnym narzędziem do tego celu, ale w czasie pisania książki nie było jeszcze do-
stępne. Dlatego skorzystamy ze stosowanego od dawna sposobu mierzenia wydajności:
liczenia mikrosekund. Napiszmy małą funkcję:
function exec_time()
(
$mtime = explode( " ", microtime());
5msec = (double)$mtime[0] ;
$sec = (double)$mtime[1];
return Ssec + 5msec;
)

Wstaw ją lub dołącz do skryptu, który chcesz zoptymalizować. Podzielmy teraz treść
skryptu na części i dodajmy w strategicznych miejscach wywołania do funkcji exec_
time():
<?php
$start_db_call = exec_time();
Sresult = mysql_db_query("test", "SELECT * from user WHERE ID=1");
while ($testrow = mysql_fetch_array($result))
echo $testrow[0];
$end_db_call = exec__time () ;
Sruntime = $end_db_call - start_db_call;
echo "Odwołanie do bazy danych i wypisanie wyniku zajęło
Sruntime sekund";
?>

Wywołaj zmodyfikowaną stronę. Skrypt teraz będzie sam podawał czas wykonania.

Korzystając z funkcji microtime o , możesz zmierzyć czas pomiędzy


pierwszym a ostatnim wierszem „pomiarowym". Nie będziemy mogli
stwierdzić, ile czasu zajęło serwerowi utworzenie procesu potomnego
lub uruchomienie CGI, jak duże opóźnienie jest generowane przez
ruch w sieci itp. Aby zmierzyć te wszystkie parametry, musisz skorzy-
stać z narzędzi diagnostycznych o wiele bardziej skomplikowanych niż
PHP. Możesz zacząć od dostarczanego z Apache na Uniksa programu
„ab" (Apache benchmark).

Teraz wiesz, jak długo trwa wykonywanie poszczególnych części skryptu, więc możesz
rozpocząć poprawianie wydajności. Oczywiście trzeba pamiętać, że wykonywanie
funkcji używające plików lub odwołujące się do innych procesów trwa dłużej niż
skryptów zawartych tylko w jednym pliku. Odwołania do bazy danych, funkcje inclu-
de ( ) i require ( ) , obiekty z dziedziczeniem, analiza XML zajmują więcej czasu niż
Rozdział 32. » Konfiguracja i dostrajanie________________________________551

proste obliczenia lub wypisywanie ciągów. Jednak te bardziej skomplikowane funkcje


są najlepszą częścią PHP; byłoby niemądrze usuwać je ze skryptów dla uzyskania kilka
mikrosekund.

W zamian powinieneś poszukać i usunąć błędy w programie, powodujące niepotrzebne


opóźnienia. Jeżeli gołym okiem możesz stwierdzić, że skrypt działa powoli, szczególnie
na własnym komputerze, należy się tym zająć. Naszpikuj kod wywołaniami microti-
me ( ) i sprawdź, gdzie tkwi problem. Zwróć szczególną uwagę na znane pułapki: użycie
w pętli regex zamiast explode ( ) , niepotrzebne programowanie obiektowe, nieprawi-
dłowe zapytania SQL, wielokrotne dołączanie tego samego pliku oraz długie pętle.

Mimo że byłoby najlepiej usunąć wszystkie błędy z kodu, można ustawić w Apache lub
PHP maksymalny czas wykonywania skryptu lub maksymalną ilość używanej pamięci.
Żadna strona nie potrzebuje przecież na utworzenie 300 sekund. Inną opcją konfiguracji
pomocną przy bardzo powolnych skryptach jest ignore_user_abort w pliku php. ini.

Optymalizator Zend
Do niedawna fanatycy prędkości mieli niewielki wybór, oprócz własnych
funkcji mierzących czas bazujących na funkcji microtime(). W tej chwili
mamy nowe bezpłatne narzędzie: optymalizator Zend. Program ten wykonuje
kilka przebiegów przez skrypt PHP, zastępując wolniejsze konstrukcje szyb-
szymi, o takim samym działaniu. Zend przewiduje od 40 do 100% poprawy
wydajności w stosunku do zwykłego kodu PHP (im bardziej skomplikowana
logika skryptu, tym więcej ulepszeń).
Produkt wzbudza kontrowersje. Niektórzy nie korzystają z optymalizatora, ani
innych produktów Zend, ponieważ nie są „politycznie poprawne". Bardziej
pragmatyczni użytkownicy nie widzą żadnego problemu w tym, że bogate fir-
my muszą zapłacić za komercyjne dodatki. Każdy użytkownik PHP może wy-
brać odpowiedni dla siebie sposób licencjonowania.
Możesz ściągnąć optymalizator Zend z witryny http://www.zend.com/php/
optimizer.php. W chwili wydawania książki optymalizator i debugger nie były
ze sobą zgodne; optymalizator wymagał kompilacji bez trybu uruchamiania.
W systemie Unix można to osiągnąć, kompilując PHP bez opcji —with-
debugger, w Windows potrzebna jest specjalna wersja, bez informacji dla
debuggera, dostępna na witrynie Zend. Ponieważ niewielu użytkowników
używa tego debuggera, to niewielki problem.
Innym produktem firmy Zend, który dodatnio wpływa na wydajność, jest bu-
for PHP. Kompiluje on i przechowuje w pamięci wywoływane strony, co
zmniejsza liczbę operacji dyskowych i kompilacji skryptów, przyspieszając
działanie witryny. W czasie wydawania książki nie były jeszcze ustalone
szczegóły licencjonowania, ale przewiduje się go dla dużych i mocno obcią-
żonych witryn. Z czasem, gdy PHP będzie bardziej popularny na dużych wi-
trynach, możemy spodziewać się napływu narzędzi optymalizujących z Zend
i innych firm.
552___________________________________Część III » Techniki zaawansowane

Ostatnie wersje PHP posiadają również zoptymalizowany plik php.ini, w którym


zmienne ustawione są tak, aby osiągnąć największą wydajność. Jeżeli zdecydujesz się
skorzystać z tego pliku, musisz poświęcić nieco czasu na zrozumienie efektów zmian
— innymi słowy, nie wrzucaj go po prostu na serwer, zastanawiając się potem, gdzie
podziały się zmienne HTTP_*_VARS.

Podsumowanie
Zaletą i wadą konfiguracji PHP jest to samo: mamy wiele opcji i wiele sposobów na ich
ustawienie. Moduł Apache w systemie Unix ma szczególnie dużo opcji, ale zespół pro-
jektantów PHP długo pracował nad jego elastycznością.

Istnieją trzy główne sposoby konfigurowania PHP. Pierwszym, dostępnym dla tych, któ-
rzy sami budują PHP, jest użycie opcji kompilacji. Wiele z tych dyrektyw to niezbędne
warunki wstępne (ustawiające warunki domyślne), które należy później potwierdzić lub
odwołać. Drugim sposobem jest skorzystanie z plików konfiguracji Apache (httpd.conf
i .htaccess), które są dostępne jedynie użytkownikom serwera Apache. Trzecią metodą
jest użycie p\ikuphp.ini, który jest zawarty w każdej instalacji PHP.
W PHP 4 wprowadzono wiele zmian w pliku php.ini. Jedną z najważniejszych jest
zdolność do indywidualnego wyłączania funkcji. Niektóre opcje w tym pliku (PHP 3
i PHP 2) stały się przestarzałe, np.: gpc_order (przesłonięta przez variables_order).
Plikphp.ini nie jest już absolutnie niezbędny w systemie Windows — PHP 4 przyjmuje
teraz wartości domyślne, jeżeli nie ma go na ścieżce Windows.

PHP 4 jest wyraźnie szybszy od PHP 3. Najczęściej stosowanym narzędziem do pomia-


ru wydajności jest wypisywanie wyników funkcji microtime ( ) , wywoływanej w kil-
ku miejscach skryptu. Za pomocą tej prostej metody możesz zlokalizować i ulepszyć te
części skryptu, które zabierają za dużo czasu. Nie udaje się jednak w taki sposób zmie-
rzyć niczego, co wykracza poza PHP, a może wpływać na wydajność. W takich wypad-
kach należy użyć narzędzi zewnętrznych, takich jak ab (Apache benchmark).

Zend.com stworzył narzędzia pozwalające na poprawienie wydajności witryn posiada-


jących wysoki stopień skomplikowania i obciążenia.
Dodatri
Dodatek A
PHP dla programistów C
Założyliśmy, tworząc ten dodatek, że masz więcej doświadczenia w programowaniu
w C (lub C++) niż w PHP i chcesz szybko się go nauczyć. Na początek przyjrzyjmy się
PHP z perspektywy C, następnie skupimy się na różnicach i podobieństwach. Na koniec
zajmiemy się tymi częściami książki, z których najwięcej skorzystasz.

Najprościej jest myśleć o PHP jak o interpretowanym C, który możesz wbudowywać


w strony HTML. PHP jest podobny C, wyjąwszy brak typów zmiennych, wszystkie bi-
blioteki dla WWW i to. co związane jest z serwerem WWW. Składnia wyrażeń i funkcji
powinna być ci znana, poza tym że zmienne są zawsze poprzedzone znakiem $, a funk-
cje nie wymagaj ą prototypów.

Podobieństwa
W tej części wymienimy kilka cech (na pewno nie są to wszystkie), które powodują, że
PHP jest bardzo podobny do C.

Składnia
Ogólnie mówiąc, składnia PHP jest identyczna ze składnią C: kod jest niezależny od
odstępów, wyrażenia zakończone są średnikami, wywołania funkcji mają taką samą
strukturę ( f u n k c j a (wyrażeniel, wyrażenie2)), nawiasy klamrowe ({ i }) łączą
wyrażenia w bloki. PHP ma komentarze zarówno w stylu C, C++ (/* */ oraz //), jak
i w stylu Perl i skryptów powłoki (#).

Operatory
Operatory przypisania (=, +=, *= itd.), operatory logiczne ( & & , | |, !), operatory porów-
nania (<, >, <=, >=, ==, ! =) oraz podstawowe operatory arytmetyczne (+, -, *, /, %) za-
chowują się tak jak w C.
556___________________________________________________Dodatki

Struktury sterujące
Podstawowe struktury sterujące (if, switch, while, for) działają identycznie jak
w C, łącznie z konstrukcjami break i continue.

Wiele nazw funkcji


Jeśli przejrzysz dokumentację, zobaczysz, że wiele nazw funkcji wygląda identycznie
jak w C. Można bezpiecznie założyć, że wykonują to samo zadanie, jednak czasami
mogą mieć nieco inną postać argumentów lub sposób zwracania wartości. Na przykład
większość funkcji modyfikujących ciągi zwraca nowy ciąg jako wartość, zamiast mody-
fikować ciąg przekazany jako argument.

Różnice
Mimo że PHP odziedziczył wiele z C, ma również cechy innych przodków (Perl
i skryptów powłoki) oraz kilka unikalnych własności, które nie pochodzą z C.

Znaki $
Wszystkie zmienne są poprzedzone znakiem $. Nie trzeba deklarować zmiennych przed
ich użyciem; nie mają one zdeterminowanego typu. Typ ostatnio przypisanej wartości
jest bieżącym typem zmiennej. Kod w PHP odpowiadający następującemu w C:
double my_number;
ray_numtier - 3 . 1 4 1 5 9 ;

upraszcza się do:


my_number = 3 . 1 4 1 5 9 ;

Typy
W PHP istnieją tylko dwa typy numeryczne: integer (odpowiadający typowi long
w C) i double (odpowiadający double w C).

Ciągi mają dowolną długość. Nie ma osobnego typu znakowego (funkcje wymagające
znaku w C jako argumentu, w PHP zwykle oczekują ciągu z jednym znakiem, na przykład
ord ( ) ) . Do PHP 4 wprowadzony został pełnowartościowy typ boolean (wartości TRUE
lub FALSE).

Konwersja typów
Typy nie są sprawdzane w czasie kompilacji, a błędy konwersji typów zwykle nie wy-
stępują w czasie wykonywania. W zamian zmienne i ich wartości są automatycznie
Dodatek A « PHP dla programistów C_________________________________557

konwertowane do wymaganego typu. C nieco podobnie „promuje" w razie potrzeby ar-


gumenty numeryczne, ale konwersja w PHP obejmuje również inne typy (więcej szcze-
gółów na temat konwersji typów w rozdziale 6.).

Tablice
Tablice są syntaktycznie prawie identyczne do tablic w C, ale ich budowa jest odmienna.
Są one tablicami asocjacyjnymi; „indeks" może być zarówno liczbą, jak i ciągiem. Nie
muszą być wstępnie deklarowane i przydzielane.

Brak struktur
W PHP nie ma typu struct, częściowo dlatego, że tablice oraz typ obiektowy dosko-
nale go zastępuj ą (elementy tablicy PHP nie muszą być tego samego typu).

Obiekty
PHP ma jedynie podstawową składnię obiektową, która umożliwia definiowanie klas
z ich składowymi danymi i funkcjami. Nie ma destruktorów (zajrzyj do części „Zarządza-
nie pamięcią" poniżej).

Brak wskaźników
W PHP nie ma wskaźników, chociaż zmienne pozbawione typu odgrywają podobną
rolę. PHP obsługuje referencje do zmiennych.

Brak prototypów
Funkcje nie muszą być deklarowane, zanim zostaną zdefiniowane. W PHP 3 należy je-
dynie definiować funkcje przed ich użyciem (wywołanie jednej funkcji z ciała innej nie
jest traktowane jako użycie przed definicją). Wystarczy, że funkcja zostanie zdefinio-
wana przed jej wywołaniem. W PHP 4 funkcje nie muszą nawet poprzedzać wywołań,
ponieważ są prekompilowane przed użyciem.

Zarządzanie pamięcią
Maszyna PHP posiada efektywny mechanizm zbierania śmieci (zliczający odwołania);
małe skrypty nie wymagaj ą żadnego zwalniania pamięci. Możesz bez obaw przydzielać
pamięć nowym strukturom — jak na przykład ciągi lub obiekty — ponieważ zostaną
usunięte, gdy tylko skrypt się zakończy. Jeżeli musisz zwolnić pamięć w czasie wyko-
nania skryptu, wywołaj unset ( ) na zmiennej. Zasoby zewnętrzne (jak na przykład
wyniki pochodzące z bazy danych) mogą być jawnie zwalnianie w skryptach; jest to ko-
rzystne jedynie w przypadkach, gdy skrypt używa zbyt dużo pamięci.
558 __________________________________________________Dodatki

Kompilacja i łączenie
W PHP nie ma osobnego etapu kompilacji skryptów — można od razu zobaczyć wynik
po edycji. Błędy i ostrzeżenia wypisywane są w przeglądarce. Dynamiczne ładowanie
bibliotek zwykle nie następuje (choć istnieje taka możliwość) — o tym, jakie grupy
funkcji będą dostępne w skryptach, decydujesz w czasie konfigurowania PHP.

Tolerancyjność
Można powiedzieć, że PHP wybacza więcej niż C (szczególnie w stosowaniu typów).
Dzięki temu w skryptach pojawia się nowa kategoria błędów. Niespodziewane wyniki
występują o wiele częściej niż błędy wykonania. Przy domyślnym ustawieniu poziomu
raportowania błędów PHP nie ostrzega o użyciu niezainicjowanej zmiennej (ale zwraca
rozsądne wyniki zamiast śmieci). Lepiej chyba zobaczyć ostrzeżenie, więc warto usta-
wić wyższy poziom raportowania błędów, używając wyrażenia error-reporting
(E_ALL) (lub error-reporting ( 1 5 ) ) na początku skryptu, albo też ustawiając po-
ziom raportowania błędów na stałe (modyfikacja p\ikuphp.ini).

Przewodnik po książce
Nie zakładaliśmy, pisząc tę książkę, że użytkownik zapoznał się wcześniej z C. Ponie-
waż PHP korzysta w wielu miejscach z C, niektóre rozdziały mogą tyczyć znanej czy-
telnikowi problematyki, (np. część I, która omawia wprowadzenie do języka).

W tabeli A. l określiliśmy stopień trudności kolejnych rozdziałów części pierwszej (dla


programisty posługującego się C). Części III i IV to problematyka specyficzna dla PHP;
są to raczej nowe informacje. Jeżeli masz doświadczenie w programowaniu baz danych
SQL, zauważysz, że część II prezentuje znane ci zagadnienia.

Premia — popatrz tylko na kod!


Programiści posługujący się C mogą bez przeszkód korzystać z otwartej natury PHP.
Mimo że kombinacja tej książki i podręcznika dostępnego na stronie WWW jest kłopo-
tliwa, jeżeli możesz zajrzeć do źródła PHP i zobaczyć, jak to wszystko działa, z pewno-
ścią uzyskasz więcej informacji. Oczywiście będziesz potrzebował do tego wiedzy
o technologiach analizy leksykalnej, aby zrozumieć kod samego analizatora, ale wiele
funkcji PHP to proste nadbudowy ich odpowiedników w C. Te, które nie mają odpo-
wiedników, napisane są w pięknym prostym C.
Dodatek A » PHP dla programistów C_________________________________559

Tabela A.l.
Przewodnik po części I dla programistów C

Rozdział Tytuł rozdziału Werdykt Uwagi


1. Dlaczego PHP? Nowość Rozdział ten pomoże ci przekonać szefa, aby wybrał PHP
2. Skrypty Nowość Ważny, jeżeli wcześniej nie miałeś do czynienia z językami
wykonywane na skryptowymi dla WWW
serwerze WWW
3. Rozpoczynamy Nowość Instalacja itp.
pracę z PHP
4. Dodajemy PHP do Nowość, „Witaj świecie PHP"
HTML łatwy
5. Składnia, zmienne Przeważnie Nic nowego aż do części o zmiennych (które są naprawdę
i wyjście znany inne w PHP)
6. Typy PHP Raczej PHP automatycznie konwertuje typy. Przeczytaj również
znany następny materiał o tablicach i obiektach
7. Sterowanie Znany Wszystkie konstrukcje sterujące PHP (if, while, switch, for)
działają identycznie jak w C
8. Użycie Przeważnie Różnice obejmują zasady zasięgu, obsługę argumentów oraz
i definiowanie znany końcową część o zmiennych nazwach funkcji
funkcji
9. Ciągi i operacje na Przeważnie Niektóre funkcje o tych samych nazwach (sprintf) zwracają
ciągach znany nowy ciąg, zamiast działać na przekazanym argumencie.
Wartości zmiennych są automatycznie wklejane do ciągów
w cudzysłowach. Ciekawy materiał na końcu rozdziału
o funkcjach dla wyrażeń regularnych i URL
10. Matematyka Znany Podobnie nazywające się funkcje działają identycznie. Nowy
fragment o arytmetyce o dowolnej dokładności (funkcje „bc")
11. Tablice i funkcje Nowość Tablice PHP są syntaktycznie identyczne z tablicami w C,
operujące na ale działaj ą inaczej
tablicach
12. Przekazywanie Nowość Specyficzny dla skryptów WWW
danych między
stronami
13. Funkcje Przeważnie Nieco nowości, na przykład czytanie z odległej strony
systemowe znany WWW (poprzez HTTP) identycznie jak z pliku
i obsługi plików
14. Styl PHP Przeważnie Takie same zasady stylistyczne jak w C — w końcowej
znany części specyfika projektowania witryn WWW.
15. Podstawowe Nowość Komunikaty o błędach i pułapki mają niewiele wspólnego z C
pułapki PHP
560________________ Dodatki
Dodatek B
PHP dla programistów ASP
Bezpłatne oprogramowanie jest coraz częściej wykorzystywane w zastosowaniach ko-
mercyjnych i projektach własnych. Z tego powodu niektórzy programiści ASP mogą
dojść do wniosku, że warto dołączyć PHP do arsenału narzędzi. Dodatek ten został po-
myślany jako pomoc w szybkim przygotowaniu do pracy.

Mimo że teoretycznie ASP jest niezależne od platformy, serwera WWW i języka to


w praktyce jedynie niewielki ułamek programistów używa czegoś innego niż Microsoft
NT, US i VBScript. Zakładamy taką domyślną kombinację technologii. Programiści
ASP korzystający z JScript skorzystaj ą więcej z rozdziału 27. „PHP i JavaScript".

Podobne koncepcje
Porównując z dalszej perspektywy, PHP i ASP są bardzo podobne (uprzedzając pytanie
przypominamy, że PHP powstał przed ASP, więc nie można mówić o kopiowaniu Micro-
softu). Oba standardy dają się wbudować w HTML, nie opartymi o znaczniki preproce-
sorami skryptów wykonywanych na serwerze. W większości przypadków można je
stosować zamiennie.

Możemy stwierdzić, że wszystko, co potrafi ASP, PHP potrafi zrobić lepiej (może poza
obsługą sesji i zmiennych aplikacji). W porównaniu z ASP PHP w większości przypad-
ków jest zgodne z hasłem NASA: „Lepiej, szybciej, taniej".

Główne różnice
Pomiędzy tymi dwoma językami można znaleźć różnice w składni, modelu obiektowym
i filozofii.
562___________________________________________________Dodatki

ASP jest oparty na Basicu, zaś PHP na języku C


Składnia języków jest różna, ponieważ PHP odziedziczył ją po języku C, natomiast ję-
zyk VBScript jest podzbiorem Visual Basica. Na przykład poniższa konstrukcja if
w języku VBScript:
<% If varPreference = "kawa" Then
Response.Write "śmietanka"
elseif varPreference - "herbata" Then
Response.Write "cytryna"
End If %>

odpowiada następującej w PHP:


<?php
if ($Preference == "kawa") fc
echo "śmietanka";
elseif (^Preference == "herbata"}
{
print ("cytryna"};
)
?>

Jak widać, składnia C używa większej liczby symboli, wcięć i nawiasów do grupowa-
nia. Składnia PHP jest jeszcze elastyczniejsza od C. W naszym przykładzie nawiasy
klamrowe nie są obowiązkowe, ponieważ korzystamy tylko z jednego wyrażenia po
warunku. Dodatkowo wstawiliśmy gałąź elseif tylko dla celów demonstracyjnych.
W PHP zarówno print, jak i echo (które sąw większości zastosowań wymienne) nie
wymagają nawiasów wokół drukowanych ciągów, więc raz ich użyliśmy, a raz nie.

Język C jest prawdopodobnie bardziej „matematyczny", natomiast w Basic stosuje się


składnię bliższą językowi naturalnemu. Różnica ta jest szczególnie widoczna w pętlach,
tak jak w przykładzie:
<% For Each Item in Request.QueryString
For iCount = 1 to Request.QueryString(Item}.Count
Response.Write Item & " = " &
Request.QueryString(Item)(iCount) & "<BR>"
Next
Next %>

W PHP możesz zastosować pętlę while lub for:


<?php
%i = 0;
while ($i <= count(Sitem))
{
print! "$Item[Si] = Si<BR>");
$i + + ;
}
?>
lub
<?php
for ($i=0; $i <= count(Sitem); $i++)
(
print("$Item[Si] = $i<BR>");
} ?>
Dodatek B » PHP dla programistów ASP________________________________563

Jeżeli chcesz poznać składnię C ponad poziom przedstawiany w tabelach na końcu tego
dodatku, skorzystaj z jednego z wielu bezpłatnych samouczków języka C dostępnych
w Internecie. Możesz skorzystać ze świetnej (krótkiej) książki Patricka Henry'ego Win-
stona „On to C" (Addison-Wesley, Nowy Jork, 1994).

Nie zapomnij o średnikach.

ASP używa do wszystkiego obiektów, a PHP nie


ASP jest zbudowany na bazie modelu obiektowego. PHP korzysta z tradycyjnego i jest
dzięki temu mniej wybredny.

Niektóre ważne konsekwencje.


* Nie musisz odwoływać się do zmiennych z formularza poprzez Request. Form
lub Request .Querystring. Zmienne są automatycznie przenoszone z for-
mularza do obsługującego go skryptu i są od razu dostępne. Więcej szczegółów
w rozdziale 12.
* Nie musisz tworzyć obiektu połączenia dla połączenia z bazą danych. Po pro-
stu skompiluj PHP z obsługą potrzebnej bazy danych i używaj specyficznych
dla bazy lub protokołu funkcji.
4 Nie musisz tworzyć obiektów, których być może nawet nie użyjesz.
4 Nie musisz za każdym razem pisać Response . W r i t e , aby wypisać jakiś tekst.
* Nigdy nie musisz myśleć o komponentach ActiveX w PHP.

Mimo że PHP posiada obsługę obiektów i klas, nie jest całkowicie obiektowy i prawdopo-
dobnie nigdy nie będzie. Każdy może wybrać sobie notację, która będzie najlepiej spełniała
potrzeby. Kod obiektowy najlepiej sprawdza się w dużych projektach wielu programistów,
a do tej pory większość witryn WWW tworzonych w PHP jest relatywnie niewielka i nie ma
tak naprawdę potrzeby stosowania obiektów. Jeżeli firmy częściej będą stosowały PHP,
programowanie obiektowe stanie się na pewno bardziej popularne.

Pamiętaj, że PHP używa notacji obiektowej w stylu C++ ze strzałką


(jak $this->titie), w przeciwieństwie do używanej w języku VBScript
notacji z kropką. Kropka jest w PHP operatorem złączenia.

ASP jest produktem firmowym


Trudno wyliczyć wszystkie zalety, jakie niesie ze sobą oprogramowanie typu open so-
urce. Pomijając nawet aspekty ideologiczne, jest wiele sposobów, dzięki którym PHP
pomoże wykonać zadanie szybciej i bardziej efektywnie.
564___________________________________________________Dodatki

Natychmiastową konsekwencją otwartego charakteru PHP jest możliwość pracy z wie-


loma serwerami WWW i podłączenia do szerokiej gamy serwerów usługowych. Im-
plementacja ASP Microsoftu działa jedynie w Windows i IIS i używa wielu technologii,
których nazwy zmieniają się z nieprawdopodobną szybkością (szybki konkurs: jasno
i zwięźle wyjaśnij związki pomiędzy OLE DB, ADO, ODBC, UDA, RDS i ADC), do
połączenia się z niewielką liczbą baz danych. PHP obsługuje większą liczbą systemów
operacyjnych, serwerów WWW, baz danych i innych standardów niż jakikolwiek inny
porównywalny język skryptów WWW (może z wyłączeniem Perl).

PHP został zaprojektowany jako „klej" internetowy. Bardzo łatwo jest łączyć różne tech-
nologie za pomocą PHP — a w czasach szybkich zmian technologii sieciowych jest to
bardzo pomocne. Możesz więc swobodnie korzystać z wszystkich zalet serwerów usłu-
gowych, wiedząc, że masz możliwość dokonania zmian wtedy, gdy zajdzie taka potrzeba.

PHP jest wyjątkowy, został od podstaw zaprojektowany jako język skryptów serwera
WWW. Nie jest to środowisko obciążone powstałymi już aplikacjami. Projektanci PHP
podejmowali decyzje w oparciu o prawdziwe potrzeby programistów WWW i niezależ-
nie od tego, że języki programowania użyte do zbudowania konkretnej aplikacji reali-
zowały zadania w jakiś określony sposób.

Ponieważ źródła PHP mają otwarty charakter, nowe funkcje są szybko dodawane (zbyt
szybko dla zmęczonych autorów książek technicznych) i wprowadzane przez inne fir-
my, bez ograniczeń. Na przykład w PHP 4 pojawiła się obsługa DOM XML i Gnu Get-
text, COM i CORBA, CyberCash i Shockwave Flash. Cykl produkcyjny Microsoftu jest
wolniejszy. Firma wspiera jedynie własne produkty, poza tymi, które są powszechnie
stosowane (na przykład Oracle). Nie ma nic złego w takiej filozofii, chociaż ogranicza
ona twórców, którzy chcą mieć dostęp do szerokiej gamy najlepszych produktów.

Jeżeli dobrze czujesz się w C, możesz „podnieść maskę" PHP i właściwie dowolnie go
zmodyfikować. Zaawansowani użytkownicy PHP rutynowo wyłączają funkcje, dodają
własne rozszerzenia, próbują stworzyć PHP dla innego serwera WWW lub włączyć nową
bibliotekę. Przeglądają oni również kod PHP w poszukiwaniu nieudokumentowanych
funkcji. Programiści C mogą dodawać własne funkcje do PHP, biorąc udział w projekcie.

Ostatnim, ale nie najmniej ważnym zagadnieniem jest kwestia wsparcia technicznego.
PHP oferuje na swojej witrynie podręcznik, który zawiera wszystkie aspekty PHP
— podaje nawet wersję dla komputerów PalmPilot. Od dawna istnieje lista dyskusyjna,
szeroko znana z aktywności i wzajemnego przyjaznego nastawienia subskrybentów.
Często się zdarza, że odpowiedzi pochodzą od twórców fragmentu, którego dotyczyło
pytanie. Istnieje również wiele dobrze zorganizowanych witryn WWW dotyczących
PHP, zawierających artykuły, tablice ogłoszeniowe i przykłady kodu. Pojawiają się
również firmy oferujące wsparcie dla komercyjnych projektów prowadzonych w PHP.

Przy okazji warto wspomnieć, że PHP nic nie kosztuje. Oprócz wsparcia komercyjnego
i niektórych serwerów (na przykład Oracle i SQL Server), wszystkie funkcje PHP są
dostępne bez opłat. Oczywiście skomercjalizowany rynek dodatków do ASP i narzędzi
dla programistów kwitnie.
Dodatek B » PHP dla programistów ASP________________________________565

Konwersja ASP na PHP


Jeżeli chcesz skonwertować dużo skryptów VBScript na PHP, pod adresem tym możesz
znaleźć taj na broń:
http://asp2php. naken. cc/

Autor tego programu Michael Kohn, który ma własne zdanie na temat ASP i nie wsty-
dzi się go wyrazić na swojej witrynie (jego motto brzmi: „Opór nie jest bezskuteczny").
Żaden program nie będzie jednak w stanie skonwertować kodu do ostatniego apostrofu
— będziesz musiał ręcznie zmieniać skrypty. Ale program ten pomaga w niektórych
powtarzających się czynnościach, które i tak trzeba wykonać.

Witryna ta zawiera łącza do dwóch przydatnych programów: pierwszy


konwertuje bazę SQL Server na Oracle, drugi konwertuje Accessa na
MySQL.

Przydatne tabele konwersji


Przygotowaliśmy szczegółowy zestaw różnic pomiędzy ASP i PHP w postaci tabel. Z po-
wodu różnic pomiędzy językami nie zawsze istniała możliwość jednoznacznej korelacji.

Tabela B.l.
Operatory ASP i PHP

Operatory ASP Operatory PHP


+ +
- -
* *

/ lub \ /
&
.=
Eqv Brak odpowiednika, używaj operatorów bitowych
" Pow()
Imp Brak odpowiednika, używaj operatorów bitowych
Is (referencja obiektu) Niepotrzebny w PHP
Mód %
And And, &&
Not i
566____________________________________________________Dodatki

Tabela B.l.
Operatory ASP i PHP (ciąg dalszy)

Operatory ASP Operatory PHP


Or or, | |

Xor xor

= ==, ===

> >

< <

Tabela B.2.
Wyrażenia ASP i funkcje PHP

Wyrażenia ASP Odpowiednik w PHP


Cali Nazwa Procedury NazwaFunkcji ( ) ;
Dim NazwaZmiennej Brak odpowiednika
Do W h i l e / U n t i l [warunek] do
[ wyrażenia] {
Loop [ funkcje] ;
}
lub
Do [wyrażenia] while ( warunek) ;
Loop W h i l e / U n t i l [warunek]
Erase NazwaTablicy unset ( [ t a b l i c a ] )
Exit [wyrażenie] [warunek] break;
For VarName = [liczba] To/Downto for (SnazwaZmiennej =
t warunek] [liczba] ; [warunek] ;
[wyrażenia] [++$ Na zwą Zmienne j /$ Na zwą Zmienne j ++/
Next $ NazwaZmiennej /S Na zwą Zmienne j--] )
{
[funkcje] ;
}
For Each NazwaZmiennej In NazwaGrupy for ( [ warunki] :
[wyrażenia] [ funkcje] ;
Next endfor;
lub
while (list ($zmienna) = each($grupa)
[funkcje] ;
P u b l i c / P r i v a t e Function f u n c t i o n NazwaFunkcji ( [argumenty] )
NazwaFunkcji ( [argumenty] ) {
[wyrażenia] [funkcje] ;
End Function }
Dodatek B » PHP dla programistów ASP________________________________567

Tabela B.2.
Wyrażenia ASP i funkcje PHP (ciąg dalszy)

Wyrażenia ASP Odpowiednik w PHP


If [warunek] Then if ( [waruneJc] )
[ wyrażenia] {
E l s e / E l s e If [funkcje] ;
[wyrażenia] )
End If e l s e / e l s e i f ( [ warunek] )
{
[funkcje] ;
}
lub
if ( [warunek] ) :
[funkcje] ;
e l s e / e l s e i f ( [ waruneJc] ) :
[funkcje] ;
endif ;
On Error Resume Next f unction ( ) or f u n c t i o n ! ) ;

lub
continue;
Option Explicit Brak odpowiednika
Private WazwaZmiennej Brak odpowiednika
Public N a z w a Z m i e n n e j Brak odpowiednika
Randomize [liczba] m t srand( (double) m i c r o t i m e 0 * 1 0 0 0 0 0 0 )

lub
s r a n d l (double) microtime 0 * 1 0 0 0 0 0 0 )
ReDim WazwaZmiennej Brak odpowiednika
Rem [Komentarz] // [Komentarz]
lub lub
' [Komentarz] /* [Komentarz] */
lub
# [Komentarz]
Select Case [ wyrażenietestujące] switch ($ na zwą Zmiennej)
[Case [lista warunków] {
[wyrażenia] ] case [ wartość] : [f unkcje] ;
[Case [ l i s t a warunków] break;
[ wyrażenia-n] ] case [wartość] : [ f u n k c j e ] ;
End Select break;
)
Set NazwaZmiennej = [wyrażenie] $NazwaZmiennej = [wyrażenie]
Public/Private Sub function NazwaFunkcj i ( [argumenty] )
WazwaPodprogramu ( [argumenty] ) f
[ wyrażenie] [funkcje] ;
End Sub }
568___________________________________________________Dodatki

Tabela B.2.
Wyrażenia ASP i funkcje PHP (ciąg dalszy)

Wyrażenia ASP Odpowiednik w PHP


While [warunek] w h i l e ! [warunek])
[wyrażenie] {
Wend [ funkcje] ;
}

lub
while ( [ warunek] ) :
[funkcje] ;
endwhile;

Funkcje, metody i wyrażenia


W języku VBScript istnieją funkcje, metody i właściwości — są one konsekwencją
obiektowego modelu ASP. W PHP wiele ich odpowiedników nazywa się funkcjami.

Zamiast wywoływać metody z różnych obiektów, można posłużyć się specyficznymi


funkcjami do komunikacji z serwerami usługowymi. Na przykład funkcje Oracle
w PHP nie są takie same jak funkcje SQL Server. W praktyce różnica jest trywialna, na
przykład: Ora_Fetch zamiast mssql_fetch_row.

Wszystkie funkcje maj ą najprostszy z formatów:


NazwaFunkcj i([argumenty));

W PHP nie ma rozróżnienia pomiędzy podprogramami (które nie zwracają wartości)


a funkcjami (zwracającymi wartość). Składnia używana do definiowania funkcji jest na-
stępująca:
f u n c t i o n NazwaFunkcji([argumenty])
{
[funkcje];
}

Więcej informacji na ten temat znajdziesz w rozdziale 8.

Typy zmiennych
W PHP nie musisz jawnie definiować typów. Lepiej zrobić to w przypadku liczb
zmiennoprzecinkowych, ale w innych nie jest to konieczne, a PHP sam manipuluje ty-
pami. Więcej na ten temat w rozdziale 6.

State
Istnieje niewielki związek pomiędzy starymi używanymi w języku VBScript a stałymi PHP.
PHP posiada niewiele wbudowanych stałych, jak na przykład PHP_VERSION i PHP_OS.
Dodatek B » PHP dla programistów ASP________________________________569

Możesz również definiować stałe w programie, co przydaje się jedynie, gdy dołączasz
wiele plików i definiujesz wiele funkcji.

Klasy i obiekty
Szczegółowy opis klas i obiektów PHP znajduje się w rozdziale 30. W skrócie można
powiedzieć, że klasy w PHP są niczym więcej niż wygodną metodą opakowania często
używanych kombinacji zmiennych i funkcji. Jeżeli naprawdę jesteś fanem programo-
wania obiektowego (choć w tym przypadku Python będzie bardziej odpowiedni dla cie-
bie), upewnij się, że czas, który zaoszczędziłeś, jest wart spadku wydajności. Wielu
zaawansowanych użytkowników PHP (włączając w to twórcę języka Rasmusa Lerdor-
fa) nigdy nie używało obiektów.

Włączanie plików
Mechanizm włączania plików w PHP jest dynamiczny, w przeciwieństwie do ASP. Po-
zwala na łatwe dołączanie fragmentów PHP lub HTML z osobnych plików. Składnia
jest następująca:

ASP
< ! — ł ł i n c l u d e f i l e = "ścieżka" — >

PHP
include("ścieżka");

lub
include_once("ścieżka");

lub
require("ścieżka");

Wartości zmiennych z dołączanego pliku są dostępne w pliku głównym, i odwrotnie


(oczywiście zachowując odpowiednią kolejność).

Pliki mogą być włączane warunkowo, przy wykorzystaniu instrukcji warunkowych:


if ($jakiswarunek ==1)
l
include("plik_warunkowy");
}
else
(
include("plik_domyślny");
}

Więcej na temat dołączania plików w rozdziałach 8 i 14.

Pomiędzy ASP i PHP występuje jeszcze wiele różnic; ten rozdział porusza najważniej-
sze zagadnienia, które musisz poznać, aby szybko zacząć pracę z PHP.
570________________________ Dodatki
Dodatek C
PHP
dla programistów HTML
Dodatek ten zawiera porady dla projektantów HTML, którzy chcieliby zaprząc do pracy
na serwerze język o większych możliwościach. Chociaż z drugiej strony, jeśli znasz już
ASP, JavaScript lub inny prawdziwy język programowania, nie skorzystasz zbyt wiele
na lekturze tego dodatku.

Dobre nowiny
Jeżeli biegle znasz HTML, korzystanie z PHP nie będzie zbyt dużym problemem. Po-
nieważ PHP jest wbudowywany w HTML, rozszerzanie statycznych stron HTML za
pomocą języka programowania jest naturalnym postępem. Mamy więc wiele powodów,
aby sądzić, że możesz dosyć szybko nauczyć się PHP.

Znajomość HTML
Prawdopodobnie masz sporo praktyki przy uruchamianiu stron HTML. Wiele błędów
powstaje w części HTML skryptów w czasie przełączania pomiędzy trybami, więc umie-
jętność sprawnego czytania i modyfikacji HTML jest tutaj bardzo ważna. Dodatkowo
komunikaty o błędach niosą ze sobą o wiele więcej informacji niż te zawarte w HTML.

Wyniki twojej pracy będą na pewno wyglądały dużo lepiej niż naszej, ponieważ wiesz
o wiele więcej na temat projektowaniu układu strony. Pokaż więc całemu środowisku
PHP, że witryny nie muszą być brzydkie lub ubogie.
572_____________________________________________________Dodatki

PHP jest dobrym pierwszym językiem programowania


W przeciwieństwie do większości języków programowania, PHP pozwala od początku robić
użyteczne projekty, zamiast w nieskończoność grać w kółko i krzyżyk lub programować
abstrakcyjne problemy matematyczne. Przeglądarka i języki oparte na znacznikach są dosyć
prymitywne, ale umożliwiają uzyskanie uniwersalnych mechanizmów wejścia-wyjścia,
okienek i multimediów. PHP w pełni korzysta z zalet WWW, wprowadzając dodatkowo
swobodne podejście do typów, zmiennych i składni. Praca programistów, potrzebna do
spełnienia wymagań języka, może być teraz spożytkowana do rozwiązania problemu.

I szczerze mówiąc, PHP pozwala nauczyć się tylko tych części, które mogą się przydać.
Jeżeli nie będziesz pisał ogromnych funkcji matematycznych, możesz opuścić rozdział,
który je omawia. A jeżeli kiedyś będziesz potrzebował się tego nauczyć, możesz sko-
rzystać z gotowych funkcji.

Tworzenie witryn to wykorzystywanie prefabrykatów


Tworząc witrynę coraz częściej korzystamy z istniejących rozwiązań, modyfikując je do
własnych potrzeb, zamiast tworzyć kod od podstaw. Większość tych zmian dotyczy je-
dynie wyglądu strony, a nie funkcji, jakie realizuje. Naucz się sprytnie żonglować ist-
niejącymi skryptami, a zaoszczędzisz sporo czasu.

Złe nowiny
Zanim przejdziemy dalej, musimy cię lojalnie uprzedzić, że zanim staniesz się zaawan-
sowanym użytkownikiem PHP, po drodze będziesz musiał pokonać kilka przeszkód.

Jeżeli programowanie jest takie łatwe,


dlaczego jeszcze tego nie potrafisz
PHP jest prawdziwym językiem programowania (jak C, chociaż działa jedynie we współ-
pracy z serwerem WWW) i nie jest oparty na znacznikach, jak HTML czy ColdFusion.
Z tego powodu jest bardziej skomplikowany. Skompletowanie zestawu sztuczek, procedur
do rozwiązania określonych problemów i po prostu osiągnięcie biegłości przy programo-
waniu wymaga czasu i praktyki — nie wymyślono do tej pory żadnych skrótów.

Większość funkcji PHP będzie dla ciebie nowością. W przeciwieństwie do programi-


stów PHP, którzy wcześniej mieli doświadczenie w programowaniu w ASP, JavaScript
lub C, możesz się spodziewać miejsc nieznanych.

Jeżeli znasz nieco JavaScript lub w szkole miałeś zajęcia z wprowadzenie do C, na pewno
niektóre zagadnienia przypomną ci się w czasie nauki PHP. Zapewne istnieją kursy, na któ-
rych możesz wygodnie nauczyć się PHP, jeżeli oczywiście odpowiada ci taka forma nauki.
Dodatek C » PHP dla programistów HTML_______________________________573

Serwery usługowe komplikują problem


PHP jest szczególnie użyteczny, jeżeli korzystamy z serwerów usługowych, takich jak
serwery baz danych lub pocztowe, ponieważ wymagają nauczenia się ich własnej składni
i zagadnień implementacji. Oprogramowanie open source, takie jak PHP, jest zwykle
używane w przypadku projektów, przy tworzeniu których nie można liczyć na pomoc ze-
społu ekspertów od baz danych, sieci i projektowania. A więc nikt cię nie wyręczy.

Jeżeli nie jesteś do tego zmuszony, nie próbuj nauczyć się wszystkiego na raz. Najważ-
niejszym zadaniem jest zapoznanie się z serwerem WWW. Apache jest potężny, ale
niestety wymaga dogłębnego poznania. Zapewne będziesz chciał nauczyć się SQL, je-
żeli jeszcze go nie znasz; obsługa poczty również jest bardzo przydatna. Po opanowaniu
tajników tej trójcy kolejne etapy nauki będą łatwiejsze.

Skoncentruj się na...


Jeden z Autorów, zanim nauczył się PHP, był już zaawansowanym programistą, nato-
miast drugi miał jedynie podstawy HTML — możemy więc podzielić się naszymi do-
świadczeniami. Aby nieco ułatwić sobie naukę, spróbuj zastosować poniższe kroki.

Czytanie istniejącego kodu


Nauczenie się czytania kodu napisanego przez kogoś innego może być trudniejsze, niż
się wydaje. Jedną z najlepszych cech PHP jest swobodna składnia i zasada „Nie przej-
muj się" — ale skrypty się różnić, nawet mimo podobnych wyniki. Początkujący mogą
utonąć w problemach stylistycznych, próbując odgadnąć, które części skryptu są nie-
zbędne, a które są wynikiem dziwactw autora. Niezależnie od trudności, im sprawniej
będziesz w stanie analizować kod PHP napisany przez innych, tym szybciej będziesz
mógł z niego korzystać.

Jednym z użytecznych ćwiczeń jest skorzystanie z archiwum listy dyskusyjnej lub


składnicy kodu źródłowego (zajrzyj do dodatku D) i wydrukowanie kilku przykładów
kodu, który rozwiązuje ten sam problem (najlepiej taki, który cię interesuje). Następnie
ułóż kartki obok siebie i za pomocą grubego pisaka zaznacz wszystkie części wspólne.
Daj kilka punktów tym autorom, którzy dobrze skomentowali swój kod i poszukaj in-
nych fragmentów kodu napisanych przez tych autorów.

Myślenie o programowaniu
Jak wspomnieliśmy, nauczenie się programowania wymaga nieco czasu, praktyki i prze-
glądania wielu przykładów. Nie ma żadnego sposobu na skrócenie tego procesu.

Nowicjuszom może przydać się skorzystanie z pseudokodu.


574___________________________________________________Dodatki

1. Zapisz zadania, jakie strona ma realizować. Ważne jest, żeby opis ten był
kompletny.
Strona powinna wyświetlać formularz wypełniony odpowiedziami, które
zostały poprzednio wprowadzone, z możliwością ich zmiany. Chcę,
aby strona ta była zabezpieczona hasłem, wiec potrzebuję identyfikatora
użytkownika z okna logowania.

2. Podziel opis na etapy i zadania, jak w przypadku przepisu. Jeżeli jest to ko-
nieczne, zmień kolejność.
1. Pobierz identyfikator użytkownika ze strony logowania.
Jeżeli nie ma go, nic nie wyświetlaj.
2. Wyświetl formularz HTML.
3. Wyświetl w formularzu wszystkie stare wartości z bazy danych
a) podłącz się do bazy danych
b) odczytaj dane związane z aktualnym elementem
c) wstaw do formularza HTML jako "value=X".
4. Zmień wartości i wstaw je do bazy danych.
5. Przekaż identyfikator użytkownika do następnej strony

3. Każdy z tych punktów zamień na kod PHP. Zwykle łatwiej jest zacząć od
głównych zadań — na przykład wysyłania poczty lub wypisywania jakichś in-
formacji na ekranie, niż od zadań pobocznych, jak podłączanie się od bazy da-
nych. Jeżeli będziesz potrzebował podłączenia do bazy danych, skorzystaj
chwilowo ze zmiennej, tablicy lub dołączanego pliku.
1. Pobierz identyfikator użytkownika ze strony logowania.
Jeżeli nie ma go, nic nie wyświetlaj.

// Wstawiamy fikcyjny identyfikator użytkownika zamiast przekazanego


// z ekranu logowania. Trzeba to później zmienić.
<?php SUserlD = 1; ?>

2. Wyświetl formularz HTML.

<HTMLXHEADX/HEAD>
<BODY>
<FORM>
Imię: <input type="text" size=30 name=" FirstName"XBR>
Nazwisko: <input type="text" size=30 name = "LastName"xBR>
E-mail: <input type="text" size=30 name = "Email"XBR>

3. Wyświetl w formularzu wszystkie stare wartości z bazy danych.

Użyję teraz zmiennych, ale później trzeba je zmienić na wartości z bazy


danych.
<?php
SFirstName = "Joyce";
$LastName = "Park";
$Email = "root@localhost";
?>
Myślę, że należy to umieścić przed formularzem.

4. Zmień wartości i wstaw je do bazy danych


5. Przekaż identyfikator użytkownika do następnej strony

4. Sukcesywnie dodawaj coraz więcej wierszy kodu, rozwiązując napotkane pro-


blemy. Możesz zachować zmodyfikowany pseudokod w postaci komentarzy.
/* Przekazanie identyfikatora użytkownika do następnej strony. Najlepszym
sposobem jest umieszczenie w formularzu ukrytego pola i zmiennej PHP.
HTML będzie emógl przekazać go razem z innymi zmiennymi POST.
*/
Dodatek C » PHP dla programistów HTML_______________________________575

Nauka SQL i innych protokołów


Zanim zaczniesz dodawać kod PHP pośredniczący pomiędzy użytkownikiem progra-
mem, warto poświęcić trochę czasu na ćwiczenie bezpośredniej komunikacji z serwe-
rem usługowym za pomocą dowolnego dostarczanego z nim narzędzia.

Możesz przy okazji, wykorzystując program obsługi serwera, utworzyć bazę danych,
nawet jeżeli w PUP istnieją narzędzia realizujące te same zadania. Program phpMy-
Admin jest bardzo sprytnym i wygodnym narzędziem do obsługi bazy danych MySQL,
ale początkujący administrator serwera MySQL nauczy się więcej korzystając z bardziej
prymitywnego interfejsu wiersza poleceń MySQL.

Wprowadzanie kosmetycznych zmian


do istniejących aplikacji PHP
Modyfikowanie istniejących aplikacji, takich jak IMP lub Phorum, może zmotywować
cię do dalszej pracy. Oto kilka porad.
* Na początek spróbuj zmienić kolory —jest to zwykle dosyć bezpieczne. Jeżeli
pójdzie dobrze, spróbuj zmienić przyciski. Następną bezpieczną operacją jest
zmiana układu — szerokości tabel, kolumn itd. Możesz bez obaw dodawać
grafikę, łącza lub eksperymentować z arkuszami stylu.
* Jeżeli istnieją pliki dołączane (szczególnie header.inc), w nich właśnie znaj-
dują się części odpowiedzialne za kosmetykę. Poszukaj na początek kolorów
w nagłówkach i stopkach, podstaw układu strony i tym podobnych szczegółów.
Pamiętaj, aby zmianom w nagłówkach odpowiadały zmiany w stopkach i na
odwrót.

Nigdy nie kasuj wierszy zawierających słowa kluczowe (na przykład if, while lub for).

Uruchamianie to też programowanie


Niewielu ludzi tak naprawdę lubi uruchamianie programów. Jeden z naszych kolegów ze
studiów zauważył, że bardziej lubi tworzenie nowych funkcji od przetrawiania cudzych.
Jednak uruchamianie może być użytecznym narzędziem w nauce programowania, ponie-
waż można poprawiać fragmenty dużego systemu bez pisania wszystkiego od podstaw.

Przy uruchamianiu programów warto skorzystać z pomocy drugiej osoby, na zasadzie


„Co dwie głowy, to niejedna". Gdy jesteś zmęczony lub widziałeś fragment kodu zbyt
wiele razy, trudno ci będzie skupić się na każdym szczególe. Warto zanalizować wspól-
nie logikę takiego fragmentu — rozpoczynając pytania, dlaczego wykonujemy każdy
krok, i sprawdzić każdy kolejny etap. Świeża para oczu może wyłapać proste pomyłki,
takie jak nieprawidłowa nazwa zmiennej lub brakujący nawias.

i
576___________________________________________________Dodatki

Na początek unikaj
Istnieje kilka zagadnień mało znanych koderom HTML, zwykle niepotrzebnych do pi-
sania funkcjonalnego kodu PHP. Przynajmniej na początku unikaj następujących za-
gadnień.

Obiekty
Obiekty mogą mylić nawet doświadczonego programistę „nieobiektowego". Ponieważ
PHP nie jest językiem w pełni obiektowym, „notacja obiektowa" może powodować
problemy w późniejszej pracy.

Maksymalny styl PHP


Więcej informacji na ten temat znajdziesz w rozdziale 14. Styl ten nie jest zalecany nawet
przez Rasmusa Lerdorfa i jedynie „niereformowalni" programiści C używają go w bardzo
specyficznych sytuacjach. Programy pisane w ten sposób zawierają po prostu zbyt wiele
apostrofów, cudzysłowów, ukośników, kodów ASCII i HTML, zmian wierszy.

Programowanie dużych aplikacji od podstaw


Po co wynajdywać na nowo koło? W krainie open source nie musisz tego robić. Twoja
praca będzie efektywniejsza, jeżeli będziesz sprawnie dostosowywał rozwiązania użyte
przez innych programistów, a nie tworzył wszystko od podstaw (nawet jeśli jesteś naj-
lepszym programistą). Naucz się kupować, gdy czegoś potrzebujesz.

Przeczytaj książkę o programowaniu w C


Niestety nie napisaliśmy kompletnego podręcznika programowania. Część I tej książki
wyjaśnia niektóre tematy, ale są one przedstawione skrótowo. Staraliśmy się szeroko ko-
mentować nasze przykłady kodu, ale nie wyjaśniamy do końca zastosowanych technik.

Uczestnicy list dyskusyjnych często doradzają początkującym programistom PHP ku-


pienie książki o programowaniu w C — może to być jedna ze standardowych odpowie-
dzi, która nie wymaga specjalnego zaangażowania respondenta. Ale sam pomysł wart
jest rozważenia, jeżeli masz problemy z programowaniem w PHP.

Prosto napisanym, krótkim podręcznikiem jest „On to C" autorstwa Patricka H. Win-
stona (Addison-Wesley, 1994). Ma niecałe 300 stron; większość materiału związanego
z PHP znajduje się na początku książki. Sztandarowym podręcznikiem jest „Język
ANSI C" Briana W. Kernighana i Dennis M. Ritchie (Prentice Hall, 1988), który jest
uważany za wzorcowy, ale ma charakter skorowidza i dlatego nie odpowiada czasem
projektantom HTML.
Dodatek C » PHP dla programistów HTML_____________________________577

Minimalny styl PHP


Na początek jest to prawdopodobnie najłatwiejszy dla projektantów HTML styl pro-
gramowania w PHP. Sugerujemy całkowite oddzielanie od siebie fragmentów HTML
i PHP. W ten sposób nie tylko unikniesz kłopotów stylistycznych, ale także połączo-
nych zakłóceń ze strony HTML i PHP, co dwukrotnie utrudnia odszukanie źródła pro-
blemu. Temat ten przedstawiliśmy dokładniej w rozdziale 14.

Najłatwiejszą metodą będzie utworzenie szablonu HTML za pomocą dowolnego narzę-


dzia. Sprawdź dokładnie działanie tego szablonu i skorzystaj z narzędzia do weryfikacji
HTML. Wstawiając części PHP będziesz pewien, że wszystkie problemy są powodo-
wane przez kod PHP, a nie HTML.

Wadą tego stylu programowania jest to, że strona nie może przekazywać zmiennych
sama do siebie. Jest to szczególnie istotne w przypadku formularzy; możesz nieco
zmienić styl programowania, jeżeli uważasz, że jesteś do tego przygotowany.

Używaj właściwych narzędzi


Na koniec gorąco polecamy używanie edytora tekstowego, rozumiejącego PHP, do pi-
sania części PHP skryptów (w rozdziale 4. przedstawiliśmy porównanie edytorów tek-
stowych i narzędzi wizualnych). Niektórzy programiści potrafią dokonać cudów,
korzystając z Notatnika bądź emacsa. Stąd wielu początkujących programistów, którzy
próbowali użyć tych narzędzi (ktoś powiedział im, że prawdziwi programiści tak pra-
cują) wpada w pułapkę. Jeżeli lepiej pracuje ci się w vim lub Visual SlickEdit, nic nie
stoi na przeszkodzie używaniu tych narzędzi.

Ta porada nie dotyczy edytorów wizualnych, których użycia odradzamy.


Wcześniej czy później będziesz musiał poprawiać HTML do postaci
czytelnej, ponieważ edytory te zwykle nie potrafią takiej utworzyć. Je-
żeli chcesz używać takiego narzędzia, twoja sprawa — ale korzystanie
z niego nie może zastępować zrozumienia i umiejętności ręcznego pi-
sania czystego kodu HTML.
578 Dodatki
Dodatek D
Zasoby Sieci na temat PHP
W tym dodatku omówimy podstawowe zasoby Sieci, które mogą pomóc lepiej poznać
PHP. Adresy przydatnych witryn zawierających opisy produktów znajdziesz również
w innych rozdziałach niniejszej książki.

Witryna PHP
Oficjalną witryną PHP jest:
http://www.php. net

Możesz znaleźć tam najnowsze informacje, uaktualnienia, opisy wykrytych błędów oraz
wciąż rosnącą listę witryn korzystających z PHP.

Podręcznik
W części Documentation znajdziesz podręcznik PHP, dostępny w kilku wersjach.
* Wersja w języku japońskim i francuskim.
+ Kilka wersji do ściągnięcia w postaci PDF, RTF i HTML, wygodnych, jeżeli
chcesz korzystać z podręcznika nie mając dostępu do Sieci. Wersja HTML jest
rozprowadzana razem z PHP.
* Wersja dla PalmPilota.
* Podstawowa wersja sieciowa w postaci HTML.

Najczęściej jednak mówiąc o podręczniku PHP mamy na myśli najbardziej znaną wer-
sję sieciową z komentarzami. Użytkownicy z całego świata mogą dodawać uwagi i ko-
mentarze do każdej strony tego podręcznika. Są to zwykle objaśnienia niejasnych
punktów głównego tekstu, dodatkowe spostrzeżenia i uwagi na temat działania PHP na
różnych platformach.
580 Dodatki

Podręcznik sieciowy nie jest miejscem do zadawania pytań! Przezna-


czony jest jedynie na konkretne komentarze. Możesz wysłać e-mail
z prośbą o pomoc do autorów komentarzy — wielu z nich pewnie
chętnie ci pomoże. Możesz również zaprenumerować listę dyskusyjną
lub umieścić ogłoszenia na forum PHP. Pamiętaj, że niemądre pytania
umieszczone w komentarzach do podręcznika mogą wpłynąć na opi-
nię środowiska o tobie.

Korzystając z podręcznika, musisz pamiętać o następujących zagadnieniach:


* Główny tekst podręcznika jest bardzo zwięźle napisany.
* Komentarze są zmieniane i weryfikowane bardzo rzadko (aby nie powiedzieć,
prawie nigdy). Korzystaj z nich bardzo ostrożnie — wiele problemów trudniej
rozwikłać, ponieważ autor bezkrytycznie zastosował rozwiązanie pochodzące
z komentarzy do podręcznika. Możesz napisać do autora komentarza i upewnić
się, że porada dotyczy interesującego cię problemu.
** Informacje zawarte w podręczniku są opóźnione w stosunku do aktualnego stanu
PHP. Przez pewien czas objaśnienia i komentarze dotyczące PHP 3 i PHP 4
mogą by ć tożsame.

Lista dyskusyjna PHP


„Oficjalna" społeczność PHP skupiona jest wokół listy dyskusyjnej. Jeżeli twoje pyta-
nie dotyczy technicznych aspektów działania PHP i wymaga odpowiedzi jednego
z twórców podstaw języka, powinieneś je przesłać do tej listy. Wraz z powstaniem PHP
4 zadecydowano podzielić główną listę na kilka bardziej specjalistycznych —jeżeli za-
stanawiasz się, dlaczego zmniejszył się ruch na liście, znasz już powód.

Lista użytkowników i lista twórców


Dla aktywnych twórców i tych, którzy nie boją się wejść w kod w C i walczyć z błęda-
mi, przewidziane są trzy listy:
php-dev Główna lista dla twórców.
Beta Najczęściej wypełniana przez osoby wyszukujące błędy.
phplib-dev Lista dla twórców PHPLIB.

To listy o średnim lub niskim ruchu, około 100 do 1000 listów miesięcznie. Poruszają
aspekty techniczne. Pewnie niewiele z nich skorzystasz, chyba że jesteś aktywnym
członkiem grupy.

Dla użytkowników przewidziane są osobne listy, przeważnie nowych:


Dodatek D » Zasoby Sieci na temat PHP_______________________________581

php-general Główna lista o bardzo dużym ruchu, ponad 100 komunikatów


dziennie.
php-db Lista przeznaczona na zagadnienia związane z bazami danych.
php-install Lista związana z instalacją.
php-i 18n Lista dotycząca lokalizacji.
php-migration Dyskusje na temat przejścia z poprzedniej wersji PHP.
php-windows Lista dla użytkowników Windows.
Phplib Lista użytkowników PHPLIB — średni ruch.
php-announce Ogłoszenia o nowych wersjach — używana tylko czasami.

Istnieje również wiele nieoficjalnych list prowadzonych w wielu językach. Ich zestaw
znajduje się w części Documentation na witrynie PHP.

Pojawiły się także listy dotyczące popularnych projektów opartych na PHP, takich jak
Midgard. Możesz się do nich zapisać poprzez ich witryny WWW.

Jeżeli wolisz korzystać z systemu internetowych list dyskusyjnych (wielu nowych użyt-
kowników tego nie potrafi), możesz przyłączyć się do listy PHP za pomocą bramki news:
news.php.net

Ten sposób korzystania z listy ma wielką zaletę — możesz wysyłać listy, nie będąc za-
pisanym. Jednak nowicjusze powinni raczej szukać odpowiedzi w archiwum, zanim
(lub zamiast) zadadząpytanie.

Większość list dyskusyjnych jest zarchiwizowana; można je przeszukiwać poprzez dwie


witryny:
http://www.phpbuilder.com/mail/
http://www.progressive-comp.com/Lists

Różnica pomiędzy tymi witrynami jest niewielka: archiwum PCC ma bardzo prosty
układ (w chwili pisania tej książki witryna ta była czarno-biała), natomiast PHPBuilder
intensywniej wykorzystuje HTML. Archiwum PHPBuilder dla PHP 4 rozpoczyna się
od maja 2000, wcześniejsze artykuły dostępne sąw części PHP 3 i PHP 4-beta. Istnieje
również archiwum Geocrawler (http://www.geocrawler.com) zawierające archiwum
PHP 3; być może nie jest uaktualniane.

Zanim umieścisz pytanie na liście dyskusyjnej, przeszukaj witrynę PHP,


archiwum i Zend.com. Być może prędko rozwiążesz problem, a pro-
gramiści nie spędzą czasu na odpowiadaniu po raz kolejny na te sa-
me pytania i będą mogli go poświęcić na tworzenie nowych funkcji
języka. W chwili obecnej przeszukiwanie archiwów właściwie jest obo-
wiązkowe. Przy tak dużej liczbie użytkowników tak zwane pytania RTFM
(w luźnym przekładzie — przeczytaj w końcu podręcznik) nie powinny
się już pojawiać na liście dyskusyjnej PHP.
582____________________________________________________Dodatki

Standardowa i skrócona wersja listy


Na głównej liście użytkowników PHP panuje tak duży ruch, że dwa razy dziennie gene-
rowana jest jej skrócona wersja. Nowe wyspecjalizowane listy również mają krótsze
odpowiedniki. Surowe i skrócone wersje list mają, jak zwykle, wady i zalety.

Jeżeli nigdy nie dostałeś ponad 100 listów, zapychających skrzynkę pocztową, nie wiesz,
jak to przeszkadza. Tylko czytanie i usuwanie może zająć kilka godzin, a odpowiadanie
na listy to już pełnoetatowe zajęcie. W żadnym wypadku nie powinieneś pobierać pełnej
listy użytkowników, jeżeli korzystasz z bezpłatnej poczty na stronie WWW.

Nieomal obowiązkowe jest posiadanie oddzielnej skrzynki pocztowej na


główną listę dyskusyjną PHP, chyba że napiszesz dobre filtry w progra-
mie pocztowym. W przeciwnym wypadku w zalewie wątków o podob-
nych nazwach szybko zgubisz przesyłki z innych źródeł.

Z drugiej strony trudniej brać udział w dyskusji korzystając ze skróconej wersji. Kilku
dzielnych członków społeczności może odpowiedzieć na wszystkie pytania, zanim
otrzymasz skrót listy.

Początkującym proponujemy subskrybowanie skróconej wersji listy. Gdy będziesz go-


towy, aby brać czynny udział w dyskusjach, możesz zmienić typ subskrypcji listy.

Powinieneś również rozważyć korzystanie z forum PHP zamiast subskrybowania listy.


Jest to świetne rozwiązanie dla tych, którzy nie lubią list dyskusyjnych via e-mail. Wadą
tego rozwiązania jest to, że twórcy PHP zwykle ich nie śledzą, więc bardzo skomplikowa-
ne pytania pozostają zwykle bez odpowiedzi. Zaletą forum jest to, że panuje tutaj lepsza
atmosfera dla początkujących, szczególnie jeżeli chodzi o powtarzające się pytania.

Zapisywanie się
Aby zapisać się na jakąś listę dyskusyjną PHP, przejdź na stronę Support witryny PHP.
W środku strony ukryty jest niepozorny formularz służący do zapisania się na listę.
Wybierz listę, która cię interesuje, wpisz adres i kliknij przycisk Subscribe. Za pomocą
tego formularza możesz również anulować subskrypcję.

Zarządca listy dyskusyjnej PHP nieomal natychmiast wyśle do ciebie e-mail, w którym
poprosi o potwierdzenie subskrypcji. Dopóki nie odeślesz potwierdzenia, nie zostaniesz
zapisany na listę. Anulowanie subskrypcji nie wymaga potwierdzenia.

Netykieta list dyskusyjnych


Listy dyskusyjne produktów typu open source szybko się zmieniają. Przecież to ludzie
korzystają z list dyskusyjnych, a poznawanie różnych charakterów i opinii może być
ciekawe. Pewnie trafisz na ludzi interesujących, a czasem, niestety, na nudziarzy. Sto-
sowanie netykiety może wszystkim uprzyjemnić życie.
Dodatek D » Zasoby Sieci na temat PHP_______________________________583

Pamiętaj, że nikt nie chce od ciebie pieniędzy


Zanim zaczniesz narzekać, przypomnij sobie ostatnie kontakty z serwisem technicznym
komercyjnego oprogramowania. Czy twój problem został rozwiązany tego samego
dnia? Czy porada kosztowała? W którym momencie mogłeś porozmawiać z autorami
programu?

Pisz dokładne opisy


Napisz możliwie dużo o używanej platformie, problemie i o działaniach, jakie podjąłeś
w celu rozwiązania problemu. Nie staraj się być zwięzły, lepiej powiedzieć nieco wię-
cej, niż zmuszać wszystkich do kilkakrotnego przeczytania twojej wypowiedzi w celu
jej zrozumienia.

Zamieszczanie fragmentów kodu jest najlepszym sposobem przedstawienia twojego


problemu. Wielu ludzi zmienia nieco swój kod, aby był bardziej abstrakcyjny. Pamiętaj
też o usunięciu haseł.

Sprawdź, czy użyłeś odpowiedniego tematu listu — im bardziej szczegółowy, tym le-
piej. Temat „PHP — pomocy" będzie prawdopodobnie zignorowany przez większość
stałych bywalców listy. Powinieneś wybrać coś bardziej opisowego, na przykład: „Ar-
gumenty mysql_connect nie są przekazy wane w wersji 4.0.0".

Istnieją pewne granice


Lista dyskusyjna i inne zasoby Sieci są przeznaczone do tego, aby ci pomóc, ale powi-
nieneś uzbroić się w cierpliwość. Pomoc nie oznacza, że ktoś przyjdzie do twojego biu-
ra i napisze za ciebie program! Nie proś również uczestników listy o wejście na twój
serwer i uruchomienie jakiegoś skryptu.

Bardzo często zdarza się sytuacja, że ktoś wchodzi na listę dyskusyjną i narzeka, że
PHP nie posiada funkcji, której właśnie jest potrzebna. Programiści zwykle wtedy py-
tają, czy zająłbyś się wykonaniem takiej funkcji. Jeżeli nie jesteś wystarczająco dobry
w C, możesz poprosić kogoś innego o stworzenie takiej funkcji.

Zrób to sam
Bezpłatne oprogramowanie może być używane bez ograniczeń, ale nie zwalnia od od-
powiedzialności. Twoim zadaniem jest zorientowanie się, gdzie i w jaki sposób możesz
spożytkować zdolności. Nie oznacza to, że wszyscy musimy stać się programistami C.
Istnieje wiele innych sposobów „dołożenia swojej cegiełki". Odpowiadanie na pytania
na listach dyskusyjnych i witrynach WWW zawsze jest dobrym pomysłem, ponieważ
zmniejsza obciążenie głównych programistów.
584________________________________________________Dodatki

Być może to twoja wina


Jeżeli masz kłopoty z porozumieniem się, może problem leży po twojej stronie. Jeżeli
znajdziesz się przypadkiem w środku wojny na obelgi, co niestety zdarza się czasami na
listach dyskusyjnych, możesz stać się pośmiewiskiem, mówiąc bez ogródek.

Inne witryny na temat PHP


Oprócz oficjalnych zasobów dotyczących PHP, przedstawionych powyżej, istnieje kilka
bardzo pożytecznych witryn prowadzonych przez znanych członków społeczności PHP.
Niektóre z nich są właściwie półoficjalne.

Maszyna wykonująca skrypty i narzędzia


Zend.com
http://www.zend.com

Zend.com jest witryną poświęconą maszynie skryptowej PHP 4 oraz centrum komer-
cjalizacji PHP. Firma sprzedaje usługi serwisowe i oferuje pisanie programów dla du-
żych firm, jednak większość użytkowników PHP jest zainteresowana dodatkami do
PHP, jakie tworzy się w siedzibie Zend.com w Izraelu. Pierwszymi takimi narzędziami
są: dostępny już optymalizator i oczekiwane przez wszystkich nowości, takie jak bufor
PHP. Witryna ta zawiera jedyne w swoim rodzaju strony, na przykład udostępnia bio-
grafie większości ważnych postaci świata PHP.

Baza wiedzy PHP/automatyczny FAQ


PHP Faqts (E-gineer)
http://www.faqts.com/knowledge-base/index.phtml

Jest to świetnie zorganizowane archiwum często zadawanych pytań, z dobrym narzę-


dziem do przeszukiwania. Witryna polecana nowicjuszom.

Dodatki do PHP
PHP Base Library
http://phplib. netuse. de/index.php3

Wiele plików, klas i funkcji służących do lepszej obsługi sesji i uwierzytelniania. Pod
egidą tej witryny prowadzony jest projekt o nazwie phpslash — wersja PHP znanej wi-
tryny Slashdot.
Dodatek D » Zasoby Sieci na temat PHP_______________________________585

Artykuły i wskazówki na temat PHP


Samouczki zwykle zawierają przykłady kodu, ale nie są pomyślane jako gotowe do
użycia fragmenty — prezentująone raczej podejście „daj człowiekowi wędkę...".

PHP Builder
http://www.phpbuilder. com/

Jedna z najobszerniejszych i dobrze prowadzonych witryn PHP, która zawiera świetne


samouczki i uczęszczane forum, ale baza kodu generalnie zawiera łącza.

Devshed
http://w\vw. devshed. com/Server _Side/PHP/

Duża komercyjna witryna z dobrymi samouczkami i forum. Devshed zajmuje się


wszystkimi językami skryptowymi (ASP, JavaScript itp.), co sprawia, że jest to świetne
źródło informacji dla tych, którzy nadal nie wybrali narzędzia.

Baza kodu PHP


Baza kodu prezentuje podejście „daj człowiekowi rybę...", oferując zasoby wszystkim
chętnym. Jakość kodu jest bardzo różna, od pierwszych skryptów do eleganckich klas.
Przejrzyj również stronę Projects na witrynie PHP, aby odszukać główne aplikacje
używające PHP.

PHP Wizard
http://www.phpwizard. net/

Witryna oferująca kilka niezłych aplikacji, na przykład phpMyAdmin i phpChat.

PX
h ftp ://px. sklar. com/

Mało jasny projekt witryny, ukrywający dużą liczbą skryptów — w większości nie są to
aplikacje, ale raczej małe skrypty.

Weberdev
http ://www. weberdev. com/

Większość zawartości tej witryny, poświęconej skryptom, jest przeznaczona jedynie dla
zarejestrowanych użytkowników; posiada jedną naprawdę dobrą funkcję: sekcję Smart-
Code, bazę doskonałych sztuczek (w większości autorstwa Rasmusa Lerdorfa).
586________________________________________________Dodatki

Nasza witryna WWW


Na potrzeby problematyki poruszanej w tej książce uruchomiliśmy witrynę WWW do-
stępną pod adresem:
http://helion.pl/ksiazki/php4bi.htm

Znajdziesz tam większość fragmentów kodu zawartych w książce, podanych w wygod-


nym formacie kodu źródłowego, dzięki czemu unikniesz żmudnego przepisywania.

Możesz tam również natrafić na poprawki kilku błędów, które umknęły bystrym oczom
i czerwonym elektronicznym ołówkom naszych redaktorów.
Słownik
Abstrakcja proceduralna. Proces przenoszenia powtarzających się linii kodu do proce-
dur lub funkcji.

Active Server Pages (ASP). Firmowe środowisko Misrosoftu do uruchamiania skryptów


na serwerze. Maszyna ASP jest wbudowana w serwer WWW produkowany przez
Microsoft, Internet Information Server (IIS).

Adres IP. Czterobajtowy adres jednoznacznie określający komputer w Internecie.

Agent obslugiformularza. Program przetwarzający dane wpisane do formularza HTML.


PHP można używać jako agenta obsługi formularza.

Analizator poprawności. W kontekście XML jest to taki analizator, który sprawdza do-
kument pod kątem zgodności z jego plikiem definicji typu (DTD). Nie wszystkie anali-
zatory PHP kontrolują poprawność, choć wszystkie sprawdzają, czy dokument ma
poprawną strukturę.

Apache, serwer HTTP. Najpopularniejszy serwer WWW typu open source. Zwany rów-
nież serwerem Apache, Apache httpd lub po prostu Apache.

Apache Software Foundation. Fundacja nie nastawiona na zysk, która nadzoruje kilka
projektów open source, a w szczególności serwer Apache.

API. Zobacz Application Program Interface.

Applet. Program napisany w języku Java, który może być szybko załadowany ze strony
WWW i wykonywany w przeglądarce użytkownika.

Application Program Interface (API). Standard opisujący, w jaki sposób powinno się
pisać programy współpracujące z określonym systemem operacyjnym lub usługą. Zwy-
kle w postaci zestawu funkcji, które wywołuje się z kodu aplikacji.

Argument. Dane wejściowe funkcji lub dla operatora.

ASP. W kontekście tej książki — Active Server Pages, ale w innym kontekście może
być również skrótem od Application Service Provider (dostawca aplikacji, odżywająca
znów idea umożliwiania klientowi dostępu aplikacji, zwykle poprzez sieć).
588___________________________________________________Dodatki

Binarny. W książce używa się kilku znaczeń: 1). Związany z liczbami o podstawie dwa,
tak jak w arytmetyce binarnej. 2). Opis kodowania typu włączony-wyłączony, używa-
nego w pamięci komputera. 3). Plik używający wszystkich wartości bajtowych, a nie
tylko tych, które są czytelne dla człowieka. Na przykład pliki wykonywane bezpośred-
nio przez komputer są często nazywane binariami, aby odróżnić je od czytelnego dla
człowieka kodu źródłowego (patrz również kompilacja, interpretacja, źródło).

Boolean. Określenie wartości logicznej prawda-fałsz. W PHP typ ten posiada tylko
dwie wartości TRUE i FALSE.
C. Jeden z najważniejszych języków programowania używany do pisania systemów
operacyjnych, języków programowania i aplikacji ogólnego przeznaczenia. Ściśle zwią-
zany z rodzinąjęzyków programowania Unix.

C++. Następca C, który został wzbogacony o programowanie obiektowe.

CSS, kaskadowe arkusze stylu. Standard centralnego definiowania kosmetycznych atry-


butów witryny WWW, który może interpretować każda przeglądarka.

CGI. Patrz Common Gateway Interface.

Cole/Fusion. Produkt firmy Allaire Corporation oparty o znaczniki, umożliwiający uru-


chamianie skryptów na serwerze.

COM. Patrz Component Object Model.

Common Gateway Interface (CGI). Protokół komunikacji pomiędzy serwerem WWW


i programami generującymi na żądanie strony WWW. Zastępowany obecnie nieco ści-
ślejszą integracją serwera WWW i języków skryptowych.

Common Object Request Broker Architecture (CORBA). Standard dla rozproszonego


programowania obiektowego, sponsorowany przez konsorcjum przemysłu komputero-
wego Object Management Group. CORBA umożliwia komunikację pomiędzy różnymi
fragmentami programu, działającymi w różnych komputerach.

Cookie. Mała porcja danych zapisana przez serwer WWW w przeglądarce lub na dysku
w komputerze użytkownika.

Component Object Model (COM). Sponsorowany przez Microsoft standard rozproszo-


nych programów obiektowych, który umożliwia komunikację pomiędzy różnymi pro-
gramami uruchomionymi w tym samym komputerze (patrz DCOM, CORBA).

CORBA. Patrz Common Object Request Broker Architecture.

DCOM. Patrz Distributed Component Object Model.

Debugger. Oprogramowanie pomagające programiście znaleźć błędy w swoich pro-


gramach.

Demon http. Program stale działający na serwerze. Służy do udostępniania stron WWW.
Słownik___________________________________________________589

Document Type Definition (DTD). Definicja typu dokumentu. Dokument w taki sposób
określający strukturę klasy dokumentów XML, że możliwe jest ich automatyczne
sprawdzanie, czy struktura ta została zachowana.

Document Object Model (DOM). Standardowy interfejs (API) dla programów współ-
pracujących z analizatorami XML. W DOM analizatory odczytują cały dokument XML
i tworzą drzewo analizy, a program może przeszukiwać lub manipulować tym drzewem
(patrz SAX, API).

DNS. Patrz Domain Name Service.

DOM. Patrz Document Object Model.

Domain Name Service. Typ programu, który zajmuje się tłumaczeniem nazw domen (na
przykład www.ibm.com) na odpowiadające im adresy IP (patrz adres IP, Nazwa dome-
ny).

Double. Podstawowy typ PHP, który reprezentuje liczby zmiennoprzecinkowe (patrz


Liczby zmiennoprzecinkowe, Integer).

DTD. Patrz Document Type Definition.

Dynamiczny. Określa strony WWW korzystające z HTML i innych bardziej skompli-


kowanych technologii. Serwer tworzy te strony na bieżąco.

Dynamiczny HTML (DHTML). Określenie zestawu rozszerzeń HTML i technik pro-


gramowania przeglądarki kontrolujących graficzne aspekty witryny WWW.

Dziedziczenie. W programowaniu obiektowym relacja pomiędzy klasą uszczegółowie-


nia a bardziej ogólną klasą bazową.

ECMAScript. Nowa oficjalna nazwa JavaScript. Standard kontrolowany przez ECMA.

Edytor. Program do modyfikacji plików tekstowych, szczególnie kodu źródłowego pro-


gramów.

Efekt uboczny. W językach programowania: wynik działania wyrażenia inny niż główna
wartość zwracana przez wyrażenie.

fhttpd. Mały, szybki serwer WWW, którego autorem jest Alex Belits.

File Transfer Protocol (FTP). Standardowa metoda przesyłania plików pomiędzy kom-
puterami w Internecie.

FreeBSD. Bezpłatna wersja systemu operacyjnego Unix.

Fronton. Swobodny termin określający części programu najbliżej użytkownika. Menu


rozwijalne w przeglądarce generowane przez serwer WWW jest częścią frontonu, na-
tomiast baza danych przechowująca wyświetlane dane jest elementem zaplecza.

FTP. Patrz File Transfer Protocol.


590___________________________________________________Dodatki

Funkcja. Podstawowa konstrukcja programowania pozwalająca na nadanie nazwy blo-


kowi kodu i używanie jej w rozmaitych sytuacjach z różnymi danymi wejściowymi.

General Public License (GPL). Typ licencji na oprogramowania lansowany przez Free
Software Foundation. Zakłada swobodną dystrybucję źródeł i dopuszcza dystrybucję
zmian do oprogramowania objętego licencją GPL, jeżeli te zmodyfikowane wersje są
nadal dostępne jako GPL.

GET. Metoda przesyłania danych poprzez HTTP w odpowiedzi na żądanie przesłania


strony WWW. Metoda GET jest często uruchamiana w czasie przechodzenia pomiędzy
stronami za pomocą łącza.

Globalny. Określenie opisujące dane lub wartości dostępne w całym kodzie określonego
programu. Przeciwieństwo określenia „lokalny".

GPL. Patrz General Public License

Hosting. Dostarczanie sprzętu i usług niezbędnych do udostępnienia witryny WWW


stworzonej przez usługobiorcę. Usługi takie obejmują zwykle udostępnienie sprzętu,
połączenia do Internetu i czynności administracyjnych niezbędnych do utrzymania wi-
tryny w ruchu.

HTML. HyperText Markup Language, podzbiór języka SGML. Powszechna metoda za-
pisu stron WWW.

HTTP. HyperText Transfer Protocol. Podstawowy protokół, za pomocą którego serwer


WWW porozumiewa się z przeglądarką.

IDE. Patrz Integrated Development Environment.

IE. Patrz Internet Explorer.

IMAP. Internet Message Access Protocol. Protokół pobierania poczty, który uzupełnia
lub zastępuje POP.

Instrukcja. W PHP: wyrażenie zakończone średnikiem.

Integer. W matematyce jest to liczba całkowita, bez części ułamkowej, dodatnia bądź
ujemna. W PHP podstawowy typ danych reprezentujący liczby całkowite.

Integrated Development Environment (IDE). Kombinacja kilku programów tworząca


wygodne środowisko do pisania programów. IDE musi posiadać co najmniej edytor ko-
du źródłowego, debugger i system kontroli wersji.

Internet Explorer (IE). Przeglądarka internetowa firmy Microsoft.

Internet Information Server (US). Serwer WWW firmy Microsoft.


Słownik___________________________________________________591

Interpretacja. W językach programowania: proces wykonywania programu kompute-


rowego z sukcesywnym tłumaczeniem i wykonywaniem kolejnych instrukcji kodu źró-
dłowego. Przeciwieństwo do kompilacji, w której tłumaczenie całości odbywa się na
osobnym etapie przed wykonaniem.

ISP. Dostawca Internetu.


Java. Obiektowy język programowania zaprojektowany i promowany przez firmę Sun
Microsystems. Posiada niezwykłą metodę kompilacji, która ma dużą przenośność kodu.
Możliwe pisanie appletów Java wykonywanych w kilku różnych przeglądarkach.

Java Database Connectivity (JDBC). Standard połączenia z bazą danych promowany


przez firmę Sun Microsystems. Umożliwia programom napisanym w języku Java pod-
łączanie do różnych systemów baz danych.

Java Server Pages (JSP). Język skryptów serwera promowany przez firmę Sun Micro-
systems, w którym fragmenty kodu w języku Java wbudowywane są w kod HTML.

JavaScript. Język skryptów wykonywanych w komputerze klienta, który rozszerza


możliwości interfejsu przeglądarki. Nie ma związku z językiem Java.

Java Virtual Machine (JVM). Maszyna wirtualna Java. Program wykonujący kod po-
średni języka Java. Moduły JVM są wbudowywane w przeglądarki i umożliwiają uru-
chamianie appletów (patrz Java, Applet, Przeglądarka).

JDBC. Patrz Java Database Connectivity.

Język programowania. Standard definiujący zbiór konstrukcji, dzięki którym można pi-
sać kod. Jeżeli istnieje różnica pomiędzy specyfikacją języka a konkretną implementa-
cją kompilatora lub interpretera, termin ten odnosi się do specyfikacji. Przykładami
języków programowania są C, C++, Common Lisp, Scheme, Basic, Pascal, Fortran,
Perl, PHP; języki skryptowe również są językami programowania (patrz Język skrypto-
wy, Kompilacja, Interpretacja).

Język skryptowy. Język programowania niewymagający jawnej kompilacji przez pro-


gramistę. Wczesne wersje języków skryptowych były zwykle interpretowane i mniej
wydajne od zwykłych języków programowania. Rozwój spowodował, że wiele języków
skryptowych (Perl, PHP 4) jest kompilowanych (bez konieczności wykonywania tego
przez programistę) i wydajnych (patrz Interpretacja, Kompilacja).

JScript. Implementacja JavaScript firmy Microsoft.

JSP. Patrz Java Server Pages.

JVM. Patrz Java Virtual Machine.

Klasa. Termin programowania obiektowego określający zdefiniowany przez programi-


stę szablon dla obiektów określonego typu.
592___________________________________________________Dodatki

Kod źródłowy. Tekst programu napisany przez programistę. Jego przeciwieństwem jest
kod wynikowy produkowany z kodu źródłowego (patrz Kompilacja, Binarny).

Kolejność operatorów. Zbiór zasad określający, który operator pierwszy pobierze dane,
jeżeli operatory występują po lewej i prawej stronie argumentu.

Kolokacja. Specjalny typ udostępniania sieci, w której klient dostarcza komputer z uru-
chomionym serwerem WWW (administrowanym zdalnie), natomiast dostawca Inter-
netu udostępnia pomieszczenie, zasilanie, dostęp do Internetu, a często inne usługi, na
przykład składowanie.

Kompilacja. Zautomatyzowany proces translacji programu w formie tekstowej (napisa-


nej przez programistę) do formy wykonywalnej przez komputer. Kompilacja zwykle
wykonywana jest w całości przed uruchomieniem jakiejkolwiek części programu (patrz
Interpretacja).

Konkretyzacja. Proces tworzenia egzemplarza na podstawie ogólnego schematu lub


wzorca. W programowaniu obiektowym oznacza tworzenie obiektu będącego konkrety-
zacją klasy.

Konwersja typów. Zamiana typu wartości wyrażenia na inny typ danych. Może być,
choć nie musi, wynikiem rzutowania typów.

Kolumna. W kontekście baz danych — wartości zapisane w odpowiednim polu zdefi-


niowanym w tabeli. Każdy wiersz w tabeli składa się z wartości z wszystkich kolumn.

Liczby zmiennoprzecinkowe. Określenie liczb mających część ułamkową reprezentowa-


ną przez liczby po prawej stronie przecinka.

Linux. Bezpłatna wersja systemu operacyjnego Unix.

Lista dyskusyjna. Zestaw adresów e-mail osób o podobnych zainteresowaniach. Poczta


przesłana na adres listy jest rozsyłana do wszystkich jej członków.

Localhost. Urządzenie sieciowe oznaczające ten sam komputer, w którym działa pro-
gram.

Lokalny. Określenie zmiennej lub wartości w programie, która jest dostępna jedynie
w ograniczonym obszarze kodu. Przeciwieństwo do określenia Globalny (patrz Zasięg).

Metadane. Dane na temat danych. W kontekście WWW — zwykle znaczniki w doku-


mencie, które opisują dane.

MySQL. Bezpłatny system relacyjnych baz danych często łączony z Linuksem, PHP
i Apache, w celu uzyskania kompletnego zestawu programów dla witryny WWW.

Nazwa domeny. Nazwa komputera lub usługi w Internecie, na przykład www. trout-
works, com. Nazwy domen drugiego poziomu, na przykład ibm.com, mogą grupować
więcej nazw domen.
Słownik____________________________________________________593

Netscape Navigator. Przeglądarka WWW wyprodukowana przez Netscape (patrz Prze-


glądarka).

Obiekt. Konstrukcja programowa hermetyzująca dane i procedury.

Odstęp. Znak w pliku tekstowym lub programie, który nie jest widoczny. Znakami od-
stępu są: spacje, tabulatory, nowe linie i znaki końca linii.

ODBC. Patrz Open Database Connectivity.

Open Database Connectivity (ODBC). Standard komunikacji pomiędzy programami


a różnymi bazami danych. Promowany przez Microsoft.

Operacje w komputerze klienta. Określenie wszystkich działań, które wykonywane są


w komputerze klienta w relacji klient-serwer. W kontekście programowania dla WWW
są to operacje wykonywane w komputerze z przeglądarką, w przeciwieństwie od opera-
cji wykonywanych w komputerze obsługującym witrynę WWW (patrz operacje na
serwerze).

Operacje na serwerze. Określenie działań podejmowanych w komputerze będącym


serwerem w rozumieniu relacji klient-serwer. Przy programowaniu dla WWW określa
operacje wykonywane w komputerze z serwerem WWW, a nie w komputerze, w któ-
rym uruchomiona jest przeglądarka.

Operator. W językach programowania: rodzaj funkcji działającej na co najwyżej dwóch


argumentach.

Oprogramowanie Open Source. Oprogramowanie, które jest rozprowadzane w postaci


źródeł (lub z załączonymi źródłami), z przyzwoleniem na jego modyfikację. Określenie
jest przeciwieństwem oprogramowania, w przypadku którego rozprowadzana jest jedy-
nie postać wykonywalna, która praktycznie nie może być zmieniana.

OS. Patrz System operacyjny.

Panel nawigacyjny. Wyróżniony zestaw łączy umożliwiających szybkie przemieszcza-


nie się po witrynie WWW.

Parametry aktualne. Argumenty przekazane do funkcji w czasie jej wywoływania


(przeciwnie do parametrów występujących w definicji funkcji, które są nazywane pa-
rametrami formalnymi).

Parametry formalne. Nazwy zmiennych w definicji funkcji, do których przypisywane


są wartości argumentów wywołania (argumenty przekazane do funkcji nazywane są pa-
rametrami aktualnymi).

Perl. Bezpłatny język skryptowy, szczególnie lubiany przez administratorów i progra-


mistów Uniksa.

Personal Web Server (PWS). Serwer Intranetu produkowany przez Microsoft.


594____________________________________________________Dodatki

Personalizacja. W programowaniu dla WWW: proces tworzenia dynamicznych stron


WWW, zależnych od osoby przeglądającej witrynę.

PHP. Bezpłatny język skryptowy wbudowywany w kod HTML. Zawiera również ma-
szynę uruchamiania skryptów.

Plaska baza danych. System bazy danych przechowujący dane w jednej uniwersalnej
tabeli, w przeciwieństwie do relacyjnej bazy danych używającej wielu tabel (patrz Rela-
cyjna baza danych).

Polimorfizm. Dla funkcji lub metod: zdolność różnego zachowywania się w zależności
od liczby i typu argumentów.

POP. Post Office Protocol. Protokół do odczytywania poczty z serwera.

Poprawność. W kontekście XML poprawny dokument to taki, który nie tylko jest od-
powiednio zbudowany, ale również zgodny ze opisem typu dokumentu w DTD (patrz
DTD).

POST. Podstawowa metoda przesyłania danych przez HTTP w odpowiedzi na żądanie


przesłania strony WWW. Uruchamiana zwykle podczas przesyłania zawartości formu-
larza (patrz GET).

Przeglądarka WWW. Aplikacja uruchamiana w komputerze użytkownika, służąca do


przeglądania sieci WWW. Popularnymi przeglądarkami są Netscape Navigator, Micro-
soft Internet Explorer i Opera.

Przypisanie. Proces lub wyrażenie zapamiętujące wartość w zmiennej.

Relacyjna baza danych. System bazy danych mający możliwość podziału danych na
wiele tabel, połączonych ze sobą relacjami pomiędzy kolumnami.

Rozróżnianie wielkości liter. Powoduje, że w nazwach programów, zmiennych lub do-


kumentów zmiana wielkości litery generuje powstanie nowego symbolu. Przeciwień-
stwem jest nierozróżnianie wielkości liter.

RPM. Red Hal Package Manager. System prekompilowanych binariów dla dystrybucji
Linuksa Red Hat i jego naśladowców.

Rzutowanie typów. Wyrażenie w języku programowania, które jawnie wymusza kon-


wersję wartości z jednego typu na drugi.

SAX. Patrz Simple Api for XML.

Serwer WWW. Program odpowiedzialny za odpowiadanie na żądania wysłania stron


WWW. Przykładami serwerów WWW są: Apache, Microsoft IIS i iPlanet.

SGML. Patrz Standard Generalized Markup Language.


Słownik___________________________________________________595

Simple Api for XML (SAX). Interfejs (API) dla programów używających analizatorów
XML. W przypadku SAX w trakcie odczytywania dokumentu XML analizator powia-
damia aplikacje o zdarzeniach. Zdarzeniem takim może być napotkanie znacznika lub
określonej zawartości (patrz DOM, API).

Solaris. Wersja systemu Unix rozwijana i sprzedawana przez firmę Sun Microsystems.

System operacyjny. Główny program działający w komputerze, który umożliwia działanie


aplikacjom i pośredniczy w komunikacji ze sprzętem. Przykładami systemów operacyj-
nych są różne rodzaje systemu Unix (Linux, FreeBSD, Solaris), Windows (Windows
NT, Windows 2000, Windows 98) i system operacyjny Macintosha.

System plików. Połączenie plików i katalogów komputera z niskopoziomowymi pro-


gramami systemu operacyjnego, które nimi manipulują. Aplikacje zwykle działają na
systemie plików, nie korzystając z bezpośredniego dostępu do dysku.

Standard Generalized Markup Language (SGML). Standard definiowania języków


opartych o znaczniki, które z kolei są językami wprowadzającymi do dokumentów
określoną strukturę, więc mogą być zarówno odczytywane przez ludzi, jak i analizowa-
ne przez programy. Jednym z takich języków jest HTML, jednak nie jest on językiem
SGML, ponieważ łamie wiele jego reguł.

Statyczny. Określenie stron HTML, które nie są tworzone na bieżąco przed wyświetle-
niem lub nie zawierają żadnych rozszerzeń uruchamianych w komputerze klienta.

Ścieżka. Ciąg znaków określający położenie pliku w systemie plików. Zawiera wszyst-
kie niezbędne nazwy katalogów.

Tablica. W PHP — podstawowy typ przechowujący wartości w powiązaniu z indeksami.

Typ danych. Dziedziny wartości w pamięci komputera. Typ danych określa zarówno
przedział dopuszczalnych wartości, a także sposób, w jaki te wartości będą przechowy-
wane w pamięci komputera. Podstawowymi typami danych w PHP są integer, do-
uble, Boolean,string, a r r a y i object.

Universal Resource Indicator (URI). Adres internetowy. Jest to rozszerzenie adresu URL.

Unix. Rodzina systemów operacyjnych, w oparciu o które działa większa część Inter-
netu, z wyjątkiem komputerów użytkowników. Implementacjami Uniksa są Linux, Fre-
eBSD i Solaris.

VBScript. Język skryptowy firmy Microsoft oparty o Visual Basic.

Wiersz. W relacyjnych bazach danych: pozycja w tabeli bazy danych zawierająca war-
tości z kolumn wymienionych w definicji tabeli.

World Wide Web Consortium (W3). Międzynarodowe konsorcjum kontrolujące podsta-


wowe protokoły internetowe, na przykład: HTTP, HTML, CSS i XML.

Wskaźnik pliku. Wartość w programie wskazująca bieżącą pozycję w odczytywanym pliku.


596___________________________________________________Dodatki

Wyrażenie. W PHP — kombinacja podstawowych wartości, konstrukcji języka, opera-


torów i wywołań funkcji, która ma określoną wartość (patrz Instrukcja).

WYSIWYG. Angielski skrót oznaczający „Otrzymasz to, co widzisz", opisujący narzę-


dzia programowania, obróbki tekstu i projektowania, w których widok projektowanego
obiektu jest maksymalnie zbliżony do wyglądu gotowego produktu.

XML. Skrót od Extensible Markup Language. Spadkobierca SGML, o bardziej złożonej


strukturze niż HTML, projektowany zarówno do wyświetlania w przeglądarkach, jak
i jako format wymiany danych.

XSL. Extensible Style-sheet Language. Standard uzupełniający XML, definiujący trans-


formacje niezbędne do prawidłowego wyświetlenia dokumentu XML.

Zaplecze. Określenie części programu komputerowego bądź usługi, która jest niewi-
doczna dla użytkownika. W programowaniu dla WWW: do tej grupy zalicza się serwer
bazy danych; do części frontowej zalicza się przeglądarkę i programy tworzące HTML.

Zasiąg. Zbiór zasad w języku programowania określający, w których miejscach wystą-


pienie tej samej nazwy określa ten sam obiekt.

Zdarzenie. Określenie rodzaju sygnału wysyłanego z jednego programu do drugiego lub


z jednego podsystemu programu do drugiego. Program wysyłający spodziewa się, że
program odbierający odpowie na zdarzenie.

Zend. Nazwa programu analizującego i wykonującego, który jest podstawą PHP 4. Jest
to również nazwa firmy założonej przez projektantów tego modułu (Zeev Suraski i Andi
Gutmans). Firma oferuje usługi konsultingowe, dodatki i narzędzia dla PHP.

Zródlo. Patrz Kod źródłowy.


Skorowidz
$PHP_SELF, 352 bcpow, 194
%%identity, 396 bcscale, 194
.=,157 bcsqrt, 194
bcsub, 194
A BinDec, 184
abs, 1 82 blok, 80
acos, 187 boolean, 95
Active Server Pages, 29 break, 121, 126
addslashes, 171,404 buforowanie wyjścia, 436
algorytm mieszający, 529
alias, 501 C
ALTER, 319 ceil, 108, 182
analizator PHP, 477 CERT, 532
analizator SAX, 477 certyfikat, 525
analizator STG, 477 checkdate, 247
Apache, 30, 35 chop, 163
array, 202 chr, 108, 140
array _flip, 219 class_exists, 504
array keys, 2 1 8 clearstatcache, 241
array _pop, 2 1 7 closelog, 244
array _push, 217 ColdFusion, 29
array _re verse, 219 COM, 38, 508
array shift, 2 1 7 compact, 222
array _unshift, 2 1 7 continue, 126
array values, 2 1 8 cookie, 383, 416, 427
array walk, 214 pułapki, 435
arsort, 224 szyfrowanie, 527
asin, 187 usuwanie, 430
asort, 224 CORBA, 38, 508
atan, 187 cos, 187
atan2, 187 count, 207
atrybut klasy, 496 count chars, 166
CREATE, 318
B crypt, 521
base convert, 184 CSS, 473
bcadd, 194 current, 2 1 0
bcdiv, 194
BCmath, 61, 193 D
bcmod, 194 DCOM, 508
bcmul, 194 poprawka, 65
598 Dodatki

DecBin, 184 fopen, 236


DecHex, 184 for, 123
DecOct, 184 fputs, 239
default, 121 fread, 238
DELETE, 316 fsockopen, 245
deskryptor pliku, 236 func get arg, 147
die, 129,263,332 func get args, 147
DOM, 478 func_num_args, 146
DOMXML, 478 function, 137
double, 94 funkcja
format zapisu, 95 abs, 182
doubleval, 106, 178 acos, 187
do-while, 123 addslashes, 171,404
DROP, 319 argumenty, 134
DID, 472 argumenty domyślne, 145
dynamiczny JavaScript, 446 array^flip, 2 1 9
dziedziczenie, 103,497 array keys, 2 1 8
array _pop, 2 1 7
E array push, 2 1 7
each, 213 array _re verse, 219
easier date, 248 array shift, 2 1 7
caster days, 248 array unshift, 217
echo, 86 array _values, 218
egzemplarz, 102 array walk, 214
element arsort, 224
CHECKBOX, 358 asin, 187
RADIO, 359 asort, 224
SELECT, 359 atan, 187
TEXT, 356 atan2, 187
TEXTAREA, 356 base convert, 184
elseif, 1 1 8 bcadd, 194
end, 212 bcdiv, 194
ereg, 174 bcmod, 194
ereg replace, 175 bcmul, 194
eregi, 175 bcpow, 194
eregi replace, 175 bcscale, 194
escapeshellcmd, 522 bcsqrt, 194
exit, 129 bcsub, 194
exp, 186 BinDec, 184
expat, 481 ceil, 108, 182
Expat, 477 checkdate, 247
XML, 61 chop, 163
explode, 108, 168 chr, 108, 140
extract, 222 class exists, 504
clearstatcache, 241
F closelog, 244
FALSE, 96 compact, 222
fc lose, 241 cos, 187
feof, 241 count, 207
FIFO, 216 count chars, 166
file, 239 crypt, 52 1
file exists, 241 current, 210
filesize, 238, 241 DecBin, 184
floor, 108, 182 DecHex, 184
Skorowidz 599

funkcja (cd.) log, 186


DecOct, 184 loglO, 186
definicja, 137 Itrim, 163
die, 129, 263, 332 mail, 462
doubleval, 106, 178 max, 182
each, 213 mcrypt_ecb, 527
easter_date, 248 mcrypt get key size, 527
easier days, 248 mhash, 530
end, 212 microtime, 246
ereg, 174 min, 182
ereg replace, 1 75 mt getrandmax, 190
eregi, 175 mt_rand, 190
eregij-eplace, 175 mt_srand, 190
escapeshellcmd, 522 mysql affected rows, 327
exit, 129 mysql connect, 325
exp, 186 mysql_create_db, 333
explode, 108, 168 mysql data seek, 329
extract, 222 mysql drop db, 333
fclose, 241 mysql_fetch_pbject, 327
feof, 241 mysql fetch_row, 327
file, 239 mysql field name, 341
file exists, 241 mysql_field_type, 330
filesize, 238, 241 mysql_insert_id, 330, 396
floor, 108, 182 mysql num rows, 406
fopen, 236 mysql pconnect, 392
fputs, 239 mysql query, 326
fread, 238 mysql_result, 327
fsockopen, 245 mysql_select_db, 326
func get arg, 147 nagłówek, 135
func_get_args, 147 new xmldoc, 479
func num args, 146 next, 210
fwrite, 239 n!2br, 176
getjitml_translation_table, 176 OctDec, 184
getdate, 246 openlog, 244
getrandmax, 190 ord, 108, 140
gettype, 104 pfsockopen, 245
header, 437 pi, 186
HexDec, 184 pow, 186
htmlentities, 176 prev, 212
htmlspecialchars, 176 print_r, 502
implode, 108, 169 printf, 171
in array, 207 quotemeta, 171
include, 73 rand, 190
include once, 261 range, 204
intval, 106, 178 readfile, 239
is_array, 207, 503 require, 74
is_int, 503 reset, 211
is object, 504 round, 108, 182
IsSet, 83 rsort, 224
JDDayofWeek, 248 serialize, 505, 507
JDMonthName, 248 session decode, 421
key, 213 session destroy, 421
krsort, 224 session_encode, 42 1
ksort, 224 session id, 421
600 Dodatki

funkcja (cd.) xml parser get option, 484


session is registered, 421 xml_parser set option, 484
session module name, 421 xmldoc, 479
session_name, 421 xmldocfile, 479
session register, 417 xmltree, 479
session_save_path, 421 fwrite, 239
session start, 417
session unregister, 420 G
setcookie, 428 get html translation table, 176
settype, 106 getdate, 246
shuffle, 220 getrandmax, 190
sin, 187 gettype, 104
sizeof, 207 global, 140
sort, 224 Gnome xml, 478
split, 175 GROUP BY, 393
sprintf, 171
srand, 190 H
str replace, 163 header, 437
strcasecmp, 160 problemy, 439
strchr, 160 hermetyzacja, 103
strcmp, 115, 159 HexDec, 184
strcspn, 166 HTML Tiddy, 254
strftime, 247 htmlentities, 176
strip_tags, 176 htmlspecialchars, 176
stripslashes, 171, 404 httpd.conf
stristr, 160 plik, 64
strien, 157
strpos, 158 1
strrpos, 158 IDispatch, 509
strspn, 165 if, 117
strstr, 160 składnia alternatywna, 1 29
strtok, 167 IMAP, 33, 38, 61,456, 460, 461
strtolower, 169 IMP, 461
strtoupper, 170 implode, 108, 169
strval, 106 in array, 207
substr, 92, 161 include, 142
substr replace, 163 include once, 261
syslog, 244 INSERT, 315
system, 522 instancja, 102
tan, 1 87 instrukcja, 86
time, 246 break, 121, 126
trim, 163 continue, 126
u mysql fetch array, 327 default, 121
uasort, 224 else, 118
ucfirst, 170 elseif, 118
ucwords, 170 if, 117
uksort, 224 print, 86
unserialize, 505, 507 return, 138
unset, 84, 207 switch, 120
usort, 224 warunkowa, 1 1 7
var dump, 502 instrukcja PHP, 77
xml_parse, 484 integer, 93
xml_parser create, 484 format zapisu, 93
xml_parser free, 484
Skorowidz 601

interfejs loglO, 186


IDispatch, 509 Itrim, 163
intval, 1 06, 1 78
iPlanet Server, 35 M
is_array, 207, 503 maił, 462
is_int, 503 max, 182
is object, 504 mcrypt, 61
ISAPI Mcrypt, 526
moduł, 65 mcrypt ecb, 527
IsSet, 83 mcrypt_get_key_size, 527
metoda, 102,495
J GET, 226
Java Server Pages, 29 POST, 230
JavaScript, 441 mhash, 529
JDBC, 303 Microsoft Internet Information Server, 35
JDDayofWeek, 248 Microsoft Management Console, 62
JDMonthName, 248 microtime, 246
mieszanie, 529
K MIME, 471
key, 213 min, 182
klasa mt getrandmax, 190
xml-tree, 486 mt rand, 190
Klasa, 494 mt srand, 190
klauzula MTA, 456
GROUP BY, 393 MUA, 456
ORDER BY, 393 MySQL, 325
klient pocztowy, 456 mysql_affected_rows, 327
klucz mysql connect, 325
prywatny, 524 mysql_create_db, 333
publiczny, 524 mysql data seek, 329
klucz obcy, 306 mysql_drop_db, 333
kolejka, 215 mysql fetch array, 327
kolejność operatorów, 113, 115 mysql fetch object, 327
Kolokacja, 59 mysql fetch row, 327
komentarz, 80 mysql field name, 341
jednowierszowy, 81 mysql_field_type, 330
wielowierszowy, 81 mysql insert id, 330, 396
koniec wiersza, 100 mysql num rows, 406
konstrukcja mysql_pconnect, 392
array, 203 mysql_query, 326
list, 205 mysql result, 327
konstruktor, 497 mysql_select_db, 326
konwersja typów, 92, 105
krsort, 224 N
ksort, 224 nagłówek
Location, 437
L WWW-authenticate, 438
LDAP, 38 NAN, 291
liczby losowe, 190 Netscape Enterprise Server, 35
liczenie referencji, 500 new, 104
LIF0.215 new_xmldoc, 479
list, 205 next, 210
log, 186 n!2br, 176
602 Dodatki

0 pi, 186
plik
Obiekt, 494
/etc/services, 40 1
obiekty, 102
fopen-wrappers.c, 240
OctDec, 184
httpd.conf, 64
ODBC, 303
php.ini, 423, 462
opcja
srm.conf, 64
— enable-discard-path, 518
podzapytania, 3 1 5
— enable-mcrypt, 526
polimorfizm, 103
--enable-track-vars, 4 1 7
połączenie trwałe, 391
— enable-trans-sid, 417
POP, 456, 459
-with-mysql, 401
open source, 32
POP3, 38
pow, 186
openlog, 244
prev, 2 1 2
operator, 77
print, 86, 99
%, 178
printer, 502
*, 178
printf, 171
.=, 157
funkcja C, 87
/, 178
priorytety, 78
+, 178
procedura przechowywana, 34'
++, 179
profiler, 265
=, 159
przeciążanie, 499
===, 181
->, 104
przesłanianie, 498
AND, 112 Q
kropka, 156
logiczny, 1 12 quotemeta, 171
modulo, 178 R
NOT, 112
OR, 112 rand, 190
priorytet, 78 rangę, 204
trójskładnikowy, 116 readfile, 239
XOR, 112 rekurencja, 143
złączenia, 156 replikacja, 306
operatory require, 142
kolejność, 113, 115 reset, 2 1 1
porównania, 1 14 return, 138
skracanie, 1 1 3 round, 108, 182
Oracle, 33 RSA, 525
ord, 140 rsort, 224
ORDER BY, 393 S
P SAX, 477
parametry SELECT, 312
aktualne, 139 sendmail, 462
formalne, 139 serialize, 505, 507
pętla sesja, 4 1 3
do-while. 123 session decode, 42 1
for, 123 session destroy, 421
nieskończona, 128 session_encode, 421
while, 122 session_id, 42 1
pfsockopen, 245 session is registered, 421
PHPLIB, 417 session_module_name, 421
phpMyAdmin, 333 session name, 421
session_register, 417
Skorowidz 603

session_save _path, 42 1 T
session start, 4 1 7
tablica, 100
session_unregister, 420
SGLOBALS, 233
setcookie, 428 SHTTP COOKIE VARS, 200. 233, 434
settype, 106
SHTTP GET VARS, 233, 434
SGML, 469 $HTTP_POST_VARS, 233, 434
shuffle, 220
asocjacyjna, 101,200
sin, 187
indeksy, 101
sito Erastonesa, 266
wielowymiarowa, 206
sizeof, 207
tan, 187
SMTP, 456, 457
tautologia, 1 13
split, 175
time, 246
sprintf, 171
transakcja, 305
srand, 190
trim, 163
srm.conf
TRUE, 96
plik, 64
trwale połączenie, 391
SSL, 531
tryb HTML, 72
stała tryb otwarcia pliku, 236
M PI, 186
tryb PHP, 72
SID.417 typ
stała logiczna, 96, 1 1 2
boolean, 95
static, 141
double, 94
statyczny JavaScript, 445
integer, 93
stos, 215
obiektowy, 102
str_replace, 163
string, 97
strcasecmp, 160
tablicowy, 100
strchr, 160
typy
strcmp, 115, 159
rzutowanie, 177
strcspn, 166
strftime, 247 U
string, 97
uasort, 224
stripjags, 176
ucfirst, 170
stripslashes, 171, 404
ucwords, 170
stristr, 160
uksort, 224
strlen, 157
strpos, 158
unserialize, 505, 507
strrpos, 158
unset, 84, 207
strspn, 165
UPDATE, 316
uprawnienia, 320
strstr, 160
usort, 224
strtok, 167
strtolower, 169 V
strtoupper, 170
strval, 106 var dump, 502
substr. 92, 161 W
ujemne indeksy, 162
substr replace, 163 while, 122
suma kontrolna, 529 składnia alternatywna, 1 29
switch, 120 widok, 344
składnia alternatywna, 129 wielkość liter
syslog, 244 wrażliwość, 77
system, 522 wyrażenia regularne
szyfrowanie, 524 POSIX, 173
wysyłanie załączników, 464
604 Dodatki

X Zend.com, 30
złączenie, 313
XML, 37, 469
zmienna, 82
Attribute, 480
$PHP SELF, 352
Document, 480
SREMOTE HOST, 414
Expat, 6 1
$REMOTE_NAME, 414
Node, 480
nieprzypisana, 83
xml_parse, 484
zmienna globalna, 140
xml _parser_create, 484
zmienna statyczna, 141
xml_parser free, 484
znacznik
xmlj>arser_get_option, 484
<BR>, 88
xml_parser set option, 484
xmldoc, 479 <PRE>, 172,502
xmldocfile, 479
<SCRIPT>, 517
<STYLE>, 274
xmltree, 479
mailto, 464
XSL, 473
znacznik czasu, 245
Z znaczniki
ASP, 71
zagnieżdżanie, 80
kanoniczne, 70
załączniki
krótkie, 70
wysyłanie, 464
zaokrąglanie
kłopty, 96
księgarnia mternetowa http://www.helion.pl

0'Reilly

Innowacyjność i profesjonalizm są nierozerwalnie związane z dziedziną


informatyki. Jeśli spełniasz oba warunki, jesteś na najlepszej drodze do osiągnięcia
sukcesu. Dostęp do fachowej literatury znacznie ułatwi Ci poznanie najgłębszych
tajemnic technik programowania czy obsługi najbardziej zaawansowanych
programów komputerowych. Książki Wydawnictwa O'Reilly to źródło rzetelnej
wiedzy, podanej w prosty i przystępny sposób. Książki z charakterystycznymi
zwierzątkami umieszczonymi na białej okładce cechuje nie tylko bardzo wysoki
poziom merytoryczny, ale również przejrzystość i kompleksowość.

Wydawnictwo Helion
ul. Chopina 6, 44-100 Gliwice; '. '. skr. poczt. 462
»(32) 230-98-63, (32) 231-22-19; e-mail: helion@helion.pl
księgarnia internatowa http://www.helion.pl

Księga eksperta

ara

Już sama nazwa serii objaśnia treść tych książek. „Księgi eksperta" to prawdziwa skarb-
nica wiedzy. Znajdziesz w nich właściwie wszystko, co jest potrzebne użytkownikom
poszczególnych rodzajów oprogramowania. Już na pierwszy rzut oka można przekonać
się, że „Księgi eksperta" to solidne opracowania, których lektura stanowi kolejny krok
w świat profesjonalnych zastosowań oprogramowania komputerowego. Konkretne zagad-
nienia omówiono w nich w sposób dogłębny i nad wyraz wyczerpujący. Ich szczegółowość
dodatkowo podkreślają odpowiedzi na „z życia wzięte" pytania. Po nabyciu książki z tej
serii nie będziesz już musiał wertować kartek kilku innych książek - znajdziesz w niej
wszystko, co potrzebne.

Wydawnictwo Helion
ul. Chopina 6, 44-100 Gliwice; ' • ' skr. poczt. 462
8(32)230-98-63, (32) 231-22-19; e-mail: helion@helion.pl
księgarnia internatowa http://www.helion.pl

Vademecum profesjonalisty

Vademecum profesjonalisty to książki poruszające bardzo zaawansowane zagadnienia


praktyczne. Każde z nich jest opisywane w sposób szczegółowy i fachowy. Ich autorzy to
profesjonaliści w pełnym znaczeniu tego słowa, posiadający wieloletnie doświadczenie
nabyte w trakcie pracy w międzynarodowych firmach branży IT. Dzięki tym książkom
poznasz sekrety pracy najlepszych informatyków na świecie i zdobędziesz część ich
doświadczenia. Seria „Vademecum profesjonalisty" wykracza daleko poza przeciętne
dyskusje o technologii, oferuje praktyczne rady i szczegółowe rozwiązania wielu
problemów. To książki, które pomogą Ci rozwiązać wiele wątpliwości związanych z pracą
zawodową; dzięki nim zaoszczędzisz czas, jaki poświęciłbyś na poszukiwanie informacji
wśród współpracowników czy kolegów z branży

Wydawnictwo Helion
ul. Chopina 6, 44-100 Gliwice; l i skr. poczt. 462
8(32) 230-98-63, (32) 231-22-19; e-mail: helion@helion.pl

You might also like