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 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 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 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 MSQL 386 DYREKTYWY KONFIGURACJI POSTGRESQL 386 DYREKTYWY KONFIGURACJI SYBASE 387 DYREKTYWY KONFIGURACJI SYBASE-CT 387 DYREKTYWY KONFIGURACJI INFORMIX 388 DYREKTYWY KONFIGURACJI BC MATH 389 DYREKTYWY KONFIGURACJI MOŻLIWOŚCI PRZEGLĄDAREK 389 DYREKTYWY KONFIGURACJI ZUNIFIKOWANEGO ODBC 389 DODATEK E. ZASOBY SIECI 390 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: Nazwa przykładu 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 Prosty przykład kodu PHP 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 Prosty przykład kodu PHP Witajcie w PHP! Preprocesor PHP wykonuje cały kod zawarty pomiędzy znacznikami 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 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. 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. 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. 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 phpinfo() 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 . 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 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 powinno być zamienione na nazwę twojego katalogu bazowego PHP. cd 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. 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. 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 ./configure Po zakończeniu działania tego skryptu można zająć się konfigurowaniem i kompilowaniem PHP. cd ./configure --with-apache= 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 ./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 make clean ./configure --enable-module=so --enable-rule=SHARED_CORE --prefix=/www make 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 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 Win32 www.php.net/extra/win32build.zip Obsługa BCMath www.php.net/version4/downloads/number.tar.gz Zastępnik pliku resolv.lib www.php.net/version4/downloads/bindlib_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 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. 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 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: 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: 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 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. 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ł 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 "można stosować również znaczniki w stylu ASP
"; %> 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 "; ?> " ?> 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 "; // To jest jednowierszowy komentarz w stylu C++ /* To jest wielowierszowy blok komentarza */ echo "Witamy ponownie.
"; # To jest komentarz w stylu skryptów Uniksa ?> To wyświetli nic.
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 " ); ?> 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 " ); $aStr2 = "Thatcher"; print( "$aStr2
" ); $aStr3 = "Nazywam się $aStr2"; // $aStr3 = "Nazywam się Thatcher" print( "$aStr3
" ); $aStr4 = "Nazywam się \$aStr2"; // $aStr4 = "Nazywam się $aStr2" print( "$aStr4
" ); $aStr5 = 'Nie rozwijaj \'$aStr2\''; // $aStr5 = "Nie rozwijaj '$aStr2'" print( "$aStr5
" ); // 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ą " ); $aVar2 = $aVar . " niejawnie skonwertowane do ciągu"; print( "\$aVar2 = $aVar2, typ = " . gettype( $aVar2 ) . "
" ); $aVar3 = $aVar2 + 1; // niejawna konwersja na liczbę całkowitą print( "\$aVar3 = $aVar3, typ = " . gettype( $aVar3 ) . "
" ); $aVar3 = $aVar2 * 1.1; // niejawna konwersja na liczbę zmiennoprzecinkową print( "\$aVar3 = $aVar3, typ = " . gettype( $aVar3 ) . "
" ); $aNotNumber = "abc"; $aVar4 = $aNotNumber * 1; // próba konwersji na liczbę, zwracane jest 0 print( "\$aVar4 = $aVar4, typ = " . gettype( $aVar4 ) . "
" ); $aIsNumber = "3 małe świnki"; $aVar5 = $aIsNumber + 1; // konwersja $aIsNumber na liczbę 3 print( "\$aVar5 = $aVar5, typ = " . gettype( $aVar5 ) . "
" ); ?> 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 " ); // Tworzenie tablicy asocjacyjnej $color["niebieski"] = "#0000FF"; $color["zielony"] = "#00FF00"; $color["czerwony"] = "#FF0000"; print( "Wartość szesnastkowa koloru czerwonego wynosi {$color['czerwony']}
" ); // 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']}
" ); // 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]}
" ); // 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]}
" ); ?> 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 " ); // 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 " ); // wypisuje "Ryan, Ryan" $variable2 = "Scott"; print( "$variable1, $variable2
" ); // wypisuje "Ryan, Scott" $variable3 = &$variable1; // przypisanie przez referencję print( "$variable1, $variable3
" ); // wypisuje "Ryan, Ryan" $variable3 = "Katie"; print( "$variable1, $variable3
" ); // 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, 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. 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 dzielenia $a % $b Reszta z 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 symetryczna $a ^ $b Bity ustawione 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 lewo $a << $b Przesunięcie bitów w $a w lewo o $b kroków >> Przesunięcie w prawo $a >> $b Przesunięcie 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 równy $a <= $b True, jeżeli $a jest mniejsze lub równe $b >= Większy lub równy $a >= $b True, jeżeli $a jest większe lub równe $b Tabela 2.8. Operatory zwiększania i zmniejszania 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 logiczny $a and $b True, jeżeli $a i $b mają wartość True or Suma logiczna $a or $b True, jeżeli $a lub $b mają wartość True xor Różnica symetryczna $a xor $b True, jeżeli $a lub $b mają wartość True, ale nie razem ! Negacja !$a True, jeżeli $a nie jest True && Iloczyn logiczny $a && $b True, jeżeli $a 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ść ($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 ponieważ zmienna $aGlobal1 wewnątrz funkcji jest poza zasięgiem. */ print( "$aGlobal1
" ); } DoPrint(); function DoPrint2( ) { global $aGlobal1; /* Poniższa instrukcja wypisze wartość zmiennej ponieważ została zadeklarowana jako globalna. */ print( "$aGlobal1
" ); } DoPrint2(); function StaticFunc( ) { static $aVal = 0; print( "$aVal
" ); $aVal++; } // Poniższe wywołania spowodują wypisanie 0, a następnie 1 StaticFunc(); StaticFunc(); ?> --- Zawartość pliku example10_inc.php3 --- " ); ?> 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 " ); print( "Jej wartością jest '" . aString . "'
" ); ?> 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 " ); // drukuje "Wartości są różne" $aVal = ( 1 == "1" ) ? "Wartości są równe" : "Wartości są różne"; print( "$aVal
" ); // drukuje "Wartości są równe" $aVal = ( 1 === "1" ) ? "Wartości są identyczne" : "Wartości nie są identyczne"; print( "$aVal
" ); // 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
I wypisanie wyników */ $aListing = `ls -l`; $aFmtList = nl2br( $aListing ); print( "
Zawartość katalogu:
$aFmtList
" ); ?> 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 " ); else print( "To nie zostanie wydrukowane.
" ); $aValue = 2; if ( $aValue == 1 ) { // Używamy nawiasów klamrowych do otaczania bloków instrukcji print( "\$aValue == 1
" ); } elseif ( $aValue == 2 ) { print( "\$aValue == 2
" ); } elseif ( $aValue == 3 ) { print( "\$aValue == 3
" ); } else { print( "\$aValue nie jest 1, 2 ani 3
" ); } ?> 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 while.
" ); $nIndex = 0; // wypisuje liczby od 0 do 9 while ( $nIndex < 10 ) { print( "$nIndex
" ); $nIndex++; } print( "Liczenie w dół przy użyciu do..while.
" ); // wypisuje liczby od 10 do 1 do { print( "$nIndex
" ); $nIndex--; } while ( $nIndex > 0 ); ?> 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 " ); } /* $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
" ); } ?> 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 " ); } $aColorArray = array( "Czerwony" => "#FF0000", "Zielony" => "#00FF00", "Niebieski" => "#0000FF" ); foreach( $aColorArray as $aKey => $aValue ) { print( "Wartość szesnastkowa $aKey to $aValue
" ); } ?> 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. 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 " ); break; case 1: print( "jeden
" ); break; case 2: print( "dwa
" ); break; } // Użycie frazy 'default' $nIndex = 17; switch ( $nIndex ) { case 0: print( "zero
" ); break; case 1: print( "jeden
" ); break; case 2: print( "dwa
" ); break; default: print( "Nie jest to zero, jeden ani dwa
" ); break; } // Switch z użyciem ciągu $aColor = "niebieski"; switch( $aColor ) { case "czerwony": print( "#FF0000
" ); break; case "zielony": print( "#00FF00
" ); break; case "niebieski": print( "#0000FF
" ); break; default: print( "inny
" ); 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
" ); case 1: print( "jeden
" ); case 2: print( "dwa
" ); } /* opuszczenie instrukcji break może być czasami przydatne */ $aColor = "Czerwony"; switch( $aColor ) { case "czerwony": case "Czerwony": // Poniższa instrukcja zostanie wykonana, jeżeli $aColor // będzie miał wartość "Czerwony" lub "czerwony" print( "#FF0000
" ); break; case "zielony": case "Zielony": print( "#00FF00
" ); break; case "niebieski": case "Niebieski": print( "#0000FF
" ); break; default: print( "inny
" ); 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 $aCurMax ) { $aCurMax = $aValue; break; // możemy napisać 'break 1;' } } // wypisuje "Bieżącym maksimum jest 20" print( " Bieżącym maksimum jest $aCurMax
" ); // 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
" ); } ?> 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 Przykład 19
Wybierz swój rok urudzenia:
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. 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 $aText" ); } else { print( "$aText" ); } } // 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
" ); } } print( "ReturnSum( 3, 5 ): " . ReturnSum( 3, 5 ) . "
" ); $aString = "Marysia miała "; StringAppend( $aString, "małą owieczkę" ); print( "$aString
" ); // wypisuje "Marysia miała małą owieczkę" PrintAnchorTag( "example10.phtml", "Zobaczmy jeszcze raz przykład 10" ); print( "
" ); PrintAnchorTag( "example10.phtml", "Zobaczmy jeszcze raz przykład 10 w nowym oknie", "_blank" ); print( "
" ); print( "Wywołanie PrintEverything( 1, 2, 3, 4, 5 ):
" ); 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 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 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:
" ); foreach( $this->fItems as $aKey => $aValue ) { print( "{$aValue['Quantity']} $aKey
" ); } print( "Wartość całkowita: $" . number_format( $this->fCurValue, 2 ) ); print( "
" ); print( "
" ); } else { print( "Koszyk jest pusty

" ); } } } /* 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 ); $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. 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: Przykład: błędny plik dołączany otwiera dziurę w systemie zabezpieczeń " ); ?> 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. Wydruk 3.1 - listing1.html
Nazwa użytkownika:
Hasło:
Wydruk 3.1: post1.phtml " ); print( "Hasło: $Password
" ); ?> 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
Wybierz kolory które lubisz:
Wprowadź twój adres:


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: Wydruk: post2.phtml " ); print( "Hasło: {$HTTP_POST_VARS['Password']}
" ); ?> 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() { 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 (""); // Wyświetlenie nagłówka tabeli print ( " "); // Wyświetlenie wszystkich par klucz/wartość z tabeli foreach( $aArray as $aKey => $aValue ) { print( "" ); // 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( ""); } else { print( ""); } } else { print( "" ); } print (""); } print ("
KluczWartość
$aKeypusty$aKey$aValue$aKey(array)"); DisplayArray( $aValue ); print ("
"); } else { print("pusty lub nieprawidłowy"); } } 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. Wyświetlenie wszystkich elementów formularza

