You are on page 1of 390

Spis Treści

SPIS TREŚCI...................................................................................................................................................... 1
WSTĘP................................................................................................................................................................ 7
Dla kogo jest przeznaczona ta książka........................................................................................................... 7
Konwencje .................................................................................................................................................... 7
Omówienie książki ........................................................................................................................................ 7
Od autora ..................................................................................................................................................... 8
Przykłady kodu ............................................................................................................................................. 8
CZYM JEST PHP................................................................................................................................................. 8
DLACZEGO POWINIENEŚ UŻYĆ PHP .................................................................................................................... 9
GDZIE SZUKAĆ POMOCY ..................................................................................................................................... 9
PODZIĘKOWANIA ............................................................................................................................................. 10
O AUTORZE ..................................................................................................................................................... 10
ROZDZIAŁ 1. KOMPILACJA I INSTALOWANIE PHP............................................................................. 11
WSTĘP ............................................................................................................................................................ 11
POBIERANIE PHP............................................................................................................................................. 11
INSTALOWANIE WERSJI BINARNEJ ..................................................................................................................... 11
Binarna instalacja dla Windows.................................................................................................................. 11
Instalowanie PHP w postaci modułu ISAPI ............................................................................................. 12
Użycie PHP jako CGI ............................................................................................................................. 14
Inne instalacje binarne................................................................................................................................ 14
KOMPILOWANIE PHP....................................................................................................................................... 15
Kompilowanie PHP w Uniksach ................................................................................................................. 15
Kompilacja modułu CGI ......................................................................................................................... 15
Kompilacja PHP jako statycznie dołączanego modułu Apache ................................................................ 17
Kompilacja PHP do postaci dynamicznie ładowanego modułu Apache ................................................... 17
Podsumowanie kompilacji PHP w systemach Unix ................................................................................. 18
Kompilowanie PHP w środowisku Windows ............................................................................................... 18
Podsumowanie kompilacji PHP .................................................................................................................. 20
KONFIGUROWANIE PHP................................................................................................................................... 20
Korzystanie z pliku php.ini .......................................................................................................................... 20
Inne metody zmiany konfiguracji PHP ........................................................................................................ 21
PODSUMOWANIE .............................................................................................................................................. 22
ROZDZIAŁ 2. JĘZYK..................................................................................................................................... 23
WSTĘP ............................................................................................................................................................ 23
OGÓLNE INFORMACJE NA TEMAT SKŁADNI ........................................................................................................ 23
TYPY ............................................................................................................................................................... 24
Liczby — całkowite i zmiennoprzecinkowe .................................................................................................. 24
Ciągi........................................................................................................................................................... 24
Tablice........................................................................................................................................................ 25
ZMIENNE I STAŁE ............................................................................................................................................. 26
Zmienne predefiniowane ............................................................................................................................. 26
Zasięg zmiennych........................................................................................................................................ 30
Stałe............................................................................................................................................................ 31
OPERATORY I KOLEJNOŚĆ OPERATORÓW .......................................................................................................... 31
PROGRAMOWANIE PRZEPŁYWU STEROWANIA ................................................................................................... 32
if, else, elseif ............................................................................................................................................... 32
while ........................................................................................................................................................... 32
do .. while ................................................................................................................................................... 32
for............................................................................................................................................................... 33
foreach........................................................................................................................................................ 33
switch ......................................................................................................................................................... 33
break i continue .......................................................................................................................................... 35
include i require.......................................................................................................................................... 36
FUNKCJE ......................................................................................................................................................... 36
Klasy i programowanie obiektowe .............................................................................................................. 37
PORÓWNYWANIE WZORCÓW ............................................................................................................................ 39
Podsumowanie............................................................................................................................................ 39
ROZDZIAŁ 3. FORMULARZE I COOKIE................................................................................................... 40
WSTĘP ............................................................................................................................................................ 40
OBSŁUGA FORMULARZY W PHP ....................................................................................................................... 41
Skalarne i wielowartościowe elementy formularza ...................................................................................... 41
Alternatywne metody odczytywania wartości z formularza .......................................................................... 42
Użycie formularzy do przesyłania plików .................................................................................................... 45
Użycie rysunku jako przycisku wysłania danych.......................................................................................... 45
KONTROLA POPRAWNOŚCI DANYCH FORMULARZA ............................................................................................ 46
Kontrola danych za pomocą wyrażeń regularnych ...................................................................................... 46
Kontrola poprawności za pomocą sprawdzania typów ................................................................................ 47
Klasa Validator........................................................................................................................................... 48
COOKIE ........................................................................................................................................................... 49
WAŻNE ZAGADNIENIA PROGRAMOWANIA DLA WWW ...................................................................................... 50
Obsługa nieprawidłowych danych............................................................................................................... 50
Obsługa i formatowanie wyświetlanych danych .......................................................................................... 52
PODSUMOWANIE .............................................................................................................................................. 57
ROZDZIAŁ 4. OPERACJE NA PLIKACH.................................................................................................... 58
WSTĘP ............................................................................................................................................................ 58
ODCZYT I ZAPIS PLIKÓW ................................................................................................................................... 58
UŻYCIE GNIAZD ............................................................................................................................................... 59
UŻYCIE POTOKÓW ........................................................................................................................................... 60
KLASA FILE ..................................................................................................................................................... 61
PODSUMOWANIE .............................................................................................................................................. 61
ROZDZIAŁ 5. WYSYŁANIE PLIKÓW PRZEZ FORMULARZ................................................................. 62
WSTĘP ............................................................................................................................................................ 62
WYSYŁANIE POJEDYNCZEGO PLIKU .................................................................................................................. 62
PUŁAPKI .......................................................................................................................................................... 64
PRZESYŁANIE WIELU PLIKÓW ........................................................................................................................... 64
BEZPIECZEŃSTWO ............................................................................................................................................ 65
PODSUMOWANIE .............................................................................................................................................. 66
ROZDZIAŁ 6. WSPÓŁPRACA Z BAZAMI DANYCH ................................................................................ 67
WSTĘP ............................................................................................................................................................ 67
WPROWADZENIE .............................................................................................................................................. 67
FUNKCJE BAZ DANYCH ..................................................................................................................................... 67
MYSQL .......................................................................................................................................................... 68
Spis Treści 2
Rozpoczynamy pracę z MySQL.................................................................................................................... 68
Użycie MySQL ............................................................................................................................................ 68
ODBC............................................................................................................................................................. 71
Podstawy ODBC......................................................................................................................................... 71
Instalowanie i kompilowanie unixODBC ................................................................................................ 72
Kompilowanie PHP z obsługą unixODBC............................................................................................... 72
Instalowanie sterownika OOB................................................................................................................. 72
Konfigurowanie OOB ............................................................................................................................. 72
Korzystanie z ODBC ................................................................................................................................... 73
PHPLIB .......................................................................................................................................................... 74
PRZECHOWYWANIE DANYCH Z FORMULARZY .................................................................................................... 75
WYKORZYSTANIE MOŻLIWOŚCI BAZY DANYCH ................................................................................................. 77
PODSUMOWANIE .............................................................................................................................................. 78
ROZDZIAŁ 7. SESJE I STAN APLIKACJI................................................................................................... 80
WSTĘP ............................................................................................................................................................ 80
PODSTAWY MECHANIZMU SESJI ........................................................................................................................ 80
WBUDOWANY W PHP MECHANIZM ZARZĄDZANIA SESJAMI ............................................................................... 81
Rozpoczęcie pracy z sesjami w PHP............................................................................................................ 81
Przesyłanie identyfikatora sesji bez użycia cookie ....................................................................................... 83
Zapisywanie zmiennych sesji w bazie danych .............................................................................................. 85
Inne funkcje i opcje dotyczące sesji ............................................................................................................. 89
UŻYCIE PHPLIB DO OBSŁUGI SESJI .................................................................................................................. 90
TWORZENIE WŁASNEGO MECHANIZMU SESJI ..................................................................................................... 92
INŻYNIERIA PROGRAMOWANIA A SESJE ............................................................................................................. 92
PODSUMOWANIE .............................................................................................................................................. 94
ROZDZIAŁ 8. UWIERZYTELNIANIE ......................................................................................................... 95
WSTĘP ............................................................................................................................................................ 95
PODSTAWOWE UWIERZYTELNIANIE W APACHE ................................................................................................. 95
AKTUALIZACJA PLIKU .HTACCESS PRZY UŻYCIU PHP ........................................................................................ 97
PODSTAWOWE UWIERZYTELNIANIE ZA POMOCĄ PHP........................................................................................ 99
KOMPLETNY SYSTEM UWIERZYTELNIANIA OPARTY O PHP .............................................................................. 100
PODSUMOWANIE ............................................................................................................................................ 104
ROZDZIAŁ 9. NIEZALEŻNOŚĆ OD PRZEGLĄDARKI .......................................................................... 105
WSTĘP .......................................................................................................................................................... 105
ROZPOCZYNAMY ........................................................................................................................................... 105
WEWNĘTRZNE FUNKCJE PHP ......................................................................................................................... 106
Dodatkowe informacje na temat Browscap ............................................................................................... 106
BROWSERHAWK ............................................................................................................................................ 109
WYKORZYSTANIE DANYCH O PRZEGLĄDARCE................................................................................................. 113
PODSUMOWANIE ............................................................................................................................................ 114
ROZDZIAŁ 10. URUCHAMIANIE .............................................................................................................. 115
WSTĘP .......................................................................................................................................................... 115
INŻYNIERIA PROGRAMOWANIA A URUCHAMIANIE ........................................................................................... 115
Projekt aplikacji........................................................................................................................................ 115
Definiowanie standardów programowania................................................................................................ 116
Przegląd oprogramowania........................................................................................................................ 116
Testowanie................................................................................................................................................ 117
Uruchamianie ........................................................................................................................................... 117
PROGRAMOWANIE DEFENSYWNE .................................................................................................................... 118
WŁASNA OBSŁUGA BŁĘDÓW .......................................................................................................................... 122
3 PHP – Kompendium wiedzy
ZAAWANSOWANA OBSŁUGA BŁĘDÓW ............................................................................................................. 125
PODSUMOWANIE ............................................................................................................................................ 129
BIBLIOGRAFIA ............................................................................................................................................... 130
ROZDZIAŁ 11. PONOWNE WYKORZYSTANIE KODU ......................................................................... 131
WSTĘP .......................................................................................................................................................... 131
PONOWNE WYKORZYSTANIE KODU A INŻYNIERIA PROGRAMOWANIA ............................................................... 131
PONOWNE UŻYCIE ISTNIEJĄCEGO KODU .......................................................................................................... 132
PHP.......................................................................................................................................................... 132
C/C++...................................................................................................................................................... 133
Java .......................................................................................................................................................... 138
Dodawanie obsługi Javy w PHP na *niksach......................................................................................... 138
Dołączanie obsługi Javy w PHP dla Windows....................................................................................... 139
Opcje konfiguracji Javy......................................................................................................................... 139
COM......................................................................................................................................................... 141
Inne metody .............................................................................................................................................. 143
PODSUMOWANIE ............................................................................................................................................ 144
BIBLIOGRAFIA ............................................................................................................................................... 144
ROZDZIAŁ 12. ODDZIELANIE KODU HTML OD PHP.......................................................................... 145
WSTĘP .......................................................................................................................................................... 145
WPROWADZENIE ............................................................................................................................................ 145
ODDZIELENIE I INTEGRACJA PRZY UŻYCIU WBUDOWANYCH FUNKCJI PHP ....................................................... 146
Motywacja ................................................................................................................................................ 146
Implementacja .......................................................................................................................................... 147
Czego należy unikać.................................................................................................................................. 151
Podsumowanie: Oddzielanie i integracja przy wykorzystaniu funkcji PHP................................................ 151
WYKORZYSTANIE SYSTEMU SZABLONÓW ....................................................................................................... 152
FastTemplate ............................................................................................................................................ 152
Zaawansowane techniki użycia FastTemplate ........................................................................................... 157
PODSUMOWANIE ............................................................................................................................................ 159
BIBLIOGRAFIA ............................................................................................................................................... 159
ROZDZIAŁ 13. FAJNY PHP......................................................................................................................... 160
WSTĘP .......................................................................................................................................................... 160
WYSYŁANIE DO PRZEGLĄDARKI PLIKÓW INNYCH NIŻ HTML .......................................................................... 160
SKRYPTY AUTOMATYZUJĄCE ......................................................................................................................... 164
WDDX ......................................................................................................................................................... 168
MONITOROWANIE SIECI .................................................................................................................................. 172
PODSUMOWANIE ............................................................................................................................................ 174
ROZDZIAŁ 14. WITRYNY OPARTE O SZABLONY................................................................................ 175
PODSTAWY WYKORZYSTANIA SZABLONÓW .................................................................................................... 175
ZAPOŻYCZANIE.............................................................................................................................................. 183
PERSONALIZACJA WITRYNY ........................................................................................................................... 185
OBSŁUGA WIELU JĘZYKÓW............................................................................................................................. 187
PODSUMOWANIE ............................................................................................................................................ 189
ROZDZIAŁ 15. WITRYNY OPARTE O BAZĘ DANYCH......................................................................... 190
WSTĘP .......................................................................................................................................................... 190
PROJEKT BAZY DANYCH ................................................................................................................................. 190
ZARZĄDZANIE DANYMI APLIKACJI .................................................................................................................. 192
WYŚWIETLANIE DANYCH ............................................................................................................................... 199
PODSUMOWANIE ............................................................................................................................................ 204
Spis Treści 4
ROZDZIAŁ 16. GENEROWANIE STATYCZNYCH STRON HTML W OPARCIU O DYNAMICZNE
DANE .............................................................................................................................................................. 205
WSTĘP .......................................................................................................................................................... 205
KONCEPCJA ................................................................................................................................................... 205
GENEROWANIE STRON STATYCZNYCH ............................................................................................................ 205
Użycie buforowania .................................................................................................................................. 205
Użycie FastTemplate................................................................................................................................. 207
TECHNIKI BUFOROWANIA ............................................................................................................................... 208
PODSUMOWANIE ............................................................................................................................................ 210
ROZDZIAŁ 17. WITRYNY HANDLU ELEKTRONICZNEGO ................................................................ 211
WSTĘP .......................................................................................................................................................... 211
BEZPIECZEŃSTWO .......................................................................................................................................... 211
Zastosowanie SSL ..................................................................................................................................... 211
Certyfikaty ................................................................................................................................................ 211
Bezpieczeństwo bazy danych ..................................................................................................................... 212
PRZETWARZANIE PŁATNOŚCI .......................................................................................................................... 212
DOSTARCZANIE PRODUKTÓW ......................................................................................................................... 219
PODSUMOWANIE ............................................................................................................................................ 220
DODATEK A. FUNKCJE.............................................................................................................................. 221
DODATEK B. PREDEFINIOWANE ZMIENNE I STAŁE PHP ................................................................ 367
ZMIENNE ....................................................................................................................................................... 367
Zmienne Apache........................................................................................................................................ 367
Zmienne środowiska.................................................................................................................................. 369
Zmienne PHP............................................................................................................................................ 369
STAŁE ........................................................................................................................................................... 370
DODATEK C. OPCJE KOMPILACJI PHP................................................................................................. 372
BAZY DANYCH ............................................................................................................................................... 372
HANDEL ELEKTRONICZNY .............................................................................................................................. 374
GRAFIKA ....................................................................................................................................................... 374
RÓŻNE........................................................................................................................................................... 375
--enable-inline-optimization...................................................................................................................... 376
SIEĆ .............................................................................................................................................................. 379
DZIAŁANIE PHP ............................................................................................................................................ 379
SERWER ........................................................................................................................................................ 380
TEKST I JĘZYK ............................................................................................................................................... 380
XML............................................................................................................................................................. 381
DODATEK D. OPCJE KONFIGURACJI PHP............................................................................................ 382
OGÓLNE DYREKTYWY KONFIGURACJI ............................................................................................................. 382
DYREKTYWY KONFIGURACJI POCZTY ............................................................................................................. 385
DYREKTYWY KONFIGURACJI TRYBU BEZPIECZNEGO ........................................................................................ 385
DYREKTYWY KONFIGURACJI DEBUGGERA ....................................................................................................... 385
DYREKTYWY ŁADOWANIA ROZSZERZEŃ ......................................................................................................... 385
DYREKTYWY KONFIGURACJI MYSQL ............................................................................................................ 386
DYREKTYWY KONFIGURACJI M SQL ............................................................................................................... 386
DYREKTYWY KONFIGURACJI POSTGRESQL .................................................................................................... 386
DYREKTYWY KONFIGURACJI SYBASE ............................................................................................................. 387
DYREKTYWY KONFIGURACJI SYBASE-CT ....................................................................................................... 387
DYREKTYWY KONFIGURACJI INFORMIX .......................................................................................................... 388
DYREKTYWY KONFIGURACJI BC MATH .......................................................................................................... 389
5 PHP – Kompendium wiedzy
DYREKTYWY KONFIGURACJI MOŻLIWOŚCI PRZEGLĄDAREK ............................................................................. 389
DYREKTYWY KONFIGURACJI ZUNIFIKOWANEGO ODBC ................................................................................. 389
DODATEK E. ZASOBY SIECI..................................................................................................................... 390

Spis Treści 6
Wstęp
Książka ta jest przeznaczona dla programistów tworzących aplikacje WWW za pomocą PHP. Należy
zwrócić uwagę, że zostało użyte określenie aplikacje WWW a nie strony WWW lub witryny WWW. W
przeszłości w Sieci znajdowały się w większości proste strony HTML o ograniczonej możliwości interakcji.
Dzisiejszy obraz Sieci jest o wiele bardziej skomplikowany. Użytkownicy i firmy oczekują od Sieci coraz więcej.
Powoduje to powstanie coraz większej ilości dynamicznych aplikacji WWW. PHP jest idealny do tworzenia
takich aplikacji, ponieważ został zaprojektowany właśnie do realizacji tego zadania.

Dla kogo jest przeznaczona ta książka
Książka ta powinna być użyteczna dla szerokiego grona programistów WWW, ale pisana była z myślą o
średnio zaawansowanych lub zaawansowanych programistach. PHP jest językiem programowania a nie językiem
opisu strony, więc przydatne będzie doświadczenie w programowaniu. Programiści znający C lub Perla powinni
uznać PHP za język bardzo przyjazny, natomiast programiści pracujący w ASP Microsoftu (Active Server Pages)
uznają PHP za język o podobnej strukturze.
Ponieważ książka ta nie jest kierowana do początkujących programistów, podstawowe pojęcia dotyczące
programowania zostaną przedstawione bardzo skrótowo. Zakłada się, że Czytelnik zna takie pojęcia
programowania, jak funkcje, zmienne i stałe.

Konwencje
W książce przyjęto następujące konwencje:
• Kod programu i jego wyniki zaznaczone są czcionką o stałej szerokości.
• Nazwy plików i katalogów zaznaczone są czcionką pochyłą.
• Komendy i elementy języka zaznaczone są czcionką o stałej szerokości.

Omówienie książki
Książka zawiera zwięzłe wprowadzenie do PHP, oraz opis języka. Został w niej również przedstawiony
sposób instalacji i konfiguracji PHP.
Druga część, „Specjalne wymagania przy programowaniu WWW”, przeznaczona jest dla programistów
tradycyjnych aplikacji rozpoczynających pracę przy aplikacjach WWW. W części tej przedstawione zostały takie
zagadnienia jak: przetwarzanie formularzy, interakcję z użytkownikiem, utrzymywanie stanu oraz niezależność
od przeglądarki.
Następna część, „Zarządzanie projektem aplikacji WWW” opisuje zalety modularności i powtórnego
użycia kodu.
Część „Przykłady zastosowań” pokazuje użycie PHP na podstawie fragmentów działających już aplikacji.
Część ta łączy zagadnienia przedstawione w poprzednich częściach i pokazuje przykłady pełnej wymiany
informacji pomiędzy przeglądarką użytkownika i aplikacją zainstalowaną na serwerze WWW.
Na końcu książki znajduje się skorowidz zawierający wszystkie funkcje PHP4.
Od autora
Od około trzech lat tworzę aplikacje WWW przy użyciu PHP i ASP, jako niezależny przedsiębiorca.
Fundamentem mojego sukcesu było użycie PHP, ponieważ pozwala on na szybkie budowanie prototypów, oraz
jest wystarczająco wydajny i pewny nawet do tworzenia dużych aplikacji WWW.
Celem tej książki jest przekazanie innym programistów użytecznych informacji. Nie będę opisywał różnic
pomiędzy PHP i innymi tego typu narzędziami i nie będę zajmował się historią PHP. Wszystkie te informacje
można znaleźć na oficjalnej witrynie PHP, www.php.net. Zamiast tego pokażę zastosowanie PHP do stworzenia
aplikacji WWW. Omówię również inżynierię programowania w projektach WWW, sposoby przeniesienia
istniejącego kodu do nowych projektów WWW. Przedstawię również kilka przydatnych narzędzi napisanych dla
PHP.

Przykłady kodu
Zamieszczone przykłady kodu były tworzone i testowane pzy użyciu PHP 4.0.1 (poprawka 2) na Apache
1.3.11 działającym w systemie RedHat 6.1. Do edycji plików HTML i PHP wykorzystuję edytor Allaire Homeite
4.5.1 zainstalowany na Windows NT.
Do testowania małych fragmentów kodu stosowałem następujący szablon HTML do którego wklejałem
odpowiedni kod PHP:
<html>
<head>
<title>Nazwa przykładu</title>
</head>
<body>
<!-- Kod PHP wkleić poniżej -->
</body>
</html>
Większe fragmenty kodu były tworzone od razu razem z kodem HTML.

Czym jest PHP
PHP to język programowania przeznaczony dla programistów WWW pozwalający na szybkie tworzenie
dynamicznych aplikacji WWW. Oficjalnym rozwinięciem skrótu PHP jest „PHP: Hypertext Preprocessor”
(preprocesor hipertekstu). Jest to język programowania osadzany w HTML składniowo podobny do C, Perla i
Javy. Na wydruku 1. przedstawiony jest przykład kodu PHP.
Wydruk 1. Prosty przykład kodu PHP
<html>
<head>
<title>Prosty przykład kodu PHP</title>
</head>
<body>
<?php
echo "Witajcie w PHP!" ;
?>
</body>
</html>
Po uruchomieniu tego przykładu (poprzez odpowiednio skonfigurowany serwer WWW) generowany jest
kod HTML zamieszczony na wydruku 2.
Wydruk 2. Wynik działania wydruku 1
<html>
<head>
<title>Prosty przykład kodu PHP</title>
</head>
<body>
Witajcie w PHP! </body>
</html>
Preprocesor PHP wykonuje cały kod zawarty pomiędzy znacznikami <?php i ?> umieszczonymi w kodzie
HTML i zwraca wynik w postaci tekstu. Nie jest to szczególnie interesujący, ale pokazuje jak łatwo można
umieszczać kod PHP w kodzie HTML. Należy pamiętać, że kod ten jest wykonywany na serwerze WWW a nie
na kliencie. Oznacza to, że przeglądarka nie wie, że do stworzenia strony był używany PHP. Otrzymuje ona po
prostu strumień kodu HTML, identycznie jak w przypadku zwykłych stron. Więcej na ten temat znajduje się w
Wstęp 8
części „Specjalne wymagania przy programowaniu WWW”.

Dlaczego powinieneś użyć PHP
PHP jest pełnowartościowym językiem programowania pozwalający na tworzenie aplikacji WWW z
wszystkimi potrzebnymi funkcjami. PHP współpracuje z wieloma systemami baz danych. Pozwala to na bardzo
łatwe tworzenie aplikacji WW korzystających z informacji zapisanych w bazie danych. Możliwy jest również
dostęp do usług sieciowych takich jak IMAP, POP3, NNTP i TTP. Pozwala on również na otwieranie gniazd
sieciowych i podłączanie się do innych protokołów TCP/IP.
PHP może być użyty we wielu konfiguracjach serwerów. Ponieważ PHP jest rozprowadzany głównie w
postaci kodu źródłowego, może być skompilowany na wielu różnych platformach, na przykład na Linuksie,
FreeBSD i nawet na Windows. Dostępne są również binarne dystrybucje dla Win32.
PHP może działać jako program CGI lub może być zainstalowany jako moduł Apache lub rozszerzenie
ISAPI. Dzięki temu może on działać z praktycznie każdym serwerem WWW, od Apache na Linuksie do IIS na
Windows NT. W celu utworzenia najbardziej elastycznego środowiska pracy należy samodzielnie skompilować i
zainstalować PHP. Jeżeli wolisz szybko zacząć pracę, możesz zastosować binarną dystrybucję PHP.

Gdzie szukać pomocy
Pomoc można uzyskać na witrynie PHP oraz poprzez kilka grup dyskusyjnych i wysyłkowych. W lutym
2000 roku około 1400000 domen korzystało z PHP. Ponieważ jest on tak popularny, istnieje ogromna grupa
programistów i konsultantów, którzy mogą odpowiedzieć na pytania. Więcej informacji na temat dostępnych
zasobów Sieci znajduje się w części „Zasoby” na końcu książki.

9 PHP – Kompendium wiedzy
Podziękowania
Na początku chciałbym podziękować wszystkim z wydawnictwa McGraw-Hill za umożliwienie mi
zrealizowania tego zadania. Szczególne podziękowania należą się Rebece Young za wsparcie i pomoc w
technicznych aspektach pisania książki. Dziękuję Johnowi Steele, mojemu redaktorowi technicznemu, który
niezmiernie pomógł mi swoimi trafnymi uwagami i informacjami.
Oczywiście, należy podziękować całemu zespołowi tworzącemu PHP. Wiele osób z tego zespołu
pomagało mi w pracy nad książką. Cała część zawierająca skorowidz funkcji jest ich zasługą. To oni spędzili
setki lub tysiące godzin tworząc ten wspaniały język programowania i bogatą dokumentację.
Dziękuję Mattowi Wilson za umożliwienie mi wykorzystania w przykładzie do książki kodu MWeather.
Dziękuję Nickowi Bradbury za pozwolenie wykorzystania informacji i rysunków z edytora TopStyle.
Podziękowania dla Nate Weiss za pomoc przy użyciu deserializera WDDX dla JavaScript, dla Johna Kos za
pomoc przy unixODBC i EasySoft ODBC-ODBC Bridge. Dla Martina Evans, głównego programisty ODBC-
ODBC Bridge, za stworzenie tego świetnego produktu. Dziękuję Michaelowi Justin za pomoc przy konwerterze
RTF do HTML firmy Scrooge. Dziękuję również Michaelowi C. Battilana za pomoc przy Cloanto Currency
Server. Dla Sama Ockman za umożliwienie wykorzystania w książce rysunku serwera Penguin 1U. Dziękuję
Richardowi Litofski za pomoc przy BrowserHawk. Podziękowania dla Ali Ersheid za umożliwienie
wykorzystania w książce dokumentacji CyberCash. Dziękuję Josephowi Harris (znany jako CDI) za klasę
FastTemplate oraz inne fantastyczne narzędzia dostępne na witrynie The Webmasters.net.
Dziękuję rodzicom i braciom za nieustanne wsparcie nawet, gdy w latach osiemdziesiątych spędzałem
całe noce na pisaniu programów w Apple Basicu. Dziękuję pani Barton, pani Smith i panu Wakefield, moim
nauczycielom angielskiego z liceum, którzy mieli ogromny wpływ na moje pisanie. Dziękuję Garemu Rogers i
Jasonowi Wallin za wskazanie mi PHP i Linuksa w czasie gdy coraz bardziej pogrążałem się wykorzystując ASP
i Windows. Dziękuję Tracy Ard za pożyteczne komentarze do tej pracy oraz za jego niezmienną przyjaźń.
Na koniec najważniejsze podziękowania należą się mojej żonie i córce za umożliwienie mi zakończenia tej
pracy. Teraz znów możemy wieczorami chodzić skakać na trampolinie.

O autorze
Blake Schwendiman rozpoczął programowanie w 1980 roku rozpoczynając od Apple IIe i języka Basic.
Zdobył licencjat na uniwersytecie Arizona State University w roku 1994. W chwili obecnej Blake zarządza firmą
Intechra LLC, http://www.intechra.net/, firmą konsultingową specjalizującą się w oprogramowaniu, która ma
siedzibę w Rexburg, Idaho. Intechra LLC specjalizuje się w tworzeniu oprogramowania dla WWW. Blake ma
żonę Holy i trzyletnią córkę. Można się z nim skontaktować pod adresem blake@intechra.net.

Wstęp 10
Rozdział 1. Kompilacja i instalowanie
PHP
Wstęp
Zanim rozpoczniemy naukę języka PHP, należy go poprawnie zainstalować i skonfigurować w używanym
środowisku interpreter PHP. Ponieważ pakiet PHP może działać na wielu serwerach WWW i systemach
operacyjnych, w rozdziale tym znajdą się szczegółowe opisy instalacji na jedynie kilku platformach, ale podane
informacje są wystarczająco uniwersalne i mogą być wykorzystane przy konfigurowaniu środowiska pracy na
innych platformach.
W rozdziale tym opisane zostaną szczegółowo Apache dla Linuksa oraz IIS dla Windows NT. Są to często
spotykane konfiguracje serwerów WWW i są one na tyle różne, że ilustrują ogólne zasady instalacji pakietu PHP
na większości platform. Szczegółowe dane na temat określonej platformy można również znaleźć na witrynie
www.php.net.

Pobieranie PHP
Pierwszym krokiem do rozpoczęcia pracy z PHP jest zaopatrzenie się w kopię interpretera PHP. Na
witrynie www.php.net umieszczone jest kilka wariantów tego pakietu. Najnowsza wersja zawsze znajduje się na
górze listy. W przypadku serwerów uniksowych zaleca się pobranie pakietu zawierającego kompletny kod
źródłowy i przeprowadzenie samodzielnej kompilacji. Platformy uniksowe to między innymi Linux, BSD, Solaris
itp. W przypadku Windows zaleca się pobranie binarnej instalacji PHP.
Ze strony zawierającej pakiety instalacyjne można również pobrać poprzednie wersje programów,
dokumentację i narzędzia pomocnicze. Poprzednie wersje mogą być potrzebne, jeżeli masz już gdzieś
zainstalowane PHP i nie chcesz ryzykować niekompatybilności.

Instalowanie wersji binarnej
Po pobraniu binarnej dystrybucji PHP, instalacja jest banalna. Najczęściej binarna instalacja PHP jest
przeznaczona dla Windows. Ponieważ jednak niektóre dystrybucje Uniksa zawierają binarną dystrybucję PHP,
opisany zostanie również taki przypadek.

Binarna instalacja dla Windows
W PHP prawie wszystkie operacje można wykonać na kilka sposobów. Instalacja binarna dla Windows
zawiera zarówno wersję CGI (Common Gateway Interface) PHP, jak również wersję ISAPI. Jeżeli korzystasz z
serwera IIS (Internet Information Server) lub PWS (Personal Web Server) zalecane jest użycie modułu ISAPI.
Wersja CGI powoduje, że PHP jest uruchamiany za każdym odwołaniem do strony, więc jest mniej efektywny od
dynamicznego modułu jakim jest rozszerzenie ISAPI. Moduł ISAPI jest również ze swojej natury bezpieczniejszy
od programu CGI.
Instalowanie PHP w postaci modułu ISAPI
Jeżeli korzystasz z serwera IIS, PWS lub innego serwera WWW obsługującego moduły ISAPI,
najlepszym rozwiązaniem będzie użycie PHP w postaci modułu ISAPI. Aby zainstalować taką wersję, należy
skopiować pliki php4ts.dll i msvcrt.dll do katalogu systemowego Windows (zwykle \windows\system w Windows
95 lub \winnt\system32 w Windows NT). Są to współdzielone biblioteki niezbędne do prawidłowej pracy każdej
wersji PHP dla Windows. Dodatkowo można skopiować do katalogu systemowego inne pliki .dll, ale nie jest to
konieczne do ich użycia.
Następnie należy tak skonfigurować IIS lub PWS, aby korzystał z modułu ISAPI do obsługi plików php.
Serwer IIS można konfigurować za pomocą konsoli konfiguracji zainstalowanej w menu Option Pack. Na
rysunku 1.1. znajduje jest rozwinięte menu pokazujące położenie tego programu w Windows NT.
Rysunek 1.1.
Uruchamianie
aplikacji
konfigurującej IIS

Po uruchomieniu konsoli Menedżer usług internetowych należy kliknąć prawym przyciskiem myszy na
węźle serwera WWW (prawdopodobnie zatytułowany Domyślna witryna sieci Web) i wybrać Właściwości, tak
jak jest to pokazane na rysunku 1.2. Następnie w oknie Właściwości należy przejść na zakładkę Katalog
macierzysty i kliknąć przycisk Konfiguracja. Opcja ta pozwala na dodawanie i edycję skojarzeń.
Rysunek 1.2.
Konfigurowanie IIS

Teraz należy kliknąć przycisk Dodaj i wprowadzić potrzebne informacje. Na rysunku 1.3. pokazany jest
proces dodawania mapowania rozszerzenia phtml do modułu ISAPI PHP.

Rozdział 1 – Kompilacja i instalowanie PHP 12
Rysunek 1.3.
Dodawanie
mapowania dla
rozszerzenia w IIS

Po dodaniu mapowania zmiany są od razu widoczne w oknie dialogowym Konfiguracja aplikacji.
Czasami może być pożyteczne skojarzenie niektórych rozszerzeń z modułem ISAPI a niektórych z programem
CGI. Na rysunku 1.4. pokazana jest konfiguracja mojego serwera WWW. Na rysunku widać mapowania dla
PHP3, dla PHP4 jako CGI oraz PHP4 jako ISAPI. Jest to przydatne przy testowaniu różnic pomiędzy wersjami 3
i 4 PHP.
Rysunek 1.4.
Mapowanie
rozszerzeń PHP

Po zakończeniu konfiguracji należy ponownie uruchomić serwer WWW. Można to zrobić przy użyciu
modułu Usługi w Panelu sterowania, lub uruchamiając z linii poleceń następujące polecenia:
net stop iisadmin
net start w3svc
Po uruchomieniu serwera należy go przetestować tworząc prosty plik testowy, na przykład taki, jak
pokazany na wydruku 1.1. i otwierając go poprzez twój serwer. Jeżeli wszystko jest skonfigurowane poprawnie,
powinieneś zobaczyć ekran z informacjami na temat instalacji PHP.
Wydruk 1.1. Testowy skrypt PHP
<html>
<head>
<title> phpinfo() </title>
</head>
<body>
<?php
phpinho();
?>
</body>
</html>

13 PHP – Kompendium wiedzy
Trzeba pamiętać, że w pakiecie instalacyjnym PHP znajduje się uwaga na temat stanu modułu ISAPI która
ostrzega, że nie powinien być stosowany w środowisku produkcyjnym. Sugeruje się, że w celu zapewnienia
odpowiedniej stabilności powinna być użyta wersja CGI. Następna część rozdziału opisuje użycie PHP jako CGI.

Użycie PHP jako CGI
Jeżeli nie masz zainstalowanego serwera WWW obsługującego moduły ISAPI lub istnieją inne powody
wyboru wersji CGI, należy przeprowadzić instalację PHP jako CGI. Instalacja jest bardzo podobna do tej
przedstawionej powyżej, różnice występują jedynie przy mapowaniu rozszerzeń. Zamiast wybierać bibliotekę .dll
ISAPI, należy wybrać plik php.exe. Serwer IIS lub PWS wysyła parametry do pliku wykonywalnego CGI, więc
oprócz nazwy pliku wykonywalnego należy podać opcje linii poleceń %s %s. Jest to pokazane na rysunku 1.4. dla
rozszerzenia .php4.
Różne serwery WWW mają różne metody określenia mapowania rozszerzeń. Do Apache dla Windows
istnieje świetny podręcznik dostępny pod adresem www.php.net/manual/config-apache-nt.html. Zasoby sieci na
temat instalacji PHP na różnych serwerach WWW dla Windows można odszukać za pomocą wyszukiwarki
umieszczonej na witrynie www.php.net.

Inne instalacje binarne
Niektóre instalacje Uniksa posiadają instalację binarną PHP zintegrowaną z instalacją serwera WWW.
Niektórzy dostawcy, na przykład Red Hat, udostępniają również binarną instalację na swoich witrynach.
Instalacja taka jest wykonana w formie plików RPM (Red Hat Package Manager). Zaletą użycia plików RPM jest
łatwość instalacji. Nie trzeba martwić się szczegółami procesu kompilacji, ponieważ plik RPM zawiera gotową do
użycia odpowiednio skompilowaną wersję programu. Wadą jest to, że z powodu wielu możliwych wariantów
platform dla Uniksa, problemem dla początkujących może być nawet wybór właściwego pliku. Poza tym w pliku
RPM nie zawsze są ustawione wszystkie potrzebne opcje konfiguracji, niezbędne do prawidłowego działania
programu.
Jeżeli masz plik RPM z PHP, możesz go zainstalować wydając polecenie rpm -i <plikrpm.rpm>.
Powoduje to zainstalowanie plików binarnych do katalogów określonych przez twórcę pliku RPM. Dla
większości użytkowników katalogi te są prawidłowe. Po instalacji należy ręcznie skonfigurować serwer WWW
tak, aby korzystał z zainstalowanych właśnie plików binarnych PHP. Poniżej przedstawimy sposób konfiguracji
Apache. Inne serwery WWW wymagają przeprowadzenia podobnych czynności.
Niezależnie od tego, czy PHP będzie działało jako program CGI czy w postaci modułu, pierwszy krok
konfiguracji jest zawsze taki sam. Trzeba utworzyć skojarzenie pomiędzy rozszerzeniem pliku a wewnętrznym
typem stosowanym przez serwer. Robi się to dodając do pliku konfiguracyjnego następujące linie:
AddType application/x-httpd-php .php
AddType application/x-httpd-php .phtml
AddType application/x-httpd-php .inc
Dyrektywy te powodują, że Apache uważa wszystkie pliki z rozszerzeniami .php, .phtml i .inc jako pliki
typu application/x-httpd-php. Do przetwarzania tego typu plików wykorzystywane jest PHP.
Zakładając, że masz zainstalowane PHP w postaci modułu Apache, kolejnym krokiem będzie modyfikacja
pliku konfiguracyjnego httpd.conf tak, aby Apache załadował moduł PHP:
LoadModule php4_module libexec/libphp4.so
Jeżeli zainstalowana została wersja CGI, należy wprowadzić nieco inne zmiany do pliku konfiguracji
httpd.conf. Dyrektywa konfiguracji jest podobna do poprzedniej, ale odwołuje się do programu w postaci pliku
wykonywalnego.:
Action application/x-httpd-php /cgi-bin/php
Dyrektywa Action definiuje typ pliku powodujący uruchomienie PHP po otrzymaniu żądania ściągnięcia
strony z serwera WWW. Oczywiście należy podać właściwą ścieżkę do pliku wykonywalnego.
Po wprowadzeniu zmian należy ponownie uruchomić serwer WWW, aby zaczęły działać nowe
ustawienia. Serwer Apache można zrestartować za pomocą polecenia:
/ścieżka/do/apachectl restart
Aby przetestować konfigurację można wczytać za pomocą przeglądarki skrypt testowy, przedstawiony na
wydruku 1.1.
Binarna dystrybucja PHP ułatwia szybkie rozpoczęcie pracy z PHP w Uniksach, ale może ona sprawiać
problemy. Z powodu istnienia wielu wariantów Uniksów, znalezienie gotowej wersji działającej na określonym
Rozdział 1 – Kompilacja i instalowanie PHP 14
systemie może być czasami trudne. W wielu przypadkach będzie to bardziej czasochłonne niż ściągnięcie kodu
źródłowego, skompilowanie i zainstalowanie PHP. Proces ten zostanie opisany w kolejnej części rozdziału.

Kompilowanie PHP
Jeżeli chcesz skorzystać z elastyczności własnej instalacji PHP lub jeżeli przewidujesz dodawanie
własnych rozszerzeń do języka PHP (opisane w rozdziale 11. „Ponowne wykorzystanie kodu”), musisz dokładnie
poznać proces kompilowania PHP. Jeżeli pracujesz na platformie, dla której nie ma instalacji binarnej, możesz nie
mieć innego wyboru jak tylko samodzielnie kompilować PHP.

Kompilowanie PHP w Uniksach
W tej części rozdziału przedstawione zostaną informacje na temat kompilowania PHP na platformie Unix.
Należy pamiętać, że Unix określa całą rodzinę systemów, np.: Linux, BSD, Solaris i inne. Oczywiście systemy te
różnią się między sobą, ale wiele z kroków niezbędnych do kompilacji PHP jest identycznych. Więcej informacji
na temat określonej platformy można odszukać za pomocą wyszukiwarki dostępnej na witrynie www.php.net.
Dla każdej platformy istnieje kilka sposobów kompilacji PHP. Jeżeli serwerem WWW jest Apache, można
skompilować PHP jako plik wykonywalny, jako moduł ładowany dynamicznie lub jako statyczną bibliotekę.
Jeżeli nie korzystasz z Apache, należy odszukać w dokumentacji PHP i serwera WWW szczegóły postępowania.
Przy okazji możemy zarekomendować korzystanie z PHP w postaci dynamicznie ładowanego modułu, jako
najlepsze rozwiązanie dla większości aplikacji. Jeżeli PHP zostanie statycznie dołączony do Apache, każda
zmiana konfiguracji wymaga większego nakładu pracy. W przypadku modułu CGI, występują problemy z
bezpieczeństwem.
W kolejnych częściach zakładamy, że ściągnąłeś już źródła PHP i rozpakowałeś je. Proces kompilacji jest
właściwie taki sam dla każdego typu pliku wynikowego. Na początku trzeba uruchomić skrypt configure, który
ustawia opcje kompilacji. Następnie przy pomocy narzędzia make przeprowadza się kompilację. Na koniec trzeba
zainstalować gotowe PHP i zrestartować serwer WWW. Informacje na temat skryptu konfiguracyjnego są
przedstawione w części poświęconej kompilowaniu modułu CGI, więc zaleca się przeczytanie tego fragmentu
jako wprowadzenia.

Kompilacja modułu CGI
Kompilacja PHP do postaci wykonywalnego modułu CGI jest najprostszą metodą kompilacji i dobrym
rozwiązaniem, jeżeli nigdy wcześniej nie kompilowałeś programów dla Uniksa. Poniżej przedstawiona jest
kompletna lista operacji jakie należy wykonać. Niektóre z nich nie są obowiązkowe. Operacje opcjonalne są
zaznaczone czcionką pochyłą. Odwołanie do <php_dir> powinno być zamienione na nazwę twojego katalogu
bazowego PHP.
cd <php_dir>
rm config.cache
make clean
./configure
make
make install
Wykonanie tych operacji spowoduje usunięcie podręcznych danych konfiguracji, usunięcie plików
wynikowych a następnie skompilowanie PHP do postaci CGI. Jest to najprostsza metoda kompilacji, przytoczona
jedynie jako przykład. W prawdziwej kompilacji do skryptu konfiguracyjnego dołącza się opcje określające
atrybuty PHP.
Druga i trzecia linia jest nieobowiązkowa, ponieważ polecenia w nich umieszczone są używane jedynie do
wyczyszczenia poprzedniej konfiguracji i pozostałości po poprzedniej kompilacji. Jeżeli wcześniej nie
konfigurowałeś ani nie kompilowałeś PHP, nie są one potrzebne. Można również nie korzystać z tych opcji przy
kompilacji PHP, choć czasami ich wykonanie jest niezbędne. Jeżeli wprowadzane są poważne zmiany w
konfiguracji lub zmieniasz typ kompilacji z CGI na inny, może okazać się, że wykonanie czyszczenia jest
niezbędne, aby kompilacja przebiegła prawidłowo. W zależności od szybkości komputera, na którym
wykonywana jest kompilacja, przeprowadzenie całej konfiguracji i kompilacji może zająć dosyć dużo czasu.

15 PHP – Kompendium wiedzy
Pozostawienie zapisanych opcji konfiguracji oraz obiektów binarnych spowoduje znaczne skrócenie czasu
tworzenia PHP.
Wszyscy, którzy nigdy nie przeprowadzali takiego procesu powinni wiedzieć, że skrypt konfiguracyjny
poszukuje w systemie narzędzi, plików i innych danych systemowych. Następnie na podstawie tych danych
tworzy specyficzny dla systemu skrypt za pomocą można skompilować kod. Jeżeli w czasie działania skryptu
konfiguracyjnego nastąpi awaria, często zdarza się, że wymagany plik lub narzędzie nie jest odnajdywane lub
niewłaściwie skonfigurowane. Po zakończeniu działania skryptu konfiguracyjnego tworzony jest plik
tymczasowy config.cache zawierający szczegóły na temat systemu, więc badanie systemu nie musi być
powtarzane przy ponownym uruchomieniu konfiguracji. Jeżeli wprowadzisz duże zmiany do konfiguracji
systemu, musisz usunąć plik tymczasowy przed kolejnym uruchomieniem skryptu konfiguracyjnego. W ten
sposób upewniamy się, że zmiany te zostaną wykryte.
Po wykonaniu wszystkich podanych poleceń zostanie utworzony nowy plik wykonywalny — php. Można
przetestować poprawność kompilacji za pomocą następującego polecenia:
php < /dev/null
Jeżeli zobaczysz wynik podobny do poniżej przedstawionego, udało ci się poprawnie skompilować i
zainstalować PHP w postaci CGI.
X-Powered-By: PHP/4.0.2
Content-type: text/html
Trzeba zauważyć, że skompilowana właśnie wersja PHP nie posiada funkcji, które być może będziemy
chcieli wykorzystywać w aplikacjach, ponieważ została skompilowana z użyciem tylko ustawień domyślnych.
Trzeba ponownie uruchomić skrypt konfiguracyjny, ustawić opcje potrzebne w aplikacji a następnie ponownie
skompilować i zainstalować PHP.
Pożyteczną cechą zestawu domyślnych ustawień jest to, że dołączone jest do niego wiele często
używanych opcji konfiguracji, w tym obsługa bazy danych MySQL, sesji i wiele, wiele innych. Oznacza to, że
przytoczone polecenia umożliwiają skompilowanie PHP, który pozwala na rozpoczęcie nauki języka.
Jeżeli potrzebujesz obsługi innej bazy danych lub innego rozszerzenia, trzeba dodać odpowiednią opcję
konfiguracji. Lista dostępnych opcji jest wyświetlana po wpisaniu:
./configure --help
Większość opcji konfiguracji wpływających na dostępne funkcje PHP ma postać --enable-FUNKCJA lub --
with-PAKIET.Aby dodać funkcję do PHP należy użyć jednej z poniższych form:
--enable-FUNKCJA
--enable-FUNKCJA=yes
Aby usunąć funkcję z PHP, należy użyć:
--disable-FUNKCJA
--enable-FUNKCJA=no
Pełna lista opcji konfiguracji znajduje się w skorowidzu na końcu książki. Funkcje korzystające ze składni
--enable są to zwykle wbudowane opcje PHP, takie jak możliwość wykorzystywania krótkich znaczników lub
obsługa protokołu FTP. Pakiety są to zwykle moduły zewnętrzne, które mogą być dołączone do PHP, na przykład
obsługa bazy danych Oracle lub Javy. Te własności wymagają zwykle wskazania zewnętrznego pliku i do jego
włączania korzysta się z następującego zapisu:
--with-PAKIET=/ścieżka/do/pakietu
Aby wyłączyć pakiet należy użyć poleceń:
--with-PAKIET=no
--without-PAKIET
Jako przykład przedstawimy następującą konfigurację:
./configure --with-apxs=/www/bin/apxs --with-java --with-cybercash=/home/blake/mck-3.2.0.6-i586-pc-linux-
gnulibc2.1 --withunixODBC=/usr/local/unixODBC --disable-debug --enabletrack-vars -- enable-fin-funcs --with-
snmp=/home/blake/ucd-snmp-4.1.2 --enable-ucd-snmp-hack
Powyższe wywołanie konfiguracji powoduje dodanie do PHP obsługi Javy, CyberCash, SNMP (Simple
Network Management Protocol) oraz unixODBC. Nie zostało podane położenie katalogu Javy, więc skrypt
konfiguracyjny użyje domyślnej ścieżki do katalogu z tym pakietem. Dodatkowo została dodana opcja -with-
apxs, która powoduje, że PHP jest kompilowane do postaci dynamicznie ładowanego modułu Apache a nie jako
program typu CGI. Później omówimy to zagadnienie dokładniej. W przedstawianej konfiguracji wyłączono
informacje dla debuggera oraz włączono opcje track-vars, fin-funcs oraz ucd-snmp-hack. Opcja fin-funcs
powoduje dodanie własnego modułu rozszerzeń opisanego w dalszej części książki (rozdział 11.), natomiast
pozostałe są standardowymi elementami konfiguracji opisanymi w skorowidzu na końcu książki.

Rozdział 1 – Kompilacja i instalowanie PHP 16
Wiele z pakietów oprogramowania jakie chcemy dodać do PHP musi być osobno zainstalowane. Więcej
informacji na temat tego, gdzie można zaopatrzyć się w potrzebne pakiety, można znaleźć w dokumentacji na
witrynie www.php.net.
Po utworzeniu PHP w postaci CGI, należy skonfigurować serwer WWW do współpracy z nowym
programem. Aby skonfigurować serwer Apache należy dodać następujące dyrektywy do pliku httpd.conf:
AddType application/x-httpd-php .php
AddType application/x-httpd-php .phtml
AddType application/x-httpd-php .inc
Action application/x-httpd-php /cgi-bin/php
Pierwsze trzy dyrektywy definiują zawartość plików z rozszerzeniami php, phtml i inc jako typ
application/x-httpd-php. Ostatnia dyrektywa powoduje wysłanie wszystkich plików tego typu do pliku
wykonywalnego php. Zakładamy, że plik ten jest umieszczony w katalogu cgi-bin serwera WWW.
Dyrektywy te są minimum wymaganym do konfiguracji PHP w Apache, ale ta sama czynność może być
zrealizowana jeszcze na kilka sposobów. Więcej szczegółów można znaleźć w dokumentacji do Apache.

Kompilacja PHP jako statycznie dołączanego modułu Apache
Apache pozwala na statyczne dołączanie modułów bezpośrednio do pliku binarnego Apache. W
porównaniu z wersją CGI użycie modułu pozwala poprawić wydajność aplikacji oraz zwiększyć bezpieczeństwo
systemu,. Wadą tej metody jest konieczność powtórnej kompilacji Apache po każdej kompilacji PHP. Może być
to czasochłonne i frustrujące, ponieważ w przypadku wystąpienia kłopotów z konfiguracją PHP, Apache może
również przestać działać. Jednak niektóre aplikacje wymagają zastosowania statycznie dołączanego modułu
Apache, opiszemy teraz sposób jego tworzenia.
Przed skonfigurowaniem i skompilowaniem PHP niezbędne jest skonfigurowanie Apache. Zakładamy, że
na dysku jest już katalog z kodem źródłowym Apache. Aby skonfigurować Apache, należy użyć następujących
poleceń:
cd <apache_dir>
./configure
Po zakończeniu działania tego skryptu można zająć się konfigurowaniem i kompilowaniem PHP.
cd <php_dir>
./configure --with-apache=<apache_dir>
make
make install
Opcja --with-apache powoduje kompilację do postaci statycznej biblioteki oraz pozwala podać katalog z
plikami źródłowymi Apache. Następnie należy skompilować serwer Apache za pomocą poleceń:
cd <apache_dir>
./configure --prefix=/www --activate-module=src/modules/php4/libphp4.a
make
make install
Dyrektywa prefix może być inna w twoim systemie, ponieważ wskazuje ona katalog gdzie zostaną
zainstalowane pliki zależne od architektury. Teraz należy uruchomić serwer Apache i przy wykorzystaniu skryptu
testowego z wydruku 1.1. sprawdzić poprawność konfiguracji.
Aby Apache prawidłowo przetwarzał pliki PHP należy odpowiednio zmodyfikować plik httpd.conf. W
zależności od rozszerzeń jakie zostały wybrane do reprezentowania plików PHP, należy wprowadzić odpowiednie
zmiany. I tym razem standardowa konfiguracja wygląda następująco:
AddType application/x-httpd-php .php
AddType application/x-httpd-php .phtml
AddType application/x-httpd-php .inc
Przedstawiony opis przedstawia jedynie bardzo prostą wersję PHP, która zawiera jedynie opcje domyślne.
Więcej informacji o zmianie konfiguracji kompilacji PHP znajduje się w części na temat kompilacji wersji CGI.

Kompilacja PHP do postaci dynamicznie ładowanego modułu Apache
Sposób kompilacji PHP do postaci dynamicznie ładowanego modułu Apache nie różni się zbytnio od
innych przedstawionych do tej pory metod. Zaletą tej metody jest możliwość kompilacji PHP bez konieczności
równoczesnej kompilacji Apache. Również niektóre moduły rozszerzeń (na przykład Java) wymagają do
poprawnej pracy, aby PHP był skompilowany do postaci dynamicznie ładowanego modułu. Aby Apache
obsługiwał dynamicznie ładowane moduły należy go przekompilować z następującymi opcjami konfiguracji:
cd <apache_dir>
make clean
./configure --enable-module=so --enable-rule=SHARED_CORE --prefix=/www
make

17 PHP – Kompendium wiedzy
make install
Oprócz kompilacji Apache przedstawione polecenia przygotowują skrypt apxs, który jest niezbędny do
kompilacji dynamicznego modułu PHP. Jeżeli wystąpią kłopoty ze skryptem apxs można powtórnie wykonać
przedstawione polecenia, co spowoduje ponowne wygenerowanie prawidłowo skonfigurowanego skryptu. Po
skompilowaniu Apache z obsługą dynamicznie ładowanych modułów, należy skompilować PHP w następujący
sposób:
cd <php_dir>
make clean
rm config.cache
./configure --with-apxs=/www/bin/apxs (pozostałe opcje)
make
make install
Polecenia porządkujące są zalecane, jeżeli PHP był już kompilowany w innej konfiguracji. Ścieżka podana
w dyrektywie konfiguracji --with-apxs powinna być pełną ścieżką do skryptu apxs na serwerze.
Tak jak w przypadku poprzednich sposobów kompilacji należy prawidłowo skonfigurować Apache, aby
przetwarzał pliki PHP. Po zmodyfikowaniu konfiguracji należy uruchomić Apache i wywołać skrypt testowy.

Podsumowanie kompilacji PHP w systemach Unix
Celem tego fragmentu książki nie było podawanie szczegółowego i wyczerpującego opisu wszystkich
możliwych opcji konfiguracji, ale pokazanie podstawowych metod kompilowania PHP do różnych postaci. Jeżeli
nie kompilowałeś wcześniej PHP, powinieneś na początku spróbować skompilować podstawową konfigurację, a
później uzupełniać potrzebne opcje. Po zapoznaniu się z procesem kompilacji jest już bardzo łatwo testować
różne konfiguracje i dodawać niestandardowe rozszerzenia.
Po skompilowaniu PHP i sprawdzeniu, czy działa z Apache, można zapoznać się z opcjami konfiguracji,
które można ustawiać bez potrzeby ponownej kompilacji. Zostały one opisane w dalszej części rozdziału.

Kompilowanie PHP w środowisku Windows
Kompilowanie PHP dla Windows jest na początku bardziej skomplikowanym procesem niż kompilacja
PHP dla Uniksa. Dokumentacja zaleca użycie Visual C++ wersja 6, choć wersja 5 również powinna działać.
Próbowałem sprawdzić, czy można użyć pakietu Borland C++ Builder, ale nie udało mi się tego zrobić. Problem
stanowiły prekompilowane pliki lib, ponieważ Microsoft i Borland korzystają z różnych formatów tych plików.
Prawdopodobnie można zastosować kompilator Borlanda, ale trzeba wcześniej przekompilować wszystkie
biblioteki. W poniższym opisie zakładamy użycie Visual C++.
Przed rozpoczęciem pracy należy się zaopatrzyć w kilka programów i plików pomocniczych. Tabela 1.1.
zawiera wszystkie dodatkowe programy oraz adresy w Internecie, gdzie były dostępne w czasie pisania książki.
Tabela 1.1. Dodatkowe pliki pomocnicze i ich adresy w Sieci
Program Położenie
Kod źródłowy PHP www.php.net/download.php
Pakiet Cygwin http://sources.redhat.con/cygwin/
Narzędzia do kompilacji PHP dla www.php.net/extra/win32build.zip
Win32
Obsługa BCMath www.php.net/version4/downloads/num
ber.tar.gz
Zastępnik pliku resolv.lib www.php.net/version4/downloads/bind
lib_w32.zip
Pakiet Cygwin zawiera popularne narzędzia GNU, takie jak gcc, make i bison. Niektóre z tych programów
są wykorzystywane w procesie kompilacji, więc trzeba wcześniej zainstalować ten pakiet. Inne potrzebne pliki są
integralną częścią dystrybucji PHP. Kod źródłowy PHP jest identyczny jak ten, który jest używany do utworzenia
wersji dla Uniksa.
Potrzebny jest również program do rozpakowywania plików. Ja używam programu Winzip, ponieważ bez
problemu radzi sobie z plikami .tar.gz. Również inne programy posiadają takie możliwości. Na początku należy
zainstalować narzędzia Cygwin. Trzeba ręcznie dodać zmienną środowiska wskazując na położenie plików
Cygwin. Jeżeli pracujesz w Windows 95, trzeba dodać tą zmienną ręcznie do pliku autoexec.bat. W Windows NT
należy kliknąć prawym przyciskiem myszy ikonę Mój komputer i wybrać z menu Właściwości. Teraz trzeba
Rozdział 1 – Kompilacja i instalowanie PHP 18
kliknąć zakładkę Środowisko i dodać nową zmienną, tak jak jest to pokazane na rysunku 1.5. Zmienna nazywa się
CYGWIN a jej wartością jest ścieżka do katalogu, gdzie zainstalowane są narzędzia Cygwin.
Rysunek 1.5.
Ustawienie
zmiennej
środowiskowej
CYGWIN

Następnie utwórz katalog i rozpakuj do niego zawartość pliku win32build.zip. Uruchom Visual C++ i
wybierz Options z menu Tools. Teraz wybierz zakładkę Directories (rysunek 1.6.) i przy użyciu listy rozwijalnej
opisanej Show directories for, wybierz opcję Executable files i dodaj katalog z plikami Cygwin. Teraz z listy
rozwijalnej wybierz Include files i dodaj katalog z win32build\include (rysunek 1.6.). Na koniec wybierz Library
files i dodaj katalog win32build\lib. Od tej pory kompilator Visual C++ będzie mógł korzystać z zainstalowanych
narzędzi i plików.
Rysunek 1.6.
Ustawienie
katalogów w Visual
C++

Kolejnym krokiem będzie skompilowanie nowej wersji pliku resolv.lib. Najpierw utwórz nowy katalog i
rozpakuj do niego pliki z archiwum bindlib_w32.zip. W Visual C++ otwórz projekt bindlib.dsp. Z menu Build
wybierz Set Active Project Configuration i wybierz wersję handlową biblioteki lub wersję do uruchamiania.
Naciśnij klawisz F7, aby skompilować projekt. Po zakończeniu kompilacji należy skopiować plik resolv.lib do
katalogu win32build\lib.
Następnie rozpakuj źródła PHP i plik number.tar.gz za pomocą zewnętrznego programu lub narzędzia tar
z pakietu Cygwin. Skopiuj rozpakowane pliki number.c i number.h do katalogu ext/bcmath w katalogu z kodem
źródłowym PHP.

19 PHP – Kompendium wiedzy
Jeżeli wykonałeś wszystkie opisane wcześniej czynności, jesteś gotowy do kompilacji PHP. Uruchom
Visual C++ i otwórz plik projektu php4ts.dsp, znajdujący się w podkatalogu win32 katalogu z kodem źródłowym
PHP. Projekt ten zawiera kilka konfiguracji. Najłatwiej jest rozpocząć od skompilowania wersji CGI wybierając
wersję handlową lub wersję z danymi dla debuggera, tak jak jest to pokazane na rysunku 1.7.
Rysunek 1.7.
Wybór konfiguracji
dla wersji CGI

Skompiluj projekt i jeżeli wszystko pójdzie dobrze posiadasz już własną wersję PHP. Jeżeli potrzebujesz
wersji PHP jako ISAPI lub NSAPI, wystarczy wybrać odpowiednią konfigurację dla kompilacji i ponownie
skompilować projekt. Jak wspomniałem wcześniej, najtrudniejszą częścią było wstępne przygotowanie
środowiska. Gdy wszystko jest już gotowe, cała reszta jest tak samo prosta jak w Uniksie.

Podsumowanie kompilacji PHP
Kompilowanie wersji PHP dla Windows jest za pierwszym razem dużo trudniejsze od wersji dla Uniksa,
ale gdy wszystkie potrzebne dodatki zostaną odpowiednio skonfigurowane, jest już proste. Gdy poznałeś już
proces kompilowania PHP dla obu platform możesz tworzyć wysoce specjalizowane wersje PHP, spełniających
precyzyjnie potrzeby konkretnej witryny. Dokładna wiedza na temat procesu kompilacji PHP jest również
niezbędna, aby móc tworzyć rozszerzenia PHP. Zagadnienie to zostało opisane w rozdziale 11.

Konfigurowanie PHP
Niezależnie od platformy na której działa PHP, sposób jego konfigurowania jest taki sam. Wykorzystuje
się w tym celu plik php.ini. Plik ten jest dostarczany w dystrybucji PHP jako php.ini-dist i php.ini-optimized.
Jeżeli nie znasz dobrze opcji konfiguracji, powinieneś rozpocząć do podstawowych ustawień z pliku php.ini-dist.
Pierwszym krokiem będzie skopiowanie i zmiana nazwy pliku. Plik powinien być nazwany php.ini i skopiowany
do katalogu zależnego od używanej platformy. W tabeli 1.2. zamieszczone są podstawowe warianty.
Tabela 1.2. Platformy PHP i położenie pliku php.ini
Platforma Położenie pliku php.ini
Windows Katalog <windows> zwykle \windows w Windows 95 i \winnt w
Windows NT
Unix Można to sprawdzić za pomocą funkcji phpinfo(), ale zwykle jest
to /usr/local/lib.
Po umieszczeniu pliku konfiguracyjnego w odpowiednim katalogu, należy do niego wprowadzić
odpowiednie zmiany. Plik php.ini jest podzielony na sekcje, rozpoczynające się od linii [nazwa_sekcji] podobnie,
jak w standardowych plikach ini systemu Windows. Plik ten zawiera obszerne komentarze opisujące
przeznaczenie sekcji i opcji konfiguracji. W pozostałej części książki czasami będą przytaczane opcje niezbędne
do uruchomienia przykładów. Zwykle przykład taki zawiera nazwę sekcji, nazwę opcji oraz wartość. Aby
wprowadzić zmiany najczęściej zmienia się plik php.ini i ponownie uruchamia Apache, ale istnieją również inne
mechanizmy zmiany opcji. Mechanizmy te opisane zostaną w późniejszej części rozdziału.

Korzystanie z pliku php.ini
Zalecaną metodą zmiany konfiguracji jest modyfikacja pliku php.ini i ponowne uruchomienie serwera
WWW. Jeżeli korzystasz z PHP w postaci programu CGI nie musisz restartować serwera, ponieważ plik php.ini
jest odczytywany za każdym uruchomieniem programu CGI. Dla przykładu można zmienić sposób raportowania
błędów przez PHP, korzystając z odpowiednich opcji konfiguracji. Opcje te mają następujące wartości domyślne:
Rozdział 1 – Kompilacja i instalowanie PHP 20
error_reporting = E_ALL & ~E_NOTICE ; Pokaż wszystkie błędy oprócz informacji
display_errors = On ; Wypisuj błędy (jako część wynikowego HTML)
log_errors = Off ; Zapisuj błędy do pliku błędów
error_log = syslog ; Zapisuj błędy do dziennika systemowego
Pierwsza opcja powoduje generowanie komunikatów dla wszystkich typów błędów poza typem E_NOTICE.
Następna linia powoduje wstawianie komunikatów błędów do wynikowego kodu HTML. Następne dwa wiersze
powodują zapisywanie komunikatów błędów w pliku. Załóżmy, że w instalacji produkcyjnej nie chcemy
wyświetlać błędów, a zamiast tego błędy będą zapisywane do określonego pliku. Można to zrealizować
zmieniając konfigurację w następujący sposób:
error_reporting = E_ALL ; Pokaż wszystkie błędy
display_errors = Off ;
log_errors = On ; Zapisuj błędy
error_log = /tmp/php_log ; Zapisuj błędy do pliku /tmp/php_log
Taka konfiguracja powoduje, że wszystkie komunikaty błędów, w tym informacje, będą zapisywane w
pliku /tmp/php_log. Oczywiście plik ten powinien mieć odpowiednio ustawione prawa dostępu, aby serwer
WWW mógł zapisać w nim dane.
Z powodu dużej ilości opcji konfiguracji, nie zostaną tu przedstawione wszystkie możliwe opcje. Pełna
lista znajduje się w skorowidzu na końcu książki. Tutaj przedstawione zostaną jedynie ogólne sposoby
wykorzystywania tych opcji. Aby zmienić opcję konfiguracji, należy otworzyć w edytorze plik php.ini i odszukać
opcję. Zwykle znajduje się tam sporo komentarzy opisujących możliwe wartości danej opcji.

Inne metody zmiany konfiguracji PHP
Istnieją dwie metody zmiany konfiguracji PHP bez konieczności modyfikacji pliku php.ini. Pierwszym
sposobem jest wstawienie tych opcji do pliku konfiguracyjnego Apache httpd.conf lub do pliku .htaccess.
Pierwsza metoda jest użyteczna, jeżeli chcemy mieć różne ustawienia PHP dla różnych serwerów wirtualnych lub
różnych katalogów. Druga metoda jest wykorzystywana, gdy nie jest możliwy dostęp do plików php.ini i
httpd.conf. Jest to częsta sytuacja, witryna jest umieszczona na dzierżawionym serwerze zewnętrznej firmy. Jest
to jednak najmniej zalecana metoda, ponieważ plik .htaccess jest wczytywany i analizowany za każdym
odwołaniem do stron znajdujących się w tym katalogu. Powoduje to znaczne spowolnienie serwera WWW.
W obu tych przypadkach sposób zmiany konfiguracji PHP jest tak sam. Należy użyć dyrektyw
konfiguracji php_value i php_flag do ustawienia potrzebnych opcji. Na przykład, aby ustawić poprzednio opisane
opcje konfigurujące sposób raportowania błędów, należy użyć następujących dyrektyw Apache:
<VirtualHost 192.1.1.1>
ServerAdmin admin@server.net
DocumentRoot /www/hosts/wwwprojects/
ServerName www.testserver.com
php_value error_reporting 2047
php_flag display_errors off
php_flag log_errors on
php_value error_log /tmp/php_log
</VirtualHost>
Umieszczenie tych ustawień w pliku httpd.conf spowoduje, że zostanie ustawiony sposób raportowania
błędów dla serwera wirtualnego o nazwie www.testserver.com. Jeżeli na tej samej maszynie istnieją inne serwery
wirtualne, używają one konfiguracji określonej przez plik php.ini. Pozwala to na posiadanie różnych konfiguracji
PHP dla różnych serwerów wirtualnych lub katalogów.
Jeżeli musisz zmienić konfigurację PHP a nie masz dostępu do pliku php.ini ani do httpd.conf, możesz
wykorzystać pliki Apache .htaccess. Jest to również użyteczne, jeżeli określony katalog musi mieć inne
ustawienia konfiguracji niż reszta witryny. Na przykład, można zmienić sposób raportowania błędów dla jednego
katalogu na czas uruchamiania skryptów w nim się znajdujących. W tym celu należy stworzyć plik .htaccess z
następującą zawartością:
php_value error_reporting 2039
php_flag log_errors off
php_flag display_errors on
Należy zauważyć, że w obu przykładowych plikach konfiguracyjnych Apache wartość zmiennej
konfiguracji error_reporting jest ustawiana za pomocą wartości numerycznej a nie stałej. Jest o jedyny sposób
poprawnego ustawienia wartości. Należy pamiętać, że konfigurując PHP poprzez dyrektywy Apache nie można
używać jako wartości żadnych stałych PHP. W przeciwnym wypadku efekty mogą być niespodziewane.
Aby zilustrować potęgę dostępnego mechanizmu konfiguracji na rysunku 1.8. przedstawiony został
schemat możliwości konfiguracji środowiska PHP.
21 PHP – Kompendium wiedzy
Rysunek 1.8.
Elastyczność
konfiguracji z
zastosowaniem
php.ini oraz plików
konfiguracyjnych
Apache

Podsumowanie
W tym rozdziale przedstawiono kilka informacji niezbędnych do rozpoczęcia pracy z PHP. Z powodu
elastyczności i dużej ilości obsługiwanych platform niemożliwe jest szczegółowe opisanie wszystkich dostępnych
konfiguracji. Korzystając jednak z informacji umieszczonych w tej książce, oraz na witrynie www.php.net
powinieneś być w stanie zainstalować i skonfigurować PHP na twojej platformie.
Trzeba zauważyć, że PHP posiada wiele własnych funkcji zmieniających ustawienia konfiguracji.
Przykładami takich funkcji są error_reporting() oraz set_time_limit(). Więcej informacji na temat tych funkcji
można znaleźć w skorowidzu na końcu książki.

Rozdział 1 – Kompilacja i instalowanie PHP 22
Rozdział 2. Język
Wstęp
W rozdziale tym znajduje się zwięzły opis języka programowania PHP. Jak wspomniałem we wstępie do
książki nie jest moją intencją poświęcać zbyt wiele czasu na omawianiu ogólnych koncepcji programowania. W
tym rozdziale znajduje się opis składni podstawowych konstrukcji programowania, na przykład zmiennych,
stałych i funkcji. Przykłady przytoczone w tym rozdziale nie pokazują najlepszych technik programowania a
jedynie ilustrują składnię i użycie omawianych elementów. Pełny opis języka znajduje się w dokumentacji języka
dostępnej na witrynie http://www.php.net.

Ogólne informacje na temat składni
Ponieważ PHP jest zwykle wbudowywany w kod HTML istnieją specjalne znaczniki ograniczające bloki
PHP. Użycie tych znaczników jest nazywane czasem wyjściem z trybu HTML.
Wydruk 2.1. Sposoby oznaczania bloku kodu PHP w HTML
<? echo "użycie krótkich znaczników PHP do wyjścia z trybu HTML<br>"; ?>
<?php echo "wyjście przy użyciu pełnych znaczników PHP<br>"; ?>
<script language="php">
echo "niektóre edytory HTML nie obsługują instrukcji przetwarzania<br>";
</script>
<% echo "można stosować również znaczniki w stylu ASP<br>"; %>
Pierwsza metoda oznaczania bloków PHP jest dostępna jedynie wtedy, gdy uaktywnione są krótkie
znaczniki. Aby to zrobić należy użyć funkcji short_tags(), włączyć w pliku konfiguracyjnym opcję
short_tag_open lub skompilować PHP z opcją -enable-short-tags. Znaczniki w stylu ASP są dostępne jedynie
wtedy, gdy uaktywniona jest opcja konfiguracji asp_tags. Więcej informacji na temat kompilowania i
konfiguracji PHP znajduje się w rozdziałach „Kompilacja i instalowanie PHP” oraz dodatku D - „Opcje
konfiguracji”.
PHP jest syntaktycznie bardzo podobny do C. Na przykład, instrukcje są oddzielone średnikiem. Znacznik
?> jest niejawnym końcem instrukcji, więc poniższe przykłady są poprawne składniowo:
Wydruk 2.2. Koniec instrukcji
<?php
echo "Test, test...<br>";
?>
<?php
echo "Test, test...<br>"
?>
Komentarze w PHP można oznaczać symbolami komentarzy pochodzącymi z C, C++ lub stosowanych w
skryptach Uniksa. Komentarze jednoliniowe komentują tekst do końca linii lub do końca bieżącego bloku PHP w
zależności od tego, co będzie pierwsze. Nie można zagłębiać wielowierszowych komentarzy w stylu C.
Wydruk 2.3. Komentarze
<?php
echo "Witaj świecie!<br>"; // To jest jednowierszowy komentarz w stylu C++
/* To jest wielowierszowy
blok komentarza */
echo "Witamy ponownie.<br>"; # To jest komentarz w stylu skryptów Uniksa
?>
<?php
/* Poniższa linia spowoduje wypisanie "To wyświetli
nic."
*/
?>
To wyświetli <?php # echo "coś"; ?> nic.<br>
<?php
/*
echo "A tutaj mamy problem."; /* Komentarz ten jest
nieprawidłowy */
*/
?>

Typy
PHP posiada następujące typy: liczby zmiennoprzecinkowe, liczby całkowite, ciągi, tablice i obiekty. Typ
zmiennej jest ustalany w oparciu o kontekst w jakim jest użyta zmienna i nie jest on jawnie ustalany przez
programistę. Jest to ważna cecha o której należy pamiętać podczas programowania aplikacji PHP, ponieważ
niejawna konwersja typów może spowodować trudne do odnalezienia błędy. Na przykład poniższa instrukcja jest
prawidłowa i spowoduje wyświetlenie liczby 9:
print( 3* "3 małe świnki");
Aby można było zapanować nad typami, PHP posiada funkcje gettype() i settype() oraz kilka funkcji
przeznaczonych dla określonych typów, na przykład is_integer() lub is_array(). W skorowidzu funkcji na
końcu książki znajduje się pełne omówienie tych funkcji. Teraz zostanie opisany każdy z typów zmiennych
(oprócz obiektów). Obiekty PHP zostaną opisane w dalszej części rozdziału.

Liczby — całkowite i zmiennoprzecinkowe
Liczby całkowite można podawać używając notacji dziesiętnej, ósemkowej i szesnastkowej. Liczby
zmiennoprzecinkowe można podawać używając notacji zwykłej lub zapisu naukowego. Na poniższym wydruku
pokazana jest składnia PHP dla wszystkich tych notacji.
Wydruk 2.4. Reprezentacja liczb
<?php
$int1 = 523; // liczba dziesiętna
$int2 = -523; // dziesiętna ujemna
$int3 = 01013; // ósemkowa reprezentacja liczby 523
$int4 = 0x20B; // szesnastkowa reprezentacja liczby 523
$float1 = 523.197; // zwykły zapis liczby zmiennoprzecinkowej
$float2 = 5.23197e2; // notacja naukowa liczby zmiennoprzecinkowej
/* Wypisanie wszystkich liczb.
Wyświetla "523, -523, 523, 523, 523.197, 523.197". */
print( "$int1, $int2, $int3, $int4, $float1, $float2<br>" );
?>

Ciągi
Ciągi w PHP są ograniczane apostrofami (') lub cudzysłowami ("). Zapisy te różnią się sposobem
interpretacji ciągu. Jeżeli ciąg jest otoczony cudzysłowami, zmienne zapisane w ciągu zostają zamienione na ich
wartości. Aby zapisać znaki specjalne w ciągach otoczonych cudzysłowami, należy użyć znaku lewego ukośnika
(\), tak jak zostało to pokazane w tabeli 2.1.
Tabela 2.1. Znaki specjalne w ciągach otoczonych cudzysłowami
Sekwencja znaków Znaczenie
\n nowa linia
\r powrót karetki (CR)
\t tabulacja
\\ lewy ukośnik
\" cudzysłów
\$ znak dolara
W ciągach otoczonych apostrofami zmienne nie są zastępowane. Jedynymi dopuszczalnymi sekwencjami
sterującymi są te oznaczające lewy ukośnik (\\) i apostrof (\'). Sekwencje te pozwalają na wpisanie do ciągu
znaku apostrofu i lewego ukośnika. Ciągi mogą być łączone przy użyciu operatora kropki (.). Dokładniej jest to
opisane w części rozdziału na temat operatorów. Podobnie jak w języku C, mamy dostęp do poszczególnych
znaków ciągu, traktując go jak tablicę znaków.
Wydruk 2.5. Przykład operacji na ciągach
<?php

Rozdział 2 – Język 24
$aStr1 = "To jest zwykły ciąg.";
print( "$aStr1<br>" );
$aStr2 = "Thatcher";
print( "$aStr2<br>" );
$aStr3 = "Nazywam się $aStr2";
// $aStr3 = "Nazywam się Thatcher"
print( "$aStr3<br>" );
$aStr4 = "Nazywam się \$aStr2";
// $aStr4 = "Nazywam się $aStr2"
print( "$aStr4<br>" );
$aStr5 = 'Nie rozwijaj \'$aStr2\'';
// $aStr5 = "Nie rozwijaj '$aStr2'"
print( "$aStr5<br>" );
// wypisuje "Nazywam się Thatcher i Nazywam się $aStr2"
print( "$aStr3" . " i " . "$aStr4" );
?>
Z powodu ulotnej natury typów w PHP, zmienne mogą zmieniać swój typ w zależności od kontekstu w
jakim występują. Liczby mogą być konwertowane niejawnie na ciągi, jeżeli zostaną użyte jako argument
operatora operującego na ciągach. Ciągi mogą również zostać skonwertowane na liczby, jeżeli będą użyte w
wyrażeniach matematycznych. Jeżeli PHP próbuje skonwertować ciąg na liczbę, korzysta z następujących zasad:
• Jeżeli ciąg zaczyna się od danych numerycznych, zostaną one skonwertowane na liczbę.
• Jeżeli ciąg nie zaczyna się prawidłowymi danymi liczbowymi, wartością ciągu będzie zero (0).
• Jeżeli dane numeryczne zawierają jeden ze znaków .,e lub E, wartość będzie liczbą zmiennoprzecinkową
a w przeciwnym przypadku liczbą całkowitą.
Prawidłowymi danymi numerycznymi są: opcjonalny znak po którym następuje jedna lub więcej cyfr,
opcjonalna kropka dziesiętna oraz opcjonalny znak wykładnika. Znakiem wykładnika jest „e” lub „E”, po którym
następuje jedna lub więcej liczb.
Wydruk 2.6. Niejawna konwersja pomiędzy ciągiem i liczbą
<?php
$aVar = 123;
print( "\$aVar = $aVar, typ = " . gettype( $aVar ) . "<br>" );

$aVar2 = $aVar . " niejawnie skonwertowane do ciągu";
print( "\$aVar2 = $aVar2, typ = " . gettype( $aVar2 ) . "<br>" );

$aVar3 = $aVar2 + 1; // niejawna konwersja na liczbę całkowitą
print( "\$aVar3 = $aVar3, typ = " . gettype( $aVar3 ) . "<br>" );

$aVar3 = $aVar2 * 1.1; // niejawna konwersja na liczbę zmiennoprzecinkową
print( "\$aVar3 = $aVar3, typ = " . gettype( $aVar3 ) . "<br>" );

$aNotNumber = "abc";
$aVar4 = $aNotNumber * 1; // próba konwersji na liczbę, zwracane jest 0
print( "\$aVar4 = $aVar4, typ = " . gettype( $aVar4 ) . "<br>" );

$aIsNumber = "3 małe świnki";
$aVar5 = $aIsNumber + 1; // konwersja $aIsNumber na liczbę 3
print( "\$aVar5 = $aVar5, typ = " . gettype( $aVar5 ) . "<br>" );
?>

Tablice
Tablice w PHP zachowują się zarówno tak jak tablice indeksowane (wektory) oraz jak tablice mieszające
(asocjacyjne). PHP pozwala również na tworzenie tablic wielowymiarowych. Z powodu unikalnej konstrukcji
tablic w PHP, można indeksować jeden wymiar tablicy wielowymiarowej liczbami a inny w sposób asocjacyjny.
Tablice mogą być tworzone przy użyciu funkcji list() lub array() albo poprzez jawne podanie każdej z
wartości. W skorowidzu funkcji na końcu książki zostały opisane wszystkie funkcje do manipulacji tablicami.
Jednowymiarowe tablice mogą zamieniane w ciągach przez mechanizm zastępowania zmiennych na
wartości w sposób identyczny jak wszystkie inne zmienne. W przypadku tablic wielowymiarowych należy użyć
nawiasów klamrowych do zaznaczenia indeksów. Poniższy wydruk pokazuje przykłady użycia różnych typów
tablic.
Wydruk 2.7. Inicjowanie i użycie tablic
<?php
// Jawne tworzenie prostej tablicy
$a[0] = "Ryan";
$a[1] = "Scott";
$a[] = "Randall"; // jawne przypisanie do indeksu (klucza) 2
$a[] = "Sherie"; // jawne przypisanie do indeksu (klucza) 3

25 PHP – Kompendium wiedzy
print( "$a[3], $a[2], $a[1], $a[0]<br>" );

// Tworzenie tablicy asocjacyjnej
$color["niebieski"] = "#0000FF";
$color["zielony"] = "#00FF00";
$color["czerwony"] = "#FF0000";
print( "Wartość szesnastkowa koloru czerwonego wynosi {$color['czerwony']}<br>" );

// Tworzenie tej samej co poprzedniej tablicy asocjacyjnej
// tylko nieco prościej
$color = array( "niebieski" => "#0000FF",
"zielony" => "#00FF00",
"czerwony" => "#FF0000" );
print( "Wartość szesnastkowa koloru zielonego wynosi {$color['zielony']}<br>" );

// Ręczne tworzenie tablicy wielowymiarowej
$m[0][0] = "Zero Zero";
$m[0][1] = "Zero Jeden";
print( "Wartością \$m[0][1] jest {$m[0][1]}<br>" );

// Ręczne tworzenie asocjacyjnej tablicy wielowymiarowej
$counties["Idaho"][0] = "Ada";
$counties["Idaho"][1] = "Adams";
$counties["Idaho"][2] = "Bannock";
$counties["Arizona"][0] = "Apache";
$counties["Arizona"][1] = "Cochise";
$counties["Arizona"][2] = "Coconino";
print( "\$counties['Idaho'][0] = {$counties['Idaho'][0]}<br>" );
?>

Zmienne i stałe
Zmienne PHP są oznaczane znakiem dolara ($), po którym następuje nazwa zmiennej. Wielkość liter w
nazwach zmiennych jest rozróżniana. Prawidłowe nazwy zmiennych muszą zaczynać się literą lub znakiem
podkreślenia, po których może nastąpić litera, liczba lub znak podkreślenia. Prawidłowymi literami w zmiennych
są a-z, A-Z lub dowolne znaki ASCII z zakresu 127-255 (0x7f-0xff).
Wydruk 2.8. Nazwy zmiennych
<?php
$variable1 = "Ryan";
$variable2 = "Scott";
print( "$variable1, $variable2<br>" ); // wypisuje "Ryan, Scott"
$1variable = 123; // nieprawidłowa nazwa zmiennej
$_test = "test"; // prawidłowo, rozpoczyna się podkreśleniem
$_ąęć = "test2"; // prawidłowo
?>
Wartości mogą być przypisywane do zmiennych przez wartość lub przez referencję. Gdy przypisanie jest
realizowane przez wartość, obliczona wartość wyrażenia jest przepisywana do docelowej zmiennej. Po
przypisaniu zmienne są niezależne i zmiana wartości w jednej nie wpływa na wartość drugiej zmiennej.
Gdy wartości są przypisywane przez referencję, nowa zmienna staje się odwołaniem do oryginalnej
zmiennej. Zmiana wprowadzona do dowolnej zmiennej powoduje zmianę drugiej. Aby wykonać przypisanie
przez referencję, należy poprzedzić nazwę znakiem &.
Wydruk 2.9. Przypisywanie zmiennych
<?php
$variable1 = "Ryan";
$variable2 = $variable1; // przypisanie wartości
print( "$variable1, $variable2<br>" ); // wypisuje "Ryan, Ryan"
$variable2 = "Scott";
print( "$variable1, $variable2<br>" ); // wypisuje "Ryan, Scott"
$variable3 = &$variable1; // przypisanie przez referencję
print( "$variable1, $variable3<br>" ); // wypisuje "Ryan, Ryan"
$variable3 = "Katie";
print( "$variable1, $variable3<br>" ); // wypisuje "Katie, Katie"
?>

Zmienne predefiniowane
Oprócz zmiennych definiowanych przez użytkownika, w PHP istnieją zmienne tworzone przez system.
Lista tych zmiennych zależy od kontekstu wykonania skryptu (na przykład, czy jest uruchamiany samodzielnie,

Rozdział 2 – Język 26
czy poprzez serwer WWW), wersji PHP i typu serwera WWW. Ponieważ lista zmiennych jest zależna od wielu
czynników, niektóre z nich mogą nie być nigdy dostępne.
PHP generuje również zmienne dla cookie i danych formularzy przesyłanych za pomocą metod GET i
POST. Szczegółowe omówienie tych zmiennych zawarte jest w rozdziale 3 „Formularze i cookie”.
Część ta zawiera podzbiór dostępnych zmiennych dostępnych w czasie pracy PHP4 wraz z serwerem
Apache 1.3.11. Aby zobaczyć wszystkie zmienne dostępne w środowisku można użyć funkcji phpinfo().
Kompletniejsza lista predefiniowanych zmiennych znajduje się w skorowidzu na końcu książki. Tabela 2.2.
zawiera podzbiór zmiennych środowiska Apache, tabela 2.3., podzbiór zmiennych środowiska systemu a tabela
2.4. zawiera zmienne generowane przez PHP. W tabeli 2.5. zebrane są operatory arytmetyczne, natomiast
operatory bitowe w tabeli 2.6. Tabela 2.7. zawiera operatory porównania, tabela 2.8 operatory zwiększania i
zmniejszania a tabela 2.9. zawiera operatory logiczne. Ostatnia tabela, 2.10. zawiera operatory przypisania.
Tabela 2.2. Zmienne środowiska serwera Apache
Zmienna Definicja
HTTP_HOST Zawartość nagłówka Host: o ile został
wysłany przez przeglądarkę.
HTTP_USER_AGENT Zawartość nagłówka User Agent:
wysłanego przez przeglądarkę. Nagłówek ten
opisuje przeglądarkę żądającą strony, na
przykład: „Mozilla/4/0 (compatible; MSIE
5.01; Windows NT)”. Więcej na temat
wykorzystania tej zmiennej znajduje się w
rozdziale 9 „Niezależność od przeglądarki”.
REMOTE_ADDR Adres IP użytkownika oglądającego
stronę.
SERVER_PROTOCOL Nazwa i wersja protokołu za pomocą
którego zostało wysłane żądanie strony, na
przykład HTTP/1.1.
GATEWAY_INTERFACE Wersja specyfikacji CGI używanej
przez serwer, na przykład CGI/1.1.

Tabela 2.3. Zmienne środowiska systemu
Zmienna Definicja
HOSTNAME Nazwa komputera serwera.
HOSTTYPE Typ komputera, na przykład i386.
PATH Systemowa ścieżka serwera.
OSTYPE System operacyjny działający na
serwerze, na przykład Linux.

Tabela 2.4. Zmienne generowane przez PHP
Zmienna Definicja
PHP_SELF Nazwa pliku z wykonywanym
skryptem.
HTTP_COOKIE_VARS Tablica asocjacyjna zmiennych
przekazanych do skryptu poprzez cookie
HTTP.
HTTP_GET_VARS Tablica asocjacyjna zmiennych
przekazanych do skryptu za pomocą metody
GET.
HTTP_POST_VARS Tablica asocjacyjna zmiennych
przekazanych do skryptu za pomocą metody
POST.

27 PHP – Kompendium wiedzy
Tabela 2.5. Operatory arytmetyczne
Operator Nazwa Przykład Wynik
+ Dodawanie $a + $b Suma $a i $b
- Odejmowanie $a - $b Różnica $a i
$b
* Mnożenie $a * $b Iloczyn $a i $b
/ Dzielenie $a / $b Iloraz $a i $b
% Reszta z $a % $b Reszta z
dzielenia dzielenie $a przez $b

Tabela 2.6. Operatory bitowe
Operator Nazwa Przykład Wynik
& Iloczyn bitowy $a & $b Bity ustawione
w $a i $b są ustawione
| Suma bitowa $a | $b Bity ustawione
w $a lub $b są
ustawione
^ Różnica $a ^ $b Bity ustawione
symetryczna w $a lub $b, ale nie w
obu na raz są
ustawione
~ Negacja ~$a Bity ustawione
nie są teraz ustawione
i odwrotnie
<< Przesunięcie w $a << $b Przesunięcie
lewo bitów w $a w lewo o
$b kroków
>> Przesunięcie w $a >> $b Przesunięcie
prawo bitów w $a w prawo o
$b kroków

Tabela 2.7. Operatory porównania
Operator Nazwa Przykład Wynik
== Równy $a == $b True, jeżeli $a
jest równe $b
=== Identyczny $a === $b True, jeżeli $a
jest równe $b i są one
tych samych typów
!= Różny $a != $b True, jeżeli $a
jest różne od $b
< Mniejszy $a < $b True, jeżeli $a
jest mniejsze od $b
> Większy $a > $b True, jeżeli $a
jest większe od $b
<= Mniejszy lub $a <= $b True, jeżeli $a
równy jest mniejsze lub
równe $b
>= Większy lub $a >= $b True, jeżeli $a
równy jest większe lub
równe $b

Tabela 2.8. Operatory zwiększania i zmniejszania

Rozdział 2 – Język 28
Operator, przykład Nazwa Wynik
$a++ Postinkrementacja Zwraca $a, a następnie
zwiększa $a o jeden
++$a Preinkrementacja Zwiększa $a o jeden i
zwraca $a
$a-- Postdekrementacja Zwraca $a, a następnie
zmniejsza $a o jeden
--$a Predekrementacja Zmniejsza $a o jeden i
zwraca $a

Tabela 2.9. Operatory logiczne
Operator Nazwa Przykład Wynik
and Iloczyn $a and $b True,jeżeli $a
logiczny i $b mają wartość
True
or Suma logiczna $a or $b True,jeżeli $a
lub $b mają wartość
True
xor Różnica $a xor $b True,
jeżeli $a
symetryczna lub $b mają wartość
True, ale nie razem
! Negacja !$a True, jeżeli $a
nie jest True
&& Iloczyn $a && $b True, jeżeli $a
logiczny i $b mają wartość
True
|| Suma logiczna $a || $b True,jeżeli $a
lub $b mają wartość
True

Tabela 2.10. Operatory przypisania
Operator Przykład Wynik
= $a = $b Przypisuje wartość $b
do $a.
+= $a += $b Przypisuje wartość
($a+$b) do $a. Jest to
identyczne z $a=$a+$b.
-= $a -= $b Przypisuje wartość
($a-$b) do $a. Jest to
identyczne z $a=$a-$b.
*= $a *= $b Przypisuje wartość
($a*$b) do $a. Jest to
identyczne z $a=$a*$b.
/= $a /= $b Przypisuje wartość
($a/$b) do $a. Jest to
identyczne z $a=$a/$b.
.= $a .= $b Przypisuje wartość
($a.$b) do $a. Jest to
identyczne z $a=$a.$b.
%= $a %= $b Przypisuje wartość
($a%$b) do $a. Jest to
identyczne z $a=$a%$b.
|= $a |= $b Przypisuje wartość

29 PHP – Kompendium wiedzy
($a|$b) do $a. Jest to
identyczne z $a=$a|$b.
&= $a &= $b Przypisuje wartość
($a&$b) do $a. Jest to
identyczne z $a=$a&$b.
^= $a ^= $b Przypisuje wartość
($a^$b) do $a. Jest to
identyczne z $a=$a^$b.
<<= $a <<= $b Przypisuje wartość
($a<<$b) do $a. Jest to
identyczne z $a=$a<<$b.
>>= $a >>= $b Przypisuje wartość
($a>>$b) do $a. Jest to
identyczne z $a=$a>>$b.

Zasięg zmiennych
Ogólnie rzecz ujmując, zmienne globalne PHP mają taki sam zasięg. Rozciąga się on również na pliki
dołączane. Wewnątrz funkcji definiowanych przez użytkownika zmienne mają zasięg lokalny. Zmienne globalne
muszą być deklarowane jako globalne, aby mogły być wykorzystywane wewnątrz funkcji. PHP posiada również
zmienne statyczne, które deklarowane wewnątrz funkcji zapewniają utrzymywanie swojej wartości pomiędzy
kolejnymi wywołaniami funkcji.
Wydruk 2.10. Zasięg zmiennych
<?php
$aGlobal1 = "To jest test";
/*
Dołączamy inny plik z kodem PHP. POwyższa zmienna
$aGlobal1, będzie dostępna w dołączanym pliku.
*/
include( "example10_inc.php" );

function DoPrint( )
{
/*
Poniższa instrukcja wydrukuje tylko <br>
ponieważ zmienna $aGlobal1 wewnątrz funkcji
jest poza zasięgiem.
*/
print( "$aGlobal1<br>" );
}
DoPrint();

function DoPrint2( )
{
global $aGlobal1;
/*
Poniższa instrukcja wypisze wartość zmiennej
ponieważ została zadeklarowana jako globalna.
*/
print( "$aGlobal1<br>" );
}
DoPrint2();

function StaticFunc( )
{
static $aVal = 0;
print( "$aVal<br>" );
$aVal++;
}
// Poniższe wywołania spowodują wypisanie 0, a następnie 1
StaticFunc();
StaticFunc();

?>
--- Zawartość pliku example10_inc.php3 ---
<?php
print( "$aGlobal1<br>" );
?>

Rozdział 2 – Język 30
Stałe
PHP posiada kilka predefiniowanych stałych oraz pozwala na definiowanie własnych. Pełna lista stałych
znajduje się w skorowidzu na końcu książki. Aby zdefiniować nową stałą używa się funkcji define(). Zauważ, że
stałe PHP nie są makrami w stylu C i dlatego muszą być wartościami skalarnymi.
Wydruk 2.11. Stałe
<?php
define( "aString", "To jest stały ciąg znaków" );
define( "aNumber", 1 );

print( "Mamy tutaj zdefiniowane " . aNumber . " stałych.<br>" );
print( "Jej wartością jest '" . aString . "'<br>" );
?>

Operatory i kolejność operatorów
PHP posiada zestaw operatorów znanych programistom C i C++. W tabelach od 2.5. do 2.10.
zamieszczone zostało zestawienie dostępnych operatorów.
Oprócz operatorów umieszczonych w tabelach istnieje jeszcze kilka operatorów, ale są one trudniejsze do
klasyfikacji. Operator trójskładnikowy, zapisywany jako ?: jest dostępny zarówno w PHP jak i w C. Wyrażenie
$wart = (wyrażenie1) ? (wyrażenie2) : (wyrażenie3); przypisuje do zmiennej $wart wartość wyrażenie2, jeżeli
wyrażenie1 będzie miało wartość True, natomiast w przeciwnym przypadku $wart będzie miało wartość
wyrażenie3.
Operator wykonania oznaczany przez znak ` (na jednym klawiszu ze znakiem ~) jest podobny do
operatora dostępnego we wielu językach programowania powłoki. Wyrażenie otoczone znakami ` jest
wykonywane na serwerze a zwracana wartość przekazywana do zmiennej.
PHP posiada również operator kontroli błędów @. Gdy operator ten jest umieszczony przed wyrażeniem,
nie są generowane komunikaty błędów powodowanych przez to wyrażenie. Użycie tego operatora pozwala na
stworzenie lepszej obsługi błędów, o ile uaktywniona jest opcja track_errors. Gdy opcja ta jest aktywna,
komunikaty błędów zatrzymane przez operator @ są zapamiętywane w zmiennej globalnej $php_errormsg.
Zmienna ta jest nadpisywana przez kolejne błędy, więc aby kontrola błędów działała poprawnie, zmienna ta
powinna być sprawdzana możliwie szybko.
Wydruk 2.12. Niektóre działania z operatorami
<?php
$aNum1 = 1;
$aNum2 = 2;

$aVal = ( $aNum1 == $aNum2 ) ? "Wartości są równe" :
"Wartości są różne";
print( "$aVal<br>" ); // drukuje "Wartości są różne"

$aVal = ( 1 == "1" ) ? "Wartości są równe" :
"Wartości są różne";
print( "$aVal<br>" ); // drukuje "Wartości są równe"

$aVal = ( 1 === "1" ) ? "Wartości są identyczne" :
"Wartości nie są identyczne";
print( "$aVal<br>" ); // prints "Wartości nie są identyczne"

/*
Poniższy fragment powoduje przypisanie do $aListing
zawartości bieżącego katalogu serwera,
a następnie konwersję znaków nowej linii
na znaczniki <br> I wypisanie wyników
*/
$aListing = `ls -l`;
$aFmtList = nl2br( $aListing );
print( "<br>Zawartość katalogu:<br><b>$aFmtList</b><br>" );
?>

31 PHP – Kompendium wiedzy
Programowanie przepływu sterowania
PHP posiada standardowe instrukcje programowania przepływu sterowania takie jak if oraz pętle while i
for. Programiści C nie będą mieli kłopotu ze składnią tych instrukcji. Dodatkowo PHP posiada dwie funkcje
dołączania plików z kodem źródłowym: include() i require().

if, else, elseif
Jest to oczywiście najważniejszy element języka. Instrukcja if organizuje przepływ sterowania poprzez
tworzenie rozgałęzień na podstawie wyrażeń logicznych.
Wydruk 2.13. Przykład użycia if, else i elseif
<?php
if ( 1 < 2 )
print( "To zostanie wydrukowane.<br>" );
else
print( "To nie zostanie wydrukowane.<br>" );

$aValue = 2;
if ( $aValue == 1 )
{
// Używamy nawiasów klamrowych do otaczania bloków instrukcji
print( "\$aValue == 1<br>" );
}
elseif ( $aValue == 2 )
{
print( "\$aValue == 2<br>" );
}
elseif ( $aValue == 3 )
{
print( "\$aValue == 3<br>" );
}
else
{
print( "\$aValue nie jest 1, 2 ani 3<br>" );
}
?>

while
Jest to najprostszy typ pętli w PHP, która zachowuje się identycznie jak w C i innych językach wysokiego
poziomu.

do .. while
Mimo, że jest to pętla podobna do while, to w pętli do..while warunek pętli jest sprawdzany po
pierwszym przebiegu pętli. Gwarantuje to, że ciało pętli zostanie wykonane co najmniej raz.
Wydruk 2.14. Przykład użycia while i do..while
<?php
print( "Liczenie w górę przy użyciu <b>while</b>.<br>" );
$nIndex = 0;
// wypisuje liczby od 0 do 9
while ( $nIndex < 10 )
{
print( "$nIndex<br>" );
$nIndex++;
}

print( "Liczenie w dół przy użyciu <b>do..while</b>.<br>" );
// wypisuje liczby od 10 do 1
do
{
print( "$nIndex<br>" );
$nIndex--;
} while ( $nIndex > 0 );
?>

Rozdział 2 – Język 32
for
Pętla for jest najbardziej złożoną instrukcją pętli w PHP, ale jest ona składniowo identyczna z instrukcją
for w języku C. Jej składnia jest następująca:
for (wyr1; wyr2; wyr3) instrukcja
Wartość pierwszego wyrażenia (wyr1) jest obliczana raz, na początku pętli. Wartość drugiego (wyr2) jest
obliczana na początku każdego przebiegu pętli. Jeżeli będzie ono miało wartość True, pętla będzie się nadal
wykonywała i zostaną wykonane instrukcje ciała pętli. Jeżeli drugie wyrażenie jest puste, przyjmowane jest, że
ma ono wartość True. Na końcu każdego przebiegu pętli wykonywane jest trzecie wyrażenie (wyr3). Każde z tych
trzech wyrażeń może być puste.
Wydruk 2.15. Przykład użycia for
<?php
// Wypisuje liczby od 0 do 9
for ( $nIndex = 0; $nIndex < 10; $nIndex++ )
{
print( "$nIndex<br>" );
}

/*
$nIndex ma wartość 10. Pokażemy teraz, że
każde z trzech wyrażeń może zostać opuszczone.
Nie jest to zalecane ze względu na czytelność kodu.
Pętla powoduje wypisanie liczb od 10 do 1
*/
for ( ; $nIndex > 0; $nIndex-- )
{
print( "$nIndex<br>" );
}
?>

foreach
Wyrażenie foreach jest wygodnym sposobem na przeglądanie tablic. Podobne konstrukcje znajdują się w
VBScript, Perl i innych językach. PHP posiada dwa warianty składni:
foreach ( tablica as zmienna_wartosc) instrukcja
foreach ( tablica as zmienna_klucz => zmienna_wartosc) instrukcja
Pierwsza postać pętli przebiega po podanej tablicy i w każdym przebiegu wartość bieżącego elementu
tablicy jest przypisywana do zmiennej (zmienna_wartosc) a wskaźnik bieżącego elementu tablicy jest przesuwany.
Druga postać realizuje to samo, ale dodatkowo do zmiennej (zmienna_klucz) jest przypisywany klucz bieżącej
pozycji.
Wydruk 2.16. Przykład użycia foreach
<?php
$aArray = array( "Czerwony", "Zielony", "Niebieski" );
foreach( $aArray as $aValue )
{
print( "Bieżąca wartość to $aValue<br>" );
}

$aColorArray = array( "Czerwony" => "#FF0000",
"Zielony" => "#00FF00",
"Niebieski" => "#0000FF" );
foreach( $aColorArray as $aKey => $aValue )
{
print( "Wartość szesnastkowa $aKey to $aValue<br>" );
}
?>

switch
Instrukcja switch upraszcza tworzenie wielokrotnych warunków. Jest ona często używana zamiast
skomplikowanych konstrukcji if...elseif...else zawierających wiele wystąpień elseif. Składnia i
implementacja tej instrukcji jest identyczna jak w C. Korzystnym ulepszeniem w PHP jest możliwość używania
ciągów jako wyrażeń instrukcji switch.

33 PHP – Kompendium wiedzy
Programiści Delphi i Pascala mają zwykle kłopoty z zapamiętaniem, że w konstrukcji switch w C
występują instrukcje break. Czasami opuszczenie tej instrukcji jest wygodne. Poniższy przykład ilustruje częste
zastosowania instrukcji switch.
Wydruk 2.17. Przykłady użycia switch
<?php
$nIndex = 2;
// Najprostsza instrukcja switch
switch ( $nIndex )
{
case 0:
print( "zero<br>" );
break;
case 1:
print( "jeden<br>" );
break;
case 2:
print( "dwa<br>" );
break;
}

// Użycie frazy 'default'
$nIndex = 17;
switch ( $nIndex )
{
case 0:
print( "zero<br>" );
break;
case 1:
print( "jeden<br>" );
break;
case 2:
print( "dwa<br>" );
break;
default:
print( "Nie jest to zero, jeden ani dwa<br>" );
break;
}

// Switch z użyciem ciągu
$aColor = "niebieski";
switch( $aColor )
{
case "czerwony":
print( "#FF0000<br>" );
break;
case "zielony":
print( "#00FF00<br>" );
break;
case "niebieski":
print( "#0000FF<br>" );
break;
default:
print( "inny<br>" );
break;
}

/*
Opuszczenie instrukcji break spowoduje
wykonanie wszystkich wyrażeń po pasującej pozycji.
Jeżeli $nIndex jest 0, zostaną wykonane wszystkie
trzy instrukcje print. Jeżeli $nIndex jest 1, wykonane zostaną
ostatnie dwie instrukcje print.
*/
$nIndex = 0;
switch ( $nIndex )
{
case 0:
print( "zero<br>" );
case 1:
print( "jeden<br>" );
case 2:
print( "dwa<br>" );
}

/*
opuszczenie instrukcji break może być czasami przydatne
*/
$aColor = "Czerwony";
switch( $aColor )

Rozdział 2 – Język 34
{
case "czerwony":
case "Czerwony":
// Poniższa instrukcja zostanie wykonana, jeżeli $aColor
// będzie miał wartość "Czerwony" lub "czerwony"
print( "#FF0000<br>" );
break;
case "zielony":
case "Zielony":
print( "#00FF00<br>" );
break;
case "niebieski":
case "Niebieski":
print( "#0000FF<br>" );
break;
default:
print( "inny<br>" );
break;
}
?>

break i continue
PHP posiada również znane z C instrukcje break i continue, które pozwalają na dodatkowe sterowanie
pętlami. Obie te instrukcje pozwalają na podanie im parametru numerycznego, który określa ilość zagłębionych
pętli, które należy przerwać lub rozpocząć od początku.
Wyrażenie break kończy wykonanie bieżącej konstrukcji sterującej (pętli lub wyrażenia switch).
Wyrażenie continue jest używane jedynie w pętlach. Powoduje ono opuszczenie pozostałych instrukcji ciała pętli
i rozpoczęcie nowej iteracji.
Najczęściej instrukcje break i continue są stosowane w zagnieżdżonych pętlach. W przypadku pętli
prostych, wyrażenia warunkowe są wystarczające do realizacji tych zadań.
Wydruk 2.18. Przykłady użycia break i continue
<?php
$aArray = array( 4, 5, 15, 12, 7, 3, 20, 11, 31 );
$aCurMax = 17;
/*
Sprawdzamy, czy istnieje w tablicy wartość większa
od bieżącej wartości maksymalnej.
*/
foreach( $aArray as $aValue )
{
/*
Wyrażenie będzie prawdziwe, gdy osiągnięta zostanie wartość
20. Ponieważ wykonujemy instrukcję break,
nie sprawdzamy wartości które są w tablicy po wartości 20
*/
if ( $aValue > $aCurMax )
{
$aCurMax = $aValue;
break; // możemy napisać 'break 1;'
}
}
// wypisuje "Bieżącym maksimum jest 20"
print( " Bieżącym maksimum jest $aCurMax<br>" );

// wypisuje liczby nieparzyste od 0 do 20
$nIndex = 0;
for ( $nIndex = 0; $nIndex < 20; $nIndex++ )
{
if ( ( $nIndex % 2 ) == 0 )
continue; // opcjonalnie 'continue 1;'
print( "$nIndex<br>" );
}
?>
PHP osiada alternatywną składnię dla konstrukcji sterujących if, while, for i switch. W każdej z tych
konstrukcji otwierająca klamra jest zamieniona na dwukropek (:) a zamykająca klamra na odpowiednio endif,
endwhile, endfor i endswitch. Gdy tworzysz duże skrypty wbudowane w HTML, składnia alternatywna może być
użyteczna, ponieważ zapewnia wyraźną identyfikację końca struktur sterujących.
Wydruk 2.19. Przykład użycia alternatywnej składni PHP na stronie HTML
<html>
<head>
<title>Przykład 19</title>

35 PHP – Kompendium wiedzy
</head>

<body>
<!-- Używamy PHP do utworzenia listy opcji -->
<form action="someotherpage.phtml" method="post">
<table>
<tr>
<td>
Wybierz swój rok urudzenia:
</td>
<td>
<select name="BirthYear" size="1">
<?php
/*
Generujemy znaczniki dla lat 1920-2000
w odwrotnej kolejności
*/
$aCurYear = 2000;
while( $aCurYear >= 1920 ):
?>
<option value="<?php print( $aCurYear ); ?>">
<?php print( $aCurYear ); ?>
</option>
<?php
$aCurYear--;
endwhile;
/*
zakładając, że pomiędzy while i endwhile jest dużo więcej tekstu,
może być trudno znaleźć końcowy średnik,
jeżeli użyjemy zwykłej składni.
*/
?>
</select>
</td>
</tr>
</table>
</form>
</body>
</html>

include i require
PHP posiada dwa mechanizmy dołączania plików zewnętrznych: include() i require(). Wyrażenie
include() jest zwykłą funkcją PHP, natomiast require() jest konstrukcją językową, która posiada kilka
ograniczeń. W obu przypadkach po dołączeniu pliku PHP przechodzi do trybu HTML na początku dołączanego
pliku. Na końcu pliku analizator wraca do trybu PHP. Oznacza to, że dowolny kod zawarty w pliku dołączanym
musi być otoczony prawidłowymi znacznikami PHP.
Funkcja include() jest wykonywana za każdym jej wywołaniem i może znajdować się wewnątrz pętli lub
instrukcji warunkowych. Pozwala to warunkowo włączać pliki, lub włączać grupy plików przy pomocy
odpowiednio skonstruowanej pętli. Funkcja include() pozwala również, aby dołączany plik zwracał wartość,
którą można następnie przypisać do zmiennej. Przetwarzanie pliku w instrukcji include() kończy się, gdy
zostanie napotkana instrukcja return.
Wyrażenie require() różni się tym od include(), że nie wchodzi w skład konstrukcji sterujących.
Oznacza to, że pliki nie mogą być warunkowo dołączane za pomocą require(). Wyrażenie to jest wykonywane
raz, jeżeli znajduje się w pętli lub nawet, jeżeli znajduje się w instrukcji warunkowej, której warunek ma wartość
False. Inną różnicą jest to, że pliki dołączane za pomocą require() nie mogą zwracać wartości. Próba zwrócenia
wartości w wyrażeniu require() powoduje błąd składni.

Funkcje
PHP pozwala na tworzenie funkcji definiowanych przez użytkownika. Funkcje nie muszą być
deklarowane przed ich użyciem w kodzie PHP4. Funkcje w PHP mogą posiadać następujące cechy: zmienne
nazwy funkcji, zmienna liczba argumentów, argumenty domyślne i argumenty przekazywane przez referencję.
PHP pozwala na wykonywanie dowolnego kodu w ciele funkcji, włączając w to wywołania innych funkcji.
Zdolność ta pozwala również na tworzenie funkcji rekurencyjnych. PHP nie pozwala na przeciążanie funkcji, nie
ma również mechanizmu usuwania lub przedefiniowania wcześniej zdefiniowanych funkcji.
Rozdział 2 – Język 36
Domyślnie argumenty są przekazywane przez wartość. Aby przekazać argument przez referencję, należy
poprzedzić nazwę zmiennej znakiem &. Używając argumentów domyślnych, muszą być one umieszczone po
wszystkich argumentach obowiązkowych. W przypadku zmiennej listy argumentów, dostępne są funkcje
func_num_args(), func_get_arg() i func_get_args(), za pomocą których można pobrać dane przekazane jako
argumenty. Poniższe przykłady pokazują użycie funkcji w PHP.
Wydruk 2.20. Przykłady funkcji definiowanych przez użytkownika
<?php
// prosta funkcja
function ReturnSum( $a, $b )
{
return $a + $b;
}

// przekazanie argumentu przez referencję
function StringAppend( &$BaseString, $AddString )
{
// ponieważ jest to przekazane przez referencję, wartość
// $BaseString może być zmieniona poza tą funkcją
$BaseString .= $AddString;
}

// wartości domyślne
/*
Funkcja ta może być wywołana przy użyciu jednej z postaci:
PrintAnchorTag( "href", "text" );
PrintAnchorTag( "href", "text", "target" );
*/
function PrintAnchorTag( $aHREF, $aText, $aTarg = "" )
{
if ( $aTarg == "" )
{
print( "<a href=\"$aHREF\">$aText</a>" );
}
else
{
print( "<a href=\"$aHREF\" target=\"$aTarg\">$aText</a>" );
}
}

// zmienna lista argumentów
function PrintEverything( )
{
$aNumArgs = func_num_args();
for ( $nIndex = 0; $nIndex < $aNumArgs; $nIndex++ )
{
$aArgVal = func_get_arg( $nIndex );
print( "Argument $nIndex: $aArgVal<br>" );
}
}

print( "ReturnSum( 3, 5 ): " . ReturnSum( 3, 5 ) . "<br>" );

$aString = "Marysia miała ";
StringAppend( $aString, "małą owieczkę" );
print( "$aString<br>" ); // wypisuje "Marysia miała małą owieczkę"

PrintAnchorTag( "example10.phtml",
"Zobaczmy jeszcze raz przykład 10" );
print( "<br>" );
PrintAnchorTag( "example10.phtml",
"Zobaczmy jeszcze raz przykład 10 w nowym oknie",
"_blank" );
print( "<br>" );

print( "Wywołanie PrintEverything( 1, 2, 3, 4, 5 ):<br>" );
PrintEverything( 1, 2, 3, 4, 5 );
?>

Klasy i programowanie obiektowe
PHP posiada zdolność tworzenia klas za pomocą składni podobnej jak w C++. PHP posiada również
bardzo prostą implementację programowania obiektowego, która jest jednak wystarczająca dla większości
aplikacji WWW. Dostępne jest dziedziczenie jednobazowe, nie ma dziedziczenia wielobazowego. Istnieją
konstruktory klas, ale nie ma destruktorów. PHP posiada (i wymaga używania) wskaźnik $this, który jest
37 PHP – Kompendium wiedzy
stosowany do odwoływania się do metod i zmiennych obiektu. Poniższy przykład pokazuje tworzenie prostej
klasy. Więcej przykładów na ten temat znajdzie się w późniejszych rozdziałach książki.
Wydruk 2.21. Przykłady użycia klas w PHP
<?php
// tworzenie prostej klasy
class ShoppingBasket
{
var $fItems;
var $fCurValue;

/*
jest to konstruktor klasy,
ponieważ ma taką samą nazwę jak klasa.
Tak samo jak w C++ konstruktor może posiadać argumenty
W tym przypadku jest to początkowa wartość koszyka.
Może być to stała prowizja lub rabat..
*/
function ShoppingBasket( $aInitialValue = 0.0 )
{
$this->fCurValue = $aInitialValue;
}

// Dodanie określonej ilości przedmiotów
function AddItem( $aName, $aValue, $aQuantity = 1 )
{
$this->fItems[$aName]["Quantity"] += $aQuantity;
$this->fItems[$aName]["Value"] = $aValue;
$this->fCurValue += $aValue * $aQuantity;

return True;
}

function RemoveItem( $aName, $aQuantity = 1 )
{
// Usuwamy określoną ilość przedmiotów
// jedynie, gdy była dostępna wystarczająca ich ilość
if ( $this->fItems[$aName]["Quantity"] > $aQuantity )
{
$this->fItems[$aName]["Quantity"] -= $aQuantity;
$this->fCurValue -= $this->fItems[$aName]["Value"] *
$aQuantity;
}
else
{
return False;
}
}

function PrintBasket( )
{
if ( count( $this->fItems ) > 0 )
{
print( "Zawartość koszyka:<blockquote>" );
foreach( $this->fItems as $aKey => $aValue )
{
print( "{$aValue['Quantity']} $aKey<br>" );
}
print( "Wartość całkowita: $" .
number_format( $this->fCurValue, 2 ) );
print( "</blockquote>" );
print( "<br>" );
}
else
{
print( "<i>Koszyk jest pusty</i><br><br>" );
}
}
}

/*
Tworzenie nowego obiektu ShoppingBasket. Dodanie kilku przedmiotów
usunięcie kilku przedmiotów i wypisanie zawartości koszyka
*/
$aBasket = new ShoppingBasket( 3.50 );
$aBasket->PrintBasket();
$aBasket->AddItem( "gizmo", 1.50 ); // dodanie 1 gizmo
$aBasket->PrintBasket();
$aBasket->AddItem( "foobar", 2.10, 6 ); // dodanie 6 foobarów
$aBasket->PrintBasket();
$aBasket->RemoveItem( "foobar", 15 );
Rozdział 2 – Język 38
$aBasket->PrintBasket();
$aBasket->RemoveItem( "foobar", 3 );
$aBasket->PrintBasket();
?>

Porównywanie wzorców
PHP posiada dwa typy funkcji do porównywania wzorców (lub wyrażeń regularnych). Pierwszy typ jest
zgodny ze specyfikacją POSIX i są to funkcje ereg(), eregi(), ereg_replace(), eregi_replace() oraz split().
Każda z tych funkcji jako pierwszego argumentu wymaga wyrażenia regularnego. PHP korzysta z rozszerzonych
wyrażeń regularnych zdefiniowanych przez POSIX 1003.2. PHP zawiera w katalogu regex strony podręcznika,
które w pełni opisują wyrażenia regularne POSIX.
Drugi typ funkcji porównywania wzorców jest zgodny z wyrażeniami regularnymi Perl. Nazwy tych
funkcji są poprzedzone ciągiem preg_. Pełna lista tych funkcji znajduje się w skorowidzu na końcu książki.
Składnia tych wyrażeń jest taka sama jak w Perl 5 z kilkoma różnicami. Bieżąca implementacja tych funkcji
odpowiada Perl 5.005. Różnica pomiędzy implementacją w Perl 5.005 i w PHP jest dokładnie opisana w
dokumentacji PHP dostępnej z witryny http://www.php.net.

Podsumowanie
Rozdział ten jest zwięzłym opisem języka PHP i nie zawiera szczegółowo opisanych podstaw
programowania. Dlatego nie zawiera on dyskusji na temat tego kiedy lub dlaczego należy używać określonych
konstrukcji. Zamieszczone zostały za to przykłady ilustrujące składnię i dostępne funkcje. PHP zawiera wszystkie
własności potrzebne do tworzenia złożonych i łatwych do zarządzania aplikacji WWW. Język jest wystarczająco
sprawny do realizacji większości zadań, ale jest przygotowany do tworzenia aplikacji dla WWW, co zostanie
pokazane w kolejnych rozdziałach.

39 PHP – Kompendium wiedzy
Rozdział 3. Formularze i cookie
Wstęp
W czasie tworzenia dowolnego typu aplikacji utworzenie dobrego mechanizmu interakcji z
użytkownikiem jest jednym z najważniejszych zadań programisty. HTML posiada elementy formularzy, które są
używane do zbierania danych od użytkownika, natomiast PHP zapewnia prosty mechanizm przetwarzania tych
formularzy. Ponieważ PHP został zaprojektowany jako język programowania dla WWW, obsługuje on
automatycznie wiele szczegółów przetwarzania formularzy. Rozdział ten zawiera informacje nie tylko na temat
sposobu użycia formularzy HTML w PHP, ale również na temat kontroli poprawności i przetwarzania danych
formularza. Dla programistów, którzy przechodzą od pisania zwykłych aplikacji do tworzenia aplikacji WWW
przeznaczona jest część zatytułowana „Ważne zagadnienia programowania dla WWW”, która sygnalizuje
niektóre problemy jakie powstają gdy jako urządzenie wyjściowe używana jest przeglądarka WWW. W rozdziale
tym znajduje się również omówienie mechanizmu cookie, ponieważ jest ono składniowo podobne do obsługi
elementów formularzy. Cookie mogą również pomóc w zrealizowaniu mechanizmu utrzymywania stanu, który
jest zwykle potrzebny w czasie dialogu z użytkownikiem.
Konwencje nazw plików

We wszystkich przykładach oraz w mojej aktualnej pracy do oznaczania skryptów PHP które generuj ą strony
HTML używam rozszerzenia .phtml oraz rozszerzeń .php lub .php3 do plików dołączanych. Nie używam
najczęściej używanych rozszerzeń .php i .php3 do stron wyświetlających dane jedynie dlatego, że uważam że
rozszerzenie .phtml lepiej wygląda. Jest to jedyny powód. Do plików dołączanych używam innego
rozszerzenia i chcemy zaznaczyć, że jest to kod PHP. Nie używam typowego rozszerzenia inc.

Możesz używać dowolnego rozszerzenia dla skryptów PHP. Wszystkie rozszerzenia jakich u żywasz do
skryptów PHP i plików dołączanych powinny zostać dołączone do konfiguracji serwera WWW. Rozszerzenia
te konfiguruje się używając opcji konfiguracji, które zostały opisane w rozdziale 1, „Kompilacja i instalowanie
PHP”. Na przykład, jeżeli używasz rozszerzeń php i inc do oznaczania skryptów PHP i plików do łączanych,
powinieneś się upewnić, że serwer WWW został tak skonfigurowany, że będzie traktował oba te rozszerzenia
jako pliki PHP i przetwarzał je przed wysłaniem do przeglądarki użytkownika. Jeżeli nie zrobisz tego,
użytkownik może zapisać twoje skrypty. Rozważmy następujący przykład:

<!-- Plik: securityhole.phtml -->
<html>
<head>
<title>Przykład: błędny plik dołączany otwiera dziurę
w systemie zabezpieczeń</title>
</head>
<body>
<?php
/*
Plik dołączany bogus.inc, zawiera błąd
ale również znajduje się w nim nazwa użytkownika bazy danych i hasło.
*/
include( "bogus.inc" );
print( "Poznajmy dziurę w systemie bezpieczeństwa.<br>" );
?>
</body>
</html>
<!-- Plik: bogus.inc -->
<?php
// Jest to dołączany plik PHP demonstrujący
// potencjalną dziurę w systemie zabezpieczeń,
// powatającą, gdy źle skonfigurowany zostanie serwer WWW.
$aDatabaseIP = "12.34.56.123";
$aDatabaseUser = "secretuser";
$aDatabasePass = "secretpassword";
/*
Zagnieżdżony komentarz powoduje błąd.
/* właśnie tutaj */
*/
?>

W przykładzie tym do głównego pliku, securityhole.phtml, dołączany jest plik bogus.inc. Dołączany plik
zawiera dane na temat połączenia z bazą danych, w tym nazwę użytkownika i hasło. Zawiera on również błąd
syntaktyczny. Gdy otwarty zostanie plik securityhole.phtml, wy świetlony zostanie błąd: „Parse error: parse
error in bogus.inc on line 12”.

Dociekliwy użytkownik może spróbować obejrzeć plik bogus.inc wpisując odpowiedni URL w pasku adresu.
Jeżeli serwer WWW jest skonfigurowany taj aby traktować pliki .inc jako tekst (tak jak mój), cały tekst pliku
pojawi się w przeglądarce. Jeżeli serwer WWW jest tak skonfigurowany, aby traktować pliki .inc jak każdy inny
skrypt PHP, użytkownik zobaczy jedynie wcześniej wspomniany komunikat błędu.

Podsumowując. W trakcie tworzenia aplikacji PHP możesz użyć dowolnego rozszerzenia, ale aby uniknąć
potencjalnego zagrożenia bezpieczeństwa należy tak skonfigurować serwer WWW, aby analizował wszystkie
pliki posiadające używane przez ciebie rozszerzenia.

Obsługa formularzy w PHP
Do pobierania danych od użytkownika w HTML stosuje się formularze. W domyślnej konfiguracji PHP
po przesłaniu danych formularza do skryptu PHP, konwertuje wszystkie elementy formularza na zmienne PHP.
Poniższa strona HTML zawiera prosty formularz, do którego należy wpisać nazwę użytkownika i jego hasło. Po
przesłaniu formularza do skryptu post1.phtml, zmienne $UserName i $Password będą zawierały wartości wpisane
jako nazwę użytkownika i hasło.
Wydruk 3.1. Strona HTML i skrypt PHP ilustrujące procedurę logowania się użytkownika.
<!-- To jest strona HTML, listing1.html -->
<html>
<head>
<title>Wydruk 3.1 - listing1.html</title>
</head>

<body>
<form action="post1.phtml" method="post">
Nazwa użytkownika: <input type="text" name="Username"><br>
Hasło: <input type="password" name="Password"><br>
<input type="submit" name="Submit" value="Wyślij">
</form>
</body>
</html>
<!-- To jest skrypt PHP, post1.html -->
<html>
<head>
<title>Wydruk 3.1: post1.phtml</title>
</head>

<body>
<?php
print( "Nazwa użytkownika: $Username<br>" );
print( "Hasło: $Password<br>" );
?>
</body>
</html>

Skalarne i wielowartościowe elementy formularza
Elementy formularzy HTML zawierają zwykle wartości skalarne. Zamieszczony na wydruku 1 przykład
zawiera formularz z dwoma wartościami skalarnymi — nazwą użytkownika i hasłem. Można również tworzyć
elementy formularza zawierające wiele wartości, na przykład listę wielokrotnego wyboru. Aby użyć
nieskalarnych elementów formularza w PHP, należy dodać do nazwy nawiasy kwadratowe oznaczające zmienną
tablicową. Formularz na wydruku 2 pokazuje takie wielowartościowe elementy formularza.
Wydruk 3.2. Formularz HTML z elementami wielowartościowymi
<form action="displayall.phtml" method="post">
<table>
<tr>

41 PHP – Kompendium wiedzy
<td valign="top">
Wybierz kolory które lubisz:
</td>
<td valign="top">
<!-- Nazwy są indeksami tablicy -->
<select name="Colors[]" size="5" multiple>
<option value="Red">Czerwony</option>
<option value="Green">Zielony</option>
<option value="Blue">Niebieski</option>
<option value="Purple">Purpurowy</option>
<option value="Yellow">Żółty</option>
</select>
</td>
</tr>
<tr>
<td valign="top">
Wprowadź twój adres:
</td>
<td valign="top">
<!-- Trzy linie na dane adresowe. Używamy notacji tablicowej
aby zaznaczyć użycie wielu wierszy tablicy -->
<input type="text" name="address[]"><br>
<input type="text" name="address[]"><br>
<input type="text" name="address[]"><br>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="Submit" value="Wyślij">
</td>
</tr>
</table>
</form>
Po przesłaniu danych formularza z wydruku 2 do skryptu PHP, każda z tablic $Colors[] i $adress[]
będzie zawierać zero lub więcej wartości.

Alternatywne metody odczytywania wartości z formularza
PHP posiada alternatywną metodę dostępu do danych przesłanych do skryptu. Predefiniowane zmienne
tablicowe HTTP_GET_VARS i HTTP_POST_VARS zawierają tablice asocjacyjne elementów przesłanych do skryptu przy
pomocy metod odpowiednio GET i POST. Skrypt wyświetlający dane z wydruku 1 może zostać przepisany w
następujący sposób:
<!-- To jest skrypt PHP, post2.html -->
<html>
<head>
<title>Wydruk: post2.phtml</title>
</head>

<body>
<?php
error_reporting( 255 );
print( "Nazwa użytkownika: {$HTTP_POST_VARS['Username']}<br>" );
print( "Hasło: {$HTTP_POST_VARS['Password']}<br>" );
?>
</body>
</html>
W niektórych przypadkach preferowane jest użycie zmiennych HTTP_GET_VARS lub HTTP_POST_VARS zamiast
korzystania ze zmiennych globalnych. Na przykład możesz chcieć wyświetlić w czasie uruchamiania skryptu
wartości wszystkich danych wysłanych z formularza. Jeżeli bardzo przejmujesz się wydajnością serwera WWW,
można tu nieco zyskać, ponieważ PHP nie będzie musiał tworzyć zmiennych globalnych dla każdego z
elementów formularza. Można więc tak skonfigurować PHP, aby nie udostępniał tych zmiennych globalnych i tak
pisać skrypty, aby korzystały z wartości zawartych w tablicach HTTP_GET_VARS i HTTP_POST_VARS. Więcej na temat
tej dyrektywy konfiguracji napisane zostało na końcu książki przy opisie opcji konfiguracji register_globals.
Poniższa funkcja demonstruje użycie tablic HTTP_GET_VARS i HTTP_POST_VARS do wyświetlenia wszystkich
danych przekazanych z formularza do skryptu:
function DisplayGetVars()
{
global $HTTP_GET_VARS;
DisplayArray( $HTTP_GET_VARS );
}
function DisplayPostVars()
{
Rozdział 3 – Formularze i cookie 42
global $HTTP_POST_VARS;
DisplayArray( $HTTP_POST_VARS );
}
Obie z tych funkcji opierają się o funkcję DisplayArray przedstawioną na wydruku 3.3. Jest to prosta
funkcja wyświetlająca wszystkie elementy tablicy w tablicy HTML. Obsługuje ona rekurencyjnie elementy
tablicy, które same są tablicami.
Wydruk 3.3. Funkcja DisplayArray
function DisplayArray( $aArray )
{
// Upewniamy się, czy $aArray jest na pewno tablicą
if ( is_array ($aArray ) && (count( $aArray ) > 0 ))
{
// Rozpoczęcie tabeli
print ("<table border = \"1\">");
// Wyświetlenie nagłówka tabeli
print ( " <tr><th>Klucz</th><th>Wartość</th></tr>");
// Wyświetlenie wszystkich par klucz/wartość z tabeli
foreach( $aArray as $aKey => $aValue )
{
print( "<tr>" );
// Jeżeli bieżąca wartość jest tablicą
// wywołujemy rekurencyjnie funkcję
// w przeciwnym wypadku wyświetlamy wartość
if (!is_array( $aValue ))
{
// jeżeli wartość jest pusta, poinformujmy o tym
if (empty( $aValue ))
{
print( "<td>$aKey</td><td><i>pusty</i></td>");
}
else
{
print( "<td>$aKey</td><td><i>$aValue</i></td>");
}
}
else
{
print( "<td>$aKey(array)</td><td>");
DisplayArray( $aValue );
print ("</td>" );
}
print ("</tr>");
}
print ("</table>");
}
else
{
print("<i>pusty lub nieprawidłowy</i>");
}
}
Używając tej funkcji można pisać własne skrypty PHP wyświetlające wartości wszystkich przesłanych
elementów formularza. Poniższy skrypt, displayall.phtml powoduje wyświetlenie wszystkich danych przesłanych
przez HTTP GET, HTTP POST i cookie odesłane przez przeglądarkę (cookie zostaną omówione w dalszej części
tego rozdziału).
Wydruk 3.4. Skrypt displayall.phtml.
<html>
<head>
<title>Wyświetlenie wszystkich elementów formularza</title>
</head>
<body>
<?php
error_reporting( 255 );
include( "../include/gen_form_funcs.php" );
?>
<h2>Cała zawartość HTTP_GET_VARS</h2>
<?php
DisplayGetVars();
?>
<br><br>
<h2>Cała zawartość HTTP_POST_VARS</h2>
<?php
DisplayPostVars();
?>
<br><br>
<h2>Cała zawartość HTTP_COOKIE_VARS</h2>
<?php

43 PHP – Kompendium wiedzy
DisplayCookieVars();
?>
<br><br>
</body>
</html>

Na rysunkach 3.1. i 3.2. korzystając z formularza z wydruku 2. przedstawiono formularz wprowadzania
danych i wyniki wysłania danych do skryptu displayall.phtml. Zauważmy, że na rysunku 3.2, tablica
HTTP_POST_VARS zawiera trzy elementy: Colors, Address i Submit. Wartości dwóch pierwszych elementów są, jak
się tego można było spodziewać, tablicami. Wartością elementu Submit jest napis umieszczony na przycisku.
Pisząc skrypt obsługujący te wartości należy pamiętać, że element Submit jest zawsze umieszczany w tablicy
HTTP_POST_VARS.
Rysunek 3.1.
Przykład
wielowartościowyc
h elementów
formularza

Rozdział 3 – Formularze i cookie 44
Rysunek 3.2. Wynik
przesłania
formularza
wielowartościoweg
o do
displayall.phtml

Użycie formularzy do przesyłania plików
Większość nowoczesnych przeglądarek posiada zdolność przesyłania plików z dysku komputera
użytkownika na serwer WWW. PHP posiada obsługę przesyłania plików wbudowaną bezpośrednio w język. Jest
ona dokładniej opisana w rozdziale 5 „Wysyłanie plików przez formularz”.

Użycie rysunku jako przycisku wysłania danych
Jeżeli projekt aplikacji WWW tak przewiduje, możesz użyć rysunku w miejsce przycisku HTML
wysyłającego dane formularza do serwera. Dla PHP nie ma znaczenia, czy jest to przycisk czy rysunek, ale jeżeli
używasz rysunku oprócz danych do serwera zostaną wysłane dodatkowo współrzędne x i y (względem lewego
górnego rogu rysunku) punktu gdzie został kliknięty rysunek. Nazwy zmiennych przechowujących współrzędne
są tworzone poprzez dodanie _x i _y do nazwy elementu reprezentującego rysunek. Na przykład na wydruku 5
nazwą elementu rysunku jest SubmitImg. Zmienne reprezentujące współrzędne będą się nazywały SubmitImg_x i
SubmitImg_y. Mechanizm ten jest wygodny do tworzenia map obrazów po stronie serwera.
Wydruk 3.5. Przykład użycia rysunku w formularzu
<!-- Strona HTML, imgsubmit.html -->
<html>
<head>
<title>Użycie rysunku zamiast przycisku</title>
</head>

<body>
<form action="displayall.phtml" method="post">
Nazwa użytkownika: <input type="text" name="Username"><br>
Hasło: <input type="password" name="Password"><br>
<input type="image" name="SubmitImg" src="submit.gif">
</form>
</body>
</html>

45 PHP – Kompendium wiedzy
Niektóre przeglądarki posiadają mechanizm pozwalający wykorzystać klawisz Enter zamiast klikania w
przycisk na formularzu. Gdy użyjemy rysunku zamiast przycisku, mechanizm ten nadal będzie działał, ale nie
zostaną wtedy przesłane dane na temat współrzędnych.

Kontrola poprawności danych formularza
Część ta jest poświęcona kontroli poprawności danych formularza przez mechanizmy umieszczone na
serwerze a nie na komputerze klienta. Języki skryptowe działające na kliencie, takie jak JavaScript mogą być
wykorzystywane do kontroli poprawności elementów formularza przez wysłaniem ich do serwera. Kontrola taka
jest zalecana w przypadku tworzenia wysoce interaktywnych aplikacji WWW, ale nie jest ona całkowicie pewna,
ponieważ może być niedostępna w wielu przeglądarkach i systemach operacyjnych. Dlatego dane muszą być
kontrolowane na serwerze nawet, jeżeli były one już kontrolowane na komputerze klienta. PHP pozwala na
stosowanie kilku metod kontroli poprawności danych, wykorzystując wyrażenia regularne, kontrolę typów
danych lub przeszukiwanie słowników w bazie danych.

Kontrola danych za pomocą wyrażeń regularnych
Prawdopodobnie najskuteczniejszym mechanizmem kontroli danych jest użycie wyrażeń regularnych i
funkcji wyrażeń regularnych w PHP. Wyrażenia te są potężnym narzędziem, ale jeżeli wcześniej nie miałeś z
nimi doświadczenia, są dość skomplikowane w użyciu.
PHP obsługuje dwa rodzaje wyrażeń regularnych — w stylu POSIX i Perl. Skupimy się tutaj na
wyrażeniach w stylu POSIX, ale wyrażenia w stylu Perl dają podobne możliwości. Nazwy funkcji wyrażeń w
stylu Perl są poprzedzone przedrostkiem preg_ i są opisane w skorowidzu na końcu tej książki. Ponieważ lepiej
znam wyrażenia regularne w stylu POSIX, są one używane we wszystkich przytoczonych tu przykładach, ale
należy pamiętać, że funkcje wyrażeń w stylu Perl są szybsze i mają większe możliwości.
Funkcje wyrażeń regularnych w stylu POSIX to: ereg(), ereg_replace(), eregi(), eregi_replace() oraz
split(). Do kontroli poprawności używa się funkcji ereg() i eregi(). Ogólna składnia tych funkcji jest
następująca:
int ereg( string wzorzec, string ciag [, array dopasowanie] )
int eregi( string wzorzec, string ciag [, array dopasowanie] )
Obie funkcje wymagają wzorca wyrażenia regularnego, ciągu do przeszukania oraz opcjonalnej tablicy,
która będzie zawierać dopasowania wzorca odnalezione w przeszukiwanym ciągu. Każda funkcja zwraca true,
jeżeli wzorzec został odnaleziony w ciągu. Funkcja eregi() jest identyczna z ereg() poza tym, że przy
przeszukiwaniu ignoruje ona wielkość liter.
Unikanie kontroli poprawności

Chociaż kontrola poprawności jest ważna, jeżeli nie musisz czegoś kontrolować, to nie rób tego. Zamiast tego można zastosować
takie mechanizmy wprowadzania danych, które zmniejszaj ą szansę pomyłki użytkownika. Na przykład zastosowanie listy rozwijalnej z
miesiącami jest mniej pracochłonne niż kontrola poprawności wpisanych nazw. Zamiast wszędzie korzystać ze zwykłych pól
tekstowych należy znaleźć miejsca, gdzie można zastosować listę, pole wyboru lub przyciski opcji.

Poniższy przykład pokazuje zastosowanie wyrażeń regularnych do kontroli poprawności amerykańskiego
kodu pocztowego oraz dat w formacie ISO (YYYY-MM-DD). Zauważ, że w przykładach tych jest sprawdzany
jedynie format a nie wartości.
Wydruk 3.6. Kontrola poprawności kodu pocztowego i daty ISO
<html>
<head>
<title>Kontrola poprawności amerykańskiego kodu pocztowego i daty ISO</title>
</head>

<body>
<?php
$aCode1 = "83440";
$aCode2 = "83440-1607";
$aCode3 = "834";
$aCode4 = "M6K 3E3";
$aCodeFormat = "[0-9]{5}(-[0-9]{4})?";
if ( ereg( $aCodeFormat, $aCode1 ) == True )

Rozdział 3 – Formularze i cookie 46
print( "'$aCode1' jest poprawnym kodem pocztowym<br>" );
else
print( "'$aCode1' nie jest poprawnym kodem pocztowym<br>" );
if ( ereg( $aCodeFormat, $aCode2 ) == True )
print( "'$aCode2' jest poprawnym kodem pocztowym<br>" );
else
print( "'$aCode2' nie jest poprawnym kodem pocztowym<br>" );
if ( ereg( $aCodeFormat, $aCode3 ) == True )
print( "'$aCode3' jest poprawnym kodem pocztowym<br>" );
else
print( "'$aCode3' nie jest poprawnym kodem pocztowym<br>" );
if ( ereg( $aCodeFormat, $aCode4 ) == True )
print( "'$aCode4' jest poprawnym kodem pocztowym<br>" );
else
print( "'$aCode4' nie jest poprawnym kodem pocztowym<br>" );

$aDate1 = "2000-06-29";
$aDate2 = "2000-7-4";
$aDate3 = "June 29, 2000";
$aDate4 = "0000-99-99";
$aDateFormat = "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}";
if ( ereg( $aDateFormat, $aDate1 ) == True )
print( "'$aDate1' jest poprawnym formatem daty ISO<br>" );
else
print( "'$aDate1' nie jest poprawnym formatem daty ISO<br>" );
if ( ereg( $aDateFormat, $aDate2 ) == True )
print( "'$aDate2' jest poprawnym formatem daty ISO<br>" );
else
print( "'$aDate2' nie jest poprawnym formatem daty ISO<br>" );
if ( ereg( $aDateFormat, $aDate3 ) == True )
print( "'$aDate3' jest poprawnym formatem daty ISO<br>" );
else
print( "'$aDate3' nie jest poprawnym formatem daty ISO<br>" );
if ( ereg( $aDateFormat, $aDate4 ) == True )
print( "'$aDate4' jest poprawnym formatem daty ISO<br>" );
else
print( "'$aDate4' nie jest poprawnym formatem daty ISO<br>" );
?>
</body>
</html>
Wyniki działania skryptu z wydruku 6 są następujące:
'83440' jest poprawnym kodem pocztowym
'83440-1607' jest poprawnym kodem pocztowym
'834' nie jest poprawnym kodem pocztowym
'M6K 3E3' nie jest poprawnym kodem pocztowym
'2000-06-29' jest poprawnym formatem daty ISO
'2000-7-4' jest poprawnym formatem daty ISO
'June 29, 2000' nie jest poprawnym formatem daty ISO
'0000-99-99' jest poprawnym formatem daty ISO
Programiści programujący wcześniej w języku Perl i ci, którzy używali już wyrażeń regularnych uważają
taką kontrolę poprawności za łatwą i wydajną. Ci zaś, którzy nie znają wyrażeń regularnych mogą wybrać inne
metody kontroli poprawności, opisane w następnych dwóch częściach.

Kontrola poprawności za pomocą sprawdzania typów
W niektórych przypadkach wystarczy sprawdzić typ wprowadzonej danej i nie przejmować się
wprowadzoną wartością. Metoda ta jest odpowiednia do kontrolo prostych typów, takich jak liczby i ciągi, ale
również pozwala na nieco więcej. Jeżeli zostanie użyta w połączeniu z dodatkowym kodem kontroli poprawności,
będzie wystarczająca dla wielu aplikacji. Poniższy przykład sprawdza typy zmiennych, aby upewnić się, że
zostały wprowadzone tylko liczby.
<html>
<head>
<title>Kontrola liczb przy użyciu kontroli typów</title>
</head>

<body>
<?php
$aValue1 = "123";
$aValue2 = "123.446";
$aValue3 = "1.56e18";
$aValue4 = "3 małe świnki";
if ( is_numeric( $aValue1 ) == True )
print( "'$aValue1' jest liczbą<br>" );
else
print( "'$aValue1' nie jest liczbą<br>" );

47 PHP – Kompendium wiedzy
if ( is_numeric( $aValue2 ) == True )
print( "'$aValue2' jest liczbą<br>" );
else
print( "'$aValue2' nie jest liczbą<br>" );
if ( is_numeric( $aValue3 ) == True )
print( "'$aValue3' jest liczbą<br>" );
else
print( "'$aValue3' nie jest liczbą<br>" );
if ( is_numeric( $aValue4 ) == True )
print( "'$aValue4' jest liczbą<br>" );
else
print( "'$aValue4' nie jest liczbą<br>" );
?>
</body>
</html>
Skrypt przedstawiony na wydruku 7 interpretuje pierwsze trzy wartości jako liczby natomiast ostatnią nie.
Więcej na temat funkcji kontroli typów znajduje się przy opisie funkcji is_xxx() w części „Funkcje zmiennych”
w skorowidzu funkcji na końcu książki.

Klasa Validator
Jedną z najpiękniejszych cech oprogramowania typu open-source jest dostępność świetnych narzędzi
uzupełniających podstawowy produkt. Dodatkowe oprogramowanie dla PHP można pozyskać z wielu źródeł.
Witryna Webmasters Net (http://www.thewebmasters.net) zawiera nieco świetnych klas i modułów z kodem
źródłowym. Do kontroli poprawności przeznaczona jest klasa Validator zawierająca wiele funkcji
upraszczających wiele zadań i oszczędzających czas. Przykładowymi funkcjami kontroli poprawności są
is_email(), is_url() i is_phone() przeznaczone do sprawdzania adresów e-mail, URL i numerów telefonów.
Więcej informacji na temat tej klasy i innych dostarczanych przez Webmasters Net znajduje się w części „PHP
Tools and Extras” — ich witryny.
Wydruk 3.8. Kontrola poprawności danych przy użyciu klasy Validator
<?php
error_reporting( 0 );
include( "../include/class.Validator.php3" );
$aValidator = new Validator;
$aPhoneNum1 = "(208) 359-1540";
$aPhoneNum2 = "+1 208-359-1540";
$aPhoneNum3 = "support@intechra.net";
if ( $aValidator->is_phone( $aPhoneNum1 ) == True )
print( "'$aPhoneNum1' jest prawidłowym numerem telefonu<br>" );
else
print( "'$aPhoneNum1' nie jest prawidłowym numerem telefonu<br>" );
if ( $aValidator->is_phone( $aPhoneNum2 ) == True )
print( "'$aPhoneNum2' jest prawidłowym numerem telefonu<br>" );
else
print( "'$aPhoneNum2' nie jest prawidłowym numerem telefonu<br>" );
if ( $aValidator->is_phone( $aPhoneNum3 ) == True )
print( "'$aPhoneNum3' jest prawidłowym numerem telefonu<br>" );
else
print( "'$aPhoneNum3' nie jest prawidłowym numerem telefonu<br>" );
/*
Funkcja is_email kontroluje nie tylko poprawność formatu adresu email
ale również sprawdza, czy istnieje w Internecie podany host
Oczywiście wymaga to podłączenia z Internetem.
W chwili pisania tego przykładu host 'invalidhost.com'
nie był zarejestrowany.
*/
$aEmail1 = "blake@intechra.net";
$aEmail2 = "john";
$aEmail3 = "nobody@invalidhost.com";
if ( $aValidator->is_email( $aEmail1 ) == True )
print( "'$aEmail1' jest prawidłowym adresem email<br>" );
else
print( "'$aEmail1' nie jest prawidłowym adresem email<br>" );
if ( $aValidator->is_email( $aEmail2 ) == True )
print( "'$aEmail2' jest prawidłowym adresem email<br>" );
else
print( "'$aEmail2' nie jest prawidłowym adresem email<br>" );
if ( $aValidator->is_email( $aEmail3 ) == True )
print( "'$aEmail3' jest prawidłowym adresem email<br>" );
else
print( "'$aEmail3' nie jest prawidłowym adresem email<br>" );
?>

Rozdział 3 – Formularze i cookie 48
Klasa Validator jest potężnym zestawem funkcji przyspieszających tworzenie oprogramowania, ale tak
jak w przypadku wszystkich narzędzi zewnętrznych należy sprawdzić, czy spełnia twoje wymagania.

Cookie
Z powodu trwającej debaty na temat użycia cookie, większość programistów WWW i użytkowników jest
zaznajomiona z koncepcją cookie. Cookie są plikami tekstowymi zapisanymi na komputerze klienta i są one ze
swojej natury niewinne. Jednak wielu użytkowników nie przyjmuje cookie wysłanych do przeglądarek z powodu
plotek na temat ich wykorzystania. Jeżeli twoja aplikacja opiera swoje działanie na cookie, niektórzy użytkownicy
nie będą mogli jej używać. Jednak jeżeli korzystasz z cookie, ale nie wymagasz ich do prawidłowej pracy, twoja
aplikacja będzie działała z większością przeglądarek.
PHP posiada tylko jedna funkcję przeznaczoną do tworzenia cookie, setcookie(). Ponieważ cookie są
wysyłane jako część nagłówka HTTP, funkcja setcookie() musi być wywołana przed wysłaniem jakichkolwiek
danych do przeglądarki lub należy zastosować buforowanie wyjścia w celu opóźnienia wysyłania danych do
przeglądarki do chwili zdefiniowania wszystkich cookie. To samo ograniczenie obowiązuje również dla funkcji
header().
Dowolne cookie odesłane do aplikacji przez przeglądarkę jest automatycznie konwertowane na zmienną
PHP tak samo, jak dzieje się to w przypadku metod GET i POST. Cookie mogą przenosić wartości skalarne jak
również tablice wartości. Funkcja setcookie() jest zdefiniowana w sposób następujący:
int setcookie( string nazwa, string wartość, int czas, string ścieżka,
string domena, int bezpieczny )
Wszystkie argumenty funkcji poza nazwą są opcjonalne. Jeżeli funkcja jest wywołana tylko z nazwą,
cookie o podanej nazwie jest usuwane. Dowolny z ciągów może zostać opuszczony podając pusty ciąg ("").
Dowolna wartość numeryczna może zostać opuszczona podając wartość zero. Argument czas jest standardowym
czasem z systemu Unix w postaci liczby, którą można uzyskać jako wynik funkcji mktime() lub time(). Parametr
bezpieczny wskazuje, że cookie może być przesyłane jedynie przez połączenie bezpieczne (HTTPS).
Należy pamiętać o następujących pułapkach i częstych błędach użycia cookie:
• Ustawione cookie nie będą widoczne w skrypcie do czasu jego powtórnego załadowania.
• Przeglądarki różnie obsługują cookie. Sprawdź aplikację na możliwie dużej ilości przeglądarek.
• Każda przeglądarka przechowuje cookie niezależnie. Oznacza to, że jeżeli użytkownik obejrzy witrynę
przy użyciu jednej przeglądarki i zostanie ustawione cookie, to cookie nie będzie dostępne, jeżeli
użytkownik ponownie obejrzy witrynę za pomocą innej przeglądarki.
Więcej ogólnych informacji na temat cookie można znaleźć w specyfikacji cookie firmy Netscape, która
jest dostępna pod adresem http://www.netscape.com/newsref/std/cookie_spec.html.
Poniższe dwa przykłady pokazują użycie funkcji setcookie(). Wydruk 3.9 pokazuje jak ustawiać i
wyświetlać cookie. Na wydruku 3.10 pokazane jest jak używać buforowania wyjścia w połączeniu z funkcją
setcookie().
Wydruk 3.9. Użycie cookie
<?php
// Sprawdzenie czy istnieje zmienna cookie $LastTime
if ( !empty( $LastTime ) )
{
$aMessage = "Ostatnia wizyta miała miejsce ";
$aMessage .= date( "d F Y", $LastTime );
$aMessage .= " o ";
$aMessage .= date( "h:i:s a", $LastTime );
}
else
{
$aMessage = "Nie byłeś tu przez ostatnie ";
$aMessage .= "dwa tygodnie.";
}
// Ustawienie cookie ważnego przez dwa tygodnie
$aTwoWeeks = time() + ( 60 * 60 * 24 * 14 );
setcookie( "LastTime", time(), $aTwoWeeks );
// sprawdzenie istnienia niezwykle ważnej tablicy z cookie
$aValMessage = "";
if ( !empty( $CookieArray ) )
{
$aValMessage = "Wartości: " . $CookieArray[0];
$aValMessage .= ", " . $CookieArray[1];

49 PHP – Kompendium wiedzy
$aStartValue = $CookieArray[1] + 1;
}
else
{
$aValMessage = "Wartości nie są dostępne!";
$aStartValue = 0;
}
// usunięcie niezwykle istotnej tablicy wartości
setcookie( "CookieArray[0]" );
setcookie( "CookieArray[1]" );
// dodanie niezwykle istotnej tablicy wartości
setcookie( "CookieArray[0]", $aStartValue, $aTwoWeeks );
setcookie( "CookieArray[1]", $aStartValue + 1, $aTwoWeeks );
?>
<html>
<head>
<title>Użycie cookie</title>
</head>
<body>
<?php
print( $aMessage . "<br><br>" . $aValMessage );
?>
</body>
</html>
Wydruk 3.10. Użycie setcookie() razem z buforowaniem wyjścia
<?php
/*
Uruchomienie buforowania wyjścia. Jeżeli funkcja ob_start()
jest zakomentowana, skrypt spowoduje błąd.
*/
ob_start();
?>
<html>
<head>
<title>Użycie setcookie() wraz z buforowaniem wyjścia</title>
</head>
<body>
<?php
setcookie( "anyname", "anyvalue", time() + 60 );
?>
Działa świetnie.
</body>
</html>
<?php ob_end_flush(); ?>

Ostatnia uwaga na temat cookie

W poprzedniej wersji PHP jeżeli chciałeś ustawić wiele cookie za pomocą jednego skryptu, musiałeś wywoływać setcookie() w
odwrotnej kolejności do tej, w jakiej chciałeś obsługiwać cookie. Na przykład, jeżeli chciałeś usunąć cookie a następnie ustawić nowe
o tej samej nazwie, należało najpierw wywołać setcookie(), aby ustawić nową wartość a następnie usunąć poprzednią wartość. W
PHP4 zostało to usunięte. Należy wywoływać setcookie() w takiej kolejności jak się spodziewasz, że będą przetwarzane przez
przeglądarkę. Proces ten jest pokazany na wydruku 3.9.

Mimo, że debata na temat tego, czy należy używać cookie będzie nadal trwała, ich tworzenie w PHP jest
łatwe i proste. Dalsze rozważania na temat cookie będą kontynuowane w rozdziale 7 „Sesje i stan aplikacji”.

Ważne zagadnienia programowania dla WWW
Część ta zawiera niektóre tematy, jakie muszą brać pod uwagę programiści przechodzący z pisania
zwykłych aplikacji na aplikacje oparte o WWW. Problemy te powstają zwykle w czasie przetwarzania i
wykorzystywania danych przesłanych z formularza HTML.

Obsługa nieprawidłowych danych
Pierwszym problemem jest sposób obsługi nieprawidłowych danych. W tradycyjnych aplikacjach dane
wprowadzone przez użytkownika są często kontrolowane natychmiast po ich wprowadzeniu. Pozwala to
natychmiast informować o nieprawidłowych danych. W aplikacjach WWW nie ma niezawodnego mechanizmu
kontroli danych po wyjściu z poszczególnych pól, więc cała kontrola poprawności jest przeprowadzana na
serwerze. Oznacza to, że jeżeli istnieje błąd w danych, użytkownik nie będzie o nim wiedział aż do chwili
Rozdział 3 – Formularze i cookie 50
przesłania danych formularza. Dlatego w trakcie tworzenia aplikacji musisz się zdecydować, w jaki sposób
reagować na błędy.
Istnieje kilka sposobów reagowania na błędy, przytoczymy tutaj dwa z nich. Pierwszą metodą jest
wypisywanie błędów i nakazanie użytkownikowi, aby wrócił do poprzedniej strony i poprawił dane. Według mnie
jest to najmniej pożądany sposób reakcji na błędy, ale jest najprostszy do zrealizowania. Jeżeli do formularza
wpisywane jest bardzo mało danych (jedno lub dwa pola) metoda ta będzie do zaakceptowania. Jeżeli jednak
tworzysz duży formularz nie należy używać tej metody, ponieważ może ona wymagać ponownego wprowadzenia
wszystkich danych. Niektóre przeglądarki nie utrzymują wartości formularza po użyciu przycisku Wstecz.
Drugą metodą obsługi nieprawidłowych danych jest ponowne pokazanie strony formularza z
zainicjowanymi wszystkimi polami i zaznaczonymi nieprawidłowymi pozycjami. Możesz utworzyć taki
formularz przesyłając dane z formularza do tego samego skryptu. Metoda taka wymaga bardziej zaawansowanego
projektowania, ale skutkuje powstaniem solidniejszej i bardziej użytecznej aplikacji, ponieważ ten sam skrypt
może być użyty do pobierania nowych danych, zmiany danych istniejących i kontroli poprawności tych danych.
Na wydruku 3.11 pokazane jest w jaki sposób można użyć jednego skryptu do zbierania i kontroli poprawności
danych na prostym formularzu używanym do wpisywania adresów e-mail i numerów telefonów.
Wydruk 3.11. Inteligentna obsługa nieprawidłowych danych
<?php
error_reporting( 0 );
// na początku przestawiamy skrypt na zbieranie nowych danych.
$aCurPhoneVal = "";
$aCurEmailVal = "";
$aPhoneTextCol = "black";
$aEmailTextCol = "black";
if ( !empty( $Submit ) )
{
/*
Jeżeli zmienna $Submit jest zainicjowana jesteśmy tutaj po
przesłaniu danych do skryptu. Próbujemy sprawdzić wartości
zmiennych formularza.
*/
include( "../include/class.Validator.php3" );
$aValidator = new Validator;
$aValidPhone = $aValidator->is_phone( $Phone );
$aValidEmail = $aValidator->is_email( $Email );
if ( $aValidPhone && $aValidEmail )
{
// Dane są prawidłowe, przechodzimy do odpowiedniej strony
header( "Location:thanks.html\n" );
}
else
{
// Dane nieprawidłowe, wyróżniamy je
$aCurPhoneVal = $Phone;
$aCurEmailVal = $Email;
if ( $aValidPhone == False )
$aPhoneTextCol = "red";
if ( $aValidEmail == False )
$aEmailTextCol = "red";
}
}
?>
<html>
<head>
<title>Inteligentna obsługa nieprawidłowych danych</title>
</head>
<body>
<?php
if ( empty( $Submit ) ) {
?>
Proszę wprowadzić numer telefonu i adres email.
<br>
<?php
} else { // if
?>
We wprowadzonych danych wystąpiły błędy.
Sprawdź dane oznaczone kolorem czerwonym.
<?php
} // end if
?>
<form action="handle_errors.phtml" method="post">
<font color="<?php print( $aPhoneTextCol );?>">
Numer telefonu:</font>
<input type="text" name="Phone"

51 PHP – Kompendium wiedzy
value="<?php echo $aCurPhoneVal;?>">
<br>
<font color="<?php print( $aEmailTextCol );?>">
Adres e-mail:</font>
<input type="text" name="Email"
value="<?php echo $aCurEmailVal;?>">
<br>
<input type="submit" name="Submit" value="Wyślij">
</form>
</body>
</html>
W skrypcie na wydruku 11, gdy strona jest otwierana bezpośrednio, zmienna $Submit jest pusta, więc
formularz jest wyświetlany z pustymi polami. Gdy użytkownik kliknie przycisk Wyślij, strona jest ładowana po
raz drugi, ale tym razem zmienna $Submit nie jest pusta, więc sprawdzana jest poprawność danych. Jeżeli oba
pola mają poprawne dane, wywoływana jest funkcje header(), która powoduje przekierowanie przeglądarki do
nowej strony zawierającej podziękowanie. Jeżeli któreś pole zawiera nieprawidłową wartość, formularz
wywoływany jest ponownie, ale tym razem pola mają wartości wprowadzone poprzednio przez użytkownika.
Dodatkowo, aby zaznaczyć wystąpienie błędu, nieprawidłowe dane są wyświetlane kolorem czerwonym.
Metoda ta pozwala na szybką identyfikację przez użytkownika danych, które wymagają poprawienia i nie
wymaga ponownego wprowadzenia całej zawartości formularza. Jeżeli tworzysz aplikację WWW, która wymaga
od użytkownika wprowadzania danych należy rozważyć użycie podobnej metody do obsługi błędnych danych.
Jeżeli twoja aplikacja będzie niewygodna lub wymagać będzie ponownego wprowadzania danych, na pewno nie
będzie lubiana.
Przedstawione metody nie są jedynymi stosowanymi do obsługi błędnych danych, ale ilustrują one
podstawy tworzenia aplikacji WWW. Wybór metody obsługi błędów może być kluczową decyzją przy
projektowaniu aplikacji.

Obsługa i formatowanie wyświetlanych danych
W zwykłej aplikacji wyświetlanie danych wprowadzonych przez użytkownika nie wymaga zwykle
formatowania lub przetwarzania. Czasami formatowane są liczby, aby wyświetlać wartości walutowe lub dodać
separatory tysięcy, ale zwykle nie ma zbyt dużo kłopotu przy wyświetlaniu danych wprowadzonych przez
użytkownika. W przypadku programowania dla WWW wyświetlanie danych wprowadzonych do formularza w
postaci strony WWW jest sprawą krytyczną. Dzieje się tak, ponieważ przeglądarka interpretuje cały tekst
otrzymany z serwera WWW. Jeżeli dostarczysz użytkownikom formularz a następnie będziesz wyświetlał
wpisane dane, niektórzy użytkownicy będą dodawać znaczniki HTML, aby sprawdzić co się stanie.
Pamiętając o tym pomyśl o formularzu, w którym użytkownicy będą mogli wpisywać swoje uwagi.
Załóżmy, że stworzysz formularz w którym zapisywane będą: nazwa użytkownika, adres e-mail oraz treść uwagi.
Po wprowadzeniu tekstu wyświetlasz komunikat w celu weryfikacji a następnie przetwarzasz ten komunikat.
Pomysłowi lub złośliwi użytkownicy mogą próbować przetestować twój serwer WWW dodając znaczniki HTML
lub kod JavaScript w treści komunikatu. Zwykle nie jest to niebezpieczne, ale na pewno skutkuje różnymi
efektami ubocznymi.
Aby uniknąć tego problemu zawsze należy przetwarzać dane wprowadzone do formularza przed ich
wyświetleniem. PHP posiada kilka funkcji pomagających w tym zadaniu. Są to funkcje strip_tags() i
htmlentities(). Funkcja strip_tags() usuwa wszystkie znaczniki z ciągu oprócz tych, które zostały podane w
dodatkowym opcjonalnym parametrze. Funkcja htmlentities() konwertuje specjalne znaki HTML na
odpowiadające im symbole HTML. Na przykład znaki < i > są zastępowane przez &lt; i &gt;. Formularz i skrypt
na wydruku 3.12 pokazuje obróbkę danych do ponownego wyświetlenia.
Wydruk 3.12. Obróbka danych do wyświetlenia.
<html>
<head>
<title>Pobieranie danych do wyświetlenia</title>
</head>
<body>
<form action="safedisplay.phtml" method="post">
Wprowadź tekst:<br>
<textarea cols="40" rows="6" name="TheText"></textarea>
<br><br>
Wybierz metodę filtrowania:
<select name="FilterType" size="1">
<option value="0">brak</option>

Rozdział 3 – Formularze i cookie 52
<option value="1">strip_tags()</option>
<option value="2">htmlentities()</option>
</select>
<br><br>
<input type="submit" name="Submit" value="Wyślij">
</form>
</body>
</html>
<!-- Skrypt safedisplay2.phtml -->
<?php
error_reporting( 255 );
switch ( $FilterType )
{
case 0 : // brak
$aDisplayText = $TheText;
break;
case 1 : // strip_tags
$aDisplayText = strip_tags( $TheText );
break;
case 2 : // htmlentities
$aDisplayText = htmlentities( $TheText );
break;
}
?>
<html>
<head>
<title>Bezpieczne wyświetlenie danych użytkownika</title>
</head>
<body>
<?php
print( $aDisplayText );
?>
</body>
</html>

Rysunki 3.3. do 3.6. pokazują formularz wejściowy i wyniki działania skryptu. Rysunek 3.3. zawiera
formularz wprowadzania danych. Rysunek 3.4. pokazuje co się dzieje, jeżeli nie ma filtrowania. Rysunki 3.5. i
3.6. pokazują wyniki filtrowania danych z formularza za pomocą funkcji odpowiednio strip_tags() i
htmlentities().

Rysunek 3.3.
Formularz
wprowadzania
danych

53 PHP – Kompendium wiedzy
Rysunek 3.4.
Wyświetlanie bez
filtrowania

Rysunek 3.5.
Wyświetlanie ze
strip_tags()

Rozdział 3 – Formularze i cookie 54
Rysunek 3.6.
Wyświetlanie z
htmlentities()

Jeżeli dokładnie przyjrzysz się tym rysunkom zauważysz, że widać niespodziewane wyniki po
wyświetleniu danych. Na przykład pojedynczy apostrof jest wyświetlany na stronie jako sekwencja \'. Również
znaki końca linii wprowadzone w polu tekstowym nie są uwzględniane w wyświetlanym tekście.
Pierwsze z zakłóceń jest powodowane przez dyrektywę konfiguracji --enable-magic-quotes oraz opcje
pliku php.ini magic_quotes_gpc, magic_quotes_runtime i magic_quotes_sybase. Jeżeli jest ona uaktywniona,
wszystkie apostrofy, cudzysłowy, NUL1 i znaki backslash pochodzące z zewnętrznych źródeł, na przykład
formularzy i bazy danych, są automatycznie poprzedzane ukośnikiem. Jest to szczególnie przydatne, jeżeli dane te
będą zapisywane w bazie danych, ponieważ nie będziesz musiał ręcznie oznaczać tych znaków w ciągu SQL. Aby
wyświetlić taki ciąg, należy wywołać funkcję strip_slashes(), która usuwa te dodatkowe znaki.
Jeżeli chodzi o problem ze znakami nowej linii, należy pamiętać, że HTML nie interpretuje znaku CR ani
LF jako znaku podziału wiersza, chyba, że wystąpi on w bloku <pre></pre>. PHP posiada funkcję nl2br(), która
konwertuje znaki nowej linii na znaczniki <br>.
Na wydruku 13 znajduje się ten sam formularz i skrypt co na wydruku 12, ale z dodatkowymi opcjami
które powodują wywołanie funkcji strip_slashes() i nl2br().
Wydruk 3.13. Ulepszona obróbka danych do wyświetlenia
<html>
<head>
<title>Pobieranie danych do wyświetlenia</title>
</head>
<body>
<form action="safedisplay2.phtml" method="post">
Wprowadź tekst:<br>
<textarea cols="40" rows="6" name="TheText"></textarea>
<br><br>
Wybierz metodę filtrowania:
<select name="FilterType" size="1">
<option value="0">none</option>
<option value="1">strip_tags()</option>
<option value="2">htmlentities()</option>
</select>
<br><br>
<input type="checkbox" name="DoSS"> strip_slashes()<br>
<input type="checkbox" name="DoNB"> nl2br()<br><br>
<input type="submit" name="Submit" value="Wyślij">
</form>
</body>
</html>
<!-- Skrypt safedisplay2.phtml -->
<?php

1
Znak o kodzie zero (przyp. tłum.)

55 PHP – Kompendium wiedzy
error_reporting( 0 );
switch ( $FilterType )
{
case 0 : // brak
$aDisplayText = $TheText;
break;
case 1 : // strip_tags
$aDisplayText = strip_tags( $TheText );
break;
case 2 : // htmlentities
$aDisplayText = htmlentities( $TheText );
break;
}
if ( $DoSS == "on" )
$aDisplayText = stripslashes( $aDisplayText );
if ( $DoNB == "on" )
$aDisplayText = nl2br( $aDisplayText );
?>
<html>
<head>
<title>Bezpieczne wyświetlenie danych użytkownika</title>
</head>
<body>
<?php
print( $aDisplayText );
?>
</body>
</html>
Rysunek 3.7.
Formularz
wprowadzania
danych

Rozdział 3 – Formularze i cookie 56
Rysunek 3.8.
Wyświetlanie
przefiltrowane
przez strip_tags(),
strip_slashes() i
nl2br()

Po wprowadzeniu zmian pokazanych na wydruku 3.13, formularz wprowadzania danych i postać danych
wynikowych jest taka, jak widać na rysunku 3.7. i 3.8.
Jeżeli wcześniej miałeś doświadczenie jedynie ze zwykłymi aplikacjami, musisz pamiętać o tych
pułapkach stosowania przeglądarki jako warstwy prezentacji aplikacji. Oprócz pamiętania o wspomnianych
problemach należy również zwrócić uwagę, że każda przeglądarka działa nieco inaczej. Szczegółowe omówienie
tych problemów znajduje się w rozdziale 9 „Niezależność od przeglądarki”.

Podsumowanie
Rozdział ten zawiera opis podstaw przetwarzania formularzy przy użyciu PHP. Ponieważ PHP został
zaprojektowany jako język programowania dla WWW, upraszcza on znacznie proces interakcji z formularzami
HTML. Ważniejsze od prostego pobierania danych od użytkowników jest prawidłowa obsługa tych danych i
zabezpieczanie serwera i użytkowników przed nieprawidłowymi lub niebezpiecznymi danymi. W rozdziale tym
omówiono niektóre narzędzia umożliwiające obsłużyć nieprawidłowe dane i zabezpieczyć przed szkodliwymi
danymi. W rozdziale tym omówiono również proces zapamiętywania i odczytywanie cookie na komputerze
klienta. Wszystkie te tematy razem stanowią podstawę do tworzenia interaktywnych aplikacji WWW.

57 PHP – Kompendium wiedzy
Rozdział 4. Operacje na plikach
Wstęp
Obsługa plików jest zawarta we wszystkich nowoczesnych językach programowania. Zdolność do
tworzenia, czytania, zapisu i innych operacji na plikach lub innych obiektach systemu plików jest niezbędna do
zrealizowania obsługi sesji i serializacji. Do obsługi plików i innych obiektów systemu plików PHP posiada
funkcje podobne do tych spotykanych w języku C. Tak jak C, w funkcjach służących do odczytu i zapisu, PHP
używa uchwytów plików oraz pozwala na tworzenie uchwytów (pozwalających na operacje innymi typami
strumieni danych, takimi jak gniazda i potoki). Zdolność ta powoduje, że równie łatwo można zapisać dane do
pliku jak również wysłać je poprzez potok do innego programu.

Odczyt i zapis plików
Jedną z głównych różnic przy pisaniu aplikacji opartych o sieć WWW w stosunku do zwykłych aplikacji,
jest sposób utrzymywania stanu aplikacji. W przypadku zwykłego programu, użytkownik uruchamia go,
wykonuje kilka komend i kończy działanie programu. W czasie pracy programu stan aplikacji jest utrzymywany
w pamięci. W aplikacjach opartych o sieć WWW stan musi być utrzymywany przez serwer WWW, ponieważ
klientem jest zwykle prosta przeglądarka WWW. Szczegółowe przedstawienie zarządzania stanem aplikacji
można znaleźć w rozdziale 7 „Sesje i stan aplikacji”. W chwili obecnej wystarczy wiedzieć, że do utrzymywania
stanu aplikacji i tworzenia innych mechanizmów przechowywania danych można użyć plików.
Ważne jest, aby używając plików, pamiętać o zagadnieniach bezpieczeństwa. Ponieważ aplikacja będzie
działać w kontekście serwera WWW, pliki będą miały uprawnienia użytkownika przy pomocy którego
uruchamiany jest serwer WWW. W przypadku Apache domyślnie jest to użytkownik nobody, którego
uprawnienia ograniczają dostęp przez aplikację do obiektów systemu plików. Należy uważać, aby korzystając z
plików nie naruszyć systemu bezpieczeństwa serwera WWW. W większości przypadków użycie bazy danych
zamiast plików jest o wiele bardziej bezpieczne i praktyczne. Oczywiście istnieje wiele sytuacji gdy narzut
czasowy wprowadzany przez bazę danych lub wymagania aplikacji powodują, że pliki są jedynym sensownym
rozwiązaniem.
Na wydruku 4.1 pokazane zostało w jaki sposób można zrealizować liczniki dostępu do stron witryny. Do
tego celu wykorzystane zostały podstawowe operacje na plikach, otwarcie, odczyt, zapis i zamknięcie prostego
pliku śladu. W przykładzie tym nie zostały wykorzystane wszystkie dostępne w PHP funkcje operujące na
plikach. Bardziej szczegółowy opis wszystkich funkcji znajdują się w skorowidzu funkcji na końcu książki.
Wydruk 4.1. Użycie plików do zliczania odwołań do stron witryny
<?php
/*
Plik ten może być dołączany do dowolnego skryptu PHP.
Powoduje to automatyczne zliczanie odwołań do strony.
UŻYCIE: Wystarczy dołączyć ten plik. Tworzy on zmienną
globalną $aPageAccessCount, która zawiera ilość
odwołań do skryptu który dołącza ten plik.
*/
error_reporting( 0 );
$aLogFilePath = "/www/auto_logs/access.log";
$aCountArray = array();
// Sprawdzenie czy plik istnieje
if ( is_file( $aLogFilePath ) == True )
{
// Otwarcie i odczytanie pliku. Format pliku to oddzielone tabulatorami
// pary opisujące kolejne skrypty:
// ścieżka-do-skryptu licznik
$aFile = fopen( $aLogFilePath, "r" );
while( !feof( $aFile ) )
{
$aLine = fgets( $aFile, 1024 );
$aTempArray = explode( "\t", $aLine );
if ( count( $aTempArray ) == 2 )
{
$aCountArray[$aTempArray[0]] = $aTempArray[1];
}
}
fclose( $aFile );
}
// Ustawienie globalnego licznika odwołań do strony
// i uaktualnienie tablicy temp
$aPageAccessCount = $aCountArray[$PATH_TRANSLATED] + 1;
$aCountArray[$PATH_TRANSLATED] = $aPageAccessCount;
// Zapis całej tablicy do pliku
$aFile = fopen( $aLogFilePath, "w" );
foreach ( $aCountArray as $aKey => $aValue )
{
fputs( $aFile, "$aKey\t$aValue\n" );
}
fclose( $aFile );
?>
Na wydruku 4.1 pokazujemy użycie jednego pliku do przechowywania liczników odwołań do dowolnej
liczby stron witryny. Nie jest to efektywny sposób, ale pokazuje ideę takiego licznika. W skrypcie tym
sprawdzamy za pomocą funkcji is_file() czy istnieje plik śladu. Jeżeli plik ten istnieje, jego kolejne linie są
odczytywane i analizowane. Każda linia zawiera pełną ścieżkę dostępu do skryptu, znak tabulacji i wartość
licznika. Linia taka jest dzielona przy pomocy funkcji explode() na nazwę skryptu i wartość licznika a następnie
wartości te są zapisywane w tablicy asocjacyjnej. Jeżeli chcesz, możesz użyć tej tablicy do wyświetlenia
liczników dla wszystkich stron witryny a nie tylko bieżącej strony. Po wypełnieniu tablicy uaktualniany jest
licznik odwołań do bieżącej strony (rozpoznawanej przy użyciu zmiennej globalnej PHP $PATH_TRANSLATED) i
wartość ta jest przypisywana do zmiennej $aPageAccessCount. Na koniec cała tablica jest zapisywana do pliku
śladu.
Na wydruku 4.2 pokazane jest strona demonstrująca jak łatwo można użyć tego licznika. Jeżeli szukasz
takiego mechanizmu do twojej witryny, należy pamiętać, że jest to bardzo nieefektywne rozwiązanie. Bardziej
efektywne jest odczytywanie i zapis tylko jednej wartości a nie całego pliku.
Wydruk 4.2. Użycie skryptu z wydruku 4.1
<?php
include( "auto_counter.php" );
?>
<html>
<head>
<title>Strona testowa 1</title>
</head>

<body>

Strona ta była oglądana <b>
<?php
print( $aPageAccessCount );
?>
</b> razy.

</body>
</html>

Użycie gniazd
PHP umożliwia dostęp do surowych gniazd TCP/IP, za pomocą których można komunikować się z innymi
aplikacjami za pomocą dowolnego protokołu. Niektóre z bardziej znanych protokołów TCP/IP, na przykład
HTTP, POP3 i SMTP posiadają swoje implementacje w PHP, więc nie musisz w tych przypadkach używać
surowych gniazd.
Na wydruku 4.3 pokazano sposób dostępu za pomocą gniazd do serwera quotd, który zwraca cytat dnia.
Protokół quotd jest bardzo prosty. Po zestawieniu połączenia serwer wysyła strumień danych tekstowych a
następnie zamyka połączenie. Z perspektywy klienta wystarczy jedynie zestawić połączenie, odczytać dane a
następnie zakończyć połączenie.

59 PHP – Kompendium wiedzy
Wydruk 4.3. Użycie gniazd
<html>
<head>
<title>Przykład wykorzystania serwera QOTD: Użycie gniazd w PHP</title>
</head>
<body>
<?php
// otwarcie gniazda serwera qotd
$aFile = fsockopen( "208.129.36.164", 17 );
// odczytanie wszystkich danych ze strumienia
while ( !feof( $aFile ) )
{
$aLine = fgets( $aFile, 1024 );
print( "$aLine<br>" );
}
fclose( $aFile );
?>
</body>
</html>

Użycie potoków
Tak jak w przypadku gniazd, potoki są traktowane jak kolejny uchwyt pliku. Jedyną różnicą pomiędzy
plikiem i potokiem jest to, że potok jest jednokierunkowym strumieniem danych. Potok może być użyty do
odczytu danych wyjściowych z programu lub skryptu. Na wydruku 4.4 pokazane jest użycie potoku do odczytania
wyniku zapytania do polecenia whois, które jest dostępne w większości systemów Unix. Ten prosty skrypt i
formularz pozwalają na wprowadzenie zapytania dla whois. Skrypt ten ilustruje również częstą praktykę używania
tego samego skryptu do wyświetlenia formularza i przetworzenia jego danych.
Wydruk 4.4. Skrypt przetwarzający zapytanie whois
<?php /* whois.php */
// ścieżka do programu whois
$whois_prog = '/usr/bin/whois';
if ( !is_file( $whois_prog ) )
{
// nie udało się znaleźć programu
echo "Nie mogę znaleźć $whois_prog!<br>";
exit;
}
?>
<html>
<head>
<title>Whois: Uzycie potoków w PHP</title>
</head>
<body>

<?php
if ( $REQUEST_METHOD == 'POST' )
{
// otwarcie potoku do polecenia whois
if ( $aFile = popen( "$whois_prog $WhoisQuery", "r" ) )
{
// odczytanie wszystkich danych z potoku
while ( !feof( $aFile ) )
{
$aLine = fgets( $aFile, 1024 );
print( "$aLine<br>" );
}
pclose( $aFile );
}
else
{
echo "Nie mogę otworzyć $whois do odczytu!<br>";
}
print( "<hr>" );
}
?>
<form action="<?php echo $PHP_SELF ?>" method="post">
Wprowadź zapytanie <b>whois</b>: <input type="text" name="WhoisQuery">
<input type="submit" name="Submit" value="Submit">
</form>
</body>
</html>

Rozdział 4 – Operacje na plikach 60
Klasa File
W poprzednim rozdziale wspominaliśmy, że do PHP dostępne są świetne narzędzia dodatkowe
pochodzące z różnych źródeł. Klasa File dostępna z WebMasters Net (http://www.theWebMasters.net) jest
przydatnym narzędziem, szczególne wtedy, gdy twoja aplikacja intensywnie wykorzystuje pliki. Klasa ta zawiera
wiele często używanych funkcji PHP operujących na plikach i hermetyzuje kontrolę błędów, dzięki czemu
możesz więcej czasu poświęcić logice aplikacji zamiast zajmować się pisaniem podstawowych konstrukcji
kontroli błędów.
Na wydruku 4.5 pokazany jest skrypt wyświetlający nazwy wszystkich plików w bieżącym katalogu w
postaci łączy. Gdy użytkownik kliknie łącze, skrypt zamieszczony na wydruku 4.6 wyświetla jego zawartość
używając celu klasy File do odczytania jego zawartości.
Wydruk 4.5. Użycie klasy File do wyświetlenia zawartości bieżącego katalogu
<?php
include( "class.File.php3" );
?>
<html>
<head>
<title>Użycie klasy File</title>
</head>
<body>
Poniżej znajduje się lista plików w bieżącym katalogu.<br>
Kliknij nazwę pliku aby zobaczyć ich zawartość.<br><br>
<?php
$aFileClass = new File();
$aDirContents = $aFileClass->get_files( "." );
for ( $nIndex = 0; $nIndex < count( $aDirContents ); $nIndex++ )
{
$aCurFile = $aDirContents[$nIndex];
print( "<a href=\"disp_file.phtml?fn=$aCurFile\">" );
print( "$aCurFile</a><br>" );
}
?>
</body>
</html>
Wydruk 4.6. Wyświetlenie zawartości pliku za pomocą klasy File
<?php
include( "./class.File.php3" );
?>
<html>
<head>
<title>Użycie klsy File</title>
</head>
<body>
<?php
print( "The file <b>$fn</b>:<br><br>" );
$aFileClass = new File();
$aFileCont = $aFileClass->read_file( $fn );
print( "<pre>" );
print( nl2br( htmlentities( $aFileCont ) ) );
print( "</pre>" );
?>
</body>
</html>

Podsumowanie
Zdecydowanie się na użycie plików w aplikacji opartej na WWW jest jedną z krytycznych decyzji w fazie
projektowania aplikacji. Noe wszystkie aplikacje używają plików, ale aby efektywnie korzystać z różnych typów
strumieni danych, na przykład gniazd i potoków, należy poznać sposoby korzystania z uchwytów plików i funkcji
operujących na plikach. Rozdział ten zawiera nie tylko opis podstawowych operacji na plikach i systemie plików,
ale również przedstawia dodatkową klasę ułatwiającą operacje na plikach. Opis operacji na plikach zawarty w
tym rozdziale oraz opis formularzy zamieszczony w rozdziale poprzednim stanowią odpowiednią podstawę do
następnego rozdziału, „Wysyłanie plików przez formularz”.

61 PHP – Kompendium wiedzy
Rozdział 5. Wysyłanie plików przez
formularz
Wstęp
Poprzednie dwa rozdziały omawiały niezbędne podstawy dla tego rozdziału, ponieważ wysyłanie plików
wymaga poznania zarówno formularzy HTML, jak i funkcji systemu plików. Obsługa przesyłania plików w PHP
jest bardzo łatwa. PHP posiada wbudowany mechanizm pozwalający na odebranie pliku wysłanego z przeglądarki
zgodnej z RFC 1867. Większość nowoczesnych przeglądarek jest zgodnych z tym dokumentem, ponieważ został
on włączony do standardu HTML 3.2.
Jeżeli pozwolisz użytkownikom na wysyłanie plików za pomocą formularza, musisz rozważyć
dopuszczalne typy plików oraz ich wielkości. Mechanizm wbudowany w PHP działa świetnie dla małych plików,
ale jeżeli masz zamiar przesyłać duże pliki należy się zastanowić nad zastosowaniem innego mechanizmu, a
przykład anonimowego FTP. Możesz również pomyśleć o stworzeniu dodatkowego mechanizmu przesyłania
plików, jeżeli są one niezbędne do działania aplikacji.

Wysyłanie pojedynczego pliku
Formularz przy pomocy którego można przesyłać pliki różni się kilkoma szczegółami od zwykłego
formularza HTML. Znacznik <FORM> musi posiadać atrybut ENCTYPE ustawiony na multipart/form-data zamiast
domyślnego application/x-www-form-urlencoded. Musisz również umieścić na formularzy znacznik <INPUT> typu
file. Wydruk 5.1. zawiera prosty formularz HTML zawierający jeden znacznik <INPUT>.
Wydruk 5.1. Formularz HTML ze znacznikiem <INPUT>
<html>
<head>
<title>Formularz do przesyłania plików</title>
</head>
<body>
<form action="upload_single.phtml"
method="post" enctype="multipart/form-data">
Wyślij plik: <input type="file" name="thefile"><br><br>
<input type="submit" name="Submit" value="Wyślij">
</form>
</body>
</html>
Po wysłaniu danych formularza z wydruku 5.1, PHP tworzy automatycznie cztery zmienne globalne, które
opisują przesłany plik:
• $thefile — Zmienna zawiera nazwę pliku tymczasowego w którym znajduje się plik przesłany na serwer.
• $thefile_name — Zmienna ta zawiera nazwę pliku na komputerze z którego został wysłany.
• $thefile_size — Zmienna zawiera wielkość przesłanego pliku w bajtach.
• $thefile_type — Zmienna ta zawiera typ MIME przesyłanego pliku (o ile przeglądarka udostępnia taką
informację).
Nazwy tych zmiennych są tworzone w oparciu o nazwę znacznika <INPUT> w formularzu, tak jak jest to
pokazane na Wydruku 5.1. Pisząc skrypt obsługujący przesyłanie pliku należy pamiętać, że PHP automatycznie
usuwa plik tymczasowy po zakończeniu skryptu, więc jeżeli nie skopujesz go, plik zostanie stracony.
Skrypt na wydruku 5.2 zawiera kod obsługi przesyłania pliku poprzez formularz z Wydruku 1 i jeżeli plik
jest rysunkiem (w formacie GIF lub JPEG) mniejszym od 100 kB, jest on wyświetlany. Jeżeli przesłany plik nie
ma właściwego typu lub jest większy, wyświetlany jest komunikat błędu.
Wydruk 5.2. Obsługa przesyłania pliku
<?php
$aErrors = "";
if ( !empty( $thefile_name ) ) // nie wybrano pliku
{
if ( ( $thefile_type == "image/gif" ) ||
( $thefile_type == "image/pjpeg" ) ||
( $thefile_type == "image/jpeg" ) )
{
if ( $thefile_size < ( 1024 * 100 ) )
{
$aCurBasePath = dirname( $PATH_TRANSLATED );
$aNewName = $aCurBasePath . "/uppics/" .
$thefile_name;
copy( $thefile, $aNewName );
}
else
{
$aErrors .= "Za duży plik !!!";
}
}
else
{
$aErrors .= "Plik nie jest typu gif ani jpeg";
}
}
else
{
$aErrors .= "Nie wybrano pliku";
}
?>
<html>
<head>
<title>Wyświetlenie przesłanego pliku</title>
</head>
<body>

<?php
if ( $aErrors != "" )
{
print( "<b>Wystąpił błąd</b>: $aErrors<br>" );
}
else
{
print( "Przesłany plik:<br><br>" );
print( "<img src=\"uppics/$thefile_name\" border=\"0\">" );
}
?>
</body>
</html>
W przykładzie zamieszczonym na wydruku 5.2. nie wzięto pod uwagę, że nie wszystkie przeglądarki
wysyłają typu MIME pliku. Opuszczono również inne zagadnienia kontroli błędów, na przykład kontrolę
poprawności wykonania funkcji copy. Jednak przykład ten miał za zadanie pokazanie jak łatwo można obsłużyć
za pomocą PHP operacje przesyłania pliku.
W przykładzie tym na początku sprawdzane jest, czy został wybrany plik do przesyłania. Jeżeli nie został
wybrany plik, zmienna $thefile_name jest pusta. Następnie sprawdzane jest, czy plik ma odpowiednią wielkość i
typ MIME. Jeżeli obie wartości zostaną zaakceptowane, przy pomocy wyrażenia dirname($PATH_TRANSLATED)
odczytywany jest bieżący katalog na serwerze WWW. Funkcja dirname() zwraca nazwę katalogu z podanej
ścieżki. Zmienna $PATH_TRANSLATED jest zmienną PHP i zawiera pełną ścieżkę do bieżącego skryptu. Dodając
/uppics/ i oryginalną nazwę pliku na komputerze lokalnym, tworzymy nową ścieżkę. Na koniec, przesłany plik
jest kopiowany z katalogu tymczasowego do katalogu określonego przez przed chwilą skonstruowaną ścieżkę.
Należy pamiętać, że aby operacja kopiowania się udała, docelowy katalog musi posiadać odpowiednio ustawione
uprawnienia. Korzystając z Apache w systemie Linux oznacza to, że uprawnienia do katalogu muszą pozwolić na
zapis przez użytkownika nobody.
PHP posiada mechanizm pozwalający na ograniczanie w skrypcie wielkości przesyłanych plików. Jest to
realizowane przez dodanie do formularza ukrytego pola o nazwie MAX_FILE_SIZE. Na wydruku 5.3 pokazany jest
formularz identyczny z tym z wydruku 5.1, ale dodane zostało pole MAX_FILE_SIZE ograniczające wielkość
przesyłanych plików do 100 kB.
Wydruk 5.3. Ograniczenie wielkości przesyłanego pliku za pomocą MAX_FILE_SIZE
<html>
<head>

63 PHP – Kompendium wiedzy
<title>Formularz do przesyłania plików</title>
</head>
<body>
<form action="upload_single.phtml" method="post" enctype="multipart/form-data">
<INPUT TYPE="hidden" name="MAX_FILE_SIZE" value="102400">
Wyślij plik: <input type="file" name="thefile"><br><br>
<input type="submit" name="Submit" value="Wyślij">
</form>
</body>
</html>

Pułapki
PHP domyślnie ogranicza wielkość plików, jakie można przesyłać używając tego mechanizmu, do 2
megabajtów. Ta wielkość jest ważniejsza od zmiennej formularza MAX_FILE_SIZE. Wartość ta może być zmieniona
przez ustawienie wartości upload_max_filesize w pliku php.ini, lub ustawienie dyrektywy w pliku Apache.conf
(więcej szczegółów znajduje się w rozdziale o opcjach konfiguracji, który znajduje się na końcu książki). Gdy
osiągnięta zostanie graniczna wielkość pliku (zarówno ustawiona w formularzu jak i globalne maksimum), PHP
generuje błąd, przerywa przesyłanie i ustawia nazwę pliku na none.
Mimo tego, że ta graniczna wielkość pliku jest ustawiana w celu chronienia serwera WWW, sieje ona
zniszczenie w twoich aplikacjach. Ponieważ błąd przekroczenia wielkości przesyłanego pliku występuje przed
wykonaniem jakiejkolwiek linii skryptu, nie ma możliwości przechwycenia generowanego ostrzeżenia
generowanego przez mechanizm przesyłania plików. Oznacza to, że jeżeli opcja konfiguracji display_errors ma
wartość On (domyślnie) w przeglądarce będzie się pojawiał komunikat błędu.
Jeżeli nie chcesz aby pojawiał się ten komunikat, musisz ustawić w pliku php.ini opcję konfiguracji
display_errors na Off. Możesz następnie ustawić opcję log_errors na On a error_log na wartość odpowiednią
dla twojego środowiska. Jeżeli używasz Linuksa i Apache, ustawienie error_log na stderr spowoduje, że
wszystkie błędy PHP trafią do dziennika błędów Apache. Aplikacja twoja może sprawdzać zmienne przesyłu
plików i odpowiednio obsługiwać błędy. Jeżeli użytkownikowi nie uda się przesył pliku, zmienna $thefile
będzie miała wartość none, a $thefile_name będzie zawierała odpowiednią wartość.
Innym problemem, nad jakim należy się zastanowić w trakcie pisania skryptu obsługi przesyłania plików
jest to, że zanim rozpocznie się wykonywanie skryptu musi zostać przesłany cały plik lub maksymalna określona
ilość bajtów. Jeżeli więc twoja aplikacja dopuszcza przesyłanie dużych plików, ale akceptuje jedynie niektóre
typy plików, twoi użytkownicy mogą dosyć długo czekać zanim zobaczą komunikat o odrzuceniu przesyłanego
pliku.

Przesyłanie wielu plików
Jeżeli chcesz przesłać kilka plików używając jednego formularza możesz skorzystać z tablicy PHP do
przesłania danych o przychodzących plikach. Poniższy przykład pokazuje użycie tablicy do przesłania czterech
plików.
Wydruk 5.4. Przesyłanie czterech plików
<html>
<head>
<title>Formularz do przesyłania plików</title>
</head>
<body>
Proszę podać cztery pliki rysunków do przesłania:
<form action="upload_multiple.phtml" method="post" enctype="multipart/form-data">
Plik 1: <input type="file" name="thefiles[]"><br><br>
Plik 2: <input type="file" name="thefiles[]"><br><br>
Plik 3: <input type="file" name="thefiles[]"><br><br>
Plik 4: <input type="file" name="thefiles[]"><br><br>
<input type="submit" name="Submit" value="Wyślij">
</form>
</body>
</html>
Wydruk 5.5. Obsługa czterech przesyłanych plików
<?php
$aBasePath = dirname( $PATH_TRANSLATED );
// każdy przesłany plik należy skopiować

Rozdział 5 – Wysyłanie plików przez formularz 64
// i zapamiętać ich nowe ścieżki do późniejszego wykorzystania
for ( $nIndex = 0; $nIndex < count( $thefiles ); $nIndex++ )
{
if ( !empty( $thefiles_name[$nIndex] ) )
{
$aType = $thefiles_type[$nIndex];
if ( ( $aType == "image/gif" ) ||
( $aType == "image/pjpeg" ) ||
( $aType == "image/jpeg" ) )
{
$aNewName = $aBasePath . "/uppics/" .
$thefiles_name[$nIndex];
copy( $thefiles[$nIndex], $aNewName );
$aNewNames[] = $thefiles_name[$nIndex];
}
}
}
?>
<html>
<head>
<title>Wyświetlanie przesłanego rysunku</title>
</head>
<body>

<?php
$aCount = count( $aNewNames );
print( "Przesłano <b>$aCount</b> rysunki:<br><br>" );
foreach( $aNewNames as $aNewName )
{
print("<img src=\"uppics/$aNewName\" border=\"0\"><br><br>");
}
?>
</body>
</html>

Bezpieczeństwo
Jeżeli dopuszcza się dostarczanie jakichkolwiek danych do aplikacji, należy brać pod uwagę każdą
ewentualność. Jeżeli pozwalasz na przesyłanie plików musisz się upewnić, że pliki te zostaną właściwie
obsłużone na serwerze. Na przykład, jeżeli tworzysz witrynę do której programiści mogą przesyłać własne
skrypty nie należy pozwalać na wykonywanie tych skryptów na serwerze. Można je jedynie odebrać i wyświetlić
w postaci czystego tekstu i nie można zakładać, że można je bezpiecznie uruchomić.
Nawet pozwolenie na wyświetlenie przesłanych plików niesie ze sobą potencjalne zagrożenie. Na
wydruku 5.6. pokazany jest prosty przykład w jaki sposób mechanizm wyświetlania plików może spowodować
dziurę w systemie bezpieczeństwa.
Wydruk 5.6. Naruszenie bezpieczeństwa podczas obsługi przesłanych plików
<?php
include( "class.File.php3" );
// skopiowanie przesłanego pliku
$aCurBasePath = dirname( $PATH_TRANSLATED );
$aNewName = $aCurBasePath . "/uploads/" . $thefile_name;
copy( $thefile, $aNewName );
?>
<html>
<head>
<title>Naruszenie bezpieczeństwa przy przesyłaniu pliku</title>
</head>
<body>
<?php
// wyświetlenie zawartości pliku
print( "Plik <b>$aNewName</b>:<br><br>" );
$aFileClass = new File();
$aFileCont = $aFileClass->read_file( $thefile );
print( "<pre>" );
print( nl2br( htmlentities( $aFileCont ) ) );
print( "</pre>" );
?>
</body>
</html>
Jest to oczywiście wymyślony przykład. W przykładzie tym przesłany plik jest kopiowany do nowego
katalogu, ale wyświetlając plik odczytywany i wysyłany do przeglądarki jest plik tymczasowy. W rzeczywistości

65 PHP – Kompendium wiedzy
prawdopodobnie odczytasz i wyświetlisz plik znajdujący się na ścieżce zapamiętanej w $aNewName. Dla potrzeb tej
prezentacji poprzedni plik pokazuje w jaki sposób źle napisany skrypt narusza system bezpieczeństwa.
Aby wykorzystać niedoskonałość skryptu ktoś może wpisać do przeglądarki nazwę skryptu i podać nazwę
dowolnego pliku na serwerze. Na przykład wprowadzenie takiego adresu URL spowoduje wyświetlenie
zawartości pliku /etc/passwd (zakładając, że będzie on wykonywany na systemie Uniksowym):
http://serwer.com/sciezka/upload_flaw.phtml?thefile=/etc/passwd
Można przetestować to na komputerze z Uniksem, że niebezpieczeństwo jest rzeczywiste. Nawet mimo
tego, że serwer WWW pracuje jako użytkownik nobody, plik /etc/passwd musi być możliwy do odczytania przez
wszystkich użytkowników.
W rozdziale o formularzach kładłem nacisk na to, że nie wolno zakładać, że wszyscy użytkownicy
aplikacji będą używali jej zgodnie z twoimi zamiarami. Tak samo jest i teraz. Niektórzy użytkownicy będą chcieli
rozmyślnie wykorzystać słabości aplikacji a inni nieświadomie spowodują jej awarię. Należy dokładnie
przemyśleć wszystkie możliwe skutki uboczne pozwolenia na przesyłanie plików na serwer WWW.

Podsumowanie
W rozdziale tym pokazane zostały sposoby odczytywania i wykorzystania plików przesłanych przez
przeglądarki zgodne z dokumentem RFC 1867. Zostały przytoczone przykłady obsługi jednego pliku jak również
tablicy plików. Na końcu rozdziału znajduje się mała część ilustrująca w jaki sposób źle napisany skrypt może
stworzyć dziurę w systemie bezpieczeństwa serwera.
Dopuszczenie do przesyłania plików do aplikacji może być w wielu przypadkach użyteczne, ale należy
pamiętać, że niektórzy użytkownicy mogą nie posiadać dostatecznie szybkiego łącza aby efektywnie korzystać z
tego mechanizmu, więc dobrym pomysłem jest zapewnienie jeszcze jednego sposobu na dostarczanie plików do
aplikacji.

Rozdział 5 – Wysyłanie plików przez formularz 66
Rozdział 6. Współpraca z bazami danych
Wstęp
Jedną z najważniejszych cech nowoczesnych języków programowania lub narzędzi programistycznych
jest zdolność współpracy z bazą danych. Jest to spowodowane tym, że systemy zarządzania relacyjnymi bazami
danych (SZRBD) posiadają wiele bardzo wydajnych i niezwykle użytecznych mechanizmów zarządzania danymi,
jak na przykład indeksowanie, relacje pomiędzy danymi, obsługa transakcji, kaskadowe operacje wykonywane na
danych i wiele innych. PHP pozwala na dostęp do danych przy użyciu bogatego zestawu funkcji związanych z
bazami danych.

Wprowadzenie
Jak można wywnioskować na podstawie dokumentacji, autorzy PHP uważają obsługę baz danych za jedną
z najważniejszych i najsilniejszych cech PHP. Obsługiwane są między innymi takie bazy danych:
Adabas D InterBase Solid
dBase mSQL Sybase
Empress MySQL Velocis
FilePro Oracle Unix dbm
Informix PostgreSQL Mictosoft SQL Server
ODBC
Obsługując ODBC, PHP może zostać użyty do prawdopodobnie dowolnej istniejącej bazy danych. Z
powodu ogromnej ilości obsługiwanych baz danych jest niemożliwe szczegółowe omówienie obsługi każdej z
nich w tej książce. Dodatkowo, język SQL jest sam w sobie niezwykle bogatym i wydajnym narzędziem, które
również nie zostanie odpowiednio dokładnie opisane w tej książce. Najlepiej posiłkować się dokumentacją
odmiany SQL zaimplementowanej w używanej przez ciebie bazie danych. Zakładamy w tym rozdziale, że
czytelnicy znają podstawy SQL w stopniu wystarczającym do zrozumienia przykładów zamieszczonych na
wydrukach.
W rozdziale tym skupimy się na przykładach użycia MySQL i ODBC. Wybrałem MySQL ponieważ jest
to wydajna baza danych dostępna na zasadach licencji GNU General Public License (GPL) i jest powszechnie
używana do współpracy z PHP. ODBC wybrałem, ponieważ do większości baz danych dostępne są sterowniki
tego standardu. Przykłady ilustrują zastosowanie języka PHP i nie zawsze pokazują najlepsze zastosowania SQL
oraz działania na bazach danych.

Funkcje baz danych
Każda z obsługiwanych baz danych posiada własny zestaw funkcji PHP. Nazwy funkcji związanych z
MySQL rozpoczynają się od mysql_ i podobna zasada obowiązuje w przypadku innych baz (W skorowidzu
funkcji na końcu książki znajduje się kompletna lista funkcji związanych z bazami danych). Mimo, że każda z baz
danych ma własny zestaw funkcji, istnieje wspólny model dostępu do każdego z typów baz danych. Pseudokod
opisujący pobieranie danych z dowolnego systemu bazy danych przedstawiony jest na wydruku 6.1.
Wydruk 6.1. Pseudokod opisujący pobieranie danych z bazy
<?php
połącz_z_Bazą();
wybierz_bazę();
wyślij_wyrażenie_SQL();
pobierz_wynik();
while ( istnieje_wiersz )
pobierz_wiersz();
zamknij_połączenie();
?>
Następne dwie części zawierają szczegóły konfiguracji i użycia MySQL i ODBC.

MySQL
MySQL jest świetną bazą danych dla większości projektów. Oficjalną witryną MySQL jest
http://www.mysql.com. Na tej witrynie znajduje się najnowsza wersja systemu oraz dokumentacja opisująca
instalację i konfigurację MySQL w różnych środowiskach.

Rozpoczynamy pracę z MySQL
W zależności od twoich potrzeb i typu serwera, możesz albo ściągnąć źródła MySQL, dystrybucję binarną
albo RPM. Najszybszą metodą zainstalowania MySQL na systemie Linux działającym na platformie Intel jest
ściągnięcie pliku RPM i zainstalowanie go. Przy użyciu tej metody instalowane są wszystkie elementy serwera,
więc możesz od razu zaczynać pracę. Jeżeli używasz systemu działającego w oparciu o Win32, najszybszą
metodą pozyskania serwera jest ściągnięcie skompresowanej instalacji binarnej.
PHP4 posiada wbudowaną obsługę MySQL, więc nie musisz ponownie kompilować PHP aby używać
funkcji mysql_. Jednak jeżeli korzystasz z wbudowanej obsługi MySQL, nie można używać innych modułów
odwołujących się do MySQL, na przykład mod_auth_mysql i mod_perl. Jeżeli potrzebujesz modułów używających
MySQL, musisz przekompilować PHP podając opcję konfiguracji --with_mysql=/ścieżka/do/mysql.

Użycie MySQL
Po zainstalowaniu i uruchomieniu MySQL można rozpocząć pisanie skryptów PHP, które korzystają z
danych umieszczonych w bazie danych. Skrypt zamieszczony na wydruku 6.2 pokazuje jak proste jest użycie
MySQL do pobrania danych z bazy. Tabela której będę używał w dwóch kolejnych przykładach została
utworzona za pomocą następującego kodu SQL:
CREATE TABLE employees
(
id tinyint(4) DEFAULT '0' NOT NULL auto_increment,
first varchar(20),
last varchar(20),
adress varchar(255),
position varchar(50),
PRIMARY KEY (id),
UNIQUE id (id)
)
Wydruk 6.2. Pobieranie danych z bazy danych MySQL
<html>
<head>
<title>Pobieranie danych z MySQL</title>
</head>
<body>
<?php
// Ukrywamy komunikaty błędów i sami je obsługujemy
$aDBLink = @mysql_connect( "db.server.com", "user", "pass" );
if ( !empty( $aDBLink ) )
{
// wybór bazy danych MySQL
if ( mysql_select_db( "mydb", $aDBLink ) == True )
{
$aSQL = "select * from employees";
// Wykonanie zapytania SELECT
$aQResult = mysql_query( $aSQL, $aDBLink );
if ( $aQResult == True )
{
// Pobranie wiersza danych i wypisanie dwóch pól
while ( $aRow = mysql_fetch_array( $aQResult ) )
{
$aFName = $aRow["first"];
$aPos = $aRow["position"];
print( "$aFName, $aPos<br>" );
Rozdział 6 – Współpraca z bazami danych 68
}
mysql_free_result( $aQResult );
}
else
{
print( "Błąd wykonania zapytania<br>" );
}
}
else
{
print( "Błąd wyboru bazy danych<br>" );
}
}
else
{
print( "Błąd przy podłączaniu do bazy danych<br>" );
}
?>
</body>
</html>
Po uruchomieniu skryptu z wydruku 6.2, próbuje się on podłączyć do serwera bazy danych MySQL
działającego na komputerze db.serer.com podając nazwę użytkownika i hasło. Symbol @ umieszczony przed
funkcją mysql_connect() powoduje zablokowanie wypisywania błędów i ostrzeżeń. Podczas testowania możesz
opuścić ten symbol, ale w normalnej pracy należy go używać i stosować własne procedury obsługi błędów.
Następną czynnością wykonywaną przez skrypt jest wybranie odpowiedniej bazy danych, w naszym przypadku
mydb. Jeżeli się to powiodło, przy pomocy funkcji mysql_query() zadawane jest zapytanie do bazy danych. W
naszym przykładzie jest to zapytanie SELECT, które powoduje pobranie rekordów z bazy danych. Może być to
dowolne zapytanie, na przykład: INSERT, UPDATE, ADD TABLE lub dowolne inne zapytanie SQL. Wynik funkcji jest
różny od zero w przypadku powodzenia i zero w przypadku błędu. Dodatkowo, jeżeli zapytanie jest typu SELECT,
wynik funkcji jest identyfikatorem wyniku przekazywanym do funkcji mysql_result(), mysql_fetch_array(),
mysql_fetch_lengths(), mysql_fetch_object(), i mysql_fetch_row(), które są używane do odczytania
wynikowych danych. W naszym przykładzie używamy funkcji mysql_fetch_array() do odczytania wiersza z
wynikowych danych a następnie wyświetlane są dane z odpowiednich pól.
Funkcje mysql_fetch_array() i mysql_fetch_row() są podobne do siebie i zwracają jeden wiersz wyniku
w postaci tablicy. Funkcja mysql_fetch_array() zwraca wynik w postaci tablicy asocjacyjnej indeksowanej
nazwami kolumn. Wywołanie funkcji mysql_fetch_row() zwraca tablicę indeksowaną liczbami. Wywołanie
jednej z tych funkcji zwraca kolejny wiersz danych zwracanych przez zapytanie i przesuwa wewnętrzny wskaźnik
do następnego wiersza. Jeżeli nie ma więcej danych, funkcja zwraca False. Wywołanie funkcji
mysql_fetch_array() nie jest zauważalnie wolniejsze niż wywołanie mysql_fetch_row(), a dostarcza o wiele
więcej danych.
Używając skryptu z wydruku 6.2 jako podstawy, można napisać wszystkie możliwe aplikacje oparte na
bazie danych. Funkcje MySQL posiadają dodatkowo kilka cech, które nie są dostępne dla wszystkich baz
obsługiwanych przez PHP. Dostarczone są specjalizowane funkcje do tworzenia i usuwania baz danych oraz
funkcje umożliwiające odczytanie struktury bazy danych. Na przykład za pomocą funkcji mysql_list_tables()
można uzyskać listę wszystkich tabel w bazie danych.
Jedną z moich ulubionych funkcji dla MySQL jest mysql_insert_id(). Używając pól o atrybucie
auto_increment można po prostu zapisać dane do tabeli a następnie odczytać unikalny identyfikator rekordu za
pomocą funkcji mysql_insert_id(). Na wydruku 6.3 mamy formularz, który pozwala na wprowadzenie danych
nowego pracownika do bazy używanej na wydruku 6.2. Skrypt korzysta z funkcji mysql_insert_id() w celu
zrealizowania potwierdzenia operacji wstawienia danych.
Wydruk 6.3. Wstawianie rekordu do bazy danych MySQL
<?php
/*
Funkcja InsertRecord
Wstawia mowy rekord do tabeli employees.
W przypadku powodzenia operacji zwraca identyfikator nowego rekordu
a w przypadku błędu wartość ujemną wskazującą na przyczynę błędu.
*/
function InsertRecord( $aFirstName, $aLastName, $aAddr, $aPos )
{
// Przygotowanie wyrażenia SQL INSERT
$aSQL = "insert into employees ( first, last, address, ";
$aSQL .= "position ) values ( '$aFirstName', '$aLastName', ";
$aSQL .= "'$aAddr', '$aPos' )";
// Przyłączenie do serwera i wykonanie instrukcji INSERT

69 PHP – Kompendium wiedzy
$aDBLink = @mysql_connect( "db.server.com", "root", "" );
if ( !empty( $aDBLink ) )
{
if ( mysql_select_db( "mydb", $aDBLink ) == True )
{
$aQResult = mysql_query( $aSQL, $aDBLink );
if ( $aQResult == True )
{
$aResult = mysql_insert_id( $aDBLink );
}
else
{
// print( "Błąd wykonania zapytania<br>" );
$aResult = -1;
}
}
else
{
// print( "Błąd wyboru bazy danych<br>" );
$aResult = -2;
}
}
else
{
// print( "Błąd przy podłączaniu do bazy danych<br>" );
$aResult = -3;
}
return $aResult;
}
?>
<html>
<head>
<title>Przykład MySQL: Wstawianie danych do bazy </title>
</head>
<body>

<?php
if ( $REQUEST_METHOD == 'POST' )
{
// Nastąpiło przesłanie danych formularza
$aResult = InsertRecord( $FirstName, $LastName,
$Address, $Position );
if ( $aResult > 0 )
{
print( "Dodano nowy wiersz, ID = $aResult<br>" );
}
else
{
print( "Błąd funkcji InsertRecord. Kod błędu = $aResult<br>" );
}
print( "<hr>" );
}
?>
Proszę wpisać dane nowego pracownika:<br>
<form action="<?php echo $PHP_SELF ?>" method="post">
Imię: <input type="text" name="FirstName" maxlength="20"><br>
Nazwisko: <input type="text" name="LastName" maxlength="20"><br>
Adres: <input type="text" name="Address" maxlength="255"><br>
Stanowisko: <input type="text" name="Position" maxlength="50"><br><br>
<input type="submit" name="Submit" value="Wyślij">
</form>
</body>
</html>
W skrypcie z wydruku 6.3, funkcja IndertRecord() zawiera całą logikę wstawienia nowego rekordu do
bazy danych. Zwraca on identyfikator nowego rekordu (wartość przypisywana przez MySQL do kolumny id) lub
wartość ujemną oznaczającą jedną z trzech obsługiwanych sytuacji błędnych. Realistycznie patrząc, Ten typ
aplikacji powinien zawierać o wiele więcej kodu odpowiedzialnego za obsługę błędów, na przykład sprawdzanie
pustych pól, ale dla naszych potrzeb kod ten nie został rozmyślnie wprowadzony. Ponieważ pole id w tabeli
employees jest polem typu auto_increment, MySQL automatycznie generuje jednoznaczne wartości tego pola
przy każdym wstawieniu rekordu. W naszym przykładzie wartość ta jest odczytywana za pomocą funkcji
mysql_insert_id().
Przykład ten miał na celu pokazanie prostoty korzystania z baz danych w PHP. Więcej przykładów użycia
baz danych w aplikacjach WWW można znaleźć w rozdziale 15 „Witryny oparte o bazę danych”. Znajdują się
tam bardziej złożone przykłady zawierające obsługę błędów i skomplikowane zapytania.

Rozdział 6 – Współpraca z bazami danych 70
MySQL jest wydajną bazą danych posiadającą funkcje wystarczające do tworzenia większości typów
aplikacji WWW. Jest ona szybka, solidna i zawiera większość funkcji dostępnych w komercyjnych bazach
danych. Jednak w czasie pisania tej książki MySQL nie zawierał mechanizmu transakcji. Niedostępne są również
niektóre elementy SQL, na przykład podzapytania. Jeżeli jeszcze nie wybrałeś swojego systemu bazy danych,
spisz swoje wymagania i porównaj ze specyfikacją dostępnych systemów. W przypadku tworzenia aplikacji o
wysokiej jakości koszt bazy nie jest jedynym czynnikiem jaki należy brać pod uwagę. Jeżeli twoja firma posiada
system bazy danych inny niż MySQL, następna część zawiera informacje na temat ODBC, które pomogą
podłączyć się do twoich istniejących danych.

ODBC
Open Database Connectivity (ODBC) to powszechnie stosowany interfejs API (application programming
interface) służący do łączenia się z bazami danych. Jest on oparty na specyfikacji Call Level Interface
pochodzącym z X/Open oraz ISO/IEC i jako języka dostępu do danych używa SQL. Istnieje kilka implementacji
ODBC API dla systemów Uniksowych. W systemie Windows ODBC jest zwykle instalowany razem z systemem.

Podstawy ODBC
PHP może obsługiwać praktycznie każdą implementację ODBC, ale musi być w tym celu odpowiednio
skonfigurowany, ponieważ ODBC nie jest w chwili obecnej domyślną opcją. W PHP istnieją cztery opcje
konfiguracji związane z ODBC: --with-unixODBC, --with-custom-ODBC, --with-iodbc oraz --with-openlink.
Opcje te są lepiej opisane w skorowidzu na końcu książki. W rozdziale tym przykłady korzystają z implementacji
ODBC unixODBC (http://www.unixodbc.org/). Jest on dostępny na zasadach licencji GPL lub LGPL i jest bardzo
łatwy do instalacji i konfigurowania.
ODBC tym różni się od MySQL i innych API baz danych tym, że wszystkie odwołania do bazy danych
wykonuje za pośrednictwem sterownika bazy danych. Oznacza to, że najpierw musisz zainstalować program
zarządzający sterownikami, na przykład unixODBC, a następnie sterownik do twojego systemu bazy danych. Na
rysunku 6.1. pokazane są powiązania pomiędzy komponentami aplikacji PHP opartej o ODBC. Aplikacja
wywołując funkcję, na przykład odbc_connect(), kontaktuje się z zarządcą sterowników. Zarządca ten jest
odpowiedzialny za załadowanie odpowiedniego sterownika bazy danych i przekazanie do niego żądania.
Sterownik bazy danych wywołuje odpowiednią funkcję bazy danych, która realizuje nasze żądanie.
Rysunek 6.1. PHP Application - aplikacja PHP
Komponenty Driver Manager - zarządca sterowników
aplikacji PHP Database Driver - sterownik do bazy danych
korzystającej z DBMS - SZRBD
PHP
Ponieważ ODBC wymaga zastosowania zarządcy sterowników oraz sterownika odpowiedniej bazy
danych, instalacja i konfiguracja ODBC jest nieco bardziej skomplikowana niż konfiguracja MySQL. Również
każdy z używanych sterowników baz danych musi zostać zainstalowany i skonfigurowany.
W książce tej zostanie opisana instalacja i konfiguracja zarządcy sterowników unixODBC oraz sterownika
ODBC-ODBC Bridge (OOB), który można uzyskać z Easysoft Limited, http://www.easysoft.com/. Sterownik
OOB powoduje wzrost komplikacji struktury, ale posiada tak dużo zalet, że jest wart zainteresowania. Sterownik
ten pozwala na dostęp do baz danych zainstalowanych na różnych platformach za pomocą własnego modelu
klient-serwer. NA rysunku 6.2. zilustrowano sposób użycia sterownika OOB.
Rysunek 6.2. PHP Application - aplikacja PHP
Dodajemy Driver Manager - zarządca sterowników
sterownik OOB The OOB driver - sterownik OOB
OOB Client - klient OOB
Network - sieć
OOB Server - serwer OOB
Driver Manager - zarządca sterowników
Database Driver - sterownik do bazy danych

71 PHP – Kompendium wiedzy
DBMS - SZRBD
Zaletą stosowania sterownika OOB jest to, że możesz dzięki niemu używać ODBC w aplikacjach
działających na serwerze WWW i korzystać z danych z bazy danych działającej na innym komputerze (który
może działać na innym systemie operacyjnym). Dla przykładu w moim testowym systemie zainstalowałem Oracle
8i na serwerze Windows NT i utworzyłem prostą bazę danych. Następnie w moim linuksowym serwerze WWW
dodałem sterownik OOB. Sterownika tego można używać do podłączania się do dowolnej bazy zgodnej z ODBC,
działającej na dowolnej platformie.
Dodatkowym utrudnieniem jest to, że używając OOB należy kolejno zainstalować klienta i serwer OOB
na oddzielnych komputerach. Na szczęście na witrynie Easysoft bardzo łatwo jest odszukać i załadować
odpowiednie programy.
Kolejne trzy części omawiają instalowanie zarządcy sterowników unixODBC, kompilację PHP z obsługą
unixODBC oraz instalowanie sterownika OOB. Części te są przeznaczone dla użytkowników Linuksa i
zakładamy, że potrafisz kompilować programy dla tego systemu oraz, że masz zainstalowane wszystkie
niezbędne kompilatory i narzędzia.

Instalowanie i kompilowanie unixODBC
Po ściągnięciu i rozpakowaniu plików unixODBC, musisz skompilować zarządcę sterowników. W
instalacji unixODBC znajduje się standardowy skrypt służący do konfigurowania środowiska kompilacji. W celu
skompilowania mojej konfiguracji PHP użyłem następujących opcji:
./configure --disable--drivers --disable-threads --prefix=/usr/local/unixODBC --disable-gui
Ponieważ miałem już potrzebny sterownik, nie potrzebowałem aby unixODBC dodał swoje sterowniki
wewnętrzne. Powodem wyłączenia wątków jest to, że moja instalacja PHP jest w postaci dynamicznie
ładowanego modułu (--with-apxs) a Apache nie obsługuje domyślnie wątków. Skompilowanie tego modułu z
obsługa wątków spowodowałoby awarię Apache w trakcie ładowania modułu. Wyłączyłem również obsługę
graficznego interfejsu użytkownika, ponieważ nie mam na moim serwerze zainstalowanego środowiska
XWindows.

Kompilowanie PHP z obsługą unixODBC
Po skompilowaniu i zainstalowaniu zarządcy sterowników unixODBC należy przekompilować PHP z
włączoną obsługą unixODBC. Odpowiednią opcją konfiguracji jest --with-unixODBC=/sciezka/do/unixODBC.
Użyta ścieżka musi być taka sama jak ścieżka użyta w opcji --prefix podczas kompilowania unixODBC. W
moim przypadku jest to /usr/local/unixODBC.
Jeżeli statycznie łączysz PHP z Apache, musisz również przekompilować Apache. Jeżeli korzystasz z
dynamicznego łączenia, wystarczy wyłączyć Apache, zainstalować nowy moduł PHP i powtórnie uruchomić
Apache.

Instalowanie sterownika OOB
W moim przypadku musiałem zainstalować serwer OOB na komputerze z Windows NT i skonfigurować
go tak, aby przyjmował żądania. Wykonałem to uruchamiając program instalacyjny i wykonując wszystkie kroki
w programie instalacyjnym. Wszystko zadziałało bez problemów. Następnie ściągnąłem i zainstalowałem
oprogramowanie klienta na serwerze z systemem Linux. Proces ten był niespodziewanie łatwy, ponieważ
dostępny był program instalacyjny prowadzący użytkownika przez kolejne kroki procedury instalacyjnej. Można
również skorzystać z witryny Easysoft, gdzie na podstawie konkretnej konfiguracji otrzymamy szczegółowy opis
tego jak ściągnąć i zainstalować serwer i klienta OOB.

Konfigurowanie OOB
Po zainstalowaniu całego oprogramowania należy utworzyć nazwy źródeł danych (DSN) zarówno na
kliencie jak i na serwerze. Źródła danych są mechanizmem specyficznym dla ODBC służącym do opisywania
sposobu współpracy z systemem bazy danych. W Windows tworzy się DSN poprzez program Źródła danych
ODBC dostępny w Panelu sterowania. OOB wymaga utworzenia systemowego DSN a nie DSN użytkownika.
Program ten zawiera plik pomocy opisujący sposób tworzenia systemowych DSN.

Rozdział 6 – Współpraca z bazami danych 72
Aby utworzyć DSN na Linuksie należy zmienić przy pomocy graficznego narzędzia unixODBC lub
edytora tekstowego pliki odbcinst.ini i odbc.ini. Plik odbcinst.ini jest używany do opisu nazw sterowników i łączy
nazwy z plikami sterowników. Mój plik wygląda następująco:
[OOB]
Driver = /usr/local/easysoft/oob/client/libesoobclient.so
Setup = /usr/local/easysoft/oob/client/libesoobsetup.so
FileUsage = 1
Plik odbc.ini zawiera opis źródeł danych. Swoje źródło skonfigurowałem następująco (serwer i hasło jest
oczywiście zmyślone):
[localdsn]
Server=satabase.server.com
Driver=OOB
Port=8888
Transport=tcpip
LogonUser=prodplaner
LogonAuth=password
TargetDSN=LocalOracle
TargetUser=prodplaner
TargetAuth=password

Korzystanie z ODBC
Po zainstalowaniu i skonfigurowaniu wszystkich komponentów korzystanie z ODBC w PHP jest bardzo
podobne do korzystania z MySQL. Na wydruku 6.4 znajduje się skrypt, który jest odpowiednikiem ODBC
skryptu umieszczonego na wydruku 6.2. Tabela używana w tym przykładzie jest odpowiednikiem używanej w
poprzednim przykładzie.
Wydruk 6.4. Odczytywanie danych z bazy ODBC
<html>
<head>
<title>Pobioeranie danych z bazy danych ODBC</title>
</head>
<body>
<?php
putenv("ODBCINI=/usr/local/unixODBC/etc/odbc.ini");
// Ukrywamy komunikaty błędów i sami je obsługujemy
$aDBLink = @odbc_connect( "localdsn", "prodplanner", "agdec" );
if ( !empty( $aDBLink ) )
{
$aSQL = "select * from employees";
$aQResult = @odbc_exec( $aDBLink, $aSQL );
if ( $aQResult == True )
{
$aRow = array();
$aRowNum = 1;
while ( odbc_fetch_into( $aQResult, $aRowNum, &$aRow ) )
{
$aFName = $aRow[1];
$aPos = $aRow[4];
print( "$aFName, $aPos<br>" );

$aRowNum++;
}
odbc_free_result( $aQResult );
}
else
{
print( "Błąd wykonania zapytania<br>" );
}
}
else
{
print( "Błąd podłączenia do bazy danych<br>" );
}
?>
</body>
</html>
Wydruk 6.4 jest właściwie taki sam jak wydruk 6.2. Mimo, że nazwy funkcji są inne, koncepcja jest
nieomal identyczna. Jedyną zauważalną różnicą jest wywołanie putenv() na początku skryptu. Wywołanie to
umieszcza w środowisku programu ścieżkę do pliku inicjalizującego ODBC. Nie jest to potrzebne, jeżeli w ten
sam sposób ustawiłeś środowisko serwera WWW. Dodatkowo, na wydruku 6.4 do pól tabeli odwołujemy się dla

73 PHP – Kompendium wiedzy
uproszczenia za pomocą numer a nie nazwy. Dostępne są funkcje ODBC zapewniające obsługę transakcji,
kursorów i wiele innych. W skorowidzu funkcji na końcu książki znajdują wszystkie funkcje do obsługi ODBC.
Uwaga na temat połączenia do baz danych

W poprzednim przykładzie połączenie do baz danych było realizowane za pomocą podstawowych funkcji xxx_connect(). PHP
posiada również zdolność tworzenia trwałych połączeń za pomocą funkcji pxxx_connect(). Użycie połączenia trwałego poprawia
wydajność aplikacji, ponieważ sam PHP utrzymuje połączenie z bazą danych, więc może być ono wielokrotnie używane. Po
utworzeniu połączenia za pomocą odpowiedniej kombinacji host-użytkownik-hasło, PHP ciągle dostarcza tego samego połączenia do
kolejnych wywołań połączenia. W bazach danych utworzenie połączenia trwa zwykle długo (na przykład w Oracle), więc użycie
trwałych połączeń może mieć ogromny wpływ na ogólną wydajność aplikacji.

PHPLIB
Jak wspomniałem w poprzednich rozdziałach, dostępne są świetne biblioteki do wykorzystania przez
programistów PHP. Jedna z najczęściej używanych bibliotek, PHP Base Library (PHPLIB) jest dostępna pod
adresem http://phplib.netuse.de. Zawiera ona klasy dostęu do baz danych, obsługi sesji, narzędzi autoryzacji i
wiele, wiele innych.
Klasy dostępu do bazy danych w PHPLIB tworzą warstwę abstrakcji dla kilku baz danych obsługiwanych
przez PHP. Warstwa ta zapewnia wspólny interfejs dla bazowych funkcji baz danych, więc programiści mogą
łatwo zmieniać typ bazy danych bez konieczności nauki nowego zestawu funkcji lub wielu zmian w kodzie. W
czasie pisania książki PHPLIB obsługiwał MySQL, PostgreSQL, mSQL, Oracle 7, Oracle 8, Sybase, Microsoft
SQL Sever i bazy ODBC.
Poniższy wydruk ilustruje siłę modułu obsługującego bazy danych z pakietu PHPLIB. W skrypcie
umieszczonym na wydruku 6.5. pokazany został sposób uproszczenia skryptu z wydruku 6.2.
Wydruk 6.5. Użycie PHPLIB do powtórzenia wyników z wydruku 6.2
<?php
include ( "db_mysql.inc" );
// Dziedziczymy po klasie DB_Sql aby użyć jej z
// naszą bazą MySQL
class MySQLDBTest extends DB_Sql
{
var $Host = "208.129.36.163";
var $Database = "mydb";
var $User = "root";
var $Password = "";
}
// Tworzenie egzemplarza nowej klasy MySQLDBTest
$aDB = new MySQLDBTest;
$aDB->query( "select * from employees" );
while( $aDB->next_record() )
{
$aFName = $aDB->f( "first" );
$aPos = $aDB->f( "position" );
print( "$aFName, $aPos<br>" );
}
?>
Dostarczona przez PHPLIB klasa DB_Sql ukrywa w sobie szczegóły procesu łączenia i wyboru bazy
danych oraz zawiera kod obsługi błędów. Pozwala to osiągnąć w wyniku kod, który jest łatwiejszy do czytania,
utrzymania i uruchamiania. Klasa DB_Sql nie jest przeznaczona do bezpośredniego używania. Zamiast tego
powinna być tworzona klasa dziedzicząca po niej, w której ustawiane są zmienne specyficzne dla twojego
środowiska pracy. Jeżeli PHP będzie obsługiwał klasy abstrakcyjne, jest to idealny kandydat do takiej właśnie
implementacji. Na wydruku 6.5 definiowana jest klasa pochodna MySQLDBTest, w której zawarte są dane opisujące
połączenie z MySQL. Następnie tworzony jest obiekt tej klasy, na którym wykonywane są operacje.
Największa zaleta korzystania z klas PHPLIB ujawnia się, gdy zachodzi potrzeba wymiany bazy danych.
Poniższy wydruk pokazuje jak łatwo można zamienić skrypt z wydruku 6.5, aby zamiast z MySQL korzystał z
Oracle poprzez sterownik ODBC.
Wydruk 6.6. Skrypt z wydruku 6.5 korzystający z Oracle i ODBC
<?php
putenv("ODBCINI=/usr/local/openlink/odbc.ini");
include( "db_odbc.inc" );
// Dziedziczymy po klasie DB_Sql aby użyć jej z
// bazą danych Oracle poprzez ODBC

Rozdział 6 – Współpraca z bazami danych 74
class OracleDBTest extends DB_Sql
{
var $Database = "localdsn";
var $User = "prodplanner";
var $Password = "agdec";
}
// Tworzenie egzemplarza nowej klasy OracleDBTest
$aDB = new OracleDBTest;
$aDB->query( "select * from employees" );
while( $aDB->next_record() )
{
$aFName = $aDB->f( "first" );
$aPos = $aDB->f( "position" );
print( "$aFName, $aPos<br>" );
}
?>
Jedyną widoczną zmianą pomiędzy wydrukami 5 i 6 jest funkcja include(), ale znaczenie tej zmiany jest
olbrzymie. Skrypt z wydruku 6.5 korzystał z danych z bazy MySQL działającej na tym samym komputerze co
serwer WWW. Skrypt z wydruku 6.6 pobiera dane z bazy Oracle zainstalowanej na serwerze z Windows NT.
Z powodu prostoty projektu i implementacji PHPLIB może być on użyteczny dla programistów
pracujących w złożonych, heterogenicznych środowiskach, jak również w prostych instalacjach składających się z
jednego serwera. Biblioteka ta zapewnia jednakowy interfejs dostępu do różnych baz danych, co powoduje bardzo
łatwe ponowne użycie istniejącego kodu. Więcej informacji na temat klas zawartych w PHPLIB można odnaleźć
na witrynie http://phplib.netuse.de/.

Przechowywanie danych z formularzy
Omówienie formularzy HTML jest potrzebne w rozdziale dotyczących baz danych, ponieważ formularze
są najczęściej używanym mechanizmem używanym do wprowadzania danych w aplikacjach WWW. Tak jak
opisano w rozdziale 3, „Formularze i cookie”, PHP dostarcza wielu funkcji potrzebnych przy używaniu
formularzy i baz danych.
W przy domyślnych ustawieniach PHP automatycznie oznacza we wszystkich zmiennych GET, POST i
COOKIE apostrofy, cudzysłowy, ukośniki i znaki NUL. Powoduje to, że wartość przekazana z formularza jest od
razu gotowa do użycia w zapytaniu SQL. Jeżeli zablokowałeś tą opcję, musisz użyć funkcji addslashes() zanim
skorzystasz w zapytaniu SQL z ciągu przekazanego z formularza. Dodatkowo, wszystkie wartości, które będą
wyświetlane muszą zostać przed wyświetleniem przetworzone za pomocą funkcji stripslashes().
Tak jak we wszystkich aplikacjach, dane wpisane do formularza HTML muszą być sprawdzone przed ich
zapisaniem do bazy danych. Mechanizmy kontroli poprawności danych były opisane w rozdziale 3. Wynikiem
braku kontroli danych może być niezadowolenie użytkowników z aplikacji a nawet załamanie systemu
bezpieczeństwa serwera. Na przykład, niektóre komunikaty błędów generowane przez bazę danych mogą
zawierać takie informacje na temat używanej bazy danych, których na pewno nie chciałbyś pokazywać
użytkownikom. Należy być przygotowanym na sytuacje, że niektórzy użytkownicy mogą próbować wyszukać
słabe punkty w aplikacji.
Aby zabezpieczyć się przed niektórymi typami ataków należy zawsze używać atrybutu maxlength w
polach tekstowych oraz kontrolować typ i postać danych. Jak wspomniano w rozdziale 3, tam gdzie jest to
możliwe należy zastępować procedury kontroli danych przez takie mechanizmy, które ze swojej natury
ograniczają pomyłki. Pola wyboru, przyciski opcji i inne tego typ elementy pozwalają na dużą elastyczność i
ograniczają możliwość błędu przy wprowadzaniu danych.
Tworząc alternatywne mechanizmy wprowadzania danych należy pamiętać o możliwości korzystania z
bazy danych przy tworzeniu początkowego zestawu danych. Umieszczenie takiego zestawu opcji w bazie danych
skutkuje w dłuższym okresie stworzeniem aplikacji łatwiejszej do zarządzania. Na przykład na wydruku 6.7
pokazane zostało tworzenie listy wyboru zawierającej kraje oraz stany USA. Oczywiście można wybrać stan w
USA a następnie Afrykę południową. Przykład ten pokazuje jedynie koncepcję. W skrypcie tym używane są
tabele us_states oraz world_countries. Każda z tabel zawiera identyfikator oraz nazwę. Identyfikator jest
przekazywany jako wartość formularza.
Wydruk 6.7. Użycie tabel słownikowych do generacji listy opcji
<?php
include ( "./db_mysql.inc" );
class MySQLDBTest extends DB_Sql

75 PHP – Kompendium wiedzy
{
var $Host = "208.129.36.163";
var $Database = "mydb";
var $User = "root";
var $Password = "";
}
function GetGenOpts( $aTableName, $aCurSel = "" )
{
$aResult = "";
$aDB = new MySQLDBTest;
$aSQL = "select ID, Name from $aTableName order by Name";
$aDB->query( $aSQL );
while( $aDB->next_record() )
{
$aName = $aDB->f( "Name" );
$aID = $aDB->f( "ID" );
if ( $aID == $aCurSel )
{
$aResult .= "<option value=\"$aID\" selected>$aName</option>";
}
else
{
$aResult .= "<option value=\"$aID\">$aName</option>";
}
}
return $aResult;
}

?>
<html>
<head>
<title>Formularz wyboru stanu USA oraz kraju</title>
</head>
<body>
<form action="some_place.phtml" method="post">
<table>
<tr>
<td>
Wybierz stan USA:
</td>
<td>
<select name="us_state" size="1">
<?php
print( GetGenOpts( "us_states", "ID" ) );
?>
</select>
</td>
</tr>
<tr>
<td>
Wybierz kraj:
</td>
<td>
<select name="world_country" size="1">
<?php
print( GetGenOpts( "world_countries", "ZA" ) );
?>
</select>
</td>
</tr>
</form>
</body>
</html>
Na rysunku 6.3. pokazany jest formularz. Funkcja GetGenOpts() może być użyta do tworzenia listy opcji z
każdej tablicy posiadającej kolumny ID oraz Name. Opcjonalny parametr $aCurSel może zostać użyty do określenia
wybranej pozycji na formularzu, jeżeli formularz jest użyty do edycji danych a nie do wprowadzania nowych
danych. Użycie tabel słownikowych pozwala również na natychmiastowe zmiany w aplikacji o ile zajdzie taka
potrzeba. Jeżeli masz listę akceptowanych przez ciebie kart kredytowych, należy ją przechowywać w bazie
danych zamiast statycznie definiować na stronie HTML, ponieważ w razie zmiany tej listy, nie trzeba przeglądać
wszystkich stron szukając odwołań. Zamiast tego można po prostu zmienić wartości w tabeli bazy danych.
Aplikacja zostanie natychmiast zmieniona się bez modyfikacji jednej linii kodu.

Rozdział 6 – Współpraca z bazami danych 76
Rysunek 6.3.
Dynamicznie
generowany
formularz do
wprowadzania
danych

Kolejny raz musimy powtórzyć, że najlepszym zabezpieczeniem przed nieprawidłowymi danymi jest
dobrze skonstruowany mechanizm wprowadzania danych. Jeżeli umieścisz na formularzu pole tekstowe, zawsze
musisz zweryfikować poprawność danych, zanim trafią one do serwera bazy danych.
Udostępniając aplikację, nie możesz zapomnieć o bezpieczeństwie bazy danych. Zawsze należy
zabezpieczyć bazę danych w taki sposób, aby możliwy był dostęp do niej jedynie za pomocą aplikacji.
Oczywiście, w aplikacji nie powinny znajdować się pola umożliwiające wykonanie dowolnego zapytania SQL.
Należy również przetestować aplikację za pomocą danych, które mogą spowodować błąd. Spróbuj wprowadzić
do pól tekstowych apostrofy oraz znaki backslash. Można również wpisać średnik i po nim wyrażenie SQL.
Na przykład, spójrzmy na przykład aplikacji, która pobiera identyfikator a następnie wykonuje wyrażenie
SQL:
select * from table where ID = wprowadzona_wartosc
Spodziewasz się, że wprowadzona_wartosc będzie jedną wartością, więc można ją wkleić do wyrażenia
SQL. Mimo, że przypadek ten jest dość nieprawdopodobny, ktoś może wpisać do formularza napis „1; drop
database”. Wynikowe wyrażenie SQL będzie następujące:
select * from table where ID = 1; drop database
Mimo, że jest nieprawdopodobne, aby przykład ten spowodował jakiekolwiek szkody, to jednak możesz
odszukać podobne przykłady, które mogą spowodować naruszenie bezpieczeństwa, lub uszkodzenie bazy danych.
W oparciu o typ tworzonej aplikacji, powinieneś określić poziom wymaganego bezpieczeństwa i kontroli
poprawności.

Wykorzystanie możliwości bazy danych
Część ta nie jest związana bezpośrednio z wykorzystaniem baz danych z PHP — jest to dodatek
poświęcony pisaniu aplikacji wykorzystujących bazy danych. Może on znajdować się również w rozdziale
poświęconym inżynierii programowania. Wszyscy, którzy nie pisali zbyt wielu aplikacji w środowisku
wielowarstwowym, powinni dokładnie przeczytać ten fragment i stosować go podczas pisania własnych aplikacji
WWW korzystających z bazy danych.
Zanim zacząłem pisać aplikacje dla WWW, tworzyłem w większości aplikacje Windows wykorzystywane
przez jednego użytkownika. We wszystkich przypadkach bazy danych używał jeden użytkownik i działała na tym
samym komputerze. Środowisko takie jest bardzo wygodne, ponieważ nie trzeba robić żadnych założeń
dotyczących współbieżności a system bazy danych jest zwykle czymś więcej niż pośrednikiem służącym do
odczytu i zapisu danych.

77 PHP – Kompendium wiedzy
W przypadku tworzenia aplikacji dla środowiska wielowarstwowego, baza danych pełni o wiele
ważniejszą rolę. Potrzebna jest zwykle nowoczesna baza danych, która potrafi zarządzać współbieżnością,
uprawnieniami użytkownika, transakcjami i to odbierając wiele równoczesnych żądań w tym samym czasie.
Oprócz wykonywania tych krytycznych funkcji, większość nowoczesnych baz danych posiada ogromną ilość
funkcji, które pomagają w pisaniu aplikacji. Niezależnie od tego, czy serwer bazy danych i serwer WWW
umieszczony jest na tym samym komputerze, czy na osobnych maszynach, powinieneś wszędzie tam gdzie jest to
możliwe wykorzystywać siłę systemu zarządzania bazą danych.
W mojej ostatniej stałej pracy trafiłem na świetny przykład. Zadaniem było powielenie kilku wierszy
danych z tabeli zmieniając w nowych wierszach zawartość kilku pól. Znaleziony przeze mnie kod wyglądał mniej
więcej tak:
wybierz wszystkie wiersze do kopiowania
dla każdego wiersza
inicjuj nowy ciąg zawierający wyrażenie INSERT
wybierz nowy klucz główny w bazie danych
jeżeli te dane powinny być skopiowane bez zmian
skopiuj istniejące dane do wyrażenia INSERT
w przeciwnym wypadku
dodaj nowe wartości do wyrażenia INSERT
wykonaj wyrażenie INSERT z wyrażenia
koniec dla każdego wiersza
Nie mam zamiaru śmiać się z programisty, który pisał ten fragment, ale fragment ten prezentuje styl
kodowania brute-force częsty u młodych lub niedoświadczonych programistów. Fragment ten wymaga 1+
(2*ilość wierszy) odwołań do bazy danych. Pierwsze wywołanie pobiera wszystkie wiersze i następnie dla
każdego z nich jest potrzebne jedno wywołanie do wygenerowania nowego klucza głównego i jedno dla
wykonania instrukcji INSERT. Cały fragment można zredukować do jednego wyrażenia SQL wykonywanego w
całości przez bazę danych:
INSERT INTO tabela SELECT sequence.nextval AS PRIMARY KEY, inne_pola FROM tabela
Wyrażenie to opiera się na generatorze sekwencji do wygenerowania nowego klucza głównego, który to
mechanizm musi posiadać baza danych, ale pokazuje w jaki sposób wykorzystując odpowiednio SQL można
uniknąć pisania sporej ilości kodu oraz poprawić wydajność aplikacji. Jeżeli zdarzy ci się pisać kod formatujący,
filtrujący bądź sortujący dane pobierane z bazy danych, przyjrzyj się dokładniej swojemu zapytaniu SQL. Możesz
skorzystać z wbudowanych funkcji bazy danych lub zastosować bardziej skomplikowany kod SQL do osiągnięcia
oczekiwanych wyników.
W jednym z ostatnich projektów musiałem posortować wiersze opisujące personel w oparciu o różnicę
wieku licząc od określonej daty. Na przykład, musiałem określić pozycję rekordu osoby w oparciu o to, jak
bardzo jej wiek jest zbliżony do 30 lat. Dodatkowo każdej osobie przypisywana była wartość, będącą liczbą od 0
do 10, obliczana w oparciu o to jak blisko wiek osoby jest zbliżony do założonych wymagań wiekowych. Każdy,
którego wiek był oddalony od założonego o więcej niż 10 lat otrzymywał tą samą ocenę. Poniższe wyrażenie
może wyglądać na dosyć skomplikowane, ale otrzymujemy wszystkie potrzebne dane bez potrzeby pisania
dodatkowego kodu manipulującego wynikami:
SELECT concat (firstname, ' ', surneme) as fullname,
ROUND(MAX(0, (10-ABS((30-ROUND(((TO_DAYS(NOW()) - TO_DAYS(birthdate))/365)))))))
AS age_dif FROM persons ORDER BY age_diff DESC
Każdy z wierszy wyniku zawiera imię i nazwisko osoby oraz liczbę oznaczającą żądaną wartość.
Wszystko czego potrzebujemy, to w pętli odczytać wyniki i wyświetlić je na ekranie. Na początku wyniku znajdą
się osoby o wieku najbliższym 30 lat.
Jednym z najważniejszych problemów przypisaniu aplikacji dla WWW jest użycie właściwych narzędzi i
właściwych ludzi do realizacji poszczególnych fragmentów projektu. Następna duża część książki, „Zarządzanie
projektem przy tworzeniu aplikacji WWW” zawiera więcej informacji na temat inżynierii programowania dla
aplikacji WWW.

Podsumowanie
Rozdział ten zawiera opis użycia baz danych razem z PHP. Ponieważ PHP obsługuje wiele typów baz
danych, skupiliśmy się na bazie MySQL oraz dostępowi poprzez ODBC. Zamieszczona została również krótka
dyskusja na temat inżynierii programowania i wykorzystaniu możliwości baz danych. Dalsze informacje na temat

Rozdział 6 – Współpraca z bazami danych 78
użycia baz danych znajdują się w rozdziałach 15, 16 i 17. Najobszerniejsze przykłady znajdują się w rozdziale 15
„Witryny oparte o bazę danych”.

79 PHP – Kompendium wiedzy
Rozdział 7. Sesje i stan aplikacji
Wstęp
W rozdziale 4 „Operacje na plikach” zostało przedstawione wykorzystanie plików do utrzymania stanu
sesji, ale nie zawiera on ogólnego opisu problemu utrzymywania stanu. W standardowych aplikacjach stan jest
automatycznie utrzymywany w zmiennych przechowywanych w pamięci komputera, plikach oraz bazach danych
wykorzystywanych przez program. Stan programu jest zawsze znany, ponieważ cały model działania programu
jest dosyć prosty: użytkownik uruchamia program, wykonuje swoje zadania a następnie zamyka program. Jeżeli
użytkownik chwilowo przełączy zadanie a następnie powróci do niego, stan aplikacji nie zmieni się. Gdy
użytkownik kończy pracę, niezbędne dane są serializowane oraz zapisywane i sesja pracy programu się kończy.
W aplikacjach WWW zarządzanie danymi sesji jest bardziej złożone. Jest to powodowane faktem, że
zarówno przeglądarka jak i serwer WWW nie były projektowane do uruchamiania aplikacji WWW. Ponieważ
aplikacje WWW są ze swojej natury aplikacjami wielodostępnymi, w utrzymanie sesji jest zaangażowany
zarówno serwer jak i klient. Pod pojęciem klienta kryją się tutaj poszczególni użytkownicy. Serwer korzysta z
danych przekazanych mu przez klientów podczas kolejnych odwołań do stron do śledzenia i utrzymywania
danych sesji.
PHP pozwala na stosowanie kilka metod utrzymywania stanu sesji. W PHP4 wbudowano funkcje do
obsługi sesji, ale dostępne są również moduły zewnętrzne realizujące to samo zadanie. Inną metodą jest napisanie
w PHP własnego mechanizmu zarządzania sesjami.

Podstawy mechanizmu sesji
Jak napisałem we wprowadzeniu, obsługa sesji w sieci WWW wymaga współpracy pomiędzy
przeglądarką klienta i serwerem WWW. Gdy sesja zaczyna się, serwer WWW tworzy unikalny identyfikator sesji
(ID) i przekazuje go do klienta. Zwykle identyfikator ten jest przechowywany w cookie na komputerze klienta.
Następnie przeglądarka wysyła ID do serwera razem z każdym żądaniem przesłania strony. Serwer wykorzystuje
ID do odczytania i odtworzenia wszystkich potrzebnych danych aplikacji. Po zakończeniu aplikacji dane są
usuwane z serwera. Na rysunku 7.1. pokazany został schemat komunikacji pomiędzy klientem a serwerem WWW
(w tym przypadku jest to serwer 1U produkowany przez Penguin Computing) w przypadku rozpoczynania sesji
oraz wykorzystywania zmiennych.
Rysunek 7.1.
Rozpoczynanie
sesji oraz użycie
zmiennych sesji

Wbudowany w PHP mechanizm zarządzania sesjami
W wersji 4 do PHP wprowadzono mechanizm zarządzania sesjami. Funkcje obsługi sesji są dokładnie
opisane w skorowidzu funkcji na końcu książki. Wszystkie nazwy funkcji obsługi sesji rozpoczynają się od
sesion_.
Wykorzystując sesje w PHP należy wykonać następujące podstawowe operacje:
1. Rozpoczęcie sesji za pomocą session_start().
2. Zarejestrowanie nazw zmiennych sesji za pomocą session_register().
3. Użycie zmiennych sesji.
System zarządzania sesjami w PHP można w dużym stopniu konfigurować. Można zmienić sposób
przesyłania identyfikatora sesji, miejsce przechowywania zmiennych sesji na serwerze oraz częstotliwość
usuwania przerwanych sesji. Ponieważ dostępne jest wiele opcji konfiguracji, część ta zostanie podzielona na
części o wzrastającym stopniu skomplikowania.

Rozpoczęcie pracy z sesjami w PHP
Wydruki 7.1 i 7.2 pokazują, jak łatwe jest użycie sesji w PHP wykorzystujące domyślne ustawienia. Na
wydruku 7.1 zamieszczony jest skrypt inicjujący wszystkie zmienne sesji używane w aplikacji oraz łącze do
skryptu z wydruku 7.2, który wykorzystuje zmienne sesji ze skryptu na wydruku 7.1.
Wydruk 7.1. Uruchamianie sesji i inicjowanie zmiennych
<?php
session_start();
session_register( "aUser", "aAccount" );
$aUser = "Cidnie";
$aAccount = "1016";
?>
<html>
<head>
<title>Podstawy sesji: Strona 1</title>
</head>
<body>
<?php
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );
?>
<br><br>
<a href="listing2.phtml">Przejście do strony 2</a>
</body>
</html>
Wydruk 7.2. Użycie zmiennych sesji z wydruku 7.1
<?php
session_start();

81 PHP – Kompendium wiedzy
?>
<html>
<head>
<title>Podstawy sesji: Strona 2</title>
</head>
<body>
<?php
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );
?>
</body>
</html>
Pierwszym krokiem skryptu z wydruku 7.1 jest uruchomienie sesji poprzez wywołanie funkcji
session_start(). W domyślnej konfiguracji PHP używa cookie do przechowywania identyfikatora sesji. Funkcja
session_start() posiada te same ograniczenia co funkcje set_cookie() i header(). Funkcje te muszą być
wywoływane przed wysłaniem do przeglądarki jakichkolwiek informacji lub musi zostać włączone buforowanie
danych wyjściowych. Chociaż można zmienić sposób przesyłania identyfikatora sesji, najprostszą metodą jest
wykorzystanie domyślnego mechanizmu — cookie. Pozostałe metody zostaną opisane później.
Po rozpoczęciu sesji, zostają zarejestrowane dwie zmienne, aUser oraz aAccount. Gdy użytkownik kliknie
łącze prowadzące do strony 2, przeglądarka samoczynnie wyśle identyfikator sesji do serwera. W skrypcie z
wydruku 7.2 w trakcie wywołania funkcji session_start(), PHP korzysta z identyfikatora sesji do odczytania
wszystkich zmiennych sesji. Na rysunkach 7.2. i 7.3. pokazane są wyniki działania skryptów umieszczonych
odpowiednio na wydrukach 1 i 2.
Rysunek 7.2. Wynik
działania skryptu z
wydruku 7.1

Rysunek 7.3. Wynik
działania skryptu z
wydruku 7.2

Możliwe jest wyświetlenie bieżącego identyfikatora sesji wypisując na ekran zawartość zmiennej
$PHPSESSID. Dodatkowo, w domyślnej konfiguracji na Apache i Linuksa, dane sesji są przechowywane w
katalogu /tmp w plikach o nazwach w postaci sess_$PHPSESSID. Po uruchomieniu poprzedniego przykładu
odszukałem plik sess_e66b342b4e76889f8f25105db11820c6, który zawierał:
aUser|s:6:"Cidnie";aAccount|s:4:"1016";.

Rozdział 7 – Sesje i stan aplikacji 82
Domyślne ustawienia PHP dotyczące zarządzania sesjami są łatwe do użycia i wystarczają dla większości
aplikacji WWW. Jednak w niektórych przypadkach cookie mogą nie być pożądaną metodą przesyłania
identyfikatora sesji, a w przypadku niektórych dużych aplikacji WWW użycie plików na serwerze WWW może
utrudniać skalowanie aplikacji. Następne dwie części zawierają omówienie sposobu rozwiązania tych problemów.

Przesyłanie identyfikatora sesji bez użycia cookie
Jeżeli aplikacja absolutnie wymaga zastosowania zmiennych sesji, do przesyłania identyfikatora sesji
możesz użyć innego mechanizmu niż cookie. Należy pamiętać, że niektórzy użytkownicy mają w swoich
przeglądarkach włączoną obsługę cookie. Pierwszą z metod jest ręczne przesyłanie identyfikatora sesji w postaci
zmiennej GET lub POST. Na wydruku 7.3 pokazany jest skrypt, który pokazuje w jaki sposób należy zmienić kod z
wydruku 7.1, aby identyfikator sesji był przesyłany jako zmienna GET.
Wydruk 7.3. Ręczne przesyłanie identyfikatora sesji za pomocą metody GET
<?php
session_start();
session_register( "aUser", "aAccount" );
$aUser = "Cidnie";
$aAccount = "1016";
?>
<html>
<head>
<title>Ręczne przesyłanie identyfikatora sesji: Strona 1</title>
</head>
<body>
<?php
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );
?>
<br><br>
<a href="listing2.phtml?<?=SID?>">Przejście do strony 2</a>
</html>
Należy zauważyć, że kod jest taki sam jak na wydruku 7.1, poza łączem w trzecim wierszu od końca. W
tym przypadku do adresu URL została doklejona stała PHP SID. Stała ta jest zdefiniowana jako
SessionName=SessionID, poniższy kod jest semantycznie identyczny z odpowiednią linią z wydruku 7.3:
<a href="listing2.phtml?<?php echo session_name() . "=" . session_id()?>">
Przejście do strony 2</a>
Należy pamiętać, że SID jest stałą a nie zmienną, więc jeżeli będziesz chciał wydrukować $SID zamiast
SID, nie otrzymasz takiego samego wyniku. Na wydruku 7.3 zastosowany został skrót <?=SID?>. Zapis taki jest
równoważny z <?php echo SID;?> zakładając, że w czasie kompilacji PHP użyto opcji --enable-short-tags.
Wydruk 7.3 ilustruje mechanizm ręcznego przesyłania identyfikatora sesji w programie, ale nie pokazuje
jak mechanizm ten wpływa na tworzenie aplikacji. Korzystając z tego mechanizmu, każde łącze i każdy
formularz w aplikacji musi zawierać identyfikator sesji. Jeżeli nie dodasz identyfikatora do jednego łącza, SID
zostanie utracony, a aplikacja będzie źle działać. Na szczęście PHP posiada opcję automatycznego
przekształcania łączy, który to mechanizm rozwiązuje ten problem za ciebie. Jeżeli chcesz przesyłać identyfikator
sesji przy użyciu metod GET i POST, możesz uaktywnić ten mechanizm kompilując PHP z opcją --enable-trans-
sid. Po przebudowaniu PHP kod z wydruku 7.3 może być zmieniony na nieco prostszy, pokazany na wydruku
7.4.
Wydruk 7.4. Przetwarzanie względnych adresów URL przez PHP w celu przekazywania identyfikatora
sesji
<?php
session_start();
session_register( "aUser", "aAccount" );
$aUser = "Cidnie";
$aAccount = "1016";
?>
<html>
<head>
<title>Automatyczne przesyłanie identyfikatora sesji: Page 1</title>
</head>
<body>
<?php
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );
?>
<br><br>
<a href="listing2.phtml">Przejście do strony 2</a>

83 PHP – Kompendium wiedzy
</html>
Różnica pomiędzy wydrukiem 3 i 4 jest taka, że na wydruku 7.4 nie ma odwołania do stałej SID w łączu.
Można również zauważyć, że wydruk 7.4 jest praktycznie identyczny z wydrukiem 7.1, ale wynik jego działania
jest różny z powodu zmiany konfiguracji. Gdy użytkownik kliknie łącze, URL będzie zawierał ciąg
PHPSESSID=xxx, tak samo jak w przypadku skryptu z wydruku 7.3. Kod ten działa, ponieważ PHP szuka na stronie
względnych adresów URL i dodaje do nich potrzebną wartość identyfikatora sesji. Aby to pokazać, następny
skrypt zawiera kilka względnych łączy i kilka bezwzględnych. Pokazujemy również w jaki sposób PHP
przetworzył łącza.
Wydruk 7.5. Przykłady automatycznego uzupełniania łączy przez PHP
<?php
session_start();
session_register( "aUser", "aAccount" );
$aUser = "Cidnie";
$aAccount = "1016";
?>
<html>
<head>
<title>Uzupełnianie adresów URL</title>
</head>
<body>
<?php
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );
?>
<br><br>
<a href="listing2.phtml">Łącze 1</a>
<br><br>
<a href="listing2.phtml?MyVar=1234">Łącze 2</a>
<br><br>
<a href="http://www.php.net/">Łącze 3</a>
<br><br>
<a href="http://localhost/listing2.phtml">Łącze 4</a>
<br><br>
<form action="listing2.phtml">
<input type="submit" name="Submit"
value="Przycisk przesłania danych formularza">
</form>
</html>
Po uruchomieniu skryptu, do przeglądarki został wysłany następujący kod HTML:
<html>
<head>
<title>Uzupełnianie adresów URL</title>
</head>
<body>
Użytkownik: Cidnie<br>Konto: 1016<br> <br><br>
<a href="listing2.phtml?PHPSESSID=9771f86dc94e367cf1c62e0339d02c4b">Łącze 1</a>
<br><br>
<a href="listing2.phtml?MyVar=1234&PHPSESSID=9771f86dc94e367cf1c62e0339d02c4b">
Łącze 2</a>
<br><br>
<a href="http://www.php.net/">Łącze 3</a>
<br><br>
<a href="http://localhost/listing2.phtml">Łącze 4</a>
<br><br>
<form action="listing2.phtml"><INPUT TYPE="HIDDEN" NAME="PHPSESSID"
VALUE="9771f86dc94e367cf1c62e0339d02c4b">
<input type="submit" name="Submit"
value="Przycisk przesłania danych formularza">
</form>
</html>
Ponieważ pierwsze dwa łącza są łączami względnymi, zostały do nich dołączone dane na temat sesji.
Łącze 3 nie jest oczywiście łączem względnym, więc identyfikator sesji nie został dołączony. Działanie takie było
przewidziane. Łącze 4 pokazuje jak ostrożnym należy być używając funkcji automatycznego uzupełniania łączy.
W tym przypadku jest to bezwzględny adres URL, chociaż strona ta znajduje się na tym samym serwerze. PHP
uzupełnia jedynie adresy URL zapisane w postaci łączy względnych.
Do przykładu został dołączony formularz, ponieważ PHP potrafi również uzupełniać formularz tak, aby
zawierał on wartość identyfikatora sesji. Oznacza to, że aplikacja korzystająca z formularzy również będzie
prawidłowo działała. Jedyną wadą tego mechanizmu jest to, że działa on jedynie dla metody POST. Jeżeli będziesz
chciał użyć formularza korzystającego z metody GET, aplikacja nie będzie działała prawidłowo. Mimo, że atrybut
ACTION zostanie prawidłowo zmodyfikowany, przeglądarka nie dodaje zmiennej PHPSESSID do ciągu zapytania
(przetestowane na najnowszych wersjach Netscape i Internet Explorer).
Rozdział 7 – Sesje i stan aplikacji 84
Przesyłanie identyfikatora sesji za pomocą metod GET i POST pozwala uniknąć niektórych problemów z
cookie i jest łatwe do zrealizowania. Jeżeli zamierzasz skorzystać z automatycznego uzupełniania adresów,
powinieneś również się zastanowić, jaki wpływ będzie to miało na wydajność aplikacji. PHP musi przecież
podczas każdego uruchomienia strony odszukać wszystkie łącza na stronie i zmienić URL.
Aby sprawdzić wpływ tego mechanizmu na wydajność stworzyłem przykładową stronę WWW oraz
skrypt, który odczytywał ją z serwera i zapamiętywał czas potrzebny na jej odczytanie. Przykładowa strona
zawierała 14 łączy z których 12 było łączami względnymi, które wymagały przepisania. Strona miała wielkość
9,55 kB. Użyłem skryptu z wydruku 7.6 do odczytania strony kolejno 1000 razy. Skrypt uruchamiałem z aktywną
opcją --enable-trans-sid, oraz bez niej.
Wydruk 7.6. Skrypt do sprawdzania wydajności
<?php
function getmicrotime()
{
$mtime = microtime();
$mtime = explode( " ", $mtime);
$mtime = $mtime[1] + $mtime[0];
return ($mtime);
}
$aStartTime = getmicrotime();
$aFileName = "http://localhost/ch07/listing5.phtml";
for ($nIndx = 0; $nIndex < 1000; $nIndex++)
{
$aData = file( $aFileName );
}
$aEndTime = getmicrotime();
$aTotTime = $aEndTime - aStartTime;
print( "Czas całkowity: $aTotTime\n");
?>
Test był uruchamiany wiele razy, aby obliczyć średnią różnicę czasów wykonania. Zanotowano średnio
9% zysk wydajności w przypadku zablokowania opcji --enable-trans-sid. Mimo, że różnica taka może być w
większości witryn mało znacząca, może ona być ważna w przypadku silnie obciążonych witryn. Zanim
zastosujesz któreś z rozwiązań, powinieneś przeprowadzić własne testy sprawdzające wpływ zastosowanego
mechanizmu na wydajność.
Błąd w PHP

Jak wspominaliśmy w tej części, PHP wersja 4.0.1 poziom poprawek 2, zawierał błąd w mechanizmie automatycznego uzupełniania
adresów URL. Błąd ten poprawiono w wersji 4.0.2. Jeżeli chcesz korzystać z tej funkcji, upewnij się, że masz zainstalowaną właściwą
wersję PHP.

Zapisywanie zmiennych sesji w bazie danych
Po wybraniu mechanizmu przesyłania identyfikatora sesji, należy zdecydować, gdzie będą zapisywane
wartości samych zmiennych sesji. PHP posiada mechanizm zapisywania zmiennych sesji w dowolnie wybrany
sposób. Do napisania dowolnego mechanizmu obsługi sesji wystarczy zdefiniować sześć funkcji i zarejestrować
je w PHP. Funkcje te wraz z ich parametrami są przedstawione poniżej.
• bool open( string save_path, string sess_name ); Funkcja ta jest wywoływana w trakcie inicjalizacji
sesji. Można jej użyć do wstępnej inicjalizacji sesji. Pierwszy argument jest ścieżką podaną w jako
wartość zmiennej konfiguracji session.save_path określonej w pliku php.ini, lub ostatnia wartość
przekazana do funkcji session_save_path(). Drugi argument jest nazwą sesji określoną przez parametr
session.name zdefiniowany w pliku php.ini, lub ostatnią wartościa przekazaną do funkcji session_name().
Wartościami domyślnymi są odpowiednio /tmp i PHPSESSID.
• bool close(); Funkcja jest wywoływana w celu zakończenia sesji.
• mixed read( string sess_id ); Funkcja jest używana do odczytania zawartości sesji. Jeżeli dane sesji nie
są dostępne, powinien zostać zwrócony pusty ciąg (""). Jeżeli wystąpi błąd, powinna zostać zwrócona
wartość False. Jeżeli istnieją dane sesji, powinny zostać zwrócone w postaci serializowanej. Dane te
powinny być dokładnie takie same, jak przekazane przez PHP do funkcji write().
• bool write( string sess_id, string value ); Funkcja jest wywoływana w przypadku konieczności
zapamiętania danych sesji. Dane przekazane do parametru value są danymi sesji w postaci serializowanej.

85 PHP – Kompendium wiedzy
• bool destroy( string sess_id ); Funkcja jest wywoływana podczas wywołania w skrypcie funkcji
session_destroy(). Powinna usunąć wszystkie dane związane z sesją.
• bool gc( int max_lifetime ); Funkcja ta jest wywoływana w czasie rozpoczynania sesji. Jest ona
wywoływana z częstotliwością określoną przez parametr konfiguracji session.gc_probability. Funkcja
jest przeznaczona do usuwania nieużywanych danych sesji. Podczas swojego działania powinna usunąć
wszystkie dane nie używane przez max_lifetime sekund. Usuwanie niepotrzebnych danych sesji jest
omówione w następnej części rozdziału.
Po utworzeniu funkcji obsługi sesji należy zarejestrować je za pomocą funkcji
session_set_save_handler(). Po tej operacji PHP będzie wywoływało wszędzie tam gdzie jest to potrzebne nowe
funkcje. Do zilustrowania tego mechanizmu przygotowany został skrypt na wydruku 7.7, który zawiera 7 funkcji
obsługi zmiennych, które jedynie wypisują swoją nazwę i parametry wywołania. Funkcja read() z tego skryptu
zwraca dla celów testowych wartości dwóch zmiennych sesji.
Wydruk 7.7. Zdefiniowany przez użytkownika mechanizm obsługi sesji: mysession.php
<?php
function mysess_open( $aSavePath, $aSessionName )
{
print( "mysess_open( $aSavePath, $aSessionName )<br>" );
return True;
}
function mysess_close()
{
print( "mysess_close()<br>" );
return True;
}
function mysess_read( $aKey )
{
print( "mysess_read( $aKey )<br>" );
return "aUser|s:6:\"Cidnie\";aAccount|s:4:\"1016\";";
}
function mysess_write( $aKey, $aVal )
{
print( "Wywołanie mysess_write!\n\nWłaśnie tutaj!\n\n" );
print( "mysess_write( $aKey, $aVal )<br>" );
return True;
}
function mysess_destroy( $aKey )
{
print( "mysess_destroy( $aKey )<br>" );
return True;
}
function mysess_gc( $aMaxLifetime )
{
print( "mysess_gc( $aMaxLifetime )<br>" );
return True;
}
session_set_save_handler( "mysess_open", "mysess_close", "mysess_read",
"mysess_write", "mysess_destroy", "mysess_gc" );
?>
Aby pokazać w jaki sposób PHP wywołuje nowe funkcje, użyjemy skryptu z wydruku 7.8. Dodatkowo
ustawiłem wartość session.gc_probability na 100, więc funkcja czyszczenia pamięci jest wywoływana na
początku każdej sesji.
Wydruk 7.8. Testowanie mysession.php
<?php
include( "./mysession.php" );
session_start();
?>
<html>
<head>
<title>Własny mechanizm obsługi sesji</title>
</head>
<body>
<?php
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );
$aUser = "Katie";
$aAccount = "2026";
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );
?>
</body>
</html>

Rozdział 7 – Sesje i stan aplikacji 86
Rysunek 7.4. Wynik
działania skryptu
mysession.php

Gdy uruchomiłem ten skrypt, wynik nie był taki, jakiego się spodziewałem. W przeglądarce nie było
informacji na temat wywołania funkcji write() i close(). Jak można zauważyć dodałem nawet dodatkową
instrukcję print() aby dokładniej sprawdzić co się stało. Wynik w przeglądarce jednak nadal nie był prawidłowy.
Po dokładniejszym sprawdzeniu odkryłem, że wynik działania obu funkcji znajduje się w pliku error_log serwera
Apache. Oznacza to, że w którymś momencie wyjście zostało tymczasowo przekierowane z stdout na stderr. W
wyniku tego przekierowania nie można wysłać żadnych danych do przeglądarki zarówno w funkcji write(), jak i
close(). Problem ten jednak nie wpływa na właściwy system obsługi sesji. Trzeba powiedzieć, że ten efet może
nie powtórzyć się w przypadku użycia innej platformy i serwera, ponieważ sposób obsługi standardowego
wyjścia danych i błędów może być inny. Na przykład w przypadku serwera Xitami działającego na Windows
widoczne były wszystkie spodziewane dane.
Po poznaniu zasady działania mechanizmu, stworzenie prawdziwego mechanizmu obsługi sesji będzie
dosyć łatwe. Jeżeli chcesz używać zmiennych sesji, powinieneś przechowywać je w bazie danych a nie w pliku.
Idea ta może być z początku mało zrozumiała, ale należy pamiętać, że zapisywanie danych aplikacji na serwerze
WWW może ograniczać skalowalność aplikacji. Jeżeli aplikacja zostanie rozdzielona na kilka serwerów WWW i
zastosowany zostanie mechanizm równoważenia obciążenia pomiędzy serwerami, użycie plików do obsługi sesji
może spowodować załamanie aplikacji. Awaria ta będzie wynikała z tego, że każde żądanie może być kierowane
do innego serwera w klastrze. Jeżeli początkowe dane zostaną zapisane na serwerze pierwszym, a kolejne żądanie
będzie przekazane do serwera dwa, to dane sesji będą nieosiągalne. Jeżeli wszystkie dane sesji będą
przechowywane w bazie danych, zmienne sesji będą dostępne dla wszystkich serwerów WWW w klasterze.
Następny wydruk zawiera prosty przykład użycia bazy danych MySQL do przechowywania danych sesji.
W celu poprawienia czytelności kodu, przykład korzysta z klasy DB_Sql pochodzącej z pakietu PHPLIB
omówionej w rozdziale 6 „Współpraca z bazami danych”. Do przechowywania danych sesji korzystamy z tabeli
zdefiniowanej w następujący sposób:
CREATE TABLE Sessions
(
SessionId char(32) not null,
LastUpdated datetime not null,
DataValue text,
PRIMARY KEY (SessionID),
INDEX (LastUpdated )
);
Tabela ta zawiera pole do przechowywania identyfikatora sesji, pole data/czas do zapamiętywania czasu
ostatniej zmiany oraz dane w postaci pola tekstowego typu BLOB. Pole LastUpdated posiada indeks w celu
poprawienia wydajności w trakcie odzyskiwania nieużytków. Cały kod funkcji obsługi sesji zamieszczony jest na
wydruku 7.9.
Wydruk 7.9. Obsługa sesji przy użyciu bazy danych MySQL
<?php
include ( "db_mysql.inc" );
// Specjalizacja klasy DB_Sql do wykorzystania na naszym
// serwerem bazy danych MySQL
class MySQLDB extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";

87 PHP – Kompendium wiedzy
var $Password = "root";
}
function mysess_open( $aSavePath, $aSessionName )
{
// nie trzeba nic robić
return True;
}
function mysess_close()
{
// nie trzeba nic robić
return True;
}
function mysess_read( $aKey )
{
$aDB = new MySQLDB;
$aSQL = "select DataValue from Sessions where SessionID='$aKey'";
$aDB->query( $aSQL );
if ( $aDB->num_rows() == 1 )
{
$aDB->next_record();
$aData = $aDB->f( "DataValue" );
return $aData;
}
else
{
// Wstaw wiersz dla bieżącej sesji aby uprościć funkcję write
$aSQL = "insert into Sessions values ( '$aKey', NOW(), '' )";
$aDB->query( $aSQL );
return "";
}
}
function mysess_write( $aKey, $aVal )
{
$aDB = new MySQLDB;
$aData = addslashes( $aVal );
$aSQL = "update Sessions set DataValue='$aData', ";
$aSQL .= "LastUpdated=NOW() where SessionID='$aKey'";
$aDB->query( $aSQL );
return True;
}
function mysess_destroy( $aKey )
{
$aDB = new MySQLDB;
$aSQL = "delete from Sessions where SessionID = '$aKey'";
$aDB->query( $aSQL );
return True;
}
function mysess_gc( $aMaxLifetime )
{
$aDB = new MySQLDB;
$aSQL = "delete from Sessions where UNIX_TIMESTAMP(NOW()) ";
$aSQL .= "- UNIX_TIMESTAMP(LastUpdated) > $aMaxLifetime";
$aDB->query( $aSQL );
return True;
}

session_set_save_handler("mysess_open", "mysess_close", "mysess_read",
"mysess_write", "mysess_destroy", "mysess_gc");
?>
Kod z wydruku jest całkiem prosty jak na tak dużą zmianę mechanizmu obsługi sesji. Funkcje
mysess_open() i mysess_close() nie są w tym przypadku używane, ponieważ nie są potrzebne. Wszystkie dane
sesji są przechowywane w jednej tabeli, więc wartości przekazane do mysess_open() nie są używane, a po
zakończeniu sesji nie trzeba niczego usuwać.
Interesującymi funkcjami są mysess_read(), mysess_write(), mysess_destroy() oraz mysess_gc(). W
funkcji mysess_read() do bazy jest kierowane zapytanie mające za zadanie odczytać dane związane z
identyfikatorem sesji. Jeżeli dane te zostaną odnalezione — są zwracane. Jeżeli nie ma jeszcze rekordu w bazie,
tworzony jest nowy rekord z pustym polem DataValue i zwracany jest pusty ciąg (""). Powodem takiego działania
jest to, że gdy później będą zapisywane dane sesji nie będzie konieczności sprawdzania, czy istnieje już
odpowiedni rekord w bazie danych.
Funkcja mysess_write() za każdym wywołaniem uaktualnia pola DataValue oraz LastUpdated. Funkcja
mysess_destroy() usuwa rekord związany z identyfikatorem sesji. Funkcja mysess_gc() usuwa wszystkie wiersze,
których wartość pola LastUpdated wskazuje że nie rekord nie był uaktualniany przez ostatnie $aMaxLifetime
sekund. Poniższy kod pokazuje sposób wykorzystania mechanizmu sesji partego o bazę danych.
<?php

Rozdział 7 – Sesje i stan aplikacji 88
include( "./mysql_session.php" );
session_start();
session_register( "aUser", "aAccount" );
$aUser = "Cidnie";
$aAccount = "1016";
?>
<html>
<head>
<title>Obsługa sesji z wykorzystaniem MySQL: Strona pierwsza</title>
</head>
<body>
<?php
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );?>
<br><br>
<a href="mysql_session_mgmt2.phtml?<?=SID?>">Przejście do następnej strony</a>
</body>
</html>
Poprzedni przykład pokazuje jak łatwo można utworzyć i używać własny mechanizm przechowywania
danych sesji w PHP. W przykładzie tym można zastosować kilka optymalizacji i usprawnień ulepszających
działanie, ale przykłady mają za zadanie pokazać ogólną ideę. Jeżeli potrzebujesz w swojej aplikacji zastosować
zmienne sesji, PHP zawiera rozwiązania pozwalające na dowolną implementację takiego mechanizmu.

Inne funkcje i opcje dotyczące sesji
PHP posiada jeszcze kilka nie omówionych jeszcze funkcji obsługi sesji. Funkcje session_name(),
session_module_name(), session_save_path() i session_id() mogą być użyte do odczytywania lub ustawiania
odpowiednio, bieżącej nazwy sesji, nazwy modułu, ścieżki zapisu i identyfikatora. Miałem kłopot wymyślić
projekt, w którym byłaby zalecana zmiana tych parametrów w czasie pracy. Jeżeli twoja zmienna posiada tak
dużo zmiennych sesji, ze musisz korzystać z dzielenia ich przy pomocy tych funkcji powinieneś poważnie
rozważyć zastosowanie sugestii zamieszczonych w części „Inżynieria programowania a sesje”, umieszczony w
dalszej części rozdziału.
W pliku php.ini znajdują się następujące opcje konfigurujące mechanizm sesji:
• sessions.save_handler. Opcja ta pozwala na zdefiniowanie nazwy programu obsługi używanego do
zapisywania i odczytywania danych związanych z sesją. Możliwe są wartości files, user i mm. Opcja ta
może być odczytywana lub zmieniana w czasie pracy za pomocą funkcji session_module_name().
Ustawienie jej wartości na files powoduje zapisywanie danych sesji w pliku na serwerze WWW.
Ustawienie na user, oznacza, że aplikacja posiada własny mechanizm obsługi. Wartość mm powoduje
korzystanie z wspólnej pamięci serwera WWW. Jeżeli wywołasz funkcje session_set_save_handler(),
wartość opcji jest niejawnie przestawiana na user. Wartością domyślną jest files.
• session.save_path. Opcja określa argument przekazywany do funkcji obsługi sesji. Jeżeli używasz
wbudowanego mechanizmu przechowywania danych, jest to nazwa katalogu gdzie są tworzone pliki.
Wartością domyślną jest /tmp.
• session.name. Opcja określa nazwę sesji, używaną również jako nazwa cookie. Powinna zawierać jedynie
znaki alfanumeryczne. Wartością domyślną jest PHPSESSID.
• session.auto_start. Opcja określa, czy moduł sesji automatycznie uruchamia sesję na początku strony.
Domyślną wartością jest 0 (wyłączony).
• session.lifetime. Pozwala określić w sekundach czas ważności cookie wysyłanego do przeglądarki.
Wartość 0 oznacza „do zamknięcia przeglądarki”. Wartością domyślną jest 0.
• session.serialize_handler. Opcja pozwala na zdefiniowanie nazwy programu obsługi używanego do
serializacji i deserializacji danych. Dostępny jest wewnętrzny format PHP (o nazwie php) oraz WDDX (o
nazwie wddx). WDDX jest dostępny jedynie po skompilowaniu PHP z obsługą WDDX. Wartością
domyślną jest php.
• session.gc_probability. Opcja określa prawdopodobieństwo w procentach uruchomienia funkcji gc
(usuwania nieużytków) podczas obsługi żądania. Usuwanie nieużytków polega na usuwaniu zmiennych
sesji zapisanych na serwerze WWW. Ponieważ często niemożliwe jest określenie, kiedy sesja się
zakończyła, PHP wywołuje funkcję usuwania nieużytków usuwającą zmienne sesji, które nie były
uaktualniane przez określony czas (patrz session.gc_maxlifetime). Wartością domyślną jest 1, co
oznacza, że 1% uruchomień sesji powoduje wykonanie funkcji usuwania nieużytków. Jeżeli do
89 PHP – Kompendium wiedzy
przesyłania identyfikatora sesji używane są metody GET i POST, należy zwiększyć tą wartość. Powodem
tego jest możliwość zapisania jako zakładki adresu URL, który posiada dołączony identyfikator sesji.
Prawdopodobnie nie chcesz, aby użytkownicy po dłuższym czasie kontynuowali starą sesję.
• session.gc_maxlifetime. Opcja określa ilość sekund, po których dane są uważane za „nieużytki” i
usuwane. Domyślnie jest to 1440 sekund (24minuty). W zależności od aplikacji możesz wydłużać lub
skracać ten czas.
• session.referer_check. Opcja określa, czy identyfikatory sesji odwołujące się do zewnętrznych witryn są
usuwane. Jeżeli identyfikator sesji jest przesyłany w adresie URL, użytkownicy nie zdając sobie sprawy ze
skutków mogą publikować identyfikator sesji. Sytuacja taka może prowadzić do problemów z
bezpieczeństwem serwera, których można uniknąć stosując tą opcję. Domyślnie jest ustawiona na 0.
• session.entropy_file. Opcja pozwala na podanie ścieżki do zewnętrznego źródła (pliku), które będzie
używane jako dodatkowe źródło entropii do generowania identyfikatorów sesji. Mogą to być /dev/random
lub /dev/urandom, dostępne we wielu systemach Unix.
• session.entropy_length. Opcja pozwala określić ilość bajtów, które należy odczytać z pliku określonego
w poprzedniej opcji. Domyślnie jest to 0 (wyłączone).
• session.use_cookies. Opcja pozwala na zdecydowanie czy do przechowywania identyfikatora sesji na
komputerze klienta używane będą cookie. Domyślnie jest to 1 (włączone).
PHP zapewnia ogromną elastyczność przy obsłudze sesji. Projektując aplikację korzystającą ze zmiennych
sesji należy rozważyć wszystkie dostępne opcje. Mimo, że użycie cookie wydaje się najprostszym rozwiązaniem,
może to spowodować poważne problemy, ponieważ użytkownicy mogą wyłączyć obsługę cookie. Inne metody
przesyłania identyfikatora sesji wymagają więcej pracy, ale korzystne jest wcześniejsze planowanie. Polecam
metodę przesyłania identyfikatora sesji w adresie URL, ponieważ metoda ta wymaga podjęcia decyzji na temat
stron, które będą używały mechanizmu sesji. Polecam również użycie projektu witryny opartego na szablonach
(szczegóły w rozdziale 12 „Oddzielanie kodu HTML od PHP”), ponieważ powoduje on, że ręczne przesyłanie
identyfikatora sesji jest o wiele prostsze niż w przypadku projektu pokazanego w tym rozdziale.

Użycie PHPLIB do obsługi sesji
W rozdziale 6 „Współpraca z bazami danych” przedstawialiśmy PHPLIB, jako świetną bibliotekę
zawierającą niektóre zaawansowane funkcje do tworzenia aplikacji opartych o PHP. PHPLIB była jedną z
pierwszych (i prawdopodobnie najbardziej znaną) bibliotek klas zawierających obsługę sesji w PHP.
Koncepcyjnie jest ona podobna do rozwiązania zastosowanego w PHP4. Obsługa sesji w PHPLIB jest oparta w
całości o kod PHP, więc jest możliwa do rozbudowy i konfiguracji poprzez dziedziczenie i rozbudowę klasy
bazowej. Posiada ona również niektóre opcje inicjalizacji, które mogą być bardzo użyteczne w naszych
aplikacjach.
Obsługa sesji w PHPLIB oparta jest o klasę kontenerową zdefiniowaną w PHPLIB. Używana jest jedna z
klas pochodnych po klasach CT_xxx, przeznaczonych do obsługi przechowywania zmiennych sesji. Powoduje to,
że PHPLIB można rozszerzyć w sposób, który pozwala na przechowywanie zmiennych sesji w praktycznie
dowolnym miejscu. PHPLIB wymaga również, aby funkcje obsługi strony sygnalizowały początek i koniec
strony, oraz która z „funkcji” PHPLIB jest używana na stronie.
Wydruk 7.10 zawiera skrypt ilustrujący użycie PHPLIB do zapisu zmiennych sesji w bazie MySQL.
Przykład ten jest identyczny z przedstawionym na wydruku 7.9, oprócz tego, że korzysta on z cookie do
przechowywania identyfikatora sesji na komputerze klienta.
Wydruk 7.10. Obsługa sesji w PHPLIB
<?php
include( "page.inc" );
include( "ct_sql.inc" );
include( "session.inc" );
include( "db_mysql.inc" );
// Specjalizacja klasy DB_Sql do połączenia z
// naszym serwerem MySQL
class MySQLDB extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";

Rozdział 7 – Sesje i stan aplikacji 90
var $Password = "root";
}
class MySQLCt extends CT_Sql
{
var $classname = "MySQLCt";
var $database_table = "active_sessions";
var $database_class = "MySQLDB";
}
class MySqlSession extends Session
{
var $classname = "MySqlSession"; // obsługa przechowywania
var $mode = "cookie";
var $lifetime = 0; // użycie cookie sesji
var $that_class = "MySQLCt"; // nazwa używanego kontenera
}
page_open( array( "sess" => "MySqlSession" ) );
$sess->register( "aUser" );
$sess->register( "aAccount" );
$aUser = "Cidnie";
$aAccount = "1016";
?>
<html>
<head>
<title>Obsługa sesji w PHPLIB: Pierwsza strona </title>
</head>
<body>
<?php
print( "Użytkownik: $aUser<br>" );
print( "Konto: $aAccount<br>" );
?>
<br><br>
<a href="phplib_session_mgmt2.phtml">Następna strona</a>
</body>
</html>
<?php
page_close();
?>
PHPLIB jest niezmiernie elastycznym narzędziem do zarządzania danymi sesji. Tak jak PHP posiada on
mechanizmy zapewniające zapisywanie danych sesji w liku, pamięci współdzielonej oraz tabeli bazy danych. Kod
z wydruku 7.10 jest nieco bardziej skomplikowany, ale doświadczeni programiści na pewno zauważą, że jest on
bardzo łatwo rozszerzalny poprzez dziedziczenie bazowych klas obsługi sesji. Pierwsza klasa pochodna, MySQLDB
jest klasą niezbędną do nawiązania połączenia z naszym serwerem bazy danych. Kolejna klasa, MySQLCt jest klasą
pochodną po klasie kontenerowej CT_Sql, która jest podstawową klasą kontenerową zapewniającą zapisywanie
danych w bazach danych SQL. Zawiera ona odwołanie do klasy MySQLDB, która jest używana do zrealizowania
dostępu do tabel. Ostatnia klasa, MySQLSession rozszerza bazową klasę Session i ustawia klasę MySQLCt jako klasę
zapisującą wszystkie dane sesji.
Tabela bazy danych wymagana przez mechanizm obsługi sesji w PHPLIB posiada następującą strukturę:
CREATE TABLE active_sessions (
sid varchar(32) not null,
name varchar(32) not null,
val text,
changed varchar(14) not null,
PRIMARY KEY (name, sid),
KEY changed (changed)
);
Po utworzeniu klas pochodnych PHPLIB wykorzystuje funkcję page_open() do dodania „funkcji” sesji do
bieżącej strony. Wywołanie tej funkcji powoduje utworzenie globalnej zmiennej $sess, która jest
wykorzystywana w kolejnych wywołaniach obsługi sesji. Podstawowe funkcje klasy Session w PHPLIB są
praktycznie identyczne z możliwościami wbudowanego mechanizmu PHP. Po utworzeniu klasy rejestrowane i
wykorzystywane są zmienne sesji. Jedynym dodatkowym wymaganiem jest wywołanie w skrypcie funkcji
page_close() do zaznaczenia końca skryptu.
Tak jak we wbudowanym mechanizmie sesji PHP, PHPLIB pozwala na wykorzystanie cookie lub
zmiennych GET i POST do przesyłania identyfikatora sesji. PHPLIB posiada również narzędzia do tworzenia
klas kontenerowych używanych do zapisywania zmiennych sesji na dowolnie wymyślonym serwerze. Dodatkową
funkcja PHPLIB jest możliwość dostarczania pliku inicjalizującego używanego do inicjalizacji zmiennych sesji
na początku każdej sesji. Poniższy kod pokazuje przykład takiego pliku inicjalizującego:
<?php
global $lang; // język aplikacji
$lang = "pl"; // domyślnie polski
$sess->register("lang");

91 PHP – Kompendium wiedzy
global $cur; // waluta aplikacji
$cur = "PLN"; // domyślnie złotówki
$sess->register("cur");
global $cart;
$cart = new Shop_Cart; // utworzenie obiektu wózka na zakupy
// zdefiniowanego w local.inc
$sess->register("cart"); // zarejestrowanie obiektu
?>
Mimo, że PHP posiada wbudowany mechanizm sesji zbliżony do rozwiązania zastosowanego w PHPLIB,
może być przydatny dostęp do całego kodu źródłowego PHP, na przykład aby wykonać niektóre optymalizacje.
Możesz również stosować PHPLIB do realizowania sesji w starszych wersjach PHP, które nie posiadają
wbudowanego mechanizmu sesji.

Tworzenie własnego mechanizmu sesji
W niektórych przypadkach w aplikacji może nie być potrzebny kompletny mechanizm sesji. W wielu
wypadkach jedyną daną, jaką musimy przesyłać pomiędzy stronami, jest klucz główny lub identyfikator. W
takich przypadkach bardziej efektywne będzie przesyłanie identyfikatora pomiędzy stronami aplikacji za pomocą
zmiennych GET i POST. Mając tą wartość możesz odczytać z bazy danych wszystkie potrzebne na stronie dane.
Rozwiązanie to jest proste do zrealizowania i nie wymaga żadnych dodatkowych narzutów
wprowadzanych przez przedstawione w tym rozdziale narzędzia obsługi sesji. Z drugiej strony, przesyłanie
identyfikatora wymaga nieco dokładniejszego projektowania aplikacji. Dodatkowo przesyłanie wartości
identyfikatora otwartym tekstem może powodować naruszenie bezpieczeństwa, więc korzystając z tej metody
zaleca się używanie odpowiedniej mechanizmu szyfrowania.
Tak jak w przypadku wszystkich innych aspektów projektowania aplikacji należy wykona dokładną
analizę potrzeb aplikacji i na jej podstawie wybrać właściwy schemat zarządzania sesjami. Wybór niewłaściwego
narzędzia na początku całego procesu może być kosztowne i prowadzić w dłuższej perspektywie do problemów z
konserwacją i rozwojem aplikacji.

Inżynieria programowania a sesje
Zmienne sesji mogą być niezmiernie istotne we wielu aplikacjach WWW. Są one elastyczne i łatwe do
użycia, ale tak jak wszystkie inne narzędzia programistyczne powinny być używane ostrożnie i według projektu.
Ponieważ zmienne sesji są bardzo łatwe do użycia, często są nadużywane w takim samym stopniu, jak zmienne
globalne przy pisaniu tradycyjnych aplikacji.
Projektując aplikację WWW należy szczegółowo rozważyć wszystkie zastosowania, w których należy
skorzystać ze zmiennych sesji. Decyzja użycia zmiennej sesji powinna być oparta na takich samych przesłankach,
jak decyzja użycia zmiennej globalnej. Steve McConnell, autor książki „Code Complete” (Microsoft Press, 1993)
uważa, że powodem użycia zmiennej globalnej mogą być następujące przypadki:
• Przechowywanie wartości globalnych. Dane globalne, to dane odzwierciedlające stan całej aplikacji, na
przykład tryb pracy (wersja próbna, pełna). Może być to duże zbiory danych używane w całej aplikacji, na
przykład tabele słownikowe.
• Zastępowanie nazwanych stałych. Ponieważ PHP posiada stałe, zastosowanie to nie jest dopuszczalne.
• Uproszczenie użycia bardzo często używanych danych. Czasami niektóre dane są tak często używane w
aplikacji, że występują w liście parametrów każdej procedury.
• Eliminowanie wędrujących danych. Czasami wartości są przekazywane do procedury tylko po to, aby
mogły być przekazane do kolejnej. Gdy procedury w takim łańcuchu nie korzystają z takich danych, są
one nazywany danymi wędrującymi.
Lista ta wskazuje powody rozważane użycia zmiennych globalnych przy programowaniu zwykłych
aplikacji, ale decyzja użycia zmiennej sesji powinna być oparta na tych samych kryteriach.
Bądź ostrożny przy uznawaniu zmiennej za zmienną sesji, ponieważ wydaje się występować w każdej
procedurze i na każdej stronie witryny WWW. Takie podejście najczęściej prowadzi do nadużywania danych
globalnych i zmiennych sesji. Należy pamiętać, że po zdefiniowaniu w aplikacji zmiennych sesji, każda strona
biorąca udział w sesji musi załadować wszystkie zmienne sesji nawet, gdy nie korzysta z żadnej. Należy również
Rozdział 7 – Sesje i stan aplikacji 92
unikać użycia zmiennych sesji jedynie dlatego, że zapewniają one wygodniejszy dostęp do danych związanych z
sesją.
W jednym z moich ostatnich kontraktów natrafiłem na świetny przykład nieprawidłowego użycia
zmiennych sesji. W aplikacji użytkownicy mogli utworzyć zbiór danych i udostępnić je do modyfikacji przez
innego użytkownika. Użytkownicy, którym dane były udostępniane byli doradcami, którym pierwsi użytkownicy
ufali. Gdy użytkownik udostępniał dane doradcy, dane o tym były zapisywane w bazie danych. Tabela uprawnień
zawierała identyfikator pierwotnego użytkownika i doradcy oraz identyfikator danych udostępnianych przez
użytkownika. Gdy doradca logował się do aplikacji, musiał na początku wybrać zbiór danych do oglądania. W
aplikacji były ustawiane zmienne sesji, które zawierały identyfikator konta doradcy, oraz identyfikator
przeglądanych danych klienta.
Jeżeli te identyfikatory byłyby jedynymi zmiennymi sesji, nie było by problemu. Jednak ktoś zdecydował,
aby nie korzystać z bazy danych do sprawdzania uprawnień i w zmiennej sesji przesyłana była lista zleconych
identyfikatorów danych. Za każdym razem, gdy doradca otwierał w programie zbiór danych, lista ta była
uaktualniana (wraz z tabelą uprawnień). Następnie, gdy element danych był pobierany z bazy, jego identyfikator
był poszukiwany w zawartości zmiennej sesji zamiast w bazie danych.
Głównym problemem powodowanym przez takie podejście były kłopoty z synchronizacją danych. Jeżeli
pierwotny użytkownik uaktualnił tabelę uprawnień w czasie, gdy doradca był zalogowany w aplikacji, doradca
miał dostęp tylko do danych zleconych w trakcie rozpoczynania sesji. Dodatkowo, rozwijanie aplikacji według
tego modelu było frustrujące, ponieważ programista nie wiedział czy używać danych z bazy danych czy ze
zmiennych sesji.
Takie niefortunne działanie było spowodowane tym, że pierwszy programista nie zapewnił realizacji
dwóch celów. Po pierwsze, zmienne sesji zostały zastosowane, aby wyeliminować odwołania do baz danych (aby
przyspieszyć ładowanie strony) przy pobieraniu danych o uprawnieniach. Po drugie, zmienne sesji były używane
do uproszczenia sprawdzania uprawnień w kodzie strony.
Pierwszy cel nie został zrealizowany, ponieważ programista nie ładuje jawnie zmiennych sesji, są one
automatycznie odczytywane z trwałego nośnika. Na wszystkich stronach aplikacji dane o uprawnieniach są
ładowane z serwera WWW i po zakończeniu strony ponownie zapisywane. Proces ten powoduje, że nawet strony,
które nie korzystają z uprawnień, są spowolnione przez proces ładowania zmiennych sesji. Ładowanie danych
uprawnień z bazy danych jedynie na stronach, które wymagają tych informacji, jest o wiele bardziej efektywne.
Drugi cel nie został spełniony, ponieważ programista nie korzystał w pełni z wydajności bazy danych. Na
wielu stronach kod wyglądał następująco:
WYIERZ wszystkie potrzebne dane z konta pierwotnego użytkownika
DLA KAŻDEGO WIERSZA
JEŻELI identyfikator tego wiersza znajduje się na liście uprawnień w zmiennej sesji
wyświetl lub wykorzystaj dane
W PRZECIWNYM WYPADKU
ignoruj ten wiersz, ponieważ doradca nie powinien go widzieć
Jak wspominaliśmy w rozdziale 6 „Współpraca z bazami danych”, należy wykorzystywać możliwości
bazy danych do poprawienia wydajności aplikacji. Poprzedni przykład kodu może być zastąpiony zapytaniem, w
którym tabela z danymi jest połączona z tabelą z uprawnieniami. Spowoduje to usunięcie ogromnej ilości kodu i
wyeliminuje konieczność przechowywania listy uprawnień w danych sesji.
Zmienne sesji są niezmiernie potrzebne przy programowaniu aplikacji WWW, ale powinny być używane
rozważnie. W trakcie projektowania należy rozważyć wszystkie alternatywne sposoby uzyskana tego samego
efektu. Większość aplikacji WWW wymaga zastosowania tylko kilku zmiennych sesji. Gdy chcesz zastosować
zmienną sesji, postaw sobie następujące pytania:
• Czy ta zmienna jest używana w całym programie? Jeżeli ta wartość jest używana na każdej stronie
aplikacji, jest to świetny kandydat na zmienną sesji. Na przykład, jeżeli aplikacja korzysta z logowania
użytkowników, dobrze jest przechowywać identyfikator użytkownika w zmiennej sesji.
• Czy zmienna ta jest unikalna dla tej sesji? Jeżeli jest to na pewno dana związana z sesją, zapamiętaj ją w
zmiennej sesji. Na przykład, jeżeli tworzysz wózek na zakupy, musisz prawdopodobnie przechowywać
jego identyfikator w zmiennej sesji. Jednak jeżeli aplikacja korzysta z kilku trwałych wózków na zakupy
w których użytkownik może przechowywać zamówienie w czasie kilku sesji, prawdopodobnie nie
potrzebujesz zmiennej sesji, ale dobrze zaprojektowanej bazy danych.
• Czy dana jest dana kluczową? Pytanie to jest związane z pierwszym. Jeżeli aplikacja korzysta ze
zmiennych sesji do przechowywania identyfikatora użytkownika, unikaj przechowywania innych danych
93 PHP – Kompendium wiedzy
związanych z użytkownikiem w zmiennej sesji. Zamiast tego, w razie potrzeby skorzystaj z identyfikatora
do odczytania potrzebnych danych. Na przykład, jeżeli aplikacja personalizuje strony, nie przechowuj
zmiennych potrzebnych do tego celu w sesji. Przechowuj w sesji identyfikator i wykorzystaj kilka funkcji
odczytujących dane potrzebne do personalizacji. Struktura taka nie tylko eliminuje powtórzenia danych,
ale również pozwala użytkownikowi na zmianę swoich ustawień w jednej sesji i natychmiastowe
odzwierciedlenie tego w drugiej równoległej sesji.
• Czy aplikacja na pewno potrzebuje zmiennych sesji? Pytanie to może wydawać się oczywiste, ale często
jest pomijane z powodu łatwości używania zmiennych sesji. To, że użytkownik przegląda witrynę i
przeprowadza jakieś operacje niekoniecznie oznacza, że potrzebna jest sesja. Zmienne sesji nie powinny
zastępować innych mechanizmów przechowywania danych.
Jeżeli odpowiedź na te pytania nie zgadza się z zasugerowanymi odpowiedziami, należy rozważyć
zastosowanie innych metod przechowywania danych. Sesje są świetnym narzędziem, ale nieprawidłowo używane
mogą doprowadzić do stworzenia aplikacji pełnej błędów i trudnej do utrzymania.

Podsumowanie
W PHP dostępne jest kilka świetnych narzędzi do stworzenia mechanizmu sesji. Należy wybrać
mechanizm, który najlepiej pasuje do potrzeb i którego zastosowanie będzie przynosiło owoce w dłuższym czasie
a nie tylko będzie miał krótkoterminowy wpływ na kodowanie aplikacji. Nieprawidłowe użycie zmiennych sesji
może doprowadzić do stworzenia aplikacji trudnej do rozwijania, która posiada trudne do zidentyfikowania błędy.

Rozdział 7 – Sesje i stan aplikacji 94
Rozdział 8. Uwierzytelnianie
Wstęp
W poprzednim rozdziale „Sesje i stan aplikacji” omówione zostały sposoby śledzenia użytkowników
witryny WWW w celu zapewnienia ciągłości pracy aplikacji. Ten rozdział poświęcony będzie sposobom
upewnienia się, że użytkownicy maja wystarczające uprawnienia do pracy w aplikacji.
Istnieją różne schematy uwierzytelniania przeznaczone do różnych zadań. Większość serwerów WWW
posiada narzędzia przeznaczone do autoryzacji użytkowników w oparciu o uprawnienia i pliki serwera. W tym
rozdziale zajmiemy się uwierzytelnianiem opartym na mechanizmach serwera, ale jedynie w oparciu o serwer
Apache na Linuksie (Windows i IIS również umożliwiają uwierzytelnianie, ale nie zostanie ono tutaj opisane). W
dalszej części rozdziału przedstawiony zostanie również mechanizm niezależny od serwera i platformy.

Podstawowe uwierzytelnianie w Apache
Rozdział ten rozpoczniemy omówieniem podstawowego schematu uwierzytelniania dostępnego w
serwerze WWW Apache oraz problemami związanymi z tą metodą. Osoby znające dyrektywy uwierzytelniania
Apache oraz przeznaczenie plików .htaccess i innych plików konfiguracyjnych serwera Apache nie dowiedzą się
tutaj zbyt wiele nowego. Nawet w prostej witrynie może być potrzebne ograniczenie dostępu do niektórych stron.
Wykorzystanie mechanizmu uwierzytelniania dostarczanego przez serwer WWW jest zwykle szybkim i
efektywnym sposobem zrealizowania takiego mechanizmu. Na przykład może być niezbędne stworzenie zbioru
stron przeznaczonych do administracji witryną, za pomocą których można przeglądać i zmieniać wybrane
elementy witryny. Strony te nie mogą być dostępne dla wszystkich użytkowników, ale administrator musi mieć
do nich dostęp z dowolnej przeglądarki.
Aby zrealizować takie założenia możesz przenieść wszystkie strony administracyjne do osobnego
podkatalogu w drzewie katalogów witryny WWW oraz zmienić konfigurację Apache tak, aby dostęp do stron
znajdujących się w tym katalogu wymagały autoryzacji. Odpowiednie dyrektywy konfiguracji mogą znajdować
się w pliku httpd.conf lub .htaccess w chronionym katalogu. Jeżeli masz dostęp do plików konfiguracyjnych
Apache powinieneś skorzystać z nich zamiast z pliku .htaccess. Korzystanie z pliku .htaccess jest mniej
efektywne od wykorzystania standardowych plików konfiguracyjnych, ponieważ jest on odczytywany za każdym
żądaniem pliku z katalogu zawierającego plik .htaccess. Jednak jeżeli witryna jest umieszczona na
dzierżawionym serwerze, prawdopodobnie nie będziesz mógł zmienić plików konfiguracyjnych i zrestartować
serwera WWW w celu pobrania zmienionej konfiguracji.
Na wydruku 8.1 zamieszczony jest wydruk prostej strony HTML zawierającej łącze do podkatalogu ze
stroną administracyjną. Strona administracyjna znajduje się w katalogu wymagającym uwierzytelniania, więc
kliknięcie tego łącza spowoduje, że przeglądarka wyświetli standardowe okno uwierzytelniania, pokazane na
rysunku 8.1. Wydruk 8.2 zawiera dyrektywy konfiguracji Apache które powodują wyświetlenie okna logowania.
Wydruk 8.1. Prosta strona HTML z łączem do stron administracyjnych
<html>
<head>
<title>Proste uwierzytelnianie Apache</title>
</head>
<body>
<a href="admin/index.phtml">Przejdź do strony administratora</a>
</body>
</html>
Wydruk 8.2. Dyrektywy konfiguracji Apache włączające podstawowe uwierzytelnianie
AuthUserFile /www/auth_users
AuthName Adminstrative
AuthType Basic
<Limit GET>
require valid-user
</Limit>
Rysunek 8.1. Okno
dialogowe
uwierzytelniania w
przeglądarce

Więcej informacji na temat użycia uwierzytelniania Apache można znaleźć w Sieci oraz we wielu
świetnych książkach poświęconych serwerowi Apache. Ten rodzaj uwierzytelniania wymaga współpracy
pomiędzy przeglądarką i serwerem. Mechanizm ten wygląda następująco: gdy użytkownik musi zostać
autoryzowany, serwer WWW wysyła żądanie 401 do przeglądarki a przeglądarka odpytuje użytkownika i odsyła
wprowadzone przez niego dane do serwera. Jeżeli serwer zaakceptuje uwierzytelnianie, chroniony zasób jest
udostępniony użytkownikowi. Przeglądarka wysyła wprowadzone dane do serwera podczas żądania sprowadzenia
wszystkich kolejnych stron aż do zakończenia pracy przeglądarki.
PHP posiada zmienne globalne, których możesz użyć w aplikacji w celu odczytania danych autoryzacji.
Możesz skorzystać ze zmiennych $PHP_AUTH_USER oraz $PHP_AUTH_PW do odczytania nazwy użytkownika i hasła.
Wydruk 8.3 zawiera stronę wyświetlająca dane autoryzacji. Na rysunku 8.2. pokazana jest zawartość tej strony po
przejściu do niej poprzez łącze znajdujące się na stronie z wydruku 8.1.
Wydruk 8.3. Wyświetlanie zawartości zmiennych autoryzacji
<html>
<head>
<title>Strona administratora</title>
</head>
<body>
<h1>Witamy na stronie administratora</h1>
<?php
print( "PHP_AUTH_USER: $PHP_AUTH_USER<br>" );
print( "PHP_AUTH_PW: $PHP_AUTH_PW<br>" );
?>
</body>
</html>
Rysunek 8.2.
Zmienne
autoryzacji w PHP

Schemat autoryzacji Apache zapewnia podstawowy stopień bezpieczeństwa witryny. Jest on szczególnie
użyteczny w sytuacjach, gdy chcesz chronić wszystkie strony i inne zasoby znajdujące się we fragmencie drzewa
Rozdział 8 – Uwierzytelnianie 96
katalogów witryny. Ograniczeniem stosowania tej metody jest konieczność dodawania i usuwania użytkowników
poprzez wykonanie odpowiednich poleceń na serwerze. W następnej części zostanie opisane w jaki sposób można
wykorzystać PHP do aktualizacji pliku haseł, co pozwoli na stworzenie narzędzia WWW do dodawania i
usuwania użytkowników.

Aktualizacja pliku .htaccess przy użyciu PHP
Jeżeli podstawowe uwierzytelnianie serwera WWW jest wystarczające w tworzonej aplikacji, można przy
użyciu PHP stworzyć narzędzie administracyjne upraszczające zarządzanie użytkownikami. Można udostępnić to
narzędzie zaufanym osobom, którzy będą mogli operować użytkownikami bez konieczności udostępniania im
bezpośredniego dostępu do serwera.
Programiści zespołu The Webmasters Net (http://www.theWebmasters.net/) stworzyli dwie klasy służące
do zarządzania użytkownikami i grupami plików dla celów podstawowego uwierzytelniana. Do manipulacji
standardowym plikiem Apache htpasswd można wykorzystać klasę Htpasswd. Na wydruku 8.4 pokazany został
przykład, jak można wykorzystać tę klasę do autoryzacji użytkownika, przy użyciu bardzo małej ilości kodu.
Przykład korzysta z tego samego pliku, który został użyty w poprzednim przykładzie.
Wydruk 8.4. Sprawdzanie poprawności autoryzacji użytkownika za pomocą klasy Htpasswd
<html>
<head>
<title>Szybkie sprawdzenie użytkownika z uzyciem klasy Htpasswd</title>
</head>
<body>
<?php
include( "./class.Htpasswd.php3" );
$aHTPasswd = new Htpasswd("/www/auth_users");
if ( !$aHTPasswd->EXISTS )
{
print( "Błąd autoryzacji<br>" );
}
else
{
if ( $aHTPasswd->verifyuser( "phpbook", "phpbook" ) )
{
print( "phpbook to prawidłowy użytkownik<br>" );
}
else
{
print( "phpbook nie jest prawidłowym użytkownikiem<br>" );
}
}
?>
</body>
</html>
Klasę Htpasswd można również wykorzystać do dodawania nowych użytkowników, usuwania
użytkowników, zmiany hasła, sprawdzania poprawności użytkownika oraz zmiany jego nazwy. Przy pomocy tej
klasy można napisać obszerne narzędzie do administracji użytkownikami, które będzie pomocne przy zarządzaniu
mechanizmem podstawowego uwierzytelniania serwera. Skrypty na wydruku 8.5, 8.6 i 8.7 zawierają skrypty
pokazujące, w jaki sposób można połączyć wszystkie te operacje w jednym formularzu.
Wydruk 8.5 zawiera pierwszą część skryptu. Jest ona używana do inicjalizacji skryptu i określenia czy
strona jest oglądana pierwszy raz, czy została wywołana w wyniku żądania POST. Skrypt ten jest podobny do
wielu przytoczonych do tej pory przykładów, które przesyłają dane do samego siebie i są używane zarówno do
wyświetlania jak i do zmiany danych.
Wydruk 8.5. Użycie klasy Htaccess do zarządzania użytkownikami
<?php
include( "./class.Htpasswd.php3" );
$aHTPasswd = new Htpasswd("/www/auth_users");
if ( !$aHTPasswd->EXISTS )
{
print( "Błąd krytyczny<br>" );
exit;
}

if ( $REQUEST_METHOD == 'POST' )
{
switch ( $acttype )
{

97 PHP – Kompendium wiedzy
case 'none' :
break;
case 'add' :
$aHTPasswd->addUser( $NewUserName, $NewUserPass );
print( "<b>Dodano użytkownika $NewUserName</b><br>" );
break;
case 'delete' :
$aUserName = $aHTPasswd->USERS[$CurUserRow]["user"];
$aHTPasswd->deleteUser( $aUserName );
print( "<b>Usunięto użytkownika $aUserName</b><br>" );
break;
case 'rename' :
$aUserName = $aHTPasswd->USERS[$CurUserRow]["user"];
$aHTPasswd->renameUser( $aUserName, $RenameName );
print( "<b>Nazwa użytkownika zmieniona z $aUserName
na $RenameName</b><br>" );
break;
case 'changepass' :
$aUserName = $aHTPasswd->USERS[$CurUserRow]["user"];
$aHTPasswd->changePass( $aUserName, $ChangePass );
print( "<b>Zmieniono hasło dla użytkownika $aUserName</b><br>" );
break;
}
}
?>
Jeżeli skrypt ten zostanie wywołany w wyniku żądania POST, na podstawie wartości zmiennej formularza
$acttype podejmowana jest decyzja co do kolejnej akcji. Zmienna posiada pięć możliwych wartości: none, add,
delete, rename oraz changepass. W zależności od wyboru użytkownika podejmowana jest odpowiednia akcja. W
skrypcie założono, że wszystkie potrzebne dane są prawidłowo wypełnione. Oczywiście, aby program mógł być
normalnie używany niezbędne jest dodanie kodu kontroli poprawności.
W następnej części skryptu ustawiana jest zmienna $acttype. Dla wszystkich przycisków znajdujących się
na formularzu zdefiniowana jest odpowiednia akcja. Do ustawiania ukrytej zmiennej formularza $acttype
wykorzystujemy JavaScript.
Wydruk 8.6. Ustawianie zmiennej $acttype
<html>
<head>
<title>Prosty program zarządający użytkownikami</title>
<script language="JavaScript">
<!--
function DoSubmit( aType )
{
document.mainform.acttype.value = aType;
document.mainform.submit();
}
//-->
</script>
</head>
Ostatnia część skryptu pokazana na wydruku 8.7 jest po prostu stroną HTML zawierającą formularz. Kod
PHP jest jedynie używany do wstawiania istniejących użytkowników do listy SELECT. Każdy przycisk na
formularzu zawiera atrybut onClick, który powoduje wywołanie przedstawionej funkcji JavaScript, która realizuje
wysłanie danych formularza do odpowiedniej strony.
Wydruk 8.7. Strona z formularzem HTML
<body>
<form action="<?=$PHP_SELF?>" method="post" name="mainform" id="mainform">
<input type="hidden" name="acttype" value="none">
<h1>Prosty program zarządzający użytkownikami</h1>
<h2>Dodanie użytkownika</h2>
Nazwa nowego użytkownika: <input type="text" name="NewUserName"><br>
Hasło: <input type="password" name="NewUserPass"><br>
<input type="button" value="Dodaj" onClick="DoSubmit( 'add' );">
<hr>
<h2>Zmiana użytkownika</h2>
<table>
<tr>
<td>
<select name="CurUserRow" size="10">
<?php
$nIndex = 0;
foreach( $aHTPasswd->USERS as $aUser )
{
print( "<option value=\"$nIndex\">$aUser[user]</option>" );
$nIndex++;
}
?>

Rozdział 8 – Uwierzytelnianie 98
</select>
</td>
<td>
Usunięcie zaznaczonego użytkownika:
<input type="button" value="Usuń" onClick="DoSubmit( 'delete' );">
<br><br>
Zmiana nazwy zaznaczonego użytkownika:
<input type="text" name="RenameName"><input type="button"
value="Zmiana nazwy" onClick="DoSubmit( 'rename' );"><br><br>
Zmiana hasła dla zaznaczonego użytkownika: <input type="password"
name="ChangePass"><input type="button" value="Zmiana hasła"
onClick="DoSubmit( 'changepass' );"><br><br>
</td>
</tr>
</table>
</form>
</body>
</html>
Na rysunku 8.3. pokazana jest strona bezpośrednio o dodaniu użytkownika scott. Jak mówiliśmy
wcześniej, skrypt ten nie jest kompletnym narzędziem zarządzającym użytkownikami, a jedynie pokazuje sposób
wykorzystania klasy Htpasswd. Można również skorzystać z dostarczanej przez The Webmasters Net klasy
Htgroup do tworzenia i zarządzania grupami użytkowników.
Rysunek 8.3.
Program
zarządzający
użytkownikami
w działaniu

Podstawowe uwierzytelnianie za pomocą PHP
Poprzednie dwie części opisywały podstawowe uwierzytelnianie serwera Apache do ochrony fragmentów
witryny WWW (zwykle katalogów). W niektórych przypadkach może być wymagane zabezpieczenie tylko
niektórych stron aplikacji lub nie jest możliwa modyfikacja odpowiednich plików na serwerze WWW. W takim
przypadku możesz wykorzystać PHP do wysyłania odpowiednich nagłówków do serwera i w ten sposób
bezpośrednio żądać autoryzacji.

99 PHP – Kompendium wiedzy
Tak jak w przypadku wysyłania innych danych nagłówka, należy albo wysyłać dane nagłówków przed
wysłaniem jakichkolwiek danych strony, albo korzystać z buforowania wyjścia. Na wydruku 8.8 zamieszczony
jest prosty skrypt żądający autoryzacji. Skrypt ten jest w postaci pliku dołączanego, auth_include.inc, więc będzie
go można łatwo dodawać do wszystkich stron wymagających autoryzacji.
Wydruk 8.8. Skrypt auth_include.php
<?php
$aDoAuth = True;
if ( isset( $PHP_AUTH_USER ) )
{
if ( ( $PHP_AUTH_USER == "ryan" ) &&
( $PHP_AUTH_PW == "dentist" ) )
{
// prawidłowa nazwa użytkownika i hasło
$aDoAuth = False;
}
}
if( $aDoAuth == True )
{
Header( "WWW-Authenticate: Basic realm=\"My Realm\"" );
Header( "HTTP/1.0 401 Unauthorized" );
echo "Nie udało się zalogowanie do systemu.\n";
exit;
}
?>
Skrypt ten na początku pracy sprawdza, czy ustawiona jest zmienna $PHP_AUTH_USER. Jeżeli tak, to
wartości zmiennych $PHP_AUTH_USER i $PHP_AUTH_PW są porównywane z prawidłową nazwą użytkownika i hasłem
aplikacji. Jeżeli sprawdzenie to się powiedzie, nie ma potrzeby wysyłania do przeglądarki nagłówka autoryzacji.
Jeżeli porównanie nie uda się skrypt wysyła do przeglądarki nagłówek HTTP 401, który powoduje wyświetlenie
okna autoryzacji. Proces ten jest powtarzany aż do podania właściwych danych autoryzacji, albo do przerwania
uwierzytelniania przez użytkownika. Dołączenie tego pliku na początku dowolnego skryptu powoduje
konieczność autoryzacji użytkownika skryptu.
Prawdziwy system uwierzytelniania nie powinien mieć zaszytych nazw użytkowników i haseł w samym
skrypcie. Zamiast tego należy wykorzystać bazę danych lub usługę katalogową (na przykład LDAP) lub nawet
pliki zawierające dane uwierzytelniania.
Jedną z zalet takiego podejścia jest możliwość odwołania uwierzytelnienia użytkownika poprzez ponowne
wysłanie nagłówka HTTP 401. Można to wykorzystać do ponownego logowania użytkownika po określonym
czasie bezczynności lub do chronienia różnymi hasłami różnych części aplikacji.
PHP posiada wystarczająco dużo narzędzi i elastyczności aby można było napisać dowolny system
autoryzacji użytkowników. Poprzednia metoda jest oparta o możliwość obsługi przez przeglądarki wywołań
HTTP 401 ale metoda ta posiada wiele ograniczeń. Następna metoda jest bardziej niezależna od platformy i
posiada bardziej elastyczne podejście do uwierzytelniania.

Kompletny system uwierzytelniania oparty o PHP
Wykorzystanie metody autoryzacji opisanej w poprzedniej części jest łatwe i proste. Większość
programistów WWW wykorzystywało już pliki .htaccess do zabezpieczania katalogów, więc sposób ten jest
zwykle dobrze znany. W tej części skupimy się na implementacji, która nie polega na wywołaniach HTTP 401 z
serwera, wymuszających na przeglądarce wyświetlenie okna uwierzytelniania. Głównym powodem użycia tego
typu implementacji jest zwiększenie elastyczności aplikacji.
Poniższa implementacja wykorzystuje klasę Auth z PHPLIB. Jest to ekstremalnie solidna i elastyczna
implementacja, ale przez o wymaga sporo zachodu, zanim można będzie ją wykorzystać. Jednak po jej
zaprogramowaniu jest świetnym zamiennikiem metody opisanej w poprzedniej części. Jeżeli nie korzystamy z
mechanizmów przeglądarki przy wyświetlaniu okna uwierzytelniania, musimy sami tworzyć formularz HTML
służący do pobierania danych niezbędnych dla naszej aplikacji. Dodatkowo, PHPLIB pozwala na stworzenie
autoryzacji opartej na uprawnieniach, więc do każdej strony można przypisać wymagany poziom uprawnień dla
każdego z użytkowników.
Mechanizm uwierzytelniania realizowany przez PHPLIB jest w wielu punktach podobny do mechanizmu
obsługi sesji. Do działania wymaga on sesji PHPLIB. Schemat autoryzacji PHPLIB jest uruchamiany podczas
wywołania funkcji PHPLIB page_open() i zażądanie „własności” sess. Gdy zostanie zażądana ta własność,
Rozdział 8 – Uwierzytelnianie 100
PHPLIB sprawdza zalogowanie użytkownika. Jeżeli użytkownik nie podawał wcześniej danych autoryzacji,
PHPLIB wyświetla zdefiniowaną przez użytkownika stronę. Strona ta pobiera dowolne dane, jakich aplikacja
wymaga do prawidłowej autoryzacji użytkownika. Następnie PHPLIB wywołuje dostarczoną przez użytkownika
funkcję sprawdzającą uprawnienia użytkownika. Jeżeli funkcja ta zaakceptuje użytkownika, PHPLIB wyświetla
stronę a w przeciwnym wypadku następuje ponowne uwierzytelnianie. Na rysunku 8.4. pokazana jest interakcja
pomiędzy klientem, serwerem WWW (i tym razem jest to 1U z Penguin Computing) oraz aplikacją PHP.
Rysunek 8.4.
Interakcja w
schemacie
autoryzacji
PHPLIB

Z powodu elastyczności jaką zapewnia PHPLIB wymagane jest wykonanie kilku niezbędnych kroków
zanim użyjemy naszej klasy autoryzacji. Po pierwsze, na wydruku 8.9 pokazane są klasy zdefiniowane przez
użytkownika niezbędne do stworzenia klasy Auth i klas ją wspomagających (Klasy sesji i bazy danych są
identyczne jak te, których używaliśmy w rozdziale 7). W naszym przypadku dane autoryzacji znajdują się w
tabeli bazy danych MySQL. Tabela użyta do autoryzacji jest zdefiniowana następująco:
CREATE TABLE MyAuth (
FirstName varchar(20) NOT NULL,
SurName varchar(30) NOT NULL,
password varchar(20) NOT NULL,
PRIMARY KEY (FirstName, SurName)
);
Wydruk 8.9. Przygotowanie klas używanych przez klasę PHPLIB Auth
<?php
include( "page.inc" );
include( "ct_sql.inc" );
include( "session.inc" );
include( "db_mysql.inc" );
include( "auth.inc" );
class MySQLDB extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}
class MySQLCt extends CT_Sql
{
var $classname = "MySQLCt";
var $database_table = "active_sessions";
var $database_class = "MySQLDB";
}
class MySqlSession extends Session
{
var $classname = "MySqlSession"; // Obsługa przechowywania
var $mode = "cookie";
var $lifetime = 0; // użycie cookie sesji
var $that_class = "MySQLCt"; // wybór kontenera
var $allowcache_expire = 0;
}
class Sample_Auth extends Auth
{
var $classname = "Sample_Auth";
var $lifetime = 20; // 20 minut (0 == ciągle)

function auth_loginform()
{
include( "./sample_lform.htinc" );
}

function auth_validatelogin()
{
global $FirstName, $SurName, $Password;

$aDB = new MySQLDB;
$aSQL = "select * from MyAuth where ( FirstName = ";
$aSQL .= "'$FirstName' ) and ( SurName = '$SurName' )";

101 PHP – Kompendium wiedzy
$aSQL .= "and ( Password = '$Password' )";
$aDB->query( $aSQL );
if ( $aDB->num_rows() > 0 )
{
return $FirstName;
}
else
{
return False;
}
}
}
?>
Klasa Sample_Auth dziedzicząca po klasie bazowej Auth zapewnia działanie specyficzne dla bieżącej
aplikacji. Zdefiniowane są odpowiednie funkcje auth_loginform() i auth_validatelogin(). Funkcja
auth_loginform() jest wywoływana, gdy klasa Auth wymaga uwierzytelnienia użytkownika. Możesz użyć
instrukcji print() do stworzenia formularza HTML potrzebnego do zalogowania, ale zwykle dołączenie pliku jest
łatwiejsze. Na wydruku 8.10 pokazany jest plik użyty w tym przykładzie.
Wydruk 8.10. Przykładowy formularz logowania (sample_lform.htinc)
<?php
global $FirstName;
global $SurName;
$aCurFirstName = "";
$aCurSurName = "";
if ( !empty( $FirstName ) )
{
$aCurFirstName = $FirstName;
}
if ( !empty( $SurName ) )
{
$aCurSurName = $SurName;
}
?>
<html>
<head>
<title>Formularz autoryzacji dla PHPLIB</title>
</head>
<body>
<form action="<?=$this->url()?>" method="post">
<table>
<tr>
<td>
Imię:
</td>
<td>
<input type="text" name="FirstName" value="<?=$aCurFirstName?>">
</td>
</tr>
<tr>
<td>
Nazwisko:
</td>
<td>
<input type="text" name="SurName" value="<?=$aCurSurName?>">
</td>
</tr>
<tr>
<td>
Hasło:
</td>
<td>
<input type="password" name="Password">
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="Submit" value="Log In">
</td>
</tr>
<?php if ( !empty( $FirstName ) ) { ?>
<tr>
<td colspan="2">
<br><br>
Podane dane są nieprawidłowe, spróbuj jeszcze raz..
</td>
</tr>
<?php } ?>
</table>

Rozdział 8 – Uwierzytelnianie 102
</form>
</body>
</html>
Strona ta jest zaprojektowana jedynie do wyświetlania przez klasę Auth. Na początku sprawdza, czy
zmiene formularza mają jakieś wartości. Może to się zdarzyć, gdy wyświetlamy formularz po nieudanej
autoryzacji użytkownika W tym przypadku klasa Auth ponownie wyświetla formularz logowania. Wartości
zmiennych formularza są przenoszone do pól formularza FirstName i SurName jedynie z grzeczności (ale
użytkownik nie musi ponownie wpisywać tych danych).
Następnie strona wyświetla trzy pola tekstowe do wprowadzenia imienia, nazwiska i hasła użytkownika.
Dane formularza są wysyłane do strony określonej przez $this->url(). W kontekście tej strony zmienna $this
wskazuje na bieżący obiekt klasy pochodnej po Auth. Funkcja url() zwraca stronę, na której był użytkownik
zanim obiekt klasy interweniował i wywołał naszą stronę autoryzacji. Na koniec definiujemy ostrzeżenie jakie
zobaczy użytkownik gdy poda niewłaściwe dane.
Gdy dane formularza zostaną przesłane do oryginalnej strony, Auth sprawdza ponownie dane autoryzacji.
Aby to zrobić wywołuje ona zdefiniowaną przez użytkownika funkcję auth_validatelogin(). Na wydruku 8.11
pokazujemy funkcję użytą w naszym przykładzie.
Wydruk 8.11. Funkcja auth_validatelogin()
function auth_validatelogin()
{
global $FirstName, $SurName, $Password;

$aDB = new MySQLDB;
$aSQL = "select * from MyAuth where ( FirstName = ";
$aSQL .= "'$FirstName' ) and ( SurName = '$SurName' )";
$aSQL .= "and ( Password = '$Password' )";
$aDB->query( $aSQL );
if ( $aDB->num_rows() > 0 )
{
return $FirstName;
}
else
{
return False;
}
}
Funkcja odwołuje się do zmiennych globalnych $FirstName, $SurName i $Password, zdefiniowanych w
formularzu logowania. Ich wartości są wyszukiwane w tabeli MySQL zawierającej trzy kolumny: FirstName,
SurName i Password. Jeżeli odnaleziony zostanie rekord w tabeli opisujący bieżącego użytkownika, funkcja
auth_validatelogin() zwraca imię użytkownika (oczywiście użycie imienia jako identyfikatora użytkownika nie
było by zbyt dobrym pomysłem). Jeżeli nie ma pasującego rekordu, zwracana jest wartość False.
Na wydruku 8.12 pokazana została typowa strona WWW wykorzystująca o autoryzacji klasę Auth. W
przykładzie wyświetlane są cztery łącza do innych podobnych stron, wymagających autoryzacji. Ostatnia strona z
listy zapewnia wylogowanie użytkownika.
Wydruk 8.12. Prosta strona wykorzystująca klasę Auth
<?php
// bez buforowania (z witryny php.net)
include( "./auth_phplib.php" );
page_open( array( "sess" => "MySqlSession",
"auth" => "Sample_Auth" ) );
header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header ("Cache-Control: no-cache");
header ("Pragma: no-cache");
?>
<html>
<head>
<title>Przykład użycia klasy PHPLIB Auth</title>
<META HTTP-EQUIV="Expires" CONTENT="-1">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
</head>
<body>

<h2>Strona główna</h2>
Posiadasz uprawnienia do oglądania tej strony!
<ul>
<li><a href="test_auth_phplib.phtml">Strona główna</a></li>
<li><a href="test_auth_phplib2.phtml">Strona druga</a></li>
<li><a href="test_auth_phplib3.phtml">Strona trzecia</a></li>
<li><a href="test_auth_phplib_logout.phtml">Wylogowanie</a></li>

103 PHP – Kompendium wiedzy
</ul>

</body>
</html>
<?php
page_close();
?>
Pierwsza strona dołącza prosty plik uwierzytelniania a następnie wywołuje funkcję PHPLIB page_open(),
która uaktywnia sesję i mechanizm uwierzytelniania. Na końcu strony wywoływana jest funkcja page_close(),
która zapisuje dane sesji. Zmienne uwierzytelniania są przesyłane pomiędzy stronami przy pomocy mechanizmu
sesji. Druga i trzecia strona jest funkcjonalnie identyczna z pierwszą. Strona Wylogowanie, zawiera na dole strony
następujący kod wymuszający wylogowanie użytkownika:
<?php
$auth->logout();
page_close();
?>
Funkcja $auth->logout() może być wywołana w dowolnym momencie, a wymusza ona ponowne
wywołanie autoryzacji użytkownika na następnej stronie.
Kilka wierszy w tym przykładzie nie jest związane z mechanizmem uwierzytelniania. Wywołania funkcji
header() oraz znaczniki <META> zapewniają, że przeglądarka nie będzie przechowywała strony w buforze. Kod ten
jest dosyć ważny, ponieważ buforowane strony mogą mylić użytkowników, którzy nie będą wiedzieli, czy są już
zalogowani. Szczególne kłopoty sprawia Microsoft Internet Explorer, więc podjęto szczególne środki
zapewniające prawidłowe działanie przykładów. Podczas testowania tego przykładu nie stwierdziliśmy żadnych
problemów w IE 5.5, wszystkich wersjach Netscape, WebTV i Opera.
Jak wcześniej wspomnieliśmy, PHPLIB jest niezwykle elastycznym narzędziem, pozwalającym na
stworzenie własnego mechanizmu uwierzytelniania. Można stworzyć skomplikowane lub proste schematy
uwierzytelniania, w zależności od wymagań aplikacji. Zaletą tego podejścia jest możliwość tworzenia własnych
formularzy logowania, porównywanie wpisanych danych z danymi przechowywanymi na dowolnym nośniku
informacji, oraz możliwość łatwego wylogowania użytkownika. Umieszczenie w aplikacji systemu zabezpieczeń
wydaje się bardziej efektywne, szczególnie dla początkujących użytkowników, którzy mogą być zaskoczeni
systemowymi oknami dialogowymi.

Podsumowanie
W tym rozdziale zostało omówionych wiele aspektów uwierzytelniania użytkowników w aplikacjach
PHP. Pierwsze kilka omówionych metod jest mocno zależne od platformy, ale są łatwe do implementacji.
Ostatnia metoda, wymagająca użycia klas PHPLIB jest bardziej złożona, ale o wiele bardziej elastyczna i
całkowicie przenośna pomiędzy serwerami WWW i systemami operacyjnymi.
Jeżeli aplikacja wymaga jakiegoś typu uwierzytelniania użytkowników, powinieneś w fazie projektowania
określić specyficzne wymagania tej aplikacji. Niektóre mechanizmy uwierzytelniania nie posiadają
wystarczającej elastyczności. Inne mogą nie zapewniać dostatecznego poziomu bezpieczeństwa. Przy pomocy
tego rozdziału można skojarzyć twój potrzeby z oferowanymi przez poszczególne metody możliwościami.

Rozdział 8 – Uwierzytelnianie 104
Rozdział 9. Niezależność od przeglądarki
Wstęp
Podczas pisania standardowych aplikacji interfejs użytkownika jest tworzony dla potrzeb aplikacji i
zwykle jest on przeznaczony dla jednej platformy. Nie przewiduje się niespodziewanych zmian tego interfejsu w
czasie działania programu w zależności od użytkownika, który używa aplikacji. W czasie pisania aplikacji dla
WWW, interfejs użytkownika nie jest już tak niezmienny, ponieważ może być on odtwarzany przez różne typy
przeglądarek na różnych platformach. Tworzenie aplikacji niezależnej od przeglądarki wymaga możliwości
wykrywania typu przeglądarka i wykorzystywania jej możliwości.
Mimo, że większość nowoczesnych przeglądarek będzie wyświetlało aplikacje w podobny sposób, zawsze
istnieją różnice. Jeżeli aplikacja wymaga jakiejś własności przeglądarki, należy tak napisać aplikację, aby
sprawdzała typ użytej przeglądarki i odpowiednio reagowała. PHP pozwala na kilka metod wykrywania rodzaju
przeglądarki, rozpoczynając od stworzenia własnego rozwiązania do użycia narzędzi firm trzecich. W rozdziale
tym przedstawimy przykłady wielu metod, z których będziesz mógł wybrać odpowiednią dla twojej aplikacji.

Rozpoczynamy
Na najbardziej podstawowym poziomie, PHP pozwala na odczytanie typu przeglądarki poprzez zmienną
globalną $HTTP_USER_AGENT. Ciąg ten jest wysyłany przez przeglądarkę do serwera wraz z każdym żądaniem.
Jeżeli wystarczy ci proste rozpoznanie typu przeglądarki, można wykorzystać bezpośrednio zmienną
$HTTP_USER_AGENT. Na przykład, sposób wykrycia przeglądarki Internet Explorer przy użyciu porównywania
ciągów zamieszczony jest na wydruku 9.1.
Wydruk 9.1. Wyświetlanie podstawowych danych na temat przeglądarki
<html>
<head>
<title>Szybkie sprawdzenie typu przeglądarki</title>
</head>
<body>
<?php
$aPos = strpos( $HTTP_USER_AGENT, "MSIE" ) ;
if ( $aPos === False )
{
print( "To <b>nie</b> jest MS Internet Explorer!<br>" );
}
else
{
print( "Przeglądarka MS Internet Explorer!<br>" );
}
?>
</body>
</html>
W przykładzie tym sprawdzamy, czy przeglądarka klienta to Internet Explorer. Przykład opiera się na tym,
że zwracana nazwa przeglądarki w większości wersji Internet Explorera zawiera fragment MSIE. Można
wykorzystać tę informację do wyświetlenia odpowiedniego komunikatu, lub przekierować użytkownika do innej
części witryny, która jest zoptymalizowana do wyświetlania stron w określonej przeglądarce. Metoda ta sprawdza
się w niektórych przypadkach, ale jest zbyt prosta, aby obsługiwać ogromną ilość prawidłowych ciągów user
agent. Kilka przeglądarek zmieniło format tego ciągu podczas jednej ze zmiany wersji, więc rozpoznanie
określonej przeglądarki może być problematyczne, jeżeli potrzebujesz dokładnej informacji o przeglądarce. Na
przykład, niektóre wersje Internet Explorera zawierały następujące ciągi user agent:
• Microsoft Internet Explorer/4.40.305beta (Windows 95)[en]
• Mozilla/2.0 (compatible; MSIE 3.02; Update a; AOL 3.0; Windows 95)[en]
• Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)[en]
Różnice w zawartości tego ciągu powodują, że sprawdzanie ich zawartości staje się nieporęczne. Istnieje
tak wiele kombinacji przeglądarek, wersji, platform i języków, że dokładne rozpoznanie choć jednego
przeglądarki staje się problematyczne.
W rzeczywistości znajomość jedynie typu przeglądarki nie jest tak ważne. Zamiast tego lepiej wiedzieć,
czy przeglądarka obsługuje określone możliwości, na przykład JavaScript lub ramki. Te informacje nie są zawarte
w ciągu informacji o przeglądarce. Następna część omawia rozwiązanie tego problemu w oparciu o PHP.

Wewnętrzne funkcje PHP
Rozpoznawanie typu serwera ma zwykle służyć do poznania możliwości przeglądarki. Z powodu
ogromnej ilości kombinacji przeglądarek i platform, trudno jest stworzyć ogólne rozwiązanie tego problemu. Na
szczęście PHP zawiera kilka metod dokładniejszego rozpoznania serwera za pomocą funkcji get_browser().

Aby użyć
funkcji
Dodatkowe informacje na temat Browscap get_browser()
należy ściągnąć z
W czasie pisania książki podejmowane były dodatkowe ulepszenia do istniejącej w sieci plik
PHP funkcji get_browser(). Odkryłem, że plik browser.ini dostępny z firmy cyScape browscap.ini,
(wspomnianej już wcześniej) wymaga kilku zmian aby działał z PHP 4.0 z poprawką 2. który jest
Pierwszą z nich jest dodanie pustej linii na końcu pliku. Bez dodatkowego znaku końca linii dostępny z wielu
w trakcie uruchamiania PHP generowany jest błąd składni. witryn w Sieci.
Drugim, o wiele bardziej skomplikowany problem jest związany z samą struktura W czasie pisania
pliku. Typowo, plik browscap.ini jest zwykłym plikiem konfiguracyjnym w którym każda książki plik był
sekcja reprezentuje określoną przeglądarkę. Każda z sekcji odwołuje się do sekcji wyższego dostępny bez
rzędu, więc zdefiniowana jest pewnego rodzaju struktura. Tym sposobem w sekcji opisującej żadnych opłat z
nową przeglądarkę zdefiniowane są tylko nowe możliwości a istniejące wcześniej znajdują witryny
cię w opisie starszej przeglądarki. Problem wynika z tego, że w czasie odczytu pliku PHP http://www.cysca
konwertuje jego zawartość na małe litery. Konwersja ta powoduje, że PHP nie potrafi pe.com/browscap
odszukać nadrzędnej sekcji, ponieważ nie wszystkie nazwy zapisane są małymi literami. Aby . Po jego
rozwiązać ten problem wykonałem prosty skrypt konwertujący wszystkie linie pliku ściągnięciu i
browscap.ini na małe litery: zainstalowaniu w
<?php
$aArray = file( "./browscap.ini" ); serwerze WWW,
$aNewFile = fopen( "./browscap.ini", "w" ); należy zmienić
foreach( $aArray as $aLine )
{ plik php.ini.
$aNewFile = strtolower( $aLine );
fputs( $aNewFile, $aNewLine );
Odszukaj opcję
} konfiguracji
fclose ($aNewFile ); browscap i zmień
?>
Skutkiem ubocznym takiej zmiany jest to, że wszystkie wywołania funkcji ją na pełną
get_browser() muszą zawierać wywołanie funkcji strtolower(). Jednak po wprowadzeniu
ścieżkę do pliku
takich zmian funkcja get_browser() działa tak, jak się tego spodziewamy. browscap.ini. Po
ponownym
uruchomieniu serwera WWW będzie można korzystać z danych zawartych w pliku. W czasie pisania książki plik
browscap.ini zawierał ponad 2100 różnych przeglądarek.
Wykorzystanie funkcji get_browser() wraz z plikiem browscap.ini upraszcza proces rozpoznawania
możliwości przeglądarki i rozszerza jego zakres. Na wydruku 9.2 pokazany jest przykład użycia funkcji
get_browser(). Tak jak opisane zostało we wskazówce „Dodatkowe informacje na temat Browscap” pierwszym
krokiem powinno być przekonwertowanie zawartości $HTTP_USER_AGENT na małe litery. Następnie, przy użyciu
metody udokumentowanej w http://www.php.net/, ciąg identyfikacyjny przeglądarki jest zmieniany do właściwej

Rozdział 9 – Niezależność od przeglądarki 106
postaci przed wywołaniem funkcji get_browser(). Do zamiany określenia języka (na przykład [en]) na gwiazdkę,
która występuje w pliku browscap.ini, używana jest funkcja eregi_replace().
Wydruk 9.2. Użycie funkcji get_browser()
<?php
function GetMassagedUA()
{
global $HTTP_USER_AGENT;
$aUserAgent = strtolower( $HTTP_USER_AGENT );
$aUserAgent = eregi_replace( "\[[a-z]{2,}\]", "*", $aUserAgent );
if ( strpos( $aUserAgent, '*' ) === False )
{
$aUserAgent .= '*';
}
return $aUserAgent;
}
?>
<html>
<head>
<title>Możliwości przeglądarki</title>
</head>
<body>
<h1>Możliwości przeglądarki</h1>
<?php
$aUserAgent = GetMassagedUA();
print( "<h2>$aUserAgent</h2>" );
$array = (array) get_browser( $aUserAgent );
if ( count( $array ) > 1 )
{
while ( list( $key, $value ) = each ($array) )
{
$aValue = stripslashes( $value );
echo ("<b>$key=</b> $aValue<br>");
}
}
else
{
print( "<i>brak danych przeglądarki</i><br>" );
}
?>
</body>
</html>

Na rysunku 9.2. w przeglądarce Netscape 4.7.

107 PHP – Kompendium wiedzy
Rysunek 9.2. Wynik
działania skryptu z
wydruku 9.1
wykonywanego w
przeglądarce
Netscape 4.7

Lista dostępnych możliwości pokazuje potęgę funkcji get_browser(). W kodzie zamieszczonym na
wydruku 9.2, wszystkie możliwości są wyświetlane poprzez rzutowanie zwracanego obiektu na tablicę i
przeglądanie kolejnych par klucz-wartość. Można również sprawdzić kolejno każdą z możliwości korzystając
bezpośrednio z obiektu i używając zapisu $obiekt->możliwość. Na wydruku 9.3 pokazujemy przykład
sprawdzenia, czy przeglądarka obsługuje ramki.
Wydruk 9.3. Użycie get_browser() do sprawdzenia obsługi ramek
<?php
function GetMassagedUA()
{
global $HTTP_USER_AGENT;
$aUserAgent = strtolower( $HTTP_USER_AGENT );
$aUserAgent = eregi_replace( "\[[a-z]{2,}\]", "*", $aUserAgent );
if ( strpos( $aUserAgent, '*' ) === False )
{
$aUserAgent .= '*';
}
return $aUserAgent;
}
?>
<html>
<head>
<title>Obsługa ramek?</title>
</head>
<body>
<?php
$aUserAgent = GetMassagedUA();
print( "<h2>$aUserAgent</h2>" );
$aBrowsCap = get_browser( $aUserAgent );
if ( $aBrowsCap->frames == 1 )
{
print( "Przeglądarka obsługuje ramki<br>" );
}
else
{
print( "Przeglądarka nie obsługuje ramek<br>" );

Rozdział 9 – Niezależność od przeglądarki 108
}
?>
</body>
</html>
Funkcja get_browser() dostarcza dużo informacji i jest dobra na początek, gdy aplikacja wymaga
rozpoznania typu przeglądarki w czasie pracy. Jednak posiada ona kilka znaczących ograniczeń. Jak
wspomniałem wcześniej, użycie pliku browscap.ini, dostępnego w Internecie, również niesie ze sobą kłopoty.
Pewność działania funkcji wymaga, aby plik browscap.ini był często uaktualniany, aby podążać za zmianami w
najnowszych dostępnych przeglądarkach. Na przykład nasz plik browscap.ini nie pozwalał na prawidłowe
rozpoznanie przeglądarek Internet Explorer 5.5 i Opera 4.02. Problem tkwi w tym, że uaktualnienie i rozesłanie
pliku browscap.ini wymaga sporo czasu. Jeżeli twoja aplikacja wymaga dokładnego wykrywania możliwości
bieżącej przeglądarki, w następnej części opisane zostanie narzędzie jednej z firm, które świetnie spełnia swoje
zadanie.

BrowserHawk
Komponent BrowserHawk®, dostępny z firmy cyScape jest świetnym narzędziem do wykrywania
możliwości serwera. Jest on dostępny w formie obiektu COM, jeżeli wykorzystujesz serwer z Windows, lub jako
Java bean dla innych platform. Obsługa języka Java jest dostępna w PHP4, ale nie jest domyślnie włączona. Aby
wykorzystać Java bean, lub inny kod Java na serwerze, PHP musi być przekompilowane z opcją konfiguracji --
with-java. Niezbędne jest również zainstalowanie maszyny wirtualnej Java (JVM) na serwerze. Wiele dystrybucji
Linuksa zawiera JVM i w wielu przypadkach pakiet ten jest instalowany automatycznie.
Dodatkowo komponent BrowserHawk wymaga kilku dodatkowych modułów. Jednym z nich jest Simple
API for XML (SAX) dostępny pod adresem http://www.megginson.com/SAX/. Wymagane moduły servletów
można znaleźć we wielu miejscach, w tym w najnowszych JVM pochodzących od różnych dostawców. Potrzebny
będzie na przykład dostęp do klasy javax.servlet.http.HttpServlet.
Po ściągnięciu i zainstalowaniu wymaganych klas Javy, należy skonfigurować PHP, aby mógł skorzystać
z BrowserHawk. Taj jak w przypadku każdej innej klasy Java należy podać położenie pliku z klasami Javy
korzystając ze zmiennej java.class.path w pliku php.ini. Poniżej znajduje się przykład:
java.class.path=/usr/share/kaffe/Klasses.jar:/homeblake/php4.0.1pl2/ext/java/php_java.jar:/home/blake/bhawk/
lib/bhawk4j.jar:/home/blake/java/sax2.jar:/home/blake/java/servlet.jar:/home/blake/bhawk:
Zapis taki wskazuje, że klasy wymagane przez BrowserHawk są dostępne dla PHP w katalogach
../bhawk4j.jar, ../sax2.jar i ../servlet.jar. Wpisana jest tutaj również ścieżka bez określenia pliku
(/home/blake/bhawk), która określa położenie plików licencyjnych i danych pakietu BrowserHawk. Ścieżka ta
powinna wskazywać na katalog, w którym został zainstalowany BrowserHawk.
Po ustawieniu wszystkich tych elementów konfiguracji i przekompilowaniu PHP z obsługą Javy, należy
sprawdzić konfigurację za pomocą funkcji phpinfo(). Wywołanie funkcji phpinfo() powoduje wyświetlenie
dużej ilości danych w postaci tabel HTML. Na wydruku 9.3. pokazana jest ta część informacji, która wskazuje na
dostępność Javy.
Rysunek 9.3.
Wynik działania
funkcji phpinfo()
pokazujący
dostępność Javy

109 PHP – Kompendium wiedzy
Po skonfigurowaniu obsługi Javy i BrowserHawk w PHP, wykorzystanie komponentów BrowserHawk
jest łatwe. Na wydruku 9.4 znajduje się kod, który pokazuje w jaki sposób stworzyć obiekt BrowserHawk i
wykorzystać go do odczytania kilku podstawowych danych o przeglądarce.
Wydruk 9.4. Przykład wykorzystania BrowserHawk
<html>
<head>
<title>Przykład wykorzystania BrowserHawk</title>
</head>
<body>
<h1>Przykład wykorzystania BrowserHawk</h1>
<?php
print( "<h2>$HTTP_USER_AGENT</h2>" );
$aBrowserHawk = new Java( "com.cyscape.browserhawk.BrowserHawk" );
$aBrowserInfo = $aBrowserHawk->getBrowserInfo( "$HTTP_USER_AGENT" );
// Czy przeglądarka obsługuje ActiveX?
if ( $aBrowserInfo->getActiveXControls() == True )
{
print( "Przeglądarka obsługuje kontrolki ActiveX<br>" );
}
else
{
print( "Przeglądarka nie obsługuje kontrolek ActiveX<br>" );
}
?>
</body>
</html>
Na wydruku 9.4 pokazujemy w jaki sposób można sprawdzić obsługę ActiveX i tak samo łatwo można
sprawdzić każdą z właściwości BrowserHawk. W tabeli 9.1. zamieszczone są metody odczytujące informacje na
temat możliwości przeglądarki dostępne w BrowserHawk.
Typ Metoda Zastosowanie
Boolean getActiveXControls() Sprawdza, czy przeglądarka obsługuje
kontrolki ActiveX.
Boolean getAOL() Sprawdza, czy użytkownik witryny
korzysta z przeglądarki firmowanej przez
America Online (AOL) (na sieci AOL).
double getAOLVersion() Zwraca numer wersji przeglądarki
AOL.
int getAuthenticodeUpdate() Zwraca numer wersji Authenticode
obsługiwanego przez przeglądarkę.
Boolean getBackgroundSounds() Sprawdza, czy przeglądarka potrafi
odgrywać dźwięk w tle.
Boolean getBeta() Sprawdza, czy przeglądarka jest w
wersji beta.
java.lang.String getBrowser() Zwraca ogólną nazwę przeglądarki, na
przykład Netscape lub IE (Internet Explorer).
Boolean getCDF() Sprawdza, czy przegladarka obsługuje
Channel Definition Format (CDF) używany do
prenumerowania zawartości WWW z
możliwością automatycznej aktualizacji.
Boolean getCompressGZip() Sprawdza, czy przeglądarka przyjmuje
dane w skompresowanym formacie GZip.
Boolean getCookies() Sprawdza, czy przeglądarka obsługuje
cookie.
Boolean getCrawler() Sprawdza, czy przeglądarka jest
szperaczem sieciowym lub innym programem
wykorzystywanym do indeksowania
zawartości witryny.
Boolean getDHML() Sprawdza, czy przeglądarka obsługuje
skrypty DHTML().
java.lang.String getFileUpload() Sprawdza, czy przeglądarka obsługuje

Rozdział 9 – Niezależność od przeglądarki 110
możliwość przesyłania plików do serwera
(przeglądarki zgodne z RFC 1867).
Boolean getFontColor() Sprawdza, czy przeglądarka potrafi
wyświetlać kolorowy tekst.
Boolean getFontSize() Sprawdza, czy przeglądarka potrafi
wyświetlać różne wielkości tekstu.
Boolean getFrames() Sprawdza, czy przeglądarka obsługuje
ramki.
java.lang.String getFullversion() Zwraca kompletną wersje przeglądarki
zawierającą wyższą i niższą część numeru oraz
litery, o ile występują.
Boolean getGold() Sprawdza, czy jest to wersja Gold
przeglądarki Netscape Navigator.
Boolean getHDML() Zwraca True, jeżeli obsługuje HDML
(poprzednik WAP).
java.lang.String getIPAddr() Zwraca adres IP klienta.
Boolean getJavaApplets() Sprawdza, czy przeglądarka obsługuje
applety Java.
Boolean getJavaScript() Sprawdza, czy przeglądarka obsługuje
JavaScript.
double getJavaScriptVer() Zwraca numer wersji JavaScript
obsługiwanego przez przeglądarkę.
java.lang.String getLanguage() Zwraca wybrany przez użytkownika
język.
int getMajorver() Zwraca wyższą część numeru wersji
przeglądarki.
double getMinorver() Zwraca niższą część numeru wersji
przeglądarki.
java.lang.String getMinorverlet() Zwraca literę niższej części numeru
przeglądarki, o ile występuje.
Boolean getMouseOver() Sprawdza, czy przeglądarka obsługuje
efekt JavaScript, mouseover.
Boolean getMSN() Sprawdza, czy użytkownik korzysta z
sieci Microsoft Network (MSN).
java.lang.String getOSDetails() Zwraca szczegóły na temat systemu
operacyjnego (OS) systemu użytkownika.
Boolean getPDA() Zwraca True, jeżeli przeglądarką jest
urządzeniem PDA na przykład PalmPilot.
java.lang.String getPlatform() Zwraca bardziej ogólne dane (w
porównaniu do getOSDetails()) na temat
platformy użytkownika.
Boolean getPNG() Sprawdza, czy przeglądarka obsługuje
format rysunków PNG (Potrable Network
Graphics).
Boolean getProxy() Sprawdza, czy użytkownik jest
połączony poprzez serwer Proxy.
Boolean getSSL() Sprawdza, czy przeglądarka obsługuje
protokół SSL (Secure Socket Layer).
Boolean getSSLActive() Sprawdza, czy użytkownik jest
połączony poprzez aktywne połączenie SSL.
java.lang.String getSSLCipherSuite() Zwraca zestaw szyfrowania SSL dla
bieżącej sesji. Dostępny jedynie w przypadku

111 PHP – Kompendium wiedzy
aktywnej sesji SSL.
int getSSLKeySize() Sprawdza wielkość klucza SSL
obsługiwaną przez przeglądarkę. Dostępne
jedynie w przypadku aktywnego połączenia
SSL.
Boolean getStyleSheets() Sprawdza, czy przeglądarka obsługuje
kaskadowe arkusze stylu (CSS).
Boolean getTableBGColor() Sprawdza, czy przeglądarka obsługuje
ustawianie kolorów dla poszczególnych
komórek tabeli HTML.
Boolean getTableBGImage() Sprawdza, czy przeglądarka obsługuje
ustawianie rysunków tła dla poszczególnych
komórek tabeli HTML.
Boolean getTables() Sprawdza, czy przeglądarka obsługuje
wyświetlanie tabel.
Boolean getVBScript() Sprawdza, czy przeglądarka obsługuje
VBScript.
double getVersion() Zwraca wersję przeglądarki.
int getVersionpos() Zwraca pozycję w numerze wersji
przeglądarki, który jest umieszczony w ciągu
identyfikacyjnym przeglądarki.
Boolean getWAP() Zwraca True dla urządzeń
obsługujących WML i WAP (Wireles
Application Protocol), na przykład telefony
komórkowe z WAP.
java.lang.String getWAPDeviceModel() Zwraca model urządzenia WAP, o ile
jest znany.
java.lang.String getWAPGateway() Zwraca szczegóły bramy UP.Link, o ile
jest wykorzystywana.
int getWAPMaxDeckSize() Zawiera przybliżoną maksymalna ilość
bajtów, jaką może obsłużyć urządzenie.
java.lang.String getWAPSubscriberID() Automatycznie ustawiany na
identyfikator abonenta dla użytkownika WAP.
Boolean getWin16() Sprawdza, czy przeglądarka pracuje w
16 bitowym systemie operacyjnym Windows,
jak na przykład Windows 3.1.
Boolean getXML() Sprawdza, czy przeglądarka obsługuje
bezpośrednie wyświetlanie plików XML.
Jedyną wadą przy używaniu komponentu BrowserHawk jest to, że jest on zaprojektowany dla
użytkowników JSP, więc niektóre metody mogą nie być bezpośrednio dostępne poprzez PHP. Niektóre
zaawansowane funkcje raportujące nie mogą być wykorzystane, ponieważ opierają się na obiektach
specyficznych dla JSP. Mimo to, standardowe obiekty zwracają wystarczająco dużo danych dla większości
zastosowań i są stale aktualne dla najnowszych przeglądarek.
Przewagą użycia komponentu BrowserHawk nad innymi metodami opisanymi w tym rozdziale jest jego
dokładność i elastyczność. BrowserHawk uaktualnia swoją bazę danych w razie potrzeby. W dokumentacji
znajduje się informacja, że rozpoznaje on około 9 razy więcej przeglądarek, niż można to zrobić korzystając z
browscap. BrowserHawk jest również zaprojektowany, aby sprawdzał o wiele więcej własności przeglądarki, niż
jest to stosowane w innych metodach.
Jeżeli aplikacja opiera się na dostarczaniu danych specyficznych dla przeglądarki lub polega na bardzo
specyficznych własnościach przeglądarki, BrowserHawk zapewnia najlepsze rozpoznawanie przeglądarki.
Aplikacja będzie nadal działała prawidłowo, niezależnie od ciągłych zmian w technologiach przeglądarek.

Rozdział 9 – Niezależność od przeglądarki 112
Wykorzystanie danych o przeglądarce
Pierwszym zadaniem podczas tworzenia aplikacji niezależnej od przeglądarki jest rozpoznanie możliwości
przeglądarki użytkownika. O wiele ważniejszym krokiem jest zadecydowanie w jaki sposób zostaną
wykorzystane te dane. Tak jak w przypadku innych decyzji podejmowanych w czasie projektowania, zależy ona
od wymagań stawianych aplikacji. Niektóre możliwości przeglądarki i własności aplikacji, takie jak animowane
podpowiedzi, lub obsługa kaskadowych arkuszy stylów nie są krytyczne. Brak innych własności może całkowicie
zatrzymać aplikację, na przykład zdolność przeglądarki do nawiązania połączenia szyfrowanego SSL lub obsługa
wysyłania plików.
Projekt aplikacji powinien zawierać listę wymaganych własności przeglądarki i zapewniać elegancką
obsługę sytuacji, gdy nie można skorzystać z którejś z wymaganych własności. Na wydruku 9.5. zamieszczony
został przykład w jaki sposób można zrealizować elegancką obsługę braku wymaganej własności przeglądarki.
Dodatkowo, można spróbować warunkowo dostarczać niektórych elementów w zależności od zdolności
przeglądarki do ich wyświetlania. Na wydruku 9.6 pokazano przykład takiego działania.
Wydruk 9.5. Eleganckie zakończenie aplikacji w przypadku braku obsługi przesyłania plików
<?php
$aBrowserHawk = new Java( "com.cyscape.browserhawk.BrowserHawk" );
$aBrowserInfo = $aBrowserHawk->getBrowserInfo( "$HTTP_USER_AGENT" );
?>
<html>
<head>
<title>Wysyłanie pliku</title>
</head>
<body>
<h1>Wysyłanie pliku</h1>
<form action="someurl.phtml" method="post" enctype="multipart/form-data">
<?php
if ( $aBrowserInfo->getFileUpload() == True ) {
?>
<input type="file" name="File"><br><br>
<input type="submit" name="Submit" value="Wyślij">
<?php
} else {
?>
Przeglądarka nie obsługuje wysyłania plików.<br><br>
proszę przesłać pliki pocztą na adres files@my.domain.com.
<?php
}
?>
</form>
</body>
</html>
Jeżeli przeglądarka posiada obsługę wysyłania plików, skrypt ten wyświetla formularz wysyłania pliku.
Jeżeli przeglądarka nie obsługuje tej funkcji, wyświetlany jest napis informujący użytkownika o możliwości
przesłania pliku za pomocą poczty elektronicznej. W rzeczywistości mechanizm taki jest niezbędny, aby
użytkownicy aplikacji uważali ją za przyjazną. Wybierając taki mechanizm należy zwrócić uwagę, aby
użytkownicy mogli zrozumieć dlaczego wykonanie operacji się nie powiodło. Większość ludzi nie chce widzieć
komunikatów typu „Twoja przeglądarka nie obsługuje RFC 1867”. Jeżeli aplikacja może działać pomimo tego, że
operacja się nie udała, nie wyświetlaj ponownie tego komunikatu. Należy po prostu zapewnić możliwie
największą dostępną ilość funkcji.
Wydruk 9.6. Warunkowe dostarczanie treści w zależności od możliwości przeglądarki
<?php
$aBrowserHawk = new Java( "com.cyscape.browserhawk.BrowserHawk" );
$aBrowserInfo = $aBrowserHawk->getBrowserInfo( "$HTTP_USER_AGENT" );
if ( $aBrowserInfo->getPNG() == True )
{
$aImage = "Logo.png";
}
else
{
$aImage = "Logo.gif";
}
?>
<html>
<head>
<title>Nasze logo</title>
</head>
<body>
<h1>Nasze logo</h1>

113 PHP – Kompendium wiedzy
<img src="<?=$aImage?>" width="180" height="70" alt="" border="0">
</body>
</html>
Skrypt ten wyświetla grafikę w formacie PNG jeżeli przeglądarka potrafi wyświetlić ten format, w
przeciwnym wypadku wysyłany jest rysunek w formacie GIF. Przykład ten jest prosty, ale ilustruje podstawową
zasadę działania. Zamiast wykorzystywać zmienne do wysyłania różnych danych, aplikacja może skorzystać z
informacji o możliwościach przeglądarki do wyświetlenia całkowicie innej sekcji witryny. Na przykład można
stworzyć witrynę zoptymalizowaną dla oglądania jej przez przeglądarki WebTV. Systemy takie mają zwykle
ograniczoną wielkość ekranu i zwykle mniej możliwości wyświetlania różnych czcionek. Dlatego trzeba inaczej
projektować taką witrynę aby poprawić widoczność wszystkich elementów. Poniższy kod jest prostym
przykładem sposobu implementacji takiego przypadku. Zakładamy, że jest to główna strona witryny.
<?php
$aBrowserHawk = new Java( "com.cyscape.browserhawk.BrowserHawk" );
$aBrowserInfo = $aBrowserHawk->getBrowserInfo( "$HTTP_USER_AGENT" );
if ( $aBrowserInfo->getBrowser() == "WebTV" )
{
// Przeglądarka WebTV, przekierowanie do stron zoptymalizowanych dla WebTV
header( "Location: http://mysite.com/webtv/\n" );
}
else
{
// To nie jest przeglądarka WebTV, przekierowanie do standardowych stron
header( "Location: http://mysite.com/main/\n" );
}
?>
W przykładzie tym, użytkownicy przeglądarek WebTV są kierowani na odpowiednio zoptymalizowane
strony. Inni użytkownicy są kierowani do zwykłego zestawu stron przeznaczonych dla innych typów
przeglądarek. Przykład ten może być rozszerzony, aby wykrywał przeglądarki działające na komputerach typu
PDA lub inne specyficzne typy przeglądarek.
Wadą takiego rozwiązania jest to, że jeżeli użytkownik wyśle znajomemu łącze do strony przeznaczonej
dla innej przeglądarki niż używa ten znajomy, wygląd strony nie będzie odpowiedni dla bieżącego typu
przeglądarki. Dodatkowo mechanizm ten wymaga, aby każda strona posiadała kilka równoległych stron
przeznaczonych dla odpowiednich typów przeglądarek. Wprowadzenie takiego projektu jest nieporęczne dla
dużych witryn. Lepszym rozwiązaniem przy tworzeniu stron specyficznych dla przeglądarki jest wyłączenie
kluczowych różniących się elementów i umieszczenie ich w osobnych plikach dla każdego typu przeglądarki.
Tego typu mechanizm może być zaimplementowany przy użyciu systemu szablonów, które będą opisane w
rozdziałach 13 i 14. Tam też przytoczymy przykłady implementacji takiego scenariusza.

Podsumowanie
Wykrywanie możliwości przeglądarki może być niezmiernie ważne dla wielu aplikacji WWW. W czasie
projektowania aplikacji należy poznać ograniczenia różnych przeglądarek, zanim zatwierdzimy realizację
specyficznych funkcji. Następnie, w oparciu o wymagania projektu należy wykorzystać narzędzia do wykrywania
przeglądarki i włączania niektórych funkcji. Należy unikać sytuacji, gdy przeglądarka wyświetla niezrozumiały
komunikat błędu w przypadku, gdy funkcja jest niedostępna. Należy dążyć do zapewnienia zestawu funkcji
niezależnych od używanego typu przeglądarki.

Rozdział 9 – Niezależność od przeglądarki 114
Rozdział 10. Uruchamianie
Wstęp
Uruchamianie aplikacji WWW jest równie krytycznym procesem jak uruchamianie innych typów
aplikacji. Problemem jest to, że może być trudno zdalnie uruchamiać program, szczególnie gdy nie masz
odpowiednich uprawnień do administracji serwerem WWW. W tym rozdziale zaprezentowane zostaną porady i
narzędzia, które mogą usprawnić uruchamianie aplikacji. Dodatkowo, niektóre części rozdziału są
przypomnieniem zasad inżynierii programowania, ponieważ można uniknąć ogromnej pracy przy uruchamianiu
aplikacji, jeżeli jej projekt jest odpowiednio przygotowany.
Z powodu ogromnego zainteresowania jakie wzbudziło programowanie aplikacji WWW, powstało wiele
narzędzi (między innymi PHP) do tworzenia takich aplikacji. Narzędzia te zostały stworzone na podstawie
istniejących już narzędzi, na przykład ASP i JSP, inne natomiast zostały stworzone jedynie w celu tworzenia
interaktywnych aplikacji WWW. Niespodziewanie, narzędzia spełniające zasady inżynierii programowania są tu
w mniejszości. Prawdopodobnie brak ten jest spowodowany potrzebą zaistnienia na rynku jako pierwsze
narzędzie lub z faktu, że pierwszymi projektantami nowej technologii nie byli bardziej zaawansowani
programiści. Niezależnie od powodu, do tego nowego środowiska programowania należy zaadaptować wszystkie
istniejące zasady inżynierii programowania. Pierwsza część rozdziału zawiera przypomnienie zasad inżynierii
programowania. Jest ona dołączona do tego rozdziału, ponieważ zbyt dużo czasu spędzonego przy uruchamianiu
jest zwykle powodowane błędami przy tworzeniu projektu.

Inżynieria programowania a uruchamianie
W rozdziale 3 „Formularze i cookie” doszliśmy do wniosku, że można uniknąć sprawdzania poprawności
niektórych danych w przypadku zastosowania lepszego mechanizmu wprowadzania danych. Tak samo, pisanie
lepszego (bardziej defensywnego) kodu powoduje ogromne zmniejszenie czasu straconego w czasie
uruchamiania.
Projekt aplikacji WWW musi być tak samo dokładnie przemyślany, jak projekt każdej innej aplikacji.
Tworzenie świetnej aplikacji wymaga właściwego projektu, zgodności z standardami tworzenia oprogramowania,
sprawdzania oprogramowania, testowania modułów oraz uruchamiania. Uruchamianie jest koniecznie ostatnie na
tej liście, ponieważ zakłada się, że kod przeszedł wszystkie wcześniejsze wymagane kroki. Kolejne części
zawierają przegląd każdego z tych kroków i zakładają, że posiadasz pewną wiedzę inżynierii programowania.

Projekt aplikacji
Identyfikacja wstępnych założeń aplikacji przed napisaniem jakiegokolwiek kodu jest krytyczna w
przypadku każdego projektu. W dużych projektach takie planowanie może zająć tygodnie lub miesiące. W
małych projektach mogą zostać zapisane na skrawku papieru w przeciągu kilku minut. W każdym z tych
przypadków kluczowe jest przemyślenie wymagań aplikacji, oraz wymyślenie możliwie wielu możliwych
rozwiązań przed rozpoczęciem pisania kodu. Dane z firm TRW i IBM wskazują, że wprowadzenie zmian do
aplikacji w początkowym okresie programowania (przed fazą programowania) jest 10 do 200 razy tańsze niż
wprowadzanie tych samych zmian na końcu tego procesu (McConnell, 1993).
W zależności od projektu, identyfikacja wymaganych funkcji aplikacji może wymagać wykonania sporej
pracy. Wstępny podział aplikacji na moduły może uprościć ten proces. W aplikacji WWW modułami takimi
mogą być współpraca z bazą danych, autoryzacja użytkownika, obsługa stanu, id. Po zdefiniowaniu zadań
poszczególnych modułów należy w razie potrzeby podzielić je na mniejsze fragmenty i zapisać przeznaczenie
każdego fragmentu. W małych aplikacjach dobrą strategią jest podział modułów na pliki kodu bądź klasy
obiektowe.
Oprócz zdefiniowania wymagań funkcjonalnych aplikacji, należy rozważyć architekturę systemu. Należy
tu pomyśleć o rodzaju stosowanego systemu zarządzania relacyjną bazą danych (SZRBD) i innych mniej
oczywistych elementach, na przykład jak będą zorganizowane pliki kodu, jak radzić sobie ze zmianami, oraz czy
niektóre moduły należy zakupić, czy też pisać je od początku. Mimo, że PHP może działać na wielu serwerach
WWW i platformach systemowych, każda z takich kombinacji posiada indywidualne cechy. Należy poświęcić
nieco czasu na zdefiniowanie powodów, dla których należy wybrać określony sprzęt i serwer. Ilość funduszy
dostępnych na początku projektu rzadko jest dobrym powodem wyboru platformy. Wybór bazy danych jest
równie istotny, jeżeli aplikacja ma być wysoce dynamiczna. Dodatkowo należy pomyśleć, czy serwer WWW i
baza danych będą pracować na tym samym komputerze, czy osobno. W zależności od wielkości i charakteru
aplikacji, może być to krytyczne zagadnienie.
Następnie, należy zaprojektować organizację kodu. Zdefiniuj konwencję nazywania plików i katalogów,
co uprości identyfikację kodu. Wymyśl alternatywny plan na wypadek, gdy istnieje duże prawdopodobieństwo
zmian. Jeżeli przewidujesz występowanie zmian, napisz aplikację lokalizującą zmiany w kilku modułach a
buforującą resztę. Taki typ projektowania jest istotny szczególnie, gdy korzystasz przy tworzeniu aplikacji z
narzędzi pochodzących z innych źródeł lub oprogramowania w wersji beta. Tworzenie zastępników takiego kodu
jest łatwe do zaimplementowania i w dłuższym okresie czasu umożliwia łatwiejsze utrzymanie aplikacji.
Na koniec należy zadecydować, które moduły aplikacji zostaną stworzone przy pomocy gotowych
narzędzi pochodzących od zewnętrznych dostawców. Decyzja „tworzyć czy kupić” jest dosyć skomplikowana. W
zależności od harmonogramu projektu, możesz nie i mieć wystarczająco dużo czasu, aby dostatecznie
przetestować dostępne narzędzia. Jednak możesz również nie mieć wystarczająco dużo czasu na stworzenie ich
od początku. Aby zmniejszyć wpływ tej decyzji na projekt można stworzyć własne funkcje pośrednie ukrywające
implementację.
Właściwe zaprojektowanie aplikacji wymaga czasu. W przypadku dobrego systemu faza projektowania
zajmuje 20 do 30 procent czasu tworzenia systemu (McConnell, 1993). Trzeba pamiętać, że ten czas jest
zużywany na projektowanie wysokiego poziomu, do projektowania szczegółów implementacji również potrzebny
jest czas.

Definiowanie standardów programowania
Zdefiniowanie standardów programowania ułatwia długoterminowe utrzymanie projektów o dowolnej
wielkości. Nawet małe aplikacje programowane przez jednego programistę mogą korzystać z odpowiednio
stosowanego zbioru standardów programowania. Taki standard obejmuje sposoby nazywania, komentowania oraz
konwencje układu. Niektóre z nich, na przykład układ kodu, są mniej ważne, ponieważ nowoczesne edytory
potrafią przeformatować kod, jednak inne, jak na przykład konwencje nazw plików i katalogów mogą mieć
ogromne znaczenie przy konserwacji kodu2.

Przegląd oprogramowania
Przegląd oprogramowania dostarcza możliwości zrealizowania kilku celów za jednym razem. Na
przykład, przegląd oprogramowania pozwala na sprawdzenie kodu z standardami kodowania. Pozwalają również
mniej doświadczonym programistom na korzystanie z wiedzy bardziej doświadczonych kolegów. Są również
jednym z bardziej efektywnych metod zapewnienia odpowiedniej jakości oprogramowania. Analizy przeglądów
oprogramowania stosowanych przy tworzeniu prawdziwych aplikacji pokazały, że pozwalają na wykrywanie
błędów z prawdopodobieństwem pomiędzy 55 a 60%. Ten wynik należy zestawić z jedynie 25%
prawdopodobieństwem dla testowania modułów, 35% dla testowania funkcji oraz 45% dla testowania
integracyjnego. Dodatkowo, przeglądy takie zwiększają w dużych projektach ogólną wydajność zespołu. W

2
Przypis tłumacza. Z powodu luźnego traktowania typów w PHP szczególnie ważne wydaje się odpowiednie nazywanie zmiennych. Dobrym pomysłem
może być stosowanie tzw. notacji węgierskiej, gdzie nazwa zmiennej zaczyna się od liter określających jej typ, np.: nIlosc to zmienna przechowująca
liczby całkowite, sTytul zawiera ciąg znaków, a bIstnieje to zmienna logiczna.

Rozdział 10 – Uruchamianie 116
niektórych przypadkach powodują one 80 do 90% spadek awarii oraz 10 do 25% wzrostu wydajności
(McConnell, 1993). Przegląd powinien być przeprowadzany zarówno podczas testowania jak i podczas
implementacji. Przegląd projektu pozwala na identyfikację jego wad w momencie, gdy ich usuwanie jest
najprostsze i najtańsze.
Można wykorzystać kilka metod realizacji przeglądu. Niektóre z nich są formalną inspekcją kodu inne
przeglądem ogólnym lub czytaniem kodu. W przypadku formalnej inspekcji kodu, kilku członków zespołu zbiera
się razem w celu odszukania błędów. Prowadzący spotkanie prowadzi je do przodu i pilnuje, aby jedynym
tematem była identyfikacja błędów. Formalna inspekcja kodu nie powinna zawierać dyskusji na temat rozwiązań.
W przypadku przeglądu ogólnego grupa programistów nieformalnie dyskutuje na temat kodu zadając pytania.
Gdy zidentyfikowany zostanie błąd, mogą powstać sugestie na temat sposobu jego usunięcia, ale głównym celem
jest nadal jego identyfikacja. Czytanie kodu poświęcone jest jedynie dla kodu programu, a nie pracy grupowej.
Zwykle część kodu jest przekazywana dwóm lub więcej osobom, które niezależnie pracując identyfikują błędy.
Wynik ich pracy jest przekazywany autorowi kodu.
W zależności od rozmiaru i organizacji projektu użycie jednej z metod ma przewagę nad inną. Na
przykład w przypadku, gdy pracujesz sam lub w małym zespole, możesz skorzystać z zatrudnienia osoby, która
wykona taki przegląd metodą czytania kodu. Niezależnie od wybranej metody, przeprowadzenie przeglądu kodu
jest najbardziej efektywną metodą identyfikacji problemów w projekcie bądź implementacji.

Testowanie
Zwykle testowanie nie jest pomijane, ale często jest przeprowadzane przypadkowymi metodami. Tak jak
w przypadku innych projektów, testowanie aplikacji WWW powinno zawierać testowanie na różnych poziomach:
testowanie funkcji, testowanie modułów oraz testowanie integracyjne. Testowanie każdego modułu powinno być
odpowiednio zaplanowane. Z testami związany jest określony zbiór oczekiwań i wymagań.
W małych projektach testowanie może wymagać jedynie wymyślenia kilku prostych przypadków użycia
aplikacji a następnie sprawdzenie każdego przypadku. Większe projekty mogą zawierać ludzi, którzy zajmują się
jedynie testowaniem, ale każdy z programistów powinien być odpowiedzialny za dostarczenie testerom
dostatecznie dużo danych, aby ich praca była efektywna. Każdy twórca kodu powinien również być
odpowiedzialny za testowanie swoich modułów na poziomie funkcji lub strony.

Uruchamianie
Uruchamianie jest ostatnim etapem w procesie tworzenia aplikacji, ponieważ w momencie, gdy następuje
uruchamianie powinny być ukończone procesy projektowania, programowania i część testowania. Uruchamianie
może być przeprowadzane w trakcie każdej z fazie testowania jako część tego procesu. Wszystkie zmiany
wprowadzone do kodu w trakcie procesu uruchamiania powinny zostać ponownie przetestowane na wszystkich
poziomach testowania, ponieważ zmiany mogły wprowadzić nowe błędy.
Uruchamianie powinno być gruntownym procesem przeznaczonym do identyfikacji źródła problemu. Nie
powinno być prostym łataniem, które naprawi błędne przypadki, ale nie zapewni trwałego rozwiązania. Każdy,
kto bierze udział w testowaniu powinien znać źródło problemu w momencie, gdy stwierdza się usunięcie
problemu. Znalezienie źródła problemu powoduje stworzenie kompletnego rozwiązania zamiast obchodzenia
problemu. W zależności od natury problemu, czasami czasowe rozwiązania mogą być wystarczające, ale muszą
zostać odpowiednio udokumentowane. Rozwiązywanie problemów powinno brać pod uwagę priorytety.
W tej książce prawdopodobnie nie zostanie opisane wszystko na temat prawidłowej inżynierii
programowania. Najważniejsze jest, aby pamiętać, że inżynieria programowania jest równie ważna w aplikacjach
WWW jak w pozostałych aplikacjach oraz o tym, ze właściwe stosowanie zasad inżynierii może ograniczyć
nakład pracy wymagany przy uruchamianiu. Następna część zawiera opis kilku technik i narzędzi specyficznych
dla aplikacji PHP.

117 PHP – Kompendium wiedzy
Programowanie defensywne
Zanim zaczniesz uruchamiać program, powinieneś podjąć kroki, prowadzące do napisania kodu
zawierającego dużo mniej błędów. Taki sposób programowania jest nazywany programowaniem defensywnym.
Takie programowanie polega na odpowiednim komentowaniu błędów, wewnętrznym sprawdzaniu stanu procedur
w trakcie procesu programowania. Sposób komentowania jest indywidualny dla każdego programisty, ale
powinno być zgodne ze standardami. Jako minimum, programiści powinni opisywać przeznaczenie funkcji, klasy
lub dołączanego pliku oraz zawsze komentować niejasne fragmenty kodu.
Do sprawdzania stanu funkcji, PHP, tak jak wiele języków wysokiego poziomu, posiada funkcję assert().
Funkcja assert() oblicza wartość przekazanego parametru i podejmuje określone akcje w przypadku, gdy jego
wartość wynosi False. W PHP do funkcji assert() można przekazać zarówno ciąg jak i wartość Boolean. Jeżeli
przekazany został ciąg, jest on wykonywany jako blok kodu PHP. Opcje asercji w pliku php.ini (assert.active,
assert.warning, assert.bail, assert.callback i assert.quiet_eval) lub opcje przekazane jako parametr
wywołania funkcji assert_options() definiują akcję jaką podejmuje funkcja assert(). W tabeli 10.1
zamieszczone są różne opcje asercji.
Tabela 10.1. Opcje asercji i ich opis
Opcja Domyślnie Opis
assert_active 1 Włącza wykonywanie assert().
assert_warning 1 Wyświetla ostrzeżenie PHP przy
każdej nieudanej asercji.
assert_bail 0 Kończy wykonanie w przypadku
nieudanej asercji.
assert_quiet_eval 0 Wyłącza raportowanie błędów w
czasie obliczenia wyrażenia asercji.
assert_callback (null) Nazwa funkcji użytkownika
wykonywanej w przypadku nieudanej asercji.
Funkcja assert() jest zaprojektowana jedynie do wykorzystywania w czasie tworzenia programu i nie
powinna być używana w czasie normalnej pracy. Aplikacja powinna działać identycznie z wywołaniami funkcji
assert(), jak i bez nich. Na wydruku 10.1 pokazany został przykład użycia funkcji assert() do kontroli
poprawności parametrów wejściowych.
Wydruk 10.1. Użycie funkcji assert() do kontroli poprawności parametrów wejściowych
<?php
function ArrayAverage( $aArray )
{
$aTotal = 0;
// Apostrofy ozanczają ciąg nie interpretowany przez PHP
assert( 'is_array( $aArray )' );
foreach( $aArray as $aElement )
{
assert( 'is_numeric( $aElement )' );
$aTotal += $aElement;
}
return ( $aTotal / count( $aArray ) );
}
?>
Funkcja ArrayAverage() umieszczona na wydruku 10.1 spodziewa się jako parametru tablicy wartości
numerycznych (liczb lub ciągów zawierających liczby) i zwraca średnią z wartości w tablicy. Wyrażenie assert()
jest wykorzystywane do kontroli poprawności parametru przekazanego do funkcji a później do sprawdzania, czy
tablica zawiera wartości numeryczne. Należy zwrócić uwagę, że do assert() można przekazać ciąg, który jest
wykonywany jako kod PHP, więc jeżeli wykorzystywane są zmienne należy zapewnić, że PHP nie podstawi zbyt
wcześnie wartości zmiennej w miejsce jej nazwy. Aby tego uniknąć należy używać ciągów w apostrofach.
Testowy skrypt dla funkcji z wydruku 10.1 jest zamieszczony na wydruku 10.2.
Wydruk 10.2. Użycie funkcji ArrayAverage()
<?php
include( "./arrayfunc.php" );
?>
<html>
<head>
<title>Testowanie asercji</title>
</head>
<body>
Rozdział 10 – Uruchamianie 118
<?php
$aResult = ArrayAverage( array( 1, 2, 3, 4, 5 ) );
print( "ArrayAverage( array( 1, 2, 3, 4, 5 ) ) = $aResult<br>" );
$aResult = ArrayAverage( array( 10.1, "5.5", 3, "4", 5 ) );
print( "ArrayAverage( array( 10.1, \"5.5\", 3, \"4\", 5 ) ) = $aResult<br>" );
$aResult = ArrayAverage( array( 1, 2, "cat", 4, 5 ) );
print( "ArrayAverage( array( 1, 2, \"cat\", 4, 5 ) ) = $aResult<br>" );
$aResult = ArrayAverage( 1, 2 );
print( "ArrayAverage( 1, 2 ) = $aResult<br>" );
?>
</body>
</html>
Testowy skrypt wywołuje funkcję ArrayAverage() cztery razy. Pierwsze dwa razy przekazywane są
właściwe wartości, natomiast ostatnie dwa razy przekazane wartości są nieprawidłowe. Na rysunku 10.1.
pokazany jest wynik działania skryptu. Należy zauważyć, że ponieważ PHP wewnętrznie wymusza typy
zmiennych, powoduje to, że wywołanie ArrayAverage(array(1, 2, "cat", 4, 5)) udaje się bez żadnych
ostrzeżeń (poza generowanymi przez asercje).
Rysunek 10.1.
Testowanie funkcji
ArrayAverage()

Podejmowane przez funkcję assert() działania zależą od ustawień asercji. Poprzedni przykład
wyorzystywał domyślne ustawienia asercji. Przyjemną cechą asercji jest to, że gdy assert.active jest ustawione
na False, przestają one działać. Aby zmienić tą opcję należy albo wywołać funkcję assert_options(
ASSERT_ACTIVE, False) lub ustawić odpowiednio dyrektywę konfiguracji. Wykorzystując opcję konfiguracji
można instalować aplikację w środowisku z wyłączonymi asercjami a pracować na innym, gdzie asercje są
aktywne.
Istnieje jeszcze jedna funkcja pomagająca w programowaniu defensywnym, error_reporting(). Funkcja
ta jako argumentu wymaga liczby całkowitej określającej poziom raportowania błędów. Argument ten jest
traktowany jako maska bitowa, więc można podać zestaw kilku ustawień. PHP posada zestaw stałych używanych
razem z tą funkcją. Są one następujące:
Wartość Nazwa
1 E_ERROR
2 E_WARNING
4 E_PARSE
8 E_NOTICE
16 E_CORE_ERROR
32 E_CORE_WARNING
64 E_COMPILE_ERROR
128 E_COMPILE_WARNING
256 E_USER_ERROR
512 E_USER_WARNING

119 PHP – Kompendium wiedzy
1024 E_USER_NOTCE
Dodatkowo istnieje również stała E_ALL, która uaktywnia wszystkie informacje o błędach. Tworząc
aplikację powinno się przestawić poziom raportowania błędów na E_ALL, co spowoduje wyświetlanie wszystkich
informacji o błędach w kodzie. Ustawienie to jest również ważne w trakcie dołączania do aplikacji zewnętrznej
biblioteki. Na wydruku 10.3 zamieszczony jest przykład kodu generujący ostrzeżenia w przypadku ustawienia
maksymalnego poziomu raportowania błędów. Przy standardowych ustawieniach skrypt nie powoduje
wyświetlenia żadnego komunikatu.
Wydruk 10.3. Przykład użycia funkcji error_reporting()
<html>
<head>
<title>Poziomy raportowania błędów</title>
</head>
<body>
<?php
$aArray = array( "state" => "Idaho", "county" => "Madison",
"city" => "Rexburg", "country" => "US" );
// standardowy poziom raportowania błędów
print( "aArray[state] = " . $aArray[state] . "<br>" );
error_reporting( E_ALL );
print( "aArray[state] = " . $aArray[state] . "<br>" );
?>
</body>
</html>
Na rysunku 10.2. pokazane są wyniki działania tego przykładu. Jak można zauważyć, ustawienie bardziej
restrykcyjnego poziomu raportowania błędów może spowodować wykrycie błędów w kodzie, które mogą
spowodować różne efekty uboczne w czasie tworzenia aplikacji. W tym przypadku problem może wydawać się
niewinny, ale jeżeli zdefiniujemy stałą o nazwie state reprezentującą stan działania aplikacji, problem przestanie
być niewinny. Na wydruku 10.4 pokazujemy skrypt z taką właśnie stałą. Wyniki jego działania są widoczne na
rysunku 10.3.
Wydruk 10.4. Drugi przykład użycia funkcji error_reporting() oraz stałej
<?php
// Stan działania aplikacji
define( state, 3 );
// Pozostały kod używający stałej state
?>
<html>
<head>
<title>Poziomy raportowania błędów</title>
</head>
<body>
<?php
$aArray = array( "state" => "Idaho", "county" => "Madison",
"city" => "Rexburg", "country" => "US" );
// domyślny poziom raportowania
print( "aArray[state] = " . $aArray[state] . "<br>" );
error_reporting( E_ALL );
print( "aArray[state] = " . $aArray[state] . "<br>" );
?>
</body>
</html>
Rysunek 10.2.
Wynik działania
skryptu
error_reporting()

Rozdział 10 – Uruchamianie 120
Rysunek 10.3.
Wynik działania
drugiego skryptu
error_reporting()

W obu przykładach, gdy poziom raportowania błędów był ustawiony na standardowy poziom, nie były
generowane żadne ostrzeżenia i program wykonywał się nieomal bezbłędnie. Jednak po dodaniu w drugim
przykładzie stałej, świetnie działający nagle przestał działać. Problem taki może być bardzo trudny do
odszukania, jeżeli stała taka byłaby zdefiniowana w dołączanym pliku. Przykład ten pokazuje zalety zastosowania
ustawienia poziomu raportowania błędów na maksimum. Wszystkie wyświetlane ostrzeżenia powinny zostać
zlikwidowane, aby uniknąć występowania błędów w przyszłości.
W zależności od twojego środowiska, możesz ustawić poziom raportowania błędów na maksymalny w
trakcie rozwijania aplikacji i na minimalny na serwerze produkcyjnym. Takie ustawienie powoduje, że w
przeglądarkach użytkowników nie pojawiają się ostrzeżenia i komunikaty błędów. Jednak osobiście uważam, że
najlepiej ustawić na serwerze produkcyjnym poziom raportowania na maksimum, ale skierować strumień błędów
do pliku dziennika. Można to zrealizować ustawiając zmienne konfiguracji display_errors, log_errors i
error_log na odpowiednio Off, On i stderr. Powoduje to, że PHP nie wyświetla błędów w przeglądarce a zamiast
tego kieruje wszystkie błędy do pliku stderr. Jeżeli używasz Apache, plikiem stderr dla Apache jest dziennik
błędów. Jeżeli chcesz, możesz skorzystać z innej lokalizacji dziennika.
Gdy zostanie wykonany skrypt z wydruku 10.3 po skonfigurowaniu PGP w sposób wspomniany powyżej,
na ekranie nie pojawią się ostrzeżenia, a w pliku dziennika błędów Apache znajdą się następujące pozycje:
[06-Dec-2001 20:53:22] PHP Warning: Undefined offset: 3 in c:\helion\php4-
devguide\site\ch10\error_reporting_2.phtml on line 17
[06-Dec-2001 20:54:03] PHP Warning: Use of undefined constant state - assumed 'state' in c:\helion\php4-
devguide\site\ch10\error_reporting.phtml on line 12
[06-Dec-2001 20:54:45] PHP Warning: Use of undefined constant state - assumed 'state' in c:\helion\php4-
devguide\site\ch10\error_reporting.phtml on line 12
[06-Dec-2001 20:54:49] PHP Warning: Undefined offset: 3 in c:\helion\php4-
devguide\site\ch10\error_reporting_2.phtml on line 17
[06-Dec-2001 20:54:51] PHP Warning: Undefined offset: 3 in c:\helion\php4-
devguide\site\ch10\error_reporting_2.phtml on line 17
[06-Dec-2001 20:54:53] PHP Warning: Use of undefined constant state - assumed 'state' in c:\helion\php4-
devguide\site\ch10\error_reporting.phtml on line 12
Następnym krokiem podczas programowania defensywnego może być napisanie własnego mechanizmu
rejestrującego. W dowolnych miejscach aplikacji możesz sprawdzać stan niektórych funkcji lub raportować błędy
wewnętrzne i kontynuować pracę. PHP posiada funkcję error_log() przy pomocy której można dodawać własne
zapisy do pliku śladu aplikacji. Prototyp funkcji error_log() wygląda następująco:
int error_log(string komunikat, int typ [, string cel [, string naglowki]])
Pierwszy parametr, komunikat, zawiera zapisywane dane. Drugi z parametrów określa gdzie zostanie
zapisany komunikat. Lista prawidłowych wartości parametru typ znajduje się w tabeli 10.2.
Tabela 10.2. Wartości parametru typ
Wartość Opis
0 Parametr komunikat jest wysyłany do systemowego mechanizmu
rejestrowania PHP przy użyciu mechanizmu rejestrowania
zapewnianego przez system operacyjny lub pliku — w zależności od
ustawienia zmiennej konfiguracji error_log.
1 Komunikat jest wysyłany pocztą elektroniczną na adres podany
w parametrze cel. Jedynie ten typ zapisu wykorzystuje parametr
naglowki. Do obsługi tego typu komunikatu wykorzystywana jest ta
sama funkcja wewnętrzna, co w funkcji mail().
121 PHP – Kompendium wiedzy
2 Komunikat jest wysyłany poprzez połączenie PHP używane do
uruchamiania zdalnego. Opcja ta jest dostępna jedynie w przypadku,
gdy włączone jest uruchamianie zdalne. W tym przypadku parametr cel
określa nazwę komputera lub adres IP oraz opcjonalnie numer portu
używanego do odbierania informacji uruchamiania.
3 Komunikat jest dołączany na koniec pliku o nazwie cel.
W czasie pisania książki typ 2 nie był dostępny. Kod źródłowy zawierał komentarz informujący, że zdalne
uruchamianie nie jest jeszcze dostępne. Inne typy komunikatów działają w sposób opisany w tabeli. Na wydruku
10.5 pokazany jest przykład użycia funkcji error_log().
Wydruk 10.5. Użycie funkcji error_log()
<html>
<head>
<title>Rejestrowanie błędów</title>
</head>
<body>
<?php
// Zapisanie błędu do dziennika systemowego
error_log( "MÓJ BŁĄD: wystąpił błąd!", 0 );
// Wysłanie błędu przez e-mail
error_log( "MÓJ BŁĄD: wystąpił błąd!", 1, "app_errors@intechra.net",
"From: error_logger@myhost.com\r\n" );
// Zapisanie błędu w pliku śladu aplikacji
error_log( "MÓJ BŁĄD: wystąpił błąd!", 3, "/tmp/error.log" );
?>
Wystąpiły błędy.
</body>
</html>
Pierwsze wywołanie funkcji error_log() zapisuje komunikat błędu w systemowym dzienniku błędów. W
tym przykładzie błąd ten wysyłany do dziennika błędów Apache. Drugie wywołanie skutkuje wysłaniem poczty
elektronicznej do odbiorcy określonego w parametrze cel. Informacje zawarte w dodatkowych nagłówkach są
utworzone przy użyciu tych samych zasad, co w przypadku funkcji mail(). Ostatnie wywołanie powoduje
dodanie komunikatu błędu do pliku, w tym przypadku /tmp/error.log.
Wykorzystanie tego mechanizmu nie jest w dosłownym znaczeniu uruchamianiem, ale jego zastosowanie
może zaoszczędzić czas spędzony przy właściwym uruchamianiu, powodując eliminację niektórych błędów i
przeoczeń już w fazie programowania. Jak wcześniej wspomniałem, unikanie uruchamiania programu poprzez
tworzenie poprawnego kodu jest o wiele cenniejsze od najlepszych narzędzi używanych przy uruchamianiu.

Własna obsługa błędów
Tak jak prawie każdy element PHP mechanizm obsługi błędów możesz dostosować do własnych potrzeb,
aby spełniał wymagania stawiane przez aplikację. Jak pokazane zostało na końcu poprzedniego przykładu,
funkcja error_log() pozwala na skonstruowanie prostego mechanizmu rejestrowania własnych błędów
występujących w aplikacji, ale nie pozwala na obsługę błędów generowanych przez PHP. Nie pozwala również
na przechwytywanie komunikatów generowanych przez funkcje assert(). Na szczęście PHP pozwala w inny
sposób obsługiwać takie przypadki.
Funkcja set_error_handler() pozwala zarejestrować funkcję w PHP, która będzie wywoływana za
każdym razem, gdy wygenerowany zostanie komunikat błędu. Funkcja set_error_handler() wymaga podania
jednego argumentu — nazwy funkcji obsługi błędów. Prototyp takiej funkcji wygląda następująco:
function ErrorCallBack( int nr_bledu, string ciag_bledu, string nazwa_skryptu,
int nr_lini, array kontekst)
Na wydruku 10.6 zamieszczony jest przykład sposobu rejestrowania i użycia funkcji obsługi błędów.
Wydruk 10.6. Wykorzystanie set_error_handler()
<?php
function myErrorHandler( $aErrorNo, $aErrorStr, $aFile, $aLine, $aContext)
{
switch ( $aErrorNo )
{
case E_ERROR:
$aErrorType = "E_ERROR"; // nie powinien wystąpić
break;
case E_WARNING:
$aErrorType = "E_WARNING";

Rozdział 10 – Uruchamianie 122
break;
case E_PARSE:
$aErrorType = "E_PARSE"; // nie powinien wystąpić
break;
case E_NOTICE:
$aErrorType = "E_NOTICE";
break;
case E_CORE_ERROR:
$aErrorType = "E_CORE_ERROR"; // nie powinien wystąpić
break;
case E_CORE_WARNING:
$aErrorType = "E_CORE_WARNING"; // nie powinien wystąpić
break;
case E_COMPILE_ERROR:
$aErrorType = "E_COMPILE_ERROR"; // nie powinien wystąpić
break;
case E_COMPILE_WARNING:
$aErrorType = "E_COMPILE_WARNING"; // nie powinien wystąpić
break;
case E_USER_ERROR:
$aErrorType = "E_USER_ERROR";
break;
case E_USER_WARNING:
$aErrorType = "E_USER_WARNING";
break;
case E_USER_NOTICE:
$aErrorType = "E_USER_NOTICE";
break;
default:
$aErrorType = "UNKNOWN ERROR TYPE";
break;
}
print( "<table border=\"1\"><tr><td>" );
print( "<b>$aErrorType</b>: <i>$aErrorStr</i><br>" );
print( "w pliku $aFile linia $aLine<br>" );
print( "</td></tr></table>" );
}
set_error_handler( "myErrorHandler" );
error_reporting( E_ALL );
?>
<html>
<head>
<title>Własna obsługa błędów</title>
</head>
<body>
<?php
trigger_error( "Test błędów", E_USER_ERROR );
$aArray = array( "state" => "Idaho", "county" => "Madison",
"city" => "Rexburg", "country" => "US" );
print( "aArray[state] = " . $aArray[state] . "<br>" );
?>
</body>
</html>
W skrypcie na wydruku 10.6 została zdefiniowana funkcja obsługi błędów myErrorHandler(), która
wyświetla komunikaty błędów w obramowanej tabeli zawierającej jedną komórkę, co pomaga w odróżnieniu
komunikatu błędu od reszty kodu HTML. Po zainstalowaniu funkcji obsługi, skrypt powoduje dwa błędy.
Pierwszy jest generowany przy użyciu funkcji PHP trigger_error(). Drugi błąd (ostrzeżenie) jest identyczny jak
błąd pokazany na wydruku 10.3. Na rysunku 10.4. pokazany został wynik działania skryptu.
Rysunek 10.4.
Działanie funkcji
set_error_handler()

123 PHP – Kompendium wiedzy
Używając funkcji set_error_handler() należy pamiętać, że PHP nie przekazuje do funkcji obsługi błędów
typu E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR oraz E_COMPILE_WARNING. Obsługa tego
typu błędów przez użytkownika nie jest bezpieczna. Z tego powodu w kodzie funkcji obsługi błędów z wydruku
10.6. pojawiły się komentarze „nie powinien wystąpić”.
Uwaga na temat set_error_handler()

Funkcja set_error_handler() jest dostępna w PHP od wersji 4.0.1. Dodatkowo, funkcja użyta w tym przykładzie posiada pięć
parametrów, w tym nazwę skryptu, numer linii i dane kontekstu. Parametry te s ą dostępne dopiero w wersji 4.0.2. Wcześniejsze
wersje miały tylko dwa parametry: typ komunikatu i komunikat.

W skrypcie z wydruku 10.6 nie są wykorzystane dane na temat kontekstu. Będą one opisane w następnej
części rozdziału. Dane te zawierają nazwy i wartości zmiennych istniejących w skrypcie w momencie wystąpienia
błędu.
Również funkcja assert() pozwala na zdefiniowanie wywoływanej funkcji. Aby to zrealizować należy
skorzystać z funkcji assert_options(). Funkcja obsługująca nieudane asercje jest zdefiniowana w następujący
sposób:
function AssertCallback ($NazwaPliku, $NrLinii, $Asercja )

Uwaga na temat assert_options()

W PHP do wersji 4.0.2 włącznie, w funkcji assert_options() występował mały błąd. Występował on w przypadku wywołania
funkcji w postaci assert_options(ASSERT_CALLBACK), w celu odczytania ustawionej funkcji obsługi. Mimo, że w dokumentacji
napisano, że wywołanie takie zwróci jedynie nazwę bieżącej funkcji obsługi, to dodatkowo oprócz zwracania nazwy, bieżące
ustawienie funkcji obsługi było czyszczone. Dlatego jeżeli chcesz użyć funkcji assert() z funkcją obsługi upewnij się, że nie jest
wywoływana później funkcja assert_options() w celu sprawdzenia nazwy zarejestrowanej funkcji. Błąd ten został zauważony i
poprawiony w wersjach PHP powyżej 4.0.2.

Na wydruku 10.7. pokazany został przykład zdefiniowania i użycia funkcji wywoływanej przez assert().
Wydruk 10.7. Wykorzystanie funkcji wywoływanej przez callback()
<?php
error_reporting( E_ALL );
function MyACallback( $aFileName, $aLineNum, $aAssertion )
{
print( "<table border=\"1\"><tr><td>" );
print( "<b>assert()</b> nieudane: <i>$aAssertion</i><br>" );
print( "w pliku $aFileName w linii $aLineNum<br>" );
print( "</td></tr></table>" );
}
// zarejestrowanie funkcji obsługi
assert_options( ASSERT_CALLBACK, "MyACallback" );
// wyłączenie normalnych ostrzeżeń
assert_options( ASSERT_WARNING, 0 );
?>
<html>
<head>
<title>Własna obsługa asercji</title>
</head>
<body>
Nieudana asercja.
<?php
assert( "1 == 2" );
?>
</body>
</html>
Kod z wydruku jest podobny do tego z wydruku 10.6. Wywołanie asercji powoduje wyświetlenie
jednokomórkowej tabeli. Jeżeli nie zostanie opcja ASSERT_WARNING, oprócz informacji zdefiniowanych przez
użytkownika wyświetlony zostanie standardowy komunikat PHP. Na rysunku 10.5. pokazany jest wynik działania
skryptu z wydruku 10.7.

Rozdział 10 – Uruchamianie 124
Rysunek 10.5.
Użycie funkcji
zdefiniowanej dla
assert()

PHP posiada elastyczny mechanizm obsługi błędów. Pozwala on dzięki temu pisać kod łatwiejszy do
uruchamiania i późniejszego utrzymania. W następnej części połączymy wszystkie przedstawione do tej pory
techniki, co w efekcie pozwoli lepiej uruchamiać programy w całym cyklu produkcyjnym.

Zaawansowana obsługa błędów
Po omówieniu technik obsługi błędów możemy rozpocząć tworzenie ogólnego narzędzia do obsługi
błędów. Motywacją do napisania tego fragmentu był brak dostarczanych przez PHP narzędzi, które
automatycznie łączą różne typy danych o błędach. Oprócz tego, PHP 3 posiadał możliwość zdalnego
uruchamiania, która nie została przeniesiona do PHP 4. Pozwalał on przesyłać dane za pomocą protokołu TCP/IP
do innego komputera. Opcja ta prawdopodobnie niedługo się pojawi, ale na razie muszą wystarczyć podstawowe
techniki obsługi błędów opisane w tym rozdziale.
Przykład przytoczony w tej części jest niezwykle długi, więc zostanie omówiony we fragmentach. Moduł
ten znajduje się w jednym pliku, MyDebug.php i będzie on określony jako moduł MyDebug. Jest on tak
utworzony, że może być łatwo dołączony do dowolnego skryptu PHP. Po dołączeniu pliku wykonywany jest kod
pokazany na wydruku 10.8.
Wydruk 10.8. Dołączanie modułu MyDebug
ParseConfig( getenv( "MYDEBUG_CONFIG" ) );
if ( $aMyDebugType & MYDEBUG_DISPLAYFILE )
{
// jeżeli nie można zapisać do pliku sladu
// poinformuj o tym uzytkownika i wyłączyć zapis do pliku
if ( CheckFileSanity( $aMyDebugFile ) == False )
{
error_log( "MyDebug nie udało się otworzyć pliku $aMyDebugFile", 0 );
$aMyDebugType &= ~MYDEBUG_DISPLAYFILE;
}
}

if ( $aMyDebugType & MYDEBUG_DISPLAYFILE )
{
$aFileHandle = fopen( $aMyDebugFile, "a" );
}

if ( $aMyDebugType & MYDEBUG_DISPLAYIP )
{
$aSocketHandle = fsockopen( "udp://$aMyDebugIP", $aMyDebugPort );
}

// Teraz rejestrujemy funkcje obsługi i funkcje porządkujące
set_error_handler( "MyErrHandler" );
assert_options( ASSERT_CALLBACK, "MyAssertHandler" );
assert_options( ASSERT_WARNING, 0 );
register_shutdown_function( "MyDebugShutdown" );
Pierwsza linia z wydruku 10.8. powoduje przetworzenie ciągu konfiguracji MyDebug, który jest
przechowywany w zmiennej środowiska serwera. Na przykład, plik konfiguracyjny Apache może zawierać kod
podobny do następującego:
SetEnv MYDEBUG_CONFIG FILE=/tmp/mydebug.log;
MAIL=mydebug@intechra.net;IP=myserver.com:5400;

125 PHP – Kompendium wiedzy
Ta opcja konfiguracji jest specyficzna dla modułu MyDebug i nie jest dostępna jako standardowa część
Apache czy PHP. Przykład ten pokazuje, że można ustawiać zmienne środowiska, poprzez pliki konfiguracyjne
serwera WWW i są one dostępne w kodzie PHP. Zmienna MYDEBUG_CONFIG definiuje miejsca, gdzie MyDebug
będzie zapisywał błędy. W tym przypadku moduł będzie zapisywał błędy do pliku (/tmp/mydebug.log), wysyłał
na adres e-mail (mydebug@intechra.net)oraz do gniazda UDP (myserver.com:5400). W normalnej pracy wybiera
się zwykle jedną z metod zapisu błędów, ale moduł MyDebug pozwala na stosowanie wielu jednoczesnych miejsc
zapisu błędów. Funkcja ParseConfg() analizuje ciąg konfiguracji i ustawia odpowiednie zmienne globalne.
Po przeanalizowaniu ciągu konfiguracyjnego sprawdzane są wszystkie pliki używane do zapisu, aby mieć
pewność, że można do nich zapisywać dane. Jeżeli moduł MyDebug nie może zapisać danych do pliku,
zapisywanie do niego jest wyłączane. Następnie otwierane są wszystkie potrzebne pliki i gniazda. Gniazdo jest
otwierane używając UDP, co nie wymaga istnienia procesu nasłuchu. Własność ta jest użyteczna szczególnie
wtedy, gdy zapisywanie jest aktywne na serwerze produkcyjnym, ale nie zawsze jest aktywny proces nasłuchu.
Następnie moduł MyDebug rejestruje funkcje obsługi błędów i asercji. Ostatnią operacją jest
zarejestrowanie funkcji wywoływanej po zakończeniu programu, co pozwala na eleganckie zakończenie działania
modułu. Na wydruku 10.9 pokazana jest funkcja kończąca program.
Wydruk 10.9. Funkcja kończąca program
function MyDebugShutdown( )
{
global $aFileHandle, $aSocketHandle, $aMyDebugType;
if ( $aMyDebugType & MYDEBUG_DISPLAYFILE )
{
fclose( $aFileHandle );
}

if ( $aMyDebugType & MYDEBUG_DISPLAYIP )
{
fclose( $aSocketHandle );
}
}
Funkcja ta zamyka wymagane pliki oraz gniazdo sieciowe. Funkcję kończącą może zarejestrować
dowolny skrypt. PHP pozwala na rejestrowanie wielu funkcji kończących, które są wykonywane w czasie
kończenia pracy skryptu. Choć ten fakt nie jest odnotowany w dokumentacji, funkcje te są wywoływane w tej
samej kolejności, co zostały zarejestrowanie. Ponieważ kolejność ta nie została udokumentowana, skrypty twoje
nie powinny polegać na kolejności wykonywania funkcji kończących lub musisz sam to przetestować. Można
przekazać dodatkowe argumenty do funkcji register_shutdown_function(). PHP przekaże je do funkcji
końcowej. Należy również pamiętać, że w funkcji końcowej nie można wysyłać żadnych danych do przeglądarki.
Po skonfigurowaniu modułu MyDebug, obsługuje on błędy za pomocą funkcji z wydruku 10.10.
Wydruk 10.10. Funkcje obsługi błędów
// Funkcja obsługi ustawiana przez set_error_handler()
function MyErrHandler( $aErrorNo, $aErrorStr, $aFile, $aLine, $aContext)
{
MyDebug( $aErrorStr, $aFile, $aLine, MYDEBUG_ERRCALLBACK,
$aErrorNo, $aContext );
}

// Funkcja obsługi dla funcji assert_options()
function MyAssertHandler( $aFileName, $aLineNum, $aAssertion )
{
MyDebug( "asercja( $aAssertion ) nieudana", $aFileName,
$aLineNum, MYDEBUG_ASSERTCALLBACK );
}
Obie funkcje przekazują parametry do głównej funkcji obsługi błędów, która może również zostać
wywołana bezpośrednio ze skryptu. Główna funkcja obsługi błędów to przedstawiona na wydruku 10.11. funkcja
MyDebug().
Wydruk 10.11. Funkcja MyDebug()
// Funkcja MyDebug jest główną funkcją obsługi
function MyDebug( $aMessage, $aFile, $aLine,
$aCallType = MY_DEBUG_INTERNAL, $aErrType = 0,
$aErrContext = array() )
{
global $aMyDebugType;

for ( $aDisplayType = MYDEBUG_DISPLAYFILE;
$aDisplayType <= MYDEBUG_DISPLAYIP;
$aDisplayType++ )
{

Rozdział 10 – Uruchamianie 126
if ( $aDisplayType & $aMyDebugType )
{
$aType = FormatType( $aCallType, $aDisplayType );
$aMessage = FormatMsg ( $aCallType, $aMessage, $aErrType, $aDisplayType );

if ( $aCallType == MYDEBUG_ERRCALLBACK )
{
$aContext = FormatContext( $aErrContext, $aDisplayType );
}
else
{
$aContext = "";
}

MyDebugOutput( $aType, $aMessage, $aFile, $aLine, $aContext, $aDisplayType );
}
}
}
Funkcja MyDebug() formatuje różne parametry w zależności od typu medium zapisu (plik, e-mail lub
TCP/IP). Następnie wywołuje funkcje MyDebugOutput() (wydruk 10.12), która wysyła dane do prawidłowego
miejsca. Funkcje formatujące z wydruku 10.11 zostaną omówione później.
Wydruk 10.12. Funkcja MyDebugOutput()
function MyDebugOutput( $aType, $aMessage, $aFile, $aLine,
$aContext, $aDisplayType )
{
global $aFileHandle, $aSocketHandle, $aMyDebugEmail;

switch( $aDisplayType )
{
case MYDEBUG_DISPLAYFILE:
$aMsg = "$aType: '$aMessage' wystąpił w $aFile w lini $aLine. ";
if ( $aContext != "" )
{
$aMsg .= "Dane kontekstu:\n{$aContext}\n";
}
else
{
$aMsg .= "\n";
}
fputs( $aFileHandle, $aMsg );
break;
case MYDEBUG_DISPLAYEMAIL:
$aMsg = "$aType: '$aMessage' wystąpił w $aFile w linii $aLine. ";
if ( $aContext != "" )
{
$aMsg .= "Dane kontekstu:\n{$aContext}\n";
}
else
{
$aMsg .= "\n";
}
mail($aMyDebugEmail, "Raport MyDebug", $aMsg, "From: mydebug@host.com\r\n");
break;
case MYDEBUG_DISPLAYIP:
$aMsg = "$aType|$aMessage|$aFile|$aLine|$aContext^^";
fputs( $aSocketHandle, $aMsg );
break;
}
}
Funkcja MyDebugOutput() wysyła dane do właściwych miejsc. Jest ona zaskakująco prosta, jeżeli pomyśli
się o efektywności każdej z tych opcji. Każda funkcja formatująca użyta w module MyDebug posiada mechanizm
zamiany wewnętrznego numeru błędu na postać czytelną dla człowieka. Na przykład funkcja FormatType()
przedstawiona na wydruku 10.13 formatuje kod typu błędu.
Wydruk 10.13. Funkcja FormatType()
/* Funkcja formatuje typ komunikatu w oparciu o to
gdzie będzie wyświetlony */
function FormatType( $aCallType, $aDisplayType )
{
switch( $aDisplayType )
{
case MYDEBUG_DISPLAYFILE:
case MYDEBUG_DISPLAYEMAIL:
switch ( $aCallType )
{
case MYDEBUG_INTERNAL:
return "INTERNAL";

127 PHP – Kompendium wiedzy
break;
case MYDEBUG_ERRCALLBACK:
return "ERROR CALLBACK";
break;
case MYDEBUG_ASSERTCALLBACK:
return "ASSERT CALLBACK";
break;
}
break;
case MYDEBUG_DISPLAYIP:
return $aCallType;
break;
}
}
Jeżeli dane są wysyłane poprzez TCP/IP, nie jest przeprowadzane formatowanie. Sam numer typu jest
wysyłany do zdalnego komputera. W innym wypadku numer typu jest zamieniany na czytelny ciąg. Inne funkcje
konwertujące użyte w MyDebug działają podobnie. Jedyną funkcją formatującą, która jest wyraźnie inna, jest
funkcja formatująca kontekst błędu, FormatContext(). Funkcja ta jest wywoływana jedynie wtedy, gdy błąd
zostanie obsłużony przez funkcję zarejestrowaną za pomocą set_error_handler(). Dane kontekstu udostępniane
przez PHP zawierają wszystkie zmienne będące w zasięgu w momencie wystąpienia błędu. Dane te są przesyłane
w postaci tablicy asocjacyjnej z nazwami zmiennych i ich wartościami. Analiza tych danych wymaga
wykorzystania funkcji rekurencyjnej, ponieważ w kontekście mogą znajdować się tablice. Funkcja zamieszczona
na wydruku 10.14. analizuje dane kontekstu, przekształcając je na postać czytelną dla człowieka.
Wydruk 10.14. Analiza danych kontekstu
/* Funkcja formatuje typ komunikatu w oparciu o to
gdzie będzie wyświetlony. Funkcja oparta o
funkcję rekurencyjną FormatContextR */
function FormatContext( $aErrContext, $aDisplayType )
{
// od tej pory wszystkie wyświetlane typy
// otrzymują ten sam ciąg kontekstu
$aString = "";
$aDelim = "\n";
FormatContextR( $aErrContext, $aString, $aDelim );
return $aString;
}

function FormatContextR( $aErrContext, &$aString, $aDelim )
{
foreach( $aErrContext as $aVarName => $aVarValue )
{
if ( is_array( $aVarValue ) == True )
{
$aString .= "$aVarName = array( ";
FormatContextR( $aVarValue, $aString, "," );
$aString .= " )$aDelim";
}
else
{
$aString .= "$aVarName = {$aVarValue}{$aDelim}";
}
}
}
Funkcja FormatContext() ustawia kilka parametrów i wywołuje funkcję rekurencyjną FormatContextR().
Funkcja rekurencyjna przegląda tablice zmiennych kontekstu i zapisuje każdą parę nazwa-wartość do
wynikowego ciągu. Jeżeli napotkana zostanie tablica, rekurencyjnie jest wywoływana funkcja FormatContextR().
W zależności od miejsca wystąpienia błędu, kontekst lokalny może zawierać sporo danych. Jeżeli błąd
wystąpi w głównej części skryptu, w zasięgu znajdą się wszystkie zmienne globalne, w tym zmienne środowiska i
zmienne GET i POST. Wszystkie te zmienne znajdą się w danych kontekstu. Jeżeli błąd wystąpi w funkcji, kontekst
będzie zawierał jedynie zmienne lokalne funkcji.
Do skryptu testującego (wydruk 10.15.) dołączyliśmy moduł MyDebug oraz ustawiliśmy zmienną
konfiguracji na zapisywanie do pliku tekstowego. Po jego uruchomieniu na końcu pliku śladu znalazł się ciąg
błędu. Poniższy tekst nie jest całym plikiem, jedynie wynikiem wystąpienia ostatniego błędu w skrypcie:
ERROR CALLBACK: 'Typ błędu PHP: E_USER_ERROR - error in sum'
wystąpił w c:\helion\php4-devguide\site\ch10\test_mydebug.phtml w lini 16. Dane kontekstu:
a = 1
b = 2
aArray = array( 0 = spring,1 = summer,2 = autumn,3 = winter, )
Wydruk 10.15. Skrypt testowy
<?php

Rozdział 10 – Uruchamianie 128
include_once( "./mydebug.php" );
?>
<html>
<head>
<title>Test modułu MyDebug</title>
</head>
<body>
Nieudana asercja.<br><br>
<?php
function sum( $a, $b )
{
$aArray = array( "spring", "summer", "autumn", "winter" );
trigger_error( "error in sum", E_USER_ERROR );
}

assert( "1 == 2" );
trigger_error( "Błąd testowy", E_USER_ERROR );
$aArray = array( "state" => "Idaho", "county" => "Madison",
"city" => "Rexburg", "country" => "US" );
print( "<br><br>aArray[state] = " . $aArray[state] . "<br>" );
sum( 1, 2 );
?>
</body>
</html>
Skrypt testowy przedstawiony na wydruku 10.15. nie robi nic, poza generowaniem błędów. Wynik funkcja
sum() nie jest nigdzie używany, ale jest ona umieszczone w tym skrypcie, aby pokazać jak wywołanie funkcji
wpływa na dane kontekstu przekazywane przez PHP. Linie z opisem błędu zamieszczone bezpośrednio przed
wydrukiem 15 są wygenerowane przy wywołaniu funkcji sum().
Aby pokazać elastyczność tego modułu, napisana została aplikacja Windows, która realizuje proces
nasłuchu portu TCP/IP i wyświetla przychodzące dane. Jest to prosta aplikacja Delphi, która odczytuje pakiety
UDP przychodzące do portu 5400. Po odczytaniu danych formatuje linie i wyświetla je. Na rysunku 10.6.
pokazana została ta aplikacja po odebraniu kilku komunikatów wygenerowanych przez PHP.
Rysunek 10.6.
Aplikacja
nasłuchu dla
MyDebug

Jednym z powodów atrakcyjności języka PHP jest to, że jest on niezwykle rozszerzalny. Moduł MyDebug
jest napisany całkowicie w PHP dodając do niego niezwykle użyteczne funkcje (kompletne źródła modułu
MyDebug są dostępne wraz z wszystkimi przykładami kodu z tej książki). Moduł ten nie jest kompletny i może
być rozwijany na wiele sposobów. Na przykład, wykorzystanie poczty elektronicznej do raportowania błędów jest
niezwykle nieefektywne, ale można wykorzystać pocztę elektroniczną do raportowania jedynie krytycznych
błędów i ostrzeżeń, co pozwoli na wykorzystanie tej opcji w środowisku produkcyjnym. Niezmiernie istotny jest
fakt, że wszystkie te opcje są zrealizowane całkowicie w PHP. Nie wszystkie narzędzia programowania dla
WWW są tak elastyczne.

Podsumowanie
W tym rozdziale przedstawione zostały informacje na temat technik programowania defensywnego, które
pozwalają na uniknięcie możliwie dużo pracy przy uruchamianiu. W chwili obecnej PHP nie posiada programu
do uruchamiania skryptów, podobnego do tych, które są dostępne we wielu nowoczesnych językach
129 PHP – Kompendium wiedzy
programowania. Jednak przy odrobinie pomysłowości i wykorzystując rozszerzalność PHP, można stworzyć
świetne narzędzia do uruchamiania aplikacji. Opisany został jeden z modułów, który zapewnia elastyczną obsługę
błędów i może być modyfikowany i rozszerzany tak, aby spełniał wymagania prawie każdego programisty.

Bibliografia
Steve McConnell. Code Complete. Seattle: Microsoft Press, 1993.

Rozdział 10 – Uruchamianie 130
Rozdział 11. Ponowne wykorzystanie
kodu
Wstęp
Podczas tworzenia dowolnej aplikacji niezmiernie ważne jest wykorzystanie istniejących modułów kodu.
Pierwszym powodem jest to, że używane moduły kodu stanowią podstawę kolejnych aplikacji i w dłuższym
czasie polepszają wydajność zespołu programistów.
Ponieważ PHP pozwala na dołączanie zewnętrznych plików oraz na tworzenie klas, ponowne
wykorzystanie kodu jest dosyć proste. W tym rozdziale ponowne wykorzystanie kodu zostanie krótko omówione
z perspektywy inżynierii programowania, oraz przytoczone zostanie kilka przykładów kodu PHP nadającego się
do powtórnego wykorzystania. W rozdziale tym omówione zostanie wykorzystanie w projektach PHP kodu
napisanego w innych językach programowania. Taka elastyczność pozwala programistom na przenoszenie do
sieci WWW istniejących aplikacji bez konieczności całkowitego przepisywania kodu.

Ponowne wykorzystanie kodu a inżynieria programowania
Ponowne wykorzystanie kodu nie polega jedynie na integracji istniejącego kodu z nowym produktem.
Nowe fragmenty kodu często są tworzone w sposób ułatwiający ich ponowne wykorzystanie. Gdy planowane jest
ponowne wykorzystanie kodu, ważne są efekty długoterminowe, ponieważ tworzenie takiego kodu zajmuje często
dużo więcej czasu i jest bardziej kosztowne w porównaniu do tego samego kodu do jednokrotnego użycia
(McConnell, 1996). Zalety ponownego użycia nie są natychmiast widoczne, ale wiele firm zauważyło około 58%
wzrost wydajności rocznie w przeciągu czterech lat (McConnell, 1996).
Zalety te nie będą wykorzystane, jeżeli nie zostanie zastosowane odpowiednie planowanie. Poniżej
przedstawione zostały niektóre ważne zagadnienia, jakie należy wziąć pod uwagę:
• Zaangażowanie kierownictwa w proces ponownego wykorzystania kodu.
• Upewnienie się, że tworzenie kodu do ponownego wykorzystania jest integralną częścią całego procesu
produkcji oprogramowania i że wszyscy programiści zgadzają się z tą ideą.
• Skupienie się na tworzeniu małych, precyzyjnych modułach kodu.
• Tworzenie kodu do ponownego wykorzystania przy użyciu możliwie najlepszych standardów
programowania i dokumentacji.
Tworząc kod należy mieć na uwadze, że przy pisaniu aplikacji w przyszłości będą potrzebne podobne
fragmenty. Mając to na uwadze, częściej należy dzielić grupy funkcji na oddzielne moduły lub klasy. Jeżeli
wiadomo, że dany fragment kodu będzie wykorzystywany w przyszłości, należy napisać go i udokumentować w
sposób zgodny z najlepszymi zaleceniami stosowanymi w zespole. Należy unikać używania danych lub założeń
specyficznych dla projektu, a zamiast tego tworzyć moduł w sposób, który uprości jego wykorzystanie w
przyszłości.
W PHP kod nadający się do powtórnego wykorzystania można tworzyć przy pomocy kilku metod, na
przykład tworząc oddzielne pliki z kodem źródłowym (pliki dołączane) lub tworząc klasy obiektowe. Wybór
plików dołączanych lub podejścia obiektowego nie wpływa zbytnio na ogólne założenia. Kluczem do sukcesu jest
tworzenie kodu nadającego się do powtórnego użycia, który jest odpowiednio zorganizowany i dobrze
udokumentowany. Użycie hermetyzacji i technik ukrywania danych da w efekcie maksymalne zwiększenie
wydajności i efektywności ponownie wykorzystanego kodu.
Ponowne użycie istniejącego kodu
Z powodu natury projektów internetowych, w firmie może nie istnieć zbyt wiele fragmentów kodu do
wykorzystania. Jednak ponowne wykorzystanie kodu może jedynie wymagać przewidywania przyszłych
projektów. Omówione zostaną teraz niektóre techniki dostępne w PHP, o których należy pamiętać przy
projektowaniu aplikacji.
Jeżeli przenosi się zwykłą aplikację biurową do sieci, lub przepisuje się z innego języka na PHP,
prawdopodobnie istnieje wtedy kod, który można wykorzystać. Ponieważ PHP jest niezwykle rozszerzalny,
istnieje wiele metod użycia obcego kodu w aplikacjach opartych o PHP. Niektóre z tych metod zostaną opisane w
późniejszych częściach.

PHP
PHP zawiera kilka narzędzi ułatwiających ponowne wykorzystanie kodu. Z tego powodu została
przygotowana podstawa do tworzenia narzędzi dla PHP tworzonych przez różne firmy. Niektóre z nich zostały
wspomniane w poprzednich rozdziałach i są wymienione na liście zasobów internetowych, na końcu książki.
Najbardziej oczywistą metodą ponownego wykorzystania kodu PHP jest użycie funkcji include() lub
require() do dołączenia istniejącego kodu. Używane już we wcześniejszych przykładach, funkcje te pozwalają
na dołączanie czystego kodu PHP, HTML lub ich kombinacji. Zaczynając od wersji PHP 4, dostępne są funkcje
include_once() i require_once(), które upraszczają proces dołączania. Funkcje te eliminują problem występujący
przy wielokrotnym dołączaniu do skryptu tego samego pliku. Na wydruku 11.1. i 11.2. przedstawiono przykład
takiego problemu i sposób jego rozwiązania.
Wydruk 11.1. Plik dołączany date_funcs.php
<?php
include_once( "./format_funcs.php" );
// Zwraca ilość dni pomiędzy datami
// jako sformatowany ciąg w postaci mm-dd-rrrr
function GetDateDiff( $aDateStr1, $aDateStr2 )
{
$aDateArray1 = explode( "-", $aDateStr1 );
assert( 'count( $aDateArray1 ) == 3' );
$aDateArray2 = explode( "-", $aDateStr2 );
assert( 'count( $aDateArray2 ) == 3' );
$aTime1 = mktime( 0, 0, 0, $aDateArray1[0], $aDateArray1[1], $aDateArray1[2] );
$aTime2 = mktime( 0, 0, 0, $aDateArray2[0], $aDateArray2[1], $aDateArray2[2] );
$aTimeDiff = abs( $aTime1 - $aTime2 );
return GetFormattedNumber( $aTimeDiff / ( 60 * 60 * 24 ) );
}
?>
Wydruk 11.2. Plik dołączany format_funcs.php
<?php
function GetFormattedNumber( $aNum )
{
return number_format( $aNum, 0, '.', ' ' );
}
?>
Skrypt z wydruku 11.3. wykorzystuje funkcje z obu poprzednich plików dołączanych.
Wydruk 11.3. Skrypt wykorzystujący oba pliki dołączane
<?php
include( "./date_funcs.php" );
include( "./format_funcs.php" );
?>
<html>
<head>
<title>Problem z wielokrotnym dołączaniem plików</title>
</head>
<body>
<?php
$aNumVisitors = 14500;
print( "Witrynę odwiedziło " );
print( GetFormattedNumber( $aNumVisitors ) );
print( " gości " );
print( "w przeciągu ostatnich " );
print( GetDateDiff( "9-21-2000", "8-15-1992" ) );
print( " dni." );
?>
</body>
</html>

Rozdział 11 – Ponowne wykorzystanie kodu 132
Problem występujący w skrypcie z wydruku 11.3 wynika z tego, że plik dołączany date_funcs.php dołącza
również plik format_funcs.php. Po uruchomieniu skryptu generowany jest komunikat błędu:
Fatal error: Cannot redeclare getformattednumber() in ./format_funcs.php on line 2
Na wydruku 11.4. pokazane zostało jak łatwo można rozwiązać ten problem korzystając z funkcji
include_once().
Wydruk 11.4. Skrypt wykorzystujący include_once()
<?php
include_once( "./date_funcs.php" );
include_once( "./format_funcs.php" );
?>
<html>
<head>
<title>Problem z wielokrotnym dołączaniem plików</title>
</head>
<body>
<?php
$aNumVisitors = 14500;
print( "Witrynę odwiedziło " );
print( GetFormattedNumber( $aNumVisitors ) );
print( " gości " );
print( "w przeciągu ostatnich " );
print( GetDateDiff( "9-21-2000", "8-15-1992" ) );
print( " dni." );
?>
</body>
</html>
Funkcje require() i require_once() działają podobnie. Ten mechanizm dołączania plików pozwala na
tworzenie własnych bibliotek często używanych funkcji oraz wykorzystanie kodu od zewnętrznych dostawców.
Dodatkowo PHP obsługuje tworzenie klas obiektowych, które można ponownie wykorzystywać lub rozszerzać.
W poprzednich rozdziałach zostały pokazane przykłady rozszerzania klas pochodzących z od różnych
dostawców. Ponieważ PHP obsługuje dołączanie modułów oraz programowanie obiektowe, naturalnie pozwala
na tworzenie kodu wielokrotnego użycia. Oprócz tego, rozszerzalność PHP pozwala na wykorzystanie innego
istniejącego kodu.

C/C++
PHP jest napisany w C i C++. Z tego powodu możliwa jest integracja istniejącego kodu C/C++
bezpośrednio w PHP. Tak naprawdę wiele z rozszerzeń PHP jest bezpośrednio przeniesiona z C lub C++. Na
przykład CyberCashTM Merchant Control Kit został napisany w C a jego funkcje są dostępne w PHP jako funkcje
cybercash_xxx().
Jeżeli posiadasz istniejący kod projektu w C lub C++, który ma być przeniesiony do środowiska WW,
możesz rozważyć dołączenie tego kodu do twojej instalacji PHP. Należy jednak pamiętać, że wymaga to sporo
pracy i w efekcie może być mniej efektywne, niż proste przepisanie kodu na PHP.
Skupmy się teraz na tworzeniu wbudowanych funkcji PHP opartych o istniejące funkcje w C. Jeżeli
istniejący kod jest napisany w C++, można utworzyć obiekty PHP używające implementacji w C++, ale nie
opiszemy tutaj tego procesu. Można zamiast tego napisać funkcje mapujące dla metod istniejących obiektów
C++.
Załóżmy, że mamy trzy funkcje zamieszczone na wydruku 11.5 i chcemy na ich podstawie utworzyć
wewnętrzne funkcje PHP. Funkcje te są wykorzystywane do obliczania płatności hipotecznych i tworzenia tabel
amortyzacji w USA. Pierwsze dwie funkcje zwracają pojedyncze wartości oznaczające odpowiednio ratę
miesięczną i sumę wszystkich rat. Ostatnia funkcja zwraca tabelę wartości reprezentujących wartość odsetek
miesięcznych w racie. Używając tych danych można wygenerować harmonogram amortyzacji.
Wydruk 11.5. Funkcje w C do konwersji na PHP
/*
_fin_mpmt:
oblicza miesięczną spłatę kredytu w oparciu o
kwotę kredytu (p), oprocentowanie (i) oraz czas (l)
*/
double _fin_mpmt (double p, double i, double l)
{
double j;
double n;
j = i / (12 * 100);
n = l * 12;

133 PHP – Kompendium wiedzy
return ( p* ( j/( 1 - (pow(( 1+j ), (n * -1))))));
}
/*
_fin_total:
oblicza całkowitą kwotę spłat w czasie trwania kredytu
w oparciu o kwotę kredytu (p), oprocentowanie (i) i czas (l)
*/
double _fin_total (double p, double i, double l)
{
return _fin_mpmt( p, i, l) * l * 12;
}
/*
_fin_table:
oblicza miesięczne odsetki używane w planie amortyzacji dla kredytów
w oparciu o kwotę (p), oprocentowanie (i), i czas (l)
*/
void _fin_table ( double p, double i, double l, double *pIntPmt)
{
double n, m, h, q, j, c;
int nIndex;
j = i / (12 * 100);
n = l * 12;
q = p;
m = _fin_mpmt( p, i, l );
for (nIndex = 0; nIndex < n; nIndex++ )
{
h = q * j;
c = m - h;
q = q - c;
pIntPmt[nIndex] = h;
}
return ;
}
W dystrybucji PHP dostarczany jest program o nazwie build_skel, który służy do tworzenia zbioru
szkieletowych plików konfiguracji dla nowych rozszerzeń PHP. Program ten znajduje się w katalogu ext
dystrybucji PHP zawierającej pliki źródłowe. Aby dodać funkcje finansowe do PHP należy uruchomić build_skel
w następujący sposób:
./build_skel --extname=fin_funcs --proto=/sciezka/do/fin_funcs.proto --assign-params
Parametr extname jest nazwą nowego rozszerzenia PHP, natomiast proto jest nazwą pliku zawierającego
prototypy tworzonych funkcji. Plik prototypów powinien zawierać prototypy funkcji PHP. Dla funkcji
finansowych użyjemy następującej zawartości pliku prototypów:
double fin_mpmt ( double principle, double interest, double length )
double fin_total ( double principle, double interest, double length )
array fin_table ( double principle, double interest, double length )
Parametr assign-params powoduje, że pliki szkieletowe dołączają parametry o prawidłowych typach, co
zostanie opisane później. Inne dostępne parametry pozwalają kontrolować wygląd dokumentacji, i inne
ustawienia generacji kodu. Więcej szczegółów można uzyskać uruchamiając skrypt build_skel bez parametrów.
Po uruchomieniu skryptu w sposób przedstawiony powyżej, tworzony jest nowy katalog ext/fin_funcs, który
zawiera pliki rozszerzeń wymagane przez PHP. Pierwsza zmiana musi zostać wprowadzona do pliku config.4m.
W pliku tym zawarty jest opis omawiający wymagane zmiany. Plik ext/fin_funcs/config.m4 jest pokazany poniżej
(komentarze zostały usunięte):
PHP_ARG_ENABLE(fin_funcs, whether to enable fin_funcs support,
[ --enable-fin_funcs Enable fin_funcs support])
if test "$PHP_FIN_FUNCS" != "no"; then
AC_DEFINE(HAVE_FIN_FUNCS, 1, [ ])
PHP_EXTENSION(fin_funcs, $ext_shared)
fi
Funkcja build_skel tworzy plik źródłowy w C, który zawiera wymagane funkcje i dołączone pliki
nagłówków, więc natychmiast po uruchomieniu tego narzędzia i poprawieniu pliku config.m4 można
skompilować PHP z obsługą nowych rozszerzeń. Aby upewnić się, że pliki szkieletowe i konfiguracyjne są
prawidłowe, można wykonać następujące czynności:
1. Uruchom skrypt buildconf w głównym katalogu PHP.
2. Uruchom skrypt configure i dodaj obsługę nowego rozszerzenia.
3. Skompiluj PHP.
4. Wykonaj skrypt testowy z katalogu z rozszerzeniem (fin_funcs.php) aby sprawdzić, czy rozszerzenie jest
aktywne w PHP.
Testowy moduł rozszerzenia jest uaktywniany za pomocą dyrektywy konfiguracji --enable-fin_funcs.
Skrypt testowy wykrywający, czy rozszerzenie działa jest podobny do następującego:
Rozdział 11 – Ponowne wykorzystanie kodu 134
<?php
if (extension_loaded( "fin_funcs" ))
{
// Wykonaj jedną z funkcji
}
else
{
print( "moduł fin_funcs niedostępny<BR>" );
}
?>
Po skonfigurowaniu PHP, aby korzystał z nowego rozszerzenia, należy jeszcze uaktualnić plik extension.c,
aby zawierał implementację każdej z funkcji. We wielu przypadkach wymaga to jedynie dołączenia oryginalnych
nagłówków i wywołaniu oryginalnych funkcji. W przykładzie z funkcjami finansowymi, funkcje są zdefiniowane
w sposób pokazany na wydruku 11.6. Należy zauważyć, że skrypt build_skel wygenerował większość kodu w
ciele każdej funkcji. Kod dodany ręcznie zaznaczony jest czcionką pogrubioną.
Wydruk 11.6. Funkcje finansowe dodane do PHP
/* {{{ proto double fin_mpmt(double principle, double interest, double length)
*/
PHP_FUNCTION(fin_mpmt)
{
zval **principle_arg, **interest_arg, **length_arg;
double principle;
double interest;
double length;
double aRetVal;

if (ZEND_NUM_ARGS() != 3 ||
zend_get_parameters_ex(3, &principle_arg, &interest_arg, &length_arg) == FAILURE){
WRONG_PARAM_COUNT;
}

convert_to_double_ex(principle_arg);
principle = Z_DVAL_PP(principle_arg);
convert_to_double_ex(interest_arg);
interest = Z_DVAL_PP(interest_arg);
convert_to_double_ex(length_arg);
length = Z_DVAL_PP(length_arg);
aRetVal = _fin_mpmt( principle, interest, length );
RETVAL_DOUBLE( aRetVal );
}
/* }}} */

/* {{{ proto double fin_total(double principle, double interest, double length)
*/
PHP_FUNCTION(fin_total)
{
zval **principle_arg, **interest_arg, **length_arg;
double principle;
double interest;
double length;
double aRetVal;

if (ZEND_NUM_ARGS() != 3 ||
zend_get_parameters_ex(3, &principle_arg, &interest_arg, &length_arg) == FAILURE){
WRONG_PARAM_COUNT;
}

convert_to_double_ex(principle_arg);
principle = Z_DVAL_PP(principle_arg);
convert_to_double_ex(interest_arg);
interest = Z_DVAL_PP(interest_arg);
convert_to_double_ex(length_arg);
length = Z_DVAL_PP(length_arg);
aRetVal = _fin_total( principle, interest, length );
RETVAL_DOUBLE( aRetVal );
}
/* }}} */

/* {{{ proto array fin_table(double principle, double interest, double length)
*/
PHP_FUNCTION(fin_table)
{
zval **principle_arg, **interest_arg, **length_arg;
double principle;
double interest;
double length;
double *pIntPmts;
int n, nIndex;

135 PHP – Kompendium wiedzy
if (ZEND_NUM_ARGS() != 3 ||
zend_get_parameters_ex(3, &principle_arg, &interest_arg, &length_arg) == FAILURE){
WRONG_PARAM_COUNT;
}

convert_to_double_ex(principle_arg);
principle = Z_DVAL_PP(principle_arg);
convert_to_double_ex(interest_arg);
interest = Z_DVAL_PP(interest_arg);
convert_to_double_ex(length_arg);
length = Z_DVAL_PP(length_arg);
n = (int)length * 12;
pIntPmts = emalloc( sizeof( double ) * n );
_fin_table( principle, interest, length, pIntPmts );
if (array_init(return_value)== FAILURE )
{
php_error( E_ERROR, "fin_table: nie udało się utworzyć tablicy");
}
else
{
for ( nIndex = 0; nindex < n; nIndex++ )
add_next_index_double( return_value, pIntPmts[nIndex]);
}
efree( pIntPmts );
}
/* }}} */
W pierwszych dwóch funkcjach do implementacji rozszerzenia wymagane było napisanie jednie trzech
linii kodu. Pierwsza linia jest deklaracją zmiennej, druga wywołuje wewnętrzną funkcję obliczającą wartość a
trzecia ustawia zwracaną wartość. Ostania funkcja jest utworzona w taki sposób, aby zwracała tablicę PHP.
Wymaga to nieco więcej pracy od dwóch pierwszych funkcji, ale wyniki tej pracy są znaczące.
Implementacja funkcji fin_table() pokazuje kilka technik ważnych dla programowania dla PHP. Po
pierwsze, przydział pamięci jest przeprowadzany za pomocą funkcji emalloc(), natomiast efree() zapewnia, że
PHP wykona normalny proces odzyskiwania nieużytków. W implementacji fin_table() pamięć jest przydzielana
dla tablicy tymczasowej zawierającej wartości wygenerowane przez funkcję C _fin_table(). Po wywołaniu
funkcji wbudowanej, zwracana wartość jest deklarowana przy pomocy wywołania funkcji array_init() jako
tablica PHP. Następnie przy pomocy pętli przebiegającej po kolejnych komórkach tablicy i kolejnych wywołań
funkcji add_next_index_double(), wartości z tablicy C są kopiowane do nowej tablicy PHP. Na koniec
tymczasowa tablica jest niszczona i funkcja się kończy.
Po wbudowaniu tych funkcji w PHP, mogą być one wywoływane identycznie, jak inne wewnętrzne
funkcje PHP. Na wydruku 11.7 pokazujemy przykład użycia nowego rozszerzenia.
Wydruk 11.7. Użycie funkcji finansowych dodanych do PHP
<html>
<head>
<title>Kalkulator kredytowy</title>
</head>
<body>
<?php
if ( $REQUEST_METHOD == 'POST' )
{
print( "Kwota pożyczki: <b>" . number_format( $Amount ) . "</b><br>" );
print( "Oprocentowanie: <b>{$Interest}%</b><br>" );
print( "Czas spłaty: <b>$Term lat</b><br><hr>" );
$aMontlyPayment = fin_mpmt( $Amount, $Interest, $Term );
print("Rata miesięczna: <b>" . number_format( $aMontlyPayment, 2 ) . "</b><br>");
print( "Suma rat: <b>" .
number_format( fin_total( $Amount, $Interest, $Term ), 2 ) . "</b><br><br>" );
$aArray = fin_table( $Amount, $Interest, $Term );
?>
<table border="1">
<tr>
<td>
Rata nr.
</td>
<td>
Podstawa
</td>
<td>
Odsetki
</td>
</tr>
<?php
$nIndex = 1;

Rozdział 11 – Ponowne wykorzystanie kodu 136
foreach( $aArray as $aIntPmt )
{
$aPrinciple = number_format( $aMontlyPayment - $aIntPmt, 2 );
$aIntPmt = number_format( $aIntPmt, 2 );
?>
<tr>
<td>
<?=$nIndex?>
</td>
<td>
<?=$aPrinciple?>
</td>
<td>
<?=$aIntPmt?>
</td>
</tr>
<?php
$nIndex++;
}
?>
</table>
<?php
}
?>
<form action="<?=$PHP_SELF?>" method="post">
<table>
<tr>
<td colspan="2">
To jest prosty kalkulator rat kredytu.
Wprowadź kwotę pożyczki, oprocentowanie i czas spłaty
</td>
</tr>
<tr>
<td>
Kwota:
</td>
<td>
<input type="text" name="Amount">
</td>
</tr>
<tr>
<td>
Oprocentowanie ("7.5" == 7.5%):
</td>
<td>
<input type="text" name="Interest">
</td>
</tr>
<tr>
<td>
Czas spłaty (w latach):
</td>
<td>
<input type="text" name="Term">
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="Submit" value="Wyślij">
</td>
</tr>
</table>
</form>
</body>
</html>
Skrypt ten wyświetla formularz do wprowadzenia danych kredytu, a następnie wysyła je do samego siebie.
Po wywołaniu go poprzez wywołanie HTTP POST, wywoływane są funkcje finansowe i wyświetlane wyniki ich
działania. Na rysunku 11.1. pokazany jest fragment strony będącej wynikiem typowego wykonania programu.

137 PHP – Kompendium wiedzy
Rysunek 11.1.
Wykorzystanie
nowych funkcji
finansowych

Jak wspomniano wcześniej, jeżeli masz dużą bibliotekę kodu C/C++ pochodzącą z istniejących aplikacji i
zamierzasz przenieść je do środowiska WWW, PHP posiada prosty mechanizm integracji istniejącego kodu z
nowymi aplikacjami. Zaletą tego rozwiązania jest możliwość ponownego wykorzystania dobrze przetestowanego
kodu oraz dobra wydajność skompilowanego kodu. Dodatkowo, funkcje napisane w C lub C++ mogą realizować
funkcje, których nie da się napisać wyłącznie w PHP. Na przykład implementacja bezpiecznych gniazd zapewnia
możliwości, które nie mogą być w chwili obecnej zrealizowane przy pomocy funkcji PHP.
Rozważając integrację istniejącego kodu C/C++ z PHP należy wziąć pod uwagę, że koszt integracji może
być wyższy od kosztu przepisania kodu na PHP. Przytoczony wcześniej przykład może być łatwo przepisany na
PHP i zajmie to mniej czasu. Dodatkowo kroki podjęte w czasie integracji muszą być w części powtórzone dla
każdej nowej wersji PHP. Jeżeli zamierzasz zawsze korzystać z najnowszej wersji PHP, powoduje to konieczność
ciągłej konserwacji istniejącej witryny.
Inną możliwością wykorzystania istniejącego kodu C/C++ jest jego skompilowanie i wykonywanie na
serwerze WWW poprzez PHP. Metoda ta zostanie opisana w dalszej części rozdziału.

Java
W rozdziale 9, „Niezależność od przeglądarki” przedstawiony został opis połączenia Javy z PHP.
Możliwość używania klas Javy została wprowadzona w PHP4. Z powodu popularności Javy, dostępne jest wiele
klas i modułów klas Javy, oferowanych przez wielu niezależnych dostawców. Obsługa Javy nie jest włączona
domyślnie do PHP, więc należy przekompilować PHP, aby móc skorzystać z tego potężnego narzędzia.

Dodawanie obsługi Javy w PHP na *niksach
Jeżeli korzystasz z PHP na platformie *nix, musisz przekompilować PHP w celu dodania obsługi Javy. W
podręczniku PHP znajdziemy, że nie można wykorzystać opcji konfiguracji --with-java, jeżeli posiadasz PHP
statycznie włączone w Apache. Opcja ta działa, jeżeli PHP jest uruchamiany jako program CGI lub dynamicznie
włączany moduł Apache. Z powodów bezpieczeństwa nie zaleca się korzystania z PHP w postaci CGI. Jeżeli
serwer Apache nie posiada obsługi dynamicznych modułów, należy go wcześniej przekompilować.

Rozdział 11 – Ponowne wykorzystanie kodu 138
Poniższy skrypt powoduje przekompilowanie Apache tak, aby korzystał z dynamicznie ładowanych
modułów oraz tworzy właściwie skonfigurowany skrypt apxs, który będzie potrzebny do skompilowania PHP. W
skrypcie tym zakładamy, że będzie on uruchomiony z głównego katalogu instalacji Apache.
make clean
./configure --enable-module=so --enable-rule=SHARED_CORE --prefix=/www
make
make install
Po przekompilowaniu Apache można uaktywnić obsługę Javy w PHP za pomocą następującego skryptu.
Zakładamy w nim, że będzie uruchomiony z głównego katalogu instalacji PHP.
make clean
./configure --with-apxs=/www/bin/apxs --with-java ...
make
make install
Opcja --with-java może zawierać ścieżkę oznaczającą katalog instalacji używanej maszyny wirtualnej
Javy. Po zakończeniu kompilacji można sprawdzić konfigurację PHP za pomocą funkcji phpinfo(). Należy
również ustawić kilka opcji konfiguracji Javy w pliku php.ini. Pierwsza jest linia z dołączeniem rozszerzenia
(extension=libphp_java.so). Pozostałe opcje zostaną omówione później.

Dołączanie obsługi Javy w PHP dla Windows
Zamiast kompilowania specjalnej wersji PHP dla Windows, rozszerzenie Javy jest dostępne do pobrania z
www.php.net. Powinieneś sprawdzić która wersja JDK (Java Development Kit) jest zainstalowana na serwerze.
Można to zrobić przy pomocy java -showversion. Należy pobrać odpowiedni plik rozszerzenia i skopiować
php_java.dll do katalogu systemowego. W Windows 95 jest to zwykle \windows\system a Windows NT
\winnt\system32.
Następnie należy uaktualnić plik php.ini. Należy doda linię ładującą rozszerzenie
(extension=php_java.dll). Następnie należy dodać odpowiednio sekcję z opcjami konfiguracji. Są one kluczowe
do prawidłowego działania Javy na każdej platformie.

Opcje konfiguracji Javy
Niezależnie od platformy, jeżeli obsługa Javy jest aktywna w PHP, musisz dodać kilka opcji konfiguracji
do pliku php.ini. W Windows sekcja ta powinna wyglądać podobnie do następującej:
[java]
java.class.path="D:\php4\php_java.jar;D:\PHP4 book\other\RTF2HTML\lib\Scrooge_09b7.jar"
java.home="D:\Program Files\JavaSoft\JRE\1.3"
java.library="D:\Program Files\JavaSoft\JRE\1.3\bin\hotspot\jvm.dll"
W przypadku systemów *nix, sekcja ta jest następująca:
[java]
java.library.path=/usr/lib/kafee:/home/blake/php-4.0.1p12/modules
java.home=/usr/lib/kaffe
java.class.path=/usr/share/kaffe/Klasses.jar:/home/blake/php-
4.0.1.p12/ext/java/php_java.jar:/home/blake/bhawk/lib/bhawk4j.jar:/home/blake/bhawk:/home/blake/java/num
berspeller.jar:/home/blake/java/sax2.jar:/home/blake/java/servlet.jar:/home/blake/java/scrooge.jar
java.library=/www/libexec/libkaffevm.so
Po skonfigurowaniu obsługi Javy, w pliku php.ini musisz podać lokalizację klas Javy lub plików JAR.
Należy to wykonać dla każdej używanej klasy Javy, której chcesz używać. Jak widać na zamieszczonych opcjach
konfiguracji, java.class.path zawiera pełną ścieżkę do plików implementacji.
Tak jak jest to w przypadku każdego języka umożliwiającego tworzenie komponentów, dla Javy dostępne
jest wiele narzędzi, z których można skorzystać za pomocą języka obsługującego API. Jednym z dostępnych
komercyjnie modułów Javy jest konwerter RTH na HTML Scrooge, który można załadować z witryny
www.betabeans.de. Moduł ten posiada prosty interfejs używany do konwertowania plików RTF na standardowy
HTML. Funkcja ta może być wykorzystywana we wielu aplikacjach, w których użytkownicy mogą wysyłać takie
pliki. Ponieważ RTF obsługuje różne czcionki i układy, wykorzystanie RTF pozwala użytkownikowi na
dostarczanie plików bez niebezpieczeństwa bezpośredniego dodawania kodu HTML do witryny. Moduł Scrooge
zawiera przykładowy plik RTF (pokazany na rysunku 11.2.) którego możemy użyć do sprawdzenia siły i
elastyczności modułu.

139 PHP – Kompendium wiedzy
Rysunek 11.2.
Przykładowy plik
RTF modułu
Scrooge

Użycie modułu Scrooge jest łatwe i proste. Dołączona dokumentacja zawiera nazwę klasy Javy, oraz listę
dostępnych metod i właściwości. W skrypcie z wydruku 11.8 pokazane jest wykorzystanie tego modułu.
Wydruk 11.8. Użycie modułu Javy Scrooge
<HTML>
<HEAD>
<TITLE>Konwersja RTF na HTML</TITLE>
</HEAD>
<BODY>
<?php
if ($REQUEST_METHOD == 'POST' )
{
if ( ( $rtffile_type == "text/richtext" ) ||
( $rtffile_type == "application/rtf" ) )
{
// utworzenie obiektu Scrooge
$aR2H = new Java ("de.betabeans.scrooge.Scrooge");
$aArray = file ( $rtffile );

$sR2H->setOptWrapHTML( False );
$aOutput = $aR2H->convert( implode( "", $aArray) );
print( $aOutput );
}
else
{
print ("Wybrany plk nie jest plikiem <b>RTF</b>.<BR>");
}
}
?>
<FORM METHOD="POST" ACTION="<?=$PHP_SELF?>" enctype="multipart/form-data">
Przesyłanie pliku: <INPUT TYPE="file" NAME="rtffile"><br><br>
<INPUT TYPE="submit" NAME="submit" value="Wyślij">
</FORM>
</BODY>
</HTML>
Skrypt ten zawiera formularz przesyłania pliku, za pomocą którego użytkownik może przesłać plik RTF.
Po przesłaniu danych formularza sprawdzany jest typ pliku i jeżeli jest prawidłowy tworzony jest obiekt Scrooge.

Rozdział 11 – Ponowne wykorzystanie kodu 140
Przesłany plik jest odczytywany do tablicy za pomocą funkcji file(), a następnie używając funkcji implode(),
tablica jest konwertowana na ciąg, który jest przekazywany do obiektu Scrooge. Zwracaną wartością jest ciąg
zawierający kod HTML utworzony na podstawie przesłanego pliku. Wynikowy kod HTML jest wysyłany do
przeglądarki. Na rysunku 11.3. przedstawiony jest wynik uzyskany z przykładowego pliku RTF.
Rysunek 11.3. nie potrafię uruchomić tego przykładu
Wynik
przetworzenia
przykładowego
pliku RTF na kod
HTML
Java jest tylko jednym z języków umożliwiających tworzenie komponentów, które można wykorzystać w
środowisku PHP. Następna część opisuje użycie obiektów COM w PHP.

COM
COM jest z natury oparty o Windows, więc dyskusja ta odnosić się będzie do PHP działającego na
serwerze pracującym pod kontrolą systemu Windows. Standardowa instalacja PHP dla Windows posiada obsługę
COM, więc nie jest potrzebna dodatkowa konfiguracja. Implementacja COM w PHP ewoluowała z opartego o
funkcje API w wersji 3, do implementacji obiektowej w PHP 4. Powoduje to, że użycie COM w PHP jest bardzo
naturalne.
W części tej omówimy serwer konwersji walut Cloanto Currency Server, dostępny z witryny
http://cloanto.com. Obiekt ten pozwala na przeliczanie walut pomiędzy sobą i posiada wewnętrzną bazę danych
kursów. Baza ta jest automatycznie uaktualniana, więc można użyć tego modułu w międzynarodowej aplikacji
handlu elektronicznego w celu umożliwienia wyświetlania cen w lokalnej walucie.
Obiekt ten posiada bogaty zestaw metod, ale bardzo łatwo można użyć podstawowych funkcji w
środowisku testowym. Na wydruku 11.9. zamieszczony jest przykład wykorzystania tego obiektu. Rysunki 11.4. i
11.5. pokazują formularz z cenami w dwóch różnych walutach.
Wydruk 11.9. Użycie serwera konwersji walut Cloanto
<html>
<head>
<title>Użycie potoków</title>
</head>
<body>
<?php
$aFreeProg = '/usr/bin/free';
if ( !is_file( $aFreeProg ) )
{
print( "Nie można znaleźć programu na serwerze<br>" );
}
else
{
$aLines = array();
if ( $aProgFile = popen( $aFreeProg . " -t", "r" ) )
{
$nIndex = 0;
$aLines = array();
while ( !feof( $aProgFile ) )
{
$aLine = fgets( $aProgFile, 1024 );
$aLines[$nIndex++] = $aLine;
}
pclose( $aProgFile );

$aCount = count( $aLines );
$aTotal = $aLines[$aCount - 2];
$aArray = split( "[ ]+", $aTotal );
$aTotalK = number_format( $aArray[1] );
$aUsedK = number_format( $aArray[2] );
$aFreeK = number_format( $aArray[3] );
?>
Całkowita ilość dostępnej pamięci <?=$aTotalK?> KB.<br>
Wolna pamięć <?=$aFreeK?> KB.<br>
Użyte <?=$aUsedK?> KB.<br>
<?php
}
else

141 PHP – Kompendium wiedzy
{
print ( "Nie można użyć programu na serwerze<br>" );
}
}
?>
</body>
</html>
Rysunek 11.4.
Przykład użycia
serwera Cloanto,
wyświetlana
waluta: dolary
amerykańskie

Rysunek 11.5.
Przykład użycia
serwera Cloanto,
wyświetlana
waluta: forinty
węgierskie

W skrypcie umieszczonym na wydruku 11.9. utworzona jest prosta tabela cen dla trzech różnych towarów.
Dodatkowo dostępny jest formularz do wyboru lokalnej waluty. Po przesłaniu danych formularza skrypt
wykorzystuje serwer Cloanto do przeliczenia cen na wybraną walutę. Obiekt ten jest używany do uzyskania listy
krajów, współczynników wymiany oraz wykonuje przeliczanie na bieżąco.
Jeżeli tworzona jest aplikacja handlu elektronicznego, przy pomocy tego obiektu można dodać niezwykle
przydatną dla użytkowników funkcję, która eliminuje frustrujące pomyłki w liczeniu cen. Wykorzystując serwer
Cloanto wraz z wykrywaniem typu przeglądarki można przeliczać ceny na lokalną walutę bez potrzeby pytania
użytkownika o jej nazwę.

Rozdział 11 – Ponowne wykorzystanie kodu 142
Z powodu dużej ilości programistów Windows oraz dojrzałości modelu COM, dostępne jest wiele
komponentów dla wszystkich typów projektów. Używając obsługi COM w PHP można z łatwością wykorzystać
istniejący kod we własnych projektach.

Inne metody
Prawdopodobnie posiadasz istniejący kod, którego nie da się wykorzystać przy użyciu żadnej z
przedstawionych metod. Jeżeli tak się stanie, nadal można go wykorzystać w PHP. Jeżeli kod ten jest skryptem
(na przykład skryptem Perla) lub można go skompilować do postaci wykonywalnej, da się go zastosować w
skrypcie.
PHP posiada kilka funkcji służących do uruchamiania programów i skryptów na serwerze. Ponieważ
tematem tego rozdziału jest integracja, najlepszą metodą wykorzystania programów na serwerze będzie
skorzystanie z funkcji popen() do uruchamiania programów i skryptów oraz przechwytywania ich wyników.
Technika ta była przedstawiona w rozdziale 4 „Operacje na plikach”, na przykładzie programu whois, dostępnego
na większości systemów *nix. Na wydruku 11.10. pokazany został przykład odczytywania bieżącej ilości użytej
pamięci w systemach *nix.
Wydruk 11.10. Użycie potoków do integracji PHP z istniejącymi skryptami lub programami
wykonywalnymi
<html>
<head>
<title>Użycie potoków</title>
</head>
<body>
<?php
$aFreeProg = '/usr/bin/free';
if ( !is_file( $aFreeProg ) )
{
print( "Nie można znaleźć programu na serwerze<br>" );
}
else
{
$aLines = array();
if ( $aProgFile = popen( $aFreeProg . " -t", "r" ) )
{
$nIndex = 0;
$aLines = array();
while ( !feof( $aProgFile ) )
{
$aLine = fgets( $aProgFile, 1024 );
$aLines[$nIndex++] = $aLine;
}
pclose( $aProgFile );

$aCount = count( $aLines );
$aTotal = $aLines[$aCount - 2];
$aArray = split( "[ ]+", $aTotal );
$aTotalK = number_format( $aArray[1] );
$aUsedK = number_format( $aArray[2] );
$aFreeK = number_format( $aArray[3] );
?>
Całkowita ilość dostępnej pamięci <?=$aTotalK?> KB.<br>
Wolna pamięć <?=$aFreeK?> KB.<br>
Użyte <?=$aUsedK?> KB.<br>
<?php
}
else
{
print ( "Nie można użyć programu na serwerze<br>" );
}
}
?>
</body>
</html>
Skrypt ten otwiera potok do standardowego programu free. Program zwraca dane na temat ilości
dostępnej pamięci w komputerze. Skrypt otwiera potok, który powoduje uruchomienie programu. Skrypt
odczytuje z potoku kolejne wiersze wyniku i wyświetla je w przeglądarce. W zależności od zwracanych danych
może być niezbędna bardziej zaawansowana analiza, ale podstawowa idea jest ta sama.
Technika ta może być użyta dla każdego programu, który zwraca wyniki na standardowe wyjście. W
przypadku systemów Unix oznacza to, że można w ten sposób użyć nieomal każdej komendy bezpośrednio z
143 PHP – Kompendium wiedzy
PHP. Pozwala to łatwo zrealizować odczytanie statusu systemu, zwracanie danych lub inne operacje. Jeżeli trzeba
przenieść istniejący kod do PHP, pozwala to na szybkie prototypowanie, przed zastosowaniem wcześniej
opisanych metod. Pozwala to na szybsze rozpoczęcie testowania funkcji aplikacji minimalizując ilość
koniecznych prac programistycznych. Należy jednak zaznaczyć, że uruchamianie programów wymaga znacznej
ilości zasobów serwera, co może powodować, że aplikacja będzie powolna i trudna do skalowania.

Podsumowanie
W PHP nie brakuje możliwości ponownego użycia kodu. Planując tworzenie biblioteki kodu w PHP
przeznaczonej do wielokrotnego użytku lub przenosząc istniejący kod do aplikacji WWW, można znaleźć
odpowiednie rozwiązanie. Z powodu olbrzymiej ilości istniejących komponentów (zarówno COM jak i Javy)
może się okazać, że większość projektowanej aplikacji jest już napisana. Używając tych komponentów można
znacznie zmniejszyć czas potrzebny na napisanie programu. Używając ich mądrze, można otrzymać aplikację
łatwiejsza w konserwacji i skalowaniu.

Bibliografia
Steve McConnell, Rapid Development. Seattle: Microsoft Press. 1996.

Rozdział 11 – Ponowne wykorzystanie kodu 144
Rozdział 12. Oddzielanie kodu HTML od
PHP
Wstęp
Przy projektowaniu zwykłych aplikacji zwykle nie bierze się pod uwagę oddzielania tworzenia interfejsu
użytkownika od tworzenia części wykonawczej programu. Jest to spowodowane tym, że standardowe aplikacje
wykorzystują standardowe narzędzia tworzenia interfejsu użytkownika, dostępne dla większości programistów.
Jedynymi fragmentami tworzonymi przez inne osoby są emblematy, rysunki przycisków i podobne elementy
wpływające na graficzny wygląd produktu.
Programowanie dla WWW, pozwala na zastosowanie o wiele bogatszego interfejsu użytkownika i przez to
bardzo często wymaga zatrudnienia projektantów specjalizujących się w tworzeniu strony graficznej aplikacji.
Gdy tworzenie interfejsu i kodu jest rozdzielone pomiędzy zespołami lub osobami, oddzielenie kodu i HTML
staje się naturalne a integracja wyników pracy ważna. Nawet w małych jednoosobowych projektach oddzielenie
HTML od logiki aplikacji powoduje, że konserwacja aplikacji jest prostsza i bardziej efektywna.

Wprowadzenie
Programowanie dla WWW jest często nazywane tworzeniem aplikacji wielowarstwowej, ponieważ
stosowane są tutaj oddzielne logiczne warstwy. Często używanymi nazwami warstw są: warstwa prezentacji,
warstwa aplikacji (biznesowa) oraz warstwa bazy danych. Każda z tych warstw może być fizycznie oddzielona od
drugiej. Najważniejszym zadaniem przy projektowaniu wielowarstwowym jest logiczne oddzielenie warstw a nie
ich fizyczna implementacja.
Głównymi zaletami podejścia wielowarstwowego przy tworzeniu aplikacji WWW są:
• Możliwość przydzielenia zadań osobom najlepiej przygotowanym do ich realizacji. Na przykład: graficy i
projektanci przygotowują stronę graficzną aplikacji, programiści tworzą logikę aplikacji a projektanci baz
danych projektują i uruchamiają infrastrukturę bazy danych.
• Możliwość zmian w warstwie bez potrzeby modyfikacji innych. W praktyce nadal jest to trudne, ale wiele
małych zmian ogranicza się do pojedynczej warstwy.
• Możliwość przeniesienia bądź replikacji określonych warstw na inny sprzęt w celu zapewnienia
skalowania bądź nadmiarowości.
Tak jak w przypadku wszystkich aplikacji, podczas trwania projektu aplikacji dla WWW mogą pojawić
się żądania wprowadzenia zmian. Jeżeli zmiany te są ograniczone do jednej warstwy, możliwe jest, że nie będzie
konieczne ponowne kodowanie. Celem tego rozdziału jest pokazanie sposobów tworzenia aplikacji odpornych na
zmiany w późnych stadiach rozwoju. Dodatkowo oddzielenie interfejsu użytkownika od logiki aplikacji jest jedną
z technik programowania modularnego, które pozwala na łatwiejsze użycie istniejących modułów.
W programowaniu tradycyjnym projekt modularny jest zwykle postrzegany jako tworzenie modułów
kodu, które mogą być używane w dowolnych aplikacjach. W przypadku projektowania dla WWW, moduły mogą
zawierać dane będące częścią interfejsu, np. prawa autorskie lub mogą być modułami kodu.
Modularność więcej wnosi do łatwości utrzymania aplikacji niż strukturalność i jest najważniejszym
czynnikiem zapobiegania konieczności tworzenia poprawek do aplikacji mających za zadanie usuwanie błędów.
Według badań 89% użytkowników kodu zgłaszało poprawienie możliwości utrzymania aplikacji modularnej, a w
rozległych testach programiści osiągali o 15% lepsze wyniki pracując nad programem modularnym niż nad
niemodularnym (McConnell, 1993).
Jak wspomniano w poprzednim rozdziale, tworzenie aplikacji modularnej wymaga dodatkowych prac
projektowych i podjęcia odpowiednich decyzji, ale w efekcie można otrzymać aplikację łatwiejszą do
zrozumienia i konserwacji. W książce A Methodology for Client/Server and Web Application Development Roger
Fournier sugeruje, że wspólne fragmenty lub moduły aplikacji zawsze powinny być najpierw projektowane,
tworzone i testowane a następnie udostępniane dla całej korporacji. Komponenty te powinny zawierać nie tylko
moduły kodu, ale również procedury przechowywane w bazie danych, wyzwalacze i zdalne procedury (Fournier,
1998).
W kolejnych częściach zostanie opisane kilka metod implementacji tych metod. Dodatkowo w tym
rozdziale jak również w rozdziale 14 „Witryny oparte o szablony”, dołączone są kompletne przykłady
zastosowania tych technik. Niektóre przykłady w kolejnych częściach pokazują techniki jakich należy unikać.

Oddzielenie i integracja przy użyciu wbudowanych funkcji
PHP
Ponieważ PHP zawiera bogaty zestaw funkcji i narzędzi, oddzielenie modułów kodu od modułów
interfejsu może być zrealizowane bezpośrednio przy pomocy narzędzi języka. Część ta opisuje kilka sposobów
zrealizowania tego zadania.

Motywacja
Pierwszą motywacją dla oddzielenia elementów HTML od kodu jest umożliwienie ponownego
wykorzystania kodu oraz jego łatwiejszej konserwacji. W wszystkich przykładach umieszczonych do tej pory w
książce, HTML i PHP były wymieszane w celu otrzymania krótkich i prostych przykładów. W przypadku
tworzenia kodu prawdziwego kodu technika ta jest niewygodna i powoduje powstanie trudnych do analizy
skryptów. Dla przykładu, skrypt z wydruku 12.1 zawiera fragment strony WWW ze zintegrowanym kodem PHP i
HTML.
Wydruk 12.1. PHP i HTML w jednym skrypcie
<?php
if ( $aShowForm == True ) {
?>
<p>
<font face="Arial" size="3">
<b>
<?php
print( $aQuestion );
?>
</b>
<form action="response.php3" method="POST">
<?php if (!empty( $UserID )) { ?>
<input type="Hidden" name="UserID" value="<?php print($aUserID );
?>">
<?php } ?>
<?php if ($aQuestionID != -1 ) { ?>
<input type="Hidden" name="QuestionID"
value="<?php print($aQuestionID ); ?>">
<?php } ?>
<ul>
<font face="Arial" size="2">
<!--wyświetl możliwe odpowiedzi-->
<?php
if ( $aQuestionID != -1 )
{
if ($aSortOrd != 0 ) //Sortowanie alfabetyczne
{
$aSQL = "SELECT * FROM Answers WHERE (QuestionID=$aQuestionID)
ORDER BY Text";
}
else
{
$aSQL = "SELECT * FROM Answers WHERE (QuestionID=$aQuestionID)";
}
$aDB->SetSQL( $aSQL );

Rozdział 12 – Oddzielanie kodu HTML od PHP 146
Oprócz tego, że przykład jest niekompletny, Wydruk 12.1 pokazuje jak skomplikowana może stać się
strona HTML z wbudowanym PHP. Nawet pomocą edytorów wyróżniających składnię, zlokalizowanie bloków
kodu może być trudne.
Problemy z utrzymaniem tego typu skryptów wykraczają jednak poza podstawowe problemy z
czytelnością kodu. Równie trudno jest wprowadzać zmiany zarówno do kodu, jak i wyglądu strony bez
wpływania na inne elementy. Na przykład, załóżmy, że projektanci uaktualnią wygląd przycisków nawigacji i
muszą być one umieszczone w witrynie. Odpowiedź na pytanie kto powinien wprowadzić zmiany jest trudna,
ponieważ projektanci mogą nie mieć wystarczająco dużo doświadczenia, aby nie popsuć kodu podczas
wprowadzania zmian, a programiści mogą być zmuszeni uaktualniać fragmenty kodu jedynie w celu zmiany
wyglądu. W obu przypadkach wynikiem są opóźnienia w projekcie. Można tego uniknąć stosując lepsze praktyki
projektowe.
Jeżeli twoja firma zamierza dostarczać wysokiej jakości i łatwe do konserwacji aplikacje WWW,
tworzenie stron za pomocą przedstawionej metody nie powinno być stosowane. Dodatkowo, jeżeli
zainwestowano w projekt interfejsu, nie należy tego marnować tworząc aplikację utrudniającą wprowadzanie
prostych zmian. Teraz zostaną zademonstrowane dostępne w PHP metody integrowania oddzielnych modułów
kodu i projektu.

Implementacja
Najprostsza metodą integracji osobnych modułów jest wykorzystanie funkcji PHP include() lub
require().Metoda ta wymaga umieszczenia elementów projektu HTML w osobnych plikach, które są używane
przez moduły kodu PHP w czasie ich wykonywania. Na przykład na wydruku 12.2 i 12.3 umieszczone są
fragmenty projektu strony rozdzielonej na nagłówek i stopkę. Na wydruku 12.4 pokazano sposób integracji tych
segmentów z dynamicznie tworzonym fragmentem strony.
Wydruk 12.2. Fragment z nagłówkiem HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Nowe książki wydawnictwa Helion</title>
</head>
<body>
<img src="logo.jpg" width="622" height="106" alt="" border="0">
<h1>Nowości wydawnictwa Helion</h1>
Wydruk 12.3. Fragment ze stopką HTML
<br><br><br>
<hr>
<p>
&copy; 2001 Helion. Wszystkie prawa zastrzeżone.
</p>
</body>
</html>
Wydruk 12.4. Skrypt łączący kod z projektem
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Nowe książki wydawnictwa Helion</title>
</head>
<body>
<img src="logo.jpg" width="250" height="68" alt="" border="0">
<h1>Nowości wydawnictwa Helion</h1>
Mimo, jest to bardzo prosty przykład, pokazuje on w jaki sposób można użyć funkcji include() w celu
integracji HTML i kodu, co ułatwia tworzenie efektywnych i łatwych do modyfikacji aplikacji. Na wydruku 12.1
pokazany jest wygląd wynikowej strony w przeglądarce. Przykład ten pokazuje wartość oddzielenia kodu od
HTML. Jeżeli projekt nagłówka lub stopki ulegnie modyfikacji, należy zmienić jedynie pliki HTML.

147 PHP – Kompendium wiedzy
Rysunek 12.1.
Łączenie
HTML i kodu
PHP przy
użyciu
include()

Zamiast funkcji include() lub reqiure(), można również wykorzystać standardowe funkcje obsługi
plików dostępne w PHP w celu odczytania plików HTML i dołączenia ich do strony. Metoda ta pozwala na
większą kontrolę nad obsługą plików, w tym odszukiwanie plików i obsługę błędów. Użycie własnych funkcji
dołączania plików pozwala na to, aby w plikach HTML nie było żadnego kodu PHP.
Kolejne wydruki zawierają bardziej szczegółowy przykład wykorzystania poprzedniej techniki
wykorzystując funkcje obsługi plików zamiast funkcji include(). Dodatkowo użyte zostały kaskadowe arkusze
stylów (CSS) w celu zapewnienia większych możliwości zmiany wyglądu strony. Na wydruku 12.5. i 12.6.
ponownie jest umieszczony nagłówek i stopka, natomiast na wydruku 12.7 znajduje się warstwa logiczna strony.
Dla celów tej demonstracji utworzone zostały dwa osobne pliki CSS.
Wydruk 12.5. Nagłówek HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Nowe książki wydawnictwa Helion</title>
<link rel="STYLESHEET" type="text/css" href="css1.css">
</head>
<body>
<img src="logo.jpg" width="622" height="106" alt="" border="0">
<h1>Nowości wydawnictwa Helion</h1>
Wydruk 12.6. Stopka HTML
<p class="copyright">
&copy; 2001 Helion. Wszystkie prawa zastrzeżone.
</p>
</body>
</html>
Wydruk 12.7. Skrypt aplikacji PHP
<?php
function MyIncludeFile( $aFileName )
{
if ( !is_file( $aFileName ) )
{
// przekierunkowanie na stronę błędu
exit;
}
$aFileArray = file( $aFileName );
foreach( $aFileArray as $aLine )
{
print( "$aLine" );

Rozdział 12 – Oddzielanie kodu HTML od PHP 148
}
}
MyIncludeFile( "./header_2.html" );

include( "./db_mysql.inc" );

// tworzenie klasy news_db class służące do odczytu wiadomości
class news_db extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}

// Tworzenie obiektu klasy news_db i odczytanie wiadomości
$aDB = new news_db;
$aDB->query( "select * from news order by date desc limit 5" );
while( $aDB->next_record() )
{
$aNewsID = $aDB->f( "news_id" );
$aAuthor = $aDB->f( "author" );
$aTitle = $aDB->f( "title" );
$aSynopsis = $aDB->f( "synopsis" );

print( "<h2>$aTitle</h2>" );
print( "<h4>autor: $aAuthor</h4>" );
print( "<p>$aSynopsis</p>" );
print( "<a href=\"full_story.phtml?news_id=$aNewsID\">pełny tekst...</a>" );
print( "<br><br><hr>" );
}

MyIncludeFile( "./footer_2.html" );
?>
Aplikacja ta wykorzystuje małą bazę danych do przechowywania tekstów wiadomości. Tak jak w
poprzednich przykładach, w przykładzie tym wykorzystane są klasy obsługi baz danych PHPLIB. Zawartość i
struktura bazy danych nie jest istotna, ponieważ jest to przykład rozdzielania kodu oraz technik jego integracji.
Dodatkowo ważna jest elastyczność rozwiązania wykorzystującego pliki CSS. Mimo, że w tekście strony PHP
znajduje się kilka podstawowych znaczników HTML, ich użycie jest ograniczone a sposób ich interpretacji jest
kontrolowany przez CSS. Wygląd kompletnej strony jest pokazany na rysunkach 12.2. i 12.3.

149 PHP – Kompendium wiedzy
Rysunek
12.2.
Przykłado
wa
aplikacja
z
pierwszym
arkuszem
stylów

Rozdział 12 – Oddzielanie kodu HTML od PHP 150
Rysunek
12.3.
Przykłado
wa
aplikacja
z
pierwszym
arkuszem
stylów

Czego należy unikać
Korzystając z tej metody należy unikać mieszania kodu programu i HTML. Dołączane pliki HTML
powinny zawierać jedynie kod HTML, a dołączane pliki PHP — jedynie kod PHP. Ułatwia to rozdzielenie
odpowiedzialności programistów PHP i projektantów interfejsu. Należy unikać kodu PHP, który generuje kod
HTML. Choć pozwala to na pisanie kodu PHP nie zawierającego znaczników HTML, powoduje to, że za zmiany
projektu witryny odpowiada programista PHP. Utrudnia to również wprowadzanie hurtowych zmian w wyglądzie
całej witryny WWW.

Podsumowanie: Oddzielanie i integracja przy wykorzystaniu funkcji
PHP
Tworząc mechanizm oddzielania kodu PHP od HTML należy pamiętać o najważniejszych celach takiego
działania. Czasami programiści przesadzają w swoim zapale pisania kodu i tworzą mechanizmy, które nie
spełniają prawdziwych potrzeb. Celem oddzielenia HTML od PHP jest uproszczenie aplikacji, podział
odpowiedzialności pomiędzy projektantami i programistami oraz ułatwiają konserwację aplikacji.
Celem oddzielenia HTML od PHP może być całkowite oddzielenie projektu od logiki aplikacji. Używając
przedstawionych technik i wykorzystując przy projektowaniu pliki CSS, cel ten jest nieomal osiągnięty. Jednak
czasami trzeba dynamicznie wygenerować niektóre znaczniki HTML, na przykład może być to generowany
dynamicznie znacznik łącza. Również jeżeli przesyłany jest identyfikator sesji lub inny identyfikator specyficzny
dla aplikacji, trzeba dynamicznie generować wszystkie łącza. W tych przypadkach jeżeli wykorzystywana będzie
opisana technika integracji, niezbędne będzie dołączenie części znaczników HTML do kodu PHP.

151 PHP – Kompendium wiedzy
W następnej części zatytułowanej „Wykorzystanie systemu szablonów” opisuję szczegóły metody
umożliwiającej całkowite oddzielenie kodu PHP od HTML. Jest to zalecana metoda, ponieważ cały HTML może
być pod opieką projektantów.

Wykorzystanie systemu szablonów
System szablonów jest mechanizmem przeznaczonym do całkowitego oddzielenia elementów projektu
HTML od kodu PHP. Oczywistą zaletą wykorzystania szablonów jest umożliwienie doświadczonym
projektantom HTML na całkowite panowanie nad wszystkimi aspektami projektu. Gdy wszystkie znaczniki
HTML zostaną usunięte z kodu, cała warstwa projektu interfejsu aplikacji może być modyfikowana niezależnie
od logiki aplikacji.
Tak jak przy wszystkich dotychczasowych technikach projektowania, aby osiągnąć zamierzony wynik
wykorzystując szablony, wymagane są dodatkowe prace projektowe oraz skuteczne wprowadzenia ich w życie.
Podstawowym założeniem jest podział strony na jeden lub więcej szablonów projektowych, które są łączone z
dynamiczną zawartością generowaną przez kod PHP. W prostych aplikacjach plik szablonu może być
wykorzystywany dla wszystkich stron witryny. W bardziej skomplikowanych, może być użyte bardzo wiele
szablonów.

FastTemplate
Mimo, że każdy może zbudować własny system szablonów, dostępne jest świetne narzędzie o nazwie
FastNet, które można załadować z witryny www.thewebmasters.net. FastTemplate jest łatwy do użycia i istnieje
specjalna dokumentacja dla początkujących. Aby rozpocząć pracę z FastTemplate należy utworzyć plik szablonu,
zwykle z rozszerzeniem tpl. Następnie w kodzie należy wykonać następujące kroki:
1. Utworzenie obiektu klasy FastTemplate.
2. Użycie metody define() do przydzielenia plikom szablonów unikalnych nazw.
3. Użycie metody assign() do skojarzenia wartości do zmiennych szablonu.
4. Uruchomienie metody parse() do przetworzenia pliki szablonu i przypisania wartości do zmiennych.
5. Użycie metod FastPrint() lub fetch() do wypisania lub odczytania przetworzonego pliku szablonu.
W szablonach FastTemplate zmienne są umieszczane w nawiasach klamrowych ({}). Przykładowy plik
szablonu znajduje się na wydruku 12.8.
Wydruk 12.8. Przykładowy szablon FastTemplate
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>{TITLE}</title>
</head>
<body bgcolor="{BODY_COLOR}">
{BODY}
<hr>
<font size="1">
{COPYRIGHT}
</font>
</body>
</html>
Przykładowy szablon pokazany na wydruku 12.8 zawiera cztery zmienne FastTemplate: TITLE,
BODY_COLOR, BODYi COPYRIGHT. W najprostszym przypadku, zmienne te mogą być ustawione bezpośrednio z kodu,
w sposób pokazany na wydruku 12.9.
Wydruk 12.9. Skrypt aplikacji
<?php
include( "class.FastTemplate.php" );

$aTemplate = new FastTemplate( "." );
$aTemplate->define( array( 'basic' => 'sample_1.tpl' ) );

$aBodyText = "Bardzo krótka zawartość strony. ";
for ( $nIndex = 1; $nIndex <= 10; $nIndex++ )
{
$aBodyText .= "$nIndex ";
}

Rozdział 12 – Oddzielanie kodu HTML od PHP 152
$aTemplate->assign( array( 'TITLE' => 'Prosty przykład',
'BODY_COLOR' => 'white',
'BODY' => $aBodyText,
'COPYRIGHT' => 'Helion 2001, wszystkie prawa zastrzeżone' ) );

$aTemplate->parse( 'PAGE', 'basic' );

$aTemplate->FastPrint( 'PAGE' );
?>
Pierwszym krokiem jest utworzenie obiektu klasy FastTemplate. Konstruktor wymaga jednego argumentu
— ścieżki do pliku szablonu. W naszym przykładzie został wykorzystany bieżący katalog „.”. Następnie plikowi
umieszczonemu na wydruku 12.8 zostaje nadana nazwa basic. W kilku kolejnych liniach generowany i
zapamiętywany jest również test strony oraz wykorzystując metodę assign(), zmiennym FastTemplate
przypisywane są wartości. Na koniec Szablon jest przetwarzany i zapamiętywany w zmiennej PAGE, a następnie jej
zawartość wysyłana jest do przeglądarki. Wynik pokazany jest na rysunku 12.4.
Rysunek 12.4.
Wynik
przetwarzania
prostego szablonu

Po przetworzeniu szablonu przez obiekt FastTemplate, wynik jest zapisany w zmiennej, która może zostać
wydrukowana lub zachowana dla innych celów, lub zmienna ta może być użyta w kolejnym szablonie. Ostatnia
opcja pozwala na zagnieżdżanie plików szablonów. Nowymi plikami są: plik z danymi o prawach autorskich,
oraz główny plik szablonu zamieszczone odpowiednio na wydrukach 10 i 11.
Wydruk 12.10. Szablon z prawami autorskimi (copyright.tpl)
&copy; {YEARS} Helion.
Wszystkie prawa zastrzeżone. Dodatkowe informacje na temat praw autorskich
mozna znaleźć <a href="legal.phtml">tutaj</a>.
Wydruk 12.11. Główny plik szablonu (body.tpl)
<table width="100%">
<tr>
<th colspan="3">
{PAGE_HEADER}
</th>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<td align="center" valign="top">
<!-- Panel nawigacji -->
<a href="index.phtml">początek</a><br><br>
<a href="about.phtml">o nas</a><br><br>
<a href="links.phtml">łącza</a><br><br>
</td>
<td width="90%" valign="top">
<!-- Główna sekcja tekstu -->
{PAGE_CONTENT}
</td>
<td width="125" valign="top">
<!-- Tutaj reklamy i inne -->
</td>
</tr>
</table>
Każdy z dodatkowych plików szablonów, które zostały do tej pory pokazane, zawierają swoje zmienne
FastTemplate i aby szablony działały prawidłowo, muszą im zostać przypisane wartości. Jeżeli nie zostanie
przypisana zmienna FastTemplate, wygenerowane zostaną następujące ostrzeżenia:
[Wed Sep 12 19:42:38 2001] [error] [FastTemplate] Warning: no value found for variable: {PAGE_HEADER}
[Wed Sep 12 19:42:38 2001] [error] [FastTemplate] Warning: no value found for variable: {PAGE_CONTENT}
[Wed Sep 12 19:42:38 2001] [error] [FastTemplate] Warning: no value found for variable: {YEARS}

153 PHP – Kompendium wiedzy
Dodając nowe pliki szablonów należy również zmienić główny plik PHP. Na wydruku 12.12 pokazany
jest nowy skrypt PHP używający zagnieżdżonych szablonów.
Wydruk 12.12. Zagnieżdżone pliki szablonów
<?php
include( "class.FastTemplate.php" );

function GetCurrentYear( )
{
$aNow = getdate();
$aNowYear = $aNow["year"];

return $aNowYear;
}

$aTemplate = new FastTemplate( "." );
$aTemplate->define( array( 'basic' => 'sample_1.tpl',
'copyright' => 'copyright.tpl',
'body' => 'body.tpl' ) );

$aBodyText = "Bardzo krótka zawartość strony. ";
for ( $nIndex = 1; $nIndex <= 10; $nIndex++ )
{
$aBodyText .= "$nIndex ";
}
$aStartYear = 1997;
$aCurrentYear = GetCurrentYear();
$aYears = "$aStartYear";
for ( $nIndex = $aStartYear + 1; $nIndex <= $aCurrentYear; $nIndex++ )
{
$aYears .= ", $nIndex";
}

$aTemplate->assign( array( 'TITLE' => 'Lepszy przykład',
'BODY_COLOR' => 'white',
'YEARS' => $aYears,
'PAGE_HEADER' => 'Lepszy przykład',
'PAGE_CONTENT' => $aBodyText
) );

$aTemplate->parse( 'BODY', 'body' );
$aTemplate->parse( 'COPYRIGHT', 'copyright' );
$aTemplate->parse( 'PAGE', 'basic' );

$aTemplate->FastPrint( 'PAGE' );
?>
W przykładzie tym zdefiniowano dwa dodatkowe pliki szablonów nadając im nazwy copyright i body.
Ponieważ te pliki szablonów zawierają własne zmienne FastTemplate, zmiennym tym należy przypisać wartości.
Wartość zmiennej YEARS jest generowana automatycznie, więc dane o prawach autorskich są zawsze aktualne.
Zmienna $aBodyText posiada identyczną wartość jak w poprzednim przykładzie. Należy zauważyć, że w tym
rozdziale zmienne BODY i COPYRIGHT nie są ustawiane w metodzie assign(). Zamiast tego zmienne te otrzymują
wartości przy wywołaniu metody parse() na końcu tego skryptu. Wynik działania tego skryptu jest pokazany na
rysunku 12.5.

Rozdział 12 – Oddzielanie kodu HTML od PHP 154
Rysunek 12.5.
Wynik działania
zagnieżdżonych
szablonów
FastTemplate

Klasa FastTemplate jest potężnym narzędziem, za pomocą którego można tworzyć bogate i złożone
projekty interfejsu. Aby lepiej zilustrować tą technikę w kolejnym przykładzie wrócimy do przykładu aplikacji
dostarczającej najnowszych wiadomości. Zamiast wykorzystywać do tego celu pliki dołączane, w kodzie tym
wykorzystana zostanie siła klasy FastTemplate.
Na wydrukach 12.13, 12.14 i 12.15 umieszczony jest kod HTML szablonów tworzących podstawowy
układ strony, treść i element wiadomości. W przykładzie tym użyty jest szablon zawierający prawa autorskie,
wykorzystany w poprzednim przykładzie.
Wydruk 12.13. Podstawowy szablon dla aplikacji dostarczającej wiadomości (ft_news_base.tpl)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>{TITLE}</title>
<link rel="STYLESHEET" type="text/css" href="css2.css">
</head>
{NEWS_BODY}
</html>
Wydruk 12.14. Szablon treści w aplikacji wiadomości (ft_news_body.tpl)
<body>
<table width="640" border="0" align="center">
<tr>
<td>
<img src="logo.jpg" width="250" height="68" alt="" border="0">
<h1>Nowości wydawnictwa Helion</h1>
</td>
</tr>
<tr>
<td>
<br><br>
{NEWS_ITEMS}
</td>
</tr>
<tr>
<td>
<br>
<p class="copyright">
{COPYRIGHT}
</p>
</td>
</tr>
</table>
</body>
Wydruk 12.15. Szablon elementu wiadomości (ft_news_item.tpl)
<h2>{NEWS_TITLE}</h2>
<h4>autor: {NEWS_AUTHOR}</h4>
<p>{NEWS_SYNOPSIS}</p>
<a href="{FULL_STORY_URL}">więcej...</a>
<br><br><hr>
Na wydruku 12.16. zamieszczony jest skrypt generujący stronę z nowościami. Skrypt ten jest bardziej
skomplikowany niż w poprzednim przykładzie, ale większość kodu pochodzi z poprzednich przykładów.
155 PHP – Kompendium wiedzy
Wydruk 12.16. Główny skrypt aplikacji
<?php
include( "class.FastTemplate.php" );
include( "./db_mysql.inc" );

error_reporting( E_ALL & ~E_NOTICE );

function GetCurrentYear( )
{
$aNow = getdate();
$aNowYear = $aNow["year"];

return $aNowYear;
}

// Tworzenie klasy news_db do pobrania wiadomości
class news_db extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}

$aTemplate = new FastTemplate( "." );
$aTemplate->define( array( "base" => "ft_news_base.tpl",
"body" => "ft_news_body.tpl",
"item" => "ft_news_item.tpl",
"copyright" => "copyright.tpl" ) );

// generowanie roku dla treści praw autorskich
$aStartYear = 1997;
$aCurrentYear = GetCurrentYear();
$aYears = "$aStartYear";
for ( $nIndex = $aStartYear + 1; $nIndex <= $aCurrentYear; $nIndex++ )
{
$aYears .= ", $nIndex";
}

$aTemplate->assign( array( "YEARS" => $aYears,
"TITLE" => "Nowości wydawnictwa Helion" ) );

// Tworzenie obiektu klasy news_db i generowanie elementów wiadomości
$aDB = new news_db;
$aDB->query( "select * from news order by date desc limit 5" );
while( $aDB->next_record() )
{
$aNewsID = $aDB->f( "news_id" );
$aAuthor = $aDB->f( "author" );
$aTitle = $aDB->f( "title" );
$aSynopsis = $aDB->f( "synopsis" );

$aURL = "full_story.phtml?news_id=$aNewsID";

$aTemplate->assign( array( "NEWS_TITLE" => $aTitle,
"NEWS_AUTHOR" => $aAuthor,
"NEWS_SYNOPSIS" => $aSynopsis,
"FULL_STORY_URL" => $aURL ) );

$aTemplate->parse( "NEWS_ITEMS", ".item" );
}

$aTemplate->parse( "COPYRIGHT", "copyright" );
$aTemplate->parse( "NEWS_BODY", "body" );
$aTemplate->parse( "BASE", "base" );
$aTemplate->FastPrint( "BASE" );
?>
W przykładzie tym funkcja generująca rok dla tekstu o prawach autorskich pochodzi z poprzednich
przykładów, a obsługa bazy danych z poprzedniej realizacji aplikacji wiadomości. Zauważmy, że na początku
skryptu wyłączone zostały ostrzeżenia PHP, ponieważ ostrzeżenie generowane przez FastTemplate zostaną
omówione później.
W skrypcie tym zdefiniowane zostały cztery pliki szablonów. Na początku skryptu inicjowane są dwie
główne zmienne FastTemplate, YEARS i TITLE reprezentujące odpowiednio rok praw autorskich i tytuł strony.
Kolejną główną funkcją skryptu jest dynamiczne dodawanie kolejnych wiadomości. Zostało to
zrealizowane przez pobranie pięciu najnowszych wiadomości z bazy danych i wypisanie ich przy pomocy pętli.
W każdym przebiegu pętli przetwarzany jest szablon item i dodawany do zmiennej FastTemplate NEWS_ITEMS.
Rozdział 12 – Oddzielanie kodu HTML od PHP 156
Dodawanie to jest zrealizowane poprzez dodanie kropki (.) przed nazwą szablonu. Dlatego właśnie w pierwszym
przebiegu pętli FastTemplate generuje ostrzeżenie wskazujące, że zmienna NEWS_ITEMS nie jest zainicjowana. Po
ustawieniu w pętli wartości zmiennej NEWS_ITEMS przetwarzana jest reszta szablonów a następnie drukowana
strona.
Poprzedni przykład pokazuje w jaki sposób można wykorzystać klasę FastTemplate do generowania
powtarzających się elementów, takich jak treść kolejnych wiadomości. Podobna konstrukcja może być
wykorzystana do tworzenia wierszy tablicy lub listy łączy. Po poznaniu podstawowych założeń używanie
FastTemplate staje się niezwykle naturalne.
Oczywistą zaletą klasy FastTemplate jak również innych systemów szablonów jest to, że elementy
projektu strony mogą być tworzone i zmieniane niezależnie od kodu aplikacji. Mimo, że wykorzystanie systemu
szablonów wymaga nieco innego myślenia w trakcie projektowania, jest ono warte zainteresowania.

Zaawansowane techniki użycia FastTemplate
W poprzednich rozdziałach dwa zagadnienia były z rozmysłem odsuwane aż do tego rozdziału. Pierwsze
zostało wspomniane w rozdziale 7. „Sesje i stan aplikacji”, w którym mówiono o lepszych sposobach
przenoszenia identyfikatora sesji w aplikacji. Drugie znalazło się w rozdziale 9. „Niezależność od przeglądarki”,
gdzie znalazła się sugestia, że system szablonów może być alternatywną metodą dostarczania zawartości zależnej
od typu przeglądarki. W tej części skupimy się na tych dwóch zagadnieniach.
Pierwsze zagadnienie, przesyłanie identyfikatora sesji, powinno być w tym momencie dosyć oczywiste.
Wykorzystując szablony projekt może zawierać dynamiczne łącza URL generowane w kodzie PHP. Podstawowy
przykład szablonu nawigacyjnego zamieszczony jest na wydruku 12.17.
Wydruk 12.17. Szablon nawigacji (navi.tpl)
<a href="{HREF_INDEX}">początek</a>
<a href="{HREF_NEWS}">nowości</a>
<a href="{HREF_LINKS}">łącza</a>
Właściwe adresy URL dla łączy mogą być tak generowane, aby zawierały identyfikator sesji, lub inny
specyficzny dla aplikacji. Inną zaletą wykorzystania takich adresów jest łatwa modyfikacja położenia stron w
czasie pracy aplikacji. Na wydruku 12.18 zamieszczony jest skrypt generujący dynamiczne adresy URL dla
poprzedniego szablonu.
Wydruk 12.18. Szablon nawigacji
<?php
include( "class.FastTemplate.php" );

session_start();
function MyGenURL( $aLinkName )
{
switch( $aLinkName )
{
case 'HREF_INDEX' :
$aBaseURL = 'index.phtml';
break;
case 'HREF_NEWS' :
$aBaseURL = 'news.phtml';
break;
case 'HREF_LINKS' :
$aBaseURL = 'links.phtml';
break;
default :
$aBaseURL = 'badlink.phtml';
break;
}
return $aBaseURL . session_name() . "=" . session_id();
}

$aTemplate = new FastTemplate( "." );
$aTemplate->define( array( 'navi' => 'navi.tpl' ) );

$aTemplate->assign( array( 'HREF_INDEX' => MyGenURL( 'HREF_INDEX' ),
'HREF_NEWS' => MyGenURL( 'HREF_NEWS' ),
'HREF_LINKS' => MyGenURL( 'HREF_LINKS' ) ) );

$aTemplate->parse( 'NAVI', 'navi' );
$aTemplate->FastPrint( 'NAVI' );
?>

157 PHP – Kompendium wiedzy
Funkcja MyGenURL() jest głównym elementem skryptu. Pobiera ona symboliczną nazwę łącza i zwraca
prawdziwą lokalizację strony wraz z dołączonymi danymi sesji. Funkcja taka ma dodatkową możliwość obsługi
nieznanych nazw łączy. Pozwala to unikać brzydkich komunikatów „HTTP 404: Page Not Found” spotykanych
we wielu aplikacjach WWW.
Wykorzystanie szablonów do dostarczania treści zależnych od przeglądarki również jest korzystne. W
rozdziale 9. zasugerowane były metody warunkowego dołączania plików lub przekierowania użytkownika do
katalogu odpowiadającego używanej przeglądarce. Używając szablonów preferowana jest pierwsza metoda.
Problemem z przekierowaniem jest to, że użytkownik może wysłać łącze do strony do osoby używającej innej
przeglądarki. Przykład umieszczony na wydrukach 19., 20., 21. i 22. pokazuje w jaki sposób można zintegrować
treści zależne od typu przeglądarki z szablonami. Na wydruku 12.19. pokazany jest główny szablon strony.
Wydruk 12.20. i 12.21. to strony zależne od możliwości przeglądarki, natomiast wydruk 12.22 zawiera skrypt
PHP.
Wydruk 12.19. Podstawowy szablon strony (base_basic.tpl)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>{TITLE}</title>
</head>
<body>
{NAVI}
</body>
</html>
Wydruk 12.20. Menu oparte o Flash (flash_menu.tpl)
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=5,0,0,0"
WIDTH=320 HEIGHT=240>
<PARAM NAME=movie VALUE="flash_menu.swf"> <PARAM NAME=quality VALUE=high> <PARAM NAME=bgcolor
VALUE=#FFFFFF> <EMBED src="Track As Menu.swf" quality=high bgcolor=#FFFFFF WIDTH=320 HEIGHT=240
TYPE="application/x-shockwave-flash"
PLUGINSPAGE="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"></EMBED>
Wydruk 12.21. Menu HTML (html_menu.tpl)
<a href="index.phtml">początek</a>
<a href="news.phtml">wiadomości</a>
<a href="links.phtml">łącza</a>
Wydruk 12.22. Skrypt
<?php
include( "class.FastTemplate.php" );

// zakładamy, że zmienna $aHasFlash jest ustawiana przez
// prawdziwą funkcję wykrywania Flash-a
$aHasFlash = False;
if ( $aHasFlash == True )
{
$aNaviFile = "flash_menu.tpl";
}
else
{
$aNaviFile = "html_menu.tpl";
}

$aTemplate = new FastTemplate( "." );
$aTemplate->define( array( "navi" => $aNaviFile,
"base" => "base_basic.tpl" ) );

$aTemplate->assign(array("TITLE" => "Przykład działania zależnego od przeglądarki"));

$aTemplate->parse( "NAVI", "navi" );
$aTemplate->parse( "BASE", "base" );
$aTemplate->FastPrint( "BASE" );
?>
Przykład ten pokazuje, w jaki sposób można umieszczać na stronie menu zrealizowane w programie
Macromedia FlashTM, gdy używana przeglądarka potrafi je wyświetlić i zwykłe menu HTML w przypadku
korzystania z innych przeglądarek. Oczywiście powinna zostać wykorzystana prawdziwa funkcja wykrywająca,
podobna do tej zamieszczonej na witrynie firmy Macromedia.
W oparciu o specyficzne potrzeby witryny można pomysłowo rozdzielać wiele aspektów projektu w
zależności od możliwości używanej przeglądarki i wykorzystać system szablonów do dostarczenia najlepszej
zawartości dla każdej z przeglądarek.

Rozdział 12 – Oddzielanie kodu HTML od PHP 158
Podsumowanie
Oddzielanie elementów projektu aplikacji WWW od jej logiki powoduje promowanie projektowania
modularnego i dzięki temu zwiększenie możliwości ponownego wykorzystania kodu oraz jego łatwiejszej
konserwacji. Z powodu elastyczności PHP cel modularnego tworzenia witryn WWW może być osiągnięty
wieloma sposobami. Zalecaną w tym rozdziale metodą jest wykorzystanie systemu szablonów, który pozwala na
pełne oddzielenie wszystkich elementów projektu od kodu.
Tak zupełnie na marginesie, rozdział ten ma podłoże czysto osobiste. Jakiś czas temu zdałem sobie
sprawę, że nigdy nie będę projektantem graficznym ani artystą, więc w mojej firmie poświęciłem sporo czasu na
znalezienie sposobu na dołączenie projektów profesjonalnych projektantów z moimi aplikacjami. Pozwala to
osiągnąć ogólną poprawę witryny, ponieważ warstwa prezentacji jest odpowiedniej jakości a ja nie muszę jej
tworzyć ani zmieniać samemu. Wykorzystuję klasę FastTemplate w nieomal wszystkich moich obecnych
projektach. Pozwala ona moim projektantom łatwo stworzyć pliki szablonów stron wykorzystując nawiasy
klamrowe do zaznaczenia elementów i pozwala mi na projektowanie w pełni funkcjonalne oraz szybkie zmiany w
logice aplikacji.

Bibliografia
Roger Fournier. A Mehodology for Client/Server and Web Application Development. New Jersey:
Prentice Hall PTR. 1998.
Steve McConnell. Code Complete. Seattle: Microsoft Press. 1993.

159 PHP – Kompendium wiedzy
Rozdział 13. Fajny PHP
Wstęp
W przypadku języka tak potężnego i rozszerzalnego jak PHP, trudno jest poszufladkować wszystkie fajne
rzeczy, jakie można zrobić przy jego pomocy. Rozdział ten zawiera opis niektórych z nich. Tematy tu omawiane
są albo zagadnieniami, które musiałem kiedyś zaprogramować, albo odpowiedziami na pytania często
pojawiające się na listach dyskusyjnych poświęconym PHP. Rozdział ten nie opisuje oczywiście wszystkich
rozszerzeń i własności PHP, ale opisuje niektóre z nich, które nie zostały opisane w innych częściach książki.

Wysyłanie do przeglądarki plików innych niż HTML
Ogólnie mówiąc, PHP jest wykorzystywany do wysyłania plików HTML do przeglądarki, ale może być
on również użyty do automatyzacji ściągania plików, lub dostarczania innych typów plików do przeglądarki. Na
przykład można umożliwić użytkownikom na zapisanie fragmentu zawartości bazy danych w formacie
tekstowym, na przykład takim jak CSV. Przykład ten ilustruje sposób, w jaki można umożliwić użytkownikom
zapisanie fragmentu tabeli bazy danych. Na wydruku 13.1. umieszczono skrypt pobierający dane z bazy danych,
formatujący je oraz wysyłający je do przeglądarki.
Wydruk 13.1. Wysyłanie CSV do przeglądarki
<?php
include_once( "db_mysql.php" );

// Tworzenie klasy pochodnej po DB_Sql służącej do pobrania danych pracownika
class employee_db extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}

$aSQL = "select * from employees ";

$aDB = new employee_db;
$aDB->query( $aSQL );
/* metoda metadata() bardzo zależy od wersji MySQL
przykład ten może nie działać dobrze na wszystkich wersjach MySQL */
$aMetaData = $aDB->metadata();

$aData = "";

$aNumFields = count( $aMetaData );
for ( $nIndex = 0; $nIndex < $aNumFields; $nIndex++ )
{
$aData .= "\"" . $aMetaData[$nIndex]["name"] . "\",";
}
// usunięcie ostatniego znaku w ciągu (,) i znaku końca linii (\n)
$aData = substr( $aData, 0, strlen( $aData ) - 1 ) . "\n";

while ( $aDB->next_record() )
{
$aLine = "";
for ( $nIndex = 0; $nIndex < $aNumFields; $nIndex++ )
{
$aLine .= "\"" . $aDB->f( $nIndex ) . "\",";
}
// usunięcie ostatniego znaku w ciągu (,) i znaku końca linii (\n)
$aLine = substr( $aLine, 0, strlen( $aLine ) - 1 );
$aLine .= "\n";
$aData .= $aLine;
}

header( "Content-length: " . strlen( $aData ) );
header( "Content-type: application/octetstream" );
header( "Content-disposition: inline; filename=\"employees.csv\"" );

print( $aData );
?>
Pierwszą czynnością realizowaną przez skrypt jest zdefiniowanie klasy pochodnej po klasie DB_Sql
pochodzącej z PHPLIB, która jest używana do uruchomienia zapytania na bazie danych. Więcej informacji na ten
temat znajduje się w rozdziale 6. „Współpraca z bazami danych”. Następnie wykonywane jest zapytanie i
pobierane z niego są metadane. Metadane zawierają ilość pól oraz nazwę każdego ze zwracanych pól. Dane te są
potrzebne do skonstruowania pierwszej linii pliku CSV. Zwykle CSV zawiera wiersz nagłówka zawierający
nazwy pól, a następnie umieszczone są wiersze danych. Każde pole w CSV jest umieszczone w cudzysłowach i
oddzielona są od siebie przecinkami. Każdy wiersz kończy się znakiem nowej linii.
Po dodaniu wiesza nagłówka, skrypt przebiega po kolejnych rekordach wyniku i konstruuje z nich
sformatowane wiersze CSV. Następnie do przeglądarki wysyłane są trzy wiersze nagłówka HTML. Pierwszy
zawiera wielkość wysyłanego pliku, Następna zawiera typ zawartości pliku. Jest ona ważna, ponieważ
przeglądarka wykorzystuje te dane do rozpoznania, w jaki sposób powinna traktować przesyłane dane. Jeżeli linia
ta zawierała by text/html, przeglądarka próbowała by wyświetlić przychodzące dane w postaci HTML. Ponieważ
typem tym jest w tym przypadku application/octetstream, przeglądarka nie próbuje wyświetlić tych danych, a
zamiast tego pozwala zapisać je na dysku. Ostatni wiersz wskazuje przeglądarce, że dane są wysyłane razem z
nagłówkami oraz sugeruje nazwę dla zapisywanego pliku. Na rysunku 13.1. pokazane jest okno dialogowe
wyświetlane przez Internet Explorer po uruchomieniu tego skryptu.
Pierwsze dwie linie tego pliku umieszczone są na wydruku 13.2., natomiast na rysunku 13.2. widać
wygląd tego pliku w programie Microsoft Excel.
Rysunek 13.1.
Okno dialogowe
Zapisz jako

161 PHP – Kompendium wiedzy
Rysunek 13.2. Plik
CSV w Excelu

Wydruk 13.2. Surowy plik CSV
"id","first","last","address","position"
"1","Bob","Smith","Poziomkowa 16, Miastko","Szef marketingu"
Innym częstym zastosowaniem przesyłania zawartości innej niż HTML do przeglądarki, jest wysyłanie
plików graficznych. Na przykład możesz mieć aplikację, która zapisuje małe rysunki w bazie danych. Następnie
można przy pomocy PHP zapisywać je oraz pobierać i wyświetlać. Poniższy wydruk pokazuje w jaki sposób
można pobierać rysunki poprzez formularz HTML, zapisywać je w bazie danych, a następnie wyświetlać je na
innej stronie. Na wydruku 13.3. znajduje się formularz HTML używany do przesyłania rysunków, natomiast
wydruk 13.4. zawiera skrypt zapisujący rysunki do bazy danych. Tabela MySQL używana do przechowywania
rysunków zdefiniowana jest następująco:
CREATE TABLE pictures (
picture_id int(11) DEFAULT '0' NOT NULL,
name varchar(30) NOT NULL,
date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
pic_data mediumblob NOT NULL,
pic_size int(11) DEFAULT '0' NOT NULL,
pic_type varchar(30) NOT NULL,
PRIMARY KEY (picture_id)
);
Wydruk 13.3. Formularz przesyłania plików
<html>
<head>
<title>Przesyłanie rysunków</title>
</head>
<body>
<form action="upload_pic.phtml" method="post" enctype="multipart/form-data">
<table>
<tr>
<td colspan="2">
Proszę wybrać plik z rysunkiem (jpeg lub gif)
do przesłania, oraz podać jego nazwę.
</td>
</tr>
<tr>
<td>
Plik rysunku:
</td>
<td>
<input type="file" name="PicFile">
</td>
</tr>
<tr>
<td>
Nazwa rysunku:
</td>
<td>
<input type="text" name="PicName" maxlength="30">

Rozdział 13 – Fajny PHP 162
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="Submit" value="Wyślij">
</td>
</tr>
</table>
</form>
</body>
</html>
Wydruk 13.4. Zapisywanie przesłanego pliku w bazie danych
<?php
include_once( "db_mysql.php" );

// Utworzenie podklasy DB_Sql do zapisywania i odczytu rysunków
class pictures_db extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}

$aErrors = False;
if ( !empty( $PicFile_name ) ) // nie wybrano pliku
{
if ( ( $PicFile_type == "image/gif" ) ||
( $PicFile_type == "image/pjpeg" ) ||
( $PicFile_type == "image/jpeg" ) )
{
$aFile = fopen( $PicFile, "rb" );
$aFileContents = addslashes( fread( $aFile, filesize( $PicFile ) ) );
fclose( $aFile );

$aDB = new pictures_db();

$aSQL = "select ( max( picture_id ) + 1 ) as new_id from pictures";
$aDB->query( $aSQL );
if ( $aDB->next_record() )
{
$aNewID = $aDB->f( "new_id" );
}
if ( empty( $aNewID ) == True )
{
$aNewID = 1;
}

$aSQL = "insert into pictures ( picture_id, name, date, pic_data, ";
$aSQL .= "pic_size, pic_type ) values ( $aNewID, '$PicName', ";
$aSQL .= "NOW(), '$aFileContents', '$PicFile_size', '$PicFile_type' )";
print( $aSQL );
$aDB->query( $aSQL );
if ( $aDB->Errno != 0 )
{
$aErrors = True;
}
}
else
{
$aErrors = True;
}
}
else
{
$aErrors = True;
}

if ( $aErrors == False )
{
header( "Location: upload_ok.html\n" );
}
else
{
header( "Location: upload_failed.html\n" );
}
?>
Skrypt ten ponownie wykorzystuje klasę PHPLIB do obsługi odwołań do bazy danych. Na początku
sprawdzane jest, czy plik jest właściwego typu. Następnie plik jest umieszczony w zmiennej i przygotowany do

163 PHP – Kompendium wiedzy
umieszczenia w bazie danych poprzez użycie funkcji addslashes(). Następnie z tabeli pobierany jest nowy
identyfikator i dane są umieszczane w bazie danych. Na końcu skryptu przeglądarka jest kierowana do
odpowiedniego pliku w zależności od tego, czy operacja się powiodła czy nie.
Aby wyświetlić rysunek, wykorzystujemy kod z wydruków 5. i 6. Na wydruku 13.5. umieszczona jest
prosta strona HTML powodująca wyświetlenie jednego, określonego rysunku. Wydruk 13.6. zawiera skrypt
wyświetlający rysunki.
Wydruk 13.5. Strona HTML powodująca wyświetlenie rysunku z bazy danych
<html>
<head>
<title>Wyświetlenie rysunku</title>
</head>

<body>

<img src="show_pic.phtml?ID=1" border="0" alt="">

</body>
</html>
Wydruk 13.6. Skrypt wyświetlający rysunki
<?php
include_once( "db_mysql.php" );

// Utworzenie podklasy DB_Sql do zapisywania i odczytu rysunków
class pictures_db extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}

$aDB = new pictures_db();

$aSQL = "select * from pictures where ( picture_id = $ID )";
$aDB->query( $aSQL );
if ( $aDB->next_record() )
{
header( "Content-length: " . $aDB->f( "pic_size" ) );
header( "Content-type: " . $aDB->f( "pic_type" ) );
print( $aDB->f( "pic_data" ) );
}
else
{
// Nie znaleziony rysunek, obsługa błędu!
Header( "HTTP/1.0 404 Not Found" );
}
?>
Mimo, że dziwny wydaje się znacznik <IMG> wskazujący na skrypt PHP, nie ma jednak żadnej różnicy.
Ważny kod znajduje się w skrypcie PHP, gdzie ustawiany jest właściwy typ zawartości dla rysunku z bazy
danych. Skrypt powoduje pobranie odpowiedniego rysunku z bazy danych i przesłanie danych. Jeżeli
identyfikator rysunku ($ID) nie zostanie odnaleziony w bazie danych, skrypt zwraca standardowy kod błędu
HTTP 404.
Ponieważ PHP pozwala na wysłanie dowolnych nagłówków HTTP, można w ten sposób przesyłać
dowolną zawartość. Elastyczność ta pozwala na łatwe tworzenie aplikacji o dużych możliwościach.

Skrypty automatyzujące
PHP nie jest jedynie językiem programowania dla WWW, który do działania wymaga serwera WWW, ale
jest on językiem skryptowym, który może zostać do dowolnych zadań programowych. Ponieważ jest on tak
bogaty w funkcje, może być wykorzystany do automatyzacji zadań, które mogą być trudne do zrealizowania w
standardowych językach programowania powłoki lub plikach wsadowych. Dodatkowo, ponieważ PHP jest
dostępny na wielu platformach, te same skrypty mogą być użyte na wielu platformach.
Wykorzystanie PHP jako osobnego narzędzia skryptowego wymaga skompilowania wersji CGI PHP. Jest
to opsane w rozdziale 1., „Kompilacja i instalowanie PHP 4”. Mając dostępną wersję CGI można uruchomić
dowolny skrypt PHP z linii poleceń. Poniższy przykład pokazuje wykorzystanie PHP do generowania plików stref
DNS co jest przydatne w przypadku obsługi wielu stref.
Rozdział 13 – Fajny PHP 164
Wydruk 13.7. Baza do obsługi DNS
CREATE TABLE Domains (
domain_id int(11) NOT NULL,
name varchar(250) NOT NULL,
soa_server_id int(11) DEFAULT '1' NOT NULL,
cname_list varchar(250) NOT NULL,
mail_server_id int(11) DEFAULT '1' NOT NULL,
ip_address_id int(11) DEFAULT '1' NOT NULL,
incl_zone_file tinyint(4) DEFAULT '1' NOT NULL,
created_date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
PRIMARY KEY (domain_id),
KEY name (name),
UNIQUE name_2 (name),
KEY created_date (created_date)
);
CREATE TABLE IPAddressess (
ip_address_id int(11) NOT NULL,
value carchar(15) NOT NULL,
PRIMARY KEY (ip_address_id),
KEY value (value)
);
CREATE TABLE MailServers (
mail_server_id int(11) NOT NULL,
name varchar(250) NOT NULL,
PRIMARY KEY (mail_server_id),
KEY name(name)
);
CREATE TABLE NameServers (
name_server_id int(11) NOT NULL,
name varchar(250) NOT NULL,
PRIMARY KEY (name_server_id),
KEY name(name)
);
CREATE TABLE SOAServers (
soa_server_id int(11) NOT NULL,
name varchar(250) NOT NULL,
PRIMARY KEY (soa_server_id),
KEY name(name)
);
Baza danych jest wykorzystywana do przechowywania danych niezbędnych do utworzenia plików strefy
oraz innych plików konfiguracyjnych niezbędnych do działania serwera DNS. Poniżej pokazany jest jeden rekord
z tabeli Domains:
INSERT INTO Domains VALUES( '4', 'intechra.net', '1', 'www, secure, mail', '1', '1', '1', '2000-08-25
13:29:37');
Wiersz ten zawiera dane DNS na temat domeny intechra.net. Chociaż rekord ten nie jest sam szczególnie
użyteczny, to jednak połączony z innymi powiązanymi tabelami pozwala uzyskać informacje na temat adresów
IP, serwerów pocztowych oraz serwerów SOA. Używając wszystkich tych danych oraz skryptu PHP można
niezmiernie uprościć proces uaktualniania serwera DNS. Poniższe siedem wydruków zawiera elementy głównego
skryptu. Wydruki 8. do 13. są plikami szablonów używanych do generowania plików wynikowych. Przykład ten
korzysta z klasy FastTemplate opisanej w rozdziale 12. „Oddzielanie kodu HTML od PHP”.
Wydruk 13.8. Główny szablon dla pliku strefy DNS (dns_primary.tpl)
$TTL 86400
{DOMAIN}. IN SOA {SOA_SERVER}. {ADMINISTRATOR}. (
{SERIAL} ; nr seryjny
10800 ; odswieżanie
3600 ; ponowna próba
604800 ; wygaśnięcie
86400 ; domyślny TTL
)
{NAMESERVERS}
{DOMAIN}. IN A {IPADDRESS}
{CNAME_RECORDS}
{DOMAIN}. IN MX 10 {MAIL_SERVER}.
{DOMAIN}. LOC 43 49 57.551 N 111 46 38.071 W 1480.7m
Wydruk 13.9. Szablon nazw hostów DNS (zastępuje CNAME_RECORDS z wydruku 13.8.)
(dns_secondary.tpl)
{CNAME} IN CNAME {DOMAIN}.{CRLF}
Wydruk 13.10. Szablon serwerów nazw DNS (zastępuje NAMESERVERS z wydruku 13.8.)
(dns_nservers.tpl)
{DOMAIN}. IN NS {NAMESERVER}.{CRLF}
Wydruk 13.11. Główny szablon dla pliku named.conf (named_primary.tpl)
165 PHP – Kompendium wiedzy
acl trustedslaves { ns1.nameserver.com;ns2.nameserver.com; };
options {
directory "/var/named";
recursion yes;
fetch-glue no;
allow-query { any; };
};
zone "." { type hint; file "cache.db"; };
{ZONES}
Wydruk 13.12. Pomocniczy szablon dla pliku named.conf (zastępuje ZONES z wydruku 13.11.)
(named_secondary.tpl)
zone "{DOMAIN}" { type master; file "{DOMAIN_FILE}"; notify yes;
allow-transfer { trustedslaves; }; };
Wydruk 13.13. Szablon dla pliku podrzędnych DNS (named_slaves.tpl)
zone "{DOMAIN}" { type slave; file "{DOMAIN_FILE}"; masters {
master.com; }; };
Szablony te tworzą szkielet niezbędny do utworzenia wszystkich plików konfiguracyjnych dla serwera
nazw BIND. Pierwsze trzy generują osobne pliki stref, które mogą posiadać różną liczbę serwerów nazw i
definicji nazw komputerów. Pozostałe są używane do utworzenia pliku named.conf oraz pliku podrzędnego,
wykorzystywanego w podrzędnym serwerze nazw.
Skrypt pokazany na wydruku 13.14 odczytuje z bazy danych dane DNS i generuje wszystkie pliki
konfiguracyjne.
Wydruk 13.14, Skrypt DNS
<?php
include( "./class.FastTemplate.php" );
include( "./db_mysql.php" ); // db_mysql.inc o zmienionej nazwie
// tworzenie klasy obsługi bazy danych dla aplikacji
class genapps_db extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}
// katalog dla plików wynikowych
$aPath = "./dns_output";

$tpl = new FastTemplate( "." );
$tpl->define( array( named_main => "named_primary.tpl",
named_zones => "named_secondary.tpl",
named_slaves => "named_slaves.tpl",
dns_main => "dns_primary.tpl",
dns_cnames => "dns_secondary.tpl",
dns_nservers => "dns_nservers.tpl" ));
// pobierz listę serwerów nazw i zapamiętaj do późniejszego wykorzystania
$aNSDB = new genapps_db();
$aSQL = "select * from NameServers";
$aNSDB->query( $aSQL ); // pobierz wszystkie dane strefy
$aDB = new genapps_db();
$aSQL = "select A.name, A.cname_list, A.incl_zone_file, B.name as soa_server,
C.name as ";
$aSQL .= "mail_server, D.value as ip_address from Domains A, SOAServers B, ";
$aSQL .= "MailServers C, IPAddressess D where ( A.soa_server_id = B.soa_server_id)";
$aSQL .= "and ( A.mail_server_id = C.mail_server_id ) and ";
$aSQL .= "( A.ip_address_id = D.ip_address_id )";
$aDB->query( $aSQL );

while ( $aDB->next_record() )
{
$aDomainName = strtolower( $aDB->f( "name" ) );
$aCNames = $aDB->f( "cname_list" );
$aSoaServer = $aDB->f( "soa_server" ) ;
$aMailServer = $aDB->f( "mail_server" );
$aIPAddress = $aDB->f( "ip_address" );
$aInclZoneFile = $aDB->f( "incl_zone_file" );
$tpl->assign( array( DOMAIN => $aDomainName,
ADMINISTRATOR => "admin." . $aSoaServer,
SERIAL => date( "Ymd" ) . "00",
MAIL_SERVER => $aMailServer,
SOA_SERVER => $aSoaServer,
IPADDRESS => $aIPAddress,
CRLF => "\n" ) );
/* nazwy hostów (rekordy CNAME) są przechowywane w liście
rozdzielanej przecinkami w bazie danyche. Dzielenie listy
i tworzenie linii dla każdego elementu */
$tpl->clear( CNAME_RECORDS );

Rozdział 13 – Fajny PHP 166
$aCNameList = explode( ",", $aCNames );
foreach( $aCNameList as $aCName )
{
$aCName = trim( $aCName );
$tpl->assign( array( CNAME => $aCName ) );
$tpl->parse( CNAME_RECORDS, ".dns_cnames");
}
// dodanie linii serwerów nazw do pliku strefy
$tpl->clear( NAMESERVERS );
$aNSDB->seek( 0 ) ;
while ( $aNSDB->next_record() )
{
$tpl->assign( array( NAMESERVER => $aNSDB->f( "name" )) );
$tpl->parse( NAMESERVERS, ".dns_nservers" );
}
$aDomainFile = $aDomainName . ".db";
/* plik strefy jest tworzony tylko wtedy, gdy pole 'incl_zone_file'
w bazie danych ma wartość '!'
*/
if ( $aInclZoneFile == "1" )
{
$tpl->parse( DNS_MAIN, "dns_main");
$aFile = fopen( "$aPath/$aDomainFile", "w" );
fwrite( $aFile, $tpl->fetch( DNS_MAIN ) );
fclose( $aFile );
print ("Plik domeny '$aDomainFile' został utworzony\n" );
}
/* dodanie biżącej nazwy domeny do głównego i pomocniczego pliku
konfiguracyjnego */
$tpl->assign( array( DOMAIN_FILE => $aDomainFile ) );
$tpl->parse( ZONES, '.named_zones' );
$tpl->parse( SLAVES, '.named_slaves' );
}
$tpl->parse( NAMED_CONF, 'named_main' );
$aFile = fopen( "$aPath/named.conf", "w" );
fwrite( $aFile, $tpl->fetch( NAMED_CONF ) );
fclose( $aFile );
print( "Główny plik 'named.conf' utworzony\n" );
$aFile = fopen( "$aPath/named.slave", "w" );
fwrite( $aFile, $tpl->fetch( SLAVES ) );
fclose( $aFile );
print( "Plik domeny 'named.slave' utworzony\n" );
?>
Skrypt ten odczytuje kolejne pozycje w tabeli Domains i tworzy plik strefy. Jest on utworzony za pomocą
szablonów z wydruków 8., 9. i 10. Przykładowy plik strefy pokazany jest na rysunku 13.4.

167 PHP – Kompendium wiedzy
Rysunek 13.4.
Przykładowy plik
strefy DNS

Dodatkowo tworzony jest odpowiednio sformatowany plik named.conf. W celu stworzenia tych plików do
odczytania zapamiętanego tekstu wykorzystana została metoda FastTemplate fetch().
Aby uruchomić ten skrypt pod Windows lub na systemach Unix należy użyć polecenia php
create_dns.php. Postęp wykonywania skryptu jest pokazywany na standardowym wyjściu, a pliki wynikowe są
tworzone w katalogu określonym na początku skryptu.
Ponieważ PHP jest kompletnym językiem skryptowym, przy jego pomocy można napisać dowolne
narzędzia automatyzujące złożone zadania. Użyty z mechanizmami automatycznego uruchamiania programów, na
przykład cron, PHP może być wykorzystany do wykonania wielu złożonych zadań. Tworzenie plików
konfiguracyjnych dla dowolnych programów może być bardzo łatwo wykonane przy użyciu systemu szablonów,
na przykład pakietu FastTemplate. Ponieważ PHP jest niezwykle elastyczny, może być on użyty do napisania
skryptów do takich zadań jak: wysyłanie masowej poczty, składowanie i uaktualnianie bazy danych,
monitorowanie sieci lub aplikacji i wiele innych.

WDDX
WDDX (Web Distributed Data Exchange) to bezpłatna, oparta na XML technologia pozwalająca
aplikacjom WWW działających na dowolnych platformach, łatwo wymieniać pomiędzy sobą dane. Obsługa
WDDX może być uaktywniona w PHP poprzez podanie opcji konfiguracji --enable-wddx i ponowne
skompilowanie PHP. Celem WDDX jest zapewnienie spójnego interfejsu danych dla informacji przekazywanych
pomiędzy aplikacjami sieciowymi. Dla przykładu, można wykorzystać WDDX do współdzielenia przez
partnerów danych z bazy danych.
SDK dla WDDX można ściągnąć z witryny www.wddx.org. Pakiet ten zawiera dokumentację i przykłady
użycia WDDX. Patrząc na najbardziej podstawowym poziomie, kompilując PHP z obsługą WDDX umożliwia się
serializację danych w postaci pakietów WDDX oraz deserializację pakietów danych WDDX do struktur danych

Rozdział 13 – Fajny PHP 168
PHP. Aby to zilustrować, skrypt z wydruku 13.15. tworzy kilka zmiennych i serializuje je w pakiet danych
WDDX, oraz wypisuje zawartość pakietu. Wynik serializacji pokazany jest na wydruku 13.16.
Wydruk 13.15. Wykorzystanie WDDX
<html>
<head>
<title>Przykład użycia WDDX</title>
</head>
<body>
<?php
$aFirstName = "Arian";
$aAge = 25;
$aArray = array( "red", "green", "blue" );
$aPacketID = wddx_packet_start( "products" );
wddx_add_vars( $aPacketID, "aFirstName" );
wddx_add_vars( $aPacketID, "aAge" );
wddx_add_vars( $aPacketID, "aArray" );
$aWDDXPacket = wddx_packet_end( $aPacketID );
print( $aWDDXPacket );
?>
</body>
</html>
Wydruk 13.16. Pakiet WDDX
<wddxPacket version='1.0'><header><comment>products</comment></header><data><struct><var
name='aFirstName'><string>Arian</string></var><var name='aAge'><number>25</number></var><var
name='aArray'><array
length='3'><string>red</string><string>green</string><string>blue</string></array></var></struct></data></wd
dxPacket>
W przedstawionym przykładzie funkcja wddx_packet_start() tworzy nowy pakiet WDDX. Następnie do
tego pakietu dodawane są trzy zmienne PHP i pakiet jest zamykany. Pakiet danych WDDX zawiera wszystkie
dane niezbędne do odtworzenia zmiennych za pomocą funkcji wddx_deserialize().
Aby pokazać jak można wykorzystać WDDX w systemie używającym kilku języków programowania, w
kolejnym przykładzie pakiet WDDX jest wysyłany do strony WWW, gdzie jest używany przez JavaScript. Dane
WDDX reprezentują informacje o towarach: nazwę, cenę i wagę. Gdy użytkownik wybierze z listy nazwę
produktu, w polach tekstowych tylko do odczytu uaktualniana jest cena i waga wybranego produktu. Przykład ten
pokazuje w jaki sposób można manipulować danymi na kliencie przy pomocy JavaScript. Na wydruku 13.17.
pokazany jest szablon HTML, natomiast wydruk 13.18. zawiera skrypt PHP wykorzystujący ten szablon.
Wydruk 13.17 Użycie WDDX do manipulacji danymi na kliencie
<html>
<head>
<title>{TITLE}</title>
<link rel="STYLESHEET" type="text/css" href="css2.css">
<!--- Dołączenie obsługi WDDX / Javascript --->
<SCRIPT SRC="wddx.js" LANGUAGE="JavaScript"></SCRIPT>
<!--- Dołączenie obsługi obiektu WddxDeserializer --->
<SCRIPT SRC="wddxDes.js" LANGUAGE="JavaScript"></SCRIPT>
<script language="JavaScript">
<!--
function show_props(obj, obj_name)
{
var result = "";
for (var i in obj)
{
result += obj_name + "." + i + " = " + obj[i] + "\n";
}
return result;
}

function SetupProductsList()
{
MyDeser = new WddxDeserializer;
aProducts = MyDeser.deserialize( '{PRODUCTS_WDDX}' );
}

function Initialize()
{
SetupProductsList();
aNumProds = aProducts.ARECORDS.length;
// Czyszczenie listy produktów

// dodanie produktu do listy
for ( var nIndex = 0; nIndex < aNumProds; nIndex++ )
{
aValue = aProducts.ARECORDS[nIndex].PRODUCT_ID;
aName = aProducts.ARECORDS[nIndex].DESCRIPTION;

169 PHP – Kompendium wiedzy
// tworzenie nowej opcji
NewOpt = new Option( aName, aValue, false, true );
// Dodanie nowego obiektu do listy SELECT
document.MainForm.Product_List.options[nIndex] = NewOpt;
}

SetInfo();
}

function SetInfo()
{
// ustawienie ceny i wagi zaznaczonego produktu
var RowNum = document.MainForm.Product_List.selectedIndex;

if ( RowNum > -1 )
{
document.MainForm.Price.value = aProducts.ARECORDS[RowNum].PRICE;
document.MainForm.Weight.value = aProducts.ARECORDS[RowNum].WEIGHT;
}
}
//-->
</script>
</head>
<body onload="Initialize()">
<form action="" name="MainForm" id="MainForm">
<table>
<tr>
<td colspan="3">
Wybierz towar z listy, aby zobaczyć jego cenę i wagę.
</td>
</tr>
<tr>
<td colspan="3">
&nbsp;
</td>
</tr>
<tr>
<th>
Towar
</th>
<th>
Cena
</th>
<th>
Waga
</th>
</tr>
<tr>
<td>
<select name="Product_List" size="1" onChange="SetInfo();">
<option></option>
<option></option>
</select>
</td>
<td>
<input type="text" name="Price" readonly>
</td>
<td>
<input type="text" name="Weight" readonly>
</td>
</tr>
</table>
</form>
</body>
</html>
Szablon ten intensywnie wykorzystuje JavaScript. Pierwsza funkcja, show_props() jest jedynie funkcją
testującą używaną przy wyświetlaniu właściwości obiektu. Funkcja SetupProductsList() deserializuje dane
towarów, które zostały odczytane z bazy danych przez PHP. Funkcja korzysta z obiektu WddxDeserializer
dostępnego w SDK dla WDDX. Po deserializacji pakietu danych WDDX obiekt JavaScript aProducts zawiera
wszystkie dane produktów.
Funkcja Initialize() inicjuje stronę i jest wywoływana automatycznie przez zdarzenie strony onLoad. Ta
funkcja z kolei wywołuje funkcję SetupProductsList() a następnie dodaje nazwy i identyfikatory produktów do
listy rozwijalnej. Funkcja SetInfo() jest wywoływana po zmianie przez użytkownika wybranego elementu w
liście rozwijalnej. Uaktualnia ona wartości wyświetlane w polach tekstowych cena i waga.
Wydruk 13.18. Przygotowanie pakietu danych WDDX
<?php

Rozdział 13 – Fajny PHP 170
include( "class.FastTemplate.php" );
include( "db_mysql.inc" );

error_reporting( E_ALL & ~E_NOTICE );

class products_db extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}

$aTemplate = new FastTemplate( "." );
$aTemplate->define( array( "base" => "products_wddx.tpl" ) );
$aTemplate->assign( array( "TITLE" => "Products Page" ) );

$aPacketID = wddx_packet_start( "products" );

$aRecords = array();
$nIndex = 0;

$aDB = new products_db;
$aDB->query( "select * from Products" );
while( $aDB->next_record() )
{
$aRecord = $aDB->Record;

foreach( $aRecord as $aName => $aValue )
{
if ( !is_numeric( $aName ) )
{
$aRecords[$nIndex][$aName] = $aValue;
}
}
$nIndex++;
}
wddx_add_vars( $aPacketID, "aRecords" );
$aWDDXPacket = wddx_packet_end( $aPacketID );

$aTemplate->assign( array( "PRODUCTS_WDDX" => addslashes( $aWDDXPacket ) ) );

$aTemplate->parse( "BASE", "base" );
$aTemplate->FastPrint( "BASE" );
?>
Główny skrypt PHP wykorzystuje klasę FastTemplate w celu utworzenia kompletnej strony HTML.
Głównym działaniem skryptu jest odczytanie zawartości tabeli Products i utworzenie pakietu WDDX
zawierającego odpowiednie dane, Jest to realizowane w pętli przebiegającej przez rekordy wyniku, dodając dane
do nowej tablicy, $aRecords. Jest to tablica dwuwymiarowa, gdzie pierwszy wymiar jest numeryczną
reprezentacją numeru wiersza. Drugi wymiar to tablica asocjacyjna z nazwami kolumn i ich wartościami.
Jeszcze jeden fragment tego kodu wymaga skomentowania. Linia z funkcją is_numeric() jest używana do
usuwania nadmiarowych informacji zwracanej przez niejawnie wykonywaną funkcję mysql_fetch_array().
Funkcja mysql_fetch_array() zwraca wiersz z wyniku w postaci tablicy. Dane pochodzące z wyniku są
przechowywane pod indeksami numerycznymi, jak również pod indeksami asocjacyjnymi gdzie jako klucze są
wykorzystywane nazwy pól. Sprawdzenie to usuwa dane indeksowane liczbami, pozostawiając jedynie dane
asocjacyjne.
Na rysunku 13.5. pokazana jest wynikowa strona w przeglądarce. Zawsze po wybraniu nowego towaru
automatycznie jest uaktualniana cena i waga.

171 PHP – Kompendium wiedzy
Rysunek 13.5.
Przykład
wykorzystania
danych WDDX na
kliencie

Choć przykład ten pokazuje w wykorzystanie WDDX do dostarczenia danych dla klienta, może być
używany na wiele sposobów. Może być używany do przesyłania firmowych danych pomiędzy serwerami, oraz
łatwego współdzielenia danych pomiędzy różnymi platformami i językami. Jeżeli planujesz współdzielenie
danych, WDDX zawiera narzędzia do szybkiego dostarczania treści w postaci neutralnej dla platformy i języka.

Monitorowanie sieci
Ponieważ PHP wewnętrznie obsługuje gniazda i protokoły sieciowe, tworzenie narzędzi monitorujących
sieć jest łatwe. Poniższy przykład wykorzystuje potoki i gniazda do zrealizowania prostych funkcji
monitorujących sieć. Można rozszerzyć ten przykład o sprawdzanie stanu serwera nazw, serwera pocztowego itd.
Przykład ten może być szkieletem, który można rozszerzać o nowe funkcje.
Na wydruku 13.19. zamieszczone są trzy funkcje, phpPing(), phpTrace() i phpPageCheck(). Pierwsze dwie
wykorzystują komendy systemowe do wykonania operacji ping i traceroute. Ostatnia wykorzystuje gniazda do
wysłania żądania HTTP HEAD do strony w celu sprawdzenia dostępności serwera i samej strony.
Wydruk 13.19. Funkcje sieciowe
<?php
$aPingCmd = '/bin/ping -c 4'; // *nix
$aTraceCmd = '/usr/sbin/traceroute -n'; // *nix

// zwraca średni czas wykonania komendy ping
function phpPing( $aAddress )
{
global $aPingCmd;

$aTotalTime = 0.0;
$aPingCount = 0;
if ( $aFile = popen( "$aPingCmd $aAddress", "r" ) )
{
// odczytanie danych z potoku
while ( !feof( $aFile ) )
{
$aLine = fgets ( $aFile, 1024 );
// odszukanie danych o czasie
$aPos = strpos( $aLine, "time=" );
if ( $aPos > 0 )
{
// wykorzystanie zmienności typów zmiennych PHP
// do konwersji czasu na liczbę
$aTime = substr( $aLine, $aPos + 5 ) * 1.0;
$aTotalTime += $aTime;
$aPingCount++;
}
}
pclose( $aFile );
}
return $aTotalTime / $aPingCount;
}

Rozdział 13 – Fajny PHP 172
function phpTrace( $aAddress )
{
global $aTraceCmd;

$aTraceResults = "";
if ( $aFile = popen( "$aTraceCmd $aAddress", "r" ) )
{
// odczytanie wszystkich danych z potoku
while ( !feof( $aFile ) )
{
$aLine = fgets ( $aFile, 1024 );
$aTraceResults .= $aLine . "<br>";
}
pclose( $aFile );
}

return $aTraceResults;
}

function phpPageCheck( $aWebPage )
{
$aURL = parse_url( $aWebPage );
$aResult = False;
if ( $aURL["scheme"] == "http" )
{
$aRequest = "HEAD {$aURL['path']} HTTP/1.0\r\n\r\n";
$aSocket = fsockopen( $aURL["host"], 80 );
if ( $aSocket )
{
fputs( $aSocket, $aRequest );
while( !feof( $aSocket ) )
{
$aLine = fgets( $aSocket, 1024 );
if ( substr( $aLine, 0, 4 ) == "HTTP" )
{
$aArray = explode( " ", $aLine );
if ( ( $aArray[1] >= 200 ) && ( $aArray[1] < 300 ) )
{
$aResult = True;
}
}
}
}
}

return $aResult;
}
?>
Funkcje te mogą być wykorzystane w skrypcie automatyzującym do okresowego zapisywania wyników
do bazy danych lub do pliku, albo mogą być użyte bezpośrednio ze strony WWW. Skrypt z wydruku 13.20.
pokazuje w jaki sposób używa się tych funkcji.
Wydruk 13.20. Wykorzystanie funkcji sieciowych
<?php
include( "./net_funcs.php" );
?>
<html>
<head>
<title>Test funkcji sieciowych</title>
</head>

<body>

<?php
print( "phpPing: " . phpPing ( 'www.php.net' ) . "<br><br>" );
print( "phpTrace: <ul>" . phptrace( 'www.php.net' ) . "</ul><br>" );
print( "phpPageCheck: " );
print( phpPageCheck( 'http://www.php.net/' ) ? "OK" : "NOT OK" );
print( "<br>" );
?>

</body>
</html>
Przykład ten pokazuje jak łatwo można wykorzystać PHP do wykonania podstawowego monitorowania
sieci. PHP obsługuje również inne protokoły sieciowe, takie jak IMAP, SNMP, NNTP i POP3, co pozwala
rozszerzać przytoczony przykład o sprawdzenie dostępności wszystkich rodzajów serwerów i komponentów
sieciowych.

173 PHP – Kompendium wiedzy
Podsumowanie
Rozdział ten opisywał różne zagadnienia pokazujące siłę i elastyczność PHP. Istnieją również rozszerzenia
do tworzenia rysunków, analizy XML, tworzenia plików PDF i wielu innych zadań. Ponieważ PHP jest tak
rozszerzalny i rozwijany przez ogromną grupę programistów, należy spodziewać się dalszego zwiększania
elastyczności i funkcjonalności.

Rozdział 13 – Fajny PHP 174
Rozdział 14. Witryny oparte o szablony
W rozdziale 12., „Oddzielanie HTML od PHP” zostało opisane użycie systemu szablonów. W tym
rozdziale zostanie szczegółowo opisane wykorzystanie szablonów do tworzenia witryn. Użycie szablonów do
projektowania aplikacji umożliwia o wiele więcej, niż jedynie oddzielanie logiki aplikacji od projektu
graficznego. Szablony umożliwiają zastosowanie zapożyczania fragmentów witryn, personalizacji, niezależności
od przeglądarki oraz obsługi wielu języków. Przykłady przytoczone w tym rozdziale wykorzystują klasę
FastTemplate dostępną z witryny http://www.thewebmasters.net/. Istnieją również inne systemy szablonów, ale ta
implementacja jest wydajna, elastyczna i łatwa do nauki.

Podstawy wykorzystania szablonów
Tworzenie witryn WWW korzystających z systemu szablonów wymaga nieco dokładniejszego
projektowania interfejsu użytkownika, niż tworzenie tej samej aplikacji bez szablonów. Jednak korzyści
wykorzystania dobrze zaprojektowanego zaczną się ujawniać bardzo szybko. Na rysunku 14.1. pokazana jest
typowa strona WWW, która może być podzielona na kilka osobnych plików szablonów.
Rysunek 14.1. Page header - nagłówek strony
Strona WWW navigation - panel nawigacji
składająca się z footer - stopka
kilku plików HTML base page - Podstawowa strona HTML
szablonów Main body - Główny obszar strony
information, links, contacts, news - Informacje, łącza,
kontakty, nowości
Na rysunku 14.1. pokazana jest strona składająca się z jednego lub więcej szablonów, które składają się na
stronę HTML z różnymi logicznymi sekcjami strony. Na niektórych witrynach WWW może być wykorzystany
tylko jeden szablon dla wszystkich stron witryny. W innych, każda strona może być tworzona na podstawie kilku
szablonów. Aby zaprojektować witrynę korzystającą z szablonów, należy ocenić potrzeby witryny i
zinwentaryzować elementy znajdujące się na stronach.
Przykładem będzie witryna pełniąca funkcję sieciowego katalogu towarów. Katalog ten jest podzielony na
kategorie produktów, takie jak: ubrania, prezenty, zabawki itd. Każda strona musi zawierać wspólne elementy
nawigacyjne oraz logo całej witryny. Przeglądając wybraną kategorię produktów, powinien być wyświetlany
element graficzny oznaczający tą kategorię. Każda strona produktu powinna zawierać dane o towarze, takie jak
cena, waga, dostępne kolory itd. Aby stworzyć system szablonów dla takiej witryny zdefiniowano następujące
szablony:
1. merch_base.tpl: Podstawowy plik zawierający ogólny układ HTML.
2. merch_header.tpl: Nagłówek wspólny dla wszystkich stron witryny.
3. merch_catXXX_header.tpl: Nagłówek określonej kategorii (XXX zastąpione przez nazwę kategorii).
4. merch_navi.tpl: Panel nawigacyjny katalogu.
5. merch_body.tpl: Treść każdej strony.
6. merch_footer.tpl: Stopka każdej strony.
Aby pokazać jak zostały stworzone te pliki, przedstawiona zostaną teraz zawartość każdego z nich.
Wydruki od 1. do 6 zawierają kolejne pliki wymienione powyżej. Na wydruku 14.3 pokazany został plik
merch_catubrania_header.tpl, właściwy dla kategorii produktów ubrania. Pliki dla pozostałych kategorii nie
zostały pokazane, ponieważ są one właściwie takie same.
Wydruk 14.1. merch_base.tpl
<html>
<head>
<title>{TITLE}</title>
</head>
<body bgcolor="White">
<table width="630" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td colspan="2">{PAGE_HEADER}</td>
</tr>
<tr>
<td colspan="2" align="center">{CAT_HEADER}</td>
</tr>
<tr>
<td valign="top">{LEFT_NAVI}</td>
<td valign="top">
{BODY}
</td>
</tr>
<tr>
<td colspan="2">
{PAGE_FOOTER}
</td>
</tr>
</table>
</body>
</html>
Wydruk 14.2. merch_header.tpl
<img src="merch_layout_r1_c1.gif" width="630" height="61" border="0">
Wydruk 14.3. merch_catubrania_header.tpl
<img name="merch_layout_r2_c1" src="merch_layout_r2_c1.gif" width="630" height="37" border="0"><br>
<div align="center"><p>{CATEGORY_SPECIALS}</p></div>
Wydruk 14.4. merch_navi.tpl
<img src="merch_layout_r3_c1.gif" width="104" height="382" border="0" usemap="#merch_layout_r3_c1">
<map name="merch_layout_r3_c1">
<area shape="rect" coords="5,180,97,221" href="{HREF_COMPANY_INFO}" >
<area shape="rect" coords="7,143,86,166" href="{HREF_CONTACT}" >
<area shape="rect" coords="12,90,84,132" href="{HREF_CART}" >
<area shape="rect" coords="18,56,73,80" href="{HREF_HOME}" >
</map>
Wydruk 14.5. merch_body.tpl
<p>
&nbsp;
</p>
<h3>
{PRODUCT_NAME}
</h3>
<p>
{PRODUCT_DESCRIPTION}
</p>
<p>
{PRODUCT_PRICE}
</p>
Wydruk 14.6. merch_footer.tpl
<hr>
<p>
&copy {COPYRIGHT_YEARS} Intechra LLC. Wszystkie prawa zastrzeżone.
</p>
Pliki te pokazują jak niewiele potrzeba do stworzenia dosyć skomplikowanej witryny korzystającej z
szablonów. Niektóre z tych plików, na przykład nagłówek zawiera jedynie rysunek. Inne, jak na przykład szablon
panelu nawigacyjnego zawiera zarówno rysunek, jak i mapę obrazu. Należy zwrócić uwagę, że szablon
nawigacyjny nie zawiera aktualnych adresów URL, a jedynie zmienne szablonu Pozwala to na stworzenie
właściwych adresów łączy, na przykład zawierających identyfikator sesji. Skrypt PHP pokazany na wydruku
14.7. pokazuje w jaki sposób można połączyć pliki szablonów w jedną całość.
Wydruk 14.7. Łączenie szablonów
<?php
include( "class.FastTemplate.php" );

// zakładamy, że wybraną kategorią są ubrania
$aCategoryHeader = 'merch_catubrania_header.tpl';

$aTPL = new FastTemplate( "." );
$aTPL->define( array( 'base' => 'merch_base2.tpl',
'header' => 'merch_header.tpl',
'navi' => 'merch_navi.tpl',
'footer' => 'merch_footer.tpl',
'cat_header' => $aCategoryHeader,
'body' => 'merch_body.tpl'

Rozdział 14 – Witryny oparte o szablony 176
) );

$aTPL->assign( array( 'TITLE' => 'Katalog towarów: Ubrania',
'CATEGORY_SPECIALS' => 'Sprzedajemy koszulki Intechra!',
'PRODUCT_NAME' => 'Koszulka Intechra',
'PRODUCT_DESCRIPTION' => 'Świetna koszulka z logo Intechra LLC!',
'PRODUCT_PRICE' => '14.95 zł',
'COPYRIGHT_YEARS' => '2001',
'HREF_HOME' => 'index.phtml',
'HREF_CART' => 'cart.phtml',
'HREF_CONTACT' => 'contact.phtml',
'HREF_COMPANY_INFO' => 'company.phtml'
) );

$aTPL->parse( 'PAGE_HEADER', 'header' );
$aTPL->parse( 'CAT_HEADER', 'cat_header' );
$aTPL->parse( 'LEFT_NAVI', 'navi' );
$aTPL->parse( 'BODY', 'body' );
$aTPL->parse( 'PAGE_FOOTER', 'footer' );
$aTPL->parse( 'BASE', 'base' );
$aTPL->FastPrint( 'BASE' );
?>
Skrypt ten wykorzystuje klasę FastTemplate do analizy i połączenia wszystkich plików szablonów
tworzących katalog produktów. W przykładzie tym wszystkie wartości zostały na stałe zaszyte w skrypcie w celu
uproszczenia opisu. W prawdziwej aplikacji dane na temat kategorii produktu oraz wyświetlanego produktu
powinny być dostarczone poprzez formularz lub inną metodę dynamicznego dostarczania danych. Skrypt ten po
prostu przypisuje wartości do wszystkich zmiennych potrzebnych we wszystkich plikach szablonów.
Aby zrozumieć w jaki sposób FastTemplate analizuje stronę należy wiedzieć, że niektóre zmienne
FastTemplate są ustawiane przy użyciu metody assign(). Na przykład zmienna COPYRIGHT_YEARS
wykorzystywana w szablonie merch_footer.tpl jest inicjowana wartością 2000 przy użyciu metody assign().
Dodatkowo niektóre zmienne FastTemplate są ustawiane za pomocą metody parse(). Dla przykładu zmienna
PAGE_HEADER jest ustawiana poprzez analizę strony o nazwie header. Powoduje to, że wartość PAGE_HEADER jest już
dostępna w czasie analizy pliku merch_base.tpl, który w naszym przykładzie został nazwany base. Należy
pamiętać, że w przypadku zagłębiania szablonów należy zainicjować wszystkie wymagane zmienne szablonu
przed jego analizą. Dodatkowo należy analizować szablony we właściwej kolejności. Dla przykładu, jeżeli w
powyższym przykładzie strona base była by analizowana na początku, większość wymaganych zmiennych (takich
jak PAGE_HEADER i BODY) nie było by dostępnych.
Siłą zastosowania szablonów jest możliwość łatwego wprowadzania zmian w projekcie graficznym
witryny. Na rysunku 14.2. pokazany został wygląd strony wygenerowanej przez skrypt z wydruku 14.6.
Zmieniając szablon o nazwie base, z zamieszczonego na wydruku 14.1. na ten z wydruku 14.8, wygląd strony
ulega całkowitej zmianie. Efekt zmiany szablonu base pokazany jest na rysunku 14.3.

177 PHP – Kompendium wiedzy
Rysunek 14.2.
Strona
wygenerowana
przez skrypt z
wydruku 14.7.

Wydruk 14.8. Nowy plik szablonu „base”
<html>
<head>
<title>{TITLE}</title>
<link rel="STYLESHEET" type="text/css" href="new_base.css">
</head>
<body bgcolor="White">
<table width="630" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td colspan="2">{PAGE_HEADER}</td>
</tr>
<tr>
<td colspan="2" align="center">{CAT_HEADER}</td>
</tr>
<tr>
<td width="526" valign="top">
{BODY}
</td>
<td valign="top">{LEFT_NAVI}</td>
</tr>
<tr>
<td colspan="2">
{PAGE_FOOTER}
</td>
</tr>
</table>
</body>
</html>

Rozdział 14 – Witryny oparte o szablony 178
Rysunek 14.3.
Strona
wygenerowana
przy użyciu
nowego szablonu
„base”

Siła systemu szablonów nie może być przeceniana. Tak jak to zostało pokazane, zmieniając zawartość
szablonu base, znacznie zmieniony został wygląd strony, natomiast nie zostały wprowadzone żadne zmiany po
stronie PHP. Elastyczność ta pozwala projektantom na tworzenie i konserwację bogatego interfejsu użytkownika,
natomiast programiści aplikacji równolegle tworzą i konserwują część logiczną. Oczywiści zachodzi niezbędna
interakcja pomiędzy programistami i projektantami interfejsu, ale po zdefiniowaniu zestawu plików szablonów i
zmiennych szablonów praca obu grup może pracować równolegle. Dodatkowo, wykorzystanie szablonów
pozwala programistom na wykorzystywanie zestawu prototypowych plików interfejsu, do czasu aż projektanci
dostarczą im ostateczne wersje.
W poprzednim przykładzie jedyną znacząca zmianą wprowadzoną w szablonie base było dołączenie pliku
CSS. CSS jest wartościowym dodatkiem do większości zastosowań WWW, ponieważ stanowi on system
szablonów dla HTML. Wykorzystując CSS, można zmienić atrybuty wszystkich elementów HTML. Na przykład
można tak zdefiniować znacznik <h3>, aby w kontekście tej witryny wyglądał w określony sposób. Na wydrukach
14.4. i 14.5. pokazany jest wygląd komercyjnego edytora CSS o nazwie TopStyle z firmy Bradbury Software
LLC (http://www.bradsoft.com/). Edytor ten upraszcza proces tworzenia plików CSS oraz pozwala na podgląd
zmienionych stylów.

179 PHP – Kompendium wiedzy
Rysunek 14.4.
Arkusz stylu
pokazujący
możliwość
modyfikacji
znaczników
<body>, <td> i
<h3>

Rysunek 14.5.
Inny arkusz stylu
pokazujący
możliwość
modyfikacji
znaczników
<body>, <td> i
<h3>

Użycie CSS wraz z systemem szablonów zwiększa możliwość wprowadzania zmian do wyglądu witryny
minimalizując konieczność wprowadzania zmian do kodu aplikacji. Pliki CSS mogą być nawet dołączane
dynamicznie, w postaci zmiennej szablonu. Mimo, że nie jest to optymalne rozwiązanie dla wszystkich typów
witryn, pozwala zrealizować kolejny poziom konfiguracji wyglądu tworzonej aplikacji.

Rozdział 14 – Witryny oparte o szablony 180
Poprzedni przykład stanowi podstawowy szkielet dla tworzenia aplikacji WWW opartej o szablony.
Jednak nie zostały tu pokazane przykłady tworzenia powtarzających się elementów. Często zachodzi potrzeba
stworzenia tabeli zawierającej wszystkie towary, lub listę kategorii zapisanych w bazie danych.
Kolejny przykład pokazuje, w jaki sposób można dołączyć powtarzające się elementy, korzystając z klasy
FastTemplate. Wydruk 14.9. zawiera główny plik szablonu. Wydruk 14.10. to zawartość szablonu items,
natomiast wydruk 14.11. szablon pojedynczego elementu. Na wydruku 14.12 znajduje się skrypt łączący te
szablony w całość.
Wydruk 14.9. Główny szablon kategorii
<html>
<head>
<title>{TITLE}</title>
</head>
<body>
{ITEMS}
</body>
</html>
Wydruk 14.10. Szablon kategorii ‘items’
Do wyboru są następujące kategorie produktów:
<ul>
{ITEM_LIST}
</ul>
Wydruk 14.11. Szablon dla pojedynczej kategorii
<li><a href="show_category.phtml?cat_id={CAT_ID}">{CAT_NAME}</a></li>
Wydruk 14.12. Skrypt generujący stronę z listą kategorii
<?php
include( "class.FastTemplate.php" );

$aTPL = new FastTemplate( "." );
$aTPL->define( array( 'base' => 'cat_base.tpl',
'items' => 'cat_items.tpl',
'item' => 'cat_item.tpl' ) );

$aCategories = array( "ubrania", "prezenty", "zabawki", "książki" );

foreach( $aCategories as $aID => $aName )
{
$aTPL->assign( array( 'CAT_ID' => $aID,
'CAT_NAME' => $aName ) );

// analiza elementu i jego dołączenie do zmiennej szablonu
// ITEM_LIST
$aTPL->parse( 'ITEM_LIST', '.item' );
}

$aTPL->assign( array( 'TITLE' => 'Lista kategorii' ) );
$aTPL->parse( 'ITEMS', 'items' );
$aTPL->parse( 'BASE', 'base' );
$aTPL->FastPrint( 'BASE' );
?>
Rysunek 14.6. Lista
kategorii

Przedstawiony przykład zawiera wbudowaną listę kategorii w celu wygenerowania strony z listą kategorii.
Wynik działania skryptu przedstawiony jest na rysunku 14.6. Po raz kolejny załóżmy, że dział projektowy
zdecydował się na zmianę formatu listy kategorii z listy wypunktowanej na tabelę. Zmiany są ograniczone jedynie
do plików items i item. Wykorzystując poprzedni przykład zmienione szablony przedstawione są na wydrukach
13. i 14. Efekt końcowy pokazany jest na rysunku 14.7.
181 PHP – Kompendium wiedzy
Wydruk 14.13. Nowy szablon „items”
Do wyboru są następujące kategorie produktów:
<br><br>
<table border="1">
{ITEM_LIST}
</table>
Wydruk 14.14. Nowy szablon „item”
<tr>
<td>
Kategoria nr. {CAT_ID}
</td>
<td>
<a href="show_category.phtml?cat_id={CAT_ID}">{CAT_NAME}</a>
</td>
</tr>
Rysunek 14.7. Lista
kategorii w postaci
tabeli

Przedstawiony przykład pokazuje podstawowe kroki potrzebne do generowania listy powtarzających się
elementów przy użyciu FastTemplate. Istnieje również w FastTemplate inny mechanizm pozwalający na
wyeliminowanie dodatkowych plików zawierających szablon pojedynczego elementu. Aby użyć tego
mechanizmu zmienimy szablon items, oraz główny skrypt PHP. Na wydrukach 15. i 16. zamieszczone są
zmienione pliki. Wynik działania tego skryptu jest taki, jak pokazany na rysunku 14.7.
Wydruk 14.15. Nowy szablon „items” korzystający z dynamicznych bloków
Do wyboru są następujące kategorie produktów:
<br><br>
<table border="1">
<!-- BEGIN DYNAMIC BLOCK: item -->
<tr>
<td>
Kategoria nr. {CAT_ID}
</td>
<td>
<a href="show_category.phtml?cat_id={CAT_ID}">{CAT_NAME}</a>
</td>
</tr>
<!-- END DYNAMIC BLOCK: item -->
</table>
W szablonie tym został zdefiniowany podszablon — blok dynamiczny o nazwie item. Jest to dokładnie to
samo, co stworzenie osobnego pliku zawierającego szablon item. Zaletą takiego rozwiązania jest utrzymanie
oryginalnej struktury pliku HTML oraz ograniczenie ilości niezbędnych plików szablonów. Użycie szablonów
wymaga również kilku zmian w skrypcie używającym klasy FastTemplate. Zostały one zamieszczone na wydruku
14.16.
Wydruk 14.16. Nowy skrypt PHP
<?php
include( "class.FastTemplate.php" );

$aTPL = new FastTemplate( "." );
$aTPL->define( array( 'base' => 'cat_base.tpl',
'items' => 'cat_items_dyn.tpl') );

Rozdział 14 – Witryny oparte o szablony 182
$aTPL->define_dynamic( 'item', 'items' );

$aCategories = array( "ubrania", "prezenty", "zabawki", "książki" );

foreach( $aCategories as $aID => $aName )
{
$aTPL->assign( array( 'CAT_ID' => $aID,
'CAT_NAME' => $aName ) );

// analiza elementu i jego dołączenie do zmiennej szablonu
// ITEM_LIST
$aTPL->parse( 'ITEM_LIST', '.item' );
}

$aTPL->assign( array( 'TITLE' => 'Lista kategorii' ) );
$aTPL->parse( 'ITEMS', 'items' );
$aTPL->parse( 'BASE', 'base' );
$aTPL->FastPrint( 'BASE' );
?>
W skrypcie tym widoczne są dwie wyraźne zmiany w stosunku do wydruku 14.12. Po pierwsze, w nowym
skrypcie brakuje jednego wywołania metody define(). Po drugie, wykorzystana jest metoda FastTemplate
define_dynamic(), która wskazuje systemowi FastTemplate, że w szablonie items istnieje blok dynamiczny o
nazwie item. Od tej chwili FastTemplate traktuje blok dynamiczny identycznie, jak byłby to osobny plik.
Korzystając z tego mechanizmu, niezmiernie ważne jest, aby blok dynamiczny był poprawny składniowo.
Składnia linii BEGIN i END musi być poprawna i wymagane jest zachowanie odpowiedniej wielkości liter. Blok
kodu zaczyna się od nowej linii tekstu przeznaczonej jedynie dla tej dyrektywy. W linii zawierającej wyrażenia
BEGIN i END nie powinno być żadnego innego tekstu, można jedynie umieszczać tam dowolną ilość znaków
odstępu. Dyrektywa musi być napisana dokładnie w takiej postaci, jak poniższa linia kodu. Linia ta musi być
dokładnie taka, jak przedstawiona, z dokładnością do odstępów pomiędzy znakami. To samo obowiązuje dla
dyrektywy END. Linie BEGIN i END nie mogą rozciągać się na większa ilość linii.
<!-- BEGIN DYNAMIC BLOCK: nazwa_bloku -->
Wszystkie te przykłady tworzą szkielet aplikacji WWW korzystających z szablonów. Następna część tego
rozdziału zawiera kilka przykładów scenariuszy stosowanych w prawdziwych aplikacjach.

Zapożyczanie
Zapożyczanie jest bardzo łatwo realizowalne za pomocą witryny opartej o szablony. Zapożyczanie
witryny to wykorzystanie projektu witryny partnerskiej jako podstawy własnej aplikacji. Dla przedstawianego
wcześniej przykładu katalogu produktów, jest możliwe aby kilka witryn dystrybutorów korzystających z
własnego projektu graficznego używało katalogu jako jednej z dostępnych usług. Istnieje kilka sposobów
zrealizowania takiego scenariusza w PHP, ale wykorzystując szablony można zrobić to bardzo szybko.
Tworzenie zapożyczonej witryn jest w zasadzie identyczne, jak tworzenie innych witryn opartych o
szablony. Ponieważ aplikacja opiera się na interfejsie z innej firmy, integracja i testowanie musi być
przeprowadzone przez obie strony, aby upewnić się, że wszystkie funkcje działają tak, jak to zostało
zaplanowane. Tworząc aplikację, która może być zapożyczana, należy zdecydować na ile konfigurowalna
powinna być taka witryna. W niektórych przypadkach partnerzy mogą umieścić dodatkowe informacje o prawach
autorskich, żądać zmian w terminologii itd. W prostych przypadkach możesz dodać jedynie kilka znaków
firmowych.
Aby zilustrować to zagadnienie, poniższe pliki szablonów są wykorzystywane w aplikacji przedstawionej
w poprzedniej części. W tym scenariuszu zmienione zostały jedynie dane o prawach autorskich oraz szablon
bazowy. Wydruki 17. i 18. zawierają stopkę z prawami autorskimi oraz plik bazowy.
Wydruk 14.17. Szablon partnera z opisem prawa autorskich
<hr>
<p>
Niektóre fragmenty witryny pochodzą z firmy Keen Partner Company. &copy 2000
&copy {COPYRIGHT_YEARS} Intechra LLC. Wszystkie prawa zastrzeżone.
</p>
Wydruk 14.18. Bazowy szablon partnera
<html>
<head>
<title>{TITLE}</title>
<link rel="STYLESHEET" type="text/css" href="new_base.css">

183 PHP – Kompendium wiedzy
</head>
<body bgcolor="White">
<table width="630" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
Firma Keen Partner Company wykorzystuje katalog produktów firmy Intechra LLC.
<td colspan="2">{PAGE_HEADER}</td>
</tr>
<tr>
<td colspan="2" align="center">{CAT_HEADER}</td>
</tr>
<tr>
<td width="526" valign="top">
{BODY}
</td>
<td valign="top">{LEFT_NAVI}</td>
</tr>
<tr>
<td colspan="2">
{PAGE_FOOTER}
</td>
</tr>
</table>
</body>
</html>
Zmodyfikowany został również główny skrypt łączący szablony a skrypt tworzący stronę wynikową jest
również tak zmieniony, aby rozpoznał właściwy wygląd witryny na podstawie nazwy partnera. Na przykład
główna witryna jest dostępna poprzez adres http://www.katalog.com/, natomiast witryna partnera poprzez
http://cobrand.katalog.com/. Nazwy te są oczywiście używane jedynie do testowania i nie muszą być to docelowe
nazwy witryny. Po uruchomieniu głównego skryptu sprawdzana jest nazwa witryny i wyświetlana jest
odpowiednia strona. Na wydruku 14.19. pokazany jest taki skrypt.
Wydruk 14.19. Główny skrypt realizujący zapożyczanie
<?php
include( "class.FastTemplate.php" );

$aHostArray = explode( ".", $HTTP_HOST );
$aPartner = $aHostArray[0];
switch ( $aPartner )
{
case "cobrand" :
$aPartnerBase = "partner_base.tpl";
$aPartnerFooter = "partner_footer.tpl";
break;
default :
$aPartnerBase = "merch_base2.tpl";
$aPartnerFooter = "merch_footer.tpl";
break;
}

// Zakładamy, że wybraną kategorią są ubrania
$aCategoryHeader = 'merch_catubrania_header.tpl';

$aTPL = new FastTemplate( "." );
$aTPL->define( array( 'base' => $aPartnerBase,
'header' => 'merch_header.tpl',
'navi' => 'merch_navi.tpl',
'footer' => $aPartnerFooter,
'cat_header' => $aCategoryHeader,
'body' => 'merch_body.tpl'
) );

$aTPL->assign( array( 'TITLE' => 'Katalog produktów: Ubrania',
'CATEGORY_SPECIALS' => 'Sprzedanemy koszulki Intechra!',
'PRODUCT_NAME' => 'Koszulka Intechra',
'PRODUCT_DESCRIPTION' => 'Świetna koszulka z logo Intechra LLC!',
'PRODUCT_PRICE' => '14.95 zł',
'COPYRIGHT_YEARS' => '2000',
'HREF_HOME' => 'index.phtml',
'HREF_CART' => 'cart.phtml',
'HREF_CONTACT' => 'contact.phtml',
'HREF_COMPANY_INFO' => 'company.phtml'
) );

$aTPL->parse( 'PAGE_HEADER', 'header' );
$aTPL->parse( 'CAT_HEADER', 'cat_header' );
$aTPL->parse( 'LEFT_NAVI', 'navi' );
$aTPL->parse( 'BODY', 'body' );
$aTPL->parse( 'PAGE_FOOTER', 'footer' );
$aTPL->parse( 'BASE', 'base' );
Rozdział 14 – Witryny oparte o szablony 184
$aTPL->FastPrint( 'BASE' );
?>
Po uruchamianiu tego skryptu analizowana jest zmienna $HTTP_HOST, aby sprawdzić, która witryna została
wywołana. Jeżeli nazwa zawiera cobrand, używane są szablony partnera, natomiast w pozostałych przypadkach
używane są standardowe szablony. Gdy strona ta zostanie wywołana poprzez adres http://www.katalog.com,
wynik jest identyczny jak na rysunku 14.3. Rysunek 14.8. przedstawia wygląd strony po wywołaniu strony
poprzez adres zapożyczonej witryny.
Rysunek 14.8.
Zapożyczenie
katalogu
produktów

Personalizacja witryny
Personalizacja wydaje się ostatnio najpopularniejszym elementem przy projektowaniu witryn. Każdy
portal i wiele dużych witryn pozwalają na personalizowanie witryny tak, aby spełniała potrzeby użytkownika. We
wielu przypadkach personalizacja jest ograniczona do typu wyświetlanych informacji i niektórych podstawowych
kolorów. Wykorzystując szablony możliwa jest o wiele bardziej zaawansowana modyfikacja wyglądu witryny.
Zamiast tworzyć przykłady dla tej części książki, chciałbym odwołać się do witryny, którą wykonałem. Nie jest
moim celem reklamować ten serwis, ale jedynie pokazać jak bardzo można modyfikować wygląd witryny
korzystając z dobrego systemu szablonów.
Tą witryną jest http://www.HopeToAdopt.com/ i jest to sieciowy serwis dla rodzin adoptujących dzieci.
Witryna pozwala na tworzenie własnych profili poprzez odpowiedź na kilka prostych pytań oraz wybranie
odpowiednich opcji. Najlepszą cechą witryny jest możliwość wybrania własnego tematu używanego w stronach
informacyjnych. Każdy z dostępnych tematów jest przedstawiony w postaci ikony na stronie.
Gdy Użytkownik kliknie ikonę tematu, w bazie danych zapisywany jest wybrany identyfikator tematu i
jest on używany później przy wyświetlaniu. Na rysunku 14.10.pokazany jest wygląd witryny po wybraniu tematu
kolejowego.
Do stworzenia tematu potrzebne są cztery pliki: {temat}_navi.jpg, {temat}_header.jpg, {temat}_map.tpl
oraz {temat}_vars.php. Pierwsze dwie są rysunkami używanymi w nagłówku oraz panelu nawigacyjnym. Trzeci
jest mapą obrazu dla panelu nawigacyjnego. Ostatni plik jest zbiorem zmiennych specyficznych dla szablonu,
które są dołączane do pliku skryptu PHP korzystającego z danych tematu. Przykład takiego pliku jest pokazany na
wydruku 14.20.
185 PHP – Kompendium wiedzy
Wydruk 14.20. Plik dołączany specyficzny dla tematu
<?php
function AddTemplateVars( &$tpl, $aCurTemplate )
{
$tpl->assign( array( BODYBGCOLOR => "#FFFFFF",
MAINWIDTH => 584,
TOPIMGWIDTH => 584,
TOPIMGHEIGHT => 128,
LEFTIMGWIDTH => 137,
LEFTIMGHEIGHT => 312,
FILLWIDTH => 584,
TABLECOLOR => "#ffadad"));
}
function GetTemplteValue( $aValName )
{
switch ( $aValName )
{
case "NavSide": return "left";
case "HasOvr" : return False;
}
}
?>
Rysunek 14.10.
Profil
użytkownika z
tematem
kolejowym

Plik ten zawiera dane na temat kolorów wykorzystywanych w temacie, oraz wysokość i szerokość różnych
rysunków używanych w temacie. Dodatkowo dostępna jest funkcja zwracająca do głównego skryptu dane na
temat tematu.
Przykład ten jest specyficzny dla aplikacji HopeToAdopt.com, ale może służyć jako ilustracja
elastyczności systemu szablonów. Witryna HopeToAdopt.com jest używana jako działający przykład pokazujący
siłę systemu szablonów — zamiast tworzenia kolejnego trywialnego przykładu. Pełny kod tej witryny nie może
być tutaj zamieszczony, ale poprzednie przykłady zawierają wystarczająco dużo informacji, aby stworzyć tego
typu witrynę.

Rozdział 14 – Witryny oparte o szablony 186
Obsługa wielu języków
Coraz częstsze jest tworzenie witryn działających w kilku językach. Wykorzystując system szablonów do
obsługi tej funkcji pozwala na tworzenie aplikacji w jednym języku, a następnie w łatwy sposób dodać później
kolejne języki. I tym razem tworzenie tak skomplikowanej aplikacji wymaga uważnego projektowania przed
rozpoczęciem prac programowych, ale efekt jest wart tej pracy. Jedną z pierwszych decyzji jest zadecydowanie, w
jaki sposób będą dzielone i identyfikowane elementy właściwe dla języka. Jedną z metod jest stworzenie
oddzielnych katalogów dla poszczególnych języków. Drugą jest umieszczenie identyfikatorów języka w nazwach
i plikach szablonów. Metoda ta zostanie zastosowana w przykładzie.
W przykładzie tym, dla każdego języka wymagane są cztery pliki: rysunek nagłówka, rysunek panelu
nawigacyjnego, mapa rysunku, oraz główny plik. Nie będziemy tu zamieszczać wszystkich plików, pokażemy
jedynie główny skrypt i wynik działania. Na wydruku 14.21. zamieszczony jest główny skrypt, który generuje
jedną stronę międzynarodowej witryny opartej o szablony.
Wydruk 14.21. Główny skrypt międzynarodowej witryny
<?php
include( "class.FastTemplate.php" );

$aTPL = new FastTemplate( "." );

if ( empty( $Lang ) )
{
$Lang = 'enu';
}

$aHeaderImg = "intl_head_{$Lang}.gif";
$aNaviImg = "intl_nav_{$Lang}.gif";
$aNaviMap = "intl_map_{$Lang}.tpl";
$aBodyTpl = "body_{$Lang}.tpl";

$aTPL->define( array( 'base' => 'intl_base.tpl',
'body' => $aBodyTpl,
'navimap' => $aNaviMap ) );

$aTPL->assign( array( 'HREF_HOME' => "intl.phtml?Lang=$Lang",
'HREF_LINKS' => "links.phtml?Lang=$Lang",
'HREF_ABOUT' => "about.phtml?Lang=$Lang",
'HREF_CONTACT' => "contact.phtml?Lang=$Lang",
'HEADER_IMG' => $aHeaderImg,
'NAV_IMG' => $aNaviImg,
'HREF_ENU' => $PHP_SELF . "?Lang=enu",
'HREF_POL' => $PHP_SELF . "?Lang=pol",
'HREF_DEU' => $PHP_SELF . "?Lang=deu" ) );

$aTPL->parse( 'NAV_MAP', 'navimap' );
$aTPL->parse( 'BODY', 'body' );
$aTPL->parse( 'BASE', 'base' );
$aTPL->FastPrint( 'BASE' );
?>
Jedyną widoczną różnicą w tym skrypcie, w porównaniu z innymi przedstawionymi w tym rozdziale jest
część na początku ustalająca bieżący język i korzystająca z tej informacji w celu dołączenia właściwego pliku. W
przykładzie tym identyfikator języka jest przesyłany poprzez adres URL, więc każde łącze w witrynie musi
przesyłać tą daną do kolejnej strony. W dużych aplikacjach przedstawiona metoda definiowania wszystkich
możliwych łączy staje się nieporęczna. W praktyce kod generujący łącza prawdopodobnie będzie umieszczony w
oddzielnym pliku dołączanym. Niezależnie od prostoty przedstawionego przykładu, elastyczność i siła tego
rozwiązania jest ogromna. Na rysunkach 14.12, 14.13 i 14.14 pokazana jest strona główna w języku odpowiednio:
angielskim, polskim i niemieckim.

187 PHP – Kompendium wiedzy
Rysunek 14.12.
Witryna
międzynarodowa
w języku
angielskim

Rysunek 14.13.
Witryna
międzynarodowa
w języku polskim

Rozdział 14 – Witryny oparte o szablony 188
Rysunek 14.14.
Witryna
międzynarodowa
w języku
niemieckim

Podsumowanie
W rozdziale tym pokazano jak zastosowanie systemu szablonów polepsza elastyczność i łatwość
utrzymania aplikacji WWW. Dostarczone przykłady pokazują, w jaki sposób można użyć szablonów do obsługi
zapożyczania, personalizacji i obsługi języków.
Używanym systemem szablonów jest FastTemplate, który można uzyskać pod adresem
http://www.thewebmasters.net/. Niezależnie od rodzaju używanego systemu szablonów jest zalecane zapoznanie
się z tym sposobem tworzenia aplikacji WWW.

189 PHP – Kompendium wiedzy
Rozdział 15. Witryny oparte o bazę
danych
Wstęp
W rozdziale 6. „Współpraca z bazami danych” opisane zostały narzędzia PHP pozwalające na dostęp do
baz danych. W ostatnim rozdziale dokładnie opisane jest wykorzystanie systemu szablonów do oddzielenia
interfejsu aplikacji od kodu aplikacji. Rozdziały te stanowią podstawę dla tego rozdziału. W rozdziale tym
opisane są szczegóły projektu i implementacji na wysokim poziomie, więc nie będą opisane niskopoziomowe
funkcje obsługi baz danych. Więcej na ten temat można przeczytać w rozdziale 6. oraz w skorowidzu funkcji na
końcu tej książki.

Projekt bazy danych
W każdym aspekcie tworzenia oprogramowania wynikiem dobrego projektu jest dobry produkt.
Nieprawidłowy projekt bazy danych zwykle prowadzi do problemów z integracją, utrzymaniem i tworzeniem
aplikacji. Prawidłowe projektowanie baz danych jest tematem wielu wspaniałych książek i jest to temat zbyt
obszerny i skomplikowany, aby go tutaj przedstawić, więc opisane zostaną niektóre podstawowe informacje.
Pierwszą decyzją jaką należy podjąć jest wybór systemu zarządzania bazą danych (SZRBD). Ponieważ
PHP obsługuje wiele popularnych systemów baz danych, przy podejmowaniu tej decyzji powinniśmy wziąć pod
uwagę koszty, funkcjonalność, skalowalność oraz inne kluczowe aspekty bazy danych, a nie obsługa języka.
Dla wielu aplikacji PHP świetnym systemem bazy danych jest MySQL, ponieważ PHP posiada domyślnie
wbudowaną obsługę MySQL. MySQL jest dostępny na zasadach licencji GNU General Public License (GPL).
Posiada on obsługę dużego podzbioru SQL oraz bogate API. Informacje na temat instalacji i korzystania z
MySQL znajdują się w rozdziale 6. Przykłady w tym rozdziale są napisane w oparciu o bazę danych MySQL.
Dostępne są również inne bazy danych, które także mają swoje silne strony. Jeżeli masz zamiar stworzyć
aplikację, która obsługiwać będzie dużą liczbę użytkowników lub potrzebujesz obsługi transakcji, powinieneś
rozważyć zastosowanie Oracle lub Microsoft SQL Server. Wybór właściwej bazy danych dla aplikacji wymaga
wyboru pomiędzy ceną, dostępnością obsługi technicznej, skalowalnością i dostępnymi funkcjami. Wybór
niewłaściwego systemu bazy danych może spowodować, że w przypadku dużego obciążenia aplikacja będzie
miała niską wydajność lub odmówi posłuszeństwa. Jeżeli przypuszczasz, że po stworzeniu aplikacji system bazy
danych może być zmieniony na inny, należy skorzystać z pośredniego API stanowiącego bufor pomiędzy
aplikacją a funkcjami specyficznymi dla określonej bazy danych, oraz korzystać ze standardowego języka SQL.
Dodatkowo należy pamiętać o różnicach w obsłudze standardu SQL w różnych systemach baz danych. Dla
przykładu w Oracle można korzystać z następujących podzapytań:
SELECT * FROM tabela1 WHERE id IN (SELECT id FROM tabela2)
W czasie pisania tej książki MySQL nie potrafił obsługiwać takiej konstrukcji. Tworzenie kodu SQL
niezależnego od systemu bazy danych jest wyzwaniem samym w sobie, więc zmiana systemu bazy danych na
inny może być niepraktyczna nawet dla małych aplikacji.
Po wybraniu systemu bazy danych kolejnym krokiem jest stworzenie i dokładne przetestowanie modelu
danych. Jako przykładu użyjemy sieciowego katalogu towarów, przeznaczonego dla wielu sprzedawców lub
sklepów. W tym rozdziale opisany zostanie proces projektowania i implementacji bazy danych, która będzie
wykorzystywana w dwóch kolejnych rozdziałach. Celem jest stworzenie sieciowego katalogu, obsługującego w
jednej bazie danych fragmenty danych przypisane do różnych sprzedawców, a każdy z tych sprzedawców może
mieć wiele kategorii produktów. Ogólny schemat tej bazy danych jest pokazany na rysunku 15.1.
Rysunek 15.1.
Podstawowy model
danych katalogu
towarów

Rozwijając model z rysunku 15.1, każdy sprzedawca może mieć jedną lub więcej kategorii produktów, a
każda z kategorii może zawierać jeden lub więcej produktów. Na tym poziomie szczegółowości można stworzyć
kompletny model danych. Na rysunku 15.2 pokazany jest kompletny model danych katalogu produktów. Jego
implementacja w SQL zamieszczona jest na wydruku 15.1.
Rysunek 15.2.
Pełny model
danych katalogu
towarów

Wydruk 15.1. Implementacja modelu danych katalogu produktów
CREATE TABLE mcMerchants
(
merchant_id int not null,
name varchar(50) not null,
created_date datetime not null,
internal_status int not null,
addtl_handling float default 0.0 not null,
mgr_email varchar(25) not null,
mgr_username varchar(30) not null,
mgr_password varchar(30) not null,
mgr_name varchar(50) not null,

primary key ( merchant_id ),
index( name ),
index( mgr_username )
);

CREATE TABLE mcCategories
(
merchant_id int not null,
category_id int not null,
name varchar(50) not null,

191 PHP – Kompendium wiedzy
created_date datetime not null,
deleted tinyint default 0 not null,

primary key ( merchant_id, category_id ),
index( name )
);

CREATE TABLE mcProducts
(
merchant_id int not null,
product_id int not null,
category_id int not null,
name varchar(200) not null,
descr text not null,
has_image_file tinyint default 0 not null,
external_id varchar(100) not null,
ship_weight float not null,
price float not null,
created_date datetime not null,
deleted tinyint default 0 not null,

primary key ( merchant_id, product_id ),
index( name )
);

CREATE TABLE mcProductsToCategories
(
merchant_id int not null,
category_id int not null,
product_id int not null,

primary key ( merchant_id, category_id, product_id )
);

CREATE TABLE mcProductsOptions
(
merchant_id int not null,
product_id int not null,
option_id int not null,
name varchar(100) not null,
sort_type tinyint default 0 not null,

primary key ( merchant_id, product_id, option_id ),
index( name )
);

CREATE TABLE mcProductsOptionsValues
(
merchant_id int not null,
product_id int not null,
option_id int not null,
value_id int not null,
name varchar(100) not null,

primary key ( merchant_id, product_id, option_id, value_id ),
index( name )
);
Po zaprojektowaniu i sprawdzeniu modelu danych można rozpocząć prace nad aplikacją. Podstawowymi
założeniami aplikacji katalogu produktów są:
• Wyświetlanie danych o produktach w logicznym i łatwym do użycia formacie.
• Umożliwienie sprzedawcom uaktualniania danych o produktach w dowolnym momencie z dowolnego
komputera przyłączonego do sieci Internet.
• Umożliwienie sprzedawcom zarządzanie kategoriami produktów i przypisywanie produktów do kategorii
w dowolnym momencie z dowolnego komputera przyłączonego do sieci Internet.
Mówiąc prościej, celem jest dostarczenie metody na dodawanie, zmianę i wyświetlanie dowolnej danej
zawartej w katalogu produktów, wykorzystując do tego celu Internet. W następnej sekcji opisane zostanie
zarządzanie tymi danymi.

Zarządzanie danymi aplikacji
Po zaprojektowaniu i stworzeniu bazy danych można rozpocząć tworzenie aplikacji zarządzającej
rekordami w bazie danych. Podstawowymi operacjami jakie można przeprowadzać na dowolnych danych jest
Rozdział 15 – Witryny oparte o bazę danych 192
dodawanie, zmiana i usuwanie. W tej aplikacji niezbędne będzie również zarządzanie hierarchią danych. Aby
spełnić to wymaganie, spełniona musi być zasada, że przed dodaniem jakiegokolwiek produktów, musi istnieć co
najmniej jedna kategoria produktów dla sprzedawcy.
Mając na uwadze tą zasadę, logicznym punktem startowym jest strona z możliwością zarządzania
kategoriami produktów. W aplikacji tej zakładamy, ze sprzedawca może się zalogować do fragmentu witryny w
celu zarządzania danymi. W aplikacji będziemy korzystać z omówionego wcześniej systemu szablonów
FastTemplate. Do podstawowego zarządzania danymi aplikacji użyte zostaną szablony przedstawione na
wydrukach od 2. do 4.
Wydruk 15.2. Podstawowy szablon zarządzania danymi aplikacji (mgmt_app_base.tpl)
<html>
<head>
<title>{TITLE}</title>
<link rel="STYLESHEET" type="text/css" href="mgmt.css">
</head>
<body bgcolor="White">
{BODY}
</body>
</html>
Wydruk 15.3. Szablon zarządzania danymi aplikacji (mgmt_body.tpl)
<table width="630" align="center">
<tr>
<td align="center" class="title">
{MERCHANT_NAME}
</td>
</tr>
<tr>
<td>
{PAGE_BODY}
</td>
</tr>
<tr>
<td>
&nbsp;
</td>
</tr>
<tr>
<td>
{FOOTER}
</td>
</tr>
</table>
Wydruk 15.4. Szablon zarządzania danymi aplikacji — stopka (mgmt_footer.tpl)
<hr>
<p class="footer">
&copy; 2000 Intechra LLC. Wszystkie prawa zastrzeżone.
</p>
Szablony przedstawione na wydrukach od 2. do 4. stanowią podstawowy szablon aplikacji zarządzania
danymi. Szablony używane do stworzenia strony zarządzania konkretną kategorią umieszczone są na wydrukach
od 5. do 7. Na wydruku 15.8. znajduje się skrypt łączący te szablony i wyświetlający dane z bazy danych.
Wydruk 15.5. Szablon zarządzania danymi aplikacji — główny szablon kategorii (mgmt_cats_ovr.tpl)
<h1>
Zarządzanie kategoriami produktów
</h1>
<p>
Proszę użyć poniższych narzędzi do dodaniam edycji i usunięcia
kategorii produktów.
</p>
<p>
<a href="mgmt_cat_add.phtml">Kliknij tutaj</a> aby dodać kategorię.
</p>
{EXISTING_CATEGORIES}
Wydruk 15.6. Szablon zarządzania danymi aplikacji — tabela kategorii (mgmt_cats_table.tpl)
<h2>
Istniejące kategorie produktów:
</h2>
<table cellspacing="0" cellpadding="0">
<tr>
<th>
&nbsp;&nbsp;Identyfikator kategorii&nbsp;&nbsp;
</th>
<th>
&nbsp;&nbsp;Nazwa kategorii&nbsp;&nbsp;
</th>

193 PHP – Kompendium wiedzy
<th>
&nbsp;&nbsp;Operacje&nbsp;&nbsp;
</th>
</tr>
{CATEGORY_LIST}
</table>
Wydruk 15.7. Szablon zarządzania danymi aplikacji — bieżąca kategoria (mgmt_cats_item.tpl)
<tr>
<td>
{CAT_ID}
</td>
<td>
{CAT_NAME}
</td>
<td class="small">
<a href="mgmt_cat_edit.phtml?cat_id={CAT_ID}">ZMIEŃ</a>
<a href="mgmt_cat_del.phtml?cat_id={CAT_ID}">USUŃ</a>
</td>
</tr>
Wydruk 15.8. Aplikacja zarządzająca danymi — zarządzanie kategoriami (mgmt_cats.phtml)
<?php
error_reporting( E_ALL & ~E_NOTICE );
session_start(); // niejawne ustawienie zmiennej sesji $aMerchantID
if ( empty( $aMerchantID ) == True )
{
header( "Location: login.phtml?retpage=" . urlencode( $REQUEST_URI ) . "\n" );
exit;
}

include( "class.FastTemplate.php" );
include( "./mgmt_db.php" );
include( "./mgmt_funcs.php" );

$aTPL = new FastTemplate( "." );
$aDB = new mgmt_db();

$aTPL->define( array( "base" => "mgmt_app_base.tpl",
"body" => "mgmt_body.tpl",
"footer" => "mgmt_footer.tpl",
"page_body" => "mgmt_cats_ovr.tpl",
"cat_table" => "mgmt_cats_table.tpl",
"cat_item" => "mgmt_cats_item.tpl" ) );

$aSQL = "select category_id, name from mcCategories
where ( merchant_id = $aMerchantID )";
$aDB->query( $aSQL );
if ( $aDB->num_rows() > 0 )
{
while ( $aDB->next_record() )
{
$aCatID = $aDB->f( "category_id" );
$aCatName = $aDB->f( "name" );

$aTPL->assign( array( "CAT_ID" => $aCatID,
"CAT_NAME" => $aCatName ) );
$aTPL->parse( "CATEGORY_LIST", ".cat_item" );
}
$aTPL->parse( "EXISTING_CATEGORIES", "cat_table" );
}
else
{
$aTPL->assign( array( "EXISTING_CATEGORIES" => "" ) );
}

$aTPL->assign( array( "TITLE" => "Zarządzanie katalogiem towarów",
"MERCHANT_NAME" => GetMerchantName( $aDB, $aMerchantID )
) );

$aTPL->parse( "PAGE_BODY", "page_body" );
$aTPL->parse( "FOOTER", "footer" );
$aTPL->parse( "BODY", "body" );
$aTPL->parse( "PAGE", "base" );
$aTPL->FastPrint( "PAGE" );
?>
Pierwszą operacją jaką wykonuje skrypt jest rozpoczęcie sesji i sprawdzenie identyfikatora sprzedawcy,
$aMerchantID.Jeżeli nie jest on ustawiony, użytkownik jest kierowany na stronę logowania. Na stronie logowania
sprawdzane są dane użytkownika i jeżeli zostanie on rozpoznany, ta zmienna sesji jest inicjowana identyfikatorem

Rozdział 15 – Witryny oparte o bazę danych 194
sprzedawcy. Następnie używając identyfikatora jako filtru, skrypt ten pobiera kategorie z bazy danych. Jeżeli
istnieje co najmniej jedna kategoria, skrypt pobiera te dane i generuje tabelę istniejących kategorii. Na rysunku
15.3. pokazana jest strona z dwiema kategoriami testowego sprzedawcy.
Rysunek
15.3.
Strona
zarządza
nia
kategoria
mi
katalogu
produktó
w

Dodawanie kategorii jest zrealizowane za pomocą kliknięcia w łącze. Jeżeli istnieją kategorie, każda z
nich posiada własne łącza ZMIEŃ i USUŃ, na rysunku 15.3. szablony dodawania kategorii oraz skrypt
umieszczone są na wydrukach 9. i 10.
Wydruk 15.9. Szablon zarządzania danymi aplikacji — dodawanie kategorii (mgmt_cat_add.tpl)
<h1>
Dodawanie kategorii produktu
</h1>
<form action="{FORM_ACTION}" method="post">
<table>
<tr>
<td colspan="2">
{ERRORS}
</td>
</tr>
<tr>
<td>
Nazwa kategorii:
</td>
<td>
<input type="text" name="CategoryName">
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="Submit" value="Wyślij">
</td>
</tr>
</table>
</form>
Wydruk 15.10. Aplikacja zarządzająca danymi — dodawanie kategorii (mgmt_cat_add.phtml)
<?php
session_start(); // niejawne ustawienie zmiennej sesji $aMerchantID
if ( empty( $aMerchantID ) == True )
{
header( "Location: login.phtml?retpage=" .
urlencode( $REQUEST_URI ) . "\n" );
exit;
}

include( "class.FastTemplate.php" );

195 PHP – Kompendium wiedzy
include( "./mgmt_db.php" );
include( "./mgmt_funcs.php" );

$aTPL = new FastTemplate( "." );
$aDB = new mgmt_db();

$aErrors = "";
if ( $REQUEST_METHOD == 'POST' ) // Tutaj wchodzimy po wysłaniu danych z formularza
{
if ( IsValidCategory( $aDB, $aMerchantID, $CategoryName ) == True )
{
SaveCategory( $aDB, $aMerchantID, $CategoryName );
header( "Location: mgmt_cats.phtml\n" );
exit;
}
else
{
$aErrors = "Nazwa kategorii została już użyta. ";
$aErrors .= "Proszę wybrać inną nazwę kategorii.";
}
}

$aTPL->define( array( "base" => "mgmt_app_base.tpl",
"body" => "mgmt_body.tpl",
"footer" => "mgmt_footer.tpl",
"page_body" => "mgmt_cat_add.tpl" ) );

$aTPL->assign( array( "TITLE" => "Zarządzanie katalogiem produktów",
"MERCHANT_NAME" => GetMerchantName( $aDB, $aMerchantID ),
"FORM_ACTION" => $PHP_SELF,
"ERRORS" => $aErrors
) );

$aTPL->parse( "PAGE_BODY", "page_body" );
$aTPL->parse( "FOOTER", "footer" );
$aTPL->parse( "BODY", "body" );
$aTPL->parse( "PAGE", "base" );
$aTPL->FastPrint( "PAGE" );
?>
Skrypt przedstawiony na wydruku 15.10. jest podobny do wielu innych skryptów zbierających i
kontrolujących dane, które były opisane w tej książce. Jest on używany do wyświetlenia formularza oraz kontroli
poprawności wprowadzonych danych. W tym przypadku cała logika kontroli poprawności oraz zapamiętywania
nowych kategorii jest umieszczona w funkcjach IsValidCategory() i SaveCategory(). Te funkcje pomocnicze
zostaną zamieszczone w dalszej części rozdziału na wydruku pliku mgmt_funcs.php. Funkcja sprawdzająca
poprawność szuka jedynie kategorii o takiej samej nazwie.
Funkcje edycji i usuwania kategorii są bardzo podobne do innych skryptów zamieszczonych już w tej
książce. Przyglądając się wydrukowi 8. można zauważyć, że łącza do usuwania i zmiany kategorii zawierają
identyfikator kategorii. Dla przykładu pełny adres URL do usuwania kategorii Ubrania to
http://server.com/ch15/mgmt_cat_del.phtml?cat_id=1. Do skryptu jest przekazany identyfikator kategorii, więc
można na jego podstawie skonstruować proste wyrażenie SQL DELETE. W przypadku edycji, ten sam mechanizm
umożliwia wczytanie nazwy kategorii do pola tekstowego. Kod źródłowy tych stron nie został tu zamieszczony,
ponieważ jest on bardzo podobny do kodu z wydruków 9. i 10. Funkcje usuwające i zmieniające kategorie
pokazane są na wydruku pliku mgmt_funcs.php (wydruk 15.12.).
Podczas tworzenia kategorii należy pamiętać, że nazwa nowej kategorii nie może znajdować się w bazie
danych. W przypadku usuwania kategorii należy sprawdzić, czy nie istnieje produkt należący do tej kategorii.
Jeżeli do kategorii należą jakieś produkty usunięcie kategorii spowodowałoby powstanie osieroconych rekordów
produktów i potencjalnie błędów aplikacji. Tworząc aplikacje WWW działające w oparciu o bazę danych,
spełnienie wszystkich zasad biznesowych w kodzie jest krytyczne do dobrego działania aplikacji. Niektóre z
zasad mogą być realizowane przez funkcje bazy danych, na przykład wymuszanie więzów integralności lub
kaskadowe operacje na danych. Inne zasady mogą być realizowane w bazie danych za pomocą wyzwalaczy lub
procedur przechowywanych. Pozostałe zasady biznesowe muszą być realizowane w kodzie aplikacji.
Tworząc kod obsługi zasad biznesowych dobrą praktyką jest tworzenie funkcji obsługi wszystkich zasad.
Dla przykładu należy użyć funkcji DeleteEntity() zamiast wplatać w kod wyrażenia DELETE. Funkcja
DeleteEntity() może zawierać w sobie całą logikę wymaganą do kontroli więzów integralności oraz zasad
biznesowych i zwracać różne wartości kodu powrotu w zależności od różnych błędów, jakie mogą wystąpić. W

Rozdział 15 – Witryny oparte o bazę danych 196
ten sposób poprawia się możliwość późniejszego użycia kodu oraz ułatwia wprowadzanie do aplikacji zmian w
logice.
Wracając do katalogu produktów, kolejnym krokiem jest utworzenie stron obsługi aktualnego zestawu
produktów. Strony te są logicznie identyczne ze stronami obsługi kategorii. Na rysunku 15.4 pokazana jest strona
zarządzania produktami, natomiast na wydruku 15.11. znajduje się skrypt generujący tą stronę.
Rysunek
15.4.
Ekran
zarządza
nia
produkta
mi

Wydruk 15.11. Aplikacja zarządzania danymi — zarządzanie produktami (mgmt_prods.phtml)
<?php
error_reporting( E_ALL & ~E_NOTICE );
session_start(); // niejawne ustawianie zmiennej sesji $aMerchantID
if ( empty( $aMerchantID ) == True )
{
header( "Location: login.phtml?retpage=" . urlencode( $REQUEST_URI )."\n" );
exit;
}

include( "class.FastTemplate.php" );
include( "./mgmt_db.php" );
include( "./mgmt_funcs.php" );

$aTPL = new FastTemplate( "." );
$aDB = new mgmt_db();

$aTPL->define( array( "base" => "mgmt_app_base.tpl",
"body" => "mgmt_body.tpl",
"footer" => "mgmt_footer.tpl",
"page_body" => "mgmt_prods_ovr.tpl",
"prod_table" => "mgmt_prods_table.tpl",
"prod_item" => "mgmt_prods_item.tpl" ) );

$aSQL = "select a.category_id, a.product_id, a.name, a.external_id, a.price,";
$aSQL .= "b.name as cat_name from mcProducts a, mcCategories b where (a.merchant_id";
$aSQL .= "= $aMerchantID) and (a.category_id = b.category_id)";
$aDB->query( $aSQL );
if ( $aDB->num_rows() > 0 )
{
while ( $aDB->next_record() )
{
$aCatID = $aDB->f( "category_id" );
$aCatName = $aDB->f( "cat_name" );
$aProdID = $aDB->f( "product_id" );
$aProdName = $aDB->f( "name" );
$aProdEID = $aDB->f( "external_id" );
$aProdPrice = $aDB->f( "price" );

$aTPL->assign( array( "PROD_ID" => $aProdID,

197 PHP – Kompendium wiedzy
"PROD_EID" => $aProdEID,
"PROD_NAME" => $aProdName,
"PROD_CAT" => $aCatName,
"PROD_PRICE" => '$' .
number_format( $aProdPrice, 2 ) ) );
$aTPL->parse( "PRODUCT_LIST", ".prod_item" );
}
$aTPL->parse( "EXISTING_PRODUCTS", "prod_table" );
}
else
{
$aTPL->assign( array( "EXISTING_PRODUCTS" => "" ) );
}

$aTPL->assign( array( "TITLE" => "Zarządzanie katalogiem produktów",
"MERCHANT_NAME" => GetMerchantName( $aDB, $aMerchantID )
) );

$aTPL->parse( "PAGE_BODY", "page_body" );
$aTPL->parse( "FOOTER", "footer" );
$aTPL->parse( "BODY", "body" );
$aTPL->parse( "PAGE", "base" );
$aTPL->FastPrint( "PAGE" );
?>
Strony umożliwiające dodawanie, usuwanie i zmianę produktów nie zostały tutaj szczegółowo
przedstawione, ale są dostępne na stronie WWW wymienionej w zasobach sieci na końcu książki. Zasady
biznesowe obowiązujące przy dodawaniu nowych produktów są następujące:
• Produkt musi zostać przypisany do jednej kategorii.
• Nazwy produktów w kategorii muszą być unikalne.
• Cena produktu musi wynosić co najmniej zero.
• Waga towaru również musi wynosić co najmniej zero.
W chwili obecnej nie ma ograniczeń na kasowanie produktów. Po zmianie danych produktu muszą być
spełnione te same zasady biznesowe co przy dodawaniu nowego produktu. Na wydruku 15.12 zamieszczony
został fragment pliku mgmt_funcs.php zawierający niektóre funkcje dostępu do bazy danych oraz funkcje zasad
biznesowych używanych w aplikacji.
Wydruk 15.12. Aplikacja zarządzająca danymi — funkcje użytkowe (mgmt_funcs.php)
<?php

function GetMerchantName( $aDB, $aMerchantID )
{
$aResult = "";
$aSQL = "select name from mcMerchants where ";
$aSQL .= "( merchant_id = $aMerchantID )";
$aDB->query( $aSQL );
if ( $aDB->next_record() == True )
{
$aResult = $aDB->f( "name" );
}
return $aResult;
}

function NewCategoryID( $aDB, $aMerchantID )
{
$aSQL = "select ( max( category_id ) + 1 ) as new_id ";
$aSQL .= "from mcCategories where ( merchant_id = $aMerchantID )";
$aDB->query( $aSQL );
if ( $aDB->next_record() )
{
$aResult = $aDB->f( "new_id" );
}
if ( empty( $aResult ) == True )
{
$aResult = 1;
}
return $aResult;
}

function IsValidCategory( $aDB, $aMerchantID, $aCategoryName )
{
$aSQL = "select category_id from mcCategories where ";
$aSQL .= "( merchant_id = $aMerchantID ) and ";
$aSQL .= "( upper( name ) = upper( '$aCategoryName' ) )";
$aDB->query( $aSQL );
// Jeżeli istnieje rekord z tą samą nazwą kategorii,
Rozdział 15 – Witryny oparte o bazę danych 198
// zwróć false
return ( $aDB->num_rows() == 0 );
}

function SaveCategory( $aDB, $aMerchantID, $aCategoryName )
{
$aNewID = NewCategoryID( $aDB, $aMerchantID );
$aSQL = "insert into mcCategories ( merchant_id, ";
$aSQL .= "category_id, name, created_date ) values ";
$aSQL .= "( $aMerchantID, $aNewID, '$aCategoryName', ";
$aSQL .= " NOW() )";
$aDB->query( $aSQL );
return ( $aDB->Errno == 0 );
}

function DeleteCategory( $aDB, $aMerchantID, $aCategoryID )
{
$aSQL = "delete from mcCategories where ( merchant_id = ";
$aSQL .= "$aMerchantID ) and ( category_id = $aCategoryID )";
$aDB->query( $aSQL );
return ( $aDB->Errno == 0 );
}

function UpdateCategory( $aDB, $aMerchantID, $aCategoryID, $aCategoryName )
{
$aSQL = "update mcCategories set name='$aCategoryName' ";
$aSQL .= "where ( merchant_id = $aMerchantID ) and ";
$aSQL .= "( category_id = $aCategoryID )";
$aDB->query( $aSQL );
return ( $aDB->Errno == 0 );
}
// i inne funkcje
?>
Aplikacja zarządzająca danymi jest jedynie małym fragmentem całego katalogu produktów. Zapewnia ona
interfejs WWW do zarządzania elementami katalogu. Inną ważną funkcją katalogu jest możliwość wyświetlania
produktów, szukania produktów oraz odczytywania szczegółowych danych o produktach w katalogu. Następna
część tego rozdziału traktuje właśnie o tych zagadnieniach.

Wyświetlanie danych
Aplikacja wyświetlająca dane produktów z bazy pozwala na wyświetlanie produktów określonej kategorii,
wyświetlanie alfabetycznej listy produktów oraz zapewnia mechanizm przeszukiwania, pozwalający na
znalezienie określonego produktu. Aby odszukać produkty i kategorie bieżącego sprzedawcy wykorzystywany
jest identyfikator sprzedawcy, przekazywany w postaci zmiennej sesji. Zmienna ta jest ustawiana jeszcze zanim
użytkownik wejdzie na stronę, na której wyświetlane są informacje o produktach.
Na rysunku 15.5. znajduje się główna strona katalogu produktów. Strona ta pozwala na natychmiastowy
dostęp do danych podzielonych na kategorie oraz na przeszukiwanie katalogu. Jeżeli nie ma zarejestrowanych
żadnych kategorii (co oznacza brak towarów), wyświetlana jest informacja, że dla ten sprzedawca nie ma
zarejestrowanych produktów. Na wydruku 15.13. zamieszczony jest skrypt generujący tą stronę.

199 PHP – Kompendium wiedzy
Rysunek
15.5.
Główna
strona
katalogu
produktó
w

Wydruk 15.13. Aplikacja zarządzająca danymi — wyświetlanie produktów (mgmt_main.phtml)
<?php
error_reporting( E_ALL & ~E_NOTICE );
session_start(); // niejawne ustawianie zmiennej sesji $aMerchantID
if ( empty( $aMerchantID ) == True )
{
print( "Błąd wewnętrzny aplikacji. Brak identyfikatora sprzedawcy." );
exit;
}

include( "class.FastTemplate.php" );
include( "./mgmt_db.php" );
include( "./mgmt_funcs.php" );

$aTPL = new FastTemplate( "." );
$aDB = new mgmt_db();

$aTPL->define( array( "base" => "mgmt_app_base.tpl",
"body" => "mgmt_body.tpl",
"footer" => "mgmt_footer.tpl",
"page_body" => "mgmt_main.tpl",
"cat_body" => "mgmt_main_body.tpl",
"cat_item" => "mgmt_main_cat_item.tpl" ) );

$aSQL = "select category_id, name from mcCategories
where ( merchant_id = $aMerchantID )";
$aDB->query( $aSQL );
if ( $aDB->num_rows() > 0 )
{
while ( $aDB->next_record() )
{
$aCatID = $aDB->f( "category_id" );
$aCatName = $aDB->f( "name" );

$aTPL->assign( array( "CAT_HREF" => GetCategoryHREF( $aCatID ),
"CAT_NAME" => $aCatName ) );
$aTPL->parse( "CATEGORY_LIST", ".cat_item" );
}
$aTPL->parse( "CATALOG_MAIN_BODY", "cat_body" );
}
else
{
$aTPL->assign( array("CATALOG_MAIN_BODY" => "Brak dostępnych produktów." ) );
Rozdział 15 – Witryny oparte o bazę danych 200
}

$aTPL->assign( array("TITLE" => "Zarządzanie katalogiem produktów",
"MERCHANT_NAME" => GetMerchantName( $aDB, $aMerchantID )
) );

$aTPL->parse( "PAGE_BODY", "page_body" );
$aTPL->parse( "FOOTER", "footer" );
$aTPL->parse( "BODY", "body" );
$aTPL->parse( "PAGE", "base" );
$aTPL->FastPrint( "PAGE" );
?>
Pliki szablonów użyte do wygenerowania tej strony są bardzo podobne do tych, które były używane w
skrypcie zarządzającym kategoriami, zamieszczonym na poprzednim wydruku. W skrypcie pokazanym na
poprzednim wydruku, do generowania adresów URL dla poszczególnych nazw kategorii, została wykorzystana
funkcja GetCategoryHREF(). Funkcja ta wchodzi w skład pliku mgmt_funcs.php i jest przedstawiona na wydruku
15.14. Funkcja ta pozwala na wygenerowanie przez ten skrypt serii statycznych stron na podstawie dynamicznych
danych. Metoda ta zostanie opisane szczegółowo w następnym rozdziale.
Wydruk 15.14. Funkcja GetCategoryHREF()
function GetCategoryHREF( $aCatID, $aDynamic = True )
{
if ( $aDynamic == True )
{
return "mgmt_prod_list.phtml?cat_id=$aCatID";
}
else
{
return "mgmt_cat_{$aCatID}.html";
}
}
Na wydruku 15.15. zamieszczony został skrypt wyświetlający produkty. Jest on używany do wyświetlania
listy podzielonej na kategorie, listy alfabetycznej oraz wyników wyszukiwania.
Wydruk 15.15. Przykład skryptu wyświetlającego produkty
<?php
error_reporting( E_ALL & ~E_NOTICE );
session_start(); // niejawne ustawianie zmiennej sesji $aMerchantID
if ( empty( $aMerchantID ) == True )
{
print( "Błąd wewnętrzny aplikacji. Brak identyfikatora sprzedawcy." );
exit;
}

include_once( "class.FastTemplate.php" );
include_once( "mgmt_db.php" );
include_once( "mgmt_funcs.php" );

$aMerchantID = 1;

if ( $REQUEST_METHOD == 'POST' ) // Tutaj wchodzimy po wysłaniu danych z formularza
{
$aSQL = "select a.category_id, a.product_id, a.name, a.external_id, a.price, ";
$aSQL .= "a.ship_weight, a.has_image_file, a.descr, b.name as cat_name from ";
$aSQL .= "mcProducts a, mcCategories b where ( a.merchant_id = $aMerchantID) ";
$aSQL .= "and ( a.category_id = b.category_id ) and ( ( upper( a.name ) like ";
$aSQL .= "upper( '%{$SearchTerms}%' ) ) or ( upper( a.descr ) like ";
$aSQL .= "upper( '%{$SearchTerms}%' ) ) ) order by a.name";
}
else
{
if ( empty( $cat_id ) == False ) // lista według kategorii
{
$aSQL = "select a.category_id, a.product_id, a.name, a.external_id, ";
$aSQL .= "a.price, a.ship_weight, a.has_image_file, a.descr, b.name as ";
$aSQL .= "cat_name from mcProducts a, mcCategories b where ";
$aSQL .= "(a.merchant_id = $aMerchantID) and (a.category_id = b.category_id) ";
$aSQL .= "and ( a.category_id = $cat_id ) order by a.name";
}
else // lista alfabetyczna
{
$aSQL = "select a.category_id, a.product_id, a.name, a.external_id, a.price, ";
$aSQL .= "a.ship_weight, a.has_image_file, a.descr, b.name as cat_name from ";
$aSQL .= "mcProducts a, mcCategories b where (a.merchant_id = $aMerchantID) ";
$aSQL .= "and (a.category_id = b.category_id) order by a.name";
}
}

201 PHP – Kompendium wiedzy
$aTPL = new FastTemplate( "." );
$aDB = new mgmt_db();

$aTPL->define( array( "base" => "mgmt_app_base.tpl",
"body" => "mgmt_body.tpl",
"footer" => "mgmt_footer.tpl",
"page_body" => "mgmt_prod_main.tpl",
"prod_item" => "mgmt_prod_item.tpl" ) );

$aDB->query( $aSQL );
if ( $aDB->num_rows() > 0 )
{
while ( $aDB->next_record() )
{
$aProdName = $aDB->f( "name" );
$aProdEID = $aDB->f( "external_id" );
$aProdPrice = $aDB->f( "price" );
$aProdWeight = $aDB->f( "ship_weight" );
$aHasImage = $aDB->f( "has_image_file" );
$aProdDescr = $aDB->f( "descr" );
$aCatName = $aDB->f( "cat_name" );
$aCatID = $aDB->f( "category_id" );
$aProdID = $aDB->f( "product_id" );

$aImageFile = "images/default.jpg";
if ( $aHasImage == True )
{
$aImageFile = "images/{$aMerchID}_{$aCatID}_{$aProdID}.jpg";
}

$aTPL->assign( array( "PROD_NAME" => $aProdName,
"CAT_NAME" => $aCatName,
"PROD_EID" => $aProdEID,
"PROD_PRICE" => number_format( $aProdPrice, 2 ).'zł',
"PROD_DESCR" => $aProdDescr,
"IMAGE_FILE" => $aImageFile
) );
$aTPL->parse( "ITEM_LIST", ".prod_item" );
}
}
else
{
$aTPL->assign( array("ITEM_LIST" => "Nie ma produktów dla wybranego kryterium."));
}

$aTPL->assign( array( "TITLE" => "Zarządzanie katalogiem produktów",
"MERCHANT_NAME" => GetMerchantName( $aDB, $aMerchantID )
) );

$aTPL->parse( "PAGE_BODY", "page_body" );
$aTPL->parse( "FOOTER", "footer" );
$aTPL->parse( "BODY", "body" );
$aTPL->parse( "PAGE", "base" );
$aTPL->FastPrint( "PAGE" );
?>
Pierwszą operacją podejmowaną przez skrypt jest sprawdzenie, jaki zbiór danych powinien zostać
wybrany. Jeżeli jest on uruchomiony poprzez wywołanie POST, oznacza to, że użytkownik chciał wyszukiwać
dane. W przeciwnym przypadku należy wygenerować listę alfabetyczną, lub tylko dla jednej kategorii. Jeżeli
ustawiona została zmienna $cat_id, potrzebna jest lista dla określonej kategorii. W oparciu o te informacje,
generowane jest odpowiednie zapytanie SQL. W zależności od wyniku zapytania wynikowa strona zawiera listę
produktów albo komunikat, który informuje użytkownika o braku produktów dla wybranej przez niego kryteriów.
Dla każdego rekordu sprawdzany jest znacznik has_image_file. Jeżeli jest on ustawiony, generowana i
wykorzystywana jest standardowa nazwa pliku, natomiast w przeciwnym wypadku wyświetlany jest domyślny
rysunek.
Na rysunkach 15.6., 15.7., i 15.8. pokazane są odpowiednio: lista dla pojedynczej kategorii, lista
alfabetyczna oraz lista wyników wyszukiwania. Szukaną frazą było „pol”.

Rozdział 15 – Witryny oparte o bazę danych 202
Rysunek
15.6.
Lista
dla
kategori
i
(katego
ria
ubrania
)

Rysunek
15.7.
Alfabetycz
na lista
produktów

203 PHP – Kompendium wiedzy
Rysunek
15.8. Lista
wyników
wyszukiwa
nia
(szukanie
„pol”)

W sekcji tej skupiliśmy się na wyświetlaniu danych w witrynie WWW. Zwykle wyświetlanie danych z
bazy danych jest dużo łatwiejsze od manipulowania danymi, ponieważ występuje tu mniej problemów i mniej
możliwości wystąpienia błędu. Następne dwa rozdziały są zbudowane w oparciu o dane i przykłady tu
zaprezentowane.

Podsumowanie
Tworzenie aplikacji WWW korzystających z bazy danych wymaga dokładnego projektowania i
programowania, ale wynik jest wart zachodu. Po stworzeniu systemu administracyjnego aplikacja może być w
uaktualniania dowolnym momencie i z dowolnego miejsca świata, co skutkuje powstaniem dynamicznej witryny
WWW, która jest bardzo łatwa do zarządzania. Najważniejszym krokiem jest dokładne zaprojektowanie bazy
danych, oraz zlokalizowanie wszystkich reguł biznesowych. Jeżeli te elementy zostaną odpowiednio
zaprojektowane i zaprojektowane, konserwacja i utrzymanie aplikacji zostanie niezwykle uproszczone.

Rozdział 15 – Witryny oparte o bazę danych 204
Rozdział 16. Generowanie statycznych
stron HTML w oparciu o dynamiczne
dane
Wstęp
Podstawowym zastosowaniem PHP jest tworzenie stron WWW z dynamicznie zmieniającą się zawartością. Zawartością tą może być
najprostszy licznik odwiedzin, aż do personalizowanych stron korzystających z bazy danych. W niektórych jednak przypadkach w pełni
dynamiczna zawartość strony nie jest konieczna lub zbytnio obniża wydajność witryny. We wielu przypadkach zawartość witryn nie jest
w pełni dynamiczna.
Dla przykładu, katalog produktów z poprzedniego rozdziału jest dynamiczny jedynie w tym sensie, że można zmieniać produkty, ale
każdy użytkownik powinien zobaczyć te same produkty i kategorie. W takich sytuacjach bardziej efektywne jest jednokrotne
generowanie statycznych stron HTML (po zmianie danych źródłowych) i ich wyświetlanie w odpowiedzi na żądania użytkowników. Na
szczęście, przy pomocy PHP można z łatwością wygenerować takie strony, więc nie będą potrzebne żadne dodatkowe narzędzia do
zamiany dynamicznej witryny na częściowo dynamiczną.

Koncepcja
Jednym z pomysłów na stworzenie statycznych stron jest wysyłanie kodu HTML do pliku zamiast do przeglądarki. Można to łatwo
zrealizować korzystając ze standardowych funkcji obsługi plików w PHP. Metoda ta wymaga jednak przepisania każdej ze stron tak, aby
cały kod HTML był włączony w kod PHP. Dla większości witryn jest to niepraktyczne. Istnieje lepsza metoda, działająca z wszystkimi
istniejącymi skryptami i stronami, wymagająca wprowadzenia jedynie minimalnych zmian. Pomysł ten został opisany w części
„Generowanie stron statycznych”.
Innym sposobem na poprawianie wydajności serwera jest buforowanie stron, co powoduje, że skrypt nie uruchamia się przy każdym
wywołaniu. Ten sposób opisany jest w części pod tytułem „Techniki buforowania”.

Generowanie stron statycznych
Ponieważ PHP jest niezwykle elastyczny, istnieją co najmniej dwa sposoby generowania statycznych stron z istniejących skryptów PHP,
przy minimalnej ilości zmian. Pierwszym sposobem jest wykorzystanie funkcji buforujących, drugim jest wykorzystanie klasy
FastTemplate opisanej w poprzednich rozdziałach.

Użycie buforowania
Jeżeli rozmiar witryny nie jest zbyt duży, można zastosować funkcje PHP pozwalające kontrolować mechanizm buforowania do
przechwycenia wynikowego kodu HTML i zapisania go do pliku. Skrypt z wydruku 16.1. Jest logicznie identyczny ze skryptem z
wydruku7 z rozdziału 6. „Współpraca z bazami danych”. Skrypt ten generuje kod strony na której można wybrać stan USA oraz kraj.
Listy te są pobierane z bazy danych. Jest to dobry kandydat do stworzenia strony statycznej, poniewa ż lista stanów USA oraz krajów nie
zmienia się często. Jeżeli trzeba poprawić wydajność aplikacji, można zrezygnować z pobierania tych elementów z bazy danych za
każdym razem, gdy użytkownik zażąda tej strony. Działanie takie ma jednak sens jedynie wtedy, gdy po zmianie wartości w bazie
danych trzeba powtórnie wygenerować stronę.
Wydruk 16.1. Wykorzystanie buforowania do tworzenia statycznych stron HTML z PHP
<?php
ob_start();
include ( "db_mysql.php" );
class MySQLDBTest extends DB_Sql
{
var $Host = "localhost";
var $Database = "mydb";
var $User = "root";
var $Password = "root";
}
function GetGenOpts( $aTableName, $aCurSel = "" )
{
$aResult = "";
$aDB = new MySQLDBTest;
$aSQL = "select ID, Name from $aTableName order by Name";
$aDB->query( $aSQL );
while( $aDB->next_record() )
{
$aName = $aDB->f( "Name" );
$aID = $aDB->f( "ID" );
if ( $aID == $aCurSel )
{
$aResult .= "<option value=\"$aID\" selected>$aName</option>";
}
else
{
$aResult .= "<option value=\"$aID\">$aName</option>";
}
}
return $aResult;
}

?>
<html>
<head>
<title>Formularz wyboru krajów i stanów USA</title>
</head>
<body>
<form action="some_place.phtml" method="post">
<table>
<tr>
<td>
Wybierz stan USA:
</td>
<td>
<select name="us_state" size="1">
<?php
print( GetGenOpts( "us_states", "ID" ) );
?>
</select>
</td>
</tr>
<tr>
<td>
Wybierz kraj:
</td>
<td>
<select name="world_country" size="1">
<?php
print( GetGenOpts( "world_countries", "ZA" ) );
?>
</select>
</td>
</tr>
</form>
</body>
</html>
<?php
$aFileName = ereg_replace( 'phtml', 'html', $PATH_TRANSLATED );
$aFile = fopen( $aFileName, "w" );
fwrite( $aFile, ob_get_contents() );
fclose( $aFile );
ob_end_clean();
print( "Plik <i>$aFileName</i> utworzony<br>" );
?>
Pierwszą czynnością wykonywaną przez skrypt jest uaktywnienie buforowania przy pomocy wywołania funkcji ob_start(). Po
uaktywnieniu buforowania, dane nie są przesyłane do przeglądarki a tylko są zbierane w wewnętrznym buforze. Na końcu skryptu
generowana jest nowa nazwa pliku i zamieniane jest rozszerzenie pliku z phtml na html. Po utworzeniu pliku bufor jest czyszczony, a do
przeglądarki wysyłany jest komunikat informacyjny. Do nowego pliku można sięgnąć za pomocą przeglądarki zmieniając rozszerzenie
żądanego pliku z phtml na html.

Rozdział 16. Generowanie statycznych stron HTML w oparciu o dynamiczne dane 206
Metoda ta działa świetnie dla wielu typów stron. W praktyce skrypty generujące strony powinny być umieszczone w obszarze serwera
WWW chronionym hasłem i jedynie niektórzy użytkownicy powinni mieć do nich dostęp. Możliwe jest również stworzenie stron za
pomocą których można zarządzać danymi dynamicznymi i za pomocą takiego interfejsu WWW generować statyczne strony.

Użycie FastTemplate
W ostatnim rozdziale tematem przykładów był katalog produktów. Jest to kolejny przykład witryny, gdzie aktualne dane strony nie
zmieniają się zbyt często. Kategorie i produkty oferowane przez firmę mogą zmieniać się raz w miesiącu lub raz w tygodniu. Katalog
jednak może być przeglądany codziennie i zawsze musi zawierać aktualne dane. Klasa FastTemplate była używana w poprzednim
rozdziale do stworzenia witryny katalogu. W tym rozdziale zostaną omówione zmiany, jakie należy wprowadzić do skryptów tak, aby
była możliwość tworzenia stron statycznych.
Po pierwsze, musi zostać zdefiniowana struktura katalogu. Dla stron kategorii każda ze stron jest nazywana korzystając z szablonu
mgmt_cat_{$aCatID}.html. Na wydruku 16.2 znajduje się skrypt, który tworzy listę kategorii produktów dla wszystkich kategorii. Jest
on podobny do skryptu z wydruku 16.15. z rozdziału 15. „Witryny oparte o bazę danych”. W tym przypadku tworzy on listę wszystkich
kategorii i zapisuje kolejne strony do osobnych plików, według wspomnianego szablonu nazw.
Wydruk 16.2. Wykorzystanie klasy FastTemplate do tworzenia statycznych stron HTML z PHP
<?php
error_reporting( E_ALL & ~E_NOTICE );
session_start(); // Niejawnie ustawia zmienną sesji $aMerchantID
if ( empty( $aMerchantID ) == True )
{
print( "Błąd wewnętrzny. Brak identyfikatora sprzedawcy." );
exit;
}

include( "class.FastTemplate.php" );
include( "./mgmt_db.php" );
include( "./mgmt_funcs.php" );

$aTPL = new FastTemplate( "." );
$aDB = new mgmt_db();
$aCatDB = new mgmt_db();
$aTPL->define( array( "base" => "mgmt_app_base.tpl",
"body" => "mgmt_body.tpl",
"footer" => "mgmt_footer.tpl",
"page_body" => "mgmt_prod_main.tpl",
"prod_item" => "mgmt_prod_item.tpl" ) );
$aSQL = "select category_id from mcCategories";
$aCatDB->query( $aSQL );
while( $aCatDB->next_record() )
{
$cat_id = $aCatDB->f( "category_id" );
//print( "$cat_id<br>" );
$aSQL = "select a.category_id, a.product_id, a.name, a.external_id, ";
$aSQL .= "a.price, a.ship_weight, a.has_image_file, a.descr, b.name as ";
$aSQL .= "cat_name from mcProducts a, mcCategories b where ";
$aSQL .= "(a.merchant_id = $aMerchantID) and (a.category_id = b.category_id)";
$aSQL .= "and ( a.category_id = $cat_id ) order by a.name";
$aDB->query( $aSQL );
if ( $aDB->num_rows() > 0 )
{
while ( $aDB->next_record() )
{
$aProdName = $aDB->f( "name" );
$aProdEID = $aDB->f( "external_id" );
$aProdPrice = $aDB->f( "price" );
$aProdWeight = $aDB->f( "ship_weight" );
$aHasImage = $aDB->f( "has_image_file" );
$aProdDescr = $aDB->f( "descr" );
$aCatName = $aDB->f( "cat_name" );
$aCatID = $aDB->f( "category_id" );
$aProdID = $aDB->f( "product_id" );
$aImageFile = "images/default.jpg";
if ( $aHasImage == True )
{
$aImageFile = "images/{$aMerchID}_{$aCatID}_{$aProdID}.jpg";
}

$aTPL->assign( array( "PROD_NAME" => $aProdName,
"CAT_NAME" => $aCatName,
"PROD_EID" => $aProdEID,
"PROD_PRICE" => '$' . number_format( $aProdPrice, 2 ),
"PROD_DESCR" => $aProdDescr,
"IMAGE_FILE" => $aImageFile

207 PHP – Kompendium wiedzy
) );
$aTPL->parse( "ITEM_LIST", ".prod_item" );
}
}
else
{
$aTPL->assign( array( "ITEM_LIST" => "Brak produktów dla kategorii <i>$aCatName</i>." ) );
}

$aTPL->assign( array( "TITLE" => "Zarządzanie
katalogiem towarów: Kategoria $aCatName",
"MERCHANT_NAME" => GetMerchantName( $aDB, $aMerchantID )
) );

$aTPL->parse( "PAGE_BODY", "page_body" );
$aTPL->parse( "FOOTER", "footer" );
$aTPL->parse( "BODY", "body" );
$aTPL->parse( "PAGE", "base" );
$aFileName = "mgmt_cat_{$aCatID}.html";
$aFile = fopen( $aFileName, "w" );
fwrite( $aFile, $aTPL->fetch( "PAGE" ) );
fclose( $aFile );
print("Kategoria, <i>$aCatName</i>, zapisana do pliku: <b>$aFileName</b><br>");
$aTPL->Clear();
}
?>
W skrypcie tym zakładamy, że w zmiennej sesji przekazany został identyfikator sprzedawcy. Podczas wykonywania swoich zadań klasa
FastTemplate zapisuje całą zawartość strony w wewnętrznych buforach. Aby zapisać te dane w dowolnej zmiennej można skorzystać z
metody fetch(). W poprzednim przykładzie metoda fetch() jest wykorzystywana do pobrania wartości zmiennej FastTemplate
PAGE, która reprezentuje całą stronę HTML. Wartość ta jest zapisywana do pliku wyjściowego, a do przeglądarki wysyłany jest
komunikat potwierdzający prawidłowe wykonanie operacji. Metoda Clear() powoduje skasowanie wszystkich buforów i zmiennych
FastTemplate, co umożliwia wykonanie następnego przebiegu pętli.
W poprzednim rozdziale przytoczona została specyficzna funkcja, GetCategoryHREF(), która była używana w aplikacji zarządzającej
katalogiem. Jest ona zamieszczona ponownie na wydruku 16.3. Jest ona u żywana do tworzenia łącza do strony zadanej kategorii. W
trybie domyślnym zwraca ona adres strony dynamicznej, ale gdy znacznik $aDynamic zostanie ustawiony na False, zwróci adres
strony statycznej.
Wydruk 16.3. Funkcja GetCategoryHREF()
function GetCategoryHREF( $aCatID, $aDynamic = True )
{
if ( $aDynamic == True )
{
return "mgmt_prod_list.phtml?cat_id=$aCatID";
}
else
{
return "mgmt_cat_{$aCatID}.html";
}
}
Stosując tą metodę, w trakcie edycji danych można przeglądać strony dynamiczne a później generować zbiór stron statycznych.
Dla wielu typów witryn generowanie statycznych stron z danych dynamicznych jest stosunkowo praktyczne i powoduje wzrost
wydajności serwera. Jeżeli dane wykorzystywane w aplikacji nie zmieniają się zbyt często, powinno się rozważyć generowanie
statycznych stron. W przypadku mocno obciążonych witryn wzrost wydajności może z łatwością przeważyć dodatkowe komplikacje
związane z tworzeniem skryptów generujących strony. Do generowania stron w regularnych odstępach czasu można wykorzystać
odpowiednie oprogramowanie systemowe, na przykład cron.

Techniki buforowania
Generowanie stron statycznych jest efektywne w przypadku wielu rodzajów witryn, ale aby było efektywne, wymaga dokładnego
projektowania i programowania. W przypadku wielu aplikacji wystarczająca powinna być pośrednia technika buforowania stron.
Koncepcyjnie buforowanie jest bardzo podobne do generowania stron statycznych i nadal wymaga ingerencji w tekst ka żdego ze
skryptów. Gdy użytkownik wysyła żądanie pobrania skryptu, na początku sprawdzane jest czy istnieje aktualna strona w buforze. Jeżeli
tak, zawartość tej strony jest wysyłana do przeglądarki. Jeżeli nie ma tej strony, jest ona generowana, umieszczana w buforze do
wykorzystania przez kolejne wywołanie.
To czy strona jest aktualna, zależy od typu strony. Na przykład w witrynie może istnieć strona powitalna z bieżącą datą. Jeżeli na tej
stronie nie ma więcej elementów dynamicznych, musi być ona generowana raz w ciągu dnia i jest aktualna przez 24 godziny. Inną stroną
może być strona z wiadomościami uaktualnianymi co godzinę. W tym przypadku strona jest aktualna przez godzinę.

Rozdział 16. Generowanie statycznych stron HTML w oparciu o dynamiczne dane 208
Implementacja stron buforowanych jest bardzo prosta. Można do tego celu wykorzystać buforowanie wyjścia lub szablony. Na
wydrukach 4. i 5. zamieszczony jest przykład z zastosowaniem buforowania wyjścia. Na wydruku 16.4. znajdują się funkcje realizujące
buforowanie, więc mogą zostać łatwo dołączone do każdej strony. Wydruk 16.5. zawiera główną stronę, która wyświetla bieżące dane o
pogodzie wykorzystując skrypt o nazwie MWeather. Skrypt ten jest dostępny na stronie http://sourceforge.net/prjects/mweather/.
Wydruk 16.4. Funkcje buforujące (cache.php)
<?php
function GetCacheFileName( $aFileName )
{
return $aFileName . ".cache";
}

function DumpCacheFile( $aFileName, $aExpire = 3600 )
{
$aCacheFile = GetCacheFileName( $aFileName );
if ( is_file( $aCacheFile ) == True )
{
$aModTime = filemtime( $aCacheFile );
$aCurTime = time();

if ( ( $aCurTime - $aModTime ) > $aExpire )
{
return False;
}
else
{
readfile( $aCacheFile );
return True;
}
}
}

function SaveCacheFile( $aFileName, $aContents )
{
$aCacheFile = GetCacheFileName( $aFileName );
$aFile = fopen( $aCacheFile, "w" );
fwrite( $aFile, $aContents );
fclose( $aFile );
}
?>
Wydruk 16.5. Wykorzystanie funkcji buforujących
<?php
error_reporting( E_ALL & ~E_NOTICE );
include( "./cache.php" );
if ( $aResult = DumpCacheFile( $PATH_TRANSLATED, 60 * 60 ) )
{
// aktualny plik w buforze, koniec skryptu
exit;
}
ob_start();

// dane strony
$aOldIncludePath = ini_get( 'include_path' );
ini_set( 'include_path', $aOldIncludePath . ":./mweather" );
include( "mweather.php" );
ini_restore( 'include_path' );

SaveCacheFile( $PATH_TRANSLATED, ob_get_contents() );
?>
Skrypt z wydruku 16.5. korzysta z funkcji buforujących umieszczonych na wydruku 16.4. w celu
zrealizowania cogodzinnego buforowania strony z prognozą pogody. Funkcja DumpCacheFile() realizuje kilka
ważnych operacji. Po pierwsze, sprawdzana jest data ostatniej modyfikacji pliku w buforze i jest ona
porównywana z bieżącą datą. Jeżeli różnica pomiędzy tymi datami jest większa od wartości expire, funkcja
zwraca False. Jeżeli tak nie jest, funkcja odczytuje plik z bufora, wyświetla go i zwraca wartość True. Po
powrocie do skryptu sprawdzana jest zwracana wartość. Jeżeli jest ona True, główny skrypt się kończy, ponieważ
plik z bufora jest aktualny i został on już wysłany do przeglądarki. Jeżeli wartość ta wynosi False, skrypt jest
wykonywany i za pomocą funkcji buforowania wydruku jego wynik jest zapamiętywany. Na końcu skryptu
wywoływana jest funkcja SaveCacheFile(), która zapisuje zawartość strony.
W przykładzie tym strona jest generowana przez dołączany skrypt mweather.php. Jedynym zadaniem
głównego skryptu jest modyfikacja zmiennej PHP include_path. Jest to wymagane przez skrypt MWeather i nie
ma nic wspólnego z buforowaniem.

209 PHP – Kompendium wiedzy
Plik cache.php może być używany z dowolnym skryptem PHP, w którym należy zrealizować funkcje
buforowania na żądanie. Bardzo ważne jest jednak, aby zdawać sobie sprawę, jakie skrypty mogą działać z tym
typem buforowania. Metoda ta sprawdza się jedynie dla stron, które nie są wynikiem żądań HTTP GET lub POST.
Inaczej mówiąc, buforowanie jest niepraktyczne w przypadku stron zależnych od wartości wprowadzonych przez
użytkownika. Poprzedni przykład pokazuje pogodę w Rexburg w stanie Idaho, niezależnie kto ogląda stronę.
Jeżeli skrypt wyświetlałby pogodę w miejscu określonym przez użytkownika, buforowanie nie może być już
zastosowane. Wyobraźmy sobie, że strona została by umieszczona w buforze przez użytkownika z Londynu, a
kolejne wywołanie pochodziło by z Cape Town. Otrzymana strona była by oczywiście nieprawidłowa dla
użytkownika z Cape Town.
Przedstawiony typ buforowania jest użyteczny, ale nie może być używany dla wszystkich przypadków.
Jeżeli aplikacja zawiera strony z informacjami zmieniającymi się co określony odcinek czasu i niezależne od
danych użytkownika, ten typ buforowania jest bardzo użyteczny i wyraźnie poprawia wydajność witryny. W
przedstawionym przykładzie do pobrania danych o aktualnej pogodzie potrzebny jest czas około dwóch sekund.
Po zbuforowaniu, strona pojawia się natychmiast.

Podsumowanie
Mimo, że PHP jest najczęściej używany do dynamicznego tworzenia stron WWW, ważne jest, aby
odnaleźć sytuacje, gdy nie są potrzebne w pełni dynamiczne strony. Generując statyczne strony lub buforując je,
można wyraźnie poprawić wydajność witryny. Projektanci witryny powinni w ten sposób zrównoważyć szybkość
ładowania się statycznych stron z elastycznością stron dynamicznych, aby aplikacja miała dostateczną szybkość i
była użyteczna dla użytkowników.

Rozdział 16. Generowanie statycznych stron HTML w oparciu o dynamiczne dane 210
Rozdział 17. Witryny handlu
elektronicznego
Wstęp
Handel elektroniczny jest dla większości firm najważniejszym zadaniem, jakie ma spełniać firmowa
witryna WWW. Z tego powodu niezmiernie ważne jest poznanie sposobu zrealizowania za pomocą PHP
wszystkich aspektów tworzenia aplikacji handlu elektronicznego. Duża część tego rozdziału jest poświęcona
koncepcjom i założeniom projektowania tego typu aplikacji. Samo programowanie i korzystanie z istniejących
narzędzi przeznaczonych dla e-handlu jest trywialne. Prawdziwym wyzwaniem jest stworzenie bezpiecznej,
stabilnej i skalowalnej aplikacji handlu elektronicznego.

Bezpieczeństwo
Pierwszorzędną kwestią przy tworzeniu aplikacji przeznaczonych do handlu elektronicznego jest
zagadnienie bezpieczeństwa. Już w czasie projektowania aplikacji należy mieć na uwadze ochronę danych o
klientach. Należy wykonać kilka kroków w celu zapewnienia możliwie najwyższego poziomu bezpieczeństwa.
Nie należy tego traktować jako propozycji, są to zazwyczaj wymagania większości centrów rozliczających karty
kredytowe.

Zastosowanie SSL
Pierwszym krokiem powinno być wyodrębnienie fragmentów witryny, które wymagają zastosowania
bezpiecznego połączenia za pomocą mechanizmu secure socket layer (SSL). Większość aplikacji posiada dwa
obszary działania. Pierwszy jest obszarem zawierającym opisy dostępnych produktów i usług oferowanych na
witrynie. W części tej zawierają się zwykle strony zawierające dane o firmie, regulaminy i inne dane nie związane
bezpośrednio z handlem. Drugi fragment zawiera aplikację handlową. W części tej zbierane są prywatne dane,
takie jak dane niezbędne do identyfikacji klienta lub numery kart kredytowych. Większość tego rozdziału jest
poświęcona temu właśnie fragmentowi witryny.
Użycie PHP z SSL nie różni się niczym do używania PHP na serwerze nie obsługującym SSL. Istnieje
kilka dostępnych bezpiecznych serwerów WWW. Jednym z nich jest Stronghold
(http://www.c2.net/products/sh3/). W dystrybucji RedHat (http://www.redhat.com/) istnieje również serwer
zawierający OpenSSL. Można go użyć do skompilowania bezpiecznego serwera Apache. Po zainstalowaniu
bezpiecznego serwera WWW opartego o Apache, można użyć jednej z metod opisanych w rozdziale 1.
„Kompilacja i instalowanie PHP” w celu dodania PHP w postaci współdzielonego modułu. Można również
przekompilować bezpieczny serwer Apache ze statycznie dołączonym modułem PHP.

Certyfikaty
Kolejnym krokiem wymaganym do stworzenia bezpiecznej aplikacji jest zainstalowanie certyfikatu
bezpieczeństwa. Certyfikat to plik na serwerze, który jest przesyłany do przeglądarki razem ze stronami WWW.
Certyfikaty są wystawiane przez kilka firm, które są rozpoznawane przez większość nowoczesnych przeglądarek
jako bezpieczne. Jedną z takich firm jest Thawte Consulting (http://www.thawte.com/). Witryna firmy Thawte
zawiera wszystkie dane niezbędne do wypróbowania i wykupienia certyfikatu bezpieczeństwa. Aby otrzymać
certyfikat należy dostarczyć kilka dokumentów:
1. Dokument potwierdzający istnienie firmy, na przykład akt założycielski spółki.
2. Dokument potwierdzający prawo do nazwy domeny.
Jeżeli nazwa widniejąca na dokumentach przedstawionych w punkcie 1. zgadza się z danymi uzyskanymi
przez zapytanie whois do domeny dla której instalowany jest certyfikat, nie trzeba dostarczać dokumentów
wymienionych w punkcie 2.
Z witryny firmy Thawte można pobrać i zainstalować certyfikat testowy. Jeżeli wykorzystujemy taki
certyfikat przeglądarka generuje ostrzeżenia, ale można przetestować aplikację bez konieczności kupienia
certyfikatu.
Jeżeli używanym serwerem jest Apache, aby zaczął on korzystać z certyfikatu należy zainstalować
certyfikat i zmodyfikować plik httpd.conf. Na wydruku 17.1. pokazana jest przykładowa konfiguracja wirtualnego
systemu hostingowego.
Wydruk 17.1. Plik httpd.conf z dyrektywami dotyczącymi certyfikatów
SSLCertificateFile /apache/conf/ssl.crt/server.crt
SSLCertificateKeyFile /apache/conf/ssl.key/server.key
NameVirtualHost 19.129.1.1:443
<VirtualHost 129.129.1.1:443>
SSLEnable
ServerAdmin webmaster@server.com
DocumentRoot /home/server/secure
ServerName secure.server.com
DirectoryIndex index.phtml index.html
</VirtualHost>
Na witrynie firmy Thawte dostępne są wszystkie informacje potrzebne do wygenerowania i
zainstalowania certyfikatu. Jeżeli potrzebne jest więcej informacji na temat instalowania certyfikatu w
konkretnym serwerze WWW, należy ich szukać w dokumentacji serwera. Jeżeli korzystasz z dystrybucji RedHat,
to informacje te są bardzo jasno napisane i łatwe do odszukania.

Bezpieczeństwo bazy danych
Jednym z odkryć jakie zawdzięczamy ostatnio internetowym złodziejom numerów kart kredytowych jest
to, że we wielu przypadkach witryny przechowują dane w niezaszyfrowanej bazie danych dostępnej bezpośrednio
z Internetu. Nawet witryny używające kodowania SSL do zbierania danych, przechowują dane prywatne w mało
bezpieczny sposób. Ostatnio jednak, niektóre umowy pomiędzy sprzedawcami a centrami rozliczeniowymi
zawierają klauzulę o przechowywaniu danych w postaci zaszyfrowanej, umieszczeniu bazy danych za firewallem,
albo o zastosowaniu obu tych rozwiązań. Zaczyna być to powszechnie stosowaną praktyką. Aplikacja powinna
kodować zarówno transmisję jak również przechowywane dane prywatne.
Wiele nowoczesnych systemów baz danych zawiera funkcje szyfrujące dane. Na przykład SZBD MySQL
zawiera funkcje ENCODE() oraz DECODE() jako integralną część języka. Funkcje te nie są jednak wystarczająco
dobre do szyfrowania krytycznych danych. Użycie tych lub podobnych funkcji poprawia jednak poziom ochrony
przechowywanych danych. Ostatecznie to ty i twoja firma jesteście odpowiedzialni za dziury w systemie
bezpieczeństwa, które spowodują ujawnienie krytycznych danych.

Przetwarzanie płatności
Po skompilowaniu i przetestowaniu bezpiecznego serwera WWW należy wybrać metodę obsługi
płatności. PHP zawiera własne interfejsy do kilku systemów przetwarzania płatności, np.: CyberCash, VeriSign i
CCVS. Na rysunku 17.1. przedstawiony został obieg informacji pomiędzy użytkownikiem, bezpiecznym
serwerem WWW i narzędziami obsługi płatności.

Rozdział 17. Witryny handlu elektronicznego 212
Rysunek 17.1.
Zależności w
aplikacji handlu
elektronicznego

Na rysunku 17.1. zostały pokazane cztery podstawowe jednostki biorące udział w obsłudze płatności:
użytkownik, serwer, system przetwarzania i instytucja finansowa. Klient i serwer komunikują się ze sobą za
pomocą protokołu SSL, który zapewnia dwukierunkową szyfrowaną transmisję. Serwer komunikuje się z
systemem obsługi płatności za pomocą protokołu narzuconego przez system. Sam system przetwarzania
komunikuje się z instytucją finansową w celu sprawdzenia czy może przyjąć płatność za pomocą karty
kredytowej (lub innej metody płatności). Jeżeli korzystamy z systemu CCVS możliwe jest ominięcie systemu
przetwarzania i bezpośrednia komunikacja z finansowym centrum rozliczeniowym.
Jednym z powodów wyboru systemu obsługi płatności CyberCash jest to, że protokół komunikacyjny
używany do przesyłania informacji pomiędzy serwerem a systemem obsługi transakcji, jest wbudowany w
interfejs programistyczny do CyberCash. Inne systemy transakcji wymagają użycia przez aplikację bezpiecznych
gniazd, w celu zrealizowania komunikacji z nimi. Należy pamiętać, że mimo użycia bezpiecznego serwera
wykorzystującego protokół SSL, sam PHP nie posiada implementacji SSL. Inaczej mówiąc, jeżeli skrypt otworzy
port, to nie zostanie automatycznie użyty protokół SSL, pomimo że serwer WWW używa SSL. Dopóki PHP nie
będzie posiadał bezpośredniej obsługi gniazd SSL, wykorzystanie niektórych możliwości systemów
transakcyjnych nie będzie możliwe. CyberCash używa do zapewnienia bezpiecznej transmisji pomiędzy
serwerem WWW a centrum przetwarzania, własnego algorytmu Triple DES (potrójny DES). W pozostałej części
rozdziału będziemy wykorzystywać CyberCash jako system przetwarzania płatności.
CyberCash (http://www.cybercash.com/) jest firmą oferującą interfejsy programistyczne (API) dla C i C++
oraz Javy. Wersja API dla C i C++ może zostać wbudowana w PHP za pomocą opcji konfiguracji --with-
cybercash. Najpierw należy ściągnąć z witryny CyberCash pakiet Merchant Connection Kit (MCK). Oprócz tego
należy upewnić się, że twoje konto sprzedawcy może być używane w systemie CyberCash. Jeżeli nie masz
jeszcze konta sprzedawcy, należy je zarejestrować u jednego z partnerów firmy CyberCash. Proces ten wymaga
podania wielu szczegółowych informacji finansowych, ale może być przeprowadzony w ciągu 24 godzin. Główne
kroki wymagane do zainstalowania MCK są następujące:
1. Nawiązanie współpracy z przedstawicielem finansowym firmy CyberCash (konto sprzedawcy).
2. Zarejestrowanie się na witrynie http://amps.cybercash.com/ jako sprzedawca CyberCash.
3. Ściągnięcie MCK.
4. Dekompresja i zainstalowanie MCK.
5. Kompilacja PHP z obsługą CyberCash (--with-cybercash).
Na witrynie WWW firmy CyberCash znajduje się dokładny opis instalacji MCK. Pakiet ten może być
zainstalowany na systemach Unix lub pod Windows. Procedura instalacji dla systemów Unix jest bardzo prosta.
Podstawowe kroki są następujące:
1. Dekompresja instalatora: uncompress install-mck-3.2.0.6-<system_operacyjny>.Z.

213 PHP – Kompendium wiedzy
2. Zmiana uprawnień do pliku instalatora w taki sposób, aby można było go uruchomić: chmod +x install-
mck-3.2.0.6-<system_operacyjny>.
3. Uruchomienie programu instalacyjnego: ./install-mck-3.2.0.6-<system_operacyjny>.
4. Uruchomienie programu konfiguracyjnego: ./configure.
W punktach 3. i 4. zostaną zadane pytania na temat firmy, sklepu sieciowego i innych informacji tego
typu. Po wykonaniu tych kroków można przekompilować PHP podając opcję konfiguracji --with-
cybercash=/ścieżka/do/MCK. Należy podać pełną ścieżkę do katalogu gdzie został zainstalowany MCK. Po
utworzeniu PHP z obsługą CyberCash można przeprowadzić testowe transakcje.
Jedną z miłych cech użycie MCK wraz z PHP jest możliwość opuszczenia większości opcji konfiguracji
(jak sugeruje to podręcznik). Użycie PHP do komunikacji z CyberCash jest bardzo proste i wymaga jedynie
dołączenia jednego skryptu. Skrypt pokazujacy sposób użycia CyberCash znajduje się w dystrybucji PHP
zawierającej pełny kod źródłowy w katalogu <php>/ext/cybercash. Na wydruku 17.2. zamieszczony jest ten
właśnie skrypt testowy.
Wydruk 17.2. Test.php (skrypt testowy CyberCash)
<?php
require "cyberlib.php";

$merchant=""; /* Tutaj należy umieścić idnetyfikator sprzedawcy. */
$merchant_key=""; /* Tutaj należy umieścić klucz sprzedawcy. */
$payment_url="http://cr.cybercash.com/cgi-bin/";
$auth_type="mauthonly";

$response=SendCC2_1Server($merchant,$merchant_key,$payment_url,
$auth_type,array("Order-ID" => "2342322",
"Amount" => "usd 11.50",
"Card-Number" => "4111111111111111",
"Card-Address" => "1600 Pennsylvania Avenue",
"Card-City" => "Washington",
"Card-State" => "DC",
"Card-Zip" => "20500",
"Card-Country" => "USA",
"Card-Exp" => "12/99",
"Card-Name" => "Bill Clinton"));

while(list($key,$val)=each($response))
{
echo $key."=".$val."<br>";
}

?>
Jak widać przetwarzanie płatności przy pomocy PHP i CyberCash jest bardzo proste. Zmienne $merchant
oraz $merchant_key muszą zawierać identyfikator sprzedawcy oraz jego klucz nadany przez CyberCash.
Następnie po ustawieniu w tablicy asocjacyjnej danych wymaganych przez CyberCash płatność jest
przekazywana do obsługi przy pomocy wywołania funkcji SendCC2_1Server() zdefiniowanej w dołączonym pliku
cyberlib.php. Nie potrzeba nawet wywoływać żadnej z funkcji cybercash_xxx() dostępnych w PHP. Funkcja
SendCC2_1Server() hermetyzuje w sobie wszystkie niezbędne funkcje, jak również obsługuje komunikację za
pomocą gniazd z serwerem CyberCash.
Dane niezbędne do działania z Cyberash są przekazywane za pomocą tablicy asocjacyjnej będącej
ostatnim parametrem funkcji SendCC2_1Server(). Zawartość tej tablicy jest określana przez wartość zmiennej
$auth_type. Zmienna ta określa rodzaj wykonywanej operacji lub typ komunikatu przesyłanego do CyberCash.
Komunikaty obsługiwane przez CyberCash zawarte są w tablicy 17.1.
Tabela 17.1. Komunikaty dostępne w CyberCash oraz zadania przez nie realizowane
Komunikat Realizowane zadanie
batch-commit Potwierdza transakcje zebrane w grupę.
batch-prep Powoduje wysłanie transakcji oznaczonych jako
gotowe do przetworzenia w postaci grupy.
batch-query Pytanie o grupę.
batch-retry Ponawia próbę przetworzenia oczekującej grupy.
batch-unroll Zapytanie o transakcję wysłaną w postaci grupy.
card-query Odczytuje dane karty kredytowej dla podanego
zamówienia.
checkauth Sprawdza i autoryzuje płatność czekiem
Rozdział 17. Witryny handlu elektronicznego 214
zainicjowaną przez sprzedawcę. Komunikat ten jest
obsługiwany jedynie przez procesor Paymentech
(wykorzystując opcję Electronic Check Payment — ECP).
checkreturn Zwraca pieniądze na konto czekowe klienta.
mauthcapture Autoryzuje i przechwytuje rozpoczętą przez
sprzedawcę transakcję za pomocą karty kredytowej. Jest
używany jedynie dla głównych procesorów
przechwytujących.
mauthonly Autoryzuje rozpoczętą przez sprzedawcę sprzedaż za
pomocą karty kredytowej. Jest używany dla końcowych
procesorów przechwytujących.
postauth Przechwytuje płatność kartą kredytową
autoryzowaną za pomocą mauthonly lub checkauth.
query Odpytuje bazę transakcji.
retry Ponawia oczekującą transakcję dla podanego
zamówienia.
return Zwraca pieniądze na kartę kredytową klienta.
void Unieważnia transakcję.
merchant-check- Sprawdza i autoryzuje zainicjowaną przez
payment
sprzedawcę płatność w systemie PayNow.
check-query Odszukuje w bazie danych PayNow danych na temat
określonych rachunków.
check-update-status Uaktualnia serwer rachunków za pomocą zmian
przeprowadzonych w bramce.
check-query-order- Sprawdza bieżący status zamówień.
status
Dokumentacja CyberCash zawiera dokładny opis każdego z komunikatów oraz jego przeznaczenia. W
większości przypadków pierwszym obsługiwanym komunikatem (i często jedynym) jest komunikat mauthonly.
Jest on używany do autoryzacji płatności dokonywanej kartą kredytową. W zależności od rodzaju sprzedawanego
produktu lub usługi, można tak skonfigurować CyberCash, aby automatycznie zaznaczał i realizował wszystkie
poprawnie autoryzowane transakcje.
W tabeli 17.2. zebrane są wszystkie pola używane do obsługi komunikatu mauthonly. W tabeli zaznaczone
są wszystkie pola, które należy obowiązkowo umieścić w komunikacie.
Tabela 17.2. Pola komunikatu mauthonly i ich opis
Pole Opis Wy
magane
order- Unikalny identyfikator transakcji. þ
id
amount Kwota do autoryzacji (to znaczy kwota płatności) w tej þ
transakcji. Należy używać notacji waluta złote.grosze (na
przykład: usd 12.50).
card- Numer karty kredytowej obciążanej tą transakcją. þ
number
card- Data ważności karty kredytowej obciążanej tą þ
exp
transakcją. Należy użyć formatu mm/rr (na przykład: 02/01 dla
lutego 2001).
card- Nazwa właściciela karty kredytowej.
name
card- Adres zamieszkania właściciela karty kredytowej.
address
UWAGA: pole jest wymagane w przypadku kart AVS i
Discover.
card- Miejscowość w której mieszka właściciel karty.
city
UWAGA: pole jest wymagane w przypadku kart AVS i
Discover.

215 PHP – Kompendium wiedzy
card- Kod pocztowy miejscowości, w której mieszka
zip
właściciel karty. Prawidłowymi zapisami są „22091”, „20191-
1448,”, „NW3 5RJ” i „113 192”. UWAGA: pole jest
wymagane w przypadku kart AVS i Discover.
card- Stan w którym mieszka właściciel karty. UWAGA:
state
pole jest wymagane w przypadku kart AVS i Discover.
card- Kraj w którym mieszka właściciel karty.
country
Funkcja SendCC2_1Server() zwraca wartości również w postaci tablicy asocjacyjnej, która zawiera dane
na temat transakcji przesłane przez CyberCash. W tabeli 17.3. znajdują się pola znajdujące się w odpowiedzi na
komunikat mauthonly.
Tabela 17.3. Pola odpowiedzi na komunikat mauthonly i ich opis
Pole Opis
MStatus Zwracany kod statusu wykonanej operacji Pole to zawsze istnieje.
Może przyjmować jedną z następujących wartości:
• success — transakcja udana.
• success-duplicate — wynik poprzedniej udanej transakcji.
• partial success — grupa zawiera nieudane transakcje.
• failure-hard — nieudana transakcja, jej powtórzenie nie uda się.
• failure-q-or-cancel, failure-q-or-discard — nieudane transakcje
z powodu problemów z transmisją, mogą być powtórzone.
• failure-swversion — transakcja nieudana z powodu użycia starego
lub nieistniejącego (nieistniejący numer wersji) oprogramowania.
• failure-bad-money — transakcja nieudana z powodu problemu z
obciążeniem przez instytucję finansową.
MErrLoc Miejsce wystąpienia błędu w transakcji. Pole to pojawia się jedynie w
przypadku nieudanej transakcji (czyli MStatus jest różny od success).
Zwracane są następujące wartości:
• smps — wystąpił błąd w CashRegister.
• ccsp — wystąpił błąd w bramce.
• financial institution — błąd wystąpił w instytucji finansowej
• CCMckDirectLib3_2 — błąd wystąpił w MCK
MErrMsg Tekst komunikatu błędu zwracanego przez transakcję. Pole to
występuje jedynie w przypadku nieudanej transakcji.
MErrCod Numer błędu odpowiadający komunikat błędu przekazanego w
e
MErrMsg.
merch- Numer używany przez bramkę do identyfikacji przeprowadzanej
txn
transakcji. Pole to występuje zawsze.
order- Identyfikator zamówienia do którego należy przetwarzana transakcja.
id
Pole to występuje zawsze.
cust- Numer transakcji używany przez portfel CyberCash do identyfikacji
txn
transakcji. Dla transakcji nie posiadających portfela jest on taki sam jak
merch-txn.
aux-msg Komunikat bramki sprzedawcy zawierający dodatkowy opis z bramki
lub serwera płatności.
MSWErrM Komunikat błędu przy użyciu nieaktualnej wersji portfela lub serwera
sg
płatności. Pole to nie zawsze występuje.
addnl- Dodatkowe dane transakcji zwracane przez bramkę. UWAGA: Jeżeli
response-data
brak jest takich danych pole to jest puste.
Na wydruku 17.3. znajduje się przykład użycia CyberCash, pochodzący z działającej witryny. Przykład
ten zawiera obsługę błędów oraz wysyła informacje zwrotne do klienta. Elementy te nie były zawarte w
przykładowym skrypcie z wydruku 17.2.

Rozdział 17. Witryny handlu elektronicznego 216
Wydruk 17.3. Użycie CyberCash
<?php
include "cyberlib.php"; // Funkcje obsługi CyberCash
include "dbclass.php"; // Klasa bazy danych dla tej aplikacji
include "class.FastTemplate.php"; // FastTemplate

// funkcja zamieniająca tablicę asocjacyjną
// w jeden ciąg rozdzielany średnikami
function ArrayCrunch( $aArray )
{
$aResult = "";

foreach( $aArray as $aKey => $aValue )
{
$aResult .= "$aKey=$aValue;";
}

return $aResult;
}

$tpl = new FastTemplate( "." );

// pobranie identyfikatora pliku z zaszyfrowanego pola formularza
$aCustomerID = UnhideID( $ID );

$merchant = ""; /* tutaj identyfikator sprzedawcy. */
$merchant_key = ""; /* tutaj klucz sprzedawcy. */
$payment_url = "http://cr.cybercash.com/cgi-bin/";
$auth_type = "mauthonly";

$aDB = new dbAccess;
$aDB->Init();

// kontrola czy klient ten nie zapłacił wcześniej
$aSQL = "select ISPAID from orders where ( ID = $aCustomerID )";
$aDB->SetSQL( $aSQL );
if ( $aDB->RecordCount() == 1 )
{
$aRow = $aDB->GetData( 0 );
$aIsPaid = $aRow["ISPAID"];
}
else
{
$aIsPaid = 0;
}

// Jeżeli klient już zapłacił, pokaż informację i zakończ skrypt
if ( $aIsPaid == 1 )
{
mail( "blake@intechra.net", "Ponowna próba płatności ($aCustomerID)",
"Czy chcesz jeszcze raz zapłacić??", "From: support@intechra.net\r\n" );

$tpl->define( array( base => "a_base.tpl",
footer => "c_footer.tpl",
body => "s_paid_already.tpl"
) );

$tpl->assign( array( TITLE => "Płatność Intechra.Net",
URL => $aInternalURL,
THEDATE => date( "l, j F Y" ),
ORDERID => $aOrderID,
HIDDENID => $ID
) );

AddSiteVars( $tpl, $aHiddenID );

$tpl->parse( FOOTER, "footer" );
$tpl->parse( BODY, "body" );
$tpl->parse( PAGE, "base" );
$tpl->FastPrint( PAGE );
exit;
}

// pobierz nowy MCK_ID dla tego klienta
$aSQL = "select MAX( MCK_ID ) as MAX_ID from customers_to_mcks
where ( ID = $aCustomerID )";
$aDB->SetSQL( $aSQL );
if ( $aDB->RecordCount() == 1 )
{
$aRow = $aDB->GetData( 1 );

217 PHP – Kompendium wiedzy
$aMaxID = $aRow["MAX_ID"];
}
else
{
$aMaxID = 0;
}

$aCurID = $aMaxID + 1;
$aOrderID = "INT-" . date( "Ymd" ) . "-" . sprintf( "%06d", $aFamilyID ) .
"-" . sprintf( "%04d", $aCurID );

$aOrderDetails = array( "Order-ID" => $aOrderID,
"Amount" => "usd 39.00",
"Card-Number" => $CCNum,
"Card-Address" => $CCAddr,
"Card-City" => $CCCity,
"Card-State" => $CCState,
"Card-Zip" => $CCZip,
"Card-Exp" => $CCExpDate,
"Card-Name" => $CCName );

$response = SendCC2_1Server( $merchant, $merchant_key, $payment_url,
$auth_type, $aOrderDetails );

$aRawRequest = ArrayCrunch( $aOrderDetails );
$aRawResponse = ArrayCrunch( $response );

$aSQL = "insert into customers_to_mcks
values ( $aCustomerID, $aCurID, 0, NOW(), ENCODE( \"$aRawRequest\",
\"good_password\" ), ENCODE( \"$aRawResponse\",
\"good_password\" ) )";
$aDB->SetSQL( $aSQL );

if ( $response["MStatus"] == "success" )
{
mail( "blake@intechra.net",
"Płatność dla Intechra.net zrealizowana ($aCustomerID)",
"Płatność zakończona sukcesem", "From: support@intechra.net\r\n" );

$aSQL = "update orders set ISPAID = 1 where ( ID = $aCustomerID )";
$aDB->SetSQL( $aSQL );

$tpl->define( array( base => "a_base.tpl",
footer => "c_footer.tpl",
body => "s_paid_ok.tpl"
) );

$tpl->assign( array( TITLE => "Płatności Intechra.net",
URL => $aInternalURL,
THEDATE => date( "l, j F Y" ),
ORDERID => $aOrderID,
HIDDENID => $ID
) );

AddSiteVars( $tpl, $aHiddenID );

$tpl->parse( FOOTER, "footer" );
$tpl->parse( BODY, "body" );
$tpl->parse( PAGE, "base" );
$tpl->FastPrint( PAGE );
}
else
{
mail( "blake@intechra.net",
"Płatność dla Intechra.net nieudana ($ ($aCustomerID)",
"Płatność nieudana", "From: support@intechra.net\r\n" );

$tpl->define( array( base => "a_base.tpl",
footer => "c_footer.tpl",
body => "s_paid_fail.tpl"
) );

$tpl->assign( array( TITLE => "Płatności Intechra.net",
HIDDENID => $ID
) );

$tpl->parse( FOOTER, "footer" );
$tpl->parse( BODY, "body" );
$tpl->parse( PAGE, "base" );
$tpl->FastPrint( PAGE );

Rozdział 17. Witryny handlu elektronicznego 218
}
?>
Skrypt z wydruku 17.2. pokazuje w jaki sposób na podstawie skryptu z wydruku 17.1. można stworzyć
prawdziwy system obsługi płatności. Skrypt ten jest używany do przetwarzania danych formularza, w którym
użytkownik podaje dane karty kredytowej. Na początku skrypt pobiera identyfikator klienta z pola formularza.
Następnie inicjuje zmienne CyberCash, w tym ustawiając typ komunikatu na mauthonly. Następnie w bazie
danych sprawdzane jest, czy użytkownik nie zapłacił już wcześniej. W tej aplikacji każdy użytkownik dokonuje
jednej płatności za usługę. Jeżeli użytkownik już zapłacił skrypt wysyła do administratora pocztę zawierającą
komunikat informujący o tym fakcie, oraz wyświetla stronę informującą użytkownika, że dokonał już wcześniej
płatności. Na tym skrypt się kończy.
Jeżeli płatność nie była jeszcze dokonana, z bazy danych pobierany jest nowy niepowtarzalny
identyfikator. Dokumentacja CyberCash nakazuje, aby identyfikator ten był co najwyżej 25 znakowy i musi być
unikalny. Można używać w nim liter, liczb, kropek, daszków i podkreśleń. Skrypt ten generuje identyfikatory w
postaci RRRRMMDD-CCCCCC-OOOO, gdzie YYYYMMDD jest bieżącą datą, CCCCCC to identyfikator klienta a OOOO jest
identyfikatorem pobieranym z bazy danych.
W tablicy $aOrderDetails umieszczane są dane płatności i wywoływana jest funkcja SendCC2_3Server().
Żądanie i odpowiedź są zapisywane do bazy danych w postaci zaszyfrowanej. Następnie sprawdzany jest status
odpowiedzi. Jeżeli transakcja zakończyła się powodzeniem, wyświetlany jest komunikat o prawidłowym
przetworzeniu transakcji i uaktualniane są dane klienta na temat płatności. Jeżeli operacja się nie powiedzie,
klient jest o tym informowany. W każdym z przypadków do administratora wysyłany jest informujący e-mail.
Przeglądając ten przykład można stwierdzić, że użycie API CyberCash w PHP jest proste, ale stworzenie
bezpiecznej i stabilnej aplikacji handlu elektronicznego wymaga sporej ilości przemyśleń i planowania. Po
systemie obsługującym prawdziwe pieniądze klienci oczekują najlepszej jakości usług. Jeżeli aplikacja pozwoli
na przypadkowe wielokrotne płatności lub nie dostarczy odpowiednich informacji, klienci nie będą zadowoleni.
Należy poświęcić nieco czasu na przestudiowanie dokumentacji procesora transakcji. Dostępne jest mnóstwo
informacji i ty jesteś odpowiedzialny za ich zrozumienie i właściwą implementację.

Dostarczanie produktów
Innym aspektem aplikacji handlu elektronicznego, który wymaga planowania jest kwestia dostarczania
towarów do klientów. Jeżeli twój towar musi być wysłany do klienta, wymagane jest kilku dodatkowych kroków
oprócz wymienionych poprzednio. Powodem jest to, że wielu wystawców kart kredytowych żąda, aby karta była
obciążana dopiero po dostarczeniu towaru do klienta. Można żądać autoryzacji przed dostarczeniem towaru, ale
obciążenie nie będzie zrealizowane do czasu dostarczenia towaru. Ważne jest również podawanie rzeczywistego
kosztu przesyłki.
Jeżeli wysyłasz towary, aplikacja przetwarzania płatności staje się o wiele bardziej skomplikowana od
pokazanej w tym rozdziale. Jednak wiele wymaganych czynności może być zrealizowane za pomocą PHP.
Zalecanym scenariuszem jest stworzenie osobnej aplikacji zapisującej stan przesyłek i po dostarczeniu przesyłki
kończona jest płatność kartą i do klienta wysyłany jest komunikat.
Dostępne jest kilka możliwości śledzenia na bieżąco przesyłek i kosztów. Zarówno UPS
(http://www.ups.com/) jak i Federal Express (http://www.fedex.com/) dostarczają narzędzia do integracji modułu
przesyłek we własnej aplikacji WWW. Narzędzia UPS są bardzo elastyczne i wydajne i łatwo mogą być
umieszczone w aplikacji PHP korzystając z niewielkiej ilości kodu. Niestety nie otrzymałem pozwolenia na
dokumentację żadnego z tych systemów przed zakończeniem tego rozdziału. Jeżeli otrzymam pozwolenie,
umieszczę odpowiednie informacje na witrynie WWW wymienionej w zasobach sieci w dodatkach. Można
jedynie powiedzieć, że wykorzystując narzędzia sieciowe UPS można skorzystać z mechanizmów obliczających
dokładne koszta przesyłek da dowolnych miejsc na ziemi przy użyciu dowolnego poziomu usługi. Dane są
pobierane z witryny UPS — aplikacja odpytuje ich serwery pobierając dane prawidłowe w momencie sprzedaży.

219 PHP – Kompendium wiedzy
Podsumowanie
Tworzenie aplikacji handlu elektronicznego nie jest zbytnim wyzwaniem patrząc jedynie od strony kodu.
Wyzwaniem staje się stworzenie aplikacji, która jest jednocześnie bezpieczna i prosta w użyciu, oraz integruje w
sobie wszystkie niezbędne technologie. Tworząc aplikacje handlowe należy poświęcić nieco czasu na poznanie
wszystkich komponentów oraz na gruntowne testowanie systemu. Należy również szyfrować przechowywane
dane oraz całą transmisję pomiędzy klientem i serwerem realizować za pomocą SSL.

Rozdział 17. Witryny handlu elektronicznego 220
Dodatek A. Funkcje
function_exists
Szuka w liście zdefiniowanych funkcji nazwy przekazanej w $function_name. Zwraca True, jeżeli
znaleziono podaną nazwę funkcji, w przeciwnym wypadku zwraca False.
int function_exists( string nazwa_funkcji )

func_get_arg
Zwraca argument numer $arg_num z listy argumentów funkcji. Argumenty są numerowane od 0.
Func_get_arg() generuje ostrzeżenie jeżeli jest wywołana poza funkcją. Jeżeli wartość $arg_num jest większa niż
ilość przekazanych argumentów, generowane jest ostrzeżenie a funkcja func_get_arg() zwraca False.
mixed func_get_arg( int arg_num )

<?php
function foo()
{
$numargs = func_num_args();
echo "Ilość argumentów: $numargs<br>\n";
if ($numargs >= 2)
echo "Drugi argument: " . func_get_arg(1) . "<br>\n";
}
foo (1, 2, 3);
?>
Funkcja func_get_arg() może być używana wraz z func_num_args() i func_get_args() do zrealizowania
funkcji ze zmienna liczbą argumentów. Funkcja została dodana w PHP 4.

func_get_args
Zwraca tablicę, w której każdy element zawiera odpowiedni argument z listy argumentów funkcji. Funkcja
func_get_args() generuje ostrzeżenie w wypadku wywołania jej spoza definicji funkcji.
array func_get_args( void )

<?php
function foo()
{
$numargs = func_num_args();
echo "Ilość argumentów: $numargs<br>\n";
if ($numargs >= 2)
echo "Drugi argument: " . func_get_arg(1) . "<br>\n";
$arg_list = func_get_args();
for ($i = 0; $i < $numargs; $i++)
echo "Argument $i = " . $arg_list[$i] . "<br>\n";
}

foo (1, 2, 3);
?>
Funkcja func_get_args() może być używana wraz z func_num_args() i func_get_arg() do zrealizowania
funkcji ze zmienna liczbą argumentów. Funkcja została dodana w PHP 4.

func_num_args
Zwraca liczbę argumentów przekazanych do bieżącej funkcji. Funkcja func_num_args() generuje
ostrzeżenie w przypadku wywołania jej z poza funkcji.
int func_num_args( void )
<?php
function foo()
{
$numargs = func_num_args();
echo "Ilość argumentów: $numargs<br>\n";
}

foo (1, 2, 3);
?>
Funkcja func_num_args() może być używana wraz z func_get_args() i func_get_arg() do zrealizowania
funkcji ze zmienna liczbą argumentów. Funkcja została dodana w PHP 4.

fwrite
Zapisuje zawartość $string do pliku wskazywanym przez $fp. Jeżeli podany został argument $length,
zapisywanie jest przerywane po zapisaniu $length bajtów lub całej zawartości $string. Jeżeli podany został
argument $length, ignorowana jest opcja konfiguracji magic_quotes_runtime i z $string nie będą usuwane
ukośniki. Patrz również fread(), fopen(), fsockopen(), popen() i fputs().
int fwrite( int fp, string [, int length])

getallheaders
Zwraca tablicę asocjacyjną z wszystkimi nagłówkami HTTP wysłanymi wraz z bieżącym żądaniem.
Wskazówka

Można również odczytać wartości zmiennych współdzielonych CGI ze środowiska. Można to zrealizować gdy PHP pracuje jako moduł
Apache lub jako CGI. Aby odczytać wszystkie zmienne środowiska zdefiniowane w ten sposób należy użyć funkcji phpinfo().

array geallheaders( void )
Przykład: getallheaders()
$headers = getallheaders();
while (list ($header, $value) = each($headers))
{
echo "$header: $value<br>\n";
}
Przykład ten wyświetla wszystkie nagłówki bieżącego żądania HTTP. Funkcja getallheaders() działa
jedynie, gdy PHP pracuje jako moduł Apache.

getcwd
Zwraca bieżący katalog.
string getcwd( void )

getdate
Zwraca tablicę asocjacyjną zawierającą informacje o dacie odczytane na podstawie parametru $timestamp.
Tablica zawiera następujące elementy:
• seconds — sekundy
• minutes — minuty
• hours — godziny
• mday — dzień miesiąca
• wday — dzień tygodnia jako numer
• mon — miesiąc jako numer
• year — rok jako numer
• yday — dzień w roku jako numer, na przykład 299
• weekday — dzień tygodnia jako tekst, na przykład Friday
• month — miesiąc jako tekst, na przykład January
Dodatek A - Funkcje 222
array getdate( int timetamp )

getenv
Zwraca wartość zmiennej środowiska o nazwie $varname, lub w przypadku wystąpienia błędu False.
string getenv( string varname )

$ip = getenv( "REMOTE_ADDR" ); // odczytuje numer IP użytkownika
Listę zmiennych środowiska można uzyskać za pomocą funkcji phpinfo(). Znaczenie wielu z nich opisane
jest w specyfikacji CGI (http://hoohoo.ncsa.uiuc.edu/cgi/) na stronie poświęconej zmiennym środowiska
(http://hoohoo.ncsa.uiuc.edu/cgi/env.html).
Uwaga

Funkcja ta nie działa w trybie ISAPI.

gethostbyaddr
Zwraca nazwę komputera o adresie przekazanym w argumencie $ip_address. W przypadku wystąpienia
błędu funkcja zwraca $ip_address. Patrz również: gethostbyname().
string gethostbyaddr( string ip_address )

gethostbyname
Zwraca adres IP komputera o nazwie przekazanej w $hostname. Patrz również: gethostbyaddr().
string gethostbyname( string hostname )

gethostbynamel
Zwraca listę adresów IP skojarzonych z nazwą $hostname. Patrz również: gethostbyname(),
gethostbyaddr(), checkdnserr(), getmxrr() oraz man named(8).
string gethostbynamel( string hostname )

GetImageSize
Odczytuje wielkość rysunku GIF, JPG, PNG lub SWF i zwraca wymiary, typ pliku oraz ciąg tekstu z
szerokością i wysokością w postaci fragmentu znacznika IMG. Funkcja zwraca tablicę z 4 elementami. Pod
indeksem 0 znajduje się szerokość rysunku w pikselach, pod indeksem 1 znajduje się wysokość rysunku. Indeks 2
zawiera znacznik określający typ rysunku, 1 = GIF, 2 = JPG, 3 = PNG, 4 = SWF. Pod indeksem 3 znajduje się
ciąg zawierający tekst height=xxx width=xxx, który może być użyty bezpośrednio w znaczniku IMG.
array GetImageSize( string filename [, array imgeinfo])
Przykład: GetImageSize()
<?php $size = GetImageSize ("img/flag.jpg"); ?>
<IMG SRC="img/flag.jpg" <?php echo $size[3]; ?>
Opcjonalny parametr $imageinfo pozwala na odczytanie dodatkowych danych z pliku rysunku. W chwili
obecnej zwracane są różne znaczniki APP pliku JPG w postaci tablicy asocjacyjnej. Niektóre programy
wykorzystują znaczniki APP do umieszczenia w rysunku informacji tekstowej. Częstym zastosowaniem jest
umieszczanie danych IPTC http://www.xe.net/iptc/ w znaczniku APP13. Do zamiany binarnego znacznika APP13
na postać czytelną dla człowieka można użyć funkcji iptcparse().
Przykład: GetImageSize() zwracający IPTC
<?php
$size = GetImageSize ("testimg.jpg", &$info);
if (isset(($info["APP13"]))
{
$iptc = iptcparse( $info["APP13"]);
var_dump( $iptc);
}
?>

Uwaga
223 PHP – Kompendium wiedzy
Funkcja ta nie wymaga biblioteki GD

getlastmod
Zwraca czas ostatniej zmiany bieżącej strony. Zwracana wartość jest znacznikiem czasu Uniksa. W
przypadku błędu zwraca False.
int getlastmod( void )
Przykład: getlastmod()
<?php
// zwraca ciąg w postaci 'Ostatnia modyfikacja: March 04 1998 20:43:59.'
echo "Ostatnia modyfikacja: ". date ("F d Y H:i:s.", getlastmod());
?>
Patrz również: date(), getmyuid(), get_current_user(), getmyinode() oraz getmypid().

getmxrr
Szuka w DNS rekordu MX skojarzonego z $hostname. Zwraca True, jeżeli znalezione zostały jakieś
rekordy, w przypadku wystąpienia błędu lub braku rekordów zwracana jest wartość False. Lista znalezionych
rekordów MX jest umieszczana w tablicy $mxhosts. Jeżeli zostanie podana tablica $weight, zostanie wypełniona
wagami odnalezionych rekordów. Patrz również: checkdnsrr(), gethostbyname(), gethostbynamel(),
gethostbyaddr() oraz man named(8).
int getxrr( string hostname, array mxhosts [, array weight])

getmyinode
Zwraca bieżący inode pliku ze skryptem lub False w przypadku wystąpienia błędu. Patrz również:
getmyuid(), get_current_user(), getmypid() i getlastmod().
Uwaga

Funkcja ta nie działa w Windows

int getmyinode( void )

getmypid
Zwraca identyfikator procesu PHP lub False w przypadku wystąpienia błędu.
Uwaga

Jeżeli PHP działa jako moduł serwera, w kolejnych wywołaniach skryptu nie jest gwarantowane, że identyfikatory procesów będą
różne. Patrz również: getmyuid(), get_current_user(), getmyinode() oraz getlastmod().

int getmypid( void )

getmyuid
Zwraca identyfikator użytkownika uruchamiającego bieżący skrypt, lub False w przypadku wystąpienia
błędu. Patrz również: getmypid(), get_current_user(), getmyinode() oraz getlastmod().
int getmyuid( void )

getprotobyname
Zwraca numer protokołu skojarzonego z protokołem $name według pliku /etc/protocols. Patrz również:
getprotobynumber().
int getprotobyname( string name )

Dodatek A - Funkcje 224
getprotobynumber
Zwraca nazwę protokołu skojarzonego z protokołem $number według pliku /etc/protocols. Patrz również:
getprotobyname().
int getprotobynumber( string name )

getrandmax
Zwraca maksymalną wartość, jaka może być zwrócona przez funkcję rand(). Patrz również: rand(),
srand(), mt_rand(), mt_srand()
oraz mt_getrandmax().
int getrandmax( void )

getrusage
Jest to interfejs do getrusage(2). Zwraca tablicę asocjacyjną zawierającą dane zwrócone przez wywołanie
systemowe. Jeżeli $who jest równe 1, getrusage zostanie wywołane z RUSAGE_CHILDREN.
array getrusage( [int who] )
Przykład: getrusage()
$dat = getrusage();
echo $dat["ru_nswap"]; # ilość stronicowań
echo $dat["ru_majflt"]; # ilość błędów strony
echo $dat["ru_utime.tv_sec"]; # użyty czas użytkownika (sekundy)
echo $dat["ru_utime.tv_usec"]; # użyty czas użytkownika (mikrosekundy)
Więcej szczegółów na temat getrusage można znaleźć w podręczniku systemowym pod hasłem
getrusage(2).

getservbyname
Zwraca numer portu używanego przez usługę $service dla protokołu $protocol, według definicji w
/etc/services. $protocol może być TCP lub UDP. Patrz również: getservbyport().
int getservbyname( string service, string protocol)

gettext
Funkcja szuka tłumaczenia ciągu w jednej z tablic tłumaczeń. Zwraca przetłumaczony ciąg lub oryginalny
ciąg, jeżeli tłumaczenie nie zostanie znalezione. Jako Znaków zastępczych można używać podkreślenia.
string gettext( string message )
Przykład: gettext()
<?php
// Ustaw język na niemiecki
putenv( "LANG=de");
// Określ połozenie tablic tłumaczeń
bindtextdomain( "myPHPApp", "./locale");
// wybierz domenę
textdomain( "myPHPApp");
// wypisz komunikat testowy
print (gettext( "Welcome to My PHP Application"));
?>

gettimeofday
Jest to interfejs do gettimeofday(2). Zwraca tablicę asocjacyjną zawierającą dane zwracane przez
wywołanie systemowe.
• sec — sekundy
• usec — mikrosekundy
• minuteswest — przesunięcie w minutach na zachód od Greenwich
• dsttime — typ poprawki dst
array gettimeofday( void )

225 PHP – Kompendium wiedzy
gettype
Zwraca typ zmiennej PHP $var. Możliwe zwracane wartości zamieszczone są poniżej:
boolean integer double string
array object resource user function
unknown type
string gettype( mixed var )
Patrz również: settype().

get_browser
Odczytuje możliwości przeglądarki użytkownika. Jest to realizowane przez odszukanie danych na temat
przeglądarki w pliku browscap.ini. Domyślnie używana jest wartość zmiennej $HTTP_USER_AGENT, ale można
przekazać dowolną dowolny parametr $user_agent (na przykład, aby odczytać możliwości innej przeglądarki).
Zwracany jest obiekt zawierający dane opisujące, na przykład numer wersji, identyfikator, wartości True lub
False dla takich własności jak, obsługa ramek, JavaScript, cookie itd. Mimo, że plik browscap.ini zawiera dane o
wielu przeglądarkach, to jednak użytkownik musi dbać o jego aktualność. Format pliku jest bardzo prosty.
Poniższy przykład pokazuje przykładowe dane zwracane dla przeglądarki użytkownika.
object get_browser( [string user_agent])
Przykład: get_browser()
<?php
function list_array ($array)
{
while (list ($key, $value) = each($array))
$str .= "<b>$key:</b> $value<br>\n";
return $str;
}
echo "$HTTP_USER_AGENT<hr>\n";
$browser = get_browser();
echo list_array( (array) $browser);
?>
Wynik działania tego skryptu może wyglądać następująco
Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)<hr>
<b>browser_name_pattern:</b> Mozilla/4\.0 (compatible; MSIE 5\.5; Windows NT 5\.0)<br>
<b>parent:</b> IE 5.0<br>
<b>version:</b> 5.5<br>
<b>minorver:</b> 5<br>
<b>platform:</b> Win2000<br>
<b>beta:</b> <br>
<b>browser:</b> IE<br>
<b>majorver:</b> 5<br>
<b>frames:</b> 1<br>
<b>tables:</b> 1<br>
<b>cookies:</b> 1<br>
<b>backgroundsounds:</b> 1<br>
<b>vbscript:</b> 1<br>
<b>javascript:</b> 1<br>
<b>javaapplets:</b> 1<br>
<b>activexcontrols:</b> 1<br>
<b>win16:</b> <br>
<b>ak:</b> <br>
<b>sk:</b> <br>
<b>aol:</b> <br>
<b>crawler:</b> <br>
<b>msn:</b> <br>
<b>cdf:</b> 1<br>
<b>dhtml:</b> 1<br>
<b>xml:</b> 1<br>
Aby skrypt ten mógł działać należy tak ustawić zmienną konfiguracji browscap, aby wskazywała na
katalog z plikiem browscap.ini. Więcej informacji (w tym adresy skąd można ściągnąć plik browscap.ini) można
znaleźć w FAQ do PHP pod adresem http://www.php.net/FAQ.php.

get_cfg_var
Zwraca wartość zmiennej konfiguracji PHP określonej przez $varname, lub False w przypadku
wystąpienia błędu. Funkcja ta nie zwraca danych konfiguracji ustawionych przy kompilacji PHP lub poprzez pliki
konfiguracyjne Apache (przy użyciu dyrektywy php3_configuration_option). Aby sprawdzić, czy system

Dodatek A - Funkcje 226
korzysta z pliku konfiguracji, należy spróbować odczytać wartość zmiennej konfiguracji cfg_file_path. Jeżeli
jest to możliwe, oznacza to, że jest używany plik konfiguracji.
string get_cfg_var( string varname )

get_class
Zwraca nazwę klasy przekazanego obiektu $obj. Patrz również: get_parent_class(), is_subclass_of().
string get_class( object obj )

get_class_methods
Zwraca tablicę z nazwami metod zdefiniowanych w klasie określonej przez $class_name. Patrz również:
get_class_vars(), get_object_vars().
array get_class_methods( string class_name )

get_class_vars
Zwraca tablicę z nazwami właściwości zdefiniowanych w klasie określonej przez $class_name. Patrz
również: get_class_methods(), get_object_vars().
array get_class_vars( string class_name )

get_current_user
Zwraca nazwę właściciela bieżącego skryptu PHP. Patrz również getmyuid(), getmypid(), getmyinode()
oraz getlastmod().
string get_current_user( void )

get_declared_classes
Zwraca tablicę nazw klas zadeklarowanych w bieżącym skrypcie. W PHP 4.0.1pl2 na początku tablicy
zwracane były trzy dodatkowe klasy: stdClass (zdefiniowana w Zend/zend.c), OverloadedTestClass
(zdefiniowana w ext/standard/basic_functions.h) oraz Directory (zdefiniowana w ext/standard/dir.c).
array get_declared_classes( void )

get_extension_funcs
Zwraca nazwy wszystkich funkcji zdefiniowanych w module $module_name.
array get_extension_funcs( string module_name )
Przykład: get_extension_funcs()
print_r (get_extension_funcs( "xml"));
print_r (get_extension_funcs( "gd"));
Wykonanie tych linii spowoduje wypisanie listy funkcji umieszczonych w modułach xml i gd. Patrz
również: get_loaded_extensions().

get_html_translation_table
Zwraca tablicę translacji używaną wewnętrznie w funkcjach htmlspecialchars() i htmlentities().
Możliwe jest określenie potrzebnej w danym momencie tabeli (HTML_ENTITIES i HTML_SPECIALCHARS). Tak samo
jak w przypadku funkcji htmlspecialchars() i htmlentities() można opcjonalnie określić rodzaj ukośników
$quote_style. Domyślnym ustawieniem jest tryb ENT_COMPAT. Opis trybów znajduje się w opisie funkcji
htmlspecialchars().
string get_html_translation_table( int table [, int quote_style])
Przykład: Tablica translacji
$trans = get_html_translation_table( HTML_ENTITIES );
$str = "To firma & <PRO> & spółka";
$encoded = strtr( $str, $trans);

227 PHP – Kompendium wiedzy
Zmienna $encoded zawiera teraz ciąg To firma &amp; &lt;PRO&gt; &amp; sp&oacute;&sup3;ka.
Ciekawym zastosowaniem jest użycie funkcji array_flip() w celu zmiany kierunku translacji.
$trans = array_flip( $trans );
$original = strtr ($str, $trans);
Teraz w zmiennej $original będzie znajdował się ciąg: To firma & <PRO> & spółka.
Uwaga

Funkcja ta została dodana w PHP 4.0

Patrz również htmlspecialchars(), htmlentities(), strtr() i array_flip().

get_included_files
Funkcja zwraca tablicę asocjacyjną z nazwami wszystkich plików dołączonych do skryptu za pomocą
funkcji include_once(). Indeksami tej tablicy są nazwy plików użytych w include_once() bez rozszerzenia .php.
W PHP 4.0.1pl2 funkcja zakładała, że pliki dołączane poprzez include_once() mają rozszerzenia .php i inne
rozszerzenia nie działają. Patrz również: require_once(), include_once(), get_required_files().
array get_included_files( void )

get_loaded_extesions
Zwraca nazwy wszystkich modułów wkompilowanych i załadowanych przez interpreter PHP.
array get_loaded_extensions( void )
Przykład get_loaded_extensions()
print_r (get_loaded_extensions());
Spowoduje to wypisanie listy podobnej do następującej:
Array
(
[0] => standard
[1] => bcmath
[2] => Calendar
[3] => com
[4] => variant
[5] => ftp
[6] => mysql
[7] => odbc
[8] => pcre
[9] => session
[10] => xml
[11] => wddx
[12] => apache
)
Patrz również: get_extension_funcs().

get_magic_quotes_gpc
Zwraca stan ustawienia magic_quotes_gpc (0 — wyłaczone, 1 — włączone). Patrz również:
get_magic_quotes_runtime(), set_magic_quotes_runtime().
long get_magic_quotes_gpc( void )

get_magic_quotes_runtime
Zwraca stan ustawienia magic_quotes_runtime (0 — wyłaczone, 1 — włączone). Patrz również:
get_magic_quotes_gpc(), set_magic_quotes_runtime().
long get_magic_quotes_runtime( void )

get_meta_tags
Otwiera plik $filename i analizuje go szukając znaczników meta.
array get_meta_tags( string filename [, int use_include_path])
Przykład: Znaczniki <META>
Dodatek A - Funkcje 228
<meta name="author" content="name">
<meta name="tags" content="php3 documentation">
</head> <!-- tutaj zatzymuje się analiza -->

Uwaga

Uwaga na końce linii — PHP wykorzystuje własną funkcję analizującą plik wejściowy, więc pliki z MacIntosha mogą nie działać na
Uniksie.

Nazwa właściwości staje się kluczem, natomiast zawartość umieszczana jest w tablicy jako wartość
elementu. Dzięki temu można wykorzystać standardowe funkcje przeglądające tablice. Znaki specjalne są
zastępowane znakiem podkreślenia, natomiast pozostały tekst jest konwertowany do małych liter. Ustawienie
use_include_path na 1 spowoduje, że PHP będzie próbował otworzyć plik znajdujący się na standardowej ścieżce
dołączania.

get_object_vars
Zwraca tablicę asocjacyjną właściwości zdefiniowanych w obiekcie określonym przez $obj. Jeżeli
zmienna zdefiniowana w klasie nie ma przypisanej wartości, nie znajdzie się ona w tablicy asocjacyjnej.
array get_object_vars( object obj)
Przykład: użycie get_object_vars()
<?php
class Point2D
{
var $x, $y;
var $label;
function Point2D( $x, $y)
{
$this->x = $x;
$this->y = $y;
}
function setLabel( $label)
{
$this->label = $label;
}
function getPoint()
{
return array( "x" => $this->x,
"y" => $this->y,
"label" => $this->label);
}
}
$p1 = new Point2D( 1.233, 3.445);
print_r(get_object_vars($p1));
// Właściwość $Label jest zdefiniowana, ale nie zainicjowana
// Array
// (
// [x] => 1.233
// [y] => 3.445
// )
$p1->setLabel("point #1");
print_r(get_object_vars($p1));
// Array
// (
// [x] => 1.233
// [y] => 3.445
// [label] => point #1
// )
?>
Patrz również: get_class_methods(), get_class_vars().

get_parent_class
Zwraca nazwę klasy bazowej dla klasy, której instancją jest obiekt $obj. Patrz również: get_class(),
is_subclass_of().
string get_parent_class( object obj )

229 PHP – Kompendium wiedzy
get_required_files
Zwraca tablicę asocjacyjną z nazwami wszystkich plików załadowanych do skryptu poprzez funkcję
require_once().
array get_required_files( void )
Przykład: Wypisywanie plików dołączanych za pomocą require i include.
<?php
require_once( "local.php" );
require_once( "../inc/global.php" );
for ($i=1; $i<5; $i++)
include "util". $i . ".php";
echo "Pliki dołączane przez require_once\n";
print_r (get_required_files());
echo "Pliki dołączane przez include_once\n";
print_r (get_included_files());
?>
Wykonanie tego skryptu spowoduje wygenerowanie następnego wyniku:
Pliki dołączane przez require_once
Array
(
[0] => C:\helion\php4-devguide\site\doda\local.php
[1] => C:\helion\php4-devguide\site\inc\global.php
[2] => C:\helion\php4-devguide\site\doda\util1.php
[3] => C:\helion\php4-devguide\site\doda\util2.php
[4] => C:\helion\php4-devguide\site\doda\util3.php
[5] => C:\helion\php4-devguide\site\doda\util4.php
)
Pliki dołączane przez include_once
Array
(
[0] => C:\helion\php4-devguide\site\doda\local.php
[1] => C:\helion\php4-devguide\site\inc\global.php
[2] => C:\helion\php4-devguide\site\doda\util1.php
[3] => C:\helion\php4-devguide\site\doda\util2.php
[4] => C:\helion\php4-devguide\site\doda\util3.php
[5] => C:\helion\php4-devguide\site\doda\util4.php
)

Uwaga

W PHP 4.0.1pl2 funkcja zakładała, że pliki dołączane poprzez include_once() mają rozszerzenia .php i inne rozszerzenia nie
działają.

Patrz również: require_once(), include_once(), get_included_files().

gmdate
Funkcja ta jest identyczna z date(), poza tym, że zwracany czas jest czasem Greenwich (GMT). Jeżeli
przykłąd zostanie uruchomiony w Polsce (GMT +0100), pierwsza linia przykładu zwróci Jan 01 1998 00:00:00,
natomiast druga Dec 31 1997 23:00:00.
string gmdate( string format, int timestamp )
Przykład: gmdate()
echo date("M d Y H:i:s", mktime( 0,0,0,1,1,1998));
echo gmdate("M d Y H:i:s", mktime( 0,0,0,1,1,1998));
Patrz również: date(), mktime() i gmmktime().

gmmktime
Funkcja identyczna jak mktime(), poza tym, że przekazane parametry reprezentują czas GMT.
int gmmktime( int hour, int minute, int second, int month,
int day, int year [, int is_dst])

gmstrftime
Zachowuje się tak samo jako jak funkcja strftime(), ale zwracany czas jest czasem GMT. Na przykład,
jeżeli będzie użyta w strefie czasowej GMT -0500, pierwsze wywołanie z przykładu wypisze ciąg Dec 31 1998
20:00:00, natomiast drugie: Jan 01 1999 01:00:00.
Dodatek A - Funkcje 230
string gmstrftime( string format, int timestamp )
Przykład: gmstrftime()
setlocale( 'LC_TIME', 'en_US');
echo strftime("%b %d %Y %H:%M:$S", mktime( 20,0,0,12,31,98));
echo gmstrftime("%b %d %Y %H:%M:$S", mktime( 20,0,0,12,31,98));
Patrz również: strftime().

GregorianToJD
Prawidłowy zakres dat kalendarza gregoriańskiego to 4714 p.n.e. do 9999 n.e. Chociaż można stosować
tak szeroki zakres dat, nie ma to jednak sensu. Kalendarz gregoriański został ustanowiony 15 października 1582
roku (lub 5 października 1582 roku według kalendarza juliańskiego). Niektóre kraje jeszcze długo nie
wprowadziły tego kaledarza. Na przykład Anglia przyjęła go w 1752 roku, Rosja w 1918 a Grecja w 1923 roku.
Większość krajów europejskich przed kalendarzem gregoriańskim używała kalendarza juliańskiego.
int GregorianToJD( int month, int day, int year)
Przykład: Funkcje kalendarza
<?php
$jd = GregorianToJD( 10, 11, 1970);
echo "$jd\n";
$gregorian = JDToGregorian( $jd );
echo "$gregorian\n";
?>

gzclose
Funkcja zamyka plik gz wskazywany przez $zp. W przypadku powodzenia zwraca True, a w przypadku
błędu, False. Wskaźnik pliku gz musi być prawidłowym wskaźnikiem wskazującym na plik otwarty przez funkcję
gzopen().
int gzclose( int zp )

gzcompress
Zwraca dane wejściowe przekazane w $data skompresowane gzipem, lub False w przypadku wystąpienia
błędu. Opcjonalny parametr $level może przyjmować wartości od 0 dla braku kompresji, do 9 dla maksymalnej
kompresji. Patrz również: gzuncompress().
string gzcompress( string data [, int level])

gzeof
Zwraca True, jeżeli wskaźnik pliku gz znajduje się na końcu pliku lub wystąpił błąd. W przeciwnym
przypadku zwraca False. Wskaźnik pliku gz musi być prawidłowym wskaźnikiem wskazującym na plik otwarty
przez funkcję gzopen().
int gzeof( int zp )

gzfile
Funkcja identyczna z readgzfile(), poza tym, że gzfile() zwraca zawartość pliku w tablicy. Można
ustawić parametr opcjonalny na 1, co spowoduje szukanie pliku również na ścieżce ustawionej w include_path.
Patrz również: readgzfile(), gzopen().
array gzfile( string filename [, int use_include_path])

gzgetc
Zwraca ciąg zawierający jeden znak (nieskompresowany) odczytany z pliku na który wskazuje $zp.
Zwraca False na końcu pliku (tak samo jak gzeof()).Wskaźnik pliku gz musi być prawidłowym wskaźnikiem
wskazującym na plik otwarty przez funkcję gzopen(). Patrz również: gzopen() i gzgets().
string gzgetc( int zp )

231 PHP – Kompendium wiedzy
gzgets
Zwraca ciąg znaków (nieskompresowany) o maksymalnej długości $length-1 odczytany z pliku na który
wskazuje $zp. Czytanie kończy się po odczytaniu $length-1 znaków, znaku nowej linii lub znaku EOF. Zwraca
False w przypadku wystąpienia błędu. Wskaźnik pliku gz musi być prawidłowym wskaźnikiem
wskazującym na plik otwarty przez funkcję gzopen(). Patrz również: gzopen(), gzgetc() i fgets().
string gzgets( int zp, int length )

gzgetss
Funkcja identyczna jak gzgets(), ale dodatkowo gzgetss() usiłuje usunąć znaczniki HTML i PHP z
odczytanego tekstu. Można użyć opcjonalnego trzeciego parametru w celu określenia znaczników, które mają
pozostać w tekście. Parametr $allowable_tags został dodany w PHP 3.0.13, PHP4B3. Patrz również: gzgets(),
gzopen() i strip_tags().
string gzgetss( int zp, int length [, string allowable_tags])

gzopen
Otwiera plik gzip (.gz) do odczytu lub zapisu. Parametr $mode jest taki jak w fopen() ("rb" lub "wb"), ale
może zawierać również poziom kompresji ("wb9") lub strategię: f dla filtrowania danych, na przykład "wb6f", h
dla kompresji tylko metodą Huffmana, np.: "wb1h". Więcej informacji na temat filtrowania znajduje się w opisie
deflateIni2 w pliku zlib.h. Można również wykorzystać gzopen() do odczytania danych, które nie są w formacie
gzip. W takim przypadku gzread() odczytuje plik bez jego dekompresji. Funkcja gzopen() zwraca wskaźnik do
otwartego pliku. Po tej operacji wszystkie dane odczytywane z tego pliku są dekompresowane, natomiast dane
zapisywane do pliku są kompresowane. Jeżeli otwarcie pliku nie uda się, funkcja zwraca False. Można użyć
opcjonalnego trzeciego argumentu i ustawić go na 1 w celu włączenia poszukiwania pliku do otwarcia na ścieżce
include_path.
int gzopen( string filename, string mode [, int use_include_path])
Przykład gzopen()
$fp = gzopen( "/tmp/file.gz", "r" );
Patrz również: gzclose().

gzpassthru
Odczytuje plik wskazywany przez wskaźnik do pliku gz, aż do znaku EOF, a jego (nieskompresowaną)
zawartość kieruje na wyjście. Jeżeli wystąpi błąd zwraca False. Wskaźnik pliku gz musi być prawidłowym
wskaźnikiem wskazującym na plik otwarty przez funkcję gzopen(). Po zakończeniu odczytu pliku jest on
zamykany.
int gzpassthru( int zp )

gzputs
Identyczna jak funkcja gzwrite().
int gzputs( int zp, string str [, int length])

gzread
Odczytuje maksymalnie $length bajtów z pliku gz wskazywanego przez $zp. Odczyt zostaje przerwany,
gdy zostanie odczytane $length znaków (nieskompresowanych), lub napotkany zostanie koniec pliku.
string gzread( int zp, int length )
Przykład: gzread()
// odzytanie zawartości pliku gz do ciągu
$filename = "/usr/local/somth.txt.gz";
$zd = gzopen( $filename, "r");
$contents = gzread( $dz, 10000 );
Patrz również: gzwrite(), gzopen(), gzgets(), gzgetss(), gzfile(), gzpassthru().

Dodatek A - Funkcje 232
gzrewind
Ustawia znacznik pozycji na początku pliku. Jeżeli wystąpi błąd, funkcja zwraca 0. Wskaźnik pliku gz
musi być prawidłowym wskaźnikiem wskazującym na plik otwarty przez funkcję gzopen(). Patrz również
gzseek(), gztell().
int gzrewind( int zp )

gzseek
Ustawia znacznik pozycji pliku wskazywanego przez $zp na pozycję określoną przez $offset. Jest to
odpowiednik wywołania (w C) gzseek( zp, offset, SEEK_SET). Jeżeli plik jest otwarty do odczytu, funkcja jest
wykonywana, ale może być to niezwykle powolne. Jeżeli plik jest otwarty do zapisu, obsługiwane jest jedynie
przesunięcie w przód; gzseek() kompresuje wtedy sekwencję zer aż do nowej pozycji. W przypadku powodzenia
operacji zwraca 0, w przeciwnym wypadku zwraca -1.
Uwaga

Przesunięcie poza znacznik EOF nie jest traktowane jako błąd.

Patrz również gztell() i gzrewind().
int gzseek( int zp, int offset )

gztell
Zwraca pozycje znacznika pozycji dla pliku wskazywanego przez $zp, to znaczy przesunięcie od początku
pliku. Jeżeli wystąpi błąd, funkcja zwraca False. Wskaźnik pliku gz musi być prawidłowym wskaźnikiem
wskazującym na plik otwarty przez funkcję gzopen(). Patrz również: gzopen(), gzseek() i gzrewind().
int gztell( int zp )

gzuncompress
Na podstawie danych $data, skompresowanych za pomocą funkcji gzcompress() zwraca oryginalne
nieskompresowane dane. W przypadku błędu zwraca False. Funkcja zakończy się niepowodzeniem, jeżeli
rozmiar rozkompresowanych danych przekracza 256 krotność wielkości danych wejściowych $data, lub jest
większy od opcjonalnego parametru $length. Patrz również gzcompress().
string gzuncompress( string data [, int length])

gzwrite
Zapisuje zawartość $string do pliku gz wskazywanego przez $zp. Jeżeli podano argument $length, zapis
jest przerywany po zapisaniu $length bajtów (nieskompresowanych), lub po osiągnięciu końca danych
wejściowych.
Uwaga

Jeżeli podany został argument $length, ignorowany jest parametr konfiguracji magic_quotes_runtime i z ciągu $string nie
będą usunięte ukośniki.

Patrz również: gzread(), gzopen() i gzputs().

header
Funkcja używana na początku pliku HTML do wysłania surowych nagłówków HTTP. Więcej informacji
na temat surowych nagłówków można znaleźć w specyfikacji protokołu HTTP 1.1
(http://www.w3.org/Protocols/rfc2616/rfc2616).
Uwaga
233 PHP – Kompendium wiedzy
Należy pamiętać, że funkcja header() musi być wywołana przed wysłaniem danych HTML, niezależnie czy poprzez normalny kod
HTML czy poprzez PHP. Bardzo często spotykanym błędem jest umieszczanie wolnych linii przed wywołaniem funkcji header().

Istnieją dwa nagłówki specjalnego przeznaczenia. Pierwszy to nagłówek Location. Oprócz odesłania tego
nagłówka do przeglądarki zwraca on do Apache kod statusu REDIRECT. Dla programistów skryptów może nie być
to zbyt ważne, ale ważne jest dla programistów zainteresowanych działaniem serwera Apache.
int header( string string )
Przykład: header()
header("Location: http://www.php.net"); /* przekierowuje rzeglądarkę do witryny PHP */
exit; /* należy się upewnić, że kod poniżej nie
zostanie wykonany. */
Drugim nagłówkiem specjalnego przeznaczenia jest nagłówek rozpoczynający się ciągiem HTTP/
(wielkość znaków bez znaczenia). Na przykład jeżeli skonfigurowana zostanie dyrektywa Apache ErrorDocument
404, tak aby wskazywała na skrypt PHP, dobrze jest się upewnić, że skrypt PHP generuje błąd 404. W tym celu
należy wysłać nastepujący nagłówek:
header("HTTP/1.0 404 Not Found");
Skrypty PHP często generują dynamiczny kod HTML, który nie powinien być buforowany w
przeglądarkach i serwerach proxy. Można zablokować wiele serwerów proxy za pomocą nagłówków:
header("Expires: Mon, 26 ul 1997 05:00:00 GMT"); //Data w przeszłości
header("LastModified: " .. gmdate("D, d M y H:i:s"); // Czas GMT
header("Cache-Control: no-cache, must-revalidate"); // zawsze zmodyfikowane HTTP/1.1
header("Pragme: no-cache"); // HTTP/1.0
Patrz również: headers_sent().

headers_sent
Zwraca True, jeżeli wysłane zostały nagłówki HTTP, w przeciwnym przypadku zwraca False. Patrz
również: header().
boolean headers_sent( void )

hebrev
Opcjonalny parametr $max_chas_per_line wskazuje na maksymalną ilość znaków w linii danych
wyjściowych. Funkcja próbuje uniknąć łamania wyrazów. Patrz również: hebrevc().
string hebrev(string hebrew_text [, int max_chars_per_line])

hebrevc
Funkcja podobna do hebrev() z tą różnicą, że konwertuje znaki nowej linii (\n) na <br>\n. Opcjonalny
parametr $max_chars_per_line wskazuje maksymalną ilość znaków w wyniku. Funkcja próbuje uniknąć łamania
wyrazów. Patrz również: hebrev().
string hebrevc( string hebrev_text [, int max_chars_per_line])

hexdec
Zwraca dziesiętny odpowiednik liczby szesnastkowej przekazanej w parametrze $hex_string. Funkcja
hexdec() konwertuje ciąg szesnastkowy na liczbę dziesiętną. Największą liczbą, jaka może być skonwertowana
jest 7fffffff lub 2147483647 dziesiętnie. Patrz również: dechex().
int hexdec( string hex_string )

highlight_file
Wynikiem działania funkcji jest wersja pliku PHP, o nazwie $filename, z wyróżnioną składnią. Używane
kolory są zdefiniowane w wewnętrznym module wyróżniania składni.
void highlight_file( string filename )
Przykład: Tworzenie łączy do plików z wyróżnioną składnią
Aby utworzyć łącze umożliwiające wyróżnienie składni dowolnego pliku, skorzystamy z dyrektywy
Apache ForceType. Pozwala to na stworzenie eleganckiego adresu URL, natomiast użycie funkcji
Dodatek A - Funkcje 234
highlight_file() pozwala na pokazanie ładnie wyglądającego kodu. W pliku httpd.conf należy dodać
następujące linie:
<Location /source>
ForceType application/x-httpd-php
</Location>
Następnie należy utworzyć plik o nazwie source i umieścić go w głównym katalogu serwera WWW:
<HTML>
<HEAD>
<TITLE>Wyświetlanie źródła</TITLE>
</HEAD>
<BODY COLOR BGCOLOR="white">
<?php
$script = getenv( "PATH_TRANSLATED");
if (!$script)
{
echo "<BR><B>BŁĄD: wymagana nazwa skryptu</B><BR>";
}
else
{
if ereg("(\.php|\.inc)$", $script))
{
echo "<H1>Źródło skryptu: $PATH_INFO</H1>\n<hr>\n";
highlight_file( $script );
}
else
echo "<H1>BŁĄD: Dopuszczalne sa jedynie rozszerzenia php i inc</H1>";
}
echo "Przetworzony: " . date( "Y/M/d H:i:s", time());
</BODY>
</HTML>
?>
Teraz można użyć adresów podobnych do przytoczonego poniżej, aby wyświetlić kolorową wersję skryptu
/sciezka/do/skryptu.php.
http://serwer.com/source/sciezka/do/skryptu.php
Patrz również: highlight_string() i show_source().

highlight_string
Wysyła na wyjście pokolorowaną wersję ciągu $str. Używane kolory są zdefiniowane w wewnętrznym
module PHP. Patrz również: highlight_file() i show_source().
void highlight_string( string str )

htmlentities
Funkcja jest identyczna z htmlspecialchars(), ale wszystkie znaki posiadające odpowiadające im symbole
HTML są zamieniane na te symbole. Tak jak w przypadku funkcji htmlspecialchars() można użyć opcjonalnego
argumentu wskazującego na sposób traktowania cudzysłowów i apostrofów. Wartość ENT_COMPAT (domyślna)
konwertuje jedynie cudzysłowy, pozostawiając bez zmian apostrofy. ENT_QUOTES powoduje konwersję zarówno
cudzysłowów jak i apostrofów. ENT_NOQUOTES powoduje, że zarówno cudzysłowy jak i apostrofy pozostają
niezmienione. W chwili obecnej używany jest zestaw znaków ISO-8859-1. Należy pamiętać, że argument
opcjonalny został dodany w PHP 3.0.17 i PHP 4.0.3. Patrz również: htmlspecialchars() i nl2br().
string htmlentities( string string [, int quote_style])

htmlspecialchars
Niektóre znaki mają specjalne znaczenie w HTML i powinny być reprezentowane przez symbole HTML.
Funkcja ta zwraca ciąg z zastosowanymi niektórymi z tych konwersji. Są to najczęściej używane konwersje przy
programowaniu dla WWW. Jeżeli wymagana jest kompletna konwersja, należy użyć funkcji htmlentities().
Funkcja ta jest użyteczna do usuwania znaczników HTML z tekstu wprowadzonego przez użytkownika, na
przykład w księdze gości lub na tablicy ogłoszeniowej. Argument opcjonalny, $quote_style wskazuje sposób
konwersji apostrofów i cudzysłowów. Domyślny tryb ENT_COMPAT jest dostępny dla zachowania zgodności z
poprzednimi wersjami i konwertuje jedynie cudzysłowy, pozostawiając bez zmian apostrofy. ENT_QUOTES

235 PHP – Kompendium wiedzy
powoduje konwersję zarówno cudzysłowów jak i apostrofów. ENT_NOQUOTES powoduje, że zarówno cudzysłowy
jak i apostrofy pozostają niezmienione.
string htmlspecialchars( string string [, int quote_style])
Wykonywane są następujące konwersje:
• & jest zamieniane na &amp;
• " jest zamieniany na &quot;, o ile nie jest ustawiona wartość ENT_NOQUOTES
• ' jest zmieniany na &#039; jeżeli jest ustawiona wartość ENT_QUOTES
• < jest zmieniane na &lt;
• > jest zmieniane na &gt;

Uwaga

Funkcja ta wykonuje jedynie przedstawione translacje. Aby przeprowadzi ć pełną translację należy użyć funkcji htmlentities().
Należy pamiętać, że argument opcjonalny został dodany w PHP 3.0.17 i PHP 4.0.3.

hw_Array2Objrec
Konwertuje $object_array na rekord obiektowy. Wielokrotne atrybuty na przykład Title w różnych
językach są obsługiwane prawidłowo. Patrz również: hw_objrec2array().
string hw_Array2Objrec( array object_array )

hw_Children
Zwraca tablicę identyfikatorów obiektów. Każdy identyfikator należy do elementu kolekcji o
identyfikatorze $objectID. Tablica zawiera wszystkie elementy potomne, zarówno dokumenty jak i kolekcje.
array hw_Children( int connection, int objectID )

hw_ChildrenObj
Zwraca tablicę rekordów obiektów. Każdy z obiektów należy do elementu potomnego o identyfikatorze
$objectID. Tablica zawiera wszystkie elementy potomne, zarówno dokumenty jak i kolekcje.
array hw_ChildrenObj( int connection, int objectID )

hw_Close
Zwraca False, jeżeli $connection nie jest prawidłowym indeksem połączenia, a w przeciwnym przypadku
True. Zamyka połączenie o podanym indeksie do serwera Hyperwave.
int hw_Close( int connection )

hw_Connect
Otwiera połączenie do serwera Hyperwave i zwraca indeks połączenia, jeżeli operacja się uda. Jeżeli nie,
zwraca False. Każdy z argumentów powinien być ciągiem poza numerem portu. Argumenty $username i
$password są opcjonalne i mogą być opuszczone. W takim przypadku nie będzie przeprowadzana identyfikacja
użytkownika. Jest to zbliżone do identyfikacji użytkownika anonimowego. Funkcja zwraca indeks połączenia,
który jest potrzebny w innych funkcjach Hyperwave. Można utrzymywać kilka połączeń do serwera, Należy
jedynie pamiętać, że przesyłane hasło nie jest zaszyfrowane. Patrz również: hw_pConnect().
int hw_Connect( string host, int port, string username, string password )

hw_Cp
Kopiuje obiekt o identyfikatorze podanym w drugim parametrze do kolekcji o identyfikatorze
$destination_id. Zwracaną wartością jest ilość skopiowanych obiektów. Patrz również: hw_mv().
int hw_Cp( int conection, array object_id_array, int destination_id

Dodatek A - Funkcje 236
hw_Deleteobject
Usuwa obiekt o identyfikatorze przekazanym w drugim argumencie funkcji. Usuwa wszystkie kopie
obiektu. Zwraca True, jeżeli nie wystąpiły żadne błędy, lub False gdy błędy wystąpiły. Patrz również hw_mv().
int hw_Deleteobject( int connection, int object_to_delete )

hw_DocByAnchor
Zwraca identyfikator obiektu, do którego należy $anchorID.
int hw_DocByAnchor( int connection, int anchorID )

hw_DocByAnchorObj
Zwraca rekord obiektu dokumentu, do którego należy $anchorID.
int hw_DocByAnchorObj( int connection, int anchorID )

hw_Document_Attributes
Zwraca rekord obiektu dokumentu. Dla zachowania zgodności z poprzednimi wersjami akceptowane jest
również hw_DocumentAttributes(). Jednak nie zaleca się stosowania tej nazwy. Patrz również:
hw_Document_BodyTag() i hw_Document_Size().

hw_Document_BodyTag
Zwraca znacznik BODY dokumentu. Jeżeli jest to dokument HTML, znacznik BODY powinien być
wydrukowany przez dokumentem. Patrz również: hw_Document_Attributes(), hw_Document_Size(). Dla
zapewnienia zgodności akceptowana jest również nazwa hw_DocumentBodyTag(). Nie zaleca się jej stosowania.
string hw_Document_BodyTag( int hw_document )

hw_Document_Content
Zwraca treść dokumentu. Jeżeli jest to dokument HTML, zawartością jest wszystko po znaczniku BODY.
Dane ze znaczników BODY i HEAD są zapamiętywane w rekordzie obiektu. Parz również:
hw_Document_Attributes(), hw_Document_Size() i hw_DocumentSetContent().
string hw_Document_Content( int hw_document )

hw_Document_SetContent
Zapisuje lub zmienia zawartość dokumentu. Jeżeli dokument jest w formacie HTML, zawartość stanowi
wszystko po znaczniku BODY. Dane ze znaczników HEAD i BODY są przechowywane w rekordzie obiektu. Jeżeli
informacje te zostaną umieszczone w zawartości dokumentu, po wstawieniu dokumentu serwer Hyperwave
zmieni odpowiednio rekord obiektu; nie jest to najlepsze rozwiązanie. Jeżeli funkcja się nie powiedzie, dokument
będzie zawierał poprzednią zawartość. Patrz również: hw_Document_Attributes(), hw_Document_Size() i
hw_Document_Content().
string hw_DocumentSetContent( int hw_document, string content )

hw_Document_Size
Zwraca wielkość dokumentu w bajtach. Patrz również: hw_Document_BodyTag() i
hw_Document_Attributes(). W celu zachowania zgodności akceptowana jest również nazwa hw_DocumentSize, ale
jej stosowanie nie jest zalecane.
int hw_Document_Size( int hw_document )

237 PHP – Kompendium wiedzy
hw_EditText
Przesyła test dokumentu na serwer. Rekord obiektu dokumentu nie może być modyfikowany do czasu
zakończenia edycji. Funkcja ta działa jedynie dla dokumentów czysto tekstowych. Nie otwiera specjalnego
połączenia i przez to na czas przesyłania blokuje połączenie sterujące. Patrz również: hw_PipeDocument(),
hw_FreeDocument(), hw_Document_BodyTag(), hw_Document_Size(), hw_Output_Document() i hw_GetText().
int hw_EditText( int connection, int hw_document )

hw_Error
Zwraca kod ostatniego błędu. Jeżeli nie występowały żadne błędy, zwraca 0. Błąd odnosi się do ostatnio
wykonanej komendy.
int hw_Error( int connection )

hw_ErrorMsg
Zwraca ciąg zawierający ostatni komunikat błędu lub ciąg No Error. Jeżeli funkcja się nie udała, zwraca
False. Komunikat błędu odnosi się do ostatnio wykonanej komendy.
string hw_ErrorMsg( int connection )

hw_Free_Document
Zwalnia pamięć zajmowaną przez dokument Hyperwave.
int hw_Free_Document( int hw_document )

hw_GetAnchors
Zwraca tablicę identyfikatorów obiektów z łączami dokumentów z identyfikatorem obiektu $objectID.
array hw_GetAnchors( int connection, int objectID )

hw_GetAnchorsObj
Zwraca tablicę rekordów obiektów z łączami dokumentów z identyfikatorem obiektu $objectID.
array hw_GetAnchorsObj( int connection, int objectID )

hw_GetAndLock
Zwraca rekord obiektu dla obiektu o identyfikatorze $objectID. Dodatkowo nakłada blokadę na obiekt,
dzięki czemu inni użytkownicy nie mają do niego dostępu do czasu zwolnienia blokady. Patrz również:
hw_Unlock() i hw_GetObject().
string hw_GetAndLock( int connection, int objectID )

hw_GetChildColl
Zwraca tablicę identyfikatorów obiektów. Każdy z identyfikatorów należy do kolekcji potomnej w
kolekcji o identyfikatorze $objectID. Funkcja nie zwraca dokumentów potomnych. Patrz również:
hw_GetChildren() i hw_GetChildDocColl().
array hw_GetChildColl( int connection, int objectID )

hw_GetChildCollObj
Zwraca tablicę rekordów obiektów. Każdy z rekordów obiektów należy do kolekcji potomnej w kolekcji o
identyfikatorze $objectID. Funkcja nie zwraca dokumentów potomnych. Patrz również: hw_ChildrenObj() i
hw_GetChildDocCollObj().
array hw_GetChildCollObj( int connection, int objectID )

Dodatek A - Funkcje 238
hw_GetChildDocColl
Zwraca tablicę identyfikatorów obiektów dokumentów potomnych w kolekcji. Patrz również:
hw_GetChildren()i hw_GetChildColl().
array hw_GetChldDocColl( int connection, int objectID )

hw_GetChildDocCollObj
Zwraca tablicę rekordów obiektów dla dokumentów potomnych w kolekcji. Patrz również:
hw_ChildrenObj()i hw_GetChildCollObj().
array hw_GetChildDocCollObj( int connection, int objectID )

hw_GetObject
Jeżeli drugi parametr jest liczą całkowitą, zwraca rekord obiektu dla obiektu o identyfikatorze $objectID.
Jeżeli drugi parametr jest tablicą liczb całkowitych, funkcja zwróci tablicę rekordów obiektów. W tym przypadku
analizowany jest również trzeci parametr — ciąg zapytania.
array hw_GetObject( int connection, {int,array} objectID, string query )
Ciąg zapytania posiada następującą składnię:
<expr> ::= "(" <expr> ")" |
"!" <expr> | /* Negacja */
<expr> "||" <expr> | /* OR */
<expr> "&&" <expr> | /* AND */
<attribute> <operator> <value>
<attribute> ::= /* dowolna nazwa atrybutu (Tytuł, Autor, TypDokumentu ...) */
<operator> ::= "=" | /* równy */
"<" | /* mniejszy od (porównanie ciągów) */
">" | /* większy od (porównanie ciągów) */
"~" /* porównywanie wyrażeń regularnych */
Zapytanie pozwala na dalszy wybór odpowiednich obiektów z listy zwracanych obiektów. W
przeciwieństwie do pozostałych funkcji zapytań, to zapytanie może działać na atrybutach bez indeksów. Ilość
zwracanych rekordów obiektów zależy od zapytania i od tego, czy możliwy jest dostęp do dokumentu. Patrz
również: hw_GetAndLock() i hw_GetObjectByQuery().

hw_GetObjectByQuery
Przeszukuje wszystkie obiekty na serwerze i zwraca tablicę identyfikatorów obiektów. Maksymalna ilość
identyfikatorów jest ograniczona do $max_hits. Jeżeli $max_hits ma wartość -1, maksymalna ilość zwracanych
identyfikatorów nie jest ograniczona. Zapytanie działa jedynie na atrybutach posiadających indeksy. Patrz
również: hw_GetObjectByQueryObj().
array hw_GetObjectByQuery( int connection, string query, int max_hits )

hw_GetObjectByQueryColl
Przeszukuje obiekty w kolekcji o identyfikatorze $objectID i zwraca tablicę identyfikatorów obiektów.
Maksymalna ilość identyfikatorów jest ograniczona do $max_hits. Jeżeli $max_hits ma wartość -1, maksymalna
ilość zwracanych identyfikatorów nie jest ograniczona. Zapytanie działa jedynie na atrybutach posiadających
indeksy. Patrz również: hw_GetObjectByQueryCollObj().
array hw_GetObjectByQueryColl( int connection, int objectID,
string query, int max_hits )

hw_GetObjectByQueryCollObj
Przeszukuje obiekty w kolekcji o identyfikatorze $objectID i zwraca tablicę rekordów obiektów.
Maksymalna ilość obiektów jest ograniczona do $max_hits. Jeżeli $max_hits ma wartość -1, maksymalna ilość
zwracanych obiektów nie jest ograniczona. Zapytanie działa jedynie na atrybutach posiadających indeksy. Patrz
również: hw_GetObjectByQueryColl().
array hw_GetObjectByQueryCollObj( int connection, int objectID,
string query, int max_hits )

239 PHP – Kompendium wiedzy
hw_GetObjectByQueryObj
Przeszukuje wszystkie obiekty na serwerze i zwraca tablicę rekordów obiektów. Maksymalna ilość
identyfikatorów jest ograniczona do $max_hits. Jeżeli $max_hits ma wartość -1, maksymalna ilość zwracanych
identyfikatorów nie jest ograniczona. Zapytanie działa jedynie na atrybutach posiadających indeksy. Patrz
również: hw_GetObjectByQuery().
array hw_GetObjectByQueryObj( int connection, string query, int max_hits )

hw_GetParents
Zwraca indeksowaną tablicę identyfikatorów obiektów. Każdy identyfikator obiektu jest obiektem
podrzędnym do obiektu o identyfikatorze $objectID.
array hw_GetParents( int connection, int objectID )

hw_GetParentsObj
Zwraca indeksowaną tablicę rekordów obiektów oraz dodatkowo skorelowaną tablicę z danymi
statystycznymi o rekordach obiektów. Dodatkowa tablica jest umieszczona w ostatnim elemencie zwracanej
tablicy. Każdy z rekordów obiektów jest obiektem podrzędnym do obiektu o identyfikatorze $objectID.
array hw_GetParentsObj( int connection, int objectID )

hw_GetRemote
Zwraca zdalny dokument. Dokumenty zdalne w sensie Hyperwave są dokumentami pobieranymi z
zewnętrznych źródeł. Najczęściej dokumentami zdalnymi są strony WWW lub zapytania do baz danych. Aby był
możliwy dostęp do zewnętrznych źródeł dokumentów, Hyperwave wprowadza protokół Hyperwave Gateway
Interface (HGI), który jest podobny do CGI. W chwili obecnej poprzez HGI można uzyskać dostęp do serwerów
http, ftp i niektórych baz danych. Wywołanie hw_GetRemote() zwraca dokument pochodzący ze zdalnego źródła
danych. Jeżeli chcesz użyć tej funkcji, powinieneś dobrze znać HGI. Powinieneś również rozważyć użycie PHP
zamiast Hyperwave do komunikacji z zdalnym źródłem danych. Dodanie obsługi baz danych poprzez Hyperwave
będzie bardziej skomplikowane, niż wykonanie tego samego w PHP. Patrz również: hw_GetRemoteChildren().
int hw_GetRemote( int connection, int objectID )

hw_GetRemoteChilden
Zwraca obiekty pochodne do zdalnego dokumentu. Obiektami pochodnymi zdalnego dokumentu są
również zdalne dokumenty. Funkcja ta może być wykorzystana, jeżeli można ograniczyć zapytanie bazy danych.
Jest to opisane w podręczniku Hyperwave Programmer’s Guide. Jeżeli istnieje tylko jeden obiekt pochodny,
funkcja zwraca ten obiekt sformatowany przez HGI. Jeżeli istneije więcej niż jeden element pochodny, funkcja
zwraca tablice rekordów obiektów, z których każdy być może może być wartością wejściową w kolejnym
wywołaniu funkcji hw_GetRemoteChildren(). Obiekty te są obiektami wirtualnymi i nie istnieją w serwerze
Hyperwave, i dlatego nie posiadają własnych identyfikatorów. Jeżeli zamierzasz wykorzystać tą funkcję
powinieneś dobrze znać HGI. Powinieneś również rozważyć użycie PHP zamiast Hyperwave do komunikacji z
zdalnym źródłem danych. Dodanie obsługi baz danych poprzez Hyperwave będzie bardziej skomplikowane, niż
wykonanie tego samego w PHP. Patrz również: hw_GetRemote().

hw_GetSrcByDestObj
Zwraca rekordy obiektów wskazujących na obiekt o identyfikatorze $objectID. Obiekt ten może być
dokumentem lub zakotwiczeniem. Patrz również: hw_GetAnchors().
array hw_GetSrcByDestObj( int connection, int objectID )

Dodatek A - Funkcje 240
hw_GetText
Zwraca dokument z identyfikatorem $objectID. Jeżeli dokument posiada zakotwiczenia, które można do
niego wstawić, zostaną one wstawione. Opcjonalny parametr $rootID/$prefix może być ciągiem lub liczbą
całkowitą. Jeżeli jest to liczba, określa w jaki sposób wstawiane są do dokumentu łącza. Wartością domyślną jest
0 i powoduje to utworzenie łączy z nazwy i obiektu docelowego łącza. Jest to uzyteczne przy tworzeniu aplikacji
WWW. Jeżeli łącze wskazuje na nazwę internet_movie, łącze HTML będzie miało postać <A
HREF="/internet_movie">. Położenie źródła i celu łącza jest ignorowane. Będziesz musiał tak ustawić swój serwer
WWW, aby zamieniał takie łącza na, na przykład /my_script.php3/internet_movie. Skrypt my_script.php3 musi
sprawdzić zawartość $PATH_INFO i odczytać dokument. Wszystkie łącza powinny zaczynać się od
/my_script.php3/. Jeżeli nie chcesz tego robić, możesz ustawić odpowiednio parametr opcjonalny
$rootID/$prefix na dowolny prefiks. W tym przypadku musi być to ciąg.
int hw_GetText( int connection, int objectID [, mixed rootID/prefix])
Jeżeli $rootID/$prefix jest liczbą różną od 0, łącze jest tworzone z wszystkich nazw względem bieżącego
obiektu, zaczynając od $rootID/$prefix oddzielanych ukośnikami. Jeżeli na przykład poprzedni dokument
internet_movie jest umieszczony w a-b-c-internet_movie, gdzie znak - jest separatorem hierarchii w serwerze
Hyperwave, a dokument źródłowy znajduje się w a-b-d-source, wynikowe łącze HTML będzie miało postać <A
HREF="../c/internet_movie">. Jest to korzystne, jeżeli chcesz zapisać całą zawartość serwera na dysk i
odwzorować hierarchię dokumentów w systemie plików. Funkcja ta działa jedynie dla dokumentów tekstowych.
Nie otwiera ona specjalnego połączenia, więc blokuje na czas przesyłu połączenie sterujące.
Patrz również: hw_PipeDocument(), hw_FreeDocument(), hw_Document_BodyTag(), hw_Document_Size() i
hw_Open_Document().

hw_getusername
Zwraca nazwę użytkownika połączenia.
string hw_getusername( int connection )

hw_Identify
Autoryzuje użytkownika za pomocą ciągów $username i $password. Autoryzacja jest ważna jedynie w
bieżącej sesji. Funkcja nie jest potrzebna zbyt często. W większości przypadków łatwiejszą metodą autoryzacji
jest otwarcie połączenia. Patrz również: hw_Connect().

hw_InCollections
Sprawdza, czy zbiór obiektów (dokumentów lub kolekcji) przekazanych w $object_id_array jest częścią
kolekcji przekazanych w $collection_id_array. Jeżeli czwarty parametr, $return_collections jest równy 0,
podzbiór identyfikatorów będących częścią kolekcji (na przykład dokumenty lub kolekcje podrzędne do jednej,
lub więcej kolekcji) jest zwracany w postaci tablicy. Jednak gdy czwarty argument jest równy 1, zbiór kolekcji
posiadających co najmniej jeden obiekt w znalezionym podzbiorze obiektów podrzędnych jest zwracany w
postaci tablicy. Opcja ta pozwala na przykład, na wyróżnienie części hierarchii dokumentów, w której zawarte są
wyniki przekazanego zapytania.
array hw_InCollections( int connection, array object_id_array,
array collection_id_array, int return_collections)

hw_Info
Zwraca dane na temat bieżącego połączenia. Zwracany ciąg posiada następującą postać: <Serwer>,
<Host>, <Port>, <Użytkownik>, <Port na kliencie>, <Zamiana bajtów>.
string hw_Info( int connection )

241 PHP – Kompendium wiedzy
hw_InsColl
Wstawia nową kolekcję z atrybutami zawartymi w tablicy $object_array, do kolekcji o identyfikatorze
$objectID.
int hw_InsColl( int connection, int objectID, array object_array )

hw_InsDoc
Wstawia nowy dokument z atrybutami zawartymi w $object_record, do kolekcji o identyfikatorze
$parentID. Funkcja wstawia rekord obiektu lub rekord obiektu oraz tekst przekazany w $text, o ile został podany.
Jeżeli chcesz wstawić dokument dowolnego typy, należy użyć funkcji hw_insertdocument(). Patrz również:
hw_InsertDocument() i hw_InsColl().
int hw_InsDoc( int connection, int parentID, string object_record, string 3text )

hw_InsertDocument
Przesyła dokument do kolekcji o identyfikatorze $parentID. Dokument musi być wcześniej utworzony za
pomocą funkcji hw_NewDocument(). Należy się upewnić, że rekord obiektu posiada co najmniej następujące
atrybuty: Type, DocumentType, Title i Name. Przydatne jest ustawienie atrybutu MimeType. Funkcja zwraca
identyfikator obiektu nowego dokumentu, lub False. Patrz również: hw_PipeDocument().
int hw_InsertDocument( int connection, int parent_id, int hw_document )

hw_InsertObject
Umieszcza obiekt na serwerze. Obiekt musi być prawidłowym obiektem Hyperwave. Więcej danych na
ten temat znajduje się w dokumentacji HG-CSP.
Uwaga

Jeżeli chcesz wstawić zakotwiczenie atrybut position musi być zawsze ustawiony na wartość początkową, końcową lub invisible.
Pozycje niewidoczne są wymagane, jeżeli opis ni ma odpowiedniego łącza do tekstu opisu.

Patrz również: hw_PipeDocument(), hw_InsertDocument(), hw_InsDoc() i hw_InsColl().
int hw_InsertObject( int connection, string object_rec, string parameter )

hw_mapid
Mapuje identyfikator globalnego obiektu na dowolnym serwerze Hyperwave, nawet jeżeli nie wykonano
podłączenia do niego za pomocą hw_connect(), do identyfikatora obiektu wirtualnego. Ten obiekt wirtualny może
być używany identycznie, jak każdy inny identyfikator obiektu, na przykład do pobrania rekordu obiektu za
pomocą hw_getobject(). Identyfikator serwera jest pierwszą częścią globalnego identyfikatora obiektu (GOid) i w
chwili obecnej jest to numer IP w postaci liczby.
Uwaga

Aby użyć tej funkcji należy ustawić znacznik F_DISTRIBUTED, co obecnie można wykonać jedynie w czasie kompilacji pliku
hg_comm.c. Nie jest to wykonywane domyślnie. Należy zapoznać się z komentarzami na początku pliku hg_comm.c.

int hw_mapid( int connection, int server_id, int object_id )

hw_Modifyobject
Polecenie to pozwala na usunięcie, dodanie lub zmianę poszczególnych atrybutów rekordu obiektu. Obiekt
jest określony za pomocą identyfikatora $object_to_change. Pierwsza tablica, $remove, jest listą atrybutów do
usunięcia. Druga tablica, $add, jest listą atrybutów które należy dodać do obiektu. W celu zmiany atrybutu, należy
usunąć stary atrybut i dodać nowy. Funkcja hw_Modifyobject() zawsze najpierw usuwa stare atrybuty a potem
dodaje nowe chyba, że wartości atrybutów do usunięcia nie są tablicą ciągów. Ostatni parametr wskazuje, czy
Dodatek A - Funkcje 242
operacja ma być wykonana w sposób rekurencyjny. Wartość 1 oznacza rekurencyjne wywołanie. Jeżeli nie jest
możliwa modyfikacja niektórych obiektów, zostaną pominięte bez żadnej informacji o tym fakcie. Funkcja
hw_error() może nie wskazać żadnego błędu, choć nie wszystkie obiekty będą zmienione.
int hw_Modifyobject( int connecion, int object_to_change, array remove,
array add, int mode )
Kluczami w obu tablicach są nazwy atrybutów. Wartością każdego z elementów tablicy może być tablica
ciągów, ciąg, bądź cokolwiek innego. Jeżeli będzie to tablica, wartość atrybutu jest tworzona z klucza, średnika i
wartości. Wstawienie pustego ciągu spowoduje całkowite usunięcie atrybutu. Jeżeli wartość nie jest ani ciągiem
ani tablicą, ale na przykład liczbą, nie będą podejmowane żadne operacje na atrybucie. Jest to niezbędne, jeżeli
chcesz dodać całkowicie nowy atrybut a nie wartość istniejącego atrybutu. Jeżeli tablica z elementami do
usunięcia będzie zawierała pusty ciąg dla atrybutu, usunięcie się nie uda. Ustawienie wartości tego atrybutu na, na
przykład 0, spowoduje, że nie będzie podjęta próba jego usunięcia, natomiast dodanie atrybutu powiedzie się.
Jeżeli chcesz zmienić atrybut Name z bieżącą wartością książki na artykuły, powinieneś utworzyć dwie tablice i
wywołać funkcję hw_modifyobject().
Przykład: zmiana atrybutu
// $connect jest istniejącym połączeniem do serwera Hyperwave
// $objid jest identyfikatorem obiektu do zmiany
$remarr = array("Name" => "książki");
$addarr = array("Name" => "artykuły");
$hw_modifyobject($connect, $objid, $remarr, $addarr);
Aby usunąć lub dodać parę nazwa–wartość, należy przekazać tablice zawierające dodawane i usuwane
atrybuty przekazując w trzecim parametrze pustą tablicę. Jeżeli atrybut jest pierwszym atrybutem o tej nazwie,
należy ustawić wartości w tablicy atrybutów do usunięcia na liczby całkowite.
Przykład: dodanie całkowicie nowego atrybutu
// $connect jest istniejącym połączeniem do serwera Hyperwave
// $objid jest identyfikatorem obiektu do zmiany
$remarr = array("Name" => 0);
$addarr = array("Name" => "artykuły");
$hw_modifyobject($connect, $objid, $remarr, $addarr);

Uwaga

Atrybuty w wielu językach, na przykład Title mogą być zmieniane na dwa sposoby. Pierwszym sposobem jest wpisywanie warto ści
atrybutów w postaci język:tytuł, lub przekazywanie tablicy z elementami dla każdego języka zapisanymi w identyczny sposób jak
poprzednio. Poprzedni przykład może być zapisany w następujący sposób:

Przykład: zmiana atrybutu Title
$remarr = array("Title" => "en:Books");
$addarr = array("Title" => "en:Articles");
$hw_modifyobject($connect, $objid, $remarr, $addarr);
lub
Przykład: zmiana atrybutu Title
$remarr = array("Title" => array("en" => "Books"));
$addarr = array("Title" => array("en" => "Articles", "ge"=>"Artikel");
$hw_modifyobject($connect, $objid, $remarr, $addarr);
W przykładzie tym zostanie usunięty angielski tytuł Books i dodany tytuł angielski Articles i niemiecki
Artikel.
Przykład: usuwanie atrybutu
$remarr = array("Title" => "");
$addarr = array("Title" => "en:Articles");
$hw_modifyobject($connect, $objid, $remarr, $addarr);

Uwaga

Wykonanie poprzedniego przykładu spowoduje usunięcie wszystkich atrybutów o nazwie Title i dodanie nowego atrybutu Title. Jest to
wygodne, jeżeli chcesz usuwać atrybuty w sposób rekurencyjny. Jeżeli chcesz usunąć wszystkie atrybuty o zadanej nazwie, musisz
przekazać pusty ciąg jako wartość atrybutu. Jedynie atrybuty Title, Description i Keyword prawidłowo obsługują prefiksy języków.
Jeżeli atrybut nie posiada prefiksu języka, użyty zostanie prefiks xx. Atrybut Name ma specjalne znaczenie; w niektórych przypadkach
nie jest możliwe jego całkowite usunięcie. Otrzymasz wtedy komunikat błędu Change of base attribute (nie jest jasna przyczyna tego
błędu). Dlatego zawsze musisz dodawać nowy atrybut Name przed usunięciem starego.

Uwaga

Nie musisz otaczać wywołania tej funkcji wywołaniami hw_getandlock() i hw_unlock(). Funkcja hw_modifyobject() robi to
samodzielnie. Zwraca True w przypadku pomyślnego wykonania i False w przypadku błędu.
243 PHP – Kompendium wiedzy
hw_Mv
Przenosi obiekt o identyfikatorze podanym w drugim parametrze z kolekcji i identyfikatorze $source do
kolekcji o identyfikatorze $destination. Jeżeli identyfikator kolekcji docelowej wynosi 0, obiekt jest usuwany ze
źródłowej kolekcji. Jeżeli jest to ostatni egzemplarz obiektu, zostanie on usunięty. Jeżeli chcesz usunąć wszystkie
wystąpienia za pomocą jednego wywołania, należy użyć funkcji hw_deleteobject(). Zwracana wartość jest
ilością przeniesionych obiektów. Patrz również: hw_cp() i hw_deleteobject().
int hw_Mv( int connection, array object_id_array, int souce_id, int destination_id )

hw_New_Document
Zwraca nowy dokument Hyperwave. Zawartość dokumentu jest przekazana w parametrze $document_data,
natomiast rekord obiektu w $object_record. Wielkość danych musi być przekazana w parametrze
$document_size. Funkcja ta nie wstawia dokumentu do serwera Hyperwave. Patrz również: hw_FreeDocument(),
hw_Document_Size(), hw_Document_BodyTag(), hw_Output_Document() i hw_InsertDocument().
int hw_new_document (string object_record, string document_data, int document_size)

hw_Objrec2Array
Konwertuje $object_record na tablicę obiektów. Kluczami wynikowej tablicy są nazwy atrybutów.
Atrybuty wielowartościowe, takie jak Title, w różnych językach tworzą własne tablice. Kluczami w tych tablicach
części stojące z lewej strony dwukropka. Część ta musi mieć dwa znaki. Inne wartości wielowartościowe nie
posiadające prefiksów tworzą indeksowaną tablicę. Jeżeli nie podany został parametr opcjonalny, atrybuty Title,
Description i Keyword są traktowane jako atrybuty języka,, natomiast atrybuty Group, Parent i HtmlAttr są
traktowane jako wartości wielowartościowe bez prefiksu. Przekazując tablice zawierająca typy każdego z
argumentów można zmienić to zachowanie. Tablica jest tablicą asocjacyjną z nazwą atrybutu jako klucze i
wartościami będącymi jedną z wartości HW_ATTR_LANG lub HW_ATTR_NONE. Patrz również: hw_array2objrec().
array hw_objrec2array (string object_record [, array format])

hw_Output_Document
Drukuje dokument bez znacznika BODY. Dla zgodności z poprzednimi wersjami można użyć nazwy
hw_OutputDocument(),
ale jest to postać przestarzała.
int hw_output_document (int hw_document)

hw_pConnect
W przypadku powodzenia zwraca numer połączenia, lub False w przypadku, gdy połączenie nie może być
wykonane. Otwiera trwałe połączenie do serwera Hyperwave. Każdy z argumentów oprócz numeru portu,
powinien być ciągiem znaków. Argumenty $username i $password są opcjonalne. W takim przypadku nie jest
wykonywana autoryzacja na serwerze. Jest to zbliżone do korzystania z konta gościa. Funkcja zwraca numer
połączenia, który jest przekazywany do wszystkich funkcji Hyperwave. Można mieć kilka jednocześnie otwartych
trwałych połączeń. Patrz również: hw_Connect().
int hw_pconnect (string host, int port, string username, string password)

hw_PipeDocument
Zwraca dokument Hyperwave o identyfikatorze obiektu $objectID. Jeżeli dokument posiada możliwe do
wstawienia zakotwiczenia, zostaną one wstawione. Dokument zostanie przesłany poprzez połączenie specjalne,
które nie blokuje połączenia sterującego. Patrz również: hw_GetText(). Więcej informacji na temat wstawiania
łączy znajduje się w opisie funkcji hw_FreeDocument(), hw_Document_Size(), hw_Document_BodyTag() i
hw_Output_Document().
int hw_pipedocument (int connection, int objectID)

Dodatek A - Funkcje 244
hw_Root
Zwraca identyfikator obiektu kolekcji korzenia; w chwili obecnej posiada ona wartość 0. Kolekcja
potomna tej kolekcji jest główną kolekcją serwera.
int hw_root ()

hw_Unlock
Odblokowuje dokument, więc użytkownicy mają nów do niego dostęp. Patrz również: hw_GetAndLock().
int hw_unlock (int connection, int objectID)

hw_Who
Zwraca tablicę z użytkownikami obecnie zalogowanymi na serwerze Hyperwave. Każda pozycja jest
kolejna tablica, zawierającą atrybuty: identyfikator elementu, nazwa, system, pola onSinceDate, onSinceTime,
TotalTime, oraz self. Wartością self jest 1 jeżeli pozycja należy do użytkownika inicjującego żądanie.
int hw_who (int connection)

ibase_close
Zamyka połączenie z bazą danych InterBase identyfikowane przez identyfikator połączenia zwracany
przez funkcję ibase_connect(). Jeżeli identyfikator połączenia jest pominięty, funkcja działa na ostatnio
otwartym połączeniu. Domyślna transakcja w tym połączeniu jest zatwierdzana, natomiast pozostałe transakcje są
wycofywane.
int ibase_close ([int connection_id])

ibase_connect
Nawiązuje połączenie z serwerem InterBase. Argument $database musi być ścieżką do pliku bazy danych
na serwerze. Jeżeli serwer nie jest serwerem lokalnym, musi być poprzedzony albo ciągiem hostname: (TCP/IP),
//hostname (NetBEUI) lub hostname@ (IPX/SPX) w zależności od protokołu używanego w tym połączeniu.
Parametry $username i $password mogą być pobierane w dyrektywach konfiguracji ibase.default_user i
ibase.default_password. Parametr $charset jest domyślnym zestawem znaków dla bazy danych. Parametr
$buffers określa ilość buforów bazy danych zakładanych na serwerze. Jeżeli podane zostanie 0 lub parametr
zostanie opuszczony, serwer zakłada domyślną dla siebie ilość buforów. Parametr $dialect wybiera domyślny
dialekt SQL obowiązujący dla wszystkich wyrażeń wykonywanych poprzez bieżące połączenie. Wartością
domyślną jest najwyższy dialekt obsługiwany przez biblioteki klienta. Jeżeli ibase_connect() jest wywoływany
po raz drugi z tymi samymi parametrami, nie jest tworzone nowe połączenie. Zamiast tego zwracany jest
identyfikator istniejącego połączenia. Połączenie z serwerem jest zamykane natychmiast po zakończeniu
wykonywania skryptu, chyba, że wcześniej została wywołana funkcja ibase_close().
int ibase_connect(string database [, string username [, string password
[, string charset [, int buffers [, int dialect [, string role]]]]]])
Przykład: ibase_connect()
<?php
$dbh = ibase_connect ($host, $username, $password);
$stmt = 'SELECT * FROM tblname';
$sth = ibase_query ($dbh, $stmt);
while ($row = ibase_fetch_object ($sth))
{
print $row->email . "\n";
}
ibase_close ($dbh);
?>

Uwaga

Parametry $buffers i $dialect zostały dodane w PHP4-RC2. Działają one jedynie z serwerem InterBase 6 i nowszymi od niego.
Parametr $role działa jedynie z InterBase 5 i wyższymi.

Patrz również: ibase_pconnect().
245 PHP – Kompendium wiedzy
ibase_execute
Wykonuje zapytanie przygotowane za pomocą ibase_prepare(). W przypadku, gdy to samo zapytanie jest
powtarzane dla różnych parametrów, jest to o wiele bardziej efektywne niż wykorzystanie ibase_query().
int ibase_execute (int query [, int bind_args])
Przykład: ibase_execute()
<?php
$updates = array(
1 => 'Eric',
5 => 'Filip',
7 => 'Larry'
);

$query = ibase_prepare("UPDATE FOO SET BAR = ? WHERE BAZ = ?");

while (list($baz, $bar) = each($updates)) {
ibase_execute($query, $bar, $baz);
}
?>

ibase_fetch_object
Pobiera wiersz w postaci pseudoobiektu korzystając z $result_id zwróconego przez ibase_query() lub
ibase_execute().
object ibase_fetch_object (int result_id)
Przykład: ibase_fetch_object()
<php
$dbh = ibase_connect ($host, $username, $password);
$stmt = 'SELECT * FROM tblname';
$sth = ibase_query ($dbh, $stmt);
while ($row = ibase_fetch_object ($sth)) {
print $row->email . "\n";
}
ibase_close ($dbh);
?>
Patrz również: ibase_fetch_row().

ibase_fetch_row
Zwraca kolejny wiersz korzystając z identyfikatora wyniku zwracanego przez funkcję ibase_query().
array ibase_fetch_row (int result_identifier)

ibase_free_query
Zwalnia zapytanie przygotowane przez ibase_prepare().
int ibase_free_query (int query)

ibase_free_result
Zwalania wynik tworzony przez ibase_query().
int ibase_free_result (int result_identifier)

ibase_num_fields
Zwraca liczbę określającą ilość pól w wyniku zapytania. Patrz również ibase_field_info().
Uwaga

Funkcja ibase_num_fields() nie działa jeszcze w PHP 4.

Przykład: ibase_num_fields()
Dodatek A - Funkcje 246
<?php
$dbh = ibase_connect ($host, $username, $password);
$stmt = 'SELECT * FROM tblname';
$sth = ibase_query ($dbh, $stmt);

if (ibase_num_fields($sth) > 0) {
while ($row = ibase_fetch_object ($sth)) {
print $row->email . "\n";
}
} else {
die ("No Results were found for your query");
}

ibase_close ($dbh);
?>

ibase_pconnect
Działa bardzo podobnie do ibase_connect() z dwiema poważnymi różnicami. Po pierwsze, w czasie
dołączania do serwera funkcja próbuje odszukać istniejące (trwałe) połączenie otwarte z tym samym zestawem
argumentów. Jeżeli zostanie znalezione takie połączenie, jego identyfikator jest zwracany i nie jest tworzone
nowe połączenie. Po drugie, Połączenie do serwera InterBase nie jest zamykane po zakończeniu wykonywania
skryptu. Połączenie pozostaje otwarte do wykorzystania w przyszłości (ibase_close() nie zamyka połączeń
utworzonych przez ibase_pconnect()). Połączenie takie nazywane jest z tego powodu połączeniem trwałym. Opis
parametrów przekazywanych do funkcji jest zamieszczony przy opisie funkcji ibase_connect(); są one
identyczne.
int ibase_pconnect(string database [, string username [, string password
[, string charset [, int buffers [, int dialect [, string role]]]]]])

ibase_prepare
Przygotowuje zapytanie dodając obsługę parametrów dołączanych później za pomocą funkcji
ibase_execute().
int ibase_prepare ([int link_identifier, string query])

ibase_query
Wykonuje zapytanie na basie danych InterBase i zwraca identyfikator wyniku wykorzystywany w
funkcjach ibase_fetch_row(), ibase_fetch_object(), ibase_free_result() i ibase_free_query(). Mimo, że
funkcja ta obsługuje dołączanie zmiennych do parametrów, używanie tego udogodnienia w tej właśnie funkcji nie
ma wielkiego sensu. Przykład użycia funkcji jest umieszczony przy opisie funkcji ibase_prepare() i
ibase_execute().
int ibase_query ([int link_identifier, string query [, int bind_args]])

ibase_timefmt
Ustala format znacznika czasu, daty i czasu dla kolumn tych typów zwracanych zapytaniach.
Wewnętrznie kolumny są formatowane przez funkcję C strftime(), więc wszelkie szczegóły na temat ciągu
formatującego znajdują się w jej dokumentacji. Parametr $columntype jest jedną ze stałych IBASE_TIMESTAMP,
IBASE_DATE i IBASE_TIME. Jeżeli zostanie ion opuszczony, przyjmowana jest wartość IBASE_TIMESTAMP w celu
zapewnienia zgodności z poprzednimi wersjami.
int ibase_timefmt (string format [, int columntype])
Przykład: ibase_timefmt()
<?php
// kolumny InterBase 6 typu TIME będą zwracane w postaci
// '05 godzin 37 minut'.
ibase_timefmt("%H godzin %M minut", IBASE_TIME);
?>
Można również ustawić domyślny format za pomocą dyrektyw konfiguracji PHP ibase.timestampformat,
ibase.dateformat i ibase.timeformat. Parametr $columntype został dodany w PHP 4.0. Ma on znaczenie jedynie
w wersjach InterBase 6 i wyższych.

247 PHP – Kompendium wiedzy
Uwaga

W PHP 4.0 wprowadzono zmianę, która powodowała niezgodność z poprzednimi wersjami, zmieniaj ąc nazwę dyrektywy konfiguracji
ibase.timeformat na ibase.timestampformat oraz dodane zostały dyrektywy ibase.dateformat i ibase.timeformat w
celu ulepszenia działania funkcji.

icap_close
Zamyka podany strumień icap.
int icap_close (int icap_stream [, int flags])

icap_delete_event
Usuwa zdarzenie z kalendarza o identyfikatorze podanym w $uid. Zwraca True.
string icap_delete_event (int sream_id, int uid)

icap_fetch_event
Pobiera zdarzenie określone przez $event_id ze strumienia kalendarza.
int icap_fetch_event (int stream_id, int event_id [, int options])
Zwraca obiekt zdarzenia zawierający następujące atrybuty:
• int id — Identyfikator zdarzenia.
• int public — True jeżeli zdarzenie jest publiczne lub False, gdy jest prywatne.
• string category — Ciąg zawierający kategorię zdarzenia.
• string title — Ciąg zawierający tytuł zdarzenia.
• string description — Ciąg z opisem zdarzenia .
• int alarm — Ilość minut przed zdarzeniem gdy wysyłane jest powiadomienie lub alarm.
• object start — Obiekt zawierający datę i czas.
• object end — Obiekt zawierający datę i czas.
Wszystkie pozycje daty i czasu zawierają następujące atrybuty:
• int year — rok
• int month — miesiąc
• int mday — dzień miesiąca
• int hour — godzina
• int min — minuty
• int sec — sekundy

icap_list_alarms
Zwraca tablicę z identyfikatorami zdarzeń które wyślą alarm o podanej godzinie. Funkcja
icap_list_alarms() wymaga podania daty i czasu oraz strumienia kalendarza. Zwracana jest tablica
identyfikatorów zdarzeń z aktywnym alarmem o podanej godzinie.
int icap_list_alarms (int stream_id, array date, array time)
Wszystkie pozycje z datą i czasem zawierają następujące atrybuty:
• int year — rok
• int month — miesiąc
• int mday — dzień miesiąca
• int hour — godzina
• int min — minuty
• int sec — sekundy

Dodatek A - Funkcje 248
icap_list_events
Zwraca tablicę identyfikatorów zdarzeń pomiędzy dwiema datami. Funkcja icap_list_events() pobiera
ze strumienia identyfikatory zdarzeń pomiędzy data początkową i końcową. Zwracana jest tablica identyfikatorów
zdarzeń pomiędzy podanymi datami.
array icap_list_events (int stream_id, int begin_date [, int end_date])
Wszystkie pozycje z datą i czasem zawierają następujące atrybuty:
• int year — rok
• int month — miesiąc
• int mday — dzień miesiąca
• int hour — godzina
• int min — minuty
• int sec — sekundy

icap_open
W przypadku powodzenia zwraca strumień ICAP, natomiast w przypadku wystąpienia błędu, False.
Funkcja icap_open() otwiera połączenie ICAP do podanego w $calendar magazynu. Jeżeli podany jest parametr
opcjonalny $options, jest on również przekazany do otwieranej skrzynki pocztowej.
stream icap_open (string calendar, string username, string password, string options)

icap_snooze
Włącza alarm dla zdarzenia kalendarza określonego przez $uid. Zwraca True.
string icap_snooze (int stream_id, int uid)

icap_store_event
Zapisuje zdarzenie w kalendarzu ICAP.
string icap_store_event (int stream_id, object event)
Obiekt zdarzenia zawiera następujące atrybuty:
• int id — Identyfikator zdarzenia.
• int public — 1 jeżeli zdarzenie jest publiczne lub 0, gdy jest prywatne.
• string category — Ciąg zawierający kategorię zdarzenia.
• string title — Ciąg zawierający tytuł zdarzenia.
• string description — Ciąg z opisem zdarzenia .
• int alarm — Ilość minut przed zdarzeniem gdy wysyłane jest powiadomienie lub alarm.
• object start — Obiekt zawierający datę i czas.
• object end — Obiekt zawierający datę i czas.
Wszystkie pozycje daty i czasu zawierają następujące atrybuty:
• int year — rok
• int month — miesiąc
• int mday — dzień miesiąca
• int hour — godzina
• int min — minuty
• int sec — sekundy
Zwraca True w przypadku powodzenia operacji lub False w przypadku błędu.

ifxus_close_slob
Usuwa obiekt slob dla podanego w $bid identyfikatora obiektu slob. Zwraca False w przypadku
wystąpienia błędu, True w przypadku powodzenia operacji.
int ifxus_close_slob (int bid)

249 PHP – Kompendium wiedzy
ifxus_create_slob
Tworzy obiekt slob i otwiera go. Tryby: 1 = LO_RDONLY, 2 = LO_WRONLY, 4 = LO_APPEND, 8 = LO_RDWR, 16 =
LO_BUFFER, 32 = LO_NOBUFFER. Można również użyć stałych o nazwach IFX_LO_RDONLY, IFX_LO_WRONLY itd. Zwraca
False w przypadku wystąpienia błędu a w przypadku powodzenia, identyfikator obiektu slob.

ifxus_free_slob
Usuwa obiekt slob. Parametr $bid jest identyfikatorem obiektu slob. Zwraca False w przypadku
wystąpienia błędu i True w przeciwnym wypadku.
int ifxus_free_slob (int bid)

ifxus_open_slob
Otwiera obiekt slob. Parametr $bid powinien być identyfikatorem istniejącego obiektu. Tryby: 1 =
LO_RDONLY, 2 = LO_WRONLY, 4 = LO_APPEND, 8 = LO_RDWR, 16 = LO_BUFFER, 32 = LO_NOBUFFER. Zwraca False w
przypadku wystąpienia błędu a w przeciwnym wypadku identyfikator nowego obiektu slob.
int ifxus_open_slob (long bid, int mode)

ifxus_read_slob
Czyta n bajtów z obiektu slob. Parametr $bid jest identyfikatorem istniejącego obiektu slob, natomiast
$nbytes jest ilością bajtów do przeczytania. Zwraca False w przypadku wystąpienia błędu a w przeciwnym
wypadku zwraca ciąg.
int ifxus_read_slob (long bid, long nbytes)

ifxus_seek_slob
Ustawia bieżący plik lub wyszukuje pozycję w otwartym obiekcie slob. Parametr $bid powinien być
identyfikatorem istniejącego obiektu slob. Tryby: 0 = LO_SEEK_SET, 1 = LO_SEEK_CUR, 2 = LO_SEEK_END natomiast
$offset jest przesunięciem w bajtach. Zwraca False w przypadku wystąpienia błędu a w przeciwnym wypadku
pozycję w pliku.
int ifxus_seek_slob (long bid, int mode, long offset)

ifxus_tell_slob
Zwraca bieżącą pozycję w pliku lub przesunięcie w otwartym obiekcie slob. Parametr $bid powinien być
identyfikatorem istniejącego obiektu slob. Zwraca False w przypadku wystąpienia błędu a w przeciwnym
wypadku pozycję w pliku.
int ifxus_tell_slob (long bid)

ifxus_write_slob
Zapisuje zawartość ciągu do obiektu slob. Parametr $bid musi być identyfikatorem obiektu slob, natomiast
$content zawiera dane do zapisania. Zwraca False w przypadku wystąpienia błędu, w przeciwnym przypadku
ilość zapisanych bajtów.
int ifxus_write_slob (long bid, string content)

ifx_affected_rows
Parametr $result_id powinien być identyfikatorem wyniku zwracanym przez funkcję ifx_query() lub
ifx_prepare(). Zwraca ilość wierszy zmienionych przez zapytanie związane z $result_id. W przypadku operacji
wstawienia, zamiany lub usunięcia, ilość ta jest prawdziwą liczbą operacji (sqlerrd[2]). W przypadku operacji
SELECT nie jest dokładną liczbą zwracanych wierszy, a jedynie oszacowaniem (sqlerrd[0]). Serwer bazy danych
może nigdy nie zwrócić ilości wierszy zwracanych przez operację SELECT w tej fazie operacji (zaraz po operacji
Dodatek A - Funkcje 250
PREPARE, po ustaleniu planu wykonania przez optymalizator), ponieważ nie rozpoczął on odczytywania
zwracanych wierszy. Jest to użyteczna funkcja do ograniczania zapytań do takich, które zwracają rozsądną ilość
wierszy po przygotowaniu zapytania przez ifx_prepare(). Patrz również: ifx_num_rows().
int ifx_affected_rows (int result_id)
Przykład: wykorzystanie funkcji serwera Informix ifx_affected_rows()
$rid = ifx_prepare ("select * from emp
where name like " . $name, $connid);
if (! $rid) {
... błąd ...
}
$rowcount = ifx_affected_rows ($rid);
if ($rowcount > 1000) {
printf ("Zapytanie zwraca zbyt dużo wierszy: (%d)\n<br>", $rowcount);
die ("Proszę ograniczyć zapytanie<br>\n");
}

ifx_blobinfile_mode
Ustawia domyślny tryb przechowywania obiektów blob w zapytaniach SELECT. Tryb 0 oznacza
przechowywanie blobów w pamięci, natomiast 1, przechowywanie ich w pliku.
void ifx_blobinfile_mode (int mode)

ifx_byteasvarchar
Ustawia domyślny tryb zapytaniach SELECT. Tryb 0 powoduje zwracanie identyfikatora blob, natomiast 1,
zwracanie pola varchar z danymi tekstowymi.
void ifx_byteasvarchar (int mode)

ifx_close
Zawsze zwraca True. Funkcja ifx_close() powoduje zamknięcie połączenia z bazą danych Informix,
skojarzoną z podanym identyfikatorem połączenia. Jeżeli nie został podany identyfikator połączenia, zamykane
jest ostatnio otwarte połączenie.
Uwaga

nie jest to operacja niezbędna, ponieważ nietrwałe połączenia są zamykane automatycznie po zakończeniu wykonywania skryptu.
Funkcja ifx_close() nie zamyka połączeń trwałych generowanych przez ifx_pconnect().

Patrz również: ifx_connect() i ifx_pconnect().
int ifx_close ([int link_identifier])
Przykład: Zamykanie połączenia z bazą danych Informix
$conn_id = ifx_connect ("mydb@ol_srv", "itsme", "mypassword");
... potrzebne zapytania ...
ifx_close($conn_id);

ifx_connect
Zwraca identyfikator połączenia lub false w przypadku wystąpienia błędu. Funkcja ifx_connect()
nawiązuje połączenie z serwerem Informix. Wszystkie argumenty są opcjonalne i w przypadku opuszczenia
któregoś z nich, z pliku konfiguracyjnego pobierane są wartości domyślne, ifx.default_host zawiera nazwę
serwera biblioteki Informixa korzystają ze zmiennej środowiskowej INFORMIXSERVER), ifx.default_user zawiera
nazwę użytkownika a ifx.default_password zawiera domyślne hasło (jeżeli nie jest zdefiniowana to bez hasła).
Jeżeli drugi raz wywołano funkcję ifx_connect() z tymi samymi argumentami, nie jest nawiązywane nowe
połączenie. Zamiast tego zwracany jest identyfikator istniejącego połączenia. Połączenie nie jest zamykane po
zakończeniu wykonywania skryptu lub po wywołaniu funkcji ifx_close(). Patrz również: ifx_pconnect() i
ifx_close().
int ifx_connect (string [database], string [userid], string [password])
Przykład: Podłączenie do bazy Informix
$conn_id = ifx_connect ("mydb@ol_srv", "itsme", "mypassword");

251 PHP – Kompendium wiedzy
ifx_copy_blob
Tworzy kopię podanego obiektu blob. Parametr $bid jest identyfikatorem obiektu blob. Zwraca False w
przypadku błędu, a w przeciwnym wypadku identyfikator nowego obiektu blob.
int ifx_copy_blob (int bid)

ifx_create_blob
Tworzy obiekt blob. Zwraca False w przypadku błędu, a w przeciwnym wypadku identyfikator nowego
obiektu blob.
int ifx_create_blob (int type, int mode, string param)
• Parametr $type: 1 = TEXT, 0 = BYTE
• Parametr $mode: 0 = obiekt blob jest przechowywany w pamięci, 1 = obiekt blob jest przechowywany w
pliku.
• Parametr $param: Jeżeli tryb=0 jest to wskaźnik na zawartość, jeżeli tryb=1, wskaźnik na strumień pliku.

ifx_create_char
Tworzy obiekt znakowy. Parametr $param powinien zawierać zawartość obiektu.
int ifx_create_char (string param)

ifx_do
Zwraca True gdy operacja się powiodła i False w przypadku błędu. Wykonuje uprzednio przygotowane
zapytanie i otwiera dla niego kursor. W przypadku wystąpienia błędu nie zwalnia $result_id. Dla zapytań innych
od SELECT ustawia również właściwą liczbę zmienionych wierszy, którą można odczytać za pomocą
ifx_affected_rows(). Patrz również ifx_prepare().
int ifx_do (int result_id)

ifx_error
Kody błędów serwera Informix (SQLSTATE i SQLCODE) są formatowane następująco:
x [SQLSTATE = aa bbb SQLCODE=cccc]
Gdy x jest spacją, oznacza to brak błędu.
E — błąd
N — koniec danych
W — ostrzeżenie
? — niezdefiniowany
string ifx_error (void)
Jeżeli znak x jest czymkolwiek poza spacją, SQLSTATE i SQLCODE dokładniej opisują błąd. Opis
SQLSTATE i SQLCODE można znaleźć w podręczniku do serwera Informix. Zwraca jeden znak określający
wynik wyrażenia oraz zarówno wartość SQLSTATE jak i SQLCODE związane z ostatnio wykonywanym
wyrażeniem SQL. Format tego ciągu jest następujący: (znak) [SQLSTATE=(dwa znaki) (trzy znaki)
SQLCODE=(jeden znak)]. Pierwszym znakiem może być spacja (sukces), W (wyrażenie wygenerowało
ostrzeżenia), E (zdarzył się błąd w czasie wykonywania wyrażenia) lub N (wyrażenie nie zwróciło żadnych
danych). Patrz również: ifx_errormsg().

ifx_erormsg
Zwraca komunikat serwera Informix opisujący ostatni błąd w serwerze lub gdy podano wartość
opcjonalnego parametru $errorcode, komunikat związany z podanym numerem błędu. Patrz również:
idx_error().
string ifx_errormsg ([int errorcode])
Przykład: ifx_errormsg()
printf("%s\n<br>", ifx_errormsg(-201));

Dodatek A - Funkcje 252
ifx_fetch_row
Zwraca tablicę asocjacyjną zawierająca odczytany wiersz, lub False jeżeli nie ma następnego wiersza.
Kolumny blob są zwracane w postaci numerycznych identyfikatorów blob, które można użyć w funkcji
ifx_get_blob(), lub jeżeli zostały wywołane funkcje ifx_textasvarchar(1) lub ifx_byteasvarchar(1), pola blob
są zwracane w postaci ciągów znaków. W przypadku wystąpienia błędu zwraca False. Parametr $result_id musi
być prawidłowym identyfikatorem wyniku zwracanym przez ifx_query() lub ifx_prepare() (tylko dla zapytań
SELECT). Opcjonalny parametr $position wskazuje na rodzaj operacji na kursorze. Może być to NEXT, PREVIOUS,
CURRENT, FIRST, LAST lub liczba. Jeżeli podana zostanie liczba, odczytywany jest wiersz o podanym numerze. Jest
to parametr opcjonalny stosowany tylko dla kursorów typu SCROLL.
array ifx_fetch_row (int result_id [, mixed position])
Funkcja ifx_fetch_row() odczytuje jeden wiersz danych z wyniku związanego z podanym
identyfikatorem wyniku. Wiersz jest zwracany w postaci tablicy. Każda kolumna wyniku jest przechowywana w
osobnej komórce tablicy, której kluczem jest nazwa kolumny. Kolejne wywołania ifx_fetch_row() zwracają
kolejne wiersze wyniku lub False, gdy nie ma już kolejnych wierszy.
Przykład: Odczyt wierszy wyniku
$rid = ifx_prepare ("select * from emp where name like " . $name,
$connid, IFX_SCROLL);
if (! $rid) {
... error ...
}
$rowcount = ifx_affected_rows($rid);
if ($rowcount > 1000) {
printf ("Zapytanie zwraca zbyt dużo wierszy: (%d)\n<br>", $rowcount);
die ("Proszę ograniczyć zapytanie<br>\n");
}}
if (! ifx_do ($rid)) {
... error ...
}
$row = ifx_fetch_row ($rid, "NEXT");
while (is_array($row)) {
for(reset($row); $fieldname=key($row); next($row)) {
$fieldvalue = $row[$fieldname];
printf ("%s = %s,", $fieldname, $fieldvalue);
}
printf("\n<br>");
$row = ifx_fetch_row ($rid, "NEXT");
}
ifx_free_result ($rid);

ifx_fieldproperties
Zwraca tablicę asocjacyjną z nazwami pól jako kluczami i właściwościami pól SQL dla wyniku zapytania
określonego przez $result_id, jako danymi. W przypadku wystąpienia błędu zwraca False. Zwraca właściwości
SQL serwera Informix w postaci tablicy asocjacyjnej, dla każdego pola zwracanego przez zapytanie. Właściwości
te są zapisywane jako SQLTYPE:długość:dokładność:skala:ISNULLABLE, gdzie SQLTYPE jest typem Informixa,
np.: SQLVCHAR, natomiast ISNULLABLE może mieć wartość Y lub N.
array ifx_fieldproperties (int result_id)
Przykład: właściwości pól SQL serwera Informix
$properties = ifx_fieldproperties ($resultid);
if (! isset($properties)) {
... błąd ...
}
for ($i = 0; $i < count($properties); $i++) {
$fname = key ($properties);
printf ("%s:\t typ = %s\n", $fname, $properties[$fname]);
next ($properties);
}

ifx_fieldtypes
Dla wyniku zapytania o identyfikatorze $result_id zwraca tablicę asocjacyjną z nazwami pól jako
kluczami i typami pól SQL jako danymi. W przypadku wystąpienia błędu zwraca False.
array ifx_fieldtypes (int result_id)
Przykład: Nazwy i typy pól SQL
types = ifx_fieldtypes ($resultid);
253 PHP – Kompendium wiedzy
if (! isset ($types)) {
... błąd ...
}
for ($i = 0; $i < count($types); $i++) {
$fname = key($types);
printf("%s :\t typ = %s\n", $fname, $types[$fname]);
next($types);
}

ifx_free_blob
Usuwa obiekt blob o podanym identyfikatorze $bid. W przypadku wystąpienia błędu zwraca False, w
przeciwnym wypadku True.
int ifx_free_blob (int bid)

ifx_free_char
Usuwa obiekt znakowy o podanym identyfikatorze $bid. W przypadku wystąpienia błędu zwraca False, w
przeciwnym wypadku True.
int ifx_free_char (int bid)

ifx_free_result
Dla identyfikatora wyniku $result_id zwalnia zasoby przydzielone dla zapytania . W
przypadku wystąpienia błędu zwraca False.
int ifx_free_result (int bid)

ifx_getsqlca
Zwraca pseudo-wiersz (tablicę asocjacyjną) z wartościami sqlca.sqlerrd[0] ... sqlca.sqlerrd[5] po
skojarzeniu zapytania z $result_id. Parametr $result_id musi być prawidłowym identyfikatorem wyniku
zwracanym przez ifx_query() lub ifx_prepare().
array ifx_getsqlca (int result_id)
Dla zapytań INSERT, UPDATE i DELETE zwracane wartości są ustawiane przez serwer po wykonaniu
zapytania. Pozwala to odczytać ilość wierszy zmienionych przez zapytanie oraz numer kolejny wstawionego
wiersza. W przypadku wyrażeń SELECT, wartości te są ustawiane po wykonaniu operacji PREPARE. Pozwala to na
odczytanie przewidywanej ilości wierszy wyniku. Wykorzystanie tej funkcji pozwala na zmniejszenei narzutu
czasowego na wykonanie zapytania select dbinfo('sqlca.sqlerrdx') i odczytanie wartości zapisanych w
odpowiednim momencie przez sterownik bazy Informix.
Przykład: Odczytywanie wartości sqlca.sqlerrd[x]
/* zakładamy, że pierwsza kolumna 'sometable' jest numerem seryjnym rekordu */
$qid = ifx_query("insert into sometable
values (0, '2nd column', 'another column') ", $connid);
if (! $qid) {
... błąd ...
}
$sqlca = ifx_getsqlca ($qid);
$serial_value = $sqlca["sqlerrd1"];
echo "Numer seryjny wstawionego wiersza wynosi: " . $serial_value<br>\n";

ifx_get_blob
Zwraca zawartość obiektu blob o podanym identyfikatorze obiektu $bid.
int ifx_get_blob (int bid)

ifx_get_char
Zwraca zawartość obiektu znakowego o podanym identyfikatorze obiektu $bid.
int ifx_get_char (int bid)

Dodatek A - Funkcje 254
ifx_htmltbl_result
Zwraca ilość odczytanych wierszy, lub False w przypadku wystąpienia błędu. Formatuje wiersze wyniku
o identyfikatorze $result_id do postaci tabeli HTML. Drugim opcjonalnym parametrem funkcji jest ciąg
atrybutów znacznika <TABLE>.
int ifx_htmltbl_result (int result_id [, string html_table_options])

ifx_nullformat
Ustawia domyślną wartość wartości NULL po odczytaniu wiersza. Tryb 0 powoduje zwracanie "" a tryb 1
zwracanie "NULL".
void ifx_nullformat (int mode)

ifx_num_fields
Zwraca ilość kolumn zapytania o identyfikatorze $result_id lub False w przypadku wystąpienia błędu. Po
przygotowaniu lub wykonaniu zapytania wywołanie to pozwala na odczytanie ilości kolumn w zapytaniu.
int ifx_num_fields (int result_id)

ifx_num_rows
Zwraca ilość wierszy odczytanych do tej pory z wyniku zapytania $result_id, po wykonaniu ifx_query()
lub ifx_do().
int ifx_num_rows (int result_id)

ifx_pconnect
Zwraca dodatni identyfikator trwałego połączenia do serwera Informix lub False w przypadku wystąpienia
błędu. Funkcja ifx_pconnect() działa bardzo podobnie do ifx_connect() z dwoma wyjątkami. Funkcja działa
identycznie jak ifx_connect(), jeżeli PHP nie działa jako moduł Apache. Po pierwsze, w czasie połączenia
funkcja próbuje znaleźć łącze (trwałe) otwarte do tego samego serwera z identycznym użytkownikiem i hasłem.
Jeżeli zostanie znalezione takie połączenie, zamiast otwierania nowego połączenia, zwracany jest identyfikator
istniejącego połączenia. Po drugie, połączenie z serwerem SQL nie jest zamykane po zakończeniu wykonywania
skryptu. Zamiast tego łącze pozostanie otwarte do wykorzystania w przyszłości (ifx_close() nie zamyka łączy
zestawionych za pomocą ifx_pconnect()). Z tego powodu ten typ łącza nazywany jest trwałym. Patrz również:
ifx_connect().
int ifx_pconnect ([string database [, string userid [, string password]]])

ifx_query
Zwraca identyfikator wyniku lub w przypadku wystąpienia błędu wartość False. Identyfikator ten jest
używany przez inne funkcje do pobrania wyników działania zapytania. Ustawia jest wartość określająca ilość
wierszy zwracanych przez zapytanie, którą można odczytać przez wywołanie ifx_affected_rows(). Funkcja
ifx_query() wysyła zapytanie do bazy danych określanej przez podany identyfikator połączenia. Jeżeli nie
zostanie podany identyfikator połączenia, operacja jest wykonywana na ostatnio otwartym połączeniu. Jeżeli nie
istnieje otwarte połączenie, funkcja próbuje je ustanowić, identycznie jak funkcja ifx_connect() i następnie
wykorzystuje to połączenie. Wykonuje zapytanie $query na połączeniu $conn_id. W przypadku zapytań SELECT
deklarowany i otwierany jest kursor. Opcjonalny parametr $cursor_type pozwala na utworzenie kursora typu
SCROLL lub (oraz) HOLD. Jest to maska bitowa, która może przyjmować wartości IFX_SCROLL, IFX_HOLD lub
jednocześnie obie wartości. Zapytania inne niż SELECT wykonywane są w trybie natychmiastowym. IFX_SCROLL i
IFX_HOLD są stałymi symbolicznymi i nie należy ich zapisywać w apostrofach. Jeżeli nie podasz tego parametru,
otwierany jest zwykły kursor sekwencyjny. Dla dowolnego typu zapytania zapamiętywana jest ilość wierszy
będących wynikiem zapytania (rzeczywista lub szacowana), którą można odczytać za pomocą funkcji
ifx_affected_rows().
int ifx_query (string query [, int link_identifier [, int cursor_type,

255 PHP – Kompendium wiedzy
mixed [blobidarray]]])
Jeżeli w zapytaniu UPDATE występuje kolumna BLOB (BYTE lub TEXT), można dodać parametr $blobidarray,
zawierający odpowiednie identyfikatory blob, i powinieneś zamienić te kolumny znakiem ? w tekście zapytania.
Jeżeli zawartość kolumny TEXT (lub BYTE) pozwala na to, można wywołać funkcje ifx_textasvarchar(1) i
ifx_byteasvarchar(1). Pozwoli to na traktowanie kolumn TEXT (lub BYTE) w zapytaniach SELECT, tak, jakby była
to zwykła (choć długa) kolumna VARCHAR, i nie przejmować się identyfikatorami obiektów blob. Wywołując
ifx_textasvarchar(0) i ifx_byteasvarchar(0) (domyślna sytuacja) zapytania SELECT kolumny BLOB będą
zwracane jako identyfikatory obiektów blob (wartości numeryczne). Mając taki identyfikator można pobrać
zawartość kolumny BLOB za pomocą funkcji obsługujących bloby. Patrz również: ifx_connect().
Przykład: Wyświetlenie wszystkich wierszy tabeli orders jako tabeli html
ifx_textasvarchar(1); // użycie "trybu tekstowego" do blobów
$res_id = ifx_query("select * from orders", $conn_id);
if (! $res_id) {
printf("Nie można wykonać zapytania : %s\n<br>%s<br>\n", ifx_error());
ifx_errormsg();
die;
}
ifx_htmltbl_result($res_id, "border=\"1\"");
ifx_free_result($res_id);

Przykład: Wstawienie kilku wierszy do tabeli catalog
// utworzenie identyfikatorów blobów dla kolunm byte i text
$textid = ifx_create_blob(0, 0, "Kolumna Text w pamięci!");
$byteid = ifx_create_blob(1, 0, "Kolumna Byte w pamięci");
// zapamiętanie identyfikatorów blob w tablicy blobid
$blobidarray[] = $textid;
$blobidarray[] = $byteid;
// wykonanie zapytania
$query = "insert into catalog (stock_num, manu_code, " .
"cat_descr,cat_picture) values(1,'HRO',?,?)";
$res_id = ifx_query($query, $conn_id, $blobidarray);
if (! $res_id) {
... błąd ...
}
// zwonienie identyfikatora wyniku
ifx_free_result($res_id);

ifx_textasvarchar
Ustawia domyślny tryb tekstowy dla zapytań SELECT. Tryb 0 powoduje zwracanie identyfikatorów blob,
natomiast tryb 1 powoduje zwracanie zawartości jako tekstu.
void ifx_textasvarchar (int mode)

ifx_update_blob
Uaktualnia zawartość obiektu blob dla podanego identyfikatora $bid. Parametr $content jest ciągiem
zawierającym nowe dane. Zwraca False w przypadku błędu a w przeciwnym przypadku True.
ifx_update_blob (int bid, string content)

ifx_update_char
Uaktualnia zawartość obiektu znakowego dla podanego identyfikatora $bid. Parametr $content jest
ciągiem zawierającym nowe dane. Zwraca False w przypadku błędu a w przeciwnym przypadku True.
ifx_update_char (int bid, string content)

ignore_user_abort
Funkcja ta ustawia znacznik, czy klient może spowodować przerwanie wykonywania skryptu. Zwraca
wcześniejsze ustawienie i może być wywołana bez argumentów w celu sprawdzenia bieżącego ustawienia, bez
jego zmiany.
int ignore_user_abort ([int setting])

Dodatek A - Funkcje 256
ImageArc
Rysuje fragment elipsy o środku o współrzędnych $cx, $cy (lewy górny róg to 0,0) na rysunku
reprezentowanym przez $im. Parametry $w i $h określają szerokość i wysokość elipsy, natomiast punkty
początkowe i końcowe są określane w stopniach podawanych w argumentach $s i $e.
int ImageArc (int im, int cx, int cy, int w, int h, int s, int e, int col)

ImageChar
Rysuje pierwszy znak w $c na rysunku określonym przez $id. Lewy górny róg litery znajduje się na
współrzędnych $x, $y (lewy górny róg to 0,0), kolor to $col. Jeżeli $font jest 1, 2, 3, 4 lub 5, używane są
wbudowane czcionki (największa liczba reprezentuje największą czcionkę). Patrz również imageloadfont().
int ImageChar (int im, int font, int x, int y, string c, int col)

ImageCharUp
Rysuje pionowo pierwszy znak w $c na rysunku określonym przez $id. Lewy górny róg litery znajduje się
na współrzędnych $x, $y (lewy górny róg to 0,0), kolor to $col. Jeżeli $font jest 1, 2, 3, 4 lub 5, używane są
wbudowane czcionki (największa liczba reprezentuje największą czcionkę). Patrz również imageloadfont().
int ImageCharUp (int im, int font, int x, int y, string c, int col)

ImageColorAllocate
Zwraca identyfikator koloru reprezentujący kolor stworzony z podanych składników RGB. Argument $im
jest wynikiem funkcji imagecreate(). Funkcja ImageColorAllocate() musi być wywołana do stworzenia każdego
koloru, który będzie używany na rysunku $im.
int imagecolorallocate (int im, int red, int green, int blue)
Przykład
$white = ImageColorAllocate ($im, 255, 255, 255);
$black = ImageColorAllocate ($im, 0, 0, 0);

ImageColorAt
Zwraca indeks koloru piksela o podanych współrzędnych. Patrz również: ImageColorSet() i
ImageColorsForIndex().
int imagecolorat (int im, int x, int y)

ImageColorClosest
Zwraca indeks koloru, w palecie kolorów rysunku, który jest najbliższy podanej wartości RGB. Odległość
od żądanego koloru i kolorów istniejących w palecie jest obliczana tak, jakby wartości RGB reprezentowały
punkty w przestrzeni trójwymiarowej. Patrz również: ImageColorExact().
int imagecolorclosest (int im, int red, int green, int blue)

ImageColorDeAllocate
Usuwa kolor poprzednio utworzony za pomocą funkcji ImageColorAllocate().
int imagecolordeallocate (int im, int index)
Przykład:
$white = ImageColorAllocate($im, 255, 255, 255);
ImageColorDeAllocate($im, $white);

ImageColorExact
Zwraca indeks podanego koloru w palecie kolorów rysunku. Jeżeli kolor nie występuje w palecie,
zwracana jest wartość -1. Patrz również: ImageColorClosest().
int imagecolorexact (int im, int red, int green, int blue)

257 PHP – Kompendium wiedzy
ImageColorResolve
Funkcja gwarantuje zwrócenie indeksu dla podanego koloru. Będzie to dokładnie identyczny kolor lub
najbliższy mu podobny. Patrz również: ImageColorClosest().
int imagecolorresolve (int im, int red, int green, int blue)

ImageColorSet
Ustawia indeks w palecie kolorów na podany kolor. Jest to przydatne do tworzenia efektów wypełniania
za pomocą palety, bez potrzeby wykonywania wypełniania. Patrz również: ImageColorAt().
bool imagecolorset (int im, int index, int red, int green, int blue)

ImageColorsForIndex
Zwraca tablicę asocjacyjną z kluczami red, green i blue, które zawierają odpowiednie wartości dla
podanego indeksu koloru. Patrz również: ImageColorAt() i ImageColorExact().
array imagecolorsforindex (int im, int index)

ImageColorsTransparent
Ustawia kolor przezroczysty w rysunku $im na $col. Parametr $im jest identyfikatorem zwracanym przez
ImageCreate(), natomiast $col jest identyfikatorem koloru zwracanym przez ImageColorAllocate(). Zwracany
jest identyfikator nowego (lub bieżącego, jeżeli nie podano nowego koloru) koloru przezroczystego.
int imagecolortransparent (int im [, int col])

ImageCopy
Kopiuje fragment rysunku $src_im do $dst_im, rozpoczynając od współrzędnych $src_x, $src_y o
szerokości $src_w i wysokości $src_h. Zdefiniowany fragment jest kopiowany do docelowego rysunku na
współrzędne $dst_x i $dst_y.
int ImageCopy (resource dst_im, resource src_im, int dst_x, int dst_y,
int src_x, int src_y, int src_w, int src_h)

ImageCopyResized
Kopiuje prostokątny fragment rysunku do innego rysunku. Parametr $dst_im jest docelowym rysunkiem,
natomiast $src_im to identyfikator rysunku źródłowego. Jeżeli współrzędne źródła i celu oraz szerokość i
wysokość różnią się, stosowane jest odpowiednie przeskalowanie kopiowanego fragmentu. Współrzędne
wskazują na lewy górny róg. Funkcja może być używana do kopiowania obszarów tego samego rysunku (jeżeli
$dst_im jest taki sam jak $src_im), ale gdy obszary te nachodzą na siebie, wyniki są nieprzewidywalne.
int ImageCopyResized (resource dst_im, resource src_im, int dstX, int dstY,
int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)

ImageCreate
Zwraca identyfikator rysunku wskazujący na pusty rysunek o rozmiarze $x_size na $y_size.
int imagecreate (int x_size, int y_size)
Przykład: Tworzenie nowego strumienia rysunku GD i tworzenie rysunku.
<?php
header ("Content-type: image/png");
$im = @ImageCreate (50, 100)
or die ("Nie można zainicjować nowego strumienia rysunku GD");
$background_color = ImageColorAllocate ($im, 255, 255, 255);
$text_color = ImageColorAllocate ($im, 233, 14, 91);
ImageString ($im, 1, 5, 5, "Prosty tekst przykładowy", $text_color);
ImagePng ($im);
?>

Dodatek A - Funkcje 258
ImageCreateFromGif
Zwraca identyfikator rysunku reprezentujący rysunek pobrany z pliku o podanej nazwie. Funkcja
ImageCreateFromGif() w przypadku wystąpienia błędu zwraca pusty ciąg. Wyświetla również komunikat błędu,
ale niestety jest on wyświetlany w przeglądarce jako nieprawidłowe łącze. Aby ułatwić uruchamianie można
zastosować poniższy przykład, który tworzy rysunek GIF z komunikatem błędu.
int ImageCreateFromGif (string filename)
Przykład: Obsługa błędu w czasie tworzenia rysunku (podziękowania dla vic@zymsys.com)
function LoadGif ($imgname) {
$im = @ImageCreateFromGIF ($imgname); /* Próba otwarcia */
if (!$im) { /* Jeżeli się nie udało */
$im = ImageCreate (150, 30); /* Tworzenie pustego rysunku */
$bgc = ImageColorAllocate ($im, 255, 255, 255);
$tc = ImageColorAllocate ($im, 0, 0, 0);
ImageFilledRectangle ($im, 0, 0, 150, 30, $bgc);
/* Wyświetlenie komunikatu błędu */
ImageString($im, 1, 5, 5, "Błąd przy ładowaniu $imgname", $tc);
}
return $im;
}

Uwaga

Ponieważ obsługa GIF została usunięta z biblioteki GD od wersji 1.6 funkcja ta nie jest ju ż dostępna.

ImageCreateFromJPEG
Zwraca identyfikator rysunku reprezentujący rysunek pobrany z pliku o podanej nazwie. Funkcja
ImageCreateFromJPEG() w przypadku wystąpienia błędu zwraca pusty ciąg. Wyświetla również komunikat błędu,
ale niestety jest on wyświetlany w przeglądarce jako nieprawidłowe łącze. Aby ułatwić uruchamianie można
zastosować poniższy przykład, który tworzy rysunek JPEG z komunikatem błędu.
int ImageCreateFromJPEG (string filename)
Przykład: Obsługa błędu w czasie tworzenia rysunku (podziękowania dla vic@zymsys.com)
function LoadJPEG ($imgname) {
$im = @ImageCreateFromJPEG ($imgname); /* Próba otwarcia */
if (!$im) { /* Jeżeli się nie udało */
$im = ImageCreate (150, 30); /* Tworzenie pustego rysunku */
$bgc = ImageColorAllocate ($im, 255, 255, 255);
$tc = ImageColorAllocate ($im, 0, 0, 0);
ImageFilledRectangle ($im, 0, 0, 150, 30, $bgc);
/* Wyświetlenie komunikatu błędu */
ImageString($im, 1, 5, 5, "Błąd przy ładowaniu $imgname", $tc);
}
return $im;
}

ImageCreateFromPNG
Zwraca identyfikator rysunku reprezentujący rysunek pobrany z pliku o podanej nazwie. Funkcja
ImageCreateFromPNG() w przypadku wystąpienia błędu zwraca pusty ciąg. Wyświetla również komunikat błędu,
ale niestety jest on wyświetlany w przeglądarce jako nieprawidłowe łącze. Aby ułatwić uruchamianie można
zastosować poniższy przykład, który tworzy rysunek PNG z komunikatem błędu.
Przykład: Obsługa błędu w czasie tworzenia rysunku (podziękowania dla vic@zymsys.com)
function LoadPNG ($imgname) {
$im = @ImageCreateFromPNG ($imgname); /* Próba otwarcia */
if (!$im) { /* Jeżeli się nie udało */
$im = ImageCreate (150, 30); /* Tworzenie pustego rysunku */
$bgc = ImageColorAllocate ($im, 255, 255, 255);
$tc = ImageColorAllocate ($im, 0, 0, 0);
ImageFilledRectangle ($im, 0, 0, 150, 30, $bgc);
/* Wyświetlenie komunikatu błędu */
ImageString($im, 1, 5, 5, "Błąd przy ładowaniu $imgname", $tc);
}
return $im;
}

259 PHP – Kompendium wiedzy
ImageDashedLine
Rysuje na rysunku $im linię przerywaną z $x1, $y1 do $x2, $y2 (lewy górny róg to 0,0) o kolorze $col.
Patrz również: ImageLine().
int imagedashedline (int im, int x1, int y1, int x2, int y2, int col)

ImageDestroy
Zwalnia pamięć zajętą przez rysunek $im. Parametr $im jest identyfikatorem rysunku zwracanym przez
funkcję ImageCreate().
int imagedestroy (int im)

ImageFill
Wykonuje wypełnianie metodą zalewania (flood fill) rysunku $im rozpoczynając od współrzędnych $x, $y
(lewy górny róg to 0,0) kolorem $col.
int imagefill (int im, int x, int y, int col)

ImageFilledPolygon
Tworzy wypełniony wielobok na rysunku $im. Parametr $points jest tablicą PHP zawierającą wierzchołki
wieloboku, to znaczy $points[0] = x0, $points[1] = y0, $points[2] = x1, $points[3] = y1 i tak dalej. Parametr
$num_points zawiera całkowitą ilość wierzchołków.
int imagefilledpolygon (int im, array points, int num_points, int col)

ImageFilledRectangle
Na rysunku $im tworzy wypełniony prostokąt o kolorze $col, rozpoczynając od górnego lewego rogu o
współrzędnych $x1, $y1, kończąc na prawym dolnym rogu o współrzędnych $x2, $y2. Lewy górny róg rysunku ma
współrzędne 0,0.
int imagefilledrectangle (int im, int x1, int y1, int x2, int y2, int col)

ImageFillToBorder
Wypełnia na rysunku obszar ograniczony kolorem zdefiniowanym w parametrze $border. Punkt
rozpoczęcia wypełniania to $x, $y (lewy górny róg to 0,0), kolor wypełnienia to $col.
int imagefilltoborder (int im, int x, int y, int border, int col)

ImageFontHeight
Zwraca wysokość znaku w pikselach dla określonej czcionki. Patrz również: ImageFontWidth() i
ImageLoadFont().
int imagefontheight (int font)

ImageFontWidth
Zwraca szerokość znaku w pikselach dla określonej czcionki. Patrz również: ImageFontHeiht() i
ImageLoadFont().
int ImageFontWidth (int font)

ImageGammaCorrect
Stosuje korekcję gamma na rysunku $im na podstawie wartości gamma wejściowej $inputgamma i
wyjściowej $outputgamma.
int imagegammacorrect (int im, float inputgamma, float outputgamma)

Dodatek A - Funkcje 260
ImageGIF
Tworzy plik GIF na podstawie rysunku $im. Parametr $im jest identyfikatorem zwracanym przez funkcję
ImageCreate(). Rysunek zostanie zapisany w formacie GIF87a chyba, że rysunek będzie zawierał kolor
przezroczysty stworzony za pomocą funkcji ImageColorTransparent(). W takim przypadku formatem pliku
będzie GIF89a. Nazwa pliku jest opcjonalna, jeżeli zostanie pominięta, utworzony zostanie bezpośredni surowy
strumień rysunku. Wysyłając za pomocą funkcji header() typ zawartości image/gif, można stworzyć skrypt PHP,
który bezpośrednio wysyła do przeglądarki rysunki GIF.
Uwaga

Ponieważ obsługa GIF została usunięta z biblioteki GD od wersji 1.6 funkcja ta nie jest ju ż dostępna.

int imagegif (int im [, string filename])

ImageInterlace
Ustawia i kasuje bit przeplotu. Jeżeli $interlace jest równy 1, rysunek będzie z przeplotem. Jeżeli
$interlace jest 0, przeplot nie zostanie zastosowany. Funkcja zwraca bieżącą wartość bitu przeplotu dla rysunku.
int imageinterlace (int im [, int interlace])

ImageJPEG
Tworzy plik JPEG na podstawie rysunku $im. Parametr $im jest identyfikatorem zwracanym przez funkcję
ImageCreate(). Nazwa pliku jest opcjonalna, jeżeli zostanie pominięta, utworzony zostanie bezpośredni surowy
strumień rysunku. Aby opuścić nazwę pliku i jednocześnie podać wartość parametru $quality należy użyć
pustego ciągu (""). Wysyłając za pomocą funkcji header() typ zawartości image/jpeg, można stworzyć skrypt
PHP, który bezpośrednio wysyła do przeglądarki rysunki JPEG.
Uwaga

Obsługa formatu JPEG jest dostępna jedynie, jeżeli biblioteka GD jest w wersji 1.8 lub nowszej.

int imagejpeg (int im [, string filename [, int quality]])

ImageLine
Na rysunku $im rysuje linię od $x1, $y1 do $x2, $y2 (lewy górny róg to 0,0) o kolorze $col. Patrz również:
ImageCreate() i ImageColorAllocate().
int imageline (int im, int x1, int y1, int x2, int y2, int col)

ImageLoadFont
Ładuje czcionkę bitmapową zdefiniowaną przez użytkownika i zwraca identyfikator czcionki (zawsze
większy od 5, więc nie koliduje z wbudowanymi czcionkami). Format pliku jest obecnie binarny, zależny od
architektury. Oznacza to, że należy generować pliki czcionek na komputerze z takim samym procesorem, co
komputer na którym jest uruchomione PHP. Format pliku jest następujący:
Pozycja Typ Opis
w bajtach danych C
0 — 3 int Ilość znaków w pliku czcionek
4 —7 int Wartość pierwszego znaku czcionki (często 32 dla spacji)
8 — 11 int Szerokość każdego znaku w pikselach
12 — 15 int Wysokość każdego znaku w pikselach
16 — char Tablica danych znakowych, jeden bajt na piksel w każdym
xxx
znaku, razem (znaki*szerokość*wyskokość) bajtów
Patrz również: ImageFontWidth() i ImageFontHeight().
261 PHP – Kompendium wiedzy
int imageloadfont (string file)

ImagePNG
Otwiera strumień GD ($im) w formacie PNG i przesyła dane do standardowego wyjścia (zwykle jest to
przeglądarka), lub jeżeli podana jest nazwa pliku $filename, zapisuje rysunek do pliku.
int imagepng (int im [, string filename])
Przykład:
<?php
$im = ImageCreateFromPng("test.png");
ImagePng($im);
?>

ImagePolygon
Tworzy wielobok na rysunku $im. Parametr $points jest tablicą PHP zawierającą wierzchołki wieloboku
to znaczy $points[0] = x0, $points[1] = y0, $points[2] = x1, $points[3] = y1 i tak dalej. Parametr
$num_points zawiera całkowitą ilość wierzchołków. Patrz również: ImageCreate().
int imagepolygon (int im, array points, int num_points, int col)

ImagePSBBox
Parametr $size jest wyrażony w pikselach, $space pozwala zmienić domyślna wartość odstępu w
czcionkach. Wartość ta jest dodawana do standardowej wartości odstępu i może być ujemna. Parametr $tightness
pozwala kontrolować ilość światła pomiędzy literami. Wartość ta jest dodawana do normalnej szerokości znaku i
może również być ujemna. Parametr $angle jest wyrażony w stopniach. Parametry $space i $tightness są
podawane w jednostkach odstępu znaku, gdzie 1 jednostka odstępu jest 1/1000 pica do kwadratu. Parametry
$space, $tightness i $angle są opcjonalne. Ramka ograniczająca jest wyliczana z wykorzystaniem dostępnych
danych z metryki czcionki i niestety nieco różni się od wyników otrzymywanych przez rasteryzację tekstu. Jeżeli
kąt wynosi 0, należy się spodziewać, że tekst będzie potrzebował o 1 piksel więcej w każdym kierunku. Funkcja
zwraca tablicę zawierającą następujące elementy: 0 — lewa dolna współrzędna x, 1 — lewa dolna współrzędna y,
2 — prawa górna współrzędna x i 3 — prawa górna współrzędna y. Patrz również: ImagePSText().
array ImagePSBBox (string text, int font, int size [, int space [, int tightness
[, float angle]]])

ImagePSEncodeFont
Ładuje z pliku wektor kodowania znaków i zmienia na niego istniejący wektor kodowania czcionki.
Ponieważ w czcionkach PostScript wektor kodowania nie zawiera wielu znaków na pozycjach powyżej 127,
prawie na pewno musisz zmienić wektor kodowania w przypadku wykorzystywania języków innych niż
angielski. Dokładny format pliku jest opisany w dokumentacji T1lib. T1lib zawiera dwa gotowe do użycia pliki,
IsoLatin1.enc i IsoLatin2.enc. Jeżeli chcesz cały czas wykorzystywać tą funkcję, lepszym sposobem na
zdefiniowanie kodowania jest ustawienie w pliku konfiguracyjnym zmiennej ps.default_encoding na odpowiedni
plik kodowania. Wszystkie załadowane przez użytkownika czcionki będą miały odpowiednio zdefiniowane
kodowanie.
int imagepsencodefont (int font_index, string encodingfile)

ImagePsExtendfont
Powiększa lub zmniejsza czcionkę $font_index. Jeżeli wartość parametru $extend jest mniejsza od jeden,
funkcja zmniejsza czcionkę.
bool imagepsextendfont (int font_index, float extend)

ImagePSFreeFont
Patrz również: ImagePSLoadFont().
void imagepsfreefont (int fontindex)

Dodatek A - Funkcje 262
ImagePSLoadFont
Jeżeli wszystko odbędzie się bez błędów, funkcja zwraca prawidłowy indeks czcionki, który może być
użyty do innych celów. Jeżeli coś się nie powiedzie, funkcja zwraca False i drukuje komunikat opisujący błąd.
Patrz również: ImagePSFreeFont().
int ImagePSLoadFont (string filename)

ImagePsSlantFont
Pochyla czcionkę wskazywaną przez parametr $font_index o wartość przekazaną w parametrze $slant.
bool imagepsslantfont (int font_index, float slant)

ImagePSText
Parametr $size jest wyrażony w pikselach. Parametr $foreground jest kolorem jakim zostanie
namalowany tekst, natomiast $background to kolor na który tekst będzie się zmieniał przy zastosowaniu
wygładzania (antialiasing). Nie są rysowane żadne punkty w kolorze $background, więc tło nie będzie
zamalowane. Współrzędne przekazane w $x, $y definiują początek (punkt odniesienia) pierwszego znaku (mniej
więcej lewy dolny róg znaku). Funkcja różni się tym od ImageString(), gdzie $x, $y określają lewy górny róg
pierwszego znaku. Jeżeli potrzebujesz bliższych informacji na temat czcionek i systemu miar, znajdują się w
dokumentacji PostScript.
array imagepstext (int image, string text, int font, int size, int foreground,
int background, int x, int y [, int space [, int tightness
[, float angle [, int antialias_steps]]]])
Parametr $space pozwala na zmianę domyślnego odstępu w czcionce. Wartość ta jest dodawana do
normalnej wartości, więc może być ujemna. Parametr $tightness powala na kontrolowanie ilości światła
pomiędzy literami. Wartość ta jest dodawana do normalnej szerokości znaki i również może być ujemna. Wartość
$angle podawana jest w stopniach. Parametr $antialiasing_steps pozwala na określanie ilości kolorów użytych
do wygładzania tekstu. Dopuszczalnymi wartościami są 4 i 16. Wyższa wartość jest polecana dla tekstów o
rozmiarze mniejszych od 20, gdzie wygładzanie mocno wpływa na jakość tekstu. W przypadku większych
czcionek można użyć wartości 4, ponieważ potrzeba wtedy mniej obliczeń. Parametry $space i $tightness są
wyrażane w jednostkach odstępu znaku, gdzie 1 jednostka odstępu jest 1/1000 pica do kwadratu. Parametry
$space, $tightness, $angle i $antialias są opcjonalne. Funkcja zwraca tablicę zawierającą następujące elementy:
0 — lewa dolna współrzędna x, 1 — lewa dolna współrzędna y, 2 — prawa górna współrzędna x i 3 — prawa
górna współrzędna y. Patrz również: ImagePSBBox().

ImageRectangle
Na rysunku $im tworzy prostokąt w kolorze $col o współrzędnych lewego górnego rogu $x1, $y1 i
prawego dolnego $x2, $y2. Lewy górny róg rysunku ma współrzędne 0,0.
int ImageRectangle (int im, int x1, int y1, int x2, int y2, int col)

ImageSetPixel
Rysuje na rysunku $im piksel w kolorze $col na współrzędnych $x, $y (lewy górny róg to 0,0). Patrz
również: ImageCreate() i ImageColorAllocate().
int ImageSetPixel (int im, int x, int y, int col)

ImageString
Na rysunku $im rysuje ciąg $s rozpoczynając od współrzędnych $x, $y (lewy górny róg to 0,0) w kolorze
$col. Jeżeli $font wynosi 1, 2, 3, 4 lub 5, używane są wbudowane czcionki. Patrz również: ImageLoadFont().
int ImageString (int im, int font, int x, int y, string s, int col)

263 PHP – Kompendium wiedzy
ImageStringUp
Na rysunku $im rysuje pionowo ciąg $s rozpoczynając od współrzędnych $x, $y (lewy górny róg to 0,0) w
kolorze $col. Jeżeli $font wynosi 1, 2, 3, 4 lub 5, używane są wbudowane czcionki. Patrz również:
ImageLoadFont().
int ImageStringUp (int im, int font, int x, int y, string s, int col)

ImageSX
Zwraca szerokość rysunku identyfikowanego przez $im. Patrz również: ImageCreate() i ImageSY().
int ImageSX (int im)

ImageSY
Zwraca wysokość rysunku identyfikowanego przez $im. Patrz również: ImageCreate() i ImageSX().
int ImageSY (int im)

ImageTTFBBox
Funkcja oblicza i zwraca ramkę otaczającą tekst TrueType (w pikselach). Parametr $text jest ciągiem do
zmierzenia. Parametr $size jest wielkością czcionki, $fontfile jest nazwą pliku z czcionką TrueType (może być
w postaci URL). $angle jest kątem pochylenia tekstu $text w stopniach. Funkcja ImageTTFBBox() zwraca tablicę
składająca się z ośmiu elementów reprezentujących cztery punkty tworzące ramkę otaczającą tekst: 0 —
współrzędna X lewego dolnego rogu, 1 — współrzędna Y lewego dolnego rogu, 2 — współrzędna X prawego
dolnego rogu, 3 — współrzędna Y prawego dolnego rogu, 4 — współrzędna X prawego górnego rogu, 5 —
współrzędna Y prawego górnego rogu, 6 — współrzędna X lewego górnego rogu, 7 — współrzędna Y lewego
górnego rogu. Punkty te są niezależne od pochylenia tekstu, więc „lewy górny” oznacza górny wierzchołek po
lewej stronie, patrząc na tekst poziomo. Funkcja wymaga bibliotek GD i FreeType. Patrz również:
ImageTTFText().
array imagettfbbox (int size, int angle, string fontfile, string text)

ImageTTFText
Na rysunku $im rysuje ciąg $text, rozpoczynając od współrzędnych $x, $y (lewy górny róg to 0,0), pod
kątem $angle w kolorze $col, za pomocą czcionki TrueType umieszczonej w pliku $fontfile. Współrzędne
podane w $x, $y określają punkt bazowy pierwszego znaku (mniej więcej lewy dolny róg znaku). Różni się to od
funkcji ImageString(), gdzie x, y określają prawy górny róg pierwszego znaku. Parametr $angle jest wyrażony w
stopniach, gdzie 0 stopni określa tekst czytany z lewej do prawej (kierunek na godzinę trzecią), natomiast wyższe
wartości reprezentują pochylenie w kierunku przeciwnym do ruchu wskazówek zegara. (wartość 90 powoduje
narysowanie tekstu od góry do dołu). Parametr $fontflile jest ścieżką do pliku TrueType z używaną czcionką.
Parametr $text jest ciągiem tekstowym, który może zawierać sekwencje znaków UTF-8 (w postaci :{) używane
do stosowania znaków o kodach powyżej 255. $col jest indeksem koloru. Użycie ujemnego indeksu koloru
powoduje wyłączenie wygładzania tekstu. Funkcja ImageTTFText() zwraca tablicę ośmioelementową
reprezentującą cztery punkty stanowiące ramkę ograniczającą tekst. Punkty te są umieszczone w tablicy w
kolejności lewy górny, prawy górny, prawy dolny i lewy dolny. Punkty te są niezależne od pochylenia tekstu,
więc „lewy górny” oznacza górny wierzchołek po lewej stronie, patrząc na tekst poziomo.
array imagettftext (int im, int size, int angle, int x, int y, int col,
string fontfile, string text)
Przykład: ImageTTFText
<?php
Header ("Content-type: image/gif");
$im = imagecreate (400, 30);
$black = ImageColorAllocate ($im, 0, 0, 0);
$white = ImageColorAllocate ($im, 255, 255, 255);
ImageTTFText ($im, 20, 0, 10, 20, $white, "/path/arial.ttf",
"Testowanie... Omega: &#937;");
ImageGif ($im);
ImageDestroy ($im);

Dodatek A - Funkcje 264
?>
Funkcja wymaga bibliotek GD i FreeType. Patrz również: ImageTTFBox().

ImageTypes
Funkcja zwraca maskę bitową związaną z formatami rysunków obsługiwanych przez bibliotekę GD
dołączoną do PHP. Zwracane są następujące bity: IMG_GIF | IMG_JPG | IMG_PNG | IMG_WBMP.
int imagetypes (void)
Przykład: ImageTypes
<?php
if (ImageTypes() & IMG_PNG) {
echo "Obsługa PNG jest aktywna";
}
?>

imap_8bit
Konwertuje ciąg 8-bitowy na ciąg quoted-printable (zgodnie z RFC2045, sekcja 6.7). Zwraca ciąg quoted-
printable. Patrz również imap_qprint().
string imap_8bit (string string)

imap_alerts
Zwraca tablicę wszystkich komunikatów ostrzeżeń IMAP wygenerowanych od ostatniego wywołania
imap_alerts(), lub od początku strony. Gdy zostanie wywołana funkcja imap_alerts(), stos ostrzeżeń jest
czyszczony. Specyfikacja IMAP wymaga, aby komunikaty te były pokazywane użytkownikowi.
array imap_alerts (void)

imap_append
Zwraca True w przypadku powodzenia i False w przypadku błędu. Funkcja dołącza ciąg komunikatu do
skrzynki pocztowej $mbox. Jeżeli podane zostały opcjonalne znaczniki $flags, funkcja dołącza również do
skrzynki te znaczniki. Działając na serwerze Cyrus IMAP, trzeba użyć terminatorów linii „\r\n” zamiast „\n”, lub
operacja się nie powiedzie.
int imap_append (int imap_stream, string mbox, string message [, string flags])
Przykład: imap_append()
$stream = imap_open("{your.imap.host}INBOX.Drafts","username", "password");

$check = imap_check($stream);
print "Ilość komunikatów przed dołączeniem: ". $check->Nmsgs."\n";

imap_append($stream,"{your.imap.host}INBOX.Drafts"
,"From: me@my.host\r\n"
."To: you@your.host\r\n"
."Subject: test\r\n"
."\r\n"
."przesyłka testowa, proszę zignorować\r\n"
);

$check = imap_check($stream);
print "Ilość komunikatów po dołączeniu : ". $check->Nmsgs."\n";

imap_close($stream);

imap_base64
Dekoduje tekst zakodowany metodą BASE-64 (patrz RFC2045, sekcja 6.8). Zdekodowany komunikat jest
zwracany w postaci ciągu. Patrz również: imap_binary().
string imap_base64 (string text)

265 PHP – Kompendium wiedzy
imap_binary
Konwertuje 8-bitowy ciąg na ciąg zakodowany metodą BASE-64 (zgodnie z RFC2045, sekcja 6.8).
Zwraca ciąg base64. Patrz również: imap_base64().
string imap_binary (string string)

imap_body
Zwraca treść przesyłki o numerze $msg_number z bieżącej skrzynki pocztowej. Opcjonalny parametr
$flags jest maską bitową zawierającą jedną lub więcej wartości:
• FT_UID — Wartość $msgno jest idnetyfikatorem UID
• FT_PEEK — Nie ustawia znacznika \Seen, jeżeli jest już ustawiony
• FT_INTERNAL — Zwracany ciąg jest w formacie wewnętrznym
Funkcja imap_body() zwraca dosłowną kopię treści przesyłki. Aby odczytać jeden fragment
wieloczęściowej przesyłki kodowanej za pomocą MIME należy użyć funkcji imap_fetch_structure() do
zanalizowania struktury i imap_fetch_body() do skopiowania pojedynczego komponentu przesyłki.
string imap_body (int imap_stream, int msg_number [, int flags])

imap_check
Zwraca dane na temat bieżącej skrzynki pocztowej. W przypadku błędu zwraca False. Funkcja
imap_check() sprawdza status bieżącej skrzynki na serwerze i zwraca dane w postaci obiektu o następujących
właściwościach:
• Date — ostatnia zmiana zawartości skrzynki
• Driver — protokół używany do komunikacji ze skrzynką: POP3, IMAP, NNTP
• Mailbox — nazwa skrzynki pocztowej
• Nmsgs — ilość przesyłek w skrzynce
• Recent — ilość nowych przesyłek w skrzynce
object imap_check (int imap_stream)

imap_clearflag_full
Funkcja powoduje usunięcie określonego znacznika ze zbioru znaczników przesyłki w określonej
sekwencji. Znacznikami do usuwania są: \\Seen, \\Answered, \\Flagged, \\Deleted, \\Draft i \\Recent (według
RFC2060). Parametr $options jest maską bitową składającą się z następujących znaczników: ST_UID, sekwencja
argumentów zawierająca UID zamiast numerów sekwencji.
string imap_clearflag_full (int stream, string sequence, string flag, string options)

imap_close
Zamyka strumień imap. Posiada opcjonalny parametr $flag CL_EXPUNGE, który powoduje
wyczyszczenie skrzynki przed zamknięciem poprzez usunięcie przesyłek zaznaczonych jako usunięte.
int imap_close (int imap_stream [, int flags])

imap_createmailbox
Tworzy nową skrzynkę pocztową o nazwie $mbox. Nazwy zawierające znaki narodowe powinny być
zakodowane przy pomocy funkcji imap_utf7_encode(). Zwraca True w przypadku powodzenia lub False w
przypadku wystąpienia błędu. Patrz również: imap_renamemailbox(), imap_deletemailbox() i imap_open() gdzie
znajduje się opis formatów nazw $mbox.
int imap_createmailbox (int imap_stream, string mbox)
Przykład: imap_createmailbox()
$mbox = imap_open("{your.imap.host}","username","password",OP_HALFOPEN)
or die("nie można połączyć: ".imap_last_error());

$name1 = "phpnewbox";

Dodatek A - Funkcje 266
$name2 = imap_utf7_encode("phpnewböx");

$newname = $name1;

echo "Nową nazwą będzie '$name1'<br>\n";

# tworzymy nową skrzynkę o nazwie "phptestbox" w skrzynce poczty przychozącej
# sprawdzamy status po utworzeniu i na koniec usuwamy, przywracając skrzynkę
# do stanu początkowego
if(@imap_createmailbox($mbox,imap_utf7_encode("{your.imap.host}INBOX.$newname")))
{
$status = @imap_status($mbox,"{your.imap.host}INBOX.$newname",SA_ALL);
if($status) {
print("nowa skrzynka '$name1' ma następujący status:<br>\n");
print("Komunikatów: ". $status->messages )."<br>\n";
print("Ostatnich: ". $status->recent )."<br>\n";
print("Nieprzeczytanych:". $status->unseen )."<br>\n";
print("Następny UID: ". $status->uidnext )."<br>\n";
print("Poprawność UID: ". $status->uidvalidity)."<br>\n";

if(imap_renamemailbox($mbox,"{your.imap.host}INBOX.$newname",
"{your.imap.host}INBOX.$name2")) {
echo "zmieniono nazwę srzyni z '$name1' na '$name2'<br>\n";
$newname=$name2;
} else {
print "Nieudane wywołanie imap_renamemailbox na nowej skrzynce: "
.imap_last_error()."<br>\n";
}
} else {
print "Nieudane wywołanie imap_status na nowej skrzynce: "
.imap_last_error()."<br>\n";
}
if(@imap_deletemailbox($mbox,"{your.imap.host}INBOX.$newname")) {
print "nowa skrzynka usunięta, przywracając stan początkowy<br>\n";
} else {
print "Nieudane wywołanie imap_deletemailbox na nowej skrzynce: "
.implode("<br>\n",imap_errors())."<br>\n";
}

} else {
print "nie można utworzyć nowej skrzyki: ".implode("<br>\n",imap_errors())
."<br>\n";
}

imap_close($mbox);

imap_delete
Zwraca True. Funkcja imap_delete() oznacza do usunięcia przesyłkę wskazywaną przez $msg_number.
Opcjonalny parametr $flags posiada tylko jedną opcję, FT_UID, która wskazuje funkcji, że argumenty
$msg_number należy traktować jako identyfikatory UID. Przesyłki zaznaczone do usunięcia pozostają w skrzynce
do czasu wywołania funkcji imap_expunge() lub imap_close() z ustawionym parametrem opcjonalnym
CL_EXPUNGE.
int imap_delete (int imap_stream, int msg_number [, int flags])
Przykład: imap_delete()
$mbox = imap_open ("{your.imap.host}INBOX", "username", "password")
or die ("nie można połączyć: " . imap_last_error());

$check = imap_mailboxmsginfo ($mbox);
print "Przesyłki przed usunięciem: " . $check->Nmsgs . "<br>\n" ;
imap_delete ($mbox, 1);
$check = imap_mailboxmsginfo ($mbox);
print "Przesyłki po usunięciu: " . $check->Nmsgs . "<br>\n" ;
imap_expunge ($mbox);
$check = imap_mailboxmsginfo ($mbox);
print "Przesyłki po wyczyszczeni: " . $check->Nmsgs . "<br>\n" ;
imap_close ($mbox);

imap_deletemailbox
suwa podaną skrzynkę pocztową (format nazw $mbox można znaleźć przy opisie imap_open()). Zwraca
True w przypadku powodzenia i False w przypadku błędu. Patrz również: imap_createmailbox(),
imap_renamemailbox() i imap_open().

267 PHP – Kompendium wiedzy
int imap_deletemailbox (int imap_stream, string mbox)

imap_errors
Zwraca tablicę wszystkich komunikatów błędów IMAP wygenerowanych od ostatniego wywołania
imap_errors() lub od początku skryptu. Po wywołaniu imap_errors() stos błędów jest czyszczony.
array imap_errors (void)

imap_expunge
Usuwa przesyłki zaznaczone jako usunięte przez imap_delete(), imap_mail_move() lub
imap_setflag_full(). Zwraca True.
int imap_expunge (int imap_stream)

imap_fetchbody
Funkcja powoduje pobranie sekcji z treści podanego komunikatu w postaci tekstu i zwrócenie tego tekstu.
Specyfikacja sekcji jest ciągiem liczb rozdzielonych kropkami, które są indeksami w liście części, w sposób
określony przez specyfikację IMAP4. Części z treścią nie są dekodowane przez tą funkcję. Parametrem
imap_fetchbody() jest maska bitowa z jedną lub więcej stałych:
• FT_UID — Parametr $msg_number jest UID
• FT_PEEK — Nie ustawia znacznika \Seen, jeżeli nie był wcześniej ustawiony
• FT_INTERNAL — Zwracany ciąg jest w postaci wewnętrznej bez konwersji końców linii.
string imap_fetchbody (int imap_stream, int msg_number, string part_number
[, flags flags])

imap_fetchheader
Powoduje odczytanie całego, nieprzefiltrowanego RFC822 nagłówka formatu podanego komunikatu i
zwrócenie go ciągu znaków. Opcje są następujące:
• FT_UID — Parametr $msg_number jest UID
• FT_INTERNAL — Zwracany ciąg jest w postaci wewnętrznej bez konwersji końców linii.
• FT_PREFETCH — TEXT.RFC822 powinien być w tym samym czasie wstępnie pobrany. Pozwala to
uniknąć dodatkowego RTT na połączeniu IMAP, jeżeli wymagany jest pełny tekst przesyłki (na przykład,
operacja „zapis do pliku”)
string imap_fetchheader (int imap_stream, int msgno, int flags)

imap_fetchstructure
Funkcja pobiera wszystkie informacje o strukturze podanej przesyłki. Opcjonalny parametr $flags posiada
tylko jedną opcję, FT_UID, która wskazuje funkcji, że argumenty $msg_number należy traktować jako identyfikatory
UID. Zwracany obiekt zawiera kopertę, datę wewnętrzną, rozmiar, znaczniki i strukturę treści, oraz podobne
obiekty dla każdego załącznika MIME.
object imap_fetchstructure (int imap_stream, int msg_number [, int flags])
Tabela 1. Zwracany obiekt z imap_fetchstructure()
Typ Typ treści
encoding Kodowanie do przesłania treści
ifsubtype TRUE jeżeli występuje ciąg podtypu
subtype Podtyp MIME
ifdescription TRUE jeżeli jest to ciąg opisu
description Ciąg opisu treści
ifid TRUE jeżeli jest to ciąg identyfikacyjny
id Ciąg identyfikacyjny
lines Ilość linii

Dodatek A - Funkcje 268
bytes Ilość bajtów
ifdisposition TRUE jeżeli jest to ciąg rozmieszczenia
disposition Ciąg rozmieszczenia
ifdparameters TRUE jeżeli istnieje tablica dparameters
dparameters Tablica parametrów rozmieszczenia
ifparameters TRUE jeżeli istnieje tablica parametrów
parameters Tablica parametrów MIME
parts Tablica obiektów opisujących każdą część przesyłki
Uwaga

Dparameters jest tablica obiektów posiadających atrybuty $attribute i $value. Parameters jest tablicą obiektów posiadających
atrybuty $attribute i $value. Parts jest tablicą obietów o identycznej strukturze jak nadrzędny obiekt, z ograniczeniem, że nie
mogą posiadać następnych obiektów parts.

Podstawowe typy treści
0:text, 1: multipart, 2:message, 3:application, 4:audio, 5:image, 6-video, 7-other
Rodzaje kodowania
0:7BIT, 1:8BIT, 2:BINARY, 3:BASE64, 4:QUOTED-PRINTABLE, 5:OTHER

imap_fetch_overview
Pobiera nagłówki dla podanej sekwencji $sequence i zwraca skrót ich zawartości. Parametr $sequence
może zawierać sekwencję indeksów wiadomości lub identyfikatorów UID, gdy parametr $flag zawiera FT_UID.
Funkcja zwraca tablicę obiektów opisujących następujące nagłówki kolejnych wiadomości:
• subject — temat wiadomości
• from — nadawca wiadomości
• date — data wysłania
• message_id — identyfikator wiadomości
• references — odwołania do tego identyfikatora wiadomości
• size — rozmiar w bajtach
• uid — identyfikator UID wiadomości w skrzynce
• msgno — numer kolejny komunikatu w skrzynce
• recent — wiadomość oznaczona jako niedawna
• flagged — wiadomość zaznaczona
• answered — wiadomość na którą została udzielona odpowiedź
• deleted — wiadomość zaznaczona do usunięcia
• seen — wiadomość przeczytana
• draft — wiadomość oznaczona jako szkic
array imap_fetch_overview (int imap_stream, string sequence [, int flags])
Przykład: imap_fetch_overview()
$mbox = imap_open("{your.imap.host:143}","username","password")
or die("błąd połaczenia: ".imap_last_error());

$overview = imap_fetch_overview($mbox,"2,4:6",0);

if(is_array($overview)) {
reset($overview);
while( list($key,$val) = each($overview)) {
print $val->msgno
. " - " . $val->date
. " - " . $val->subject
. "\n";
}
}
imap_close($mbox);

269 PHP – Kompendium wiedzy
imap_getmailboxes
Zwraca tablicę obiektów zawierających dane na temat skrzynek pocztowych. Każdy obiekt posiada
następujące atrybuty: $name — zawiera pełną nazwę skrzynki, $delimiter — znak podziału w tej części
hierarchii, w której znajduje się skrzynka, $attributes — maska bitowa, która może być testowana za pomocą
następujących stałych:
• LATT_NOINFERIORS — skrzynka nie posiada „dzieci” (skrzynek w niej założonych)
• LATT_NOSELECT — jest to kontener a nie skrzynka i nie może być otwierany
• LATT_MARKED — skrzynka jest zaznaczona i używana jedynie przez UW-IMAPD
• LATT_UNMARKED — skrzynka nie jest zaznaczona i używana jedynie przez UW-IMAPD
array imap_getmailboxes (int imap_stream, string ref, string pattern)
Nazwy skrzynek zawierające znaki narodowe z poza drukowalnego zakresu kodów ASCII są zakodowane
i mogą być rozkodowane za pomocą funkcji imap_utf7_decode(). Normalnie $ref powinien być określany przez
specyfikację serwera, tak jak jest to opisane przy funkcji imap_open(), natomiast $pattern określa początek
przeszukiwania w hierarchii skrzynek pocztowych. Jeżeli chcesz uzyskać wszystkie skrzynki, należy przekazać
do parametru $pattern ciąg "*". Mogą być tu używane dwa znaki specjalne: * i %. Jeżeli użyjemy znaku * w
wyniku otrzymamy wszystkie skrzynki w hierarchii. Znak % powoduje otrzymanie jedynie skrzynek z bieżącego
poziomu hierarchii. Jeżeli podamy jedynie "%", otrzymamy skrzynki z głównego poziomu hierarchii, "~/mail/%"
na UW-IMAPD zwróci wszystkie skrzynki z katalogu ~/mail, ale bez podkatalogów.
Przykład: imap_getmailboxes()
$mbox = imap_open("{your.imap.host}","username","password",OP_HALFOPEN)
or die("błąd połączenia: ".imap_last_error());

$list = imap_getmailboxes($mbox,"{your.imap.host}","*");
if(is_array($list)) {
reset($list);
while (list($key, $val) = each($list))
{
print "($key) ";
print imap_utf7_decode($val->name).",";
print "'".$val->delimiter."',";
print $val->attributes."<br>\n";
}
} else
print "nieudane wywołanie funkcji imap_getmailboxes: ".imap_last_error()."\n";
imap_close($mbox);

imap_getsubscribed
Identyczna z imap_getmailboxes(), ale zwraca jedynie skrzynki, do których użytkownik posiada
subskrypcję.
array imap_getsubscribed (int imap_stream, string ref, string pattern)

imap_header
Alias funkcji imap_headerinfo(), działa dokładnie tak samo.
imap_header( void )

imap_headerinfo
Zwraca obiekt do różnych fragmentów nagłówka: remail, date, Date, subject, Subject, in_reply_to,
message_id, newsgroups, followup_to, references.
imap_header( void )
Dostępne są następujące znaczniki wiadomości:
• Recent — 'R' jeżeli wiadomość jest przeczytana i niedawna, 'N' jeżeli jest niedawna i nieprzeczytana, ' '
jeżeli nie jest niedawna
Unseen — 'U' jeżeli nie jest przeczytana i nie jest niedawna, ' ' jeżeli przeczytana lub niedawna
Answered — 'A' jeżeli udzielono odpowiedzi, ' ' jeżeli nie udzielono odpowiedzi
Deleted — 'D' gdy usunięta, ' ' jeżeli nie usunięta

Dodatek A - Funkcje 270
Draft — 'X' jeżeli jest szkicem, ' ' gdy nim nie jest
Flagged — 'F' jeżeli jest zaznaczona, ' ' jeżeli jest oznaczona

Uwaga

Działanie znaczników Recent i Unseen jest nieco dziwne. Jeżeli chcesz sprawdzić, czy wiadomość nie jest przeczytana, sprawdź
Unseen == 'U' || Recent == 'N'.

• toaddress (kompletna linia to:, do 1024 znaków)
• to[] (zwraca tablicę obiektów z linii to: zawierająca: personal, adl, mailbox, host)
• fromaddress (kompletna linia from:, do 1024 znaków)
• from[] (zwraca tablicę obiektów z linii from: zawierająca: personal, adl, mailbox, host)
• ccaddress (kompletna linia cc:, do 1024 znaków)
• cc[] (zwraca tablicę obiektów z linii cc: zawierająca: personal, adl, mailbox, host)
• bccaddress (kompletna linia bcc:, do 1024 znaków)
• bcc[] (zwraca tablicę obiektów z linii bcc: zawierająca: personal, adl, mailbox, host)
• reply_toaddress (kompletna linia reply_to:, do 1024 znaków)
• reply_to[] (zwraca tablicę obiektów z linii reply_to: zawierająca: personal, adl, mailbox, host)
• senderaddress (kompletna linia sender:, do 1024 znaków)
• sender[] (zwraca tablicę obiektów z linii sender: zawierająca: personal, adl, mailbox, host)
• return_path (kompletna linia return_path:, do 1024 znaków)
• return_path[] (zwraca tablicę obiektów z linii return_path: zawierająca: personal, adl, mailbox, host)
• udate (mail, message, date, in, unix, time)
• fetchfrom (linia from: sformatowana tak, aby zmieściła się w $fromlength znakach)
• fetchsubject (linia subject: sformatowana tak, aby zmieściła się w $fromlength znakach)

imap_headers
Zwraca tablicę ciągów sformatowanych z danymi nagłówków. Pozwala na jeden element na przesyłkę.
array imap_headers (int imap_stream)

imap_last_error
Zwraca pełny tekst ostatniego komunikatu błędu IMAP, który wystąpił na bieżącej stronie. Stos błędów
pozostaje niezmieniony. Kolejne wywołania imap_last_error() zwrócą ten sam błąd, jeżeli nie wystąpią w
międzyczasie inne błędy.

imap_listmailbox
Zwraca tablicę zawierającą nazwy skrzynek pocztowych. Opis parametrów $ref i $pattern znajduje się
przy opisie imap_getmailboxes().
array imap_listmailbox (int imap_stream, string ref, string pattern)
Przykład: imap_listmailbox()
$mbox = imap_open("{your.imap.host}","username","password",OP_HALFOPEN)
or die("błąd połaczenia: ".imap_last_error());

$list = imap_listmailbox($mbox,"{your.imap.host}","*");
if(is_array($list)) {
reset($list);
while (list($key, $val) = each($list))
print imap_utf7_decode($val)."<br>\n";
} else
print "Nieudane wywołanie funkcji imap_listmailbox: ".imap_last_error()."\n";
imap_close($mbox);

271 PHP – Kompendium wiedzy
imap_listsubscribed
Zwraca tablicę z wszystkimi skrzynkami pocztowymi, do których posiadasz subskrypcję. Jest ona niemal
identyczna jak imap_listmailbox(), ale zwraca jedynie te skrzynki, do których zalogowany użytkownik posiada
subskrypcję.
array imap_listsubscribed (int imap_stream, string ref, string pattern)

imap_mail
Funkcja ta jest obecnie dostępna tylko w PHP 3.
string imap_mail (string to, string subject, string message
[, string additional_headers [, string cc [, string bcc [, string rpath]]]])

imap_mailboxmsginfo
Zwraca informacje na temat bieżącej skrzynki pocztowej. Zwraca False w przypadku wystąpienia błędu.
Funkcja imap_mailboxmsginfo() sprawdza status skrzynki na serwerze. Jest podobna do imap_status(), ale
dodatkowo sumuje rozmiary wszystkich wiadomości w skrzynce, co jednak powoduje wydłużenie czasu
wykonywania funkcji. Zwraca dane w postaci obiektu z następującymi właściwościami.
object imap_mailboxmsginfo (int imap_stream)
Właściwości skrzynki pocztowej
• Date — data ostatniej zmiany
• Driver — sterownik
• Mailbox — nazwa skrzynki
• Nmsgs — ilość wiadomości
• Recent — ilość ostatnich wiadomości
• Unread — ilość wiadomości nie przeczytanych
• Deleted — ilość usuniętych wiadomości
• Size — rozmiar skrzynki
Przykład: imap_mailboxmsginfo()
<?php

$mbox = imap_open("{your.imap.host}INBOX","username", "password")
or die("błąd połączenia: ".imap_last_error());

$check = imap_mailboxmsginfo($mbox);

if($check) {
print "Date: " . $check->Date ."<br>\n" ;
print "Driver: " . $check->Driver ."<br>\n" ;
print "Mailbox: " . $check->Mailbox ."<br>\n" ;
print "Messages: ". $check->Nmsgs ."<br>\n" ;
print "Recent: " . $check->Recent ."<br>\n" ;
print "Unread: " . $check->Unread ."<br>\n" ;
print "Deleted: " . $check->Deleted ."<br>\n" ;
print "Size: " . $check->Size ."<br>\n" ;
} else {
print "imap_check() failed: ".imap_last_error(). "<br>\n";
}

imap_close($mbox);

?>

imap_mail_compose
string imap_mail_compose (array envelope, array body)
Przykład: imap_mail_compose()
<?php

$envelope["from"]="musone@afterfive.com";
$envelope["to"]="musone@darkstar";
$envelope["cc"]="musone@edgeglobal.com";

$part1["type"]=TYPEMULTIPART;
Dodatek A - Funkcje 272
$part1["subtype"]="mixed";

$filename="/tmp/imap.c.gz";
$fp=fopen($filename,"r");
$contents=fread($fp,filesize($filename));
fclose($fp);

$part2["type"]=TYPEAPPLICATION;
$part2["encoding"]=ENCBINARY;
$part2["subtype"]="octet-stream";
$part2["description"]=basename($filename);
$part2["contents.data"]=$contents;

$part3["type"]=TYPETEXT;
$part3["subtype"]="plain";
$part3["description"]="description3";
$part3["contents.data"]="contents.data3\n\n\n\t";

$body[1]=$part1;
$body[2]=$part2;
$body[3]=$part3;

echo nl2br(imap_mail_compose($envelope,$body));

?>

imap_mail_copy
Zwraca True w przypadku sukcesu lub False w przypadku wystąpienia błędu. Kopiuje wiadomość
określoną przez $msglist do określonej skrzynki pocztowej. Parametr $msglist może zawierać zakres a nie tylko
numery komunikatów, jak to zostało opisane w RFC2060 (http://www.faqs.org/rfcs/rfc2060.html). Parametr
$flags jest maską bitową zawierającą CP_UID — sekwencja liczb zawiera UID i CP_MOVE — usuwa
komunikaty ze skrzynki po ich skopiowaniu.
int imap_mail_copy (int imap_stream, string msglist, string mbox [, int flags])

imap_mail_move
Przenosi przesyłkę pocztową określoną przez $msglist to podanej skrzynki pocztowej. Parametr $msglist
może zawierać zakres a nie tylko numery komunikatów, jak to zostało opisane w RFC2060
(http://www.faqs.org/rfcs/rfc2060.html). Parametr $flags jest maską bitową i może zawierać jedną wartość
CP_UID. Zwraca True w przypadku sukcesu lub False w przypadku wystąpienia błędu.
int imap_mail_move (int imap_stream, string msglist, string mbox [, int flags])

imap_mime_header_decode
Dekoduje rozszerzenia nagłówków komunikatów MIME, które zawierają tekst z poza ASCII (RFC2047
http://www.faqs.org/rfcs/rfc2047.html). Zdekodowane elementy są zwracane w postaci tablicy obiektów, z który
posiada dwie właściwości: charset i text. Jeżeli element nie może być zdekodowany a inne słowa są w US-
ASCII, właściwość charset jest ustawiona na wartość domyślną.
array imap_mime_header_decode (string text)
Przykład: imap_mime_header_decode()
$text="=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>";

$elements=imap_mime_header_decode($text);
for($i=0;$i<count($elements);$i++) {
echo "Charset: {$elements[$i]->charset}\n";
echo "Text: {$elements[$i]->text}\n\n";
}
W przedstawionym przykładzie otrzymamy dwa elementy, gdzie pierwszy element jest zakodowany za
pomocą ISO-8859-1 a drugi będzie US-ASCII.

imap_msgno
Zwraca numer sekwencji wiadomości dla podanego UID. Jest to odwrotność imap_uid().
int imap_msgno (int imap_stream, int uid)

273 PHP – Kompendium wiedzy
imap_num_msg
Zwraca ilość przesyłek w bieżącej skrzynce pocztowej.
int imap_num_msg (int imap_stream)

imap_num_recent
Zwraca ilość ostatnich przesyłek w bieżącej skrzynki pocztowej.
int imap_num_recent (int imap_stream)

imap_open
W przypadku powodzenia zwraca strumień IMAP, a w przypadku błędu False. Funkcja może być
używana do otwarcia strumienia do serwerów POP3 i NNTP i nie wszystki