Cała zawartość HTTP_GET_VARS



Cała zawartość HTTP_POST_VARS



Cała zawartość HTTP_COOKIE_VARS



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ściowych elementów formularza Rysunek 3.2. Wynik przesłania formularza wielowartościowego 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 Użycie rysunku zamiast przycisku
Nazwa użytkownika:
Hasło:
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 Kontrola poprawności amerykańskiego kodu pocztowego i daty ISO " ); else print( "'$aCode1' nie jest poprawnym kodem pocztowym
" ); if ( ereg( $aCodeFormat, $aCode2 ) == True ) print( "'$aCode2' jest poprawnym kodem pocztowym
" ); else print( "'$aCode2' nie jest poprawnym kodem pocztowym
" ); if ( ereg( $aCodeFormat, $aCode3 ) == True ) print( "'$aCode3' jest poprawnym kodem pocztowym
" ); else print( "'$aCode3' nie jest poprawnym kodem pocztowym
" ); if ( ereg( $aCodeFormat, $aCode4 ) == True ) print( "'$aCode4' jest poprawnym kodem pocztowym
" ); else print( "'$aCode4' nie jest poprawnym kodem pocztowym
" ); $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
" ); else print( "'$aDate1' nie jest poprawnym formatem daty ISO
" ); if ( ereg( $aDateFormat, $aDate2 ) == True ) print( "'$aDate2' jest poprawnym formatem daty ISO
" ); else print( "'$aDate2' nie jest poprawnym formatem daty ISO
" ); if ( ereg( $aDateFormat, $aDate3 ) == True ) print( "'$aDate3' jest poprawnym formatem daty ISO
" ); else print( "'$aDate3' nie jest poprawnym formatem daty ISO
" ); if ( ereg( $aDateFormat, $aDate4 ) == True ) print( "'$aDate4' jest poprawnym formatem daty ISO
" ); else print( "'$aDate4' nie jest poprawnym formatem daty ISO
" ); ?> 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. Kontrola liczb przy użyciu kontroli typów " ); else print( "'$aValue1' nie jest liczbą
" ); if ( is_numeric( $aValue2 ) == True ) print( "'$aValue2' jest liczbą
" ); else print( "'$aValue2' nie jest liczbą
" ); if ( is_numeric( $aValue3 ) == True ) print( "'$aValue3' jest liczbą
" ); else print( "'$aValue3' nie jest liczbą
" ); if ( is_numeric( $aValue4 ) == True ) print( "'$aValue4' jest liczbą
" ); else print( "'$aValue4' nie jest liczbą
" ); ?> 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 is_phone( $aPhoneNum1 ) == True ) print( "'$aPhoneNum1' jest prawidłowym numerem telefonu
" ); else print( "'$aPhoneNum1' nie jest prawidłowym numerem telefonu
" ); if ( $aValidator->is_phone( $aPhoneNum2 ) == True ) print( "'$aPhoneNum2' jest prawidłowym numerem telefonu
" ); else print( "'$aPhoneNum2' nie jest prawidłowym numerem telefonu
" ); if ( $aValidator->is_phone( $aPhoneNum3 ) == True ) print( "'$aPhoneNum3' jest prawidłowym numerem telefonu
" ); else print( "'$aPhoneNum3' nie jest prawidłowym numerem telefonu
" ); /* 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
" ); else print( "'$aEmail1' nie jest prawidłowym adresem email
" ); if ( $aValidator->is_email( $aEmail2 ) == True ) print( "'$aEmail2' jest prawidłowym adresem email
" ); else print( "'$aEmail2' nie jest prawidłowym adresem email
" ); if ( $aValidator->is_email( $aEmail3 ) == True ) print( "'$aEmail3' jest prawidłowym adresem email
" ); else print( "'$aEmail3' nie jest prawidłowym adresem email
" ); ?> 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 Użycie cookie
" . $aValMessage ); ?> Wydruk 3.10. Użycie setcookie() razem z buforowaniem wyjścia Użycie setcookie() wraz z buforowaniem wyjścia Działa świetnie. 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 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 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"; } } ?> Inteligentna obsługa nieprawidłowych danych Proszę wprowadzić numer telefonu i adres email.
We wprowadzonych danych wystąpiły błędy. Sprawdź dane oznaczone kolorem czerwonym.
Numer telefonu:
Adres e-mail:
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 < i >. Formularz i skrypt na wydruku 3.12 pokazuje obróbkę danych do ponownego wyświetlenia. Wydruk 3.12. Obróbka danych do wyświetlenia. Pobieranie danych do wyświetlenia
Wprowadź tekst:


Wybierz metodę filtrowania:

Bezpieczne wyświetlenie danych użytkownika 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 Rysunek 3.4. Wyświetlanie bez filtrowania Rysunek 3.5. Wyświetlanie ze strip_tags() 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
. PHP posiada funkcję nl2br(), która konwertuje znaki nowej linii na znaczniki 
. 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 Pobieranie danych do wyświetlenia
Wprowadź tekst:


Wybierz metodę filtrowania:

strip_slashes()
nl2br()

Bezpieczne wyświetlenie danych użytkownika Rysunek 3.7. Formularz wprowadzania danych 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. 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 $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 Strona testowa 1 Strona ta była oglądana razy. 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. Wydruk 4.3. Użycie gniazd Przykład wykorzystania serwera QOTD: Użycie gniazd w PHP " ); } fclose( $aFile ); ?> 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 "; exit; } ?> Whois: Uzycie potoków w PHP " ); } pclose( $aFile ); } else { echo "Nie mogę otworzyć $whois do odczytu!
"; } print( "
" ); } ?>
Wprowadź zapytanie whois:
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 Użycie klasy File Poniżej znajduje się lista plików w bieżącym katalogu.
Kliknij nazwę pliku aby zobaczyć ich zawartość.

get_files( "." ); for ( $nIndex = 0; $nIndex < count( $aDirContents ); $nIndex++ ) { $aCurFile = $aDirContents[$nIndex]; print( "" ); print( "$aCurFile
" ); } ?> Wydruk 4.6. Wyświetlenie zawartości pliku za pomocą klasy File Użycie klsy File $fn:

" ); $aFileClass = new File(); $aFileCont = $aFileClass->read_file( $fn ); print( "
" );
    print( nl2br( htmlentities( $aFileCont ) ) );
    print( "
" ); ?> 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". 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
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 typu file. Wydruk 5.1. zawiera prosty formularz HTML zawierający jeden znacznik . Wydruk 5.1. Formularz HTML ze znacznikiem Formularz do przesyłania plików Wyślij plik:

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 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 Wyświetlenie przesłanego pliku Wystąpił błąd: $aErrors
" ); } else { print( "Przesłany plik:

" ); print( "" ); } ?> 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 Formularz do przesyłania plików
Wyślij plik:

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 Formularz do przesyłania plików Proszę podać cztery pliki rysunków do przesłania:
Plik 1:

Plik 2:

Plik 3:

Plik 4:

Wydruk 5.5. Obsługa czterech przesyłanych plików Wyświetlanie przesłanego rysunku $aCount rysunki:

" ); foreach( $aNewNames as $aNewName ) { print("

"); } ?> 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 Naruszenie bezpieczeństwa przy przesyłaniu pliku $aNewName:

" ); $aFileClass = new File(); $aFileCont = $aFileClass->read_file( $thefile ); print( "
" );
    print( nl2br( htmlentities( $aFileCont ) ) );
    print( "
" ); ?> 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 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ł 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 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 Pobieranie danych z MySQL " ); } mysql_free_result( $aQResult ); } else { print( "Błąd wykonania zapytania
" ); } } else { print( "Błąd wyboru bazy danych
" ); } } else { print( "Błąd przy podłączaniu do bazy danych
" ); } ?> 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 " ); $aResult = -1; } } else { // print( "Błąd wyboru bazy danych
" ); $aResult = -2; } } else { // print( "Błąd przy podłączaniu do bazy danych
" ); $aResult = -3; } return $aResult; } ?> Przykład MySQL: Wstawianie danych do bazy 0 ) { print( "Dodano nowy wiersz, ID = $aResult
" ); } else { print( "Błąd funkcji InsertRecord. Kod błędu = $aResult
" ); } print( "
" ); } ?> Proszę wpisać dane nowego pracownika:
Imię:
Nazwisko:
Adres:
Stanowisko:

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. 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. Komponenty aplikacji PHP korzystającej z PHP PHP Application - aplikacja PHP Driver Manager - zarządca sterowników Database Driver - sterownik do bazy danych DBMS - SZRBD 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. Dodajemy sterownik OOB PHP Application - aplikacja PHP Driver Manager - zarządca sterowników 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 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. 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 Pobioeranie danych z bazy danych ODBC " ); $aRowNum++; } odbc_free_result( $aQResult ); } else { print( "Błąd wykonania zapytania
" ); } } else { print( "Błąd podłączenia do bazy danych
" ); } ?> 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 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 query( "select * from employees" ); while( $aDB->next_record() ) { $aFName = $aDB->f( "first" ); $aPos = $aDB->f( "position" ); print( "$aFName, $aPos
" ); } ?> 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 query( "select * from employees" ); while( $aDB->next_record() ) { $aFName = $aDB->f( "first" ); $aPos = $aDB->f( "position" ); print( "$aFName, $aPos
" ); } ?> 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 query( $aSQL ); while( $aDB->next_record() ) { $aName = $aDB->f( "Name" ); $aID = $aDB->f( "ID" ); if ( $aID == $aCurSel ) { $aResult .= ""; } else { $aResult .= ""; } } return $aResult; } ?> Formularz wyboru stanu USA oraz kraju
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. 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. 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 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". 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 Podstawy sesji: Strona 1" ); print( "Konto: $aAccount
" ); ?>

Przejście do strony 2 Wydruk 7.2. Użycie zmiennych sesji z wydruku 7.1 Podstawy sesji: Strona 2" ); print( "Konto: $aAccount
" ); ?> 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";. 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 Ręczne przesyłanie identyfikatora sesji: Strona 1" ); print( "Konto: $aAccount
" ); ?>

Przejście do strony 2 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: "> Przejście do strony 2 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 . Zapis taki jest równoważny z 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 Automatyczne przesyłanie identyfikatora sesji: Page 1" ); print( "Konto: $aAccount
" ); ?>

Przejście do strony 2 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 Uzupełnianie adresów URL" ); print( "Konto: $aAccount
" ); ?>

Łącze 1

Łącze 2

Łącze 3

Łącze 4

Po uruchomieniu skryptu, do przeglądarki został wysłany następujący kod HTML: Uzupełnianie adresów URL Użytkownik: Cidnie
Konto: 1016


Łącze 1

Łącze 2

Łącze 3

Łącze 4

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). 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 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. * 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 " ); return True; } function mysess_close() { print( "mysess_close()
" ); return True; } function mysess_read( $aKey ) { print( "mysess_read( $aKey )
" ); 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 )
" ); return True; } function mysess_destroy( $aKey ) { print( "mysess_destroy( $aKey )
" ); return True; } function mysess_gc( $aMaxLifetime ) { print( "mysess_gc( $aMaxLifetime )
" ); 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 Własny mechanizm obsługi sesji" ); print( "Konto: $aAccount
" ); $aUser = "Katie"; $aAccount = "2026"; print( "Użytkownik: $aUser
" ); print( "Konto: $aAccount
" ); ?> 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 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. Obsługa sesji z wykorzystaniem MySQL: Strona pierwsza" ); print( "Konto: $aAccount
" );?>

Przejście do następnej strony 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 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 "MySqlSession" ) ); $sess->register( "aUser" ); $sess->register( "aAccount" ); $aUser = "Cidnie"; $aAccount = "1016"; ?> Obsługa sesji w PHPLIB: Pierwsza strona " ); print( "Konto: $aAccount
" ); ?>

Następna strona 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: register("lang"); 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ż 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 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ł 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 Proste uwierzytelnianie ApachePrzejdź do strony administratora Wydruk 8.2. Dyrektywy konfiguracji Apache włączające podstawowe uwierzytelnianie AuthUserFile /www/auth_users AuthName Adminstrative AuthType Basic require valid-user 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 Strona administratora

Witamy na stronie administratora

" ); print( "PHP_AUTH_PW: $PHP_AUTH_PW
" ); ?> 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 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 Szybkie sprawdzenie użytkownika z uzyciem klasy HtpasswdEXISTS ) { print( "Błąd autoryzacji
" ); } else { if ( $aHTPasswd->verifyuser( "phpbook", "phpbook" ) ) { print( "phpbook to prawidłowy użytkownik
" ); } else { print( "phpbook nie jest prawidłowym użytkownikiem
" ); } } ?> 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 EXISTS ) { print( "Błąd krytyczny
" ); exit; } if ( $REQUEST_METHOD == 'POST' ) { switch ( $acttype ) { case 'none' : break; case 'add' : $aHTPasswd->addUser( $NewUserName, $NewUserPass ); print( "Dodano użytkownika $NewUserName
" ); break; case 'delete' : $aUserName = $aHTPasswd->USERS[$CurUserRow]["user"]; $aHTPasswd->deleteUser( $aUserName ); print( "Usunięto użytkownika $aUserName
" ); break; case 'rename' : $aUserName = $aHTPasswd->USERS[$CurUserRow]["user"]; $aHTPasswd->renameUser( $aUserName, $RenameName ); print( "Nazwa użytkownika zmieniona z $aUserName na $RenameName
" ); break; case 'changepass' : $aUserName = $aHTPasswd->USERS[$CurUserRow]["user"]; $aHTPasswd->changePass( $aUserName, $ChangePass ); print( "Zmieniono hasło dla użytkownika $aUserName
" ); 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 Prosty program zarządający użytkownikami 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

Prosty program zarządzający użytkownikami

Dodanie użytkownika

Nazwa nowego użytkownika:
Hasło:

Zmiana użytkownika

Wybierz stan USA:
Wybierz kraj:
Usunięcie zaznaczonego użytkownika:

Zmiana nazwy zaznaczonego użytkownika:

Zmiana hasła dla zaznaczonego użytkownika:

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. 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 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ść, 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 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) Formularz autoryzacji dla PHPLIB
Imię:
Nazwisko:
Hasło:


Podane dane są nieprawidłowe, spróbuj jeszcze raz..
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 "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"); ?> Przykład użycia klasy PHPLIB Auth

Strona główna

Posiadasz uprawnienia do oglądania tej strony! 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: 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 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ł 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 Szybkie sprawdzenie typu przeglądarki nie jest MS Internet Explorer!
" ); } else { print( "Przeglądarka MS Internet Explorer!
" ); } ?> 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 Dodatkowe informacje na temat Browscap W czasie pisania książki podejmowane były dodatkowe ulepszenia do istniejącej w PHP funkcji get_browser(). Odkryłem, że plik browser.ini dostępny z firmy cyScape (wspomnianej już wcześniej) wymaga kilku zmian aby działał z PHP 4.0 z poprawką 2. Pierwszą z nich jest dodanie pustej linii na końcu pliku. Bez dodatkowego znaku końca linii w trakcie uruchamiania PHP generowany jest błąd składni. Drugim, o wiele bardziej skomplikowany problem jest związany z samą struktura pliku. Typowo, plik browscap.ini jest zwykłym plikiem konfiguracyjnym w którym każda sekcja reprezentuje określoną przeglądarkę. Każda z sekcji odwołuje się do sekcji wyższego rzędu, więc zdefiniowana jest pewnego rodzaju struktura. Tym sposobem w sekcji opisującej nową przeglądarkę zdefiniowane są tylko nowe możliwości a istniejące wcześniej znajdują cię w opisie starszej przeglądarki. Problem wynika z tego, że w czasie odczytu pliku PHP konwertuje jego zawartość na małe litery. Konwersja ta powoduje, że PHP nie potrafi odszukać nadrzędnej sekcji, ponieważ nie wszystkie nazwy zapisane są małymi literami. Aby rozwiązać ten problem wykonałem prosty skrypt konwertujący wszystkie linie pliku browscap.ini na małe litery: Skutkiem ubocznym takiej zmiany jest to, że wszystkie wywołania funkcji get_browser() muszą zawierać wywołanie funkcji strtolower(). Jednak po wprowadzeniu takich zmian funkcja get_browser() działa tak, jak się tego spodziewamy. 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 get_browser() należy ściągnąć z sieci plik browscap.ini, który jest dostępny z wielu witryn w Sieci. W czasie pisania książki plik był dostępny bez żadnych opłat z witryny http://www.cyscape.com/browscap. Po jego ściągnięciu i zainstalowaniu w serwerze WWW, należy zmienić plik php.ini. Odszukaj opcję konfiguracji browscap i zmień ją na pełną ścieżkę do pliku 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 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() Możliwości przeglądarki

Możliwości przeglądarki

$aUserAgent" ); $array = (array) get_browser( $aUserAgent ); if ( count( $array ) > 1 ) { while ( list( $key, $value ) = each ($array) ) { $aValue = stripslashes( $value ); echo ("$key= $aValue
"); } } else { print( "brak danych przeglądarki
" ); } ?> Na rysunku 9.2. w przeglądarce Netscape 4.7. 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 Obsługa ramek? $aUserAgent" ); $aBrowsCap = get_browser( $aUserAgent ); if ( $aBrowsCap->frames == 1 ) { print( "Przeglądarka obsługuje ramki
" ); } else { print( "Przeglądarka nie obsługuje ramek
" ); } ?> 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(r), 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 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 Przykład wykorzystania BrowserHawk

Przykład wykorzystania BrowserHawk

$HTTP_USER_AGENT" ); $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
" ); } else { print( "Przeglądarka nie obsługuje kontrolek ActiveX
" ); } ?> 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 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 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. 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 getBrowserInfo( "$HTTP_USER_AGENT" ); ?> Wysyłanie pliku

Wysyłanie pliku

getFileUpload() == True ) { ?>

Przeglądarka nie obsługuje wysyłania plików.

proszę przesłać pliki pocztą na adres files@my.domain.com.
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 getBrowserInfo( "$HTTP_USER_AGENT" ); if ( $aBrowserInfo->getPNG() == True ) { $aImage = "Logo.png"; } else { $aImage = "Logo.gif"; } ?> Nasze logo

Nasze logo

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. 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ł 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 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. 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 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() Testowanie asercji " ); $aResult = ArrayAverage( array( 10.1, "5.5", 3, "4", 5 ) ); print( "ArrayAverage( array( 10.1, \"5.5\", 3, \"4\", 5 ) ) = $aResult
" ); $aResult = ArrayAverage( array( 1, 2, "cat", 4, 5 ) ); print( "ArrayAverage( array( 1, 2, \"cat\", 4, 5 ) ) = $aResult
" ); $aResult = ArrayAverage( 1, 2 ); print( "ArrayAverage( 1, 2 ) = $aResult
" ); ?> 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 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() Poziomy raportowania błędów "Idaho", "county" => "Madison", "city" => "Rexburg", "country" => "US" ); // standardowy poziom raportowania błędów print( "aArray[state] = " . $aArray[state] . "
" ); error_reporting( E_ALL ); print( "aArray[state] = " . $aArray[state] . "
" ); ?> 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 Poziomy raportowania błędów "Idaho", "county" => "Madison", "city" => "Rexburg", "country" => "US" ); // domyślny poziom raportowania print( "aArray[state] = " . $aArray[state] . "
" ); error_reporting( E_ALL ); print( "aArray[state] = " . $aArray[state] . "
" ); ?> Rysunek 10.2. Wynik działania skryptu error_reporting() 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(). 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() Rejestrowanie błędów Wystąpiły błędy. 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() " ); print( "$aErrorType: $aErrorStr
" ); print( "w pliku $aFile linia $aLine
" ); print( "" ); } set_error_handler( "myErrorHandler" ); error_reporting( E_ALL ); ?> Własna obsługa błędów "Idaho", "county" => "Madison", "city" => "Rexburg", "country" => "US" ); print( "aArray[state] = " . $aArray[state] . "
" ); ?> 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() 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() " ); print( "assert() nieudane: $aAssertion
" ); print( "w pliku $aFileName w linii $aLineNum
" ); print( "" ); } // zarejestrowanie funkcji obsługi assert_options( ASSERT_CALLBACK, "MyACallback" ); // wyłączenie normalnych ostrzeżeń assert_options( ASSERT_WARNING, 0 ); ?> Własna obsługa asercji Nieudana asercja. 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. 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; 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++ ) { 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"; 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 Test modułu MyDebug Nieudana asercja.

"Idaho", "county" => "Madison", "city" => "Rexburg", "country" => "US" ); print( "

aArray[state] = " . $aArray[state] . "
" ); sum( 1, 2 ); ?> 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 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ł 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 Wydruk 11.2. Plik dołączany format_funcs.php Skrypt z wydruku 11.3. wykorzystuje funkcje z obu poprzednich plików dołączanych. Wydruk 11.3. Skrypt wykorzystujący oba pliki dołączane Problem z wielokrotnym dołączaniem plików 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() Problem z wielokrotnym dołączaniem plików 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; 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: " ); } ?> 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; 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 Kalkulator kredytowy " . number_format( $Amount ) . "
" ); print( "Oprocentowanie: {$Interest}%
" ); print( "Czas spłaty: $Term lat

" ); $aMontlyPayment = fin_mpmt( $Amount, $Interest, $Term ); print("Rata miesięczna: " . number_format( $aMontlyPayment, 2 ) . "
"); print( "Suma rat: " . number_format( fin_total( $Amount, $Interest, $Term ), 2 ) . "

" ); $aArray = fin_table( $Amount, $Interest, $Term ); ?>
Rata nr. Podstawa Odsetki
To jest prosty kalkulator rat kredytu. Wprowadź kwotę pożyczki, oprocentowanie i czas spłaty
Kwota:
Oprocentowanie ("7.5" == 7.5%):
Czas spłaty (w latach):
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. 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ć. 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/numberspeller.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. 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 Konwersja RTF na HTML setOptWrapHTML( False ); $aOutput = $aR2H->convert( implode( "", $aArray) ); print( $aOutput ); } else { print ("Wybrany plk nie jest plikiem RTF.
"); } } ?>
Przesyłanie pliku:

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. 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. Wynik przetworzenia przykładowego pliku RTF na kod HTML nie potrafię uruchomić tego przykładu 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 Użycie potoków " ); } 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 KB.
Wolna pamięć KB.
Użyte KB.
" ); } } ?> 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ę. 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 Użycie potoków " ); } 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 KB.
Wolna pamięć KB.
Użyte KB.
" ); } } ?> 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 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ł 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