Professional Documents
Culture Documents
Ułatwi on
zapoznanie się z najnowszą wersją języka umożliwiającego tworzenie skryptów dołączanych do stron HTML.
Książka jest przeznaczona dla każdego, kto chce tworzyć witryny WWW, bardziej złożone, niż pozwala
HTML. Mamy szczególnie na myśli trzy grupy:
* projektantów stron WWW. którzy znają HTML i chcą rozpocząć tworzenie dynamicznych witryn WWW;
* zaawansowanych programistów (C, Java, Perl itp.), ale bez doświadczenia w projektowaniu dla WWW,
którzy chcą szybko nabrać biegłości w programowaniu dla serwerów WWW;
* programistów WWW, którzy używali innych technik programowania (np.: Active Server Pages, Java
Server Pages, Cold Fusion) i chcą zmienić lub po prostu poznać inne narzędzie.
ISBN: 83-7197-391-8
Original English language edition Copyright © 2000 by IDG Books Worldwide, Inc.
All rights reserved including the right of reproduction in whole or in part of any form. This translation pu-
blished by arrangement with IDG Books Worldwide, Inc.
The IDG Books Worldwide logo is a trademark or registred trademark in the United States and/or other co-
untries under exclusive to license to IDG Books Worldwide, Inc., from International Data Group, Inc. The
Bible series trade dress is a trademark of IDG Books Worldwide, Inc. in the United States and/or other coun-
tries. Used by permission.
Wydawnictwo HELION
ul. Chopina 6, 44-100 GLIWICE
tel. (32) 231-22-19, (32) 230-98-63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie7php4bi
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Plik z przykładami do książki znajduje się pod adresem
ftp://ftp.helion.pl/przyklady/php4bi.zip
Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właści-
cieli.
Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były
kompletne i rzetelne. Nie biorąjednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane
z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie po-
noszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawar-
tych w książce.
Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji
w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzna, fotograficzną, a także
kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich
niniejszej publikacji.
Printed in Poland.
Druk: INTERDRUK - Łódź, tel. 682-18-56
Książka ta jest dedykowana naszym rodzicom:
Za ich m ilość,
za poświęcenie
i za to, że pozwolili nam w dzieciństwie dużo czytać.
Podziękowania
Projekt tej książki powstał w trakcie rozmów z Debrą Williams Cauley, która jest re-
daktorem w wydawnictwie IDG Books. Prowadziła cały projekt, znajdowała dodatko-
wych współpracowników oraz izolowała naiwnych początkujących autorów od twardej
rzeczywistości przemysłu wydawniczego. Susan Christopherson pracowała jako redak-
tor projektu, zanim przekazała pałeczkę Barb Guerra, która z kolei przekształciła nasze
chaotyczne notatki w rękopis. Bob Campbell przepisał go z niesłychaną szybkością.
Nie stworzyliśmy tej książki sami. Dustin Mitchell napisał rozdział o bezpieczeństwie
i szyfrowaniu, Patrick McCuller jest autorem pierwszego szkicu rozdziałów o XML
i OOP. Ariel Garcia współpracowała w trakcie powstawania pierwszych projektów roz-
działów na temat PHP i JavaScript. Współpracownicy Joyce w firmie Epinions (szcze-
gólnie Lou Montuli i Jay Ashton) również zasługują na wdzięczność za współpracę
przy tworzeniu przykładów kodu oraz za wskazówki, jak używać PHP w silnie obcią-
żonym środowisku.
Szczególne podziękowania należą się twórcom PHP (a to: Rasmus Lerdorf, Zeev Sura-
ski, Andi Gutmans, Thies Arntzen, Stig Bakken, Sasha Schumann, Andrei Zmievski
oraz wielu ich współpracowników) ludziom, którzy tworzyli dokumentację do PHP
(m.in. Stig Bakken, Alexander Aulbach, Egon Schmid, Lars Torben Wilson, Jim Win-
stead) oraz wszystkim z listy dyskusyjnej o PHP. Szczególnie dziękujemy Rasmusowi,
Sashay oraz Richardowi Lynchowi za odpowiedzi na liście dyskusyjnej.
Autorzy chcieliby móc podziękować za pomoc swoim małżonkom, ale niestety jest to
w tej chwili niemożliwe. :)
Rzut oka na książkę
O Autorach...............................................................................................................19
Przedmowa..............................................................................................................^!
Dodatki............................................................................................553
Dodatek A PHP dla programistów C ......................................................................555
Dodatek B PHP dla programistów ASP ..................................................................561
Dodatek C PHP dla programistów HTML................................................................571
Dodatek D Zasoby Sieci na temat PHP..................................................................579
Słownik..................................................................................................................587
Skorowidz...............................................................................................................597
Spis treści
O Autorach...............................................................................................................19
Przedmowa...............................................................................................................21
Dodatki............................................................................................553
Dodatek A PHP dla programistów C ......................................................................555
Dodatek B PHP dla programistów ASP ..................................................................561
Dodatek C PHP dla programistów HTML................................................................571
Dodatek D Zasoby Sieci na temat PHP..................................................................579
Słownik ..................................................................................................................587
Skorowidz...............................................................................................................597
JL8________________________________________________PHP 4. Biblia
O Autorach
Tim Converse pisał oprogramowanie pomagające w wyborze szalików, odpowiadające
na pytania na temat stacji kosmicznych, pobierające notowania giełdowe oraz symulu-
jące robienie kolacji. Zdobył dyplom magistra informatyki na University of Chicago.
Pracuje teraz w Excite@Home, gdzie zajmuje się wyszukiwarką internetową.
Joyce Park posiada dyplom magistra historii University of Chicago; pracowała nad kil-
koma witrynami informacyjnymi w PHP, między innymi nagrodzoną MysteryGuide.
com. Jej teksty zdobyły uznanie redaktorów oraz czytelników Slashdot, OSOpinion, Li-
nux.com i wielu innych na całym świecie. Joyce jest teraz projektantem witryn w firmie
Epinions.com.
20 PHP 4. Biblia
Przedmowa
Witamy w książce P HP 4. Biblial Mimo że jesteśmy stronniczy, wierzymy, że PHP,
skryptowy język programowania dla WWW, zajął niszę najłatwiejszego i najbardziej
elastycznego narzędzia dla serwerów WWW, pozwalając na tworzenie wspaniałych
i bardzo szybkich witryn. Mimo że miliony programistów WWW na całym świecie
zwykle mogą się mylić, w tym konkretnym przypadku nie mylą się.
PHP 4.0, udostępniony na wiosnę 2000 roku, został, w porównaniu do PHP 3, uzupeł-
niony o wiele nowych możliwości, działa dużo szybciej. W książce tej przedstawione są
główne możliwości tej wersji programu. Szczegółowe przykłady pokazują, w jaki spo-
sób tworzyć witryny WWW przy użyciu PHP.
Co to jest PHP?
PHP jest językiem skryptowym wbudowywanym w strony WWW, wykonywanym na
serwerze. Jest on zgodny z większością najważniejszych serwerów WWW (najbardziej
ze znakomitym Apache). PHP pozwala na wbudowanie fragmentów kodu w normalne
strony HTML — kodu, który jest interpretowany, gdy strony są przesyłane do użyt-
kownika. PHP spełnia rolę „kleju" ułatwiającego łączenie stron WWW z bazami da-
nych umieszczonych po stronie serwera.
Dlaczego PHP?
Odpowiedzi na to pytanie poświęcamy prawie cały rozdział l. Najkrótsza odpowiedź
to: jest darmowy, ma duże możliwości, jest niezależny, stabilny, szybki, łatwy do nauki,
dobrze zaprojektowany, dobrze współpracuje z innymi produktami. A poza tym mamy
dostęp do kodu źródłowego.
22___________________________________________________PHP 4. Biblia
Co nowego w PHP 4?
PHP 4 zawiera wszystkie funkcje PHP oraz wbudowane wsparcie dla sesji, bardziej
spójną analizę składni, nowy typ Boolean oraz wiele nowych funkcji. Rdzeniem ma-
szyny skryptowej PHP jest teraz „Zend", który został napisany na nowo, aby zachować
spójność oraz zyskać rewelacyjną szybkość działania.
PHP to żywy organizm. Oryginalną wersję książki wydrukowano na wiosnę 2000 roku,
mniej więcej w tym samym czasie, gdy PHP 4 został oficjalnie wydany. Przykłady za-
warte w niej były intensywnie testowane za pomocą PHP 3 oraz różnych wersji beta PHP
4. Mimo, że opisaliśmy większość głównych funkcji PHP 4, wciąż pojawiają się nowe.
Podręcznik jest obszerny, obejmuje wszystkie aspekty i funkcje języka, jednak pozba-
wiony jest szczegółów. My, w przeciwieństwie, mamy możliwość skupienia się na te-
matach, które są najczęściej poruszane lub najmniej zrozumiałe, możemy wyjaśniać
i dawać długie przykłady.
Część l Podstawy
Rozdziały od 1. do 4. stanowią wprowadzenie do PHP i opisują zagadnienia, które mu-
sisz poznać przed rozpoczęciem pracy.
Rozdziały 14. i 15. to podręcznik stylu PHP oraz opis najczęściej spotykanych przy
programowaniu pułapek.
Rozdział 18. poświęcony jest funkcjom PHP dla MySQL — systemu baz danych, który
będziemy omawiali aż do końca drugiej części książki.
W rozdziałach 23. i 24. znajdują się wskazówki i opisy pułapek w pracy z PHP i bazami
danych.
Jeżeli wygląd strony WWW wygenerowanej przez PHP jest ważny, zamieszczamy ry-
sunek z kopią ekranu; gdy nie jest — zamieszczamy źródło strony wygenerowanej
przez PHP przy użyciu czcionki o stałej szerokości. Jeżeli chcemy odróżnić wynik
skryptu PHP widoczny w przeglądarce od aktualnego wyniku PHP (który tłumaczy
przeglądarka), nazywamy go „wynikiem z przeglądarki".
Jeżeli zaznaczamy fragment kodu kursywą, oznacza to, że to miejsce należy odpowied-
nio zmienić, a nie traktować tekst dosłownie. Jeżeli w normalnym tekście wyróżniamy
wyraz za pomocą kursywy, oznacza to, że wyraz jest niezbyt znany i zdefiniowany
w słowniku.
Znaczenie ikon
Ikony, przedstawione poniżej, są rozrzucone w całym tekście. Ich zadaniem jest wyraź-
ne zaznaczenie wagi informacji.
,.»»*».,„„,„ „„.„-u,*,
Cześć l
Podstawy PHP
Rozdział 1.
Dlaczego PHP?
W tym rozdziale:
+ Co to jest PHP?
«• Historia PHP
*• Dlaczego kochamy PHP?
* Wyprzedzamy konkurencję z PHP
Co to jest PHP?
PHP pochodzi od Hypertext Preprocessor. Właściwie produkt ten wcześniej nazywał
się Personal Home Page Tools, jednak gdy jego zakres rozszerzył się, w drodze głoso-
wania została wybrana nowa, bardziej właściwa nazwa. Dla pliku PHP można używać
dowolnego rozszerzenia, ale zalecane są .php, ,php3 oraz .phtml. Aktualnym numerem
wersji PHP jest 4, nazywany PHP 4 lub po prostu PHP.
które pozwalają dodać funkcje obsługi serwera do stron WWW. Możesz np. użyć PHP
do tworzenia na bieżąco skomplikowanych stron WWW lub do uruchamiania programu
do obciążania karty kredytowej, gdy klient złoży zamówienie.
Ściśle mówiąc, PHP ma niewiele wspólnego z układem strony, zdarzeniami lub czym-
kolwiek innym, co nadaje wygląd stronom WWW. Właściwie wszystko, co robi PHP, jest
niewidoczne dla użytkownika. Ten, kto ogląda stronę PHP, nie jest w stanie powiedzieć,
że strona nie została napisana w HTML, ponieważ wynikiem PHP jest kod HTML.
PHP jest oficjalnym modułem do serwera HTTP Apache, który jest wiodącym, bezpłat-
nym serwerem WWW, napędzającym około 55% serwerów sieci. Oznacza to, że ma-
szyna skryptowa PHP jest wbudowana w serwer WWW, co powoduje szybszą obsługę
stron. Podobnie jak serwer Apache PHP jest niezależny od platformy, działa w kilku ro-
dzajach systemu Unix oraz w Windows. Wszystkie projekty prowadzone przez Apache
Software Foundation — włączając w to PHP — są dostępne na zasadzie open source
(mamy dostęp do kodu źródłowego).
Różne wersje PHP zebrały przez kilka lat wiele pochwał i nagród. PHP 3 był w 1999 r.
finalistą konkursu LinuxWorld Editor's Choice Awards (w kategorii biblioteka — na-
rzędzie programistyczne), w 1998 r. wygrał z ColdFusion CNet Builder.com Product
Awards (w kategorii najlepsze narzędzie skryptowe). Kombinacja PHP 3/MySQL wy-
grała w konkursie Database of the Year na Web '98. Nieźle, jak na program bez działu
PR, bez reklam i bez agencji reklamowej.
Historia PHP
Rasmus Lerdorf— programista, członek zespołu Apache —jest twórcą i siłą napędową
PHP. Pierwszą część PHP napisał na własny użytek w 1994 roku. Był to interfejs Perl
CGI, który pomagał śledzić, kto odwiedza stronę domową. W następnym roku, w od-
powiedzi na żądania użytkowników, którzy zaangażowali się w ten projekt, skompleto-
wał pakiet nazwany Personal Home Page Tools (znany również jako PHP Construction
Kit). Niebawem została wydana wersja 2. o nazwie PHP/FI, zawierająca Form Inter-
preter, narzędzie do przetwarzania zapytań SQL.
W połowie 1997 r. około witryn na całym świecie 50 000 używało PHP. Obsługa prze-
kroczyła możliwości jednej osoby, nawet tak energicznej jak Rasmus. Niewielki zespół
programistów rozpoczął projekt na zasadzie open source, korzystając z pomocy pro-
gramistów i użytkowników z całego świata. Dwóch izraelskich programistów, Zeev Su-
raski i Andi Gutmans (autorów analizatora składni do PHP 3 oraz PHP 4), rozszerza
i uogólnia go pod szyldem Zend.com (od ich imion, Zeev i Andi).
W czwartym kwartale 1998 r. nastąpił gwałtowny rozwój PHP, który korzystając z za-
sady open source cieszył się masowym zainteresowaniem. W październiku 1998 r. około
100 000 różnych domen używało w jakiś sposób PHP. Rok później przełamana została
bariera l 000 000 domen. W czasie pisaliśmy tę książkę, liczba ta eksplodowała do
około dwóch milionów.
Rozdział 1. » Dlaczego PHP?_______________________________________31^
Dla porównania w tabeli 1.1 zamieściliśmy średnie ceny detaliczne podobnych pro-
duktów.
Tabela 1.1.
Porównanie wydatków
W świecie serwerów idea open source oddziały wuj e jeszcze silniej. Takie produkty nie
tylko konkurują z komercyjnymi, ale wydaje się, że są poza konkurencją. Nie musisz
nam wierzyć! Jeżeli nie jesteś przekonany, więcej dowiesz się na witrynach:
http://www. opensource. org
http://www.fsf.org
Licencja PHP
Schemat licencjonowania typu open source i Free Software gwarantuje, że program jest
bezpłatny. Schemat ten jest najbardziej znany pod nazwą GPL (Gnu General Public Li-
cense) lub „copyleft". PHP był rozprowadzany na zasadach licencji GPL i własnej — do
wyboru przez każdego użytkownika. Ostatnio jednak całość programu rozprowadzana
jest na podstawie liberalnej licencji PHP 4, a Zend, jako osobny produkt, jest dystrybu-
owany na zasadach licencji Q Public license (klauzula jest stosowana, gdy Zend zosta-
nie oddzielony od PHP i ktoś będzie chciał go sprzedawać).
Zwykle użytkownicy programów open source mogą wybrać optymalną wersję spośród
różnych propozycji: bezpłatnie i bez gwarancji, drogie, ale z świetnym serwisem lub
coś pomiędzy. Nie ma jeszcze zorganizowanego serwisu ani wsparcia dla PHP (przy-
puszczalnie będzie to jeden z dodatków do Zend).
Musimy jednak ostrzec: „łatwy" może oznaczać co innego dla różnych użytkowników.
Dla niektórych projektantów WWW oznac/a środowisko graficzne, używające technik
„przenieś i upuść" lub „dostaniesz, co widzisz". Aby być naprawdę wydajnym, musisz
umieć ręcznie tworzyć strony HTML. Można oczywiście używać narzędzi WYSIWYG
do zaprojektowania witryny, sformatowania stron i dodania interakcji z użytkownikiem
przed dodaniem kodu PHP do źródeł. Są również sposoby, które opisujemy w rozdziale
3., pozwalające dodać funkcje PHP do ulubionego środowiska projektowania. Jednak
nie można w pełni korzystać z możliwości PHP bez patrzenia na kod źródłowy.
Powtórzmy jeszcze raz, że PHP jest łatwy. Jest tylko nieco bardziej wymagający niż
HTML, ale chyba łatwiejszy niż JavaScript czy ASP i dużo mniej złożony koncepcyjnie
niż JSP.
Gdy klient zażąda tej strony, serwer WWW przetworzy ją. Oznacza to, że przejrzy ją od
początku do końca, szukając sekcji PHP, które spróbuje przetworzyć. Analizator składni
pobierze wszystkie przypisane zmienne (oznaczone znakiem $) i spróbuje wstawić je do
późniejszych wyrażeń (w tym przypadku do funkcji print ( ) ) . Jeżeli wszystko się uda,
preprocesor zwróci normalną stronę WWW do przeglądarki klienta (rysunek 1.1).
Rysunek 1.1.
Wynik
przetworzonego PHP
Jeżeli zerkniesz do kodu źródła strony w przeglądarce, powinieneś zobaczyć coś takiego:
<HTML>
<HEAD>
<TITLE> Powitanie</TITLE>
</HEAD>
<BODY>
<P> Cześć, Ms. Hari. Czy mogę nazywać cię Mata?</P>
</BODY>
</HTML>
Brak kompilacji
Prawdopodobnie najlepszą rzeczą w językach skryptowych jest to, że nie muszą być
kompilowane do postaci binarnej przed ich testowaniem i użyciem — po prostu napisz
i uruchom. PHP jest interpretowany (jak prawie wszystkie najnowsze języki programo-
wania), jednak Zend wykonuje czasami ukrytą prekompilację do zwiększenia szybkości
działania skryptów.
Rozdział 1. * Dlaczego PHP?_______________________________________35^
A może potrzebujesz kompilacji? Może być ona użyteczna, gdy chcesz rozprowadzać
kod w postaci binarnej tak, aby inni mogą go używać nie mając dostępu do źródła. Ze-
spół Zend pracuje nad kompilatorem, który na to pozwoli, usprawniając przy tym duże
i skomplikowane skrypty PHP.
PHP jest zgodny z trzema wiodącymi serwerami WWW: Apache HTTP Server dla
Unix i Windows, Microsoft Internet Information Server oraz Netscape Enterprise Server
(znanym również jako iPlanet Server). Działa również z mniej znanymi serwerami, jak
np.: Alex Belits fhttpd, Microsoft Personal Web Server, AOLServer oraz Omnicentrix
Omniserver. Nie działa na platformie Macintosh. Podstawowe kombinacje OS i serwera
WWW są pokazane w tabeli 1.2.
Tabela 1.2.
Systemy operacyjne i serwery WWW dla PHP
Unix Windows
Rodzaj AIX, A/UX, BSDI, Digital UNIX/Tru64, Windows 95, Windows 98,
FreeBSD, HP-UX, IRIX, Linux, NetBSD, Windows NT, Windows 2000
OpenBSD, SCO UnixWare, Solaris, SunOS,
Ultrix, Xenix i inne
Serwer WWW Apache, fhttpd, Netscape US, PWS, Netscape, Apache, Omni
Mimo że PHP nie działa jeszcze na Macintoshu, BeOS lub innych platformach, możesz
projektować w tych systemach za pomocą ulubionego narzędzia, a następnie wrzucić
skrypt PHP na serwer Unix lub Windows. Przedyskutujemy dokładnie ten proces
w rozdziale 3.
Dowcipny członek społeczności PHP powiedział: „W ColdFusion łatwe rzeczy robi się
łatwo, średnio trudne są niemożliwe". Każdy programista może to potwierdzić; gdy po-
znasz siłę nawiasów i pętli, nigdy nie wrócisz do znaczników.
36__________________________________________Część l » Podstawy PHP
PHP jest również stabilny w sensie stabilności funkcji. Zespół projektantów jest bar-
dziej zainteresowany jasną wizją projektu, a nie spełnianiem nieprzemyślanych za-
chcianek przypadkowych klientów. Większość pracy koncentruje się na ulepszeniach,
na przykład przyspieszeniu analizatora czy lepszej obsłudze sesji. Bardzo niewiele
funkcji zostało usuniętych przy zmianach wersji PHP.
W chwili obecnej PHP 4 jest dużo szybszy niż jakikolwiek skrypt CGI. Niestety praw-
dziwy jest żart, że CGI to skrót od „Can't Go Instantly" (nie może działać bez przerwy).
Mimo że wiele ze skryptów CGI jest napisanych w C, jednym z najszybszych języków
programowania, szybkość jest ograniczana obsługą każdego żądania przez nowy proces
uruchamiany przez serwer http. Czas oraz zasoby potrzebne na utworzenie i zakończe-
nie procesu są znaczne, a system może ograniczać liczbę procesów działających jedno-
cześnie. Inne skrypty CGI mogą być napisane w Perl lub Tcl i dlatego stosunkowo
powolne. Większość witryn WWW odchodzi od stosowania skryptów CGI, aby polep-
szyć wydajność i zwiększyć bezpieczeństwo.
Mimo że PHP traci nieco na wydajności, ponieważ jest interpretowany, a nie kompilowa-
ny, równoważy to działaniem jako moduł serwera. Ponieważ nie ma komunikacji pomię-
dzy osobnymi aplikacjami (jak w ColdFusion), żądania są przetwarzane dużo szybciej.
Wersja PHP dla Uniksa jest dostarczana tylko jako kod źródłowy, dlatego zespół progra-
mistów PHP nie musi rozprowadzać wersji skompilowanej dla każdego rodzaju Uniksa.
Z początku nowi użytkownicy (szczególnie ci, którzy terminują w Uniksie) stwierdzają,
że kod źródłowy jest równie użyteczny co piąte koło u wozu; większość z nich woli mi-
łe i wygodne pliki rpm. Istniejąjednak idealistyczne i pragmatyczne powody zamiesz-
czania katalogów pełnych plików . c i .h.
Pierwszą z zalet takiego rozwiązania jest to, że możesz skompilować instalację PHP
zawierającą tylko te elementy, których naprawdę potrzebujesz. Podejście to pozwala na
zwiększenie szybkości działania i poziomu bezpieczeństwa. Możesz dodać połączenia
tylko do tych baz danych, których będziesz używał. Możesz kompilować PHP na nowo
tak często, jak tylko zechcesz: gdy dostaniesz uaktualnienie zabezpieczeń do serwera
Apache lub stwierdzisz, że potrzebujesz obsługi XML.
To, co odróżnia oprogramowanie open source od konkurentów, to nie tylko cena, ale
także kontrola. Większość oprogramowania użytkowego jest obecnie rozdawana pod
różnymi warunkami. Dokładne przeczytanie licencji odkryje ograniczenia sposobu, w ja-
ki program może być używany. Być może dopuszczalne będzie korzystanie z niego tyl-
ko w domu, a nie w biurze. Możliwe, że będziesz mógł zainstalować go w twoim lapto-
pie, ale złamiesz umowę, wykorzystując program do celów zawodowych. Zwykle można
używać program w dowolny sposób, ale nie można obejrzeć kodu źródłowego, a tym
bardziej go zmieniać. Istnieją nawet „licencje grupowe" nakładające na użytkowników
warunek oddawania właścicielowi programu swoich pomysłów na ulepszenie, chociaż
używanie jest płatne!
W tabeli 1.3 zestawiliśmy różne sposoby kontroli źródeł i opłat na dzisiejszym rynku
oprogramowania.
Tabela 1.3.
Różne metody opłat i kontroli źródel
Prawdziwe programy open source, jak PHP, mogą być wykorzystywane do wszelkich
zastosowań. Znamiennym dowodem tego jest możliwość wprowadzania zmian i upo-
wszechnianie ich razem z oryginalnym programem. Skrajne przypadki określane są
mianem „rozwidlania kodu".
Sytuacja taka przywodzi na myśl inną, często zapominaną zaletę oprogramowania open
source: możesz być pewien, że oprogramowanie będzie w obiegu przez ładnych kilka
lat. W dzisiejszych czasach trudno jest wybrać produkt utrzymujący się na szczycie
przez dłuższy czas. Wielbiciele OS/2, Amigi, NeXT, Newton, Firefly, Netscape znają
uczucie osamotnienia, kiedy firma decyduje się zaprzestać wspierania technologii lub
sprzedaje ją innej firmie. My również zostaliśmy uwiedzeni i porzuceni przez inny (te-
raz przestarzały) język skryptowy. Model open source zmniejsza możliwość porzucenia
projektu i pozwala na bardziej realistyczne, długoterminowe planowanie.
Łącza do baz danych są silną stroną PHP. Zapewniają funkcje obsługi około 15 popular-
nych baz danych oraz ODBC. PHP obsługuje dodatkowo kilka popularnych protokołów,
np.: POP3, IMAP oraz LDAP, a także języka Java oraz rozproszonej architektury obiek-
towej (COM i CORBA), co umożliwia tworzenie programów o wielowarstwowej archi-
tekturze
Rysunek 1.2.
Pomiar popularności
PHP (program
Netcraft)
Mimo że trudno jest uzyskać odpowiednie wykresy, wydaje się, że PHP stoi na mocnej
pozycji w porównaniu do podobnych produktów. Technologia ASP firmy Microsoft jest
używana w około 12% serwerów WWW, ColdFusion około 4%, natomiast z PHP ko-
rzysta 12% serwerów.
PHP jest bardzo elastyczny, ponieważ jest „afirmowy". Nie jest związany z żadnym
serwerem ani systemem operacyjnym, w przeciwieństwie do Active Server Pages. Nie
jest też związany z żadnym przenośnym standardem lub warstwą pośrednią, jak Java
Server Pages lub ColdFusion, ani też z przeglądarką, językiem programowania lub bazą
danych. PHP narzuca też warunku pracy z innym oprogramowaniem.
Społeczność PHP
PHP jest stworzony i wspierany przez światową społeczność użytkowników. Jednak
„niektóre zwierzęta (główni programiści) są równiejsze od innych" — trudno się o to
spierać, ponieważ włożyli najwięcej pracy, mieli najlepsze pomysły oraz utrzymywali
kontakty z największą liczbą innych użytkowników.
Dla nowych użytkowników główną korzyścią jest bezpłatne wsparcie techniczne, bez
granic i pokrętnych odpowiedzi. Użytkownicy, na listach dyskusyjnych przez 24 godzi-
ny na dobę i 7 dni w tygodniu, odpowiadają na pytania, pomagaj ą usunąć błędy i wysłu-
chuj ą narzekań. Członkowie społeczności mogą odesłać do podręcznika, zadać pytanie
na właściwej liście dyskusyjnej lub powstrzymać twoje jęki — nigdy nie zaproponują
sformatowania dysku C i nie zażądają pieniędzy za usługę. Mogą zajrzeć do twojego
kodu źródłowego i wskazać źródło błędu lub nawet pomóc zaprojektować aplikację od
podstaw.
Gdy lepiej poznasz PHP, będziesz mógł się odwdzięczyć. Szukanie błędów, pomoc in-
nym, wysyłanie skryptów do publicznych archiwów, tworzenie dokumentacji lub pisa-
nie kodu w C i C++, oto kilka sposobów na spłacenie długu.
Podsumowanie
Mimo że PHP posiada wiele zalet, nie jest panaceum na wszystkie problemy programi-
stów WWW. Został stworzony przez programistów dla programistów i jest wspierany
przez olbrzymią społeczność. Jego główne zalety to: lekkość, siła, pewność i łatwość
użycia. Zapewnia możliwość podłączenia do olbrzymiej ilości różnych typów serwerów
usługowych. W dodatku za darmo.
Rozdział 2.
Skrypty wykonywane
na serwerze WWW
W tym rozdziale:
* Statyczne i dynamiczne strony WWW
4 Skrypty wykonywane na kliencie i na serwerze
* Wprowadzenie do skryptów wykonywanych na serwerze
Statyczny HTML
Podstawowym typem stron WWW są tekstowe strony napisane od podstaw w kodzie
HTML. Przykład takiej strony jest pokazany na rysunku 2.1.
Rysunek 2.1.
Przykład
statycznego HTML
Gdy komputer klienta zażąda tej strony z serwera poprzez Internet lub Intranet w spo-
sób pokazany na rysunku 2.2, serwer po prostu wyśle tekst strony do klienta.
Gdy dane wracają do komputera klienta, przeglądarka rysuje stronę interpretując kod, bio-
rąc pod uwagę ustawienia użytkownika, rozmiar monitora i inne czynniki. Zawartość pli-
ku HTML na serwerze jest dokładnie taka sama, jak źródło strony na komputerze klienta.
Rysunek 2.2.
Proste
żądanie HTTP
Z tych powodów statyczny HTML staje się wyznacznikiem amatorszczyzny lub rygo-
rów ideologicznych.
Kilka technologii radzi sobie z tymi ograniczeniami. Należą do nich JavaScript, Casca-
ding Style Sheet i aplety Java na końcówkach klienta oraz skrypty i połączenie z bazą
danych po stronie serwera. Pojawiły się też XML i XSL, technologie, które występują
równieżjako części innych specyfikacji (XHTML, XSLT, Xpath, ICE itd.).
Poświęć trochę czasu na zrozumienie, jaki zestaw funkcji każda z nich zawiera i która
może przydać się przy tworzeniu twojej witryny. Podstawowe pytanie, które powinie-
neś sobie zadać przy tworzeniu konkretnego zadania, to: gdzie następuje przetwarzanie,
na kliencie czy na serwerze.
44___________________________________________Część l » Podstawy PHP
Technologie
wykonywane po stronie klienta
Najwięcej rozszerzeń HTML wykorzystuje się po stronie klienta. Zawierają one: rozsze-
rzenia formatowania, jak Cascading Style Sheet i Dynamie HTML, języki skryptów wy-
konywanych na kliencie i aplety Java. Obsługa tych technologii może być wbudowana
w przeglądarkę (zadania wykonywane przez te technologie opisane są w tabeli 2.1).
Tabela 2.1.
Rozszerzenia HTML po stronie klienta
Rysunek 2.3.
Przy Mad HTML
ze skryptami
wykonywanymi
po stronie klienta
Rozdział 2. » Skrypty wykonywane na serwerze WWW________________________45
Co oznacza „dynamiczny"
Często zaznacza się podział pomiędzy „statycznymi" i „dynamicznymi" stro-
nami HTML — „dynamiczny" oznacza prawie wszystko, co wykracza ponad
czysty HTML. Termin ten jest używany do opisu funkcji klienta i serwera.
W przypadku klienta mogą to być prezentacje multimedialne, przewijające
się nagłówki, automatycznie uaktualniające się strony lub elementy, które
pojawiają się i znikają. Dla serwera termin ten oznacza, że zawartość stron
jest tworzona na bieżąco.
Jak można zauważyć w źródłach, w przykładzie tym dodano arkusze stylów i skrypty
wykonywane na kliencie oraz nieco bardziej skomplikowany kod HTML.
<HTML>
<HEAD>
<TITLE>Przewodnik TechBiz: sprzęt</TITLE>
<STYLE TYPE="text/css">
< ! --
BODY (color: black; font family: verdana; font size: 10 pt}
HI (margin-top: 10; color: white; font-family: arial; font-size: 12 pt}
H2 (margin-bottom: -10; color: black; font-family:
verdana; font-size: 18 pt}
A:link (color: #000080; text-decoration: none}
.roll ( )
A.roll:hover (color: #008080}
-->
</STYLE>
<SCRIPT LANGUAGE="JavaScript">
function ListVisit(form, i)
f
// pobranie adresu URL
var site = form.elements [i] . selectedlndex;
// jeżeli to nie jest pierwsza opcja, przejdź do niej
iflsite >= 1)
(
top.location = form.elements[i].options[site].value;
(
// and then reselect the null (it functions as a label)
form.elements[i].selectedlndex = 0;
(
//-->
</SCRIPT>
</HEAD>
<BODY>
<TABLE BORDER=0 CELLPADDING=0 WIDTH=100%>
<TR>
<TD B G G O L O R = " # F O F 8 F F " ALIGN=CENTER VALIGN=TOP W I D T H = 1 7 % >
<TABLE C E L L P A D D I N G = 5 W I D T H = 1 0 0 % >
<TR ALIGN=CENTER>
<TD B G C O L O R = " D 0 0 0 0 8 0 " >
<Hl>Przewodnik ksiazkowy<BR>TechBiz</Hl>
</TDX/TRX/TABLE>
<BR>
<A H R E F = " i n d e x . p h p " > < B > P O W R O T < / B X / A > < B R X B R >
<BR>
<FORM action="">
<SELECT NAME="industries" onChange="LisLVisit(this.form, 0)">
< O P T I O N > I n n e branże
<OPTION VALUE="software.html">Oprogramowanie
<OPTION VALUE="biotech.html">Biotechnologie
<OPTION VALUE="aerospace.html">Lotnictwo
<OPTION VALUE="telephone.html">Telefonia
</SELECTX/FORMXBR>
46__________________________________________Część l » Podstawy PHP
</TD>
<TD BGCOLOR="#FFFFFF" ALIGN=LEFT VAL1GN=TOP WIDTH-83%>
<CENTERXH3>Ksiażki o przemyśle sprzętu komputerowego
</H3X/CENTER>
<H5>Apple Computer</H5>
<UL>
<LIXA H R E F = " b o o k l . h t m l " > A p p l e < / A > : Carlton,
Jim ( 1 9 9 7 )
<LIXA HREF="book2.html">Insanely Great</A>:
Levy, Steveri (1993)
<LIXA HREF="book3.html">Odyssey: Pepsi to
Apple</A>: Sculley, John (1997)
<LIXA HREF="book4.html">Steve Jobs and the
NeXT Big Thing</A>: Stress, Randall (1993)
</UL>
<H5>Dell Computer</H5>
<UL>
<LIXA H R E F = " b o o k 5 . h t m l " > D i r e c t from Dell</A>
: Dell, Michael (1999)
</UL>
<H5>InteK/H5>
<UL>
<LIXA H R E F = " b o o k 6 . h t m l " > O n l y the Paranoid Survive</A>:
Grove, Andrew S. (1996)
<LIXA H R E F = " b o o k 7 . h t m l " > I n s i d e I n t e l < / A > : Jacksvn, Tim ( 1 9 9 7 )
</UL>
<H5>Sun Microsystems</H5>
<UL>
<LIXA H R E F = " b o o k 8 . h t m l " > H i g h N o o n < / A > : Southwick, Karen ( 1 9 9 9 )
</UL>
</TD>
</TRX/TABLE>
</BODY>
</HTML>
Niestety, najlepszą cechą technologii działających po stronie klienta jest również naj-
gorszą: są one całkowicie zależne od przeglądarki. Istnieją spore różnice w możliwo-
ściach przeglądarek, a nawet pomiędzy różnymi wersjami tej samej przeglądarki. Tak
zwany Dynamie HTML to dwie różne koncepcje skryptów, którym zdarzyło się mieć tę
samą nazwę. Można, niestety, skonfigurować przeglądarkę w sposób sprawiający kło-
poty: niektórzy wyłączają JavaScript ze względów bezpieczeństwa, co powoduje nie-
możność obejrzenia stron, które używają JavaScript do nawigacji (umyślnie zrobiliśmy
to w przykładzie powyżej).
Nie ulepsza się też przeglądarek z powodu kosztów lub kłopotów technicznych. Doświad-
czeni projektanci WWW powinni również pomyśleć o przeglądarkach tekstowych, do-
stępności i globalnym zasięgu. Nieprzypadkowo duże witryny, o masowej oglądalności,
starają się trafić do możliwie dużej widowni. Chociaż Yahoo! i Amazon nie używają ar-
kuszy stylów i JavaScript i to po ponad trzech latach od wprowadzenia tych standardów.
Mimo nalegań World Wide Web Consortium wiele witryn uparcie trzyma się znaczników
FONT i atrybutu BGCOLOR jako jedynego sposobu dotarcia do klientów używających
AOL 3.0 w pięcioletnich komputerach Macintosh z 13-calowymi ekranami. Opór użyt-
kowników przed uaktualnieniem jest przekleństwem projektantów rozwiązań wykorzy-
stujących technologie klienckie. Ironicznie można podsumować to w sposób następujący:
po pięciu latach gwałtownego rozwoju Sieci jedyną rzeczą, której projektant może być
pewien jest fakt, że klient na ekranie monitora zobaczy czysty tekstowy HTML (lub raczej
podzbiór HTML, który jest powszechnie obsługiwany i przetrwał próbę czasu).
Rozdział 2. » Skrypty wykonywane na serwerze WWW_______________________47_
Aplety Java
Aplety Java, znane również pod nazwą „Java na stronie klienta", są uważane
za mniej zależne od przeglądarki niż inne technologie klienckie. Jak sugeruje
nazwa, są to małe aplikacje, które są przesyłane przez Internet. Jednak za-
miast współdziałać bezpośrednio z systemem operacyjnym, jak aplikacje
napisane w innych językach programowania, aplety Java działają na war-
stwie pośredniczącej, zwanej maszyną wirtualną Javy (JVM). Maszyna wirtu-
alna może być rozumiana jako system operacyjny działający na prawdziwym
systemie operacyjnym maszyny. Najnowsze przeglądarki posiadają maszynę
wirtualną Javy, ale także można ściągnąć ją oddzielnie. Taki podział zadań
pozwala apletom używać możliwości przeglądarki bez ograniczania przez jej
relatywnie ubogi zakres funkcji.
Aplety ucierpiały z powodu opinii o ich niewielkiej przydatności, ponieważ na
początku były używane do tworzenia trywialnych „tańczących literek": logo,
które wygląda jak zrobione z żelatyny, przewijane nagłówki, skaczące połą-
czenia i inne przyprawiające o ból głowy błahostki.
Rysunek 2.4.
Zadania na serwerze
Skrypty dla serwera składają się z dwóch głównych elementów: języka skryptowego i ma-
szyny skryptowej (która może, ale nie musi być wbudowana w serwer WWW). Maszyna
skryptowa analizuje i interpretuje strony zapisane w znanym jej języku. Często obie części
są stworzone przez tę samą firmę lub zespół i mogą być używane tylko razem. Przykła-
dem tego jest zarówno ColdFusion, jak i PHP 3. Istnieją jednak wyjątki od tej reguły. Java
Server Pages są napisane w zwykłym języku programowania, a nie w specjalnym języku
skryptowym. Stworzono kilka maszyn skryptowych wykorzystujących ten język, np.
Allaire JRun, Apache JServ. Teoretycznie ASP pozwala na użycie dowolnego języka
skryptów i kilku z maszyn skryptowych zgodnych z ActiveX (jednak w praktyce proble-
matyczne jest użycie czegoś innego niż kombinacja NT, US, VBScript, JScript). PHP jest
również dwuczęściową technologią skryptową, ponieważ maszyna skryptowa (Zend) jest
oddzielona od języka programowania.
Rysunek 2.5 pokazuje prosty przykład skryptów dla serwera. Strona tworzona jest na
bieżąco na podstawie bazy danych i źródła interpretowanego przez serwer, wynik jest
przesyłany do klienta. W przykładzie poniżej załączyliśmy również odwołania do bazy
danych (nie zajmowaliśmy się nimi do tej pory) i pozostawiliśmy dołączane pliki, po-
nieważ zadaniem tego przykładu jest przedstawienie wyniku działania PHP, a nie goto-
wego kodu.
Rysunek 2.5.
Przyktad skryptów
dla serwera
<BODY>
<?php include{"thhg-navbar.txt"); ?>
<TD BGCOLOR="(tFFFFFF" ALIGN=LEFT VALIGN=TOP WIDTH=83%>
<TABLE CELLPADDING=5 WIDTH=100%?<TRXTD ALIGN=LEFT
VALIGN=MIDDLE>
<H2>Książki o <?php print("$co") ; //przekazany poprzez URL
?></H2XBR></TDX/TR>
<TR><TD WIDTH=50% ALIGN=LEFT>
<?php
$dbh = mysql_connect('localhost', 'Joyce') or
die("Błąd otwarcia bazy danych");
mysql_select_db("techbizbookguide") or die("Błąd otwarcia bazy danych");
S fquery = "SELECT Blurb FROM Company WHERE CompanyName = '$co'";
Sqresult = mysql_query($query) or die(mysql_error());
$blurb = mysql_fetch_array($qresult} or die(mysql_error(});
print("$blurb[0]");
?>
</TDX/TR>
<TRXTD ALIGN=LEFT>
<TABLE BORDER=1 CELLPADDING=3>
<?php
Squery2 = "SELECT ID, Title, AuthorFirst, AuthorLast FROM
bookinfo WHERE CompanyName='$co' ORDER BY AuthorLast";
$qresult2 = mysql_query{$query2} or die(mysql_error());
while($titlelist = mysql_fetch_array($qresult2))
(
$bookID = Stitlelist[0];
$title = Stitlelist[1];
$authorfirst = $titlelist[2];
Sauthorlast = $titlelist[3];
print ("<TRXTDXA HREF=\"book . php?bn=$bookID\" class=\"roll\">
Stitle</Ax/TDXTD>Sauthorfirst
Sauthorlast</TD>") ;
)
?>
</TRX/TABLE>
</TDX/TRX/TABLE>
</TDX/TRX/TABLE>
<BODYX/HTML>
_50__________________________________________Część l » Podstawy PHP
</TRX/TABLE>
</TDX/TRX/TABLE>
</TDX/TRX/TABLE>
</BODYX/HTML>
Jak można się zorientować z podanych przykładów, nie ma możliwości obejrzeć skryp-
tów dla serwera z końcówki klienta. Tworzenie strony jest realizowane przed wysłaniem
jej do klienta. Po wyjściu z serwera WWW strona wygląda jak zwykła strona HTML.
Oznacza to, że nie możesz powiedzieć, jakiego użyto języka skryptowego, chyba że ist-
nieje taka informacja w nagłówku. Skrypt pokazany w ostatnim przykładzie był napisa-
ny w PHP dla bazy danych MySQL. Współpraca pomiędzy tymi dwoma programami
zostanie opisana w drugiej części tej książki.
Istnieje jedna dziedzina zastosowań, w której serwer nie potrafi pomóc: trójwymiarowe
gry zręcznościowe. Im krótszy wymagany czas odpowiedzi, tym mniej użyteczny jest
do tego PHP. W chwili obecnej Sieć jest zbyt powolna na takie zastosowania (jednak
pionierzy szerokiego pasma starają się to zmienić).
Wystarczy tej retoryki! Masz teraz jasny opis różnic pomiędzy technologiami dla ser-
wera i dla klienta, więc należy je zastosować w praktyce. Pokażemy teraz, jak zainsta-
lować i skonfigurować PHP (możesz też znaleźć kogoś, kto zrobi to za ciebie...).
Podsumowanie
Aby wiedzieć, jakie zadania można wykonać za pomocą PHP (lub innego języka
skryptowego serwera), musisz dokładnie poznać granicę pomiędzy pracą na serwerze
i na komputerze klienta. W rozdziale tym przedstawiliśmy przykłady zwykłego statycz-
nego HTML, HTML z dodanymi rozszerzeniami CSS i kodem JavaScript, umożliwia-
jącym interakcję z klientem, a także wygenerowaną przez PHP stronę, przedstawioną
zarówno z punktu widzenia serwera, jak i klienta.
Zalety
Dzierżawa serwera ma wiele zalet. Dostawca będzie zarządzał (przynajmniej w teorii)
wieloma ważnymi szczegółami niezbędnymi do utrzymania witryny w ruchu, takimi jak:
* sprzęt;
* uaktualnianie oprogramowania;
* rejestracja domeny, adresowanie IP, DNS;
* serwer poczty (POP/IMAP i SMTP);
* przepustowość łącza;
* podtrzymanie napięcia;
* archiwizacja;
** bezpieczeństwo.
Hakerzy, brak prądu czy pogięte taśmy backupu nie są już twoim zmartwieniem. To
pracownik dostawcy usług internetowych dostaje informację, że coś złego dzieje się
z t woj ą witryną, i musi się zająć kłopotem.
Dzierżawa serwera jest w wielu przypadkach bardzo korzystna finansowo. PHP na Li-
nuksie lub którymś BSD jest tani i dostępny. W chwili obecnej niewielu dostawców
oferuje PHP na platformie NT (zwykle drożej).
Wady
Ale dzierżawa serwera ma też swoje wady...
Większość jest związana z kontrolą. Jeżeli korzystasz z usług dostawcy, jesteś gościem
i musisz dostosować się do jego zasad (mimo, że płacisz i wymagasz).
Głównym problemem związanym z PHP jest sposób jego pracy — moduł serwera lub
CGI. PHP działa lepiej jako moduł, jest wtedy wbudowany w serwer, a nie jest osob-
nym procesem. Prawie wszyscy wolą uruchamiać PHP jako moduł, jednak dostawcy
niekiedy uruchamiają go jako CGI, ponieważ wtedy łatwiej nim bezpiecznie admini-
strować. Są oczywiście dostawcy oferujący PHP działający jako moduł serwera, ale są
to zwykle mniejsze firmy oferujące niższą przepustowość łącza. Istnieje jeszcze kilka
innych sytuacji, w których oczekiwania użytkowników pozostają w konflikcie z wygo-
dą dzierżawy serwera.
Zwykle sprawdza się zasada: im mniej wyszukane żądania, tym bardziej możliwe i od-
powiednie jest wydzierżawienie serwera. Najgorsze jest jednak to, że na ten sam serwer
wpycha się coraz więcej witryn, czy tego chcesz, czy nie.
Istnieje kilka czynników, które mogą spowodować duże kłopoty w znalezieniu odpo-
wiedniego dostawcy.
Rozdział 3. » Rozpoczynamy pracę z PHP______________________________57
Szczegóły
Gdy wybierzesz już sposób, w jaki będziesz chciał udostępnić witrynę, możesz wybrać
spośród wielu ofert obecnych na rynku. Początkujący klienci powinni uważać. Słowo
ISP może dziś znaczyć prawie wszystko. W tabeli 3.1 zestawiliśmy specjalizację róż-
nych dostawców oraz ich potencjalnych klientów (firmy wymienione w tabeli są tylko
przykładami, nie popieramy tych firm, ani nie rekomendujemy ich usług).
Mimo, że znalezienie dobrego dostawcy Internetu czasami wydaje się tak samo trudne,
jak znalezienie partnera na całe życie, istniej ą witryny WWW ułatwiające to zadanie:
http://hosts.php. net
http://www. od-site. com/php
http://www. ispcheck. com
Zwróć uwagę na komentarze użytkowników, zarówno na te pozytywne, jak i negatyw-
ne. Spytaj przyjaciół i kolegów o opinie. Przeszukaj archiwa list dyskusyjnych — cza-
sami zdarzają się rekomendacje oraz opisy niedobrych doświadczeń.
Tabela 3.1.
Różni dostawcy Internetu
końca życia: „O jakim życiu myślisz?" Analogicznie, pasmo, jakiego nigdy nie zajmie
witryna miłośników poezji epickiej Joego, nie będzie już wystarczające dla witryny ofe-
rującej bezpłatne transmisje video. Przed zawarciem kontraktu sprawdź, czy nie wpad-
niesz w pułapkę.
Jednak prowadzenie serwera wymaga o wiele więcej pracy i może być nieco droższe,
szczególnie w przypadku małych i średnich witryn. Samodzielnie udostępniana witryna
świadczy o twoich umiejętnościach. Jeżeli nikt z twojego otoczenia nie zna się dobrze
na zagadnieniach bezpieczeństwa, możesz się spodziewać problemów.
Rozdział 3. » Rozpoczynamy pracę z PHP______________________________59
Mówiąc bardziej ogólnie, nie będziesz miał kogo winić, jeżeli coś pójdzie źle. Jeżeli
możesz codziennie spojrzeć w lustro i powiedzieć: „To wszystko moja zasługa i dobrze
mi z tym", masz dość pewności siebie, aby prowadzić własny serwer.
Rozwiązania pośrednie
Pomiędzy własnym serwerem a wynajmowaniem witryny rozciąga się spektrum możli-
wości. Istnieje kilka kompromisowych rozwiązań czerpiących najlepsze cechy obu po-
wyższych.
Kolokacja
Kolokacja polega na zakupieniu własnego komputera i wstawieniu go do pomieszczenia
dostawcy, gdzie zostanie wpięty do sieci i monitorowany. Jesteś odpowiedzialny za za-
kup sprzętu, licencji, zabezpieczenie, instalowanie, konfigurację oraz utrzymywanie
oprogramowania i sprzętu, oprócz UPS-a. Jeżeli serwer będzie wymagał konfiguracji,
będziesz musiał skonfigurować go sam lub zapłacić grube pieniądze za godzinę pracy
serwisanta. Jeżeli zdecydujesz się na kolokację, przygotuj się na to, że serwisant nie bę-
dzie miał żadnego doświadczenia z produktami, które zainstalowałeś na serwerze.
Serwer dedykowany
Dedykowany serwer jest dokładnie tym, co wskazuje nazwa. Dostawca kupuje maszy-
nę, konfiguruje ją według twojego życzenia (za twoje pieniądze oczywiście), włączają
do sieci i od tej pory każdy cykl procesora należy do ciebie. Dostawca zapewnia zwykle
wsparcie techniczne. Jest to bezpieczniejsze od dzierżawy i korzystne finansowo dla
średnich i dużych witryn.
Instalowanie PHP
Jeżeli decydujesz się wydzierżawić kompletne środowisko programowania, możesz bez
obawy opuścić resztę tego rozdziału (dotyczy tylko własnego serwera).
60__________________________________________Część l » Podstawy PHP
Zanim zaczniesz
Przed rozpoczęciem instalacji PHP na jakiejkolwiek platformie będziesz potrzebował:
* komputera z wystarczającą ilością pamięci dla systemu operacyjnego;
* zainstalowanego systemu Unix lub Windows 95/98/NT/2000;
* działającego połączenia z Internetem, jeżeli jest to witryna robocza lub instala-
cja w Intranecie do tworzenia witryny. Nie potrzebujesz połączenia, jeżeli jest
to całkowicie oddzielna instalacja.
Pomoc na wstępnym etapie wykracza poza ramy tej książki. Możesz zajrzeć do nastę-
pujących witryn, aby zasięgnąć informacji:
http://164.109.153.102/idgsearchresult.asp?searchtype=2?keyword=networking
http://www. linuxdoc. org/HO WTO/HOWTO-INDEX/howtos. html
* bison i flex (wpisz „find . -name bison -print" oraz „find . -name
flex -print" z katalogu /usr, aby sprawdzić, czy masz zainstalowane te
programy, lub pozwól gcc, aby sprawdził w trakcie instalacji, czy są dostępne.
Jeżeli nie masz tych programów, możesz skopiować bison z www.gnu.org/soft-
ware/bison, a flex z ftp://ftp.ee. lbl.gov);
* ścieżki do pliku konfiguracyjnego HTTP. Do niedawna były to access, conf,
httpd.conf\ srm.conf dla serwera Apache, ale w tej chwili są połączone w jeden
plik httpd.conf. Zależy to od systemu operacyjnego, dystrybucji, serwera WWW.
Możesz użyć narzędzia Fin d, aby je odszukać;
* ścieżki do plików źródłowych Apache (zwykle /usr/local/apache_1.3.x);
* ścieżki do demona Apache znanego jako httpd (zwykle /usr/local/bin).
Pamiętaj, że programy, z którymi PHP będzie się łączył, muszą być za-
instalowane przed jego kompilowaniem. Baza danych jest najbardziej
znanym typem serwera zewnętrznego. Innym przykładem jest bibliote-
ka BCmath, serwer IMAP, biblioteka mcrypt czy analizator XML Expat.
Różnica pomiędzy kompilacją dla Apache a kompilacją jako CGI jest bardzo niewielka.
Właściwie polega na ustawieniu opcji -with-apache lub -with-apxs w trakcie
konfiguracji. Wielu użytkowników dla wygody kompiluje jednocześnie wersję dla
Apache i CGI.
Procedura instalacji
Z powodu niezależności PHP można zastosować wiele specyficznych metod instalacji,
zbyt wiele, aby je tu wszystkie wymienić. Spróbujemy opisać tylko te, które uważamy
za najbardziej popularne.
Instalacja PHP pod Windows NT/2000 z serwerem IIS jest bardzo prosta. Należy pa-
miętać, że zasad stosowanych dla NT/2000 nie stosuje się dla Windows 95/98, i od-
wrotnie. W dalszej części rozdziału zamieściliśmy uwagi o instalacji pod 95/98, ta część
jest poświęcona Windows NT/2000.
1. Rozpakuj archiwum używając narzędzia typu unzip. Umieść pliki w katalogu
C:\PHP.
2. Skopiuj p\\kphp.ini-dist do katalogu Windows i zmień jego nazwę naphp.ini.
3. Przejdź do katalogu System32, który znajduje się w katalogu Windows. Powi-
nieneś mieć wiele plików DLL. Poszukaj pliku msvcrt.dll, jeżeli go nie ma, sko-
piuj go z katalogu PHP. Skopiuj również p\ikphp4ts.dll do katalogu System32.
j62_______________________________________Część l » Podstawy PHP
Unix i Apache
Za pierwszym razem gdy będziesz tworzył demona HTTP ze źródeł, możesz mieć pew-
ne obawy. Proces ten jest jasny; warto podjąć wysiłek kompilacji serwera WWW, za-
miast zależeć od pliku RPM stworzonego przez kogoś innego. Poza tym własny serwer
jest zwykle szybszy.
Tych, którzy już kompilowali wcześniejsze wersje PHP, informujemy, że procedura jest
identyczna, ale trwa dłużej.
Instalacje Red Hat lub Mandrake Linux mogą być dostarczane z Apa-
che i PHP w postaci plików RPM. Musisz usunąć je przed rozpoczę-
ciem kompilacji własnego PHP! Możesz dodatkowo mieć wersje RPM
innych serwerów, takich jak MySQL lub PostgreSQL, które instaluje
się inaczej, niż ich odpowiedniki w postaci źródeł. Jeżeli napotkasz
problemy, wyrzuć pliki RPM i zainstaluj program ze źródeł.
1. Rozpakuj programem unzip i tar dystrybucję Apache, jeśli jeszcze tego nie
zrobiłeś. Umieść je w /usr/local, jeżeli nie ma do tego przeciwwskazań.
gunzip -c apache_l.3.x.tar.gz
tar -xf apache_l.3.X.tar
cd apache_1.3.x
./configure
dów, abyś jej nie używał. Jeżeli jest to konieczne, zamień /usr/local/bin na
właściwą ścieżkę do twojego demona HTTP. Jeżeli jesteś pewien, że nie masz
httpd, możesz po prostu wykonać make install. W środowiskach, w których
wymagana jest wysoka dostępność, możesz wstrzymać wykonanie tego etapu
aż do zakończenia konfigurowania PHP i Apache (punkty 7. i 8.), co spowo-
duje chwilowe przerwanie funkcjonowania witryny WWW.
cd src/support
./apachectl stop
cd . .
cp httpd /usrlocal/bin
11. Uruchom edytor tekstu. Wpisz <?php phpinf o ( ) ; ?>. Zapisz plik w głównym
katalogu z dokumentami serwera WWW jako info.php. Uruchom przeglądarkę
i wczytaj utworzony plik. Musisz wczytać go, podając URL (www.testdomain.
com/info.php) lub wywołanie HTTP (http://localhost/info.php), a nie nazwę
pliku (/home/httpd/html/info.php). Powinieneś zobaczyć długą tabelę z infor-
macjami o nowej instalacji PHP 4. Gratulacje.
Windows 95/98 nie był projektowany jako platforma dla serwerów. Aby zainstalować
serwer WWW i maszynę skryptową, należy dosyć głęboko „wkopać się" w rejestr. Je-
żeli coś pójdzie źle, system nie zadziała. Jeżeli zaś zdecydujesz się usunąć PHP z kom-
putera, nie będzie to prosta operacja usunięcia plików — należy przywrócić pierwotną
postać zapisów w rejestrze. Istnieje skrypt instalujący PHP, ale nie ma skryptu usuwają-
cego go z systemu.
Jeżeli decydujesz się na tę instalację mimo naszych ostrzeżeń, postępuj zgodnie z nastę-
pującymi wskazówkami.
1. Jeżeli pracujesz z Windows 95, zainstaluj poprawkę DCOM.
2. Rozpakuj archiwum zip i umieść jego zawartość w katalogu C:\PHP.
3. Skopiuj plik php.ini-dist z katalogu PHP do katalogu Windows i zmień jego
nazwę na php. ini. Plik musi być skopiowany przed wykonaniem następnych
czynności, inaczej instalacja się nie uda.
4. Przejdź do katalogu System, który znajduje się w katalogu Windows. Zoba-
czysz sporo plików DLL. Sprawdź, czy znajduje się tutaj plik msvcrt.dll; jeżeli
go nie ma, skopiuj go z katalogu PHP. Skopiuj również do katalogu System
p\ikphp4ts.dll.
5. Otwórz plik php.ini. Zmień ustawienie extension_dir na c:\php (tak, jak
nazwałeś katalog z PHP). Ustaw zmienną doc_root, aby wskazywała na
główny katalog dokumentów serwera WWW (c:\webroot). Usuń komentarze
przy właściwych wierszach extension=php_* .dli, aby załadować dodatko-
we moduły.
6. Wskaż PWS, gdzie znajdzie moduł ISAPI. Aby to zrobić, dodaj ścieżkę ISAPI
do pliku PWS-php4.reg, który znajdziesz w plikach dostarczanych z instalacją
PHP (czytaj podwójny backslash jak pojedynczy). Jeżeli zainstalowałeś PHP
w innym katalogu niż c:\php, musisz zmienić ścieżkę w pliku .reg
".php"=C:\\PHP\\php4isapi.dli"
fhttpd:
http://ww\v.fhttpd. org/www/install.html
Omni:
http ://www. umcsd. k!2. or. us/php/win32install. html
Narzędzia programistyczne
PHP nie posiada luksusowego graficznego środowiska programistycznego, z kreatorami,
polami wyboru czy ikonami. Jeżeli takie rzeczy są dla ciebie istotne, możesz używać
edytora typu WYSIWIG do formatowania stron, a następnie ręcznie dodawać funkcje
PHP. Wadą takiej strategii może być nieczytelność kodu napisanego przez program.
Wydaje się, że większość użytkowników PHP woli używać edytorów tekstowych. Pro-
gramy te powinny zapewniać minimum pomocy, na przykład podświetlanie składni, od-
szukiwanie nawiasów, domykanie znaczników. Większość z tych opcji pozwala na
zmniejszenie liczby pomyłek i działa jako podręczna ściągawka, nie tworząc pliku za ciebie.
Pamiętaj, że program ten nie musi znajdować się na tej samej maszynie co serwer. Jest
to szczególnie przydatne, jeżeli używamy Uniksa, w którym (parafrazując Blues
Brothers) „mamy oba rodzaje edytorów: emacs i vi". Macintosh, BE i Windows posia-
dają szeroką gamę sprytnych i przyjaznych edytorów tekstu. Patrząc z drugiej strony,
Unix ułatwia obsługę wielu systemów klienckich. Wielu programistów korzysta z zalet
obu rozwiązań.
Keith Edmunds prowadzi długą listę edytorów obsługujących PHP, wiele z nich można
uzyskać bezpłatnie albo za niewielką kwotę:
http:'//www. itworks. demon. co. uk/phpeditors. htm
Rozdział 3. « Rozpoczynamy pracę z PHP________________________________67^
Tabela 3.2.
Znane edytory PHP dla różnych platform
Podsumowanie
Zanim będziesz mógł używać PHP, musisz zdecydować, czy będziesz utrzymywał ser-
wer WWW, czy go wydzierżawisz, czy wreszcie wybierzesz jakieś kompromisowe
rozwiązanie, np. kolokację. Wybór determinują: koszt, rozmiar i wielkość ruchu gene-
rowanego przez witrynę, nietypowe wymagania sprzętowe lub programowe oraz rodzaj
prezentowanych stron. Najlepszym wyjściem dla małej witryny bez specjalnych wyma-
gań jest dzierżawienie serwera.
Jeżeli zdecydowałeś się na uruchomienie własnego serwera, masz już informacje o naj-
częściej używanych platformach. PHP 4 obsługuje wiele serwerów WWW, ale opis
wszystkich instalacji nie był dostępny.
Wszystko, co jest prawidłowym kodem HTML u klienta jest zgodne z PHP. PHP nie
przejmuje się fragmentami w JavaScript, wywołaniami dźwięków i animacji, apletów
i innych elementów działających u klienta. PHP ignoruje te fragmenty, a serwer po pro-
stu przesyła całość do klienta.
Powinno być już jasne, że możesz użyć dowolnej metody tworzenia strony WWW i do-
dawania do niej kodu PHP. Jeżeli pracujesz nad stronami w grupie, używając dużych
multimedialnych pakietów, możesz nadal tak pracować. Nie potrzebujesz radykalnej
zmiany narzędzi czy przebiegu pracy — rób to, co do tej pory, a na koniec dodaj do ko-
du skrypty serwera.
70__________________________________________Część l » Podstawy PHP
Wszystko pomiędzy tymi znacznikami jest traktowane przez moduł lub CGI jako PHP.
Tekst na zewnątrz sekcji PHP nie interesuje serwera i po prostu jest przesyłany do
klienta bez względu na to, czy jest to HTML, JavaScript lub cokolwiek innego.
Można stosować cztery rodzaje znaczników PHP, według własnego uznania i wygody,
w różny sposób uzasadniając ich użycie.
Jeżeli używasz takich znaczników, możesz być pewien, że znaczniki te będą zawsze
poprawnie zinterpretowane. Jeżeli nie musisz stosować innego stylu znaczników, najle-
piej używać tego sposobu.
Musisz używać tych znaczników, jeżeli stosujesz XML w PHP, ponieważ XML korzy-
sta z krótkich znaczników otwierających.
To najkrótsza z możliwych opcji. Jeżeli często przełączasz się pomiędzy HTML i PHP,
styl ten może kusić mniejszą liczbą uderzeń w klawiaturę. Jednak cena stosowania tych
znaczników może być wysoka. Musisz wykonać jedną z trzech czynności, aby PHP
rozpoznawał takie znaczniki:
1. Wybierz opcję -enable-short-tags w trakcie konfigurowania przed kom-
pilacją PHP.
2. Uaktywnij ustawienie short_open_tag w pliku php.ini. Musisz je wyłączyć,
aby używać XML z PHP.
3. Użyj funkcji short_tags ( ) .
Krótkie znaczniki nie były obsługiwane przez wersję beta PHP 4. Zmusiło to wielu pro-
gramistów do konwersji plików do postaci zgodnej ze znacznikami kanonicznymi, być
może tylko tymczasowej.
Rozdział 4. » Dodajemy PHP do HTML__________________________________71^
Użytkownicy FrontPage często wybierają ten rodzaj znaczników. Aby można było ich
używać, należy włączyć opcję konfiguracyjną w pliku php.ini. Oczywiście jeżeli uży-
wasz znaczników w stylu ASP i rozszerzenia .asp (np. konwertując witrynę z ASP do
PHP), musisz wyłączyć ASP na serwerze IIS.
Witaj świecie
Jesteśmy gotowi do napisania pierwszego programu w PHP. Otwórz nowy plik w ulu-
bionym edytorze i wpisz:
<HTML>
<HEAD>
<TITLE>Pierwszy program w PHP</TITLE>
</HEAD>
<BODY>
<?php print("Witaj okrutny świecie");?>
</BODY>
</HTML>
Większość przeglądarek nie wymaga niczego poza sekcją PHP. Jednak dobrym pomy-
słem jest stosowanie kompletnej struktury HTML, do której wbudowujemy PHP.
Jeżeli nie uzyskasz czegoś podobnego do rysunku 4.1, najprawdopodobniej zaistniał ja-
kiś błąd podczas konfiguracji lub instalacji.
Przeczytaj jeszcze raz rozdział 3., opisujący instalację, i rozdział 31., traktujący o opcjach
konfiguracyjnych. W rozdziale 15. rozpoznamy niektóre częste problemy i opiszemy
wskazówki na temat uruchamiania programów.
72 Część l » Podstawy PHP
Rysunek 4.1.
Pierwszy skrypt PHP
Możesz błyskawicznie przełączać się do trybu PHP na tak długo i tak często, jak po-
trzeba. Na przykład:
<?php Sid=l; ?>
<FORM METHOD="POST" ACTION="registration.php">
<P>Imię:
<INPUT TYPE="TEXT" NAME="firstname" SIZE 20>
<P>Nazwisko:
<INPUT TYPE="TEXT" NAME="lastname" SIZE 20>
<P>Pozycja:
<INPUT TYPE="TEXT" NAME="rank" SIZE 10>
<INPUT TYPE="HIDDEN" NAME="serial_number" VALUE="<?php
print("Sid");?>">
<INPUT TYPE="SUBMIT" VALUE="INPUT">
</FORM>
Dołączanie plików
Innym sposobem na dodanie kodu PHP do HTML jest umieszczenie kodu w oddziel-
nym pliku i wywołanie go za pomocą funkcji include. Na przykład plik o nazwie
dziewczyna.inc zawiera tylko:
<?php $dziewczyna="Ilona";
print ("$dziewczyna") ; ?>
Wynik pokazano na rysunku 4.2 (jeżeli twoje uczucia się zmienią, wystarczy zmienić
jedną zmienną w pliku dziewczyna.inc).
Rysunek 4.2.
Wynik
dołączenia pliku*
Funkcja include przekazuje po prostu zawartość pliku jako tekst. Wielu użytkowni-
ków myśli, że skoro funkcja include jest wykonywana w bloku PHP, dołączany tekst
jest również w trybie PHP. Nieprawda! Serwer przełącza się w tryb HTML przy dołą-
czaniu każdego pliku i po cichu wraca do trybu PHP na jego końcu.
Aby upewnić się, że nie zdarzy się taka sytuacja, należy zapamiętać, że każda część do-
łączanego pliku, która ma być wykonana jako PHP, musi być opatrzona prawidłowymi
znacznikami PHP.
Ponieważ include tylko przekazuje tekst, a nie kod PHP, może być stosowane do wsta-
wiania fragmentów HTML. Możesz np. umieścić informację o prawach autorskich
w pliku tekstowym i używać na każdej stronie funkcji include. Spowoduje to, że
uaktualnianie informacji o prawach autorskich jest szybsze i mniej nużące.
Podsumowanie
PHP łatwo można wbudowywać w istniejący kod HTML. Możesz użyć dowolnej ulu-
bionej metody tworzenia HTML, a następnie dodać sekcje PHP. Dodany kod PHP może
wykonywać wiele zadań, od wypisania liczby, aż do stworzenia sporego fragmentu kodu.
Każdy blok PHP, dowolnej długości, jest ograniczony znacznikami. Istnieje kilka sty-
lów znaczników PHP; początkujący użytkownicy powinni raczej używać znaczników
kanonicznych. Możesz również włączać pliki PHP używając funkcji include ( ) lub
require ( ) , lecz ich zawartość nie jest traktowana jako PHP, chyba że otoczysz ją
znacznikami PHP.
Rozdział 5.
Składnia, zmienne
i wyświetlanie
W tym rozdziale:
4 Podstawowe zasady pisania kodu PHP
•* Zapamiętywanie danych w zmiennych
+ Wyświetlanie wyników na stronie WWW
W rozdziale tym opiszemy podstawy składni PHP i zasady, jakich musi się trzymać do-
brze napisany kod PHP. Opiszemy sposób użycia zmiennych do zapamiętywania
i odczytywania informacji w czasie wykonywania skryptu. Na koniec pokażemy naj-
prostszy sposób wyświetlania tekstu w przeglądarce użytkownika.
Jednak PHP nie potrafi czytać w twoich myślach, zakłada minimalny zestaw zasad, któ-
re muszą być zastosowane. Jeżeli zamiast świetnej strony WWW zobaczysz w przeglą-
darce komunikat „parse error", oznacza to, że złamałeś zasady i PHP musiał się poddać
podczas analizowania twojej strony.
Składnię PHP stosuje się tylko do PHP, więc zakładamy, że do końca tego rozdziału
mamy cały czas aktywny tryb PHP; większość fragmentów kodu należy wbudować
w stronę HTML i otoczyć odpowiednimi znacznikami.
Używanie znaku końca wiersza jest bardzo wygodne, ponieważ nigdy nie trzeba się sta-
rać, aby wyrażenie zmieściło się w jednym wierszu.
^^fe.
Rozdział 5. » Składnia, zmienne i wyświetlanie__________________________ 77
Inaczej niż w C, nazwy funkcji i podstawowe konstrukcje języka (if, then, else,
while itp.) ignoruj ą wielkość liter.
Następny, bardziej skomplikowany składnik PHP to wyrażenie, które jest dowolną kom-
binacją elementów mających jakąś wartość. Zarówno pojedyncza liczba, jak i zmienna
są wyrażeniami. Proste wyrażenia mogą być łączone w złożone, zwykle poprzez wsta-
wienie pomiędzy nie operatora, (np.: 2 + ( 2 + 2 ) ) lub użycie funkcji (np.: power_
of (2*3, 3*2)). Operatory działające na dwóch argumentach są wstawiane pomiędzy,
natomiast funkcje pobierają dane z nawiasów następujących bezpośrednio po nazwie
funkcji. Dane te (parametry) rozdziela się przecinkami.
J78__________________________________________Część l » Podstawy PHP
Obliczanie wyrażeń
Jeżeli interpreter PHP napotka w tekście wyrażenie, natychmiast oblicza jest jego war-
tość. Oznacza to, że PHP oblicza wartości najmniejszych części wyrażenia i łączy ze
sobą wartości połączone operatorami lub wywoływanymi funkcjami, dopóki nie zosta-
nie uzyskana wartość całego wyrażenia. Kolejne kroki obliczania wartości wyrażenia
wyglądają następująco:
$wynik = 2 * 2 + 3 * 3 + 5;
( = 4 + 3 * 3 + 5 ) // kolejne kroki obliczania
(=4+9+5)
(= 13 + 5)
(= 18)
Na przykład:
$wynikl = 2 + 3 * 4 + 5 //=19
Swynik2 = (2 + 3) * (4 + 5) // = 45
Na przykład:
$ile = 3 . 0 / 4 . 0 / 5 . 0 ;
Wynik może być 0,15 lub 3,75 w zależności od tego, który operator dzielenia pierwszy
„złapie" liczbę 4. W podręczniku znajduje się wyczerpująca lista zasad łączenia, jednak
najważniejszą z nich jest łączenie podwyrażeń od lewej do prawej. Zgodnie z tym nasze
wyrażenie ma wartość 0,15, ponieważ pierwszy z lewej operator dzielenia będzie wy-
konany najpierw.
Ostatnim problemem jest kolejność obliczeń, co nie jest tożsame z łączeniem wyrażeń.
Dla przykładu spójrzmy na następujące wyrażenie:
3*4 + 5*6
Wiemy, że mnożenie wykonywane jest przed dodawaniem, jednak nie wiemy, które
mnożenie zostanie wykonane pierwsze. Zwykle nie będziesz musiał przejmować się
Rozdział 5. » Składnia, zmienne i wyświetlanie_____________________________79
Na przykład:
Sha = ($to = Stamto + 5) + (Stamto = Sto + 3); // ŹLE
Nie pisz w ten sposób! PHP może, ale nie musi mieć zdefiniowanej kolejności oblicza-
nia wyrażeń, jednak nie powinieneś na tym polegać (jedynym dozwolonym użyciem
obliczania od lewej do prawej jest „skracanie" wyrażeń logicznych, które opiszemy
w rozdziale 7.).
Wyrażenia i typy
Zwykle programiści są ostrożni przy dobieraniu typów wyrażeń dla operatorów lub
funkcji. Najczęściej występujące to wyrażenia matematyczne (używające operatorów
matematycznych do działań na liczbach), logiczne (obliczające wyrażenia typu prawda
lub fałsz za pomocą operatorów and i or) lub operacje na ciągach (za pomocą operato-
rów i funkcji tworzących ciągi znaków). Rozważmy poniższe wyrażenie, które umyśl-
nie miesza typy podwyrażeń:
2 + 2 * "nonsens" + TRUE
Zamiast komunikatu o błędzie pojawi się liczba 3 (możesz potraktować to jako zagad-
kę; w następnym rozdziale wyjaśnimy, co się stało).
Przypisywanie wyrażeń
Często używa się wyrażeń, w których do zmiennej przypisywany jest wynik jakiegoś
wyrażenia. Ma ono postać nazwy zmiennej (poprzedzonej znakiem S), następnie poje-
dynczego znaku równości i obliczanego wyrażenia. Na przykład:
Sosiem - 2 * (2*2}
Są dwa powody pisania wyrażeń: uzyskanie ich wartości oraz efektu ubocznego. War-
tością wyrażenia jest to, co może być przekazane do bardziej skomplikowanych wyrażeń.
Efekt uboczny to wszystko, co zdarzyło się w czasie obliczania wyrażenia. Najbardziej
typowymi efektami ubocznymi to przypisanie wartości do zmiennej, zmiana wartości
zmiennej, wypisanie czegoś na ekranie lub wykonanie trwałej zmiany w środowisku
programu (np. zmiany w bazie danych).
Bloki
Mimo że instrukcji nie można łączyć tak jak wyrażeń, można zawsze umieścić sekwen-
cję instrukcji tam, gdzie spodziewana jest jedna instrukcja, poprzez otoczenie grupy in-
strukcji nawiasami klamrowymi.
Możesz umieścić w bloku dowolny rodzaj instrukcji, nawet kolejną instrukcję if,
która będzie miała swój blok instrukcji. Oznacza to, że instrukcja if może zawierać
inną instrukcję if. Takie zagnieżdżanie jest możliwe bez szczególnych ograniczeń
liczby poziomów.
Komentarze
Komentarz to fragment programu przeznaczony tylko dla programistów, nie dla inter-
pretera. Pierwszą czynnością, jaką wykonuje analizator języka, jest usunięcie komenta-
rzy, więc nie mają one wpływu na działanie programu. Komentarze są nieocenioną
Rozdział 5. » Składnia, zmienne i wyświetlanie___________________________81
pomocą dla innej osoby, czytającej twój kod i starającej się zorientować, o czym my-
ślałeś, gdy pisałeś ten fragment. Często tąosobąjesteś ty sam, po kilku tygodniach bądź
miesiącach od napisania programu.
PHP czerpał inspirację z języków programowania C, Perl oraz skryptów powłoki Unix.
W efekcie PHP zapewnia kilka sposobów komentowania pochodzących z tych języków.
Style te mogą być dowolnie mieszane w kodzie PHP.
Trzeba pamiętać, że komentarzy nie można zagłębiać. Nie możesz umieścić jednego
komentarza w drugim. Jeżeli spróbujesz to zrobić, komentarz zakończy się na */,
a reszta tekstu, która miała być komentarzem, będzie interpretowana, co najprawdopo-
dobniej spowoduje błąd. Na przykład:
/* Ten komentarz spowoduje /* błąd
na ostatnim wyrazie tego */ zdania
V
Łatwo jest zrobić coś takiego nieświadomie, zwykle gdy próbujesz wyłączyć fragment
kodu przez jego zakomentowanie.
Komentarze jednowierszowe: # i //
Oprócz wielowierszowych komentarzy / * . . . * / możesz na dwa sposoby umiesz-
czać komentarze rozciągające się do końca wiersza. Jeden sposób pochodzi z C++
i Java, drugi z Perl i skryptów powłoki systemowej. Komentarze w stylu skryptów po-
włoki rozpoczynają się od znaku #, natomiast komentarze w stylu C++ od //. Oba te
sposoby komentowania powodują, że reszta bieżącego wiersza jest traktowana jako
komentarz.
tt To jest komentarz
t a to jest druga linia komentarza
// To również jest komentarz. Oba style komentują tylko
// jedna linię. Więc ostatnie słowo tego zdania spowoduje
błąd.
Zmienne
Główną metodą przechowywania informacji w środku programu PHP jest użycie zmien-
nych, które są sposobem na nazwanie i zapamiętanie wartości używanych później.
Po początkowym znaku „$" zmienna może składać się z liter (małych bądź wielkich),
cyfr (O - 9) oraz podkreślenia („_")• Pierwszy znak po $ nie może być cyfrą.
Deklarowanie zmiennych
Ten podpunkt umieściliśmy w tym miejscu, ponieważ programiści piszący w innych ję-
zykach mogą go szukać. W językach takich jak C, C++ i Java programista musi zade-
klarować nazwę i typ zmiennej przed jej użyciem. Ponieważ w PHP typy są związane
z wartościami, a nie ze zmiennymi, deklaracja nie jest konieczna; pierwszym krokiem
użycia zmiennej jest przypisanie jej wartości.
Nieprzypisane zmienne
Wiele języków programowania sprzeciwia się używaniu zmiennej przed przypisaniem
jej wartości. Inne pozwalają na to, ale mogą odwoływać się do losowej zawartości ob-
szaru pamięci. W PHP domyślne ustawienie raportowania błędów pozwala na używanie
zmiennych bez wcześniejszego przypisania. Zmienne będą miały rozsądne domyślne
wartości.
Wartości domyślne
Zmienne w PHP nie mają ustalonego typu, zmienne „nie wiedzą wcześniej", czy będą
przechowywały liczby, czy ciągi znaków. W jaki sposób znana im jest wartość domyśl-
na, skoro nie były jeszcze przypisywane?
Odpowiedź jest taka sama jak przy przypisywaniu zmiennych: typ zmiennej jest ustala-
ny w zależności od kontekstu, w którym jest użyta. W sytuacjach gdy spodziewamy się
liczby, będzie ona liczbą, w innej sytuacji będzie ciągiem znaków. Jeżeli kontekst wy-
maga uznania nieprzypisanej zmiennej za liczbę, zmienna taka będzie traktowana, jakby
miała wartość 0. W innym kontekście, w którym spodziewamy się ciągu znaków, bę-
dzie to ciąg pusty (o długości 0).
Zmienna $never__set nie była nigdzie przypisana, więc gdy spodziewany był ciąg zna-
ków, dawała pusty ciąg znaków (w instrukcji print), lub zero, gdy spodziewana była
liczba (jak w porównaniu sprawdzającym, czy zmienne są takie same). Jednak funkcja
IsSet mogła określić różnicę pomiędzy $set_var i $never_set.
Zasięg zmiennych
Zasięg jest technicznym terminem zasad określających, kiedy nazwa (zmienna lub
funkcja) ma to samo znaczenie w dwóch różnych miejscach i w jakich sytuacjach dwie
identyczne nazwy mogą odwoływać się do różnych rzeczy.
W PHP każda zmienna, która nie jest w ciele funkcji, posiada zasięg globalny i roz-
ciąga się na cały przebieg wykonania. Inaczej mówiąc, jeżeli przypiszesz zmienną na
początku pliku PHP, nazwa tej zmiennej będzie miała takie samo znaczenie i jeżeli
nie zostanie powtórnie przypisana, będzie miała taką samą wartość w całym kodzie
(oprócz ciała funkcji).
Przypisanie wartości nie wpływa na wartości zmiennych o takiej samej nazwie w in-
nych plikach PHP, a nawet na kolejne użycia tego samego pliku. Załóżmy, że mamy
dwa pliki start.php i nastepny.php, które zwykle są odwiedzane w takiej kolejności
przez użytkowników. Załóżmy również, że na początku pliku start.php znajduje się wiersz
$username = "Jan Kowalski";
Oczywiście w wielu sytuacjach będziesz chciał przechować informacje dłużej niż tylko
na czas wygenerowania jednej strony. Jest kilka sposobów na zrealizowanie tego zada-
nia, opiszemy je w dalszej części książki. Możesz przesyłać dane między stronami, uży-
wając zmiennych GET i POST (rozdział 12.), zapisywać dane w bazie danych (II część
książki), przypisać do sesji użytkownika przy użyciu nowego mechanizmu PHP obsłu-
gującego sesje (rozdział 25.) lub zapisać na dysku użytkownika używając mechanizmu
cookie (rozdział 26.).
Wyjście
Większość konstrukcji języka PHP jest wykonywana w sposób ukryty; nie piszą one nic
na wyjściu. Jedynym sposobem, aby wbudowany kod PHP wyświetlił cokolwiek
w przeglądarce użytkownika jest użycie funkcji piszącej coś do wyjścia lub użycie in-
strukcji print.
Echo i print
Dwoma podstawowymi konstrukcjami drukującymi dane na wyjściu są echo i print.
Ich status w języku jest nieco mylący, ponieważ są podstawowymi konstrukcjami języ-
ka, a nie funkcjami. Dzięki temu mogą być używane z nawiasami, albo bez nich (funk-
cje zawsze mają nazwę, po której następuje otoczona nawiasami lista parametrów).
Echo
Najprostszym sposobem użycia funkcji echo jest wypisanie ciągu podanego jako ar-
gument, np.:
echo "To będzie wyświetlone w oknie przeglądarki,";
lub
echo ("To będzie wyświetlone w oknie przeglądarki.");
Print
Instrukcja print jest bardzo podobna do echo, ale:
•* print przyj muj e tylko j eden argument;
* print zwraca wartość wskazującą na to, czy udało się wykonanie instrukcji
print.
Wartość zwracana przez print to l, gdy drukowanie zakończyło się sukcesem, i O, gdy
się nie udało (rzadko się zdarza, że poprawna składniowo instrukcja print nie powie-
dzie się, jednak teoretycznie umożliwia sprawdzenie, czy np. przeglądarka użytkownika
zerwała połączenie). Zarówno echo, jak i print są używane zwykle z ciągami znaków
Rozdział 5. » Składnia, zmienne i wyświetlanie_____________________________87^
Technicznie rzecz biorąc, print w drugiej linii spodziewał się ciągu znaków jako ar-
gumentu, więc liczba zmiennoprzecinkowa została skonwertowana do ciągu, zanim
print ją dostał. Jednak głównym efektem jest to, że zarówno print, jak i echo do-
skonale drukują liczby i ciągi znaków.
Zmienne i ciągi
Programiści C są przyzwyczajeni do używania funkcji print f, która pozwala na
wklejenie wartości i wyrażeń do specjalnie sformatowanego ciągu. PHP posiada analo-
giczną funkcję (opiszemy ją w rozdziale 9.), możemy jednak uzyskać ten sam efekt,
używając print (lub echo).
Fragment kodu:
Sanimal = "Antylopa";
$animal_heads = 1;
$animal_legs = 4;
print! "Sanimal ma $animal_heads głowę.<BR>");
print( "$animal ma $animal_legs nogi.<BR>");
da następujący wynik:
Antylopa ma l głowę.
Antylopa ma 4 nogi.
Stało się tak, ponieważ „Antylopa" została wklejona do ciągu $saved_string przed
zmianą wartości zmiennej $ an ima l.
PHP zmieni każdy „\n" na znak końca wiersza. Jeżeli obejrzysz źródło strony HTML,
powinieneś zobaczyć kilka pustych wierszy w środku kodu (w oknie przeglądarki wszyst-
ko będzie w jednej linii; przeczytaj „HTML i końce wiersza" w dalszej części rozdziału).
Ciągi otaczane apostrofami (na przykład 'takie'} zachowują się inaczej. PHP nie wyko-
nuje wklejania zmiennych i przetwarza jedynie dwie sekwencje sterujące (możesz wsta-
wić znak ' do środka ciągu, pisząc Y oraz \, pisząc \\). Oprócz tych dwóch wyjątków PHP
interpretuje ciąg dosłownie. Jeżeli do ciągu wstawisz znak $ i wypiszesz go, w przeglą-
darce zobaczysz znak $. Takie traktowanie ciągów może być użyteczne, jeżeli chcesz na
przykład wypisać ścieżkę w stylu Windows. Wyrażenie
print('c:\newcode\php\myphp.php1);
wypisze ścieżkę tak, jak tego chcemy. Wersja z cudzysłowami zinterpretuje pierwszy
backslash jako początek sekwencji sterującej i wstawi znak końca wiersza zaraz za 'c:'.
Podsumowanie
Kod PHP jest zgodny z kilkoma zasadami składniowymi, w większości zapożyczonymi
z języków C i Perl. Wymagania składniowe są minimalne; PHP zwykle próbuje wy-
świetlać wynik.
Rozdział 5. » Składnia, zmienne i wyświetlanie___________________________89
Najprostszą metodą wysłania danych na wyjście jest użycie funkcji echo lub print,
które wypisują ciąg przekazywany jako argument. Są one dosyć użyteczne, szczególnie
w połączeniu z ciągami w cudzysłowach, które automatycznie modyfikują zmienne na
ich wartości.
90
Część l » Podstawy PHP
Rozdział 6.
Typy w PHP
W tym rozdziale:
4 Poznajemy sześć typów w PHP: integer, double, boolean, string, array i object
4 Tworzenie, czytanie, drukowanie i manipulacja obiektami różnych typów
4 Konwersja z jednego typu do innego
Wszystkie języki programowania mają jakiś rodzaj systemu typów, który definiuje różne
rodzaje wartości, jakie mogą pojawiać się w programie. Typy często korespondują
z różną reprezentacją bitową w pamięci komputera, jednak w wielu przypadkach pro-
gramiści nie muszą myśleć o reprezentacji na poziomie bitów. System typów PHP jest
prosty, wydajny, elastyczny; izoluje od niskopoziomowych szczegółów.
Rozdział ten opisuje podstawowe typy PHP (integer, double, boolean, string, array i object)
i pokazuje, w jaki sposób są one odczytywane, drukowane, przypisywane do zmiennych
konwertowane i łączone. Rozdział ten stanowi zarówno przegląd, jak i podręcznik:
zaznajomieni z programowaniem mogą go opuścić, mniej zaawansowani powinni prze-
czytać początkowe fragmenty; ale doń wracać, aby zgłębić te szczegóły, które nie wy-
dawały się ważne za pierwszym razem.
$first_number = 55.5;
$second_number = "To nie liczba";
Ponieważ substr spodziewa się ciągu znaków, a nie liczby, PHP skonwertuje dla nas
liczbę 12345 do postaci ciągu znaków '12345'.
Z powodu automatycznej konwersji typów ciężko jest zmusić PHP do zwrócenia błędu
typu. Jednak programiści PHP muszą upewnić się, że konwersje niepowodujące błędów
nie dadzą nieoczekiwanych wyników.
Typy w PHP
PHP posiada tylko sześć typów danych: integer, double, boolean, string, array i object.
•* integer to liczby całkowite bez przecinka, na przykład 4 9 5 .
• Double to liczby zmiennoprzecinkowe: 3 .14159 lub 4 9 . 0 .
•* Boolean ma tylko dwie wartości: TRUE i FALSE.
•* String to ciąg znaków, np.'PHP 4.0 obsługuje operacje na ciągach'.
Typy proste
Proste typy danych powinny być znane dla wszystkich, który już wcześniej programowali.
Jedyną rzeczą, jaka zaskoczy programistów C, jest niewielka liczba typów.
Integer
Integer to najprostszy typ; odpowiada on prostym liczbom całkowitym zarówno, do-
datnim, jak i ujemnym. Dane tego typu można przypisywać do zmiennych lub używać
w wyrażeniach.
$int_var = 12345;
$inny_int = -12345 + 12345; // równe zero
Formaty zapisu
Zmienne numeryczne mogą być zapisywane w trzech postaciach: dziesiętnej, oktalnej
i szesnastkowej. Format dziesiętny jest formatem domyślnym, liczby oktalne oznaczane
są przez dodanie początkowego 'O', a liczby szesnastkowe rozpoczynają się od 'Ox'.
Każdy z tych formatów może być poprzedzony znakiem '-' w celu uzyskania liczby
ujemnej. Na przykład:
$integer_10 = 1000;
$integer_8 = -01000;
$integer_16 = 0x1000;
print("integer_10: $integer_10<BR>") ;
print("integer_8: $integer_8<BR>");
print("integer_16: $integer_16<BR>");
daje w przeglądarce:
94__________________________________________Część l » Podstawy PHP
integer_10: 1000
integer_8: -512
integer_16: 4096
Format wpływa tylko na to, w jaki sposób liczba zostanie skonwertowana w trakcie od-
czytu, wartość zapisana w zmiennej $integer_8 nie pamięta, że była liczbą ósemkową.
Wewnętrznie liczby te są zapisywane w postaci binarnej, ale widzimy je skonwertowane
do postaci dziesiętnej, ponieważ takie jest domyślne ustawienie dla konwertowania liczb
całkowitych na ciągi.
Rozmiar
Jak duże (lub małe) mogą być liczby typu integer? Ponieważ typ ten odpowiada ty-
powi long w C, który zależy od długości słowa maszyny, odpowiedź na to pytanie
jest trudna. Dla typowych maszyn największa liczba całkowita to 2 31 - l (lub 2 147
483 647), najmniejsza to -(231 - 1) (lub -2 147 483 647).
W PHP nie ma stałej (jak MAXINT w C) określającej największą liczbę typu integer.
Na końcu tego rozdziału zamieściliśmy program określający tę liczbę. Jeżeli potrzebu-
jesz naprawdę dużych liczb, PHP ma kilka funkcji o dowolnej dokładności (opisane
w części BC rozdziału 10.).
Double
Typ double to liczby zmiennoprzecinkowe, takie jak:
Sfirst_double = 123.456;
$second_double = 0.456;
Seven_double = 2.0;
Zauważ, zmienna $even_double jest liczbą „okrągłą", nie znaczy to jednak, że całko-
witą. Liczby integer i double zapisywane sąw różnych postaciach binarnych. Wynik
wyrażenia
$five = $even_double + 3;
jest liczbą double, a nie integer. W prawie wszystkich sytuacjach będziesz mógł
mieszać w wyrażeniach matematycznych dowolnie liczby double oraz integer i po-
zwalać PHP na konwersję typów.
da w wyniku:
2 . 2 8 8 8 8 + 2.21112 = 4 . 5
Rozdział 6. » Typy w PHP_________________________________________95^
Formaty zapisu
Typowym formatem zapisu liczb double jest -x. Y, gdzie - określa liczbę ujemną, na-
tomiast x i Y są sekwencjami cyfr pomiędzy O a 9. Część x lub część Y może zostać
opuszczona. Początkowe i końcowe zera nie mają znaczenia. Wszystkie poniższe przy-
kłady są prawidłowymi liczbami double:
$sraall_positive = 0.12345;
$small_negative = -.12345;
$even_double = 2.000000;
$still_double = 2.;
Można dodatkowo używać notacji naukowej, dodając literę e i żądaną potęgę liczby 10
na końcu przedstawionego przed chwilą formatu. Na przykład 2.2e-3 oznacza
2 . 2 * 1 0 - 3 . Zmiennoprzecinkowa część tego formatu nie musi być ograniczana do liczb
z zakresu 1,0 i 10,0. Wszystkie poniższe przykłady są prawidłowe:
$small_positive = 5.5e-3;
print("small_positive = $small_positive<BR>"};
$large_positive = 2.8e+16;
print("large_positive - $large_positive<BR>");
$small_negative = -2222e-10;
print("small_negative = $small_negative<BR>") ;
$large_negative = -0.001189e6;
print("large_negative = Slarge_negative<BR>");
Zauważ, że zmienna nie pamięta, czy była podana jako liczba w notacji naukowej.
W czasie drukowania liczb PHP podejmuje decyzję, czy użyć formatu naukowego do wy-
świetlania dużych liczb, ale nie ma to związku z oryginalnym formatem zapisu tej liczby.
Boolean
Typ boolean posiada tylko wartości: prawda i fałsz, które są używane do tworzenia
struktur sterujących, takich jak wyrażenia „testujące" w instrukcji if. W następnym roz-
dziale opiszemy, w jaki sposób można za pomocą operatorów łączyć wartości logiczne
w bardziej skomplikowane wyrażenia.
Prawdziwy typ boolean jest nowością w PHP 4. PHP 3 nie miał osobnego typu bo-
olean, a zamiast tego traktował określone wartości innych typów jako TRUE lub FALSE
(podejście takie jest znane hakerom Perl). Różnica ta nie jest wielka, ponieważ nadal
możesz używać innych typów w kontekście logicznym, a PHP 4 wykonuje automatycz-
ną konwersję typów (na końcu rozdziału wypisaliśmy kilka przypadków, kiedy zacho-
wanie PHP 3 i PHP 4 może się różnić).
96__________________________________________Część l » Podstawy PHP
Stałe boolean
PHP posiada parę stałych logicznych: TRUE i FALSE, które można używać w sposób na-
stępujący:
if (TRUE)
print("To będzie zawsze wydrukowane<BR>") ;
else
print("To nie będzie nigdy wydrukowanę<BR>");
Przykłady
Każda z poniższych zmiennych, użyta jako wyrażenie logiczne, posiada wartość lo-
giczną określoną przez ich nazwę.
$true_num = 3 + 0.14159;
$true_str = "Zmęciony i prawdziwy";
$true_array[49) = "Element tablicy"; // opis w następnej części
$false_num = 999 - 999;
$false_str = ""; // ciąg o długości O
Wartość $floatbool jest bardzo bliska 0,0, jednak nie jest to dokładnie zero i dlatego
jego wartością logicznąjest true. Liczby integer są o wiele bezpieczniejsze jako war-
tości boolean; tak długo, jak wynik działania jest liczbą całkowitą, nie trzeba obawiać
się błędów zaokrągleń.
Zachowanie się PHP 3 i PHP 4 może być różne. Jeżeli $truth_value będzie równe
np. 3, PHP 4 skonwertuje jądo wartości logicznej TRUE przed porównaniem, natomiast
PHP 3 porówna 3 z aktualną wartością stałej TRUE i test da prawdopodobnie wartość
FALSE.
String
string służy do zapamiętania sekwencji znaków:
$string_l = "Ciąg znaków w cudzysłowach.";
$string_2 = 'Nieco dłuższy ciąg znaków w apostrofach';
$string_39 = " Ciąg ma trzydzieści dziewięć znaków. ";
Sstring_0 = ""; // Ciąg pusty
Ciągi mogą być zamykane apostrofami lub cudzysłowami, które mają działają inaczej
w programie. Ciągi z apostrofami są traktowane nieomal dosłownie, natomiast w cią-
gach z cudzysłowami nazwy zmiennych są zamieniane na ich wartości i interpretowane
są sekwencje sterujące.
Ciągi w apostrofach
Oprócz pary znaków specjalnych, ciągi w apostrofach są odczytywane i przechowywa-
ne dosłownie. Poniższy przykład:
Sdoslownie = 'Ta Szmienna nie zostanie wydrukowana!\n';
print(Sdoslownie);
98__________________________________________Część l » Podstawy PHP
Ciągi w apostrofach działają zgodnie z ogólną zasadą, że apostrofy innego typu nie po-
dzielą otaczanego tekstu. To jest prawidłowy zapis:
$apostrofy = 'Ten znak " nie jest problemem';
Ciągi w cudzysłowach
Ciągi otaczane znakami cudzysłowu (jak w "tym" przypadku) są przez PHP przetwarza-
ne według dwóch zasad:
1. Odpowiednie sekwencje sterujące rozpoczynające się znakiem \ są zamieniane
na znaki specjalne.
2. Nazwy zmiennych są zastępowane przez ich wartość, zamienioną na ciąg znaków.
Podobnie jak dla ciągów w apostrofach, terminator innego typu może być bezpiecznie
wstawiany do ciągu bez sekwencji sterującej:
$ma_apostrof = "Nie ma problemu 'z* apostrofami";
Interpretacja zmiennych
Gdziekolwiek wystąpi w ciągu znak $, PHP stara się określić, jaka zmienna znajduje się
za znakiem $ i wstawić w to miejsce wartość zmiennej. W zależności od tego, jak jest
ustawiona zmienna, może się to odbywać na kilka sposobów:
* Jeżeli do zmiennej jest przypisany ciąg znaków, jest ona wstawiana (lub wkle-
jana) do ciągu w cudzysłowach.
* Jeżeli zmienna zawiera wartość inną niż ciąg znaków, wartość ta jest konwer-
towana do postaci wklejanego ciągu.
** Jeżeli zmienna nie posiada wartości, jest zamieniana na pusty ciąg (PHP wkleja
pusty ciąg).
Na przykład:
Sto = "to";
$tamto = "tamto";
$cos_innego = 2.2000000000;
print("Sto, $nie_ustawiona, $tamto+$cos_innego<BR>");
da w wyniku:
to, ,tamto+2.2<BR>
Ograniczenia
Nie ma sztucznych ograniczeń długości ciągu. W granicach dostępnej pamięci możesz
tworzyć ciągi dowolnej długości.
Tablice
Typ tablicowy pozwala na grupowanie różnych wartości i dostępu do nich dzięki warto-
ści indeksu (również dzięki nazwie, ale o tym później). Jeżeli zdarzyło ci się korzystać
ze zmiennych Szmiennal, $zmienna2, $zmienna3 itd., możesz używać w ich miej-
sce tablicy ($zmienna [1], $ z m i e n n a [ 2 ] , $ z m i e n n a [ 3 ] , ...). Do elementów tablicy
można się odwoływać dzięki indeksowi w nawiasach kwadratowych ( [ 1 ] , [ 2 ] , [3]
itd.). Do tablicy PHP można przypisywać wartości różnych typów.
Przed pierwszym przypisaniem PHP nie wiedział, że zmienna $ tab l jest przeznaczona
na tablicę; była to po prostu niezainicjowana zmienna. Została wydrukowana jako pusty
ciąg. Również odwołanie do zerowego i piątego elementu jest traktowane identycznie
jak niezainicjowana zmienna. Wynik działania pierwszych trzech instrukcji print koń-
czy się więc na wyrazie wartość.
Po wykonaniu przypisania zmienna $tabl jest teraz oficjalną tablicą, w wyniku umiesz-
czenia jej nazwy w ciągu zwrócony zostanie predefiniowany ciąg Array. Element tabli-
cy o indeksie 5 zawiera ciąg Element # 6 (jak w większości języków, elementy tablicy
są numerowane od 0) i mamy do niego dostęp poprzez podanie tego indeksu. To jest je-
dyna zmiana, element zerowy pozostaje nadal niezainicjowana zmienną.
Implementacja tablic
Tablice są jedną z najbardziej użytecznych funkcji w PHP i mimo że wyglądają tak sa-
mo jak tablice w innych językach, są implementowane w zupełnie inny sposób.
Deklaracja taka rezerwuje blok pamięci na 10 liczb całkowitych, do których można od-
woływać się poprzez indeksy od O do 9.
Jedną z konsekwencji tego mechanizmu jest to, że nie musisz się obawiać przypisywa-
nia wartości do elementów tablicy o bardzo dużych indeksach:
Stablica[100000000] = "Nie obawiaj się"; // to jest OK
Wykonanie tej linii nie zarezerwuje 100 milionów elementów. Elementy o niższych in-
deksach prawdopodobnie jeszcze nie istnieją, więc nie zajmuj ą pamięci.
Takich indeksów używa się identycznie jak indeksów numerycznych. Indeksy nume-
ryczne i ciągi znaków mogą być bez obaw mieszane w tej samej tablicy.
PHP ma typ obiektowy, który może posłużyć jako rekord lub struktura. Nawet przed
wprowadzeniem typu obiektowego PHP nie będzie potrzebował struktur, ponieważ ta-
blice nie są ograniczone do przechowywania elementów jednego typu. Jeżeli przenosisz
program z innego języka do PHP i masz w kodzie struktury, możesz użyć tablic indek-
sowanych ciągami odpowiadającymi nazwom pól struktury.
Obiekty
Ostatnim z pięciu typów PHP jest typ obiektowy, wprowadzający PHP do modnego
świata programowania obiektowego (OOP). W rozdziale tym podamy tylko krótkie
wprowadzenie do koncepcji i składni programowania obiektowego w PHP, pełny opis
znajdziesz w rozdziale 33.
Przegląd OOP
Używając podejścia obiektowego programista definiuje nowe jednostki zwane klasami.
Każda klasa jest nowym typem danych. Po zdefiniowaniu klasy można utworzyć do-
wolną liczbę obiektów będącymi egzemplarzami bądź instancjami klasy.
Proces jest podobny do definiowania struktur lub rekordów w językach C lub Pascal.
Programowanie obiektowe idzie jednak dalej, dołączając jedną lub więcej z następują-
cych własności:
* Metody: oprócz pól danych obiekty mogą posiadać metody (funkcje definio-
wane na rzecz konkretnej klasy).
Rozdział 6. » Typy w PHP_________________________________________103
function pop()
(
if ($this->count>0)
f
$this->count--;
Stop = $this->the_stack[$this->count];
return ($top);
)
else
print("Stos jest pusty!<BR>");
)
I
Klasa intstack ma dwie zmienne ($the_stack i $count) oraz dwie metody (push ( )
i pop ( ) ) . Zmienne zostały zadeklarowane za pomocą słowa kluczowego var, a defini-
cja metod jest podobna do definicji zwykłej funkcji, oprócz tego, że znajdują się w ciele
klasy. Metody mogą odwoływać się do zmiennych klasy dzięki specjalnej zmiennej
$this, która wskazuje na obiekt.
Tworzenie obiektów
Po zdefiniowaniu klasy można tworzyć obiekty za pomocą słowa kluczowego new po-
przedzającego nazwę klasy. Metody wywołujemy używając operatora -> na wyniko-
wym obiekcie. Poniżej przykład użycia klasy Intstack zdefiniowanej na wydruku 6.1.
$my_stack = new Intstack;
$my_stack->push(l J ;
$my_stack->push(49) ;
$my_stack->push("To nie podziała");
$pop_result = $my_stack->pop() ;
print("Na górze stosu było $pop_result<BR>");
Spop_result = $my_stack->pop();
print("Na górze stosu było $pop_result<BR>");
$pop_result = $my_stack->pop();
Kontrola typów
Ponieważ zmienne mogą zmieniać typy po przypisaniach, czasami niezbędne jest
sprawdzenie typu zmiennej w czasie działania programu. PHP posiada zarówno ogólną
funkcję kontrolującą typ (gettype ( ) ) , jak i indywidualne funkcje logiczne dla każdego
z pięciu typów. Funkcje te wraz z ich alternatywnymi nazwami są zebrane w tabeli 6.1.
Rozdział 6. » Typy w PHP_________________________________________105
Tabela 6.1.
Funkcje kontroli typów
Funkcja Działanie
gettype ( a r g ) Zwraca ciąg określający typ argumentu: integer, double, string, array,
object lub unknown type
is_int (arg) Zwraca true, jeżeli arg jest liczbą całkowitą, false, jeżeli nie jest
is_integer(arg)
is_long(arg)
is_double ( a r g ) Zwraca true, jeżeli arg jest liczbą zmiennoprzecinkową, false, jeżeli nie jest
is_float(arg)
is_real(arg)
is_bool ( a r g ) Zwraca true, jeżeli arg jest wartością logiczną (TRUE lub FALSE),
false, jeżeli nie jest
is_string ( a r g ) Zwraca true, jeżeli arg jest ciągiem znaków, f a l s e , jeżeli nie jest
is_array ( a r g ) Zwraca true, jeżeli arg jest tablicą, false, jeżeli nie jest
is_object (arg) Zwraca true, jeżeli arg jest obiektem, false, jeżeli nie jest
Przypisania i konwersje
PHP często konwertuje wartości z jednego typu na inny, wymagany w bieżącym zasto-
sowaniu. Programista może również zażądać niektórych z takich konwersji.
Jawne konwersje
PHP zezwala na trzy sposoby manipulacji typami: funkcje konwersji, rzutowanie typów
(jak w języku C) oraz wywołanie settype ( ) na zmiennej:
1. Funkcje intval (), doubleval () i strval () konwertują wartość argumentu
na odpowiednio liczbę integer lub double, albo ciąg znaków (do chwili na-
pisania książki nie było funkcji boolval).
2. Każde wyrażenie może być poprzedzone rzutowaniem typu (nazwa typu w na-
wiasach), co konwertuje wynik wyrażenia do żądanego typu.
3. Jako pierwszy argument funkcji settype ( ) może wystąpić dowolna zmienna,
która zostanie zmieniona na typ podany w drugim argumencie funkcji.
Podejście 1.
$dog_count = intval(strval(doubleval("101 dalmatyńczyków")));
Podejście 2.
$dog_count = (int)(string)(double) "101 dalmatyńczyków";
Podejście 3.
$dog_count = "101 dalmatyńczyków";
settype($dog_count, "double");
settype(Sdog_count, "string");
settype (Sdog_count,- "int");
Każdej z sześciu nazw typów (integer, double, boolean, string, array i object)
można użyć przy rzutowaniu typów oraz jako drugi argument funkcji s e t t y p e f ) .
Dodatkowo przy rzutowaniu (ale nie w funkcji settype ()) dopuszcza się kilka nazw
zastępczych: ( i n t ) zamiast ( i n t e g e r ) , ( f l o a t ) zamiast (double) oraz (bool) za-
miast ( b o o l e a n ) .
Przykłady konwersji
Na wydruku 6.2 podany jest kod, który wyświetla kilka konwersji typów w tabeli
HTML, pokazanej na rysunku 6.1 (kod ten nie jest przykładem stylu programowania
i używa kilku nieopisanych jeszcze konstrukcji).
Rysunek 6.1.
Przykład
konwersji typów
108_________________________________________Część l » Podstawy PHP
Tabela 6.2.
Inne funkcje konwersji typów
Funkcja ceil () jako argument pobiera liczbę double i zwraca liczbę typu integer,
która jest od niej większa lub jej równa. Na przykład:
$my_double = 4.7;
$my_int = ceil($my_double) ; // $my_int = 5
$my_double = -4.7;
$my_int = f loor ($my_double) ; // $my__int = -4
Funkcja round ( ) zwraca liczbę całkowitą najbliższą liczbie double podanej jako argu-
ment. Jeżeli część ułamkowa jest równa dokładnie 0,5, liczba jest zaokrąglana w stronę
liczby parzystej.
$my_int = round(4.7); // $my_int = 5
$my_int = round(-4.7); // $my__int = -5
$my_int = round(-4.5); // ?my_int = -4
Funkcja chr ( ) pobiera integer i zwraca ciąg składający się z jednej litery o kodzie
ASCII równym podanemu argumentowi. Funkcja ord ( ) działa odwrotnie, zwraca war-
tość ASCII pierwszego znaku ciągu.
da w efekcie
Jedno krótkie zdanie.
da w wyniku:
Czy 111000 to jeszcze integer?
Czy 111000000 to jeszcze integer?
Czy 111000000000 to jeszcze integer?
Czy 1.11E+14 to jeszcze integer?
Czy 1.11E+17 to jeszcze integer?
Zmiana sposobu wyświetlania liczby na postać naukową, jest związana z zamianą typu
zmiennej $za_duzo na typ double. Oczywiście można w ten sposób utracić część in-
formacji, ponieważ double ma ograniczoną dokładność, ale PHP stara się zrobić wszyst-
ko, co jest możliwe, aby uniknąć błędu.
Podsumowanie
PHP 4 posiada sześć typów: integer, double, boolean, string, array i object.
Cztery z nich to typy proste: integer to liczby całkowite, double to liczby rzeczywi-
ste, boolean to wartości prawda i fałsz, natomiast string to łańcuch znaków (typ
boolean jest nowością w PHP 4). Tablice stanowią typ złożony z innych wartości PHP
indeksowanych za pomocą liczb lub ciągów. Obiekty są egzemplarzami klas definiowa-
nych przez programistę, które zawierają zarówno zmienne, jak i funkcje klasy. Klasy
mogą dziedziczyć funkcje i zmienne z innych klas.
W PHP tylko wartości posiadają typy — zmienne nie posiadają wewnętrznego typu, in-
nego niż typ ostatniego przypisanej wartości. PHP samoczynnie konwertuje typy, jeżeli
jest to wymagane przez kontekst, w jakim została użyta wartość. Programista może
również jawnie kontrolować typy zmiennych poprzez użycie konwersji lub rzutowania.
Rozdział 7.
Sterowanie
W tym rozdziale:
* Tworzenie i łączenie warunków logicznych
* Instrukcje warunkowe if i switch
+ Pętle while i for
* Przerywanie przetwarzania strony za pomocą exit i die
Trudno jest napisać interesujący program, jeżeli wykonanie programu nie zależy od
czynników zewnętrznych. Można stworzyć kod, którego wykonanie zależy tylko od
wartości zmiennych, ale jest to równie pasjonujące, co wypełnianie formularza. Pro-
gramiści chcą pisać programy, na które coś oddziałuje (świat, czas, wprowadzone dane,
zawartość bazy danych), a program na skutek tego oddziaływania robi coś innego.
Wyrażenia logiczne
Każda z instrukcji sterujących opisanych w tym rozdziale posiada dwie oddzielne części:
warunek (określający, która część struktury będzie wykonana) oraz sam kod (osobne
gałęzie kodu lub ciało pętli). Wyrażenia logiczne działają poprzez wyliczenie wartości
wyrażenia, którego wartość jest traktowana jako prawda lub fałsz.
Stałe logiczne
Najprostszym rodzajem wyrażenia jest po prostu wartość. Najprostszymi wartościami
logicznymi są stałe TRUE i FALSE. Możemy np. wstawić je do warunku instrukcji if-else:
if (TRUE)
print{"To będzie zawsze wydrukowane<BR>") ;
else
print("To nie będzie nigdy wydrukowane<BR>");
lub zamiennie:
if (FALSE)
printC'To nie będzie nigdy wydrukowane<BR>") ;
else
printC'To będzie zawsze wydrukowane<BR>") ;
Operatory logiczne
Operatory logiczne wiążą wartości logiczne i dają w wyniku nowe wartości logiczne.
Standardowe operatory logiczne (and, or, not i exclusive or) są dostępne w PHP.
Tabela 7.1.
Operatory logiczne
Operator Działanie
and
Daje wartość TRUE, gdy oba argumenty są TRUE
° r
Daje wartość TRUE, gdy jeden z argumentów (lub oba) jest TRUE
! TRUE, gdy jego argument jest FALSE, FALSE gdy argument jest TRUE
xor
TRUE, gdy jeden z argumentów (ale nie oba) jest TRUE
&s
To samo co 'and', ale ściślej wiąże argumenty (patrz opis kolejności wykonywania w dalszej
części rozdziału)
ll To samo co 'or', ale ściślej wiąże argumenty
Rozdział 7. » Sterowanie_________________________________________113
Operatory & & i I I są znane programistom C. Operator '!' jest czasami zwany operato-
rem not.
Tłumacząc na polski: „Z podanych trzech wyrażeń jedno spełnia tylko jedno z poda-
nych dwóch twierdzeń — 1) wszystkie trzy wyrażenia są prawdziwe, 2) istnieje para
wyrażeń, z których oba nie są prawdziwe".
W przypadku gdy $podz jest równy zero, operator & & powinien zwrócić FALSE bez
względu na wartość drugiego wyrażenia. Ponieważ z powodu skracania nie wykona się
114_________________________________________Część l « Podstawy PHP
drugie wyrażenie, unikniemy błędu dzielenia przez zero. W przypadku gdy $podz jest
różny od zera, operator & & ma zbyt mało informacji i musi obliczyć wartość logiczną
drugiego wyrażenia.
Do tej pory formalnie opisaliśmy stałe TRUE i FALSE i sposób ich użycia. Teraz zaj-
miemy się operatorami, które pozwolą na zbudowanie prawdziwych wyrażeń logicznych.
Operatory porównania
W tabeli 7.2 zamieszczone są operatory porównania, które mogą być używane do po-
równywania zarówno liczb, jak i ciągów (przeczytaj ostrzeżenie w dopisku).
Tabela 7.2.
Operatory porównania
Kolejność operatorów
Mimo że duże zaufanie do reguł kolejności może być kłopotliwe dla osoby czytającej
twój kod, jednak należy zapamiętać, że operatory porównania mają wyższy priorytet niż
operatory logiczne. Oznacza to, że warunek:
if ($small_num > 2 && $small_num < 5) ...
Ostrożnie z porównaniami
Mimo że operatory porównania działają zarówno z liczbami, jak i ciągami,
kryje się tutaj kilka pułapek.
Po pierwsze, mimo że porównania mniejszy lub równy, większy lub równy na
liczbach double (lub pomiędzy integer i double) zawsze są bezpieczne,
niebezpieczne jest poleganie na równości liczb double, szczególnie jeżeli są
wynikiem działań matematycznych. Problem tkwi w błędach zaokrągleń, które
mogą spowodować, że liczby teoretycznie równe nieco się od siebie różnią.
Po drugie, pomimo że operatory porównania działają równie dobrze na cią-
gach, jak i na liczbach, to automatyczna konwersja typów PHP może dopro-
wadzić do nieintuicyjnych wyników, gdy ciągi zostaną zinterpretowane jako
liczby, na przykład:
$ciag_l = "00008";
Sciag_2 = "007";
$ciag_3 - "00008-OK";
if ($ciag_2 < $ciag_l)
print ("$ciag_2 jest mniejszy od $ciag_l<BR>");
if ($ciag_3 < Sciag_2)
print ("Sciag_3 jest mniejszy od $ciag_2<BR>");
if ($ciag_l < $ciag_3)
print ("$ciag_l jest mniejszy od Sciag_3<BR>");
PHP konwertuje ciągi na liczby, jeżeli tylko potrafi to zrobić i gdy obie strony
mogą być potraktowane w ten sposób (porównywane są wartości liczbowe,
a nie ciągi). Projektanci PHP uważają, że nie jest to błąd, to po prostu wła-
sność. Zalecamy korzystanie z funkcji strcmp ( ) , jeżeli porównywane ciągi
mają jakąkolwiek szansę być zinterpretowane jako liczby (rozdział 9.).
116_________________________________________Część l » Podstawy PHP
Porównywanie ciągów
Operatory porównania mogą być używane do porównywania ciągów tak samo jak liczb
(przeczytaj ostrzeżenie powyżej). Możemy się spodziewać, że poniższy kod wypisze
skojarzone z nim zdanie:
if (("Marek" < "Maria") and
("Maria" < "Margaryna"))
{
print("W słowniku pomiędzy Marek i Maria ");
print("znajduje się Margaryna,<BR>");
}
Porównanie zależy od wielkości liter. Przykład ten wypisze zdanie tylko dlatego, że
w słowie Margaryna zastosowaliśmy niewłaściwą wielkość liter. Z powodu nieodpo-
wiedniej wielkości liter poniższy przykład nic nie wydrukuje:
if (("głębokie morze" < "gramatyka") and
("gramatyka" < "Grażyna"))
{
print("Pomiędzy głębokie morze a Grażyna ");
print("znajduje się gramatyka.<BR>");
)
Operator trójskładnikowy
Jedną z bardziej użytecznych konstrukcji jest trójskładnikowy operator porównania,
który zajmuje miejsce pomiędzy operatorem logicznym a prawdziwą instrukcją warun-
kową. Jego działanie polega na użyciu wartości logicznej pierwszego wyrażenia do wy-
boru, które z pozostałych dwóch wyrażeń będzie wynikiem operatora. Składnia tego
operatora jest następująca:
wyrażenie-logiczne ? wyrażenie-prawda : w y r a ż e n i e - f a ł s z
Instrukcje warunkowe
Dwoma głównymi instrukcjami sterującymi są if i switch, if to prawdopodobnie
pierwsza instrukcja warunkowa, jakiej wszyscy się uczą. Switch jest jej użyteczną od-
mianą w szczególnych przypadkach, gdy potrzebujesz kilku gałęzi opartych o tę samą
wartość, a seria instrukcji if byłaby niewygodna.
If-else
Składnia dla instrukcji if jest następująca:
if (test)
wyrażenie_l
Podczas wykonywania instrukcji if obliczana jest wartość wyrażenia test, jego war-
tość jest interpretowana jako wartość logiczna. Jeżeli test ma wartość TRUE, wykony-
wane jest wyrażenie_l, w przeciwnym przypadku wykonywane jest wyrażenie_2.
Jeżeli wyrażenie test ma wartość FALSE, a nie ma części else, wykonywana jest po
prostu następna instrukcja po konstrukcji if.
Zauważ, że każde „wyrażenie" może być zarówno jedną instrukcją, blokiem instrukcji
lub inną instrukcją warunkową (każda jest traktowana jako pojedyncza instrukcja). In-
strukcje warunkowe mogą być dowolnie zagnieżdżane. Wyrażenia logiczne mogą być
prawdziwymi wyrażeniami typu boolean (TRUE, FALSE lub wynik operatora logiczne-
go, albo funkcji logicznej) lub wartościami innych typów, interpretowanymi jako war-
tości logiczne.
_^X* "Ł ———————————————————————" ——"—————————————...———————————————-——— _ ___———— ____... ..———————————————————————————————————————.——__———————————————
}
else
print("Nie ma żadnej różnicy.<BR>");
Kod ten polega na tym, że wartość O jest interpretowana jako fałsz, jeżeli różnica wyno-
si zero, drukowany jest komunikat o braku różnicy. Jeżeli liczby się różnią, wykonywa-
ne są następne instrukcje (przykład ten jest sztuczny, ponieważ warunek $ first ! =
$second załatwiłby sprawę bardziej wszechstronnie).
Dołączamy else
Programiści Pascala mogą zastanawiać się, w jaki sposób klauzula else wie, do której
instrukcji if należy. Zasada jest prosta i identyczna jak w większości języków oprócz
Pascala. Instrukcja else jest dołączana do najbliższej instrukcji if, respektując ograni-
czenia nawiasów klamrowych. Jeżeli chcesz się upewnić, że wyrażenie if pozostanie
bez części else, musisz otoczyć je klamrami.
if ($num % 2 == 0) // $num jest parzyste ?
f
if (Snum > 2)
print("Liczba nie jest liczba parzysta<BR>"};
)
else
print("Liczba jest nieparzysta<BR>");
Fragment ten wypisze „Liczba nie jest liczbą parzystą", jeżeli $num będzie parzystą
liczbą większą od 2; „Liczba jest nieparzysta", jeżeli $num jest nieparzysta; nic nie wy-
pisze, jeżeli liczba będzie wynosiła 2. Jeżeli usuniemy klamry, else zostanie przyłą-
czone do wewnętrznej instrukcji if; program nieprawidłowo wypisze „Liczba jest nie-
parzysta" jeżeli liczba będzie równa 2; nic nie wypisze, jeżeli liczba będzie nieparzysta.
Else-if
Można wykonać kaskadową sekwencję testów, jak w poniższej zagnieżdżonej in-
strukcji if:
if ($day == 5)
print("Pięć złotych obraczek<BR>");
else
if ($day == 4)
print("Cztery śpiewające ptaki<BR>") ;
else
if (Sday == 3)
print("Trzech muszkieterów<BR>" ) ;
else
if ($day == 2)
print("Dwie szare myszy<BR>" ) ;
else
if ($day == 1)
print("Kuropatwa na gruszy<BR>");
Rozdział 7. » Sterowanie______________________________________119
Konstrukcja if, elseif, elseif, ... pozwala na wykonanie sekwencji testów i wykona-
nie tylko pierwszej pasującej gałęzi. Teoretycznie jest to składniowo różna instrukcja od
przedstawionej w poprzednim przykładzie (mamy jedną instrukcję z pięcioma rozgałę-
zieniami, zamiast pięciu zagnieżdżonych instrukcji), ale działanie jest identyczne. Mo-
żesz używać tej konstrukcji, która wydaje ci się wygodniejsza w konkretnej sytuacji.
Ale nie jest oczywiste, że strategia ta działa również wewnątrz instrukcji wa-
runkowych. Można użyć PHP do wyboru, który fragment HTML należy wysłać,
a następnie „wysłać" odpowiedni fragment dzięki chwilowemu przełączeniu
się do trybu HTML.
Poniższy, niewygodny kod używa instrukcji print do stworzenia strony HTML
na podstawie spodziewanej płci użytkownika (zakładamy istnienie funkcji lo-
gicznej female o , która ją sprawdza).
<HTMLXHEAD>
<?php
if (female())
t
print ("<TITLE>Witryna tylko dla kobiet</TITLEXBR>") ;
print ("</HEADXBODY>") ;
print("Witryna ta została utworzona tylko ");
print("dla kobiet.<BR> Mężczyźni nie są tu mile widziani!"};
}
else
(
print("<TlTLE>Witryna tylko dla meżczyzn</TITLE><BR>");
print ( "</HEADXBODY>" ) ;
print{"Witryną ta została utworzona tylko "};
120______________________________________Część l » Podstawy PHP
Zamiast używać instrukcji print, można przełączyć się do trybu HTML w każ-
dej z dwóch gałęzi:
<HTMLXHEAD>
<?php
if (female())
(
?>
<TITLE>Witryna tylko dla kobiet</TITLEXBR>
</HEADXBODY>
Witryna ta została utworzona tylko dla kobiet.<BR>
Mężczyźni nie są tu mile widziani!
<?php
)
else
{
?>
<TITLE>Witryna tylko dla mężczyzn</TITLE><BR>
</HEADXBODY>"
"Witryna ta została utworzona tylko dla mężczyzn.<BR>
Kobiety nie są tu mile widziane!"
<?php
}
?>
</BODYX/HTML>
Wersja ta jest być może nieco trudniejsza do czytania, ale jedyną różnicą
jest zamiana serii instrukcji print na blok HTML, który rozpoczyna się za-
mykającym znacznikiem PHP (?>) i kończy się otwierającym znacznikiem
PHP(<? P hp).
W przykładach umieszczonych w tej książce zwykle unikamy tego rodzaju wa-
runkowego włączania, ponieważ jest to trudniejsze do czytania dla początku-
jących programistów PHP. Jednak nie powinno to cię powstrzymywać.
Włączanie takie powoduje znaczne przyspieszenie wykonania. W trybie HTML
maszyna PHP tylko przesyła znaki, szukając otwierającego znacznika PHP.
Jest to o wiele szybsze niż analiza i wykonywanie instrukcji print, szczególnie
gdy zawierają ciągi w cudzysłowach.
Switch
Konstrukcja switch jest bardzo użyteczna dla specyficznych rodzajów rozgałęzień.
Zamiast wybierać ścieżkę obliczając wartość wyrażeń logicznych, switch bierze pod
uwagę wartość jednego wyrażenia. Składnię przedstawiamy poniżej, część nieobowiąz-
kowa otoczona jest nawiasami kwadratowymi ([]).
switch (wyrażenie)
(
case wartość_l:
instrukcja_l
instrukcja_2
[break;]
case wartość_2:
instrukcja_3
Rozdział 7. » Sterowanie______________________________________121
instrukcja_4
[break;]
[default:
instrukcja_domyślna]
}
Wyrażenie może być zmienną lub innym rodzajem wyrażenia, które można zinterpre-
tować jako wartość typu prostego (liczba lub ciąg). Konstrukcja ta oblicza wartość wy-
rażenia i sprawdza, czy wynik jest równy kolejnym wartościom podanym w klauzuli
case. Gdy zostanie znaleziona odpowiednia wartość, wykonane będą kolejne instrukcje
aż do napotkania instrukcji break lub zakończenia instrukcji switch (break może
również służyć do przerywania pętli). Część default może być umieszczona na końcu
instrukcji i będzie wykonana dla wszystkich wartości wyrażenia, dla których nie było
odpowiedników.
Kod ten wypisze odpowiednią linię dla wartości 2 - 5, a dla innych wartości — „Kuro-
patwa na gruszy".
Pętle
Gratulacje! Właśnie minąłeś granicę pomiędzy pisaniem skryptów a „prawdziwym pro-
gramowaniem". Struktury warunkowe, które omówiliśmy, są użyteczne, ale istnieją
granice ich samodzielnego użycia. Z drugiej strony istnieją podstawy teoretyczne mó-
wiące, że każdy język z warunkami i pętlami może wykonać wszystko, co tylko może
122_________________________________________Część l » Podstawy PHP
zrobić dowolny język programowania. Nie musisz od razu pisać kompilatora za pomocą
PHP, ale dobrze jest wiedzieć, że nie ma do tego wewnętrznych przeszkód.
While
Najprostszą pętlą w PHP jest while, które ma następującą składnię:
while (warunek)
instrukcja
Pętla while oblicza wartość wyrażenia warunkowego, traktując je jako wyrażenie lo-
giczne. Jeżeli jest prawdziwe, wykonywana jest instrukcja ciała pętli i ponownie obli-
czana jest wartość warunku. Jeżeli warunek jest fałszywy, wykonywanie pętli while
jest zakończone. Oczywiście jak w przypadku instrukcji if, instrukcja może być jedną
instrukcją lub blokiem.
Ostatni przykład wydrukuje dokładnie dziesięć linii (inne interesujące przykłady znaj-
dziesz w „Przykładach pętli" w dalszej części rozdziału).
Do-while
Konstrukcja do-while jest podobna do while, poza tym, że warunek jest wykonywa-
ny na końcu pętli. Składnia jest następująca:
do
instrukcj a
while (wyrażenie);
Instrukcja jest wykonywana, następnie obliczana jest wartość wyrażenia. Jeżeli wyrażenie
ma wartość TRUE, instrukcja jest powtarzana, aż wyrażenie będzie miało wartość FALSE.
Jedyną praktyczną różnicą pomiędzy do-while i while jest to, że ciało pętli do-
while zostanie wykonane co najmniej raz. Na przykład:
Slicznik = 45;
do
{
print("Licznik: Scount");
$count = Scount + 1;
l
while (Slicznik <= 10);
For
Najbardziej złożoną pętlą jest pętla for. Jej składnia jest następująca:
for (wyrażenie_początkowe;
warunek_zakońcżenią;
wyrażenie_końcowe)
instrukcja
Jeżeli przedstawimy konstrukcję for jako pętlę while, będzie wyglądała następująco:
wyrażenie_początkowe;
while ( w a r u n e k _ _ z a k o ń c z e n i a )
{
instrukcja;
wyrażeńie_końcowe;
}
124_________________________________________Część l » Podstawy PHP
Typowa konstrukcja for posiada jedno wyrażenie początkowe, jeden warunek zakoń-
czenia i jedno wyrażenie końcowe, jednak można usunąć każdy z tych składników.
Jeżeli zostanie usunięty warunek zakończenia, jest to traktowane tak, jak gdyby wsta-
wiona została za niego wartość TRUE. Konstrukcja:
for (;;)
instrukcja;
jest tożsama z:
while (TRUE)
instrukcja;
Można również wstawić więcej niż jeden składnik każdego typu, oddzielony przecin-
kiem. Warunek zakończenia będzie prawdziwy, jeżeli którykolwiek z podwarunków zo-
stanie spełniony, tak jak przy użyciu operatora or. Poniższa instrukcja:
for ($x = 1 , Sy = l, $z = 1; // wyrażenie_początkowe
$y < 10, $z < 10; // warunek_zakończenia
?x = $x + l, $y = $y + 2; // wyrażenie_końcowe
Sz = $z +3}
print("$x, Sy, $z<BR>");
da w wyniku:
1. i, i
2. 3, 4
3. 5, 7
Mimo że składnia for jest najbardziej skomplikowana ze wszystkich pętli, jest często
używana do tworzenia prostych pętli ograniczonych przy użyciu następującego schematu:
for ($licznik = 0; $licznik < Smaksimum; $licznik = Slicznik + l )
instrukcja;
Przykłady pętli
Prześledzimy teraz kilka przykładów użycia pętli.
<?php
print("<TR>");
print ("<THX/TH>") ;
for ($count_l = $start_num;
$count_l <= $end_num;
Scount_l++)
print ("<TH>$count_K/TH>") ;
print("</TR>");
Rysunek 7.1.
Tabliczka dzielenia
Główną częścią tego programu są dwie zagłębione pętle. Każda z nich wykonywana jest
dziesięć razy, co w wyniku daje tabelę 10 x 10. Każdy przebieg zewnętrznej pętli wypi-
suje wiersz tabeli, natomiast każdy przebieg pętli wewnętrznej tworzy pojedynczą ko-
mórkę. Jedyną nowością w tym przykładzie jest użycie funkcji printf do wypisywania
liczb (opisana w rozdziale 9.), która pozwala na kontrolę liczby cyfr po przecinku dru-
kowanej liczby.
126_________________________________________Część l » Podstawy PHP
Rysunek 7.2.
Przybliżanie
pierwiastka
kwadratowego
Rozdział 7. « Sterowanie_________________________________________127
Mimo że przykład bardzo ładnie pokazuje potencjał nieograniczonych pętli, jest jednak
dosyć sztuczny: po pierwsze, ponieważ PHP posiada świetną funkcję do obliczania
pierwiastka kwadratowego (sqrt); po drugie, liczba 81 jest na stałe wpisana do kodu.
Nie możemy użyć tej strony do obliczania pierwiastka dowolnej liczby.
Break i continue
Normalnie pętla kończy się, gdy warunek zakończenia ma wartość FALSE. Specjalne
instrukcje break i continue zapewniają alternatywny sposób wyjścia z każdej pętli.
* Instrukcja break kończy najbardziej wewnętrzną pętlę, której ciało zawiera
break.
da w efekcie
2 4 6 8
Używając instrukcji break, programista nie musi korzystać z głównego warunku za-
kończenia pętli. Przeanalizujmy poniższy fragment, który drukuje listę liczb pierwszych
(niepodzielnych przez żadną inną liczbę oprócz jeden i samej siebie).
$limit = 500;
Sto_test = 2;
while(TRUE)
{
Stestdiv = 2;
128______________________________________Część l » Podstawy PHP
W kodzie tym mamy dwie zagnieżdżone pętle while, zewnętrzna przebiega przez
wszystkie liczby od l do 500, wewnętrzna pętla sprawdza prawdopodobne podzielniki.
Jeżeli wewnętrzna pętla znajdzie podzielnik, liczba nie jest pierwsza, więc pętla jest
przerywana i nic nie drukuje. Jeżeli sprawdzanie dojdzie do pierwiastka z liczby, mo-
żemy bezpiecznie założyć, że liczba jest pierwsza i wewnętrzna pętla jest przerywana
po wydrukowaniu liczby. Zewnętrzna pętla jest przerywana, gdy osiągnie granicę liczb
do sprawdzenia. Wynikiem jest lista liczb pierwszych mniejszych od 500.
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103
107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211
223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331
337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449
457 461 463 467 479 487 491 499
Zauważ, że krytyczny dla działania tego programu jest fakt, że break przerywa tylko
wewnętrzną pętlę.
Pętle nieskończone
Jeżeli kiedykolwiek programowałeś w innym języku, zdarzyło ci się prawdopodobnie
przypadkowo stworzyć pętle nieskończone (pętla, której warunek zakończenia zawsze
ma wartość TRUE, dlatego nigdy się nie kończy). Pierwszą rzeczą, jaką się wtedy robi,
jest przerwanie programu, ponieważ bez interwencji z zewnątrz będzie działał „ciągle".
Jednak w jaki sposób przerwać skrypt PHP? Czy wystarczy przycisnąć przycisk Stop
w przeglądarce?
Składnia alternatywna
PHP pozwala na inny sposób rozpoczynania i kończenia ciała instrukcji if, switch,
for oraz while. Polega to na zastąpieniu początkowej klamry bloku dwukropkiem,
a zamykającej klamry specjalnym słowem kluczowym dla każdej z instrukcji (endif,
endswitch, endf or oraz endwhile).
endif;
lub
if (wyrażenie):
instrukcjal;
instrukcja2;
elseif (wyrażenie2):
instrukcja3;
else:
instrukcja4 ;
endif;
Zauważ, że ciała części elseif oraz else również rozpoczynają się dwukropkiem.
Alternatywna składnia while jest następująca
while (wyrażenie):
instrukcja;
endwhile;
Możesz używać takiej składni, jaka ci przypadnie do gustu. Alternatywna składnia jest
włączona do PHP ze względów historycznych i dla wygody ludzi używających jej od
wczesnych wersji programu. W całej książce będziemy konsekwentnie używać składni
standardowej.
Przerywanie wykonania
Czasami trzeba poddać. PHP oferuje dwie konstrukcje pozwalające na zakończenie wy-
konywania skryptu:
1. Konstrukcja exit ( ) nie posiada argumentów. Kończy przetwarzanie skryptu
w dowolnym momencie.
2. Konstrukcja die ( ) ma ciąg znaków jako argument. Kończy przetwarzanie
skryptu, wysyłając na wyjście ciąg znaków podany jako argument.
130______________________________________Część l » Podstawy PHP
Wszystko, co PHP stworzył do czasu wykonania exit { ) lub die ( ) , zostanie wysłane
do klienta.
Jaki jest cel używania exit ( ) lub die ( ) ? Jedną z możliwości jest przerwanie przetwa-
rzania strony, jeżeli skrypt zorientuje się, że nie ma już nic do wysłania, a ty nie chcesz
tworzyć skomplikowanych instrukcji warunkowych. Jednak takie podejście może utrud-
nić czytanie i uruchamianie długich skryptów.
Lepszym zastosowaniem konstrukcji die ( ) jest obsługa błędów. Dobrze jest przyzwy-
czaić się do sprawdzania nieoczekiwanych sytuacji, które mogą zepsuć wykonanie
skryptu. Jeżeli sytuacja taka zostanie obsłużona za pomocą die ( ) , zostanie wypisany
komunikat informujący o problemie. Jeżeli instrukcja wykona się prawidłowo, die ( )
nie zostanie wykonane.
Rozważmy poniższy pseudokod, który zakłada, że posiadamy funkcje łączące się z bazą
danych i używające tego połączenia:
$connection = make_database_connection();
if (!$connection)
die ("Brak połączenia z baza. danych");
use_database_connection($connection);
Taka konstrukcja działa, ponieważ operator or jest skracany, a instrukcja die ( ) będzie
wykonana dopiero, gdy wyrażenie $connection = make_database_connection ( )
będzie miało wartość false. Ponieważ przypisywana wartość jest wartością przypisa-
nia, fragment działa identycznie jak poprzednia wersja.
Podsumowanie
PHP posiada zestaw struktur sterujących podobny do C, które działają w zależności od
wartości wyrażenia logicznego, do zbudowania którego można użyć operatorów logicz-
nych (and, or, xor, !, & & , I I ) . Do prostego rozgałęziania kodu używamy struktur if
oraz switch, pętle budujemy przy użyciu while, do-while oraz for, exit i die są
używane do zakończenia wykonywania skryptu.
W tabeli 7.3 zebraliśmy wszystkie struktury sterujące, opisane w tym rozdziale, oprócz
alternatywnej składni instrukcji.
Rozdział 7. » Sterowanie______________________________________131
Tabela 7.3.
Struktury sterujące PHP
[break;]
[default:
instrukcja_domyślna
}
While while ( w a r u n e k ) Wykonywany jest warunek i jego wartość jest
instrukcja interpretowana jako wartość logiczna. Jeżeli
warunek ma wartość false, wykonanie konstrukcji
while jest zakończone. Jeżeli ma wartość true,
wykonywana jest instrukcja aż do momentu, gdy
warunek będzie miał wartość false. Pętla while
jest przerywana, jeżeli napotkana zostanie instrukcja
break; jeżeli wystąpi continue, bieżąca iteracja
jest opuszczana
Do-while do
Bezwarunkowo wykonywana jest instrukcja,
instrukcja następnie jest wykonywana aż do chwili, gdy
while ( w a r u n e k ) ; i u j •
warunek będzie •miał
i ••
wartość false ,• , •
(instrukcje
break i continue działają tak samo jak dla while)
132______________________________________Część l » Podstawy PHP
Tabela 7.3.
Struktury sterujące PHP (ciąg dalszy)
Mechanizm PHP do zapewnienia tego rodzaju abstrakcji to funkcje. Istnieją w tym ję-
zyku dwa rodzaje funkcji, wbudowane oraz definiowane przez programistę.
W tym rozdziale wyjaśnimy sposób użycia funkcji dostarczanych przez PHP, a nieco
później omówimy sposób definiowania własnych. Na szczęście nie ma różnicy pomię-
dzy używaniem własnych i wbudowanych funkcji.
Użycie funkcji
Podstawowa składnia użycia (wywoływania) funkcji jest następująca:
nazwa_funkcji(wyrażenie_l, wyrażenie_2, ..., wyrażenie_n)
134_________________________________________Część l » Podstawy PHP
Wartość zwracana jest wartością, którą otrzymujemy przez wywołanie funkcji. Wartość
możesz np. przypisać do zmiennej:
?moje_pi = p i ( i ;
Funkcje mogą być zastosowane do szerokiej gamy efektów ubocznych, takich jak: pisa-
nie do pliku, manipulowanie bazą danych, drukowanie w przeglądarce klienta. Można
również jednocześnie korzystać z efektów ubocznych i zwracanej wartości; często funk-
cja, podejmująca jakieś akcje, zwraca wartość określającą, czy zostały one zakończone
sukcesem.
Wynik funkcji może być dowolnego typu; jeżeli funkcja zwraca wiele wyników, często
korzysta się z typu tablicowego.
Dokumentacja funkcji
Architektura PHP została zaprojektowana w sposób, który ułatwia rozszerzanie jej
przez programistów. Podstawowy język PHP jest bardzo jasny i elastyczny, ale nie ofe-
ruje zbyt wielu możliwości. Większość siły PHP jest zawarta w wielu wbudowanych
funkcjach. Można łatwo rozszerzać pakiet poprzez dodawanie nowych funkcji wbudo-
wanych, nie zmieniając żadnych mechanizmów, z których mogliby korzystać inni użyt-
kownicy.
Rozdział 8. » Użycie i definiowanie funkcji_____________________________135
Najobszerniejszą częścią podręcznika jest lista funkcji; każda funkcja posiada własną
stronę dokumentacji. Każda ze stron rozpoczyna się nazwą funkcji i krótkim opisem.
Zawiera deklarację nagłówka w stylu języka C i nieco dłuższy opis działania. Podaje się
również przykłady użycia oraz objaśnienia i ostrzeżenia od użytkowników.
Nagłówki w dokumentacji
Dla osób nieznających języka C nagłówki funkcji, zawarte na stronach dokumentacji,
mogą wydać się nieco mylące. Format nagłówka jest następujący:
typ_zwracany nazwa_funkcj i(typl argl, typ2 ar2, ...);
Definiuje typ wartości zwracanej przez funkcję, nazwę funkcji oraz liczbę i spodziewa-
ne typy argumentów.
Oznacza to, że funkcja substr zwraca ciąg znaków i oczekuje ciągu i jednego lub
dwóch liczb całkowitych jako argumentów. Nawiasy kwadratowe otaczające int
length oznaczają argument opcjonalny, więc substr może być wywoływany z cią-
giem znaków i liczbą lub ciągiem znaków i dwoma liczbami.
Pełna wersja podręcznika zapewnia, dla pierwszego rodzaju pytań, automatyczne szukanie
opisu funkcji na podstawie jej nazwy. Przycisk quick refna panelu nawigacji otwiera, po
przesunięciu nań kursora myszy, pole pozwalające na wpisanie nazwy funkcji. Kliknięcie
przycisku quick re/'przenosi nas do strony z alfabetyczną listą wszystkich funkcji.
Podobnie jak nazwy zmiennych, nazwy funkcji mogą składać się z liter, cyfr i podkre-
ślenia; nie mogą rozpoczynać się cyfrą. W przeciwieństwie do zmiennych, nazwy funk-
cji są konwertowane do małych liter przed ich zapamiętaniem, więc wielkość liter nie
ma znaczenia.
Ponieważ taki rodzaj porównania występuje bardzo często, zamieńmy ten fragment na
funkcję, która może być wielokrotnie używana. Przykładem jest następujący kod:
function better_deal ($amount_l, Sprice_l,
Samount_2, $price_2)
{
$per_amount_l = $price_l / $amount_l;
$per_amount_2 = $price_2 / $amount_2;
return ($per_amount_l < $per_amount_2);
}
$liters_l = 1.0;
$price_l =1.59;
$liters_2 = 1.5;
$price_2 = 2.09;
if better_deal($liters_l, $price_l,
$liters_2, Sprice_2)
print("Pierwsza oferta jest lepsza!<BR>");
else
printC'Druga oferta jest lepsza ! <BR>" );
Jeżeli jedynym zastosowaniem takiego porównania cen jest wypisanie lepszej oferty,
można zawrzeć drukowanie w ciele funkcji:
function print_better_deal ($amount_l, Sprice_l,
$amount_2, $price_2)
{
$per_amount_l = $price_l / $amount_l;
$per_amount_2 = $price_2 / $amount_2;
if ($per_amount_l < $per_amount_2)
print("Pierwsza oferta jest lepsza!<BR>");
else
printC'Druga oferta jest lepsza ! <BR>" ) ;
l
$liters_l = 1.0;
$price_l = 1.59;
$liters_2 = 1.5;
$price_2 = 2.09;
print_better_deal($liters_l, Sprice_l,
Sliters_2, $price_2);
Nasza pierwsza funkcja zwracała, przy użyciu instrukcji return, wartość wyrażenia
logicznego, którego użyto w warunku instrukcji if. Druga funkcja nie posiada instrukcji
. return, ponieważ używana jest tylko ze względu na jej efekt uboczny — drukowanie
tekstu w przeglądarce klienta. Gdy zostanie wykonany ostatni wiersz funkcji, PHP bę-
dzie kontynuował wykonanie programu od następnego wiersza po wywołaniu funkcji.
Rozdział 8. » Użycie i definiowanie funkcji_______________________________139
Zauważ, że podajemy przykłady, w których parametry aktualne mają takie same nazwy
jak parametry formalne (na przykład $price_l), oraz takie, w których nazwy są różne
($liters_l). Jak się przekonamy za chwilę, parametry formalne funkcji są zupełnie
niezależne od reszty programu, nawet od wywołania funkcji.
Podstawowa zasada rządząca zmiennymi w ciele funkcji brzmi: każda funkcja jest od-
rębną jednostką. Oznacza to, że, poza specjalnymi deklaracjami, zmienne w ciele funk-
cji nie mają nic wspólnego ze zmiennymi o takiej samej nazwie poza funkcją. Nie jest
to błąd — to bardzo pożyteczna cecha, funkcje mogą być używane wielokrotnie w wielu
miejscach programu, więc muszą być niezależne od kontekstu, w którym są wywołane.
W innym przypadku spędziłbyś wiele czasu, szukając błędów spowodowanych użyciem
zmiennej o tej samej nazwie w różnych fragmentach kodu.
$count = Scount + 1;
)
print("<BR>Znam teraz $count liter<BR>");
)
$count = 0;
SayMyABCs(};
Scount = $count + 1;
print("Teraz wykonałem $count wywołanie funkcj i.<BR>");
SayMyABCs(} ;
Scount = Scount + 1;
print("Teraz wykonałem Scount wywołanie funkcji.<BR>");
Funkcja SayMyABCs ( ) drukuje sekwencję liter (funkcje chr ( ) i ord ( ) konwertują po-
między literami a ich kodami ASCII). Wynikiem działania jest:
ABCDEFGHIJ
Znam teraz 10 liter
Teraz wykonałem l wywołanie funkcji.
ABCDEFGHIJ
Znam teraz 10 liter
Teraz wykonałem 2 wywołanie funkcji.
Zarówno definicja funkcji, jak i kod poza funkcją używają zmiennej o nazwie $ count,
jednak są to zupełnie różne, niekolidujące ze sobą zmienne.
Zmienne zainicjowane w ciele funkcji domyślnie nie mają wpływu na resztę kodu, są
tworzone na nowo przy każdym wywołaniu funkcji. Obie te własności można zmienić
za pomocą specjalnej deklaracji.
Mimo że przykład ten ukazuje global w złym świetle, deklaracja taka może być uży-
teczna. PHP posiada mechanizm (opisany w rozdziale 12.) przypisywania kilku zmien-
nych do każdej strony przed wykonaniem jakiegokolwiek kodu. Można łatwo uzyskać
dostęp do tych zmiennych w funkcji, bez konieczności przekazywania ich jako parametry.
Zmienne statyczne
Zmienne lokalne funkcji są tworzone na nowo za każdym wywołaniem funkcji. Aby
zmienić ten mechanizm można, użyć deklaracji static, która powoduje, że zmienna
zapamięta wartość pomiędzy kolejnymi wywołaniami tej samej funkcji. Przy użyciu tej
deklaracji zmodyfikujemy naszą wcześniejszą funkcję:
function SayMyABCs3 O
f
static $count = 0; // przypisanie tylko za pierwszym razem
Slimit = Scount + 10;
while (Scount < $limit)
(
print(chr(ord('A'} + Scount));
$count = $count + 1;
(
print("<BR>Znam teraz $count liter<BR>");
}
$count = 0;
SayMyABCs3();
Scount = Scount + 1;
print{"Teraz wykonałem Scount wywołanie funkcji.<BR>"} ;
SayMyABCsS();
$count = Scount + 1;
print("Teraz wykonałem Scount wywołanie funkcji.<BR>") ;
Słowo kluczowe static powoduje, że pierwsze przypisanie zachodzi, jeśli funkcja nie
była wcześniej wywoływana. Za pierwszym wywołaniem SayMyABCsS ( ) lokalna wer-
sja $count jest zerowana. Za drugim wywołaniem zmienna będzie miała już wartość
z poprzedniego wywołania. Zauważ, że zmiany zmiennej $ count na zewnątrz funkcji
nie mają wpływu na lokalną wartość zmiennej.
142______________________________________Część l » Podstawy PHP
Zasięg funkcji
Mimo że zasady rządzące zasięgiem zmiennych sąjasne i proste, zasady zasięgu funkcji
są jeszcze prostsze. W PHP 4 istnieje tylko jedna zasada: funkcje mogą być zdefinio-
wane tylko raz w skrypcie, który używa tej funkcji (zajrzyj do opisu nowej funkcji, do
omówienia pomiędzy wersjami PHP). Zasięg zmiennych jest domyślnie globalny, więc
funkcja zdefiniowana w skrypcie jest dostępna we wszystkich jego miejscach. Dla
większej przejrzystości dobrze jest definiować funkcje na początku skryptu, przed uży-
wającym ich kodem.
•4^Lj. W PHP 3 funkcje mogły być używane jedynie po ich definicji. Oznaczało
t0 ze na bez iecznie
funkcja ' J P Jszą praktyką było zdefiniowanie wszystkich funkcji
' (lub włączenie ich definicji) na początku skryptu, przed ich użyciem.
PHP 4 prekompiluje skrypty przed ich uruchomieniem, w związku z tym
ich definicje są znane wcześniej. Pozwala to na umieszczenie definicji
funkcji i kodu w dowolnej kolejności, pod warunkiem jednokrotnego wy-
stąpienia definicji każdej z funkcji.
które importują dwa różne pliki z definicjami funkcji. Nawiasy nie są obowiązkowe za-
równo w include ( ) , jak i require ( ) . Jeżeli pliki te zawierają tylko definicje funkcji,
kolejność włączania nie ma znaczenia.
Rekurencja
Niektóre języki kompilowane, jak np.: C i C++, nakładaj ą skomplikowane ograniczenia
kolejności definiowania funkcji. Aby skompilować funkcję, kompilator musi znać
wszystkie wywoływane funkcje, co oznacza, że muszą być one wcześniej zdefiniowane.
A jeśli funkcje się wzajemnie wywołują? Takie problemy spowodowały oddzielenie
przez projektantów C deklaracji funkcji (prototypów) od ich definicji (implementacji).
Idea tego podziału była następująca: deklaracje informują kompilator o typach argu-
mentów i typie zwracanej wartości, co wystarcza kompilatorowi na poprawne przetwo-
rzenie definicji w dowolnym porządku.
W PHP problem ten zniknął, więc nie ma prototypów funkcji. W PHP 3 funkcje mu-
siały być zdefiniowane przed ich użyciem, lecz definiowanie w jednej funkcji wywoła-
nia innej nie jest liczone jako użycie funkcji. Gdy PHP 3 napotyka definicję funkcji A,
nie oponuje, jeżeli ciało funkcji A zawiera wywołanie funkcji B, która jeszcze nie jest
zdefiniowana. Funkcja B musi być zdefiniowana, gdy wywołana jest funkcja A. W PHP 4
w ogóle problem kolejności definicji nie istnieje
Funkcje rekurencyjne (wywołujące same siebie) nie sprawiają więc trudności. Dla
przykładu zdefiniujemy funkcję rekurencyjną i wywołamy ją.
function countdown (Snum_arg)
f
if ($num_arg > 0)
{
print("Odliczamy: $num_arg<BR>");
countdown( $num_arg-l );
l
) .
countdown(10);
Istotne jest, czy funkcja posiada część podstawową (gałąź nierekurencyjną) i część re-
kurencyjną, a także czy część podstawowa na pewno zostanie wykonana. Jeżeli część
podstawowa nie jest nigdy wykonywana, mamy sytuację podobną do pętli while z wa-
runkiem zawsze spełnionym, nieskończoną pętlę wywołań funkcji. Z analizy przykła-
dowej funkcji wiemy, że część podstawowa zostanie wykonana, ponieważ każde
rekurencyjne wywołanie zmniejsza wartość przekazywanej liczby, która w końcu bę-
dzie równa zero. Oczywiście zakładamy, że liczba jest dodatnią liczbą całkowitą, a nie
liczbą ujemną bądź liczbą double. Zauważ, że warunek „większy od zera" zabezpiecza
przed nieskończoną rekurencją nawet w takich przypadkach, gdy warunek „różny od ze-
ra" tego zabezpieczenia nie zapewnia.
W wyniku otrzymamy:
Odliczan e (pierwsze): 5
Odliczan e (drugie): 4
Odliczan e (pierwsze): 3
Odliczan e (drugie): 2
Odliczan e (pierwsze): l
Zagadnienia zaawansowane
Zajmiemy się teraz bardziej zaawansowanymi zagadnieniami dotyczącymi funkcji: spo-
sobami użycia zmiennej liczby argumentów, modyfikacji przekazanych zmiennych oraz
użyciem funkcji jako wartości zmiennych.
Argumenty domyślne
Aby zdefiniować funkcję z argumentami domyślnymi, należy zmienić nazwę parametru
formalnego na wyrażenie przypisania. Jeżeli wywołanie będzie miało mniejszą liczbę
argumentów niż w definicji funkcji, PHP skojarzy wszystkie parametry aktualne z for-
malnymi, brakującym parametrom formalnym przyporządkuje wartości domyślne.
PHP nie wygeneruje ostrzeżeń, jeżeli funkcja zostanie wywołana z większą liczbą pa-
rametrów, niż wynika to z jej definicji. Możesz zdefiniować funkcję bez parametrów
i używać opisanych wcześniej funkcji do pobierania wartości przekazanych parametrów.
Jako przykład podaliśmy dwie funkcje zwracające tablicę argumentów, które zostały do
nich przekazane:
function args_as_array_l ()
{
$arg_count = func_num__args () ;
$counter = 0:
$local_array = array O;
while ($counter < $arg_count)
(
$local_array[Scounter] = func_get_arg($counter);
$counter = $counter + 1;
)
return($local_array};
)
function args_as_array_2 {)
(
return(func_get_args());
}
Przepiszmy teraz naszą funkcję tour_guide ( ) tak, aby skorzystać z funkcji do odczy-
tywania wartości parametrów, a nie z argumentów domyślnych:
function tour_guide_2(}
{
Snum_args = func_num_args();
$city = $num_args > O ?
func_get_args(0) : "Gotham City";
$desc = $num_args > l ?
func_get_args(1) : "rozległa metropolia";
$how_many = $num_args > 2 ?
func_get_args{2) : "przez wielu";
$of_what = $num_args > 3 ?
func_get_args(3) : "opryszków";
148___________________________________________Część l » Podstawy PHP
Funkcja ta działa identycznie, jak jej poprzednia wersja, i posiada te same ograniczenia.
Argumenty są kojarzone ze sobą na podstawie ich kolejności, więc nie ma sposobu na
zmianę „opryszków" na cokolwiek innego, przy domyślnej wartości „Gotham City".
Zademonstrujemy teraz wywołanie przez wartość przy bardzo słabej i niewydajnej im-
plementacji odejmowania:
function lame_subtract ($numl, Snum2)
(
if (Snuml < Snum2)
die("Liczby ujemne nie są obsługiwane");
$return_result = 0;
while(Snuml > Snum2)
(
$numl = Snuml - l;
$return_result = $return_result + 1;
)
return{$return_result};
)
$first_op = 493;
$second_op = 355;
Sresultl = lame_subtract($first^op, $second_op);
print("Wynik 1: $resultl<BR>");
$result2 = lame_subtract($first^op, $second_op);
print("Wynik 2: $result2<BR>"); ~
Upewnijmy się, że funkcja za każdym razem da identyczny wynik (dla tych samych
danych):
Wynik It 138
Wynik 2: 138
Dzieje się tak, ponieważ parametr formalny $numl odwołuje się do tej samej liczby, co
parametr aktualny $f irst_op, a zmiana jednej zmiennej powoduje zmianę drugiej.
Funkcja może pobierać argumenty przez referencję. Aby to wywołać, umieść znak &
przed parametrem aktualnym. Możemy użyć oryginalnej funkcji, przekazującej para-
metry przez wartość i uzyskać efekt przekazania przez referencję:
$first_op = 493;
$second_op = 355;
$resultl = lame_subtract(s$first_op, S$second_op);
print("Wynik 1: $resultl<BR>");
$result2 = lame_subtract(&$first op, &$second_op);
print("Wynik 2:~$result2<BR>");
co w efekcie da:
Wynik 1: 138
Wynik 2: O
da w efekcie:
The Red Baron to Manfred von Richthofen
The Scarlet Pimpernel to The Scarlet Pimpernel
150_________________________________________Część l » Podstawy PHP
Ponieważ nazwy funkcji są po prostu ciągami, mogą być argumentem lub wynikiem
działania funkcji.
return($num + 1);
else
return{$num - 1);
}
Kod pokazany wydruku 8.1 składa się z trzech części. W pierwszej zdefiniowaliśmy kilka
funkcji działających na liczbach od O do 25, które reprezentują litery od A do Z naszego
szyfru. Funkcja add_l dodaje l do podanej liczby modulo 26 (co oznacza, że liczby 26
i większe są „zawijane" i rozpoczynają znów od zera). O jest zamieniane na l, l na 2, ...,
a 25 jest zamieniane na 0. Sub_l przesuwa liczby w odwrotnym kierunku, dodając 25 (co
w arytmetyce modulo jest odpowiednikiem odjęcia 1). Swap_2 zamienia miejscami pary
liczb: O na l, l na O, 2 na 3, 3 na 2 itd. Swap_26 zamienia małe liczby na duże (25 na O, O
na 25, 24 na l, l na 24 itd.). Każda z tych funkcji jest podstawą szyfrowania. Dodatkowo
mamy kilka funkcji sprawdzających, czy znak jest wielką czy małą literą.
Część drugą stanowi jedna funkcja letter_cipher ( ) , która za pomocą funkcji z pierw-
szej części koduje pojedynczy znak. Na początek sprawdza, czy podany ciąg składa się
z liter (powinien składać się z jednej litery); jeżeli nie, zwraca go bez szyfrowania.
152_________________________________________Część l » Podstawy PHP
Jeżeli znak jest literą, zamieniany jest na liczbę za pomocą funkcji ord ( } . Odejmowa-
na jest od niego wartość ASCII liter 'A' bądź 'a', co sprowadza liczbę do przedziału O -
25. Jeśli liczba znajduje się w tym przedziale, jest szyfrowana, konwertowana z powro-
tem do postaci litery i zwracana.
Możemy napisać funkcję, która stosuje więcej niż jeden sposób szyfrowania w ciągu.
Funkcja ta korzysta z funkcji odczytywania wartości argumentów w PHP 4.
function chained_code ($message)
f
/* Pobiera ciąg oraz dowolną liczbę nazw funkcji kodujących,
zwraca wynik zastosowania w ciągu wszystkich metod */
$argc = func_num_args() ;
$coded = Smessage;
for (Scount = 1; Scount < Sargc; Scount++)
<
Sfunction_name = func_get_arg($count);
Scoded = string_cipher(Scoded, $function_name);
}
return(Scoded};
}
Stricky =
chained__code (Soriginal,
1 t
add_l', swap_26'/
1
add_l', 'swap_2');
print("Zakodowana wersja to $tricky<BR>");
$easy =
chained_code(Soriginal,
'add_l', 'swap_26',
1 swap_2', 'sub_l',
1 add_l', 'swap_2',
'swap_26', 'sub_l');
print("Zakodowana wersja to $easy<BR>");
Daje to w efekcie:
Zakodowana wersja to Bygbavjkcyna okmenqoyh hk YZWXUVS
Zakodowana wersja to Zaszyfrowany komunikat to ABCDEFG
Pierwsza wersja zakodowania ciągu jest kombinacją poprzednich kodowań, jednak nie
jest dokładnym odpowiednikiem żadnego z pojedynczych kodowań. Druga wersja jest
jeszcze bardziej skomplikowaną kombinacją funkcji, jednak daje niezmieniony komu-
nikat! Nie oznacza to, że nasze funkcje szyfrujące nie działają.
Morał naszej kryptograficznej opowieści jest taki: ponieważ kod szyfrujący jest nieco
skomplikowany, warto skorzystać z przekazywania nazw funkcji jako argumentów
funkcji.
Podsumowanie
Wartość PHP jest ukryta w dużej liczbie funkcji wbudowanych, dostarczonych przez
armię programistów PHP. Każda z nich jest udokumentowana (nawet zwięźle) w pod-
ręczniku, pod adresem http://www.php.net. Możesz również pisać własne funkcje, uży-
wane dokładnie w ten sam sposób, co funkcje wbudowane. Funkcje pisze się za pomocą
prostej składni w stylu C:
function moja_funkcja( $argl, $arg2, ...)
f
wyrażeniel;
wyrażenie2;
return ($wartość);
)
Funkcje mogą używać argumentów dowolnego typu, mogą również zwracać dowolnego
typu argumenty, które nie muszą być deklarowane.
Kolejność deklaracji funkcji i wywołania funkcji nie ma znaczenia, ale funkcja powinna
być zdefiniowana tylko raz. Nie ma potrzeby oddzielania deklaracji funkcji lub ich pro-
totypów. Zmienne zainicjowane w funkcji są zmiennymi lokalnymi funkcji, chyba że są
zadeklarowane jako globalne. Zmienne lokalne mogą być zadeklarowane jako statyczne,
co oznacza, że przechowuj ą wartość pomiędzy kolejnymi wywołaniami funkcji.
rametr znakiem & w wywołaniu albo w definicji funkcji. Nazwy wywoływanych funkcji
mogą być ustalane w czasie pracy programu poprzez użycie zmiennej jako nazwy funk-
cji — pozwala to na traktowanie funkcji jak danej i przekazywanie jej jako parametru
innych funkcji.
Rozdział 9.
Ciągi i funkcje
operujące na ciągach
W tym rozdziale:
* Tworzenie ciągów i manipulowanie nimi
+ Sprawdzanie, porównywanie i przeszukiwanie ciągów
* Zaawansowane operacje na ciągach przy użyciu wyrażeń regularnych
* Funkcje obsługi kodu HTML
Mimo że rysunki, dźwięki, animacje i aplety stają się coraz ważniejszą częścią sieci
WWW, nadal bazuje ona na tekście. Podstawowym typem PHP przechowującym tekst
jest typ string.
Ciągi w PHP
Ciągi to sekwencje znaków traktowane jako odrębna jednostka. Mogą być przypisywa-
ne do zmiennych, używane jako parametry funkcji, zwracane z funkcji lub wysyłane na
wyjście i oglądane w przeglądarce klienta.
Najprostszą metodą stworzenia ciągu w PHP jest otoczenie znaków cudzysłowami (")
bądź apostrofami ('):
$ciag = 'Ciąg1;
$inny_ciag = "Inny ciąg";
Można uzyskać znak z ciągu, traktując ciąg jak tablicę z pierwszym indeksem równym
0. Uzyskany w ten sposób znak jest jednoliterowym ciągiem. Na przykład:
$ciag - "Podwojony";
for (Sindex = O ; $index < 9; $index++ )
print("$ciag[$index]$ciąg t $index]") ;
daje w efekcie:
PPooddwwooj joonnyy
Każda litera została wypisana dwa razy (liczba 9 wpisana jest w tym przykładzie dlatego,
że nie wiemy jeszcze, jak określić długość ciągu — ponatrz r.a opis funkcji strlen ()
w części „Sprawdzanie ciągów").
da w efekcie:
Chciałbym podzielić się z wami moimi uwagami...
Złączenie i przypisanie
PHP posiada operator skrócony (.=) który jest złączeniem z przypisaniem. Wyrażenie:
$ciag .= $dodatek;
jest równoznaczne z:
$ciag = Ściąg . Sdodatek;
Przy użyciu tego operatora nowy ciąg jest dodawany po prawej stronie starego. Jeżeli
chcesz zmienić kolejność, musisz użyć dłuższej formy:
$ciag = Sdodatek . Ściąg;
Sprawdzanie ciągów
Jakie pytania można zadać na temat ciągu? Na początek sprawdźmy przy użyciu funkcji
strlen ( ) , ile znaków zawiera.
Ściąg = "Ten ciąg zawiera 26 znaków";
print("To ma", strlen(Ściąg)."znaków");
Odczytanie długości ciągu jest użyteczne w sytuacjach, gdy chcemy za pomocą pętli
dostać się do wszystkich znaków ciągu. Bezużytecznym, ale pouczającym przykładem
może być (używamy ciągu z poprzedniego przykładu):
for (Sindex=0; $index <strlen ($ciag); $index++)
print("$ciag[$index]");
Pozycja litery 'q' pozostała niewypełniona, ponieważ funkcja strpos () zwraca FALSE
w przypadku nieznalezienia znaku, a FALSE zostało skonwertowane do pustego ciągu.
Za pomocą funkcji strpos ( ) można również szukać ciągów, nie tylko pojedynczych
znaków. Możesz także podać dodatkowy parametr numeryczny określający pozycję, od
której funkcja ma rozpocząć szukanie.
Możliwe jest też szukanie wstecz przy użyciu funkcji strrpos ( ) (dodatkowe 'r' po-
chodzi od słowa „reverse", czyli w tył). Funkcja ta pobiera ciąg do szukania oraz poje-
dynczy znak, którego szukamy. W przeciwieństwie do strpos ( ) , nie można podać
podciągu do odszukania. Jeżeli użyjemy tej funkcji w naszym przykładowym zdaniu,
odszukamy inne wystąpienie litery k.:
Stwister = "Król Karol kupił królowej Karolinie";
print("'k' występuje na pozycji", strrpos(Stwister, 'k'). "<BR>");
Rozdział 9. » Ciągi i funkcje operujące na ciągach________________________159
Porównywanie i przeszukiwanie
Często trzeba sprawdzać, czy dwa ciągi są identyczne. Szczególnie często w programach
pracujących na danych wprowadzonych przez użytkownika.
Najprostszą metodą porównania ciągów jest zastosowanie operatora równości (==), któ-
ry porównuje zarówno liczby, jak i ciągi.
Ciągi są takie same dla operatora ' = = ' , jeżeli zawierają dokładnie
taką samą sekwencję znaków. Operator ten nie wykonuje żadnych
dokładniejszych porównań (np. sprawdzenia obszarów pamięci).
Podstawową funkcją do porównywania ciągów jest strcmp ( ) . Posiada dwa ciągi jako
argumenty, porównuje je znak po znaku aż do znalezienia różnicy. Funkcja zwraca
wartość ujemną, jeżeli pierwszy ciąg jest „mniejszy" od drugiego, wartość dodatnią,
gdy drugi ciąg jest mniejszy, oraz O, jeżeli ciągi są identyczne.
160 Część lI »» Podstawy
Podstawy PHP
PHP
Porównanie ciągów za pomocą operatora ' = = ' (a także < i >) jest
prawidłowe tylko w przypadku, gdy oba argumenty są takimi ciągami,
nie wykonano żadnej konwersji typów (więcej na ten temat w rozdzia-
le 6.). Jedynie używając funkcji strcmpo otrzymujemy zawsze pra-
widłowe wyniki.
Funkcja strcasecmpO nie bierze pod uwagę wielkości liter podczas porównywania
ciągów. Wywołanie funkcji: strcasecmp ( " h e j ! " , " H E J ! " ) powinno zwrócić 0.
Przeszukiwanie
Funkcje porównujące ciągi sprawdzają, czy ciągi są identyczne. Aby sprawdzić, czy je-
den ciąg zawiera się w drugim, używamy funkcji strpos () (opiszemy ją później) lub
strstr () (ewentualnie jednej z jej odmian).
Puste miejsce za dwukropkiem w drugim wierszu wyniku jest wynikiem próby wydru-
kowania wartości FALSE, która została skonwertowana na pusty ciąg.
Funkcja strstr () posiada alternatywną nazwę strchr (). Można używać dowolnej
z nazw tej funkcji, wynik działania będzie identyczny.
Podobnie jak strcmp (), strstr () posiada odmianę, która przy szukaniu ciągów iden-
tycznie traktuje małe i wielkie litery — stristr (). Funkcja ta działa identycznie jak
strstr (), nie rozróżnia jednak wielkości liter.
Tabela 9.1.
Funkcje porównujące, przeszukujące i badające ciągi
Funkcja Opis
Wycinanie podciągu
Wiele z funkcji operujących na ciągach ma na celu wybranie określonego podciągu lub
modyfikację go w ciągu oryginalnym. Warto wiedzieć, że większość funkcji modyfiku-
jących ciąg nie zmienia oryginalnego ciągu, ale zwraca zmienioną kopię pozostawiając
oryginał nietknięty.
Podstawową metodą wycięcia fragmentu ciągu jest użycie funkcji substr ( ) , która
zwraca ciąg będący określonym fragmentem ciągu przekazanego jako argument. Oprócz
ciągu, na którym operuje, funkcja wymaga podania liczby określającej początek wyci-
nanego ciągu. Trzeci argument jest opcjonalny i określa długość wynikowego podciągu.
Jeżeli nie zostanie on podany, funkcja zwraca fragment od podanej pozycji (za pomocą
drugiego argumentu) do końca ciągu. Należy pamiętać, że pierwszy znak ciągu znajduje
się na pozycji 0.
Oba argumenty numeryczne, pozycja początkowa i długość mogą być ujemne. Jeżeli
pozycja początkowa jest ujemna, oznacza to, że początek podciągu jest określany wzglę-
dem końca ciągu. Pozycja -l oznacza początek ciągu na ostatnim znaku, -2 na przed-
ostatnim itd.
W wyniku otrzymujemy:
3: defghijklmnop
-3: nop
3,5: defgh
3,-5: defghijk
-3,-5:
-3, 5: nop
•*&
pJ^fejL W przykładzie z pozycją początkową -3 i długością -5 pozycja końco-
jjggsjf wa znajduje się przed pozycją początkową, co jest sytuacją określaną
•^- jako „ciąg o ujemnej długości". W podręczniku, dostępnym pod adre-
sem http://www.php.net/manual, jest napisane, że w takiej sytuacji
funkcja substr o zwróci ciąg zawierający jeden znak znajdujący się
na pozycji początkowej. Zamiast tego widzimy, że PHP 4.0.0 zwraca
pusty ciąg. Należy szczególnie zwrócić uwagę na takie sytuacje.
Funkcje porządkujące
Mimo że funkcje c h o p ( ) , l t r i m ( ) i t r i m ( ) są zwykłymi funkcjami wycinającymi
podciąg, używa się ich do porządkowania ciągów. Funkcje te wycinają znaki odstępów
z początku, końca lub początku i końca ciągu. A oto przykład:
Rozdział 9. » Ciągi i funkcje operujące na ciągach________________________163
W przeglądarce dostajemy:
Ciąg oryginalny: ' To przechodzi ludzkie pojęcie '
Długość ciągu:33
Po funkcji chop: ' To przechodzi ludzkie pojęcie'
Długość ciągu:30
Po funkcji Itrim: 'To przechodzi ludzkie pojęcie '
Długość ciągu:32
Po funkcji trim: 'To przechodzi ludzkie pojęcie1
Długość ciągu:29
Ciąg oryginalny ciąg ma na końcu trzy spacje (usuwane przez chop ( ) i trim ( ) ) oraz
jedną na początku (usuwana przez Itrim ( ) i t r i m ( ) ) . Gdyby nazewnictwo funkcji
było spójne, funkcja chop ( ) powinna nazywać się rtrim ( ) . Dokładniej opiszemy za-
wartość okna przeglądarki po wykonaniu tego fragmentu. Powtarzające się odstępy zo-
stały przez przeglądarkę połączone w jeden. Jeżeli jednak zajrzysz do źródła strony, to
na końcu ciągu nadal znajdują się trzy odstępy.
Oprócz spacji funkcje te usuwają znaki zapisane jako sekwencje sterujące \n, \r, \t
oraz \ O (znaki końca wiersza, tabulatory oraz znak końca ciągu używany w programach
napisanych w C).
Mimo że nazwa funkcji chop ( ) (ang. rąbać) sugeruje destrukcyjne działanie, nie wpływa
ona na ciąg przekazany jako argument. Po wykonaniu funkcji ciąg nie zmienia się.
Zastępowanie ciągów
Funkcje operujące na ciągach, które omówiliśmy do tej pory, zwracały fragment ciągu
przekazanego jako argument, zamiast tworzyć całkowicie nowy ciąg. Teraz zajmiemy
się funkcjami str_replace ( ) i substr_replace ( ) .
Musisz zwrócić uwagę na sytuację, gdy wiele miejsca wystąpienia ciągu nachodzi na
siebie. Fragment programu:
$ciag = "ABA jest częścią ABABA";
Swynik = str_replace("ABA", "DBF", $ciag);
print("Wynik zamiany to '$wynik'<BR>"};
wyświetli ciąg:
Wynik zamiany to 'DEF jest częścią DEFBA'
daje w wyniku:
AB-FG
Fragment CDE został zamieniony na pojedynczy znak minus. Możemy więc zastępo-
wać fragmenty ciągu ciągami o innej długości. Jeżeli nie zostanie podana długość frag-
mentu, wymieniony zostanie fragment ciągu od pozycji startowej do końca ciągu.
Mamy również kilka rzadziej używanych funkcji, które tworzą nowy ciąg na podstawie
podanego jako parametr. Funkcja strrev () zwraca ciąg podany jako argument, ale pi-
sany wspak. Funkcja str_repeat ( ) tworzy ciąg zawierający ciąg źródłowy powtórzo-
ny daną liczbę razy. Przykładowo:
print(str_repeat("witaj ", 3));
daje w wyniku:
witaj witaj witaj
Tabela 9.2.
Funkcje przeszukujące i zamieniające ciąg
Funkcja Działanie
substr ( ) Zwraca fragment ciągu opisany przez drugi argument, określający pozycję
początkował opcjonalny trzeci argument, określający długość. Fragment
rozpoczyna się na pozycji startowej i ma długość podaną w trzecim argumencie,
a jeżeli nie został podany trzeci argument, obejmuje ciąg do końca. Ujemna
wartość pozycji startowej oznacza, że jest określana od końca ciągu, ujemny
parametr określający długość powoduje, że koniec podciągu jest wyznaczany
przez podaną liczbę znaków od końca ciągu
chop ( ) Zwraca ciąg podany jako argument bez końcowych znaków odstępu. Znakami
odstępu są znaki " ", \n, \r, \t i \0
itrim ( ) Zwraca ciąg podany jako argument bez początkowych znaków odstępu
trim ( ) Zwraca ciąg podany jako argument bez początkowych i końcowych znaków
odstępu
str_replace ( ) Zamienia podany fragment ciągu na inny. Funkcja ma trzy argumenty: ciąg do
odszukania, ciąg, na który jest on zamieniany oraz ciąg bazowy. Zwraca kopię
z zamienionymi na drugi argument wszystkimi miejscami wystąpienia pierwszego
argumentu
substr_replace ( ) Wstawia fragment podany jako argument na pozycję określoną przez parametry
numeryczne. Funkcja posiada cztery argumenty: ciąg bazowy, fragment
wstawiany, pozycję początkową i liczbę znaków do wymiany. Zwraca kopię ciągu
podanego jako pierwszy argument, z ciągiem do zamiany wstawionym na
określonej pozycji
Jeżeli opuszczony zostanie czwarty argument, koniec ciągu zostanie zamieniony
na fragment przekazany w argumencie. Ujemne wartości parametrów traktowane
są identycznie jak w funkcji substr ( )
Pierwsza z nich to funkcja strspn ( ) , za pomocą której można określić, jaka część cią-
gu składa się tylko ze znaków podanych jako argument. Przykładowo:
$twister = "Król Karol kupił królowej Karolinie";
Sznaki = "Król kupił Karolinie";
print {"Fragment zawierający '$znaki' ma" .
strspn($twister, $znaki). "znaki");
daje w wyniku:
Fragment zawierający 'Król kupił Karolinie' ma 22 znaki
Funkcja strcspn ( ) działa bardzo podobnie, ale szuka znaków, które nie znajdują się
w podanym ciągu. Na przykład wywołanie:
echo (strcspn($twister, "abcd"));
spowoduje wypisanie liczby 6, ponieważ pierwszych sześć znaków ciągu nie zawiera
znaków z ciągu „abcd".
Na koniec przyjrzyjmy się fragmentowi, który wylicza statystykę liter (przykład korzy-
sta z kilku nieopisanych jeszcze własności ciągów).
$twister = "Król Karol kupił królowej Karolinie";
print ("Stwister<BR>");
$letter_array = count_chars(Stwister, 1);
while ($cell = each($letter_array))
(
$letter = chr(Scell['key']);
$frequency = $cell['value'];
print("Znak: '$letter'; liczba: $ requency<BR>");
)
Tabela 9.3.
Funkcje analizujące znaki zawarte w ciągu
Funkcja Opis
count_chars ( ) Wymaga ciągu znaków oraz liczby oznaczającej tryb pracy (od O do 4). Podaje raport
częstości występowania znaków w ciągu, zwracając tablicę lub ciąg
strspn ( ) Wymaga dwóch ciągów. Zwraca długość początkowego fragmentu pierwszego ciągu,
który można utworzyć z znaków zawartych w drugim ciągu
strcspn ( ) Wymaga dwóch ciągów. Zwraca długość początkowego fragmentu pierwszego ciągu,
który można utworzyć ze znaków, których nie ma w drugim ciągu
Funkcje analizujące
Czasami trzeba podzielić ciąg na fragmenty, biorąc pod uwagę różny sposób definio-
wania tych fragmentów. Proces podziału długiego ciągu na „słowa" jest zwany analizą,
jest częścią interpretacji lub kompilacji zachodzącej przy pisaniu każdego programu,
także PHP. PHP posiada funkcję, która realizuje taką operację — strtok ( ) .
Funkcja strtok ( ) posiada dwa argumenty: ciąg przeznaczony do podziału oraz ciąg
zawierający wszystkie znaki podziału (znaki występujące pomiędzy częściami). Pod-
czas pierwszego wywołania używane są oba argumenty, a funkcja zwraca pierwszy
fragment. Aby odczytać kolejne fragmenty, należy ponownie wywołać funkcję bez po-
dawania ciągu. Ciąg jest zapamiętany jako ciąg bieżący, a funkcja zapamiętuje, w któ-
rym miejscu skończyła go analizować podczas poprzedniego wywołania. Rozpatrzmy
następujący przykład:
?token = strtok(
"klient-serwer CD-ROM baza danych", " ");
while {Stoken)
{
print(Stoken. "<BR>");
Stoken = strtok(" ");
.)
Początkowy ciąg został podzielony w miejscu każdej spacji. Zmieńmy teraz znak se-
paratora:
Stoken = strtok(
"klient-serwer CD-ROM baza danych", "-");
while ($token)
(
print(Stoken. "<BR>");
Stoken = s t r t o k ( " - " ) ;
}
klient
serwer CD
ROM baza danych
Możemy również podzielić ten ciąg we wszystkich tych miejscach, jednocześnie poda-
jąc separator w postaci" -".
Stoken = strtok(
"klient-serwer CD-ROM baza danych", " -");
while ($token)
(
print($token. "<BR>");
$token = s t r t o k t " -");
}
Funkcja explode ( ) wymaga dwóch argumentów: ciągu separatora oraz ciągu do po-
dzielenia. Zwraca ona tablicę zawierającą kolejne fragmenty ciągu. Przykład wywołania
funkcji:
$wynik = explode!",", "jeden, dwa, t r z y " ) ;
Tablica $wynik po wykonaniu tego wiersza programu będzie zawierał trzy elementy:
"jeden", "dwa" i "trzy".
Ponieważ cały separator jest usuwany podczas pracy explode ( ) , funkcja ta może być
podstawą wielu ciekawych zastosowań. Poniższy przykład, przytaczany w dokumenta-
cji PHP, dla wygody używa krótkich ciągów; należy pamiętać, że mogą mieć prawie
dowolną długość. Funkcja explode ( ) jest szczególnie użyteczna w przypadku długich
ciągów, kiedy analizowanie w inny sposób może być nużące. Policzymy miejsca wy-
stąpienia podanego ciągu w pliku tekstowym dzięki wczytaniu pliku do ciągu i użyciu
funkcji explode ( ) (w tym przykładzie użyliśmy kilku nieomówionych jeszcze funkcji,
ale mamy nadzieję, że ich działanie będzie jasne).
Rozdział 9. » Ciągi i funkcje operujące na ciągach__________________________169
<?php
// Wczytujemy plik tekstowy do zmiennej $filestring
Sfilename = "hardware_jDooks.html";
Sfd = fopen($filename, "r");
$filestring = fread($fd, filesize(Sfilename));
fclose(Sfd);
// Podzielimy na części, używając znacznika <TABLE> jako separatora
$pieces = explode("<TABLE", Sfilestring);
// Policzenie liczby fragmentów
$num_pieces = count(Spieces);
strtolowerO
Funkcja strtolower ( ) zwraca ciąg ze wszystkimi małymi literami. Nie ma znaczenia, czy
na początku cały ciąg był zapisany wielkimi literami, czy wielkie i małe były wymieszane.
<?php
Soryginalny = "On NIE wiE, że KRZYCZY";
Smalę = strtolower(Soryginalny);
echo Smalę;
?>
strtoupper()
Funkcja strtoupper ( ) zwraca ciąg z wszystkimi wielkimi literami. Nie ma znacze-
nia, czy na początku cały ciąg był zapisany małymi literami, czy wielkie i małe były
wymieszane.
<?php
Soryginalny = "napisz to wyraźnie";
echo("<B>".strtoupper($oryginalny)."</B>");
?>
ucfirst()
Funkcja ucf irst () zmienia pierwszą literę ciągu na wielką.
<?php
Soryginalny = "przykładowe zdanie.";
echo(ucfirst(Soryginalny)};
?>
ucwordsQ
Funkcja ucwords ( ) zamienia pierwsze litery wyrazów w ciągu na wielkie.
<?php
Soryginalny = "miasto nowy Jork";
echo( ucwords(Soryginalny));
?>
^^\ Zarówno ucwords o, jak i ucf irst o nie konwertują liter na małe.
^-A Zmieniają tylko właściwe początkowe litery na wielkie. Jeżeli w środku
ciągu wystąpią wielkie litery, nie zostaną zamienione.
echo Squote;
?>
Na przykład:
$literal= 'Znaki ($, *) maja dla mnie szczególne znaczenie \n<BR>';
$qm = quotemeta($literal};
echo $qm;
Formatowanie danych
Podstawowymi konstrukcjami używanymi do drukowania danych są print i echo,
które zostały opisane w rozdziale 5. Zwykle wartości zmiennych są wypisywane po-
przez wbudowywanie zmiennych w ciągi otoczone cudzysłowami (i są zamieniane na
wartości) i przekazywanie takiego ciągu do instrukcji echo lub print.
Jedyną różnicą pomiędzy printf ( ) i sprintf ( ) jest to, że printf ( ) wysyła wynik
bezpośrednio na wyjście, a sprintf ( ) zwraca wynik jako ciąg.
172_________________________________________Część l * Podstawy PHP
Najbardziej skomplikowaną częścią tych funkcji jest ciąg formatujący. Każdy znak, jaki
umieścisz w tym ciągu, pojawi się w ciągu wynikowym, oprócz sekwencji znaków roz-
poczynających się od %. Znak % oznacza początek „specyfikacji konwersji", która
wskazuje sposób wydruku argumentu odpowiadającego tej specyfikacji.
Przykładem może być drukowanie tej samej liczby rzeczywistej na różne sposoby:
<pre>
<?php
$wartosc = 3.14159;
printf("%f, %10f, %-010f, %2.2f\n", $wartosc, $wartosc, $wartosc, $wartosc);
?>
</pre>
Wyrażenia regularne
Pełny opis wyrażeń regularnych wykracza poza ramy tego rozdziału. W zamian poka-
żemy, do czego mogą się one przydać. Podamy też kilka przykładów ich użycia.
Po krótkim przemyśleniu staje się jasne, że nie ma wygodnego sposobu na użycie po-
równania ciągów i wycinania podciągów do stworzenia odpowiedniego testu. Możemy
sprawdzić, czy istnieje www. oraz .com, ale trudno sprawdzić to, co jest w środku. Do
tego świetnie nadają się wyrażenia regularne.
Przy użyciu tych zasad stworzyliśmy wyrażenie, które pasuje do interesujących nas ad-
resów WWW:
"www\.[a-z]+\.com$
Na początku tego wyrażenia mamy znak A, który wskazuje, że na początku ciągu powinny
znajdować się znaki www. Następnie znajduje się kropka poprzedzona znakiem bac-
kslash, który wskazuje, że w tym miejscu naprawdę powinna być kropka, a nie specjalny
znak zastępujący dowolny znak w porównywanym ciągu. Kolejnym fragmentem wyraże-
nia jest otoczony nawiasami kwadratowymi zakres liter, obejmujący wszystkie małe lite-
ry. Co najmniej jedna z tych liter musi wystąpić tutaj jeden lub więcej razy. Na koniec
w ciągu musi wystąpić fragment .com. Znak specjalny $ wskazuje, że .com kończy ciąg.
Tak skonstruowanego wyrażenia użyjemy jako argumentu funkcji ereg ( ) , która wy-
maga ciągu zawierającego wyrażenie regularne oraz ciągu z nim porównywanego jako
argument. Napiszmy funkcję sprawdzającą, przy użyciu funkcji ereg ( ) , interesujące
nas adresy WWW.
function simple_dot_com(Surl)
(
return (ereg('"www\.[a-z]+\.com$', $url));
)
Funkcja ta zwraca TRUE lub FALSE w zależności od tego, czy udało jej się porównanie
z naszym wzorcem. Użyjmy tej funkcji do weryfikacji kilku wymienionych wcześniej
adresów (program przegląda tablicę w sposób, który dokładnie opiszemy w następnym
rozdziale).
$urls_to_test =
array{'www.ibm.com', 'www.java.sun.com', 'www.zend.com',
'java.sun.com', 'www.php.net', 'www.IBM.com',
'www.adresy WWW nie mają odstępów.com');
while ($test = array_pop($urls_to_test))
(
if (simple_dot_com($test))
print ("\"$test\"jest prostym adresem ,com<BR>");
else
print ("\"$test\"NIE jest prostym adresem .com<BR>");
}
Tabela 9.4.
Funkcje korzystające z wyrażeń regularnych
Funkcja Opis
ereg ( ) Wymaga dwóch argumentów i opcjonalnie trzeciego. Pierwszy ciąg jest wyrażeniem
regularnym w stylu POSIX, drugi jest porównywanym ciągiem. Funkcja zwraca
TRUE, jeżeli porównanie się powiodło. Jeżeli porównanie nie udało się, zwraca
FALSE. Jeżeli w trzecim argumencie zostanie podana tablica, a fragmenty wzorca
są otoczone nawiasami, części porównywanego ciągu, które pasują do kolejnych
fragmentów w nawiasach, są kopiowane do kolejnych komórek tablicy
ereg_replace ( ) Wymaga trzech argumentów: wyrażenia regularnego w stylu POSIX, ciągu, który
jest wstawiany, oraz ciągu, na którym przeprowadzane jest wstawianie. Funkcja
przegląda ciąg podany jako trzeci argument i zamienia fragmenty pasujące do
wzorca ciągiem podanym jako drugi argument. Zwracany jest zmodyfikowany ciąg
Jeżeli we wzorcu wy stępuj ą części otoczone nawiasami (jak w ereg ( ) ) , ciąg
wstawiany może zawierać specjalny fragment '\\cyfra' (dwa znaki backslash
i pojedyncza cyfra), zamieniany na odpowiedni fragment ciągu wynikowego
eregi < > Funkcja działa identycznie jak ereg ( ) , ale litery w wyrażeniu regularnym są
porównywane niezależnie od wielkości
eregi_replace ( ) Funkcja działa identycznie jak ereg^replace ( ) , ale litery w wyrażeniu
regularnym są porównywane niezależnie wielkości
split () Wymaga wzorca, ciągu źródłowego i opcjonalnego limitu fragmentów. Zwraca
tablicę ciągów utworzonych przez podział ciągu źródłowego na fragmenty,
rozdzielone fragmentami pasującymi do wyrażenia regularnego. Jest to funkcja
analogiczna do explode ( ) poza tym, że separator nie jest literałem, ale
wyrażeniem regularnym
Funkcje HTML
Na koniec przedstawimy kilka funkcji manipulujących ciągami, które są specyficzne dla
pracy ze stronami WWW.
Tabela 9.5.
Funkcje specyficzne dla pracy ze stronami WWW
Funkcja Opis
html special char s () Pobiera jako argument ciąg i zwraca ciąg z zamienionymi czterema znakami,
które mają specjalne znaczenie w kodzie HTML. Każdy z tych znaków jest
zamieniony na odpowiadającą mu jednostkę HTML, która wygląda
w przeglądarce identycznie jak oryginał. Znak & jest zamieniany na &,
"" (cudzysłów) jest zamieniany na ", < zamieniany jest na < a > na >
htmlentities ( ) Zamienia wszystkie znaki posiadające jednostki HTML na te jednostki
ge t_html_ Wymaga jednej z dwóch starych (HTML_SPECI AL_CHARS,
translation_table () HTML_ENTITIES) i zwraca tablicę konwersji, używaną przez funkcje
htmlspecialcharsO i htmlentities(). Kluczami tablicy konwersji są znaki,
a wartościami ich zamienniki
n!2br ( ) Wymaga ciągu jako argumentu. Zwraca ciąg z zamienionymi wszystkimi
miejscami wystąpienia \n na <BR>. Pomaga przy obliczaniu długości akapitów
wyświetlanych później w przeglądarce
strip_tags () Pobiera jako argument ciąg i zwraca ten ciąg usuwając znaczniki HTML i PHP
Podsumowanie
Ciągi są sekwencjami znaków. Jest to jeden z sześciu podstawowych typów danych
w PHP. W przeciwieństwie do niektórych innych języków, nie ma tu osobnego typu
znakowego, pojedynczy znak zachowuje się jak ciąg o długości l. Ciągi w kodzie są
otaczane apostrofami (') lub cudzysłowami ("). Ciągi otoczone apostrofami są interpre-
towane prawie dosłownie, a ciągi otoczone cudzysłowami interpretują kilka sekwencji
sterujących i automatycznie wstawiaj ą wartości zmiennych.
Podstawowym operatorem ciągów jest ' . ', który łączy dwa ciągi. Istnieje dodatkowo
spora gama funkcji, które umożliwiają sprawdzanie, porównywanie, szukanie, wycina-
nie, zamianę zawartości ciągu. PHP udostępnia także wyrażenia regularne, zgodne ze
standardem POSIX i Perl, do zaawansowanych zastosowań.
Rozdział 10.
Matematyka
W tym rozdziale:
* Podstawowe typy numeryczne i operacje arytmetyczne
** Funkcje wykładnicze, trygonometryczne i funkcje konwersji podstawy
* Generowanie liczb losowych
* Arytmetyka o dowolnej dokładności
Jeżeli chcesz wykonywać poważne obliczenia naukowe lub statystyczne, języki skryp-
towe nie są do tego odpowiednim narzędziem. Mimo tego PHP zawiera szeroką gamę
funkcji, które zapewniają wykonanie większości zadań matematycznych. Zapewnia aryt-
metykę o dowolnej dokładności czy dostęp do bibliotek funkcji mieszających i krypto-
graficznych.
Projektanci PHP nie próbowali dodawać nowości w tej dziedzinie. Wiele z podstawo-
wych funkcji matematycznych w PHP to proste odpowiedniki analogicznych funkcji w C
(więcej na ten temat w przypisie „Rzut oka za kurtynę" poniżej).
Typy numeryczne
PHP posiada tylko dwa typy numeryczne: integer (znany również jako long) oraz
double (float), które odpowiadają największym typom numerycznym w C. PHP
automatycznie konwertuje typy numeryczne, mogą więc być bezpiecznie mieszane
w wyrażeniach arytmetycznych; zwykle dają prawidłowy wynik. PHP potrafi również
w razie potrzeby skonwertować ciąg na liczbę.
Jeżeli chcesz, aby wartość została zinterpretowana jako wartość określonego typu nume-
rycznego, skorzystaj z rzutowania typu, poprzedzając zmienną nazwą typu w nawiasach:
(double) $zmierma
(integer) Szmienna
178_________________________________________Część l » Podstawy PHP
Możesz również użyć funkcji intval () i doubleval (), które skonwertują argument
wywołania na odpowiedni typ numeryczny.
Operatory matematyczne
Większość operacji matematycznych w PHP jest realizowana przez wywołania funkcji,
a nie przy użyciu operatorów. Oprócz operatorów porównania, opisanych w rozdziale 7.,
PHP zawiera pięć operatorów realizujących proste działania oraz kilka operatorów „skró-
towych", pozwalających na bardziej zwięzłe przypisywanie i zwiększanie wartości.
Operatory arytmetyczne
Pięć operatorów dostępnych w PHP to cztery operatory dostępne w prostym cztero-
działaniowym kalkulatorze oraz operator dzielenia modulo (%). Operatory te zostały
opisane w tabeli 10.1.
Tabela 10.1.
Operatory arytmetyczne
będzie tego typu. Jeżeli jeden z argumentów będzie typu double, wynik również będzie
typu double. W przypadku dzielenia nastąpi też zmiana typu na double, jeżeli argu-
menty nie dzielą się bez reszty przez siebie.
Operator modulo
Arytmetyka modulo jest czasami nazywana w szkole „arytmetyką zegara". Proces
dzielenia modulo przez liczbę jest podobny do „owijania" pierwszego argumentu do-
okoła drugiego. Wynik tej operacji jest zawsze mniejszy od drugiego argumentu.
Można to porównać do zwykłego zegara analogowego, który pokazuje czas modulo 12,
natomiast „wojskowe" zegary pokazują czas modulo 24 (nie jest to dokładnie to samo,
ponieważ dzielenie modulo daje wynik od O do n-1, a nie od l do n).
Operator modulo w PHP (%) oczekuje argumentu typu integer i jeżeli otrzyma war-
tość double, najpierw konwertuje ją do typu integer, odrzucając część ułamkową.
Wynikiem działania jest zawsze liczba całkowita.
Większość języków programowania posiada operator modulo. Zwykle różnią się obsłu-
gą ujemnych argumentów. W niektórych językach wynik działania jest zawsze dodatni
i-2 % 26 jest równe 24. W PHP-2 % 26 wynosi -2, a wyrażenie $mod = $first
% $ second jest równoważne wyrażeniu:
if ($first >=0)
$rood = $first % abs(Ssecond);
else
$mod = -(abs(Sfirst) % abs(Ssecond));
Operator inkrementacji
PHP korzysta z większości składni C, a wiadomo, że programiści C są dumni ze zwię-
złości. Zapożyczone z C operatory inkrementacji i dekrementacji pozwalają na zwięzły
zapis konstrukcji $count=$count+l, które wy stępuj ą bardzo często.
Scount •= 0;
$result - $count++;
print("Przyrostkowy++: count wynosi: $count, result wynosi: Sresult<BR>") ;
$count = 0;
$result = ++$count;
print("Przedrostkowy + +: count wynosi: $count, result wynosi: $result<BR>");
$count = 0;
Sresult = $count--;
print("Przyrostkowy —: count wynosi: Scount, result wynosi: Sresult<BR>");
Scount = 0;
Sresult = —Scount;
print("Przedrostkowy —: count wynosi: Scount, result wynosi: Sresult<BR>");
Operator przypisania
Operator inkrementacji ++ pozwala uniknąć pisania przy operacji dodawania jeden do
zmiennej. Nie pomoże, jeżeli chcemy dodać dowolną inną liczbę lub wykonać inną ope-
rację arytmetyczną. Na szczęście wszystkie operatory arytmetyczne posiadają odpo-
wiednie operatory przypisania (+=, -=, *=, /= i %=), które przypisują zmiennej wynik
operacji arytmetycznej na niej samej. Wyrażenie następujące:
Scount = Scount * 3;
natomiast wyrażenie:
Scount = Scount + 17;
odpowiada operacji:
Scount += 17;
Operatory porównania
PHP zawiera zwykle arytmetyczne operatory porównania, które wymagają prostych
wartości (liczb lub ciągów) jako argumentów; zwracaj ą wartość TRUE bądź FALSE:
Rozdział 10. » Matematyka_______________________________________181
* Operator < (mniejszy niż) zwraca wartość TRUE, jeżeli wartość po lewej stronie
jest mniejsza od wartości po prawej stronie. W przeciwnym przypadku zwraca
FALSE.
** Operator <= (mniejszy lub równy) zwraca wartość TRUE, jeżeli wartość po le-
wej stronie jest mniejsza bądź równa wartości po prawej stronie. W przeciw-
nym przypadku zwraca FALSE.
* Operator > (większy niż) zwraca wartość TRUE, jeżeli wartość po lewej stronie
jest większa od wartości po prawej stronie. W przeciwnym przypadku zwraca
FALSE.
* Operator >= (większy lub równy) zwraca wartość TRUE, jeżeli wartość po le-
wej stronie jest większa lub równa od wartości po prawej stronie. W przeciw-
nym przypadku zwraca FALSE.
* Operator == (równy) zwraca wartość TRUE, jeżeli argumenty są sobie równe.
W przeciwnym przypadku zwraca FALSE.
* Operator ! = (różny) zwraca wartość FALSE, jeżeli argumenty są sobie równe.
W przeciwnym przypadku zwraca TRUE.
+ Operator === (identyczny) zwraca wartość TRUE, jeżeli argumenty są sobie
równe i są tego samego typu. W przeciwnym przypadku zwraca FALSE.
Okazuje się, że wartością tego wyrażenia jest 2. Dużo łatwiej to stwierdzić, jeżeli di
damy nawiasy (prawdę mówiąc, niepotrzebne).
( (l + (2 * 3) - 4) - ( (5 / 4) % 3)
Tabela 10.2.
Proste funkcje matematyczne
Funkcja Opis
floor ( ) Wymaga jednego argumentu (zwykle rzeczywistego) i zwraca największą liczbę całkowitą,
mniejszą lub równą argumentowi
cel l ( ) Wymaga jednego argumentu (zwykle rzeczywistego) i zwraca najmniejszą liczbę całkowitą,
większą lub równą argumentowi
round ( ) Wymaga jednego argumentu (zwykle rzeczywistego) i zwraca najbliższą liczbę całkowitą.
Jeżeli część ułamkowa wynosi 0,5, zwracana jest najbliższa liczba parzysta
abs ( ) Wartość bezwzględna. Jeżeli argument jest ujemny, zwraca odpowiadającą mu wartość
dodatnią. Jeżeli argument jest dodatni, zwraca argument
m
in ( ) Pobiera dowolną liczbę argumentów numerycznych (ale co najmniej jeden) i zwraca
najmniejszy z nich
max ( ) Pobiera dowolną liczbę argumentów numerycznych (ale co najmniej jeden) i zwraca
największy z nich
Konwersja podstawy
Domyślną podstawą liczb w PHP, używaną do czytania i drukowania, jest 10. Dodat-
kowo można wczytywać liczby ósemkowe (o podstawie 8), umieszczając dodatkowe O
na początku liczby, lub liczby szesnastkowe (o podstawie 16), rozpoczynając liczbę
znakami Ox.
Tabela 10.3.
Funkcje konwersji podstawy
Funkcja Opis
BinDec ( ) Wymaga ciągu zawierającego binarną reprezentację liczby jako argumentu; zwraca
ciąg zawierający reprezentację tej liczby o podstawie 10
DecBin ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 10 na 2
OctDec ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 8 na 10
DecOct ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 10 na 8
HexDec ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 16 na 10
DecHex ( ) Działa jak BinDec ( ) , ale konwertuje podstawę 10 na 16
base_convert ( ) Jako argumentu wymaga ciągu (liczby do skonwertowania) i dwóch liczb, określających
podstawę liczby z pierwszego argumentu oraz wymaganą podstawę. Zwraca ciąg
reprezentujący liczbę o żądanej podstawie. Cyfry większe od 9 są reprezentowane
jako litery od a do z. Obie podstawy muszą zawierać się pomiędzy 2 a 36
display_bases("Ijj", 20);
Rozdział 10. » Matematyka_____________________________________185
Mimo że funkcje konwersji podstawy spodziewają się ciągu jako argumentu i zwracają
ciąg, możesz użyć jako argumentu liczby dziesiętnej i polegać na konwersji typów PHP
(przeczytaj ostrzeżenie poniżej). Inaczej mówiąc, oba wywołania DecBin ( " 1 2 3 4 " )
i DecBin (1234) dadzą ten sam wynik.
Tabela 10.4.
Funkcje wykładnicze
Funkcja Opis
^^> Stała matematyczna e (około 2,718) nie występuje w PHP, ale jej
\A przybliżenie można uzyskać jako wynik funkcji log < i).
Trygonometria
Mimo że nie objaśniamy natury obliczeń wykonywanych przez omawiane funkcje, tym
razem zrobimy wyjątek (spójrz na przypis „Trygonometria w jednym akapicie"). Jeżeli
ktoś nie wie nic na temat trygonometrii, nic nie skorzysta z tego opisu.
PHP oferuje standardowy zestaw funkcji trygonometrycznych oraz stałą M_P1, która
jest przybliżeniem liczby pi, liczbą typu double i posiada wartość 3,1415926535898
(jest to chyba jedyna stała matematyczna w PHP). Stała ta może być używana wszędzie
tam, gdzie jest potrzebna liczba/?; i może być używana zamiennie z funkcją pi ( ) .
Rozdział 10. » Matematyka_______________________________________187
Tabela 10.5.
Funkcje trygonometryczne
Funkcja Opis
Zamiast tworzyć tabelę przykładowych wyników tych funkcji, napiszmy program, który
automatycznie wyświetli wyniki w tabeli HTML. Na wydruku 10.1 zamieszczona jest
ogólna funkcja wyświetlająca zestaw wyników jednoargumentowych funkcji działających
na zbiorze argumentów. Następnie użyjemy tej funkcji do stworzenia tabeli przykłado-
wych wyników działania funkcji trygonometrycznych i odwrotnych funkcji trygonome-
trycznych. Wynik działania na rysunku 10.1.
?>
</BODY>
</HTML>
Rysunek 10.1.
Przykład
działania funkcji
trygonometrycznych
Liczby losowe
Funkcje generujące liczby losowe w PHP zebrane zostały w tabeli 10.6. Jeżeli potrze-
bujesz więcej wyjaśnień na temat ich losowości, przeczytaj objaśnienia w dopisku.
Tabela 10.6.
Funkcje liczb losowych
Funkcja Opis
srand () Jako argumentu wymaga dodatniej liczby całkowitej i inicjuje nią generator liczb
losowych
rand < > Wywołana bez argumentów zwraca liczbę „losową" z zakresu od O do RAND_MAX
(może zostać odczytana za pomocą funkcji getrandmax ( ) ) . Funkcja może być
wywołana z dwoma argumentami ograniczającymi zakres losowanych liczb:
pierwszy z nich jest minimum, drugi maksimum
getrandmax ( ) Zwraca największą liczbę, jaką można uzyskać za pomocą funkcji r a n d ( )
mt_srand ( ) jak srand ( ) , ale korzysta z „lepszego" generatora liczb losowych
mt_rand () j a k r a n d (), ale korzysta z „lepszego" generatora liczb losowych
mt_getrandmax ( ) Zwraca największą liczbę, jaką można uzyskać za pomocą funkcji mt_rand ( )
Istnieją dwa generatory liczb losowych, każdy z nich ma trzy takie same funkcje: ini-
cjującą, zwracającą liczbę losową oraz zwracającą największą liczbę, jaką można uzy-
skać z generatora.
Inicjowanie generatora
Typowym sposobem inicjowania generatorów liczb pseudolosowych w PHP jest użycie
funkcji mt_srand ( ) lub srand ( ) :
mt_srand( (double)microtime()*10000000);
Rozdział 10. » Matematyka_______________________________________191
Uruchamiając taki program, otrzymasz liczby różniące się od podanych, ponieważ ten
sposób inicjowania generatora daje odmienne wyniki.
192_________________________________________Część l » Podstawy PHP
mt_srand((double)microtime()*10000000) ;
$charset = "abcdefghijklmnopqrstuvwxyz";
W wyniku otrzymaliśmy:
Losowy ciąg: efwwrpzd
Losowy ciąg: zwewvdck
Losowy ciąg: jynhricn
mt_srand(43);
$random_string = random_string($charset, 8);
print("Losowy ciąg: $random_string<BR>") ;
mt_srand(43);
$random_string = random_string(Scharset, 8) ;
print("Losowy ciąg: $random_string<BR>") ;
W tym przykładzie wybieraliśmy losowe litery z ciągu, lecz proces ten może zostać
uogólniony do wybierania elementów np. z tablicy (lub dowolnego losowego elementu
zbioru). Musisz zdefiniować przestrzeń elementów, sposób ich ponumerowania i wy-
brania na podstawie wylosowanej liczby. Następnie można użyć funkcji rand ( ) lub
mt_rand ( ) do wyboru losowego numeru porządkowego elementu.
Funkcje BC wymagają ciągu jako argumentu, a także zwracają ciągi, zamiast używać
typów numerycznych o stałej wielkości. Ponieważ ciągi w PHP są ograniczone jedynie
dostępną pamięcią, oznacza to, że liczby mogą mieć dowolną długość. Obliczenia
wykonywane przez te funkcje są realizowane na liczbach dziesiętnych. Funkcje BC są
dokładne w działaniu na liczbach całkowitych, używają tylu cyfr, ilu potrzeba. W przy-
padku działań na liczbach rzeczywistych obliczenia są realizowane z taką dokładnością,
jakiej zażądasz. Funkcje BC przedstawione sąw tabeli 10.7.
Tabela 10.7.
Funkcje matematyczne o dowolnej dokładności (BC)
Funkcja Opis
Większość funkcji posiada opcjonalną dokładność obliczeń jako ostatni argument, okre-
ślającą, ile liczb po przecinku znajdzie się w wyniku. Jeżeli ten argument nie zostanie
podany, przyjęta będzie domyślna dokładność, która jest ustawiana wywołaniem funkcji
bcscale ( ) . Przed wywołaniem funkcji bcscale ( ) dokładność jest ustawiona na war-
tość umieszczoną w pliku php.ini.
Jeżeli użyjemy do tych obliczeń zwykłego typu całkowitego, zakres liczb całkowitych
zostanie przekroczony; pod koniec pętli obliczenia będą wykonywane na przybliżonych
liczbach zmiennoprzecinkowych.
Ciąg ten nie zbiega się wystarczająco szybko, ale jest bardzo prosty.
function pi_approx( $iterations, $print_frequency)
{
$squaręd_approx = 12;
$next_sign = -1;
$dęnom - 2;
Program co pewien czas wypisuje bieżące przybliżenie liczby pi, więc możemy spraw-
dzić w jaki sposób jest obliczana. Wywołajmy funkcję, wypisując dla porównania war-
tość liczby pi zaszytej w PHP.
pi_approx(10000, 1000);
print ("Wartość PHP: ". pil). "<BR>");
Aby zamienić naszą funkcję na wersję używającą funkcji dowolnej dokładności, należy
wymienić wszystkie funkcje matematyczne i operatory na odpowiadające im funkcje BC.
function pi_approx_bc( $iterations, $print_frequency, Sscale)
(
Ssquared_approx = "12";
$next_sign = -1;
Sdenora =2;
Mimo że funkcje BC wymagają ciągu jako argumentu, można zawsze użyć w ich miej-
sce zwykłych liczb, a PHP skonwertuje je do postaci ciągów. Nie używamy funkcji BC
do prostych obliczeń, które nie wymagają dużej dokładności (na przykład pozostawili-
śmy $denom++, nie zamieniliśmy go na bcadd ($denom, l)). Dodaliśmy również ar-
gument określający dokładność, z jaką realizowane są obliczenia w każdej z funkcji BC.
Niestety Autorzy nie mieli tyle cierpliwości, aby obliczyć za pomocą tego ciągu liczbę
pi z dokładnością dostępną w PHP. Poniżej kilka ostatnich wyników dla wywołania
pi_approx_bc(125000, 5000, 50):
Rozdział 10. » Matematyka_______________________________________197
Otrzymane wyniki różnią się od wartości pi zaszytej w PHP. Wiąże się to z wybranym
ciągiem przybliżającym, a nie z funkcjami BC. Można j ą przybliżyć za pomocą bardziej
skomplikowanych i szybszych ciągów.
print("Pierwiastek kwadratowy z dwóch wynosi:" . bcsqrt(2, 40));
Podsumowanie
Zagadnienia matematyki omówione w niniejszym rozdziale zostały zebrane w tabeli 10.8.
Tabela 10.8.
Zestaw operatorów i funkcji matematycznych w PHP
Zagadnienie Opis
Operatory porównania <, <=, >, >=, ==, ! =. Operator === jest prawdziwy, gdy oba argumenty są równe
i mają ten sam typ
Podstawowe floor (), ceil () oraz round () konwertują liczbę double na całkowitą, min (),
funkcje matematyczne ma x ( ) wybierają największą i najmniejszą wartość podanego parametru, abs ( )
podaje wartość bezwzględną liczby
Podstawowe Funkcje wyspecjalizowane: OctDec ( ) , DecOct ( ) , BinDec ( ) , DecBin ( ) ,
funkcje konwersji HexDec ( ) , DecHex ( ) konwertuj ą odpowiednie pary podstaw; base_convert ( )
konwertuje dowolne podstawy
198_________________________________________Część l » Podstawy PHP
Tabela 10.8.
Zestaw operatorów i funkcji matematycznych w PHP (ciąg dalszy)
Zagadnienie Opis
Użycie tablic
W tym rozdziale będziemy m.in. objaśniali sposób działania tablic i omawiali wszystkie
funkcje wbudowane, przeznaczone do manipulacji tablicami. Zanim to nastąpi, wylicz-
my zastosowania tablic w kodzie stron WWW.
200_________________________________________Część l » Podstawy PHP
W prawie każdej sytuacji, gdy odwołujesz się do kilku danych, które mogą zostać spa-
kowane do jednej struktury i używane w jednorodny sposób, użycie tablic będzie wła-
ściwym posunięciem.
To wystarczy, aby używać tablic do zapamiętywania par klucz i wartość. Jeżeli chcesz
zapamiętywać wartości w porządku numerycznym, musisz użyć liczb jako wartości
klucza:
$tablica[l] = 'Pierwszy element';
$tablica[2] = 'Drugi element';
Oprócz zapamiętywania par klucz i wartość, procedury obsługi tablic zawierają mecha-
nizmy pozwalające na traktowanie tablic jak innych struktur danych. Tablice mogą być
wielowymiarowe, co pozwala na zapisywanie wartości w połączeniu z sekwencją war-
tości kluczowych, a nie tylko z pojedynczą wartością. Tablice mogą automatycznie
utrzymywać alfabetyczny porządek zapisywanych elementów, niezależnie od wartości
ich klucza. Pozwala to na traktowanie tablic jak list. Przybliżymy te aspekty działania
tablic omawiając funkcje o specyficznych własnościach.
Rozdział 11. * Tablice i funkcje operujące na tablicach_______________________201
Wynikiem wykonania tych wierszy programu jest tablica zawierająca trzy war-
tości (l, 2, 3) połączone z kluczami (l, ' o r a n g e ' , 3).
Elastyczność tablic asocjacyjnych jest okupiona sporymi kosztami, ponieważ
wykonywanie kodu do momentu obliczenia adresu interesującego nas ele-
mentu zabiera więcej czasu niż w analogicznym przypadku dla wektorów.
W większości zastosowań skryptów WWW ten dodatkowy czas nie gra zbyt
wielkiej roli.
Ponieważ liczby całkowite mogą być stosowane jako klucze w tablicy asocja-
cyjnej, można imitować działanie wektora, używając wartości całkowitych ja-
ko kluczy.
Tworzenie tablic
W skrypcie PHP można utworzysz tablicę na trzy różne sposoby: przypisując wartość
do jednego klucza (tworząc ją tajnie), używając konstrukcji array ( ) oraz wywołując
funkcję zwracającą tablicę jako wartość.
Bezpośrednie przypisanie
Najprostszą metodą utworzenia tablicy jest przypisanie wartości do zmiennej mającej
być tablicą:
Stablicafl] = "Pierwszy element właśnie utworzonej tablicy";
Jeżeli $tablica była niezainicjowaną zmienną (lub zmienną nie będącą tablicą), po
wykonaniu tego wiersza będzie tablicą z jednym elementem. Jeżeli zmienna $tablica
była już tablicą, zostanie w niej zapamiętany ciąg wraz z kluczem o wartości całkowitej 1.
Jeżeli do tej pory nie był on związany z żadną wartością, zostanie utworzony nowy
element tablicy do przechowywania naszego ciągu. Jeżeli istniała wartość związana
z kluczem l, zostanie nadpisana. Można również przypisywać wartości do tablicy bez
podawania indeksu: $tablica [ ] (co zostanie opisane poniżej).
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________203
Konstrukcja array()
Innym sposobem tworzenia tablicy jest użycie konstrukcji array ( ) , która tworzy nową
tablicę, zawierającą podane jako argumenty wartości i ich klucze. Prostsza wersja kon-
strukcji array () zawiera tylko listę wartości zapisywanych do tablicy, bez określania
ich kluczy. Wynikiem jest tablica z wartościami skojarzonymi z kluczami całkowitymi,
rozpoczynającymi się od 0. Na przykład wyrażenie:
$owoce = array('jabłko', 'pomarańcza 1 , 'banan', 'gruszka'};
Wykonanie tego wiersza da ten sam efekt. Każdy z ciągów zostanie zapisany w tablicy
i skojarzony z indeksami O, 1,2, 3. Możesz użyć tej samej składni do zapamiętania tych
elementów z innymi kluczami:
$owoce = array('czerwony' => 'jabłko', 'pomarańczowy' => 'pomarańcza',
'żółty' => 'banan', 'zielony' => 'gruszka');
Cztery elementy, w tym samym porządku, indeksowane są kolorem, a nie liczbą. Aby
odczytać na przykład nazwę żółtego owocu, można użyć następującego wyrażenia:
Sowoce['żółty']; // jest równe 'banan1
Może to być przydatne w przypadku funkcji, które spodziewają się tablicy jako argumentu.
Wiele funkcji związanych z bazami danych zwraca wynik w postaci na bieżąco tworzo-
nej tablicy. Inne funkcje, tworzące tablice, są wygodnymi narzędziami dla innych funk-
cji operujących na tablicach. Jedną z nich jest funkcja rangę ( ) , która pobiera dwie
liczby i zwraca tablicę zawierającą te liczby oraz wartości spomiędzy nich:
$tablica = r a n g ę ( 1 , 5 ) ;
Odczytywanie wartości
Po zapisaniu wartości do tablicy musimy poznać sposób jej odczytania.
Rozdział 11. » Tablice i funkcje operujące na tablicach_____________________205
Konstrukcja list()
Istnieje kilka sposobów odczytania wartości z tablicy bez korzystania z kluczy. Więk-
szość z nich wykorzystuje to, że tablice pamiętają porządek, w jakim były zapisywane
elementy. Opiszemy to dokładniej w części „Iteracje". Tu zamieścimy przykład użycia
konstrukcji list (), która może być używana do przypisywania kilku kolejnych ele-
mentów tablicy do zmiennych. Wykonajmy następujące dwa wiersze kodu:
$owoce = array('jabłko', 'pomarańcza1, 'banan');
list($owoc_czerw, $owoc_pom) = $owoce;
Tablice wielowymiarowe
Do tej pory zajmowaliśmy się tylko tablicami jednowymiarowymi, z jednym poziomem
kluczy w nawiasach kwadratowych. Jednak PHP potrafi obsługiwać tablice wielowy-
miarowe z dowolną liczbą kluczy. Pierwsze odwołanie do tablicy może być przypisa-
niem np.:
Stablica[l][2][3][4][5] = "skarb głęboko schowany";
Pamiętaj, że wartości zapisane w tablicy mogą być tablicą, liczbami lub ciągami zna-
ków. Wielowymiarowa składnia z poprzedniego przykładu może być zwięzłą formą
odwołania do (czterowymiarowej) tablicy, połączonej z kluczem o wartości l, która to
tablica zawiera tablicę (trójwymiarową) itd. Zauważ, że w takiej strukturze mogą istnieć
różne poziomy zagłębienia:
5tablica_wielowym[0] = "prosty ciąg";
$ tablica_wielowym [ l ][' zawiera '] = "głębiej zapisany ciąg";
Jest to prosta tablica zawierające dwa elementy, zapamiętane wraz z ich wartościami
klucza; każdy z nich jest tablicą. Po utworzeniu tej tablicy można się do niej odwołać
w następujący sposób:
$rodzaj = 'kwiat';
$ kolor = 'fioletowy';
print("$kolor Srodzaj to". S tablica[Srodzaj ] [$kolor));
w wyniku otrzymasz:
fioletowy owoc to
Informacje o tablicach
Potrafimy już tworzyć tablice, zapisywać w nich wartości oraz odczytywać je. W tabeli
11.1 zebraliśmy kilka funkcji pozwalających otrzymać informacje na temat tablic.
Tabela 11.1.
Proste funkcje zwracające informacje o tablicach
Funkcja Opis
is_array ( ) Pobiera jeden argument dowolnego typu i zwraca wartość TRUE, jeżeli argument jest
tablica, a w przeciwnym wypadku zwraca FALSE
count ( ) Pobiera jako argument tablicę i zwraca liczbę pełnych elementów tablicy (dla ciągów
i liczb będzie to 1)
sizeof o Identycznie jak count()
in_array ( ) Pobiera dwa argumenty: element (który może znajdować się w tablicy) oraz tablicę (która
może zawierać element). Zwraca TRUE, jeżeli element zawiera się w tablicy
Usuwanie z tablicy
Usunięcie elementu z tablicy jest analogiczne do usuwania zmiennej. Po prostu wywo-
łaj funkcję unset ( ) , np.:
208_________________________________________Część l » Podstawy PHP
$tablica[0] = 'potrzebny';
$tablica[l] = 'niepotrzebny';
$tablica[2] = 'znowu potrzebny';
unset($tablica[l]l;
Zauważ, że operacja ta nie jest równoważna przypisaniu do wartości pustej. Jeżeli za-
miast wywołania funkcji unset ( ) wykonamy następujący wiersz:
Stablicall] = ' ' ;
otrzymamy tablicę zawierającą trzy elementy (' potrzebny', ' ', ' znowu potrzebny')
połączone z kluczami O, l i 2.
Iteracje
Oprócz zapisywania wartości połączonych 7 kluczami, tablice PHP budują uporządko-
waną w kolejności tworzenia listę par klucz i wartość. Wspierają w ten sposób operację
przeglądania całej zawartości tablicy (iteracji). Trudno jest zbudować zwykłą pętlę na
bazie rosnącego indeksu, ponieważ indeksy tablicy nie muszą być liczbami.
^^> Każda tablica pamięta, która z par klucz i wartość jest „bieżąca", a funk-
\A cje iteracyjne przesuwają znacznik bieżącego elementu po wewnętrznej
liście kluczy i wartości. Mimo że nazywamy ten znacznik „wskaźnikiem
bieżącym", PHP nie zawiera wskaźników w sensie C i C++; określenie to
występuje tylko w kontekście iteracji po tablicy.
Rysunek 11.1.
Wewnętrzna .. t
struktura tablicy .----------- —————————————— dynamiczna
.. . indeks Wartość
przeszukiwanie _______ _________
—J*» x______«—— ?
indeks Wartość y
+—-^
. indeks
t Wartość Sy
^ - ~ ^
\v indeks Wartość J
\ *~^
L^T^—————^F^
funkcje działające
na indeksach
funkcje
iteracyjne
$stolica = arrayU;
Sstolica(0] = 'Caracas';
$stolica['Caracas'] = 'Wenezuela';
$stolica[l] = 'Paryż';
$stolica['Paryż'] = 'Francja';
$stolica[2] = 'Tokio';
Sstolica['Tokio'] = 'Japonia1;
Teraz odczytamy dane zawarte w tej tablicy, używając systemu kluczy. Jeżeli przyj-
miemy konwencję opisaną w powyższym przykładzie (miasta zapisane z indeksami
numerycznymi, kraje indeksowane nazwą miasta), możemy napisać funkcję wypisującą
miasto i związany z nim kraj.
function city_by_number ($number__index, $city_array)
(
if (IsSet($city_array[$number_index]))
Sthe_city = $city_array[$number_index];
$the_country = Scity_array[?the_city];
print("$the_city to miasto w kraju $the_country<BR>");
}
}
city_by_number(O, $stolica);
city_by_number(l, Sstolica};
city_by_number(2, $stolica) ;
210___________________________________________Część l » Podstawy PHP
Jeżeli zawartość tablicy jest identyczna jak w poprzednim fragmencie kodu, w przeglą-
darce powinieneś zobaczyć:
Caracas to miasto w kraju Wenezuela
Paryż to miasto w kraju Francja
Tokio to miasto w kraju Japonia
Taka metoda odczytywania działa doskonale, jeżeli znamy strukturę zapisanych w ta-
beli danych i wszystkie klucze. Co zrobić, aby wypisać całą zawartość tablicy? Nie ma
dobrej metody sprawdzenia wszystkich możliwych kluczy. Jeżeliby istniała metoda,
byłaby nieefektywna.
print_all_array(Sstolica) ;
print_all_array(Sstolica); // powtórzone, aby sprawdzić jak działa
Funkcja current ( ) zwraca wartość znacznika bieżącego elementu (na rysunku 11.1
pokazano schemat wewnętrznej budowy tablic). Jeśli tablica jest tworzona za pomocą
przypisania jej elementu, element będzie wskazywany jako bieżący. Funkcja n e x t ( )
przesuwa ten wskaźnik i zwraca wartość bieżącego elementu. Jeżeli funkcja n e x t ( )
zostanie wywołana, gdy bieżący wskaźnik znajduje się na ostatnim elemencie tablicy,
zwróci wartość FALSE.
"<3
f^ilfcfe W ostatnim przykładzie znajduje się pułapka (nie widać jej w tym przy-
LjggSjl' padku) obniżająca wiarygodność tej metody poszukiwania elementów
•^"• w tablicy. Jeżeli w tablicy mamy zapisane wartości FALSE, nasza pę-
tla while nie będzie ich potrafiła odróżnić od wartości FALSE, którą
next o zwraca przy wyczerpaniu się elementów tablicy. Rozwiązaniem
tego problemu jest użycie funkcji each ( ) , która zostanie opisana w dal-
szej części tego rozdziału.
Japonia
Caracas
Wenezuela
Paryż
Francja
Tokio
Japonia
W jaki sposób otrzymaliśmy te same wyniki przy drugim wywołaniu funkcji print_
all_array ( ) ? W jaki sposób wskaźnik bieżącego elementu wrócił na początek, aby za
drugim razem funkcja znów przejrzała wszystkie elementy? Odpowiedź jest związana
z faktem, że przekazywanie argumentów do funkcji jest realizowane poprzez wartość,
a nie argument. Oba wywołania funkcji pobieraj ą nową kopię tablicy, na której funkcja
next ( ) nie była wykonywana.
^f—^ ———————————————————————————————————————————————————————————————————————————————————————————————————————
LDirtfP
v
Więcej na temat kopiowania argumentów przez funkcję w rozdziale 8.
Ue _____________________________
Możemy sprawdzić to wyjaśnienie, przekazując do funkcji tablicę poprzez referencję,
a nie przez wartość. Jeżeli zdefiniujemy tę samą funkcję, ale wywołamy ją za pomocą
znaków & przy argumentach:
print_all_array(&$stolica);
print_all_array(&$stolica);
Tym razem wskaźnik bieżącego elementu globalnej wersji tablicy został przesunięty
przez pierwsze wywołanie funkcji.
ten wskaźnik na początek. Ustawia wskaźnik na pierwszy element i zwraca jego war-
tość. Możemy użyć tej funkcji, aby ulepszyć działanie naszej funkcji drukującej, zamie-
niając wywołanie funkcji current () na reset ().
function print_all_array_reset($city_array)
(
// Uwaga! Nadal nie najlepiej. Sprawdź funkcje each()
$current_item = reset($city_array); // przesuwa i zwraca wartość
if ($current_item)
print("$current_item<BR>");
else
print("Nie ma nic do wypisania<BR>");
while ($current_item = next(Scity_array))
print("$current_item<BR>");
)
Użycie funkcji reset () może wydać się niejasne. Można zamienić pierwszy wiersz
ciała funkcji na dwa następujące:
reset($city_array); // przesunięcie na pierwszy element
$current_item = current($city_array); // wartość pierwszego el.
W wyniku otrzymujemy:
Klucz 0; Wartość: Caracas
Klucz Caracas; Wartość: Wenezuela
Klucz 1; Wartość: Paryż
Klucz Paryż; Wartość: Francja
Klucz 2; Wartość: Tokio
Klucz Tokio; Wartość: Japonia
Na ratunek przychodzi nam funkcja each ( ) , która jest nieco podobna do next ( ) , ale
zwraca wartość FALSE jedynie wtedy, gdy zabraknie elementów tablicy. Funkcja each ()
dodatkowo zwraca tablicę zawierającą zarówno wartość klucza, jak i wartość elementu.
Utrudnia to opisywanie tej funkcji, ponieważ trzeba rozróżnić dwie tablice: tablicę, któ-
rą przeglądamy, i tę, którą each ( ) zwraca za każdym wywołaniem. Zwracana tablica
ma cztery pary wartości klucz i wartość:
* klucz: O, wartość: bieżący klucz;
•* klucz: l, wartość: bieżąca wartość;
214___________________________________________Część l » Podstawy PHP
Bieżący klucz i bieżąca wartość to klucz i wartość bieżącego elementu przeglądanej ta-
blicy. Inaczej mówiąc, zwracana wartość jest paczką zawierającą parę klucz i wartość
z przeglądanej tablicy (oferuje indeksy numeryczne oraz będące ciągami).
Użyjmy funkcji each ( ) , aby napisać lepszą wersję funkcji wypisującej wartości kluczy
i wartości zapisane w tablicy:
function print_keys_and_values_each($city_array)
(
// Skutecznie wypisuje całą zawartość tablicy
reset($city_array);
while ($array_cell = each($city_array))
(
$current_value = $array_cell['value'];
$current_key = $array_cell['key'];
print("Klucz: $current_key; Wartość: $current_value<BR>"};
)
}
print_keys_and_values_each(Sstolica) ;
Funkcja ta nie korzysta z drugiego argumentu. Użyjmy teraz tej funkcji do naszej stan-
dardowej tablicy z wykorzystaniem funkcji a r r a y _ w a l k ( ) :
array_walk(Sstolica, 'print_value_length');
W efekcie zobaczymy:
Ciąg Caracas ma długość: 7
Ciąg Wenezuela ma długość: 9
Ciąg Paryż ma długość: 5
Ciąg Francja ma długość: 7
Ciąg Tokio ma długość: 5
Ciąg Japonia ma długość: 7
Stosy i kolejki
Stosy i kolejki są abstrakcyjnymi strukturami danych, często używanymi w teorii in-
formatyki. Zapewniają odpowiednią dyscyplinę w dostępie do przechowywanych ele-
mentów. Jak wcześniej wspomnieliśmy, tablice PHP pozwalają na imitację innych
struktur danych. Niezależność tablic od typu zapisywanych danych pozwala łatwo im-
plementować stosy i kolejki. PHP zawiera kilka funkcji przeznaczonych do tego celu.
Jeżeli będziesz używał tylko tych funkcji, możesz zapomnieć, że bazują na tablicach.
Stos jest kontenerem zapamiętującym wartości, zapewniającym tryb dostępu do danych
LIFO (ang. last in-first out — ostatni wchodzi, pierwszy wychodzi). Oznacza to, że stos
zapamiętuje kolejność zapisu elementów, a jedynym sposobem otrzymania wartości
elementu jest pobranie (i usunięcie) ostatnio zapisanej wartości. Zwykle porównuje się
tę strukturę do stosu tac w kawiarni w jednym z zasobników, utrzymujących stały po-
ziom górnej tacy. Można dodać nowe tace do stosu starych i można zabrać tace z góry,
ale nie da się wyjąć starej tacy bez wyjęcia nowych. Czynność dodawania do stosu na-
zywana jest „położeniem" wartości na stos, a czynność uzyskania wartości z góry jest
nazywana „zdjęciem" ze stosu. Inną analogią jest sposób, w jaki niektóre przeglądarki
zapamiętują odwiedzone strony, aby można było wrócić do nich za pomocą przycisku
Wstecz. Przejście do nowej strony powoduje położenie nowego adresu na stos, a użycie
przycisku Wstecz zdejmuje adres ze stosu.
216_________________________________________Część l » Podstawy PHP
Tabela 11.2.
Funkcje iteracyjne
Kolejka jest podobna do stosu, ale dostęp do danych jest organizowany na zasadzie FI-
FO (ang. First in-First out — pierwszy wchodzi, pierwszy wychodzi). Zwykle porów-
nuje się tę strukturę z kolejką osób. Ten, kto czeka dłużej w kolejce, będzie obsłużony
w następnej kolejności.
Rozdział 11. » Tablice i funkcje operujące na tablicach_____________________217
W efekcie otrzymamy:
Zdejmujemy ze stosu i mamy: ostatni
Zdejmujemy ze stosu i mamy: środkowy
Zdejmujemy ze stosu i mamy: pierwszy
PHP 4 posiada również funkcje działające w ten sam sposób, co array_push ( ) i ar-
ray_pop ( ) , ale na drugim końcu tablicy (dodają i usuwają elementy z początku tabli-
cy). Funkcja array_unshif t ( ) jest analogiczna do array_push ( ) , a r r a y _ s h i f t ( )
działa podobnie do array_pop ( ) . Jeżeli weźmiemy jedną z funkcji z kolumny A i jed-
ną z kolumny B, otrzymamy obsługę kolejki. Zmodyfikujmy nasz poprzedni przykład,
aby położyć elementy na początek tablicy (za pomocą array_unshift ( ) ) i zdjąć je
z końca (za pomocą array_pop ( ) ) :
$kolejka = arrayO; // wymagany - array_unshif t () nie stworzy tablicy
array_unshift($kolejka, "pierwszy", "środkowy");
array_unshift(Skolejka, "ostatni");
while ($pobrany = array_pop($kolejka))
print{"Zdejmujemy ze stosu i mamy: $pobrany<BR>");
Tabela 11.3.
Funkcje stosu i kolejki
Przekształcenia tablic
PHP 4 posiada wiele funkcji manipulujących danymi zawartymi w tablicy. Wszystkie funk-
cje przedstawione w tej części wykonuj ą określone czynności na tablicy i zwracają wynik
w postaci innej tablicy (funkcje sortujące tablice zostały wydzielone w osobnej części).
W tym rozdziale ulepszaliśmy funkcję drukującą całą zawartość tablicy; w tej części
użyjemy ostatniej wersji (print_keys_and_values_each ( ) ) do pokazania zawarto-
ści tablicy zwracanej przez opisywane funkcje.
Teraz wypiszemy zawartość tablic będących wynikiem działania tych dwóch funkcji:
print("Klucze tablicy:<BR>");
print_keys_and_values_each( array_keys($pizza_requests));
print("Wartości tablicy:<BR>");
print_keys_and_values_each( array_values($pizza_requests));
W wyniku otrzymamy:
Klucze tablicy:
Klucz 0; Wartość Alice
Klucz 1; Wartość Bob
Klucz 2; Wartość Carl
Klucz 3; Wartość Dennis
Wartości tablicy:
Klucz 0; Wartość pepperoni
Klucz 1; Wartość grzyby
Klucz 2; Wartość kiełbasa
Klucz 3; Wartość grzyby
W wyniku otrzymujemy:
Klucz: pepperoni; Wartość: l
Klucz: grzyby; Wartość: 2
Klucz: kiełbasa; Wartość: l
co daje:
Klucz: pepperoni; Wartość: Alice
Klucz: grzyby; Wartość: Dennis // co stało się z Bobem?
Klucz: kiełbasa; Wartość: Carl
Należy zapamiętać, że w tablica gwarantuje unikalność kluczy, a wartości mogą się po-
wtarzać. Z tego powodu wszystkie powtarzające się wartości z oryginalnej tablicy stają
się kluczem w nowej. Tylko jeden z oryginalnych kluczy pozostaje nową wartością.
W wyniku otrzymamy:
Klucz: Dennis; Wartość: grzyby
Klucz: Carl; Wartość: kiełbasa
Klucz: Bob; Wartość: grzyby
Klucz: Alice; Wartość: pepperoni
Mimo że został zmieniony wewnętrzny porządek tablicy, wszystkie pary klucz i wartość
pozostały takie same. Jednak funkcja ta (jak kilka innych PHP) traktuje indeksy nume-
ryczne w specjalny sposób. Zakłada, że porządek kluczy powinien zostać również odwró-
cony, aby można było użyć kodu pobierającego porządek elementów z indeksów, zamiast
porządku wewnętrznej listy. Funkcja array__reverse ( ) zamienia kolejność kluczy nu-
merycznych, aby nowy porządek kluczy odpowiadał porządkowi listy wewnętrznej.
Funkcja shuffle ( ) pobiera tablicę jako argument i zmienia w losowy sposób porządek
elementów tablicy. Używa funkcji randO (funkcja generująca kolejne liczby losowe),
więc przed jej wywołaniem trzeba zainicjować generator liczb losowych za pomocą
srand ( ) . Odpowiednia kolejność wywoływanych funkcji przedstawiona jest poniżej:
s r a n d ( (double) m i c r o t i m e O * 1 0 0 0 0 0 0 ) ;
/ / zainicjowanie generatora liczb losowych
shuffle(Spizza_requests);
print_keys_and_values_each($pizza_requests);
VoatfP Więcej na temat generowania liczb losowych w rozdziale 10. Jeżeli za-
tskZfi rnierzasz użyć funkcji shuffle o, nie czytając rozdziału 10., na stro-
nie używającej shuffle o wywołaj funkcję srand o (tylko raz), jak
w przykładzie.
"<j
r^ltel& ^ przeciwieństwie do wielu funkcji operujących na tablicach, funkcja
[jlJs|f shuffle o jest destrukcyjna. Operuje bezpośrednio na argumentach
~ tablicy, zmieniając je, nie zwraca nowej tablicy. Oznacza to, że
$nowa_tablice = shuffle($stara_tablica); // ŹLE
Tabela 11.4.
Funkcje przekształcające tablice
Funkcja Opis
a r r a y keys O Posiada tablicę jako argument; zwraca nową tablicę zawierającą klucze
z wejściowej tablicy; nowymi kluczami są liczby rozpoczynające się od zera
array values ( ) Posiada tablicę jako argument; zwraca nową tablicę zawierającą wartości
z wejściowej tablicy; nowymi kluczami są liczby rozpoczynające się od zera
a r r a y count values ( ) Posiada tablicęjako argument; zwraca nową tablicę, w której kluczami są
wartości ze starej tablicy; wartościąjest liczba wystąpień oryginalnej tablicy
array f l i p O Posiada tablicęjako argument; zamienia klucze na wartości i odwrotnie
a r r a y reverse ( ) Posiada tablicęjako argument; zmienia wewnętrzny porządek elementów
tablicy na odwrotny. Klucze numeryczne są przy tym przenumerowywane
shuffleO Posiada tablicęjako argument; zmienia wewnętrzny porządek elementów
tablicy na przypadkowy. Zmienia wartości kluczy numerycznych tak, aby
pasowały do nowego uporządkowania. Funkcja korzysta z generatora liczb
losowych rand ( ) ; przed wywołaniem shuffle ( ) musi zostać wywołana
funkcja srand ()
a r r a y merge ( ) Dwie tablicęjako argumenty; zwraca tablicę będącą połączeniem obu tablic,
elementy pierwszej znajdują się na początku, następnie elementy drugiej.
Najbardziej użyteczne dla tablic używanych w charakterze list, ponieważ
wartości powtarzających się kluczy zostaną nadpisane. Również klucze
numeryczne są przenumerowywane dla odzwierciedlenia nowego porządku
a r r a y pad ( ) Posiada trzy argumenty: wejściową tablicę, rozmiar wypełnienia i wartość
wypełnienia. Zwraca nową tablicę wypełnioną według następujących zasad:
jeżeli rozmiar wypełnienia jest większy od długości tablicy wejściowej,
tablica jest wydłużana, za pomocą wartości wypełnienia, do rozmiaru
wypełnienia dzięki kolejnym przypisaniom typu $tablica [ ] =$wart
wypełnienia. Ujemna wartość rozmiaru wypełnienia powoduje działanie
podobne, ale wypełnianie jest realizowane od początku tablicy, a nie od
końca. Jeżeli tablica ma długość większą od podanego rozmiaru wypełnienia
(w liczbach bezwzględnych), funkcja ta nie daje żadnego efektu
array_slice ( ) Posiada trzy argumenty: tablicę wejściową, przesunięcie oraz opcjonalnie
długość. Zwraca nową tablicę, będącą wycinkiem wejściowej. Początek
i koniec wycinka określany jest przez przesunięcie i długość. Dodatnia
wartość przesunięcia oznacza, że początek jest liczony od początku tablicy;
wartość ujemna powoduje obliczenie punktu początkowego od końca tablicy.
Opcjonalny argument długość określa, ile elementów będzie zawierać nowa
tablica (jeżeli jest to wartość dodatnia) lub na którym elemencie od końca się
zakończy (w przypadku wartości ujemnej). Jeżeli argument ten nie zostanie
podany, wycinek kończy się na końcu tablicy wejściowej
array spliceO Usuwa fragment tablicy i wstawia w to miejsce fragment pochodzący z innej.
Posiada cztery argumenty: tablicę wejściową, przesunięcie, opcjonalnie
długość i opcjonalnie tablicę zawierającą wartości do wstawienia. Zwraca
nową tablicę, zawierającą fragment wycięty z wejściowej. Zasady wyliczania
początku i końca wycinka są identyczne jak w funkcji a r r a y slice ( ) .
Jeżeli nie zostanie podana tablica do wstawienia, funkcja usuwa wycinek
tablicy wejściowej i zwraca go jako wartość. Jeżeli przekazana zostanie
tablica do wstawienia, jej elementy zostaną wstawione w miejsce usuniętych
222_________________________________________Część l » Podstawy PHP
Tabela 11.5.
Funkcje zamiany pomiędzy zmiennymi i tablicą
Funkcja Opis
compact ( ) Pobiera podany zbiór ciągów i analizuje go w poszukiwaniu nazw zmiennych; zwraca
tablicę, w której kluczami są nazwy zmiennych, a wartościami — wartości tych zmiennych
Funkcja przyjmuje dowolną liczbę argumentów, będących ciągami lub tablicami
zawierającymi ciągi na dowolnym poziomie zagłębienia. Zbiór ciągów przekazanych do
funkcji jest analizowany w poszukiwaniu zmiennych. Ciągi niezawierające nazw
zainicjowanych zmiennych są odrzucane
extract ( ) Jako argument posiada tablicę oraz dwa argumenty opcjonalne. Importuje pary klucz
i wartość do środowiska wykonania skryptu. Klucze tablicy stają się nazwami zmiennych,
a wartości z tablicy — wartościami zmiennych. Klucze, które nie są prawidłowymi nazwami
zmiennych, są ignorowane
Argumentami opcjonalnymi są: liczba całkowita (zawierająca jedną ze stałych) oraz ciąg
zawierający przedrostek. Zadaniem tych argumentów jest obsłużenie kolizji istniejących
zmiennych z zmiennymi tworzonymi na podstawie kluczy tablicy. Dozwolonymi stałymi dla
drugiego argumentu funkcji są: 1. EXTRJDVERWRITE; 2. EXTR_SKIP; 3.
EXTR_PREFIX_SAME oraz 4. EXTR__PREFIX_ALL. Dla tych czterech wartości funkcja działa
w następujący sposób: 1. Nadpisz istniejące zmienne; 2. Opuść przypisanie mogące zamazać
istniejącą zmienną; 3. Użyj przedrostka, jeżeli przypisanie zniszczy istniejącą zmienną;
4. Użyj przedrostka dla wszystkich zmiennych tworzonych przez funkcję. Dla przykładu:
e x t r a c t ( a r r a y ( ' z m i e n n a ' => 4 ) , E X T R _ P R E F I X _ S A M E , ' d i f f _ ' ) ; spowoduje
przypisanie do zmiennej Szmienna wartości 4, jeżeli $ zmienna nie była do tej pory użyta.
W przeciwnym przypadku wartość 4 zostanie przypisana do $dif f__zmienna
Sortowanie
Na koniec przedstawimy zestaw funkcji sortujących tablice. Jak wcześniej zauważyli-
śmy, często występuje rozdźwięk pomiędzy utrzymywaniem asocjacji pary klucz
i wartość a traktowaniem kluczy numerycznych jako elementu porządkującego tablicę
(co skutkowało zmianą wartości kluczy w przypadku zmiany kolejności elementów
w tablicy). Na szczęście PHP ma odpowiednie warianty funkcji sortujących, które po-
zwalają na wybór pomiędzy sortowaniem w porządku rosnącym i malejącym oraz za-
pewniają możliwość przekazania własnej funkcji porządkującej.
Rozdział 11. » Tablice i funkcje operujące na tablicach_______________________223
Nazwy tych funkcji są bardzo zwięzłe, każda litera (poza frazą ' s o r t ' ) ma własne
znaczenie. Sposób ich rozszyfrowywania jest następujący:
* początkowe ' a ' oznacza, że funkcja sortując wartości utrzymuje asocjacje po-
między kluczami i wartościami;
* początkowe ' k ' oznacza, że funkcja sortuje klucze utrzymując asocjacje;
* brak początkowego ' a ' oraz ' k ' oznacza, że funkcja sortując wartości nie
utrzymuje asocjacji pomiędzy kluczem i wartością. Numeryczne klucze zostają
przenumerowane dla odzwierciedlenia nowego porządku elementów;
** litera ' r ' oznacza odwrotny porządek sortowania;
+ początkowe ' u ' oznacza, że funkcja spodziewa się w drugim argumencie na-
zwy funkcji, określającej porządek dowolnych dwóch sortowanych elementów
(więcej informacji w tabeli 11.6).
Tabela 11.6.
Funkcje sortujące tablice
Funkcja Opis
asort ( ) Posiada jeden argument. Sortuje elementy według ich wartości, zachowując powiązanie
pomiędzy kluczem i wartością. Dobra dla tablic asocjacyjnych
arsort ( ) Identycznie jak asort ( ) , ale sortuje w odwrotnym porządku
ksort ( ) Posiada jeden argument. Sortuje elementy według ich kluczy, zachowując powiązanie
pomiędzy kluczem i wartością
krsort ( ) Identycznie jak krsort ( ) , ale sortuje w odwrotnym porządku
sort () Posiada jeden argument. Sortuje elementy według ich wartości. Klucze mogą zostać
przenumerowane dla odzwierciedlenia nowego porządku wartości
rsort ( ) Identycznie jak rsort ( ) , ale sortuje w odwrotnym porządku
uasort ( ) Sortuje elementy używając funkcji porównującej. Jest podobna do asort ( ) , ale porządek
elementów jest ustalany przez funkcję, której nazwa jest przekazana w drugim argumencie.
Funkcja powinna zwracać wartość ujemną, jeżeli pierwszy element jest „przed" drugim,
wartość dodatnią, jeżeli pierwszy element znajduje się „za" drugim, oraz zero, jeżeli elementy
są takie same
Uksort ( ) Identycznie jak uksort ( ) , ale sortuje w odwrotnym porządku
usort ( ) Sortuje tablicę przy użyciu przekazanej funkcji porównującej. Podobna do uasort ( ) poza
tym, że nie utrzymuje powiązania klucza z wartością (jak sort ())
Podsumowanie
Tablica jest podstawowym typem PHP. Pozwala na emulację typu rekordowego oraz
wektorów znanych z innych języków programowania. Tablice w PHP są asocjacyjne, co
224_________________________________________Część l » Podstawy PHP
Z powodu ulotności typów do tablicy PHP można zapisać dowolną wartość. Oznacza to
również, że tablice mogą występować jako elementy tablicy. „Tablice wielowymiaro-
we" to po prostu tablice zawierające tablice jako elementy. Do kolejnych poziomów
można się dostać za pomocą indeksów w kolejnych nawiasach (wyrażenie $ tabli-
ca [ 3 ] [ 4 ] odwołuje się do elementu tablicy — indeksowanego przez 4, który jest ele-
mentem tablicy $tablica — indeksowanym przez 3).
Nawet jeżeli projekt twojej witryny zakłada nawigację tylko w jedną stronę (ze strony 1.
tylko do strony 2., a następnie do strony 3. itd.), protokół HTTP nigdy nie będzie wiedział,
czy użytkownik oglądający stronę 2. załadował wcześniej stronę 1. Nie możesz ustawić na
stronie 1. zmiennej i oczekiwać, że zostanie ona zaimportowana do strony 2. tylko przez
mechanizmy HTML. Możesz użyć HTML do wyświetlenia formularza, do którego można
wprowadzić informacje, ale jeżeli nie zrealizujesz mechanizmów przekazujących dane do
innej strony lub programu, zmienne znikną w momencie przejścia na inną stronę.
226_________________________________________Część l » Podstawy PHP
Argumenty GET
Metoda GET pozwala na przesyłanie argumentów pomiędzy stronami za pomocą frag-
mentu adresu URL. W przypadku użycia tej metody do obsługi formularzu, GET powo-
duje dodanie nazw zmiennych i ich wartości do adresu URL podanego jako atrybut
ACTION. Poszczególne zmienne rozdzielone są znakiem zapytania. Kompletny adres
URL przesyłany jest do przetworzenia (w tym przypadku przez PHP).
Gdy użytkownik wybierze jedną z wartości i kliknie przycisk, przeglądarka sklei bez
dodatkowych odstępów następujące elementy:
•* adres URL podany w apostrofach po słowie ACTION (http://localhost/php/ba-
seball.php);
* znak zapytania (?);
Rozdział 12. » Przekazywanie danych pomiędzy stronami______________________227
Adres ten jest przesyłany przez przeglądarkę jako nowe żądanie. Skrypt PHP, do które-
go zostało przesłane powyższe żądanie, pobiera z adresu wartości GET i używa ich do
stworzenia strony. W naszym przypadku wkleimy wybraną wartość do tekstu.
<HTML>
<HEAD>
<TITLE>Przykład GET część 2</TITLE>
<STYLE TYPE="text/css">
<! --
BODY (font-size: 24pt;)
-->
</STYLE>
</HEAD>
<BODY)
<P>Naprzód,
<?php print("$Team"); ?> !
</BODY>
</HTML>
Rysunek 12.1.
Wynik działania
formularza używającego
METHOD=GET
Obsługa formularzy za pomocą metody GET posiada jedną wielką zaletę w porównaniu
z metodą POST. Tworzy ona różne adresy, które użytkownik może zapamiętać. Z wyni-
ku działania formularza bazującego na metodzie POST nie da się zrobić zakładki.
Sposób obsługi formularzy za pomocą metody GET posiada tyle wad, że standard
HTML 4.0 odradza jego stosowanie. Wadami tymi są między innymi:
* GET nie jest odpowiedni do logowania użytkownika, ponieważ nazwa użyt-
kownika i hasło są widoczne na ekranie i mogą być zapisane w pamięci odwie-
dzonych stron w przeglądarce.
228_________________________________________Część l « Podstawy PHP
Ponieważ można zapamiętywać adresy utworzone przez metodę GET, pojawiło się wiele
głosów za zachowaniem tej metody przez W3. Tak się stało i mimo że jest to metoda
najczęściej używana do obsługi formularzy, jest zalecana tylko dla powtarzalnych za-
stosowań, czyli takich, które nie powoduj ą stałego efektu ubocznego. Najbardziej odpo-
wiednim zastosowaniem GET jest okno przeszukiwania. Dla innych formularzy lepiej
użyć metody POST.
Lecz gdy witryna zacznie się rozrastać, płaska struktura plików może wymagać dużo
pracy przy administracji, szczególnie gdy proste poprawki trzeba będzie wykonać na
każdej stronie. Jeżeli struktura stron jest podobna, możesz zastanowić się nad zastoso-
waniem systemu szablonów razem z PHP.
Można zastosować pojedynczy szablon oraz oddzielne pliki tekstowe dla każdego zwie-
rzęcia (zawierające dane, fotografie itp.):
fleecee.php
alpaca.inc
guanaco.inc
llama.inc
vicuna.inc
Rozdział 12. » Przekazywanie danych pomiędzy stronami______________________229
W obu przypadkach plik szablonu będzie wyglądał podobnie (nie zamieściliśmy wszyst-
kich niezbędnych plików, więc przykład ten nie będzie działał):
<HTML>
<HEAD>
<TITLE>Zwierzęta hodowane dla wełny</TITLE>
<STYLE TYPE="text/css">
< ! --
BODY (font: verdana; font-size: 14pt)
</STYLE>
</HEAD>
<BODY>
<TABLE BORDER=0 CELLPADDING=0 WIDTH=100%>
<TR>
<!- Panel nawigacji z adresami w stylu GET. —>
<TD BGCOLOR="#428284" AL1GN=CENTER VALIGN=TOP WIDTH=25%>
<P>
<A HREF="f leecee .php?Name=alpaca"XB>Alpaca</BX/A>
<BR>
<A HREF=" f leecee. php?Name=guanaco"XB>Guanaco</BX/A>
<BR>
<A HREF="fleecee,php?Name=llama"><B>Llama</B></A>
<BR>
<A HREF="f leecee.php?Name=vicuna"XB>Vicu&tł241a</BX/A>
<BRXBR>
</TD>
<TD BGCOLOR«"ltFFFFFF" ALIGN=LEFT VALIGN=TOP W I D T H = 7 5 % >
<? i n c l u d e ( " $ N a m e . i n c " ) ; ?>
</TDX/TRX/TABLE>
</BODY>
</HTML>
Łącza na panelu nawigacji są obsługiwane przez przeglądarkę tak samo jak wynik
działania metody GET.
Jednak nawet w przypadku takiego rozwiązania masz jeszcze wiele ręcznej pracy: nale-
ży się upewnić, że każdy włączany plik jest prawidłowo sformatowany, dodanie nowe-
go pliku do witryny wymaga dodania nowego łącza i innych tego typu prac. Zgodnie
z zasadą oddzielania formy od treści możesz przejść na wyższy poziom abstrakcji przy
użyciu bazy danych. W tym przypadku adres typu:
fleecee.php?animalID=2
powinien spowodować sięgnięcie przez szablon do bazy danych (użycie zmiennej nu-
merycznej zamiast słowa przyspiesza interakcję z bazą danych). Taki system powinien
automatycznie umieszczać na panelu na podstawie zawartości bazy danych. Dzięki te-
mu możesz tworzyć kolejne strony WWW bez interwencji.
230_________________________________________Część l » Podstawy PHP
Argumenty POST
POST jest zalecaną metodą obsługi formularzy, szczególnie dla niepowtarzalnych zasto-
sowań (takich, które skutkują trwałymi efektami działania), takich jak dodawanie da-
nych do bazy. Zbiór danych formularza jest zawarty w ciele formularza w czasie jego
wysyłania do agenta przetwarzania (w tym przypadku PHP). Nie ma żadnych widocz-
nych zmian w adresie URL w zależności od przesyłanych danych.
Wiele, jeżeli nie większość zastosowań POST powoduje zainicjowanie połączenia w usłu-
gowej warstwie systemu. Przykład pokazany na wydruku 12.1 jednak tego nie wyko-
nuje (ponieważ skupiamy się na metodzie POST, usunęliśmy sprawdzanie poprawności
danych, więc formularz ten wymaga jeszcze pracy).
STotal = SContrib;
while(SYearCount <= SYears)
(
STotal = round(STotal * (1.0 + $AnnGain/100) + SContrib);
$YearCount = $YearCount + l;
(
)
?>
<BODY>
<DIV ALIGN=CENTER ID=Divl class=heading>
Kalkulator emerytalny
</DIV>
<P class=blurb>Wypęłnij wszystkie pola (Poza "Koszyk") i sprawdź,
ile pieniędzy zaoszczędzisz na emeryturę dla różnych przypadków!
Możesz zmieniać wartości i ponownie przeliczać dane tak często,
jak chcesz. Musisz wypełnić dwa pola wieku. Pole "Procent roczny"
posiada wartość domyślna, korygowaną o inflację
(7% = wzrost 8% minus 1% inflacja),
którą możesz zmieniać w zależności od przypadku optymistycznego i
pesymistycznego.</P>
<FORM ACTION="<?php print("$PHP_SELF"); ?>" METHOD="POST">
<P>Twój wiek: <INPUT TYPE="text" SIZE=5 NAME="CurrentAge"
VALUE="<?php print("$CurrentAge"); ?>">
<P>Planowany wiek przejścia na emeryturę: <INPUT TYPE="text"
SIZE=6 NAME="RetireAge" VALUE="<?php print("$RetireAge"); ?>">
<P>Roczna składka: <INPUT TYPE="text" SIZE=15
NAME="Contrib" VALUE="<?php print("$Contrib"); ?>">
<P>Procent roczny: <INPUT TYPE="text" SIZE=5 NAME="AnnGain"
VALUE="<?php print("$AnnGain"); ?>"> %
<BRXBR>
<PXB>Koszyk</B>: <?php print ( "STotal"); ?>
<PXINPUT TYPE="hidden" NAME="stage" VALUE=1>
<PXINPUT TYPE="submit">
</FORM>
</BODY>
</HTML>
Rysunek 12.2.
Formularz używający
metody POST
232_________________________________________Część l » Podstawy PHP
Najprostszą metodą zilustrowania tej różnicy jest pokazanie różnych metod przetwarza-
nia danych z tego samego formularza. Formularz ten wygląda następująco:
<HTML>
<HEAD>
<TITLE>Formularz preferencji słodyczy</TITLE>
</HEAD>
<BODY>
<FORM ACTION="candy.php" METHOD="POST">
Jakie słodycze lubisz najbardziej?<BR>
<INPUT TYPE="radio" NAME="Candy" VALUE="orzeszki ziemne">Orzeszki ziemne<BR>
<INPUT TYPE="radio" NAME="Candy" VALUE="snickers">Snickers<BR>
<INPUT TYPE="radio" NAME="Candy" VALUE="krówki">Krówki<BR>
<INPUT TYPE="submit">
</FORM>
</BODY>
</HTML>
Skrypt ASP realizujący te same funkcje (aby go użyć zmień argument ACTION formula-
rza na candy .asp):
<HTML>
<HEAD>
<TITLE>Odpowiedź na wybór preferencji</TITLE>
</HEAD>
<BODY>
Hmmm
<% Candy = Request.Form("Candy") %>
Rozdział 12. » Przekazywanie danych pomiędzy stronami______________________233
Rysunek 12.3.
Wynik użycia
PHP lub ASP
W skrypcie ASP należy odczytać każdą zmienną przysłaną za pomocą metody POST,
w PHP nie jest to konieczne. W przypadku ASP należy użyć Request. Form w przy-
padku metody POST oraz Request .QueryString w przypadku metody GET. Jeżeli
zmienisz metodę przesyłania danych, musisz wszędzie zmienić nazwę kolekcji, co mo-
że być źródłem błędów.
Podsumowanie
Protokół HTTP jest protokołem bezstanowym. Oznacza to, że sam HTML nie potrafi
wymieniać danych pomiędzy stronami witryny WWW. Może zostać użyty do przeka-
zywania wartości, ale zewnętrzny program obsługi formularza musi wysilić się, aby
przekazać wartość. PHP jest prawdopodobnie najbardziej naturalnym programem ob-
sługi formularza.
Dane można przekazywać za pomocą trzech podstawowych metod: GET, POST oraz co-
okie (którymi zajmiemy się w rozdziale 26.). Metoda GET jest używana do tworzenia
złożonych adresów URL, stosowanych wraz z szablonami w dynamicznych witrynach.
Nie jest to jednak metoda zalecana do obsługi formularzy, w przeciwieństwie do POST.
Rozdział 13.
Funkcje
systemu operacyjnego
i dostępu do plików
W tym rozdziale:
* Funkcje dostępu do plików
* Funkcje operujące na katalogach
* Funkcje sieciowe
* Funkcje daty i czasu
* Funkcje konwersji kalendarza
W rozdziale tym opiszemy wiele funkcji systemowych dostępnych w PHP. Wiele z nich
pozwala na dostęp do funkcji systemowych protokołu HTTP. Najbardziej użyteczne są
funkcje do odczytu i zapisu plików oraz funkcje zwracające datę i czas.
Wynikiem wykonania tego kodu jest zdublowanie zawartości pliku. Mówiąc inaczej,
końcowym wynikiem działania będzie plik z zapisaną dwa razy oryginalną zawartością.
Funkcja ta nie nadpisuje zawartości pliku.
Otwarcie pliku
Wynik działania funkcji f open ( ) należy bezwzględnie przypisać do zmiennej (trady-
cyjnie $fd lub $fp, pochodzących od deskryptora lub wskaźnika pliku. Lepiej jednak
nadawać bardziej opisowe nazwy zmiennych). Jeżeli funkcja zadziała, PHP przypisze do
zmiennej wartość całkowitą, która jest potrzebna do dalszych operacji na tym pliku, takich
jak f read lub fwrite. Jeżeli funkcja się nie powiedzie, zwracana jest wartość FALSE.
Pliki można otwierać w sześciu trybach (podobnie do poziomów dostępu). Jeżeli spró-
bujesz wykonać operację niedozwoloną w danym trybie, nie zostanie ona wykonana:
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików________________237
Przed użyciem trybów w lub w+ należy się upewnić, że poprzednia zawartość pliku zo-
stała zapisana. W przypadku użycia innych trybów możliwość utraty danych jest dużo
mniejsza.
HTTP fopen
Funkcja fopen próbuje otworzyć połączenie HTTP 1.0 do pliku, który może zostać
przesłany przez serwer WWW (pliki typu HTML, PHP, ASP itd.). PHP „oszukuje"
serwer WWW tak, aby wydawało mu się, że żądanie dostępu do pliku zostało przesłane
przez przeglądarkę, a nie przez funkcję otwarcia pliku.
Pamiętaj jednak, że nie musisz koniecznie używać HTTP do dostępu do plików HTML.
Możesz po prostu otworzyć go z systemu plików, traktując jak zwykły plik tekstowy.
Pokazana powyżej metoda jest szczególnie przydatna do zdalnych serwerów WWW.
Efekt działania jest podobny do obejrzenia strony HTML i zapisania jej źródła.
FTP fopen
Ten rodzaj funkcji fopen próbuje zestawić połączenie FTP do zdalnego serwera, udając
klienta FTP. Jest to najbardziej sprytna z podanych czterech wersji, ponieważ musisz
podać nazwę użytkownika i hasło oprócz nazwy komputera i ścieżki do pliku.
238_________________________________________Część l » Podstawy PHP
Aby metoda ta prawidłowo działała, serwer FTP musi obsługiwać tryb pasywny. Dzięki
FTP pliki mogą być otwierane w trybach tylko do odczytu lub tylko do zapisu.
W systemie Windows możesz używać formatu ścieżki, ale musisz pamiętać o stosowa-
niu podwójnych znaków \:
$fp = fopen("c:\\php\\phpdocs\\navbar.inc", "r");
Czytanie pliku
Funkcja f read ( ) wymaga wskaźnika pliku oraz wielkości pliku jako argumentów. Je-
żeli rozmiar pliku nie jest wystarczający do odczytania całego pliku, mogą wystąpić
problemy (chyba że podajesz mniejszą wartość, aby odczytywać plik we fragmentach).
PHP może samodzielne obliczyć wielkość pliku, używając funkcji f ilesize () z na-
zwą pliku (lub zmienną) jako argumentem.
Sfstring = fread($fd, filesize($filename));
Jest to bardzo użyteczna funkcja, ponieważ pozwala zamienić plik na ciąg, nad którym
można pracować za pomocą wielu funkcji działających na ciągach. Każdy ciąg można
zamienić na tablicę, używając funkcji f i l e () lub explode (), co daje nam dostęp do
wielkiego arsenału funkcji manipulujących tablicami. PHP zawiera więcej funkcji tną-
cych i szatkujących niż cały zestaw narzędzi chirurgicznych.
Jeżeli chcesz wyświetlić plik na standardowe wyjście (dla większości instalacji PHP jest
to przeglądarka), możesz użyć funkcji readf ile ( ) . Funkcja ta ma wbudowane otwarcie
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików_______________239
pliku, więc nie musisz używać do tego osobnej funkcji. Jeżeli chcesz czytać i wykony-
wać operacje na kolejnych wierszach pliku, możesz używać f gets ( ) zamiast f read ( ) .
$fd = fopen{"plikprzykladowy.inc", "r");
while (Ifeof(Sfd))
{
Ślinę = fgets($fd, 4096);
if (strcmp(Sline, $targetline) — 0)
echo "Ciąg został znaleziony!";
}
fclose ($fd);
Jeżeli chcesz odczytać plik jako tablicę, możesz użyć funkcji file (}. Tworzy ona ta-
blicę, w której każdy element jest wierszem oryginalnego pliku, wraz ze znakiem koń-
czącym wiersz. Funkcja f i l e d nie wymaga osobnego otwarcia i zamknięcia pliku.
Poniższa linia używająca f i l e (}:
$line_array = file("plik.inc");
jest odpowiednikiem:
Sfd = fopen($filename, "r+") or die("Nie mogę otworzyć pliku Sfilename");
Sfstring = fread($fd, fiłesize($filename));
$line_array = explode( "\n", Sfstring);
Jeżeli zamierzasz odczytać plik znak po znaku, możesz użyć funkcji fgetc ( ) . Zwraca
znak ze wskaźnika pliku aż do końca pliku.
Zapis do pliku
Jeżeli prawidłowo otwarłeś plik w odpowiednim trybie, zapis danych do pliku jest już
bardzo prosty. Funkcja f write ( ) pobiera wskaźnik do pliku jako argument, zapisywa-
ny ciąg znaków oraz opcjonalnie długość w bajtach. Parametr ten nie powinien być
używany oprócz niektórych specyficznych sytuacji. Funkcja zwraca liczbę zapisanych
znaków.
$fout = fwrite(Sfp, Sstring);
echo "Zapisałem do pliku Sfout znaków";
Funkcja f put s ( ) jest identyczna z fwrite ( ) . Jedynie nazwa fputs ( ) jest w stylu ję-
zyka C.
Najczęściej spotykanym błędem użycia trybów zapisu w PHP jest sposób realizowania
edytowania pliku tekstowego w przeglądarce (przy użyciu formularza HTML). Jeżeli
chcesz otworzyć plik, odczytać jego zawartość, następnie zapisać zmienioną wersję tego
pliku, nie możesz korzystać z trybów "w+". Tryby w usuwają zawartość pliku natych-
miast po jego otwarciu; możesz czytać z pliku otwartego w trybie w+ jedynie te dane,
które zostały do niego zapisane po otwarciu. Aby obejść to ograniczenie, należy raz
otworzyć plik do odczytu i raz do zapisu tak, jak zostało to pokazane w przykładzie
($f ilename zawiera nazwę pliku tekstowego).
240_________________________________________Część l » Podstawy PHP
<?php
if (IsSet($submited))
(
$fd = fopen($filename, "w+") or
die ("Nie można otworzyć pliku Sfilename");
Sfout = fwrite($fd, $newstring);
fclose ($fd);
}
Sfd = fopen (Sfilename, "r"} or
die ("Nie można otworzyć pliku Sfilename");
Sinitstring = freadISfd, filesize(Sfilename));
fclose(Sfd);
echo "<HTML>";
echo "<FORM METHOD='POST' ACTION=\"SPHP_SELF\">";
echo "<INPUT TYPE='HIDDEN' NAME=' submitted' VALUE=1>";
echo "<INPUT TYPE='SUBMIT'>";
echo "</FORM>";
echo "</HTML>";
?>
Powtórzmy jeszcze raz, że zapisywanie plików nie jest dobrym pomysłem, chyba że
możesz bardzo dokładnie konfigurować środowisko pracy programu! Dobrze zabezpie-
czony serwer Intranetu może być odpowiedni dla takich działań, ale zapisywanie na
produkcyjnej witrynie niesie ze sobą spore niebezpieczeństwo. Więcej na ten temat
znajduje się w rozdziale 31. „Bezpieczeństwo i kryptografia".
Aby zablokować zapis do plików, należy odszukać w głównym katalogu PHP plik o na-
zwie fopen-wrappers.c. Po otwarciu tego pliku w edytorze odszukaj definicję funkcji
php__f open_with_path ( ) . Należy dodać w odpowiednim miejscu (na przykład 10 li-
nii od początku funkcji) następujące linie:
// dopuszczam tylko tryb "r"
if (!strcmp(mode, "r"))
return NULL;
Można również zablokować jeszcze kilka opcji, ale pozostawimy w twojej gestii.
Zamknięcie pliku
Operacja zamknięcia pliku jest bardzo prosta:
fclose($ d)
określoną liczbę dostępnych uchwytów plików. Może więc ich zabraknąć, jeżeli nie bę-
dziesz zamykał plików.
Większość z funkcji opisanych poniżej niesie pewne ryzyko, ponieważ powiela funkcje,
które są i powinny być wykonywane przez lokalny komputer.
Niektóre z tych funkcji działają jedynie, gdy proces PHP jest uruchamiany z prawami
administratora. Ponieważ nie jest to domyślne ustawienie, więc tylko administratorzy,
którzy z rozmysłem ustawiaj ą taką konfigurację, strzelaj ą sobie samobójczą bramkę.
feof
Funkcja feof ( ) pobiera nazwę pliku jako argument i sprawdza, czy wskaźnik pliku
znajduje się na jego końcu.
file_exists
Funkcja f ile_exists () jest prostą funkcją, której będziesz często używał korzysta-
jąc z innych funkcji systemu plików. Sprawdza ona, czy plik o podanej nazwie istnieje
w systemie.
if {!file_exists("test.php"))
<
$fd = fopen("test.php", "w+");
)
Funkcja zwraca wartość TRUE, jeżeli plik istnieje, lub FALSE, jeżeli nie został odnale-
ziony. Wynik testu jest zapisywany w pamięci podręcznej, którą można wyczyścić za
pomocą funkcji clearstatcache ( ) .
filesize
Kolejną prostą lecz użyteczną funkcją jest filesize ( ) , która zwraca i zachowuje
w pamięci podręcznej rozmiar pliku w bajtach. Użyliśmy jej w przykładach korzystają-
cych z funkcji f read ( ) . Nigdy nie przekazuj wielkości pliku za pomocą liczby, jeżeli
możesz zrobić to za pomocą funkcji f i l e s i z e f ) .
242_________________________________________Część l » Podstawy PHP
Tabela 13.1.
Funkcje systemu plików
Funkcja Opis
basename (ścieżka) Zwraca nazwę pliku z pełnej ścieżki do pliku
chgrpiplik, grupa) Zmienia grupę pliku w jedną z grup, do których należy proces PHP.
Nie działa w Windows
chmod(plik, tryb) Zmienia tryb dostępu na podany w systemie ósemkowym. Nie działa
w Windows
chown (plik, użytkownik) Jeżeli funkcja jest wykonywana przez administratora, zmienia
właściciela pliku na podanego użytkownika. Nie działa w Windows
clearstatcache ( ) Czyści pamięć podręczną danych o plikach
copytpJiAr, cel) Kopiuje plik w podane miejsce
delete (plik) Patrz unlink
dirname (ścieżka) Zwraca katalog z podanej pełnej ścieżki do pliku
diskf reespace ( "/katalog") Zwraca ilość dostępnego miejsca w podanym katalogu
f g e t c s v f f p , długość, Czyta i przetwarza linię z pliku w formacie CSV
ogranicznik)
f g e t s s ( f p , długość Czyta z pliku wiersz (zakończony znakiem nowego wiersza) i usuwa
[, dostępne znaczniki}) znaczniki HTML i PHP oprócz tych, które zostały dopuszczone
w wywołaniu funkcji
f i l e a t i m e (plik) Zwraca (i zapamiętuje w pamięci podręcznej) datę i czas ostatniego
dostępu do pliku
f i l e c t i m e (plik) Zwraca (i zapamiętuje w pamięci podręcznej) datę i czas ostatniej
zmiany i-node
f ilegroup (plik) Zwraca (i zachowuje w pamięci podręcznej) identyfikator grupy pliku.
Nazwa grupy może być odczytana za pomocaposix getgrid ( )
f ileinode (plik) Zwraca (i zachowuje w pamięci podręcznej) i-node pliku
f ilemtime (plik) Zwraca (i zachowuje w pamięci podręcznej) datę i czas ostatniej
modyfikacji pliku
fileowner (plik) Zwraca (i zachowuje w pamięci podręcznej) identyfikator właściciela
pliku. Nazwa właściciela może zostać odczytana za pomocą
posix g e t p w u i d l )
f ileperms (plik) Zwraca (i zachowuje w pamięci podręcznej) poziom dostępu do pliku
f iletype (plik) Zwraca (i zachowuje w pamięci podręcznej) jeden z typów pliku: fifo,
char, dir, block, link, file, unknown
f lock (plik, operacja) Funkcja blokowania pliku. Wartość parametru musi wynosić:
1 (współdzielony), 2 (na wyłączność), 3 (zwolnienie blokady) lub
+4 (bez blokady w czasie blokowania)
fpassthru ( f p) Przesyła na standardowe wyjście wszystkie dane z pliku określonego
przez deskryptor fp
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików________________243
Tabela 13.1.
Funkcje systemu plików (ciąg dalszy)
Funkcja Opis
fseekffp, przesuniecie, Przesuwa wskaźnik pliku o liczbę bajtów równą przesunięciu,
skąd) rozpoczynając od pozycji określonej przez parametr „skąd"
f t e l l (fp) Zwraca pozycję wskaźnika pliku
set file b u f f e r ( f p Ustawia wielkość bufora zapisu do pliku. Domyślna wartość to 8 KB
[, rozmiar bufora]
is dir (katalog) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli
istnieje podany katalog
is executable (plik) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli plik
jest plikiem wykonywalnym
is_file (plik) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli plik
jest zwykłym plikiem
is l i n k t p l i t ) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli plik
jest dowiązaniem
is readable (plik) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli plik
może być odczytany przez PHP
is w r i t a b l e (plik/katalog) Zwraca (i zachowuje w pamięci podręcznej) wartość TRUE, jeżeli PHP
może zapisywać do podanego pliku lub katalogu
l i n k (cel, plik) Tworzy dowiązanie. Nie działa w Windows
linkinfo (ścieżka) Potwierdza istnienie dowiązania. Nie działa w Windows
mkdir (ścieżka, tryb] Tworzy podkatalog w katalogu określonym przez ścieżkę Prawa
dostępu są określone przez parametr „tryb" w systemie ósemkowym
pclose (fp) Zamyka deskryptor pliku procesu otwarty przez popen ( )
popen ( komenda , tryb) Otwiera plik procesu
readlink ( d o w i ą z a n i e ) Zwraca obiekt zwracany przez dowiązanie. Nie działa w Windows
rename( stara nazwa, Zmienia nazwę pliku
nowa nazwa)
rewind (fp) Przesuwa wskaźnik pliku na początek pliku
rmdir (katalog) Usuwa pusty katalog
stat (plik) Zwraca wybrane informacje o pliku
Istat (plik) Zwraca wybrane informacje o pliku lub dowiązaniu
s y m l i n k ( c e J , łącze) Tworzy łącze symboliczne pomiędzy obiektem docelowym a łączem.
Nie działa w Windows
touch (plik [ , c z a s ] ) Ustawia datę modyfikacji pliku
umask (maska) Zwraca wartość umask i ustawia go na & 0777. Wywołany bez
argumentów zwraca tylko wartość umask
u n l i n k (plik) Usuwa plik
244___________________________________________Część l « Podstawy PHP
Funkcje sieciowe
Funkcje sieciowe to zbiór stosunkowo rzadko używanych funkcji umożliwiających two-
rzenie połączeń sieciowych oraz zwracających informacje. Uruchamia się je z wiersza
poleceń, chyba że tworzysz narzędzia monitorujące pracę urządzeń sieciowych.
•* closelog ( ) jest opcjonalna, jeżeli używa się jej wraz z funkcją syslog ( } .
Funkcje DNS
PHP oferuje kilka funkcji odpytujących DNS; zebraliśmy je w tabeli 13.2.
Tabela 13.2.
Funkcje DNS
Funkcja Opis
checkdnsrr ($/iost, [Styp] ) Sprawdza, czy istnieje zapis w DNS. Domyślnym typem jest MX,
inne typy to: A, ANY, CNAME, NS, SOA
gethostbyaddr ($adresip) Pobiera nazwę komputera o podanym adresie
gethostbyname ( $ n a z w a _ h o s t a ) Pobiera adres komputera o podanej nazwie
get host byname l ($nazwa_hosta) Pobiera listę adresów odpowiadających podanej nazwie komputera
getmxrr ($nazwa_hosta, Sprawdza, czy istnieją wpisy MX dla podanego komputera. Wynik
[tablica hosty_mx] , [ w a g a ] ) zostaje umieszczony w tablicy hosty mx, wypełnia również
informacje o wadze
Funkcje gniazd
Gniazdo jest rodzajem dedykowanego połączenia, umożliwiającego komunikację róż-
nych programów (mogą być uruchomione w różnych komputerach) poprzez przesyłanie
tekstu. Funkcje PHP operujące na gniazdach pozwalają na tworzenie przez skrypt połą-
czenia do serwera, który komunikuje się dzięki gniazdom. Po połączeniu dalsza komu-
nikacja odbywa się przy wykorzystaniu zwykłych funkcji zapisujących i odczytujących
pliki (fputs (), f gets () itd.).
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików_______________245
Tabela 13.3.
Funkcje gniazd
Funkcja Opis
Działanie tych funkcji łatwo zrozumieć, jeżeli wiesz, co to jest znacznik czasu z syste-
mu Unix. Funkcje te można podzielić na trzy rodzaje: zwracające datę lub czas, forma-
tujące datę lub czas oraz sprawdzające poprawność daty.
format, jeżeli chcesz przekazać ten znacznik do innej funkcji lub programu. Przy użyciu
funkcji opisanych w dalszej części rozdziału można przetworzyć tę wartość do postaci
czytelnej dla człowieka.
Najczęściej używaną konstrukcją jest getdate (time ( ) ) , lecz można użyć getda-
te ( ) z dowolną wartością $timestamp.
Jeżeli chcesz za jednym razem pobrać czas i sformatować go, należy użyć funkcji da-
tę ( ) zamiast getdate ( ) . Jeżeli nie zostanie podany znacznik czasu, datę ( ) auto-
matycznie użyje bieżącej daty i czasu. Funkcja ta lepiej formatuje wynik (patrz dalsza
część rozdziału). Funkcja strftime ( ) także formatuje bieżący znacznik czasu (rów-
nież ją opiszemy później), jeżeli nie podamy innego.
Funkcja m k t i m e ( ) konwertuje dowolną datę na znacznik Uniksa. Różni się nieco ko-
lejnością parametrów od komendy Uniksa o tej samej nazwie. Funkcja g m m k t i m e O
robi to samo dla czasu Greenwich.
Jeżeli zajmujesz się historią Pranej i, będziesz zadowolony, że za pomocą kilku wierszy
kodu możesz konwertować daty z kalendarza rewolucji francuskiej na kalendarz grego-
riański. Można powiedzieć: Bon Thermidor, Citoyens et Citoyennes!
Aby użyć funkcji kalendarza, należy samemu skompilować PHP. Funkcje te znajdują
się w jeszcze niestabilnej bibliotece dynamicznej opisanej w pliku dl/README. W czasie
pisania tej książki funkcje kalendarza nie były dostępne w binarnej wersji dla Windows.
jest liczbą double, reprezentującą liczbę dni i godzin od zerowego dnia juliańskiego.
PHP nie pozwala na taki poziom dokładności. Wspomnieliśmy o tym na wypadek, gdy-
by ktoś szukał tej informacji.
Podsumowanie
PHP posiada wiele wbudowanych funkcji, operujących na systemie plików oraz syste-
mowych, które mogą być użyteczne, ale niosą pewne ryzyko. Dla przykładu, istnieje
wiele funkcji powielających narzędzia systemowe Uniksa, takie jak chmod ( ) czy co-
py ( ) . PHP posiada również bardziej zaawansowane funkcje, na przykład odpytujące
DNS. Mimo że lepiej wyłączyć niektóre z nich, mogą być bardzo przydatne, w odpo-
wiednio zaprojektowanym środowisku. Funkcje PHP pozwalające na otwarcie, odczyt
i zapis plików są bardzo silnym narzędziem. Większość problemów związanych z tymi
Rozdział 13. » Funkcje systemu operacyjnego i dostępu do plików_______________249
PHP posiada również wiele funkcji określających datę i czas, więc zawsze będziesz
wiedział, która jest godzina.
250 ______________________________________Część l « Podstawy PHP
Rozdział 14.
Styl PHP
W tym rozdziale:
* Jak dostać punkty za styl
* Komentowanie kodu
* Pisanie kodu łatwego do konserwacji
+ Łączenie PHP i HTML
+ Oddzielenie funkcji od układu strony
W tym rozdziale opiszemy główne punkty stylu PHP oraz sposoby polepszenia funk-
cjonalności, łatwości utrzymania i atrakcyjności kodu. Zawartość tego rozdziału ma
pomóc początkującym programistom PHP wybrać styl, w jakim będą pisali programy.
Większość z tych porad ma zastosowanie we wszystkich językach programowania.
Mamy nadzieję pomóc nowym programistom PHP rozszyfrować kod pisany przez in-
nych. Czytanie trzech różnych podręczników, które sanie ze sobą zgodne i brak w nich
wyjaśnienia przyczyn tej niezgodności, może być dla uczącego się pisać skrypty bardzo
denerwujące. Informacje zawarte w tym rozdziale powinny pomóc zidentyfikować
istotne fragmenty kodu, ukryte w stylistycznych udziwnieniach, i lepiej objaśnić anali-
zowany kod.
Czytelność
Podstawowa cecha skryptu to czytelność. Oko ludzkie lubi wyraźne wzory, logiczną or-
ganizację oraz znaczące powtórzenia. Czytelności sprzyja również umieszczanie naj-
ważniejszego słowa na początku wiersza, a nie ukrywanie go w środku.
Jeżeli projektujesz stronę HTML przy użyciu narzędzia wizualnego, noty za czytelność
nie będą zbyt wysokie. Programy te notorycznie generują źle sformatowany i zoriento-
wany na grafikę kod HTML, wypełniony niewidocznymi rysunkami GIF, absolutnym
skalowaniem i innymi tego typu niedociągnięciami. Wszystkie te błędy znajdują się
w poniższym przykładzie, który został wygenerowany przez powszechnie znany pro-
gram do projektowania. Jego nazwę zachowamy w tajemnicy. Należy dodać, że nie ro-
biliśmy nic, aby pogorszyć wygląd kodu.
<HTMLXHEADXTITLE>Przepis: brzoskwinie w s y r o p i e < / t i t l e x m e t a http-
equi v= "Con ten t -Type "content "text /html;
charset=iso-8859-l"x/HEADxbody bgcolor="#FFFFFF"
t e x t = " # 6 6 6 6 6 6 " link="#CC3300" vlink="#CC3300" alink-"#CC3300">
<table width="401" align="center" border="0"
CELLSPACING="1" C E L L P A D D I N G = " l " >
<tr>
<td width="50"ximg src="spacer .gif"
width="50" height="l"x/tdx/tr>
<td width="300" height="30"
colspan="5"xbxfont face="sans-serif" s i z e = " 2 "
color="#DDAODD">Brzoskwinie w syropie</fontx/bx/td>
<td width="51"ximg src="spacer.gif"
width="51" height="l"x/tdx/tr>
</tr>
<tr>
<td w i d t h = " 4 0 1 " colspan-"7"ximg src="spacer . gif'
width="300" height = "5"x/td>
</tr>
<tr>
<td width="50"ximg src="spacer. gif"
width="50" height="l"x/tdx/tr>
<td width="300" colspan="5"xfont face="sans-
serif" s i z e = " 2 " > < b x i > S k ł a d n i k i < / i x / b > < / f o n t x / t d >
<td width="51"ximg src="spacer . gif"
width="51" height = " l " x / t d x / t r >
Rozdział 14. » Styl PHP__________________________________________253
</tr>
<tr>
<td width="300" colspan="7"xfont face="sans-
s e r i f " size="l"ximg src="line.gif" width="300" h e i g h t = " l "
border="0" align="top"x/fontx/td>
</tr>
<tr>
<td width="50" align="left"xfont
size="l" face="sans-serif"> </td>
<td w i d t h = " 1 0 0 " align="left"xfont
size="l" f a c e = " s a n s - s e r i f " > D u ż e b r z o s k w i n i e < / f o n t x / t d >
<td w i d t h = " 5 0 " a l i g n = " l e f t "
colspan="2"xfont size="l" face="sans-
serif " > 6 & n b s p ; < / f o n t x / t d >
<td align="le t" width="100">
<font size="l" face="sans-
serif"XNULL> </font>
</td>
<td width="51 "ximg src"spacer . gif "
width="51" height="l"x/tdx/tr>
</tr>
<tr>
<td width="50" align="left"xfont
size="l" face="sańs-serif"> </td>
<td width="100" align="left"xfont
size="l" face="sans-serif">Białe w i n o < / f o n t x / t d >
<td width="50" a l i g n = " l e f t "
colspan="2"xfont size="l" f a c e = ' " s a n s - s e r i f " > 2 & n b s p < / f o n t x / t d >
<td align="left" width="100">
<font size="l" face="sans-
serif">szklanki</font>
</td>
<td width="51"ximg
src="spacer.gif" width="51" h e i g h t = " l " x / t d x / t r >
</tr>
<tr>
<td width="50" a l i g n = " l e f t " x f o n t
size="l" f a c e = " s a n s - s e r i f " > & n b s p ; < / t d >
<td width="100"
align="left"xfont size="l" f a c e = " s a n s - s e r i f " > C u k i e r
granulowany</f ontx/td>
<td width="50" align="left"
colspan="2"xfont size="l" face="sans-
serif">lsnbsp;</fontx/td>
<td align-"left" width="100">
<font size="l" face="sans-
serif">szklanka</font>
</td>
<td width="51"ximg src="spacer .gif"
width="51" height="l"x/tdx/tr>
</tr>
<tr>
<td width="50" a l i g n = " l e f t " x f o n t
size="l" f a c e = " s a n s - s e r i f " > s n b s p ; < / t d >
<td width="100"
align="left"xfont size="l" face="sans-serif'>Świeży
imbir</fontx/td>
<td width="50" align="left"
colspan="2"xfont size="l" face="sans-
serif " > 2 s n b s p ; < / f o n t x / t d >
<td align="left" width="100">
<font size="l" face="sans-
serif">plastry  </font>
</td>
<td width="51"><img src="spacer.gif"
width="51" height="l"x/tdx/tr>
</tr>
</table>
<table border="0" w i d t h = " 3 0 0 " align="center">
<tr height = "30"x/tr>
254_________________________________________Część l » Podstawy PHP
<tr>
<td width="29" align="center">i,nbsp; < / t d >
<td colspan="2"xfont face="sans-serif"
size="2"><b><i>Przygotowanie</i></bx/fontx/td>
</tr>
<tr>
<td colspan="2"xfont face="sans-serif"
size="l"ximg src="line .gif" width="300" height = "l" border="0"
align="top"x/fontx/td>
</tr>
<tr>
<td valign="top"xfont size="l"
face="sans-serif " valign="top">l. Łnbsp; < / f o n t x / t d >
<tdxfont size-"l" face="sans-
s e r i f " > W r z u ć brzoskwinie na 15 sekund do wrzącej wody, ułatwi to
obranie ze skórek. Ostrożnie p r z e k r ó j na pół, usuwając pestkę.
snbsp;</fontx/td>
</tr>
<tr>
<td valign="top"xfont size="l" face="sans-
serif " valign=" top">2 . Snbsp; </f ontx/td>
<tdxfont size = "l" face="sans-
serif">Wrzuć wszystkie składniki oprócz brzoskwiń do dużego
rondla. Gotuj aż do rozpuszczenia cukru. snbsp: </fontx/td>
</tr>
<tr>
<td valign="top"xf ont size="l"
face="sans-serif" valign="top">3 . Snbsp; < / f o n t x / t d >
<tdxfont size="l" face="sans-
serif">Gotuj brzoskwinie w syropie na wolnym ogniu przez 15 minut.
Obracaj co 5 minut. Podawaj cieple, polanę syropem
. Snbsp:</fontx/td>
</tr>
</table>
</table>
</bodyx/html>
Próba dodania PHP do takiego pliku HTML może być źródłem problemów. Jeżeli jed-
nak chcesz to robić pamiętaj, że nie jest to wina PHP i skieruj swoje żale do innego
producenta.
Spróbujemy jednak znaleźć jakąś radę dla tych, którzy nie potrafią uwolnić się od na-
rzędzi graficznych.
Program ten naprawia częste błędy w kodzie HTML, takie jak brakujące znaczniki za-
mykające. Poza tym posiada (na razie niewielkie) możliwości współpracy z PHP, jeżeli
używamy standardowych znaczników <?php ... ?>. Można więc naprawić sytuacje,
gdy „tak się śpieszyłem, że zapisałem dokument Microsoft Office jako HTML i doda-
łem kilka fragmentów w PHP". Program ten odczytuje dokumenty Microsoft Office za-
pisane jako HTML, mimo że w chwili pisania książki HTML Tiddy nie był dostępny na
platformie Windows.
wylicza wszystkie miejsca w dokumencie, w których kod nie jest zgodny ze standardem
HTML. W przeciwieństwie do HTML Tiddy, nie poprawia kodu, trzeba samemu punkt
po punkcie wprowadzić zmiany.
Kolejną efektywną metodą zwiększenia czytelności kodu jest kupno dobrego pakietu
zawierającego zarówno dobre narzędzie wizualne, jak i dobry edytor tekstowy. Nie jest
to reklama, ale znanym programem spełniającym te warunki jest Macromedia Dream-
weaver. Działa na Macach i pod Windows, posiada obsługę PHP i może współpracować
z wieloma programami obróbki obrazu.
Nawet projektanci PHP, którzy piszą cały kod ręcznie, mogą sobie pomóc, wybierając
dobry edytor tekstowy (mówiliśmy o tym w rozdziale 3.). Po co marnować czas na za-
mykanie znaczników HTML, czy szukanie brakujących nawiasów? Większość dzisiej-
szych edytorów tekstu potrafi automatycznie domykać znaczniki i nawiasy, co pomaga
uniknąć trywialnych błędów.
Niektórzy nie potrafią zaakceptować faktu, że pamięć w komputerze nie jest bezcenna.
Spędzają mnóstwo czasu na próbach zacieśnienia tekstu programu tak, aby zajmował
możliwie mało miejsca. Praktyka ta jest niestety wspierana przez niektóre książki tech-
niczne oraz wydawców czasopism, którzy żądaj ą od autorów, aby używali różnych spo-
sobów ograniczenia długości wydruków programów (jak w poniższym przykładzie).
<?php if ($UserID Si strlen($Horse)>0) {
if($Horse=="Man O'War") print("Kasztan, białe plamki");
elseif($Horse=="Native Dancer") print("Jasnoszary");
elseif($Horse=="Seattle Slew") print("Czarny");
) else {
print{"Spróbuj jeszcze raz."); }?>
<HTMLXBODY>
<?php
if (SUserlD && strlen (SHorse) >0)
(
if($Horse=="Man 0'War")
print("Kasztan, biaie plamki");
elseif($Horse=="Native Dancer")
print{"Jasnoszary");
elseif($Horse=="Seattle Siew")
print("Czarny");
)
else
{
print("Spróbuj jeszcze raz.");
(
?>
</BODYX/HTML>
Na koniec należy wspomnieć, że staranność również się liczy. Staje się szczególnie
ważna, gdy liczba współpracujących ze sobą programistów wzrasta i coraz ważniejsze
stają się zagadnienia łatwości konserwacji.
Komentarze
Wstawianie komentarzy do kodu jest często porównywane do czyszczenia zębów za
pomocą nitki: ważne dla zdrowia i higieny, ale zbyt często pomijane „tylko tym razem".
Ponieważ w PHP używa się o wiele więcej zmiennych niż w HTML, powinieneś opra-
cować wydajny schemat nazywania zmiennych na wszystkie okazje. W dalszej części
rozdziału przedstawimy kilka wskazówek.
Nawet jeżeli większość systemów plików pozwala na używanie drugich nazw, to kata-
log taki oglądany jako ikona nie wygląda dobrze. Użytkownicy GUI mogą być świado-
mie lub nieświadomie uprzedzeni do używania długich nazw. Etykiety ikon są zwykle
krótkie i to powoduje tendencję do używania bardzo zwięzłych nazw plików. Spróbuj
nadać plikowi długą i skomplikowaną nazwę (np.: PrzepisBrzoskwinieWSyropie.php)
i umieść go na pulpicie — nie wygląda to najlepiej.
W tej sytuacji możesz korzystać z zalet obu rozwiązań: krótkich nazw plików i jedno-
znacznej identyfikacji.
PHP nie ogranicza długości nazw zmiennych, więc możesz tworzyć długie, ale niosące
dużo danych nazwy, jak np. $AdcłressOfCli v entCompanyInSasketchewan. To twoje
skrypty. Musisz jedynie ostrożnie stosować zmienne o długich nazwach jako fragmenty
formularza, obsługiwanego za pomocą metody GET.
Wewnętrzne duże litery powodują, że nazwa posiada „garby", dlatego ten sposób kon-
struowania nazw jest często nazywany wielbłądzim.
Łatwość konserwacji
Weterani programowania twierdzą, że łatwość konserwacji jest najważniejszą z zalet.
Niestety głównym problemem jest to, że łatwość konserwacji jest zwykle w konflikcie
z innymi celami, najczęściej z wydajnością. W czasach Internetu zdarza się, ktoś inny
konserwuje nasz kod, wydaje się więc, że widać zwycięzcę tego konfliktu.
Gdy oprocentowanie zmieni się, np.: 15%, ktoś będzie musiał znaleźć i zmienić miej-
sca, w których ta wartość występuje. Podczas szukania w tekście wartości 15,5 bardzo
łatwo opuścić wartość 1.155 w drugim wierszu przykładu. Spowoduje to prezentację
nieprawdziwych danych.
Funkcje
Po próbach prowadzenia złożonej witryny WWW, która używała języka skryptowego
nieposiadającego funkcji, możemy powiedzieć, że funkcje pełnią kluczową rolę w pie-
lęgnacji oprogramowania. Sztuka stosowania abstrakcji proceduralnej przy użyciu
funkcji może być opisana w osobnej książce. Przytoczymy tylko krótkie wskazówki.
* Musi istnieć powód przeniesienia fragmentu kodu PHP do funkcji, szczególnie
w przypadku wielokrotnego użycia tego kodu.
•* Pisz krótkie definicje funkcji. Jeżeli funkcja zbytnio się rozrasta, podziel ją na
mniejsze funkcje.
•* Funkcja musi być zdefiniowana przed użyciem. Kolejność funkcji nie ma
znaczenia.
Pliki dołączane
Jedną z największych przewag dynamicznych stron WWW nad statycznym HTML jest
zdolność zmniejszania redundancji. Każdy, kto kiedykolwiek zarządzał statyczną wi-
tryną dowolnych rozmiarów, wie, jak wiele stałych fragmentów znajduje się na stronie
— zmiana każdego znaku nie jest prosta, jeżeli witryna ma 200 stron.
Rysunek 14.1.
Plik PHP
z włączanymi
plikami
<BODY>
<TABLE BORDER=0 CELLPADDING=0 WIDTH=100%>
<TR>
<font c o l o r = " # O O O O B B " x ? p h p include ( "navbar . inc" ) ; ?>
</fontx/TD>
<TD BGCOLOR="#FFFFFF" ALIGN-LEFT VALIGN=TOP W I D T H = 8 0 % >
<TABLE CELLPADDING=15XTRXTD ALIGN=LEFT VALIGN=MIDDLE>
<Hl>Dzisiejsze menu</Hlx/TDX/TR>
<TRXTD ALIGN=LEFT>
262_________________________________________Część l » Podstawy PHP
<font color="łOOOOBB"><?
Stoday = date("Ymd");
include("Stoday.inc");
?>
</font> </TDX/TRX/TABLE>
</TDX/TRX/TABLE>
<BRXBR>
<!-- Nie ma szczególnego powodu umieszczania tej stopki w włączanym pliku.
Mamy po prostu taki kaprys —>
<?php include("footer.inc") ; ?>
navbar.inc
<TD BGCOLOR="#FFA500" ALIGN=CENTER VALIGN=TOP WIDTH=20%>
<H2>Bar<BR>firmowy</H2>
<BRXBR>
Wpisz <BR>
<A HREF=" suggest ion. php">propozycję</AXBRXBR>
Powiedz nam o twoich<BR>
<A HREF="preferences .php">ulubionych potrawach</AXBRXBR>
Zapisz się na specjalne <BR>
<A HREF="holidays.php">menu świąteczne</AXBRXBRX/font>
20010127.inc
<PXB>27 styczeń 2001</Bx/P>
Zupa: Pomidorowa<BR>
Kanapki: Sałatka z kurczaka<BR>
Potrawa wegetariańska: Makaron z serem<BR>
Deser: Budyń czekoladowy<BR>
<PX?php
//Możesz włączać pliki do włączanego pliku
include("always.inc"};
?>
</P>
always.inc
Bułki, ciastka, paluszki<BR>
Woda, kawa, herbata, mleko, napoje<BR>
Lody waniliowe<BR>
footer.inc
<P>Copyright 1994-<?php Stoday=date("Y"); print("Stoday"); ?>
Szef kuchni</P>
</BODYX/HTML>
Interfejs obiektowy
Ponieważ jeszcze nie opisaliśmy systemu obiektowego PHP, wspomnimy tylko, że kon-
sekwentne użycie obiektów może ułatwić utrzymanie systemu. Dla przykładu, niektórzy
projektanci witryn PHP na podstawie bazy danych zawarli wszystkie funkcje specyficz-
ne dla bazy w metodach obiektu, więc pozostały kod nie wie nawet, z jaką bazą danych
współpracuje. Jeżeli zdecydują się przenieść bazę z MySQL na Oracle, tylko obiektowa
część kodu będzie wymagała zmian.
Solidność
1. Kod powinien wykrywać sytuacje wyjątkowe i odpowiednio je obsługiwać.
2. Program powinien informować o awaryjnym zakończeniu pracy.
Ozdobne języki, takie jak Java, posiadają obszerne systemy obsługi wyjątków, które
wymagaj ą napisania kodu określającego rangę awarii i sposoby reagowania na sytuacje
awaryjne. Niestety PHP nie ma takich konstrukcji — programiści PHP sprawdzać po-
prawność operacji i posługiwać się funkcją die ( ) .
Niedostępność usługi
PHP jest w części językiem zapewniającym jedno środowisko, w którym mogą być
używane różne biblioteki kodu i zewnętrzne usługi. Każda strona PHP może otworzyć
plik, połączyć się z bazą danych, odpytać serwer LDAP, wysłać nagłówek HTTP lub
wysłać pocztę przez serwer SMTP. Ważne jest aby przewidzieć, kiedy zewnętrzna usłu-
ga będzie niedostępna, przekroczony zostanie czas oczekiwania, połączenie będzie źle
działało lub zostanie przerwane w czasie pracy.
Bardzo często usługi te mają możliwość odczytania kodu błędu i wyświetlenia go, jeżeli
jedyną możliwością jest zakończenie pracy. Rozsądną konstrukcją realizującą połącze-
nie do bazy danych MySQL jest:
Sconnection = mysql_connect([argumenty]) or
die("Połączenie nieudane: $php_errormsg<BR>");
Jest to dobra metoda obsługiwania błędów. Lepiej zakończyć działanie, niż wykonywać
skrypt, zakładając, że połączenie z bazą istnieje.
Zwięzłość i wydajność
Zwięzłość i wydajność nie są tożsame. Zwięzły kod realizuje zadanie przy użyciu małej
liczby wierszy lub znaków, a wydajny kod realizuje to zadanie szybko, używając nie-
wiele pamięci. W tym fragmencie rozdziału podzielimy się kilkoma wskazówkami na
temat pisania zwięzłego i wydajnego kodu PHP oraz subiektywnymi opiniami na temat
sensowności takich działań.
Jest jednak odmiana dbałości o wydajność, która prawdopodobnie zawsze będzie ważna:
wybór algorytmu lub sposobu, w jaki twój kod realizuje zadanie. Jeżeli twój kod poszu-
kuje nazwy w bazie danych poprzez pobranie wszystkich nazw i kolejne ich porówny-
wanie, szybko przekonasz się, jak ważna jest wydajność.
Poprawianie wydajności
Przedstawimy teraz kilka sposobów, jakie powinieneś stosować w trakcie programowania.
Znajdź sedno
Lepiej od początku używać efektywnych algorytmów. Jeżeli tego nie zrobiliśmy, nie
warto optymalizować kodu, zanim nie znajdziemy miejsca, które zużywa zbyt dużo
Rozdział 14. » Styl PHP___________________________________________265
Zwięzłość: zmniejszanie
Zanim przedstawimy sposoby pisania bardziej zwięzłego kodu, musimy powiedzieć, że
według nas zwięzłość jest przecenianą zaletą, z poniższych powodów.
266_________________________________________Część l » Podstawy PHP
Splist = $i;
return($plist);
}
Oczywiście jest to implementacja sita Erastonesa, a $plist, jest listą wszystkich liczb
pierwszych mniejszych od $n. Oczywiście.
Pętla ta (zakładając, że $count jest liczbą całkowitą dodatnią) wywoła funkcję dla każ-
dej liczby, aż do l.
/* użycie funkcji */
if (PodzielnyPrzezZleO, 3))
$divisible_result = TRUE;
else
$divisible_result = FALSE;
if ($divisible_result == TRUE)
print("Jest podzielne!<BR>") ;
else
if ($divisible result == FALSE)
print("Nie jest podzielne!<BR>");
/* użycie funkcji */
if (PodzielnyPrzezLepiej(9,3))
print("Jest podzielne!<BR>");
else
print("Nie jest podzielne!<BR>");
Powtórzmy raz jeszcze, opisowa nazwa funkcji jest już częścią dokumentacji; każdej
funkcji można użyć w przyszłości, co oczywiście ułatwia utrzymanie oprogramowania.
Najprostszym sposobem pokazania różnych sposobów jest napisanie tego samego skryptu
w stylu „minimalnym PHP", „maksymalnym PHP i „średnim PHP". Pamiętaj, że wszyst-
kie są prawidło we i daj ą ten sam wynik. Wybory stylistyczne są sprawą upodobań i zwię-
złości, a czasami niewielkich różnic w możliwościach.
Rysunek 14.2.
Minimalny PHP,
część l
Rysunek 14.3.
Minimalny PHP,
ekran drugi
270_________________________________________Część l » Podstawy PHP
<HTML>
<HEAD>
<TITLE>MorningService.html</TITLE>
<STYLE TYPE="text/css">
<! --
BODY {color: fłOOFFOO; font-family: times; font-size: 12 pt)
HI (margin-top: 10; margin-bottom: 10; color: red; font-family: cursive;
font-size: 16 pt; font-weight: bold}
A:link {color: red)
—>
</STYLE>
</HEAD>
<BODY>
<CENTERXHl>Hotel California</HlX/CENTER>
<P>Dziękujemy za zatrzymanie się w hotelu California!<BR>
Proszę powiedzieć nam, co może sprawić, aby poranne słońce świeciło
jeszcze jaśniej.
<FORM METHOD="POST" ACTION="MorningService.php">
<P>Numer pokoju <INPUT TYPE="TEXT" NAME="Room" SIZE=10>
<P>Czy zamawia pani/pan budzenie?<BR>
Jeżeli tak, proszę wpisać godzinę <B>w formacie 24-godzinnym (GG:MM)</B>
<INPUT TYPE="TEXT" NAME="WakeupTime" SIZE=10XBR>
<P>Proszę wybrać gazetę:<SELECT NAME="Newspaper" SIZE=1>
<!-- Ponieważ całkowicie oddzielamy PHP od HTML, nie da się tworzyć
tej listy dynamicznie. Jeżeli lista gazet zmieni się,
Musisz ręcznie zmodyfikować ten formularz.
—>
<OPTION VALUE=0>Proszę dostarczyć...
<OPTION VALUE=1>USA Today
<OPTION VALUE=2>New York Times
<OPTION VALUE=3>Wall Street Journal
</SELECT>
<PXINPUT TYPE="Submit">
</FORM>
</BODYX/HTML>
<HTML>
<HEAD>
<TITLE>MorningService.php</TITLE>
</HEAD>
<BODY>
<?php if(SNewspaper == O && $WakeupTime == "") (?>
<P>Przepraszam, ale nie powiedziałeś nam, co mamy zrobić!
<?php ) elseif($Newspaper == O && SWakeupTime != "")
{
maił("deskclerk@hotelcali.com", "$Room", "Budzenie o: SWakeupTime"); ?>
<P>Budzenie zostało zarejestrowane. Śpij dobrze!
<?php } elseif($Nęwspaper > 0) {
maił{"deskclerk@hotelcali.com", "$Room", "Budzenie o:
SWakeupTime i dostarczenie SNęwspaper"); ?>
<P>Budzenie i dostarczenie gazety zostały zarejestrowane, śpij dobrze!
<?php ); ?>
</BODYX/HTML>
W stylu minimalnym oddzielamy HTML i PHP, stosując dwa osobne pliki, MorningSe-
rvice.html (formularz) oraz MorningService.php (skrypt przetwarzający formularz). Jest
to szczególnie użyteczne dla grup projektantów, które mają oddzielne zespoły zajmują-
ce się opracowaniem produktu od strony klienta i od strony serwera. Format ten po-
zwala na równoległą pracę i umożliwia szybkie wprowadzanie zmian do projektu. Styl
ten utrudnia jednak stosowanie zabiegów, takich jak pokazywanie nieco zmienionej
strony HTML w zależności od określonych warunków.
Rysunek 14.4.
Maksymalny PHP
po wysłaniu danych
<?php
print("<HTML>\n");
print("<HEAD>\n");
print("<TITLE>MorningService.html</TITLE>\n");
print("<STYLE TYPE=\"text/css\">\n") ;
print ("<!—\n") ;
print("BODY (color: #000000; font-family: times; font-
size: 12 pt)\n");
print("HI (margin-top: 10; margin-bottom: 10; color:
red; font-family: cursive; font-size: 16 pt; font-weight:
bold)\n");
print("A:link (color: red)\n");
print("~->\n");
print("</STYLE>\n");
print("</HEAD>\n");
print("<BODY>\n");
print ("<CENTERXHl>Hotel Calif ornia</Hlx/CENTER>\n" ) ;
print("<P>Dziękujemy za zatrzymanie się w hotelu California!<BR>\n") ;
/* Poniższy test używa wartości przycisku SUBMIT z formularza,
aby sprawdzić, czy użytkownik ogląda pierwszy raz stronę
(w tym przypadku nie wysyłamy komunikatu do recepcji)
lub wysłał/zmienia wcześniejsze zamówienie */
if(llsSet($Submit))
(
/* Poniższe puste wartości spowodują wyświetlenie pustego
formularza. */
$Room = "";
SWakeupTime = "";
$Newspaper = "";
print("Proszę powiedzieć nam, co może sprawić, aby poranne słońce świeciło
jeszcze jaśniej.\n");
)
else
{
if(SNewspaper == O S5 SWakeupTime == "")
print("<P>Właśnie usunąłeś wszystkie zamówienia porannej obsługi.\n");
elseif($Newspaper == O ss SWakeupTime != "")
(
maił("deskclerk@hotelcali.com", "$Room", "Budzenie o:
SWakeupTime");
272_________________________________________Część l » Podstawy PHP
Styl ten jest faworyzowany przez programistów C, ponieważ jest bardzo podobny do
sposobu realizacji zadań w tym języku programowania. Jeżeli nie masz dużej praktyki
w pisaniu kodu tworzącego HTML, styl ten może wydawać ci się niezgrabny i mylący.
Ponieważ PHP i HTML są ze sobą połączone, możesz uzależnić wyświetlenie każdego
wiersza HTML od wielu warunków sprawdzanych w PHP. Jednak niełatwo wprowa-
dzić zmiany projektu graficznego.
Średni PHP:
<?php
function display_form()
{
?>
<CENTERXHl>Hotel California</Hl></CENTER>
<P>Dziękujemy za zatrzymanie się w hotelu California!<BR>
Proszę powiedzieć nam, co może sprawić, aby poranne słońce świeciło
jeszcze jaśniej.
<FORM METHOD="POST" ACTION="<?php echo($PHP_SELF); ?>">
<P>Numer pokoju <INPUT TYPE="TEXT" NAME="Room" SIZE=10>
<P>Czy zamawia pani/pan budzenie?<BR>
Jeżeli tak, proszę wpisać godzinę <B>w formacie 24-godzinnym (GG:MM)</B>
<INPUT TYPE="TEXT" NAME="WakeupTime" SIZE=10XBR>
<P>Proszę wybrać gazetę:<SELECT NAME="Newspaper" SIZE=1>
<OPTION VALUE=0>Proszę dostarczyć...
Rozdział 14. » Styl PHP__________________________________________273
function process_form()
{
global SNewspaper, $WakeupTime, SRoom;
if($Newspaper == O ss SWakeupTime == "")
print("<P>Przepraszam, ale nie powiedziałeś nam, co mamy zrobić!");
elseif($Newspaper == O ss $WakeupTime != "")
(
maił("deskclerk@hotelcali.com", "$Room", "Budzenie o SWakeupTime"};
print("<P>Budzenie zostało zarejestrowane. Śpij dobrze!");
}
elseif($Newspaper > 0)
f
maił("deskclerk@hotelcali.com", "SRoom", "Budzenie o SWakeupTime
i dostarczenie SNewspaper");
print("<P>Budzenie i dostarczenie gazety zostały zarejestrowane.
Śpij dobrze!");
}
}
?>
<HTML>
<HEAD>
<TITLE>MorningService.php</TITLE>
<STYLE TYPE="text/css">
BODY (color: łOOOOOO; font-family: times; font-size: 12 pt)
HI {margin-top: 10; margin-bottom: 10; color: red; font-
family: cursive; font-size: 16 pt; font-weight: bold}
A:link (color: red}
</STYLE>
</HEAD>
<BODY>
<?php
if(!IsSet($Submit))
(
Sroom = "";
SWakeupTime = "";
SNewspaper = "";
display_form();
}
else
(
process_form(};
print("<P>Możesz zmienić wartości w formularzu i ponownie je wysłać
An") ;
display_form();
)
?>
</BODYX/HTML>
Jest to, jak można było przypuszczać, kompromis. Część HTML oprócz nagłówka jest
umieszczona w funkcji, również część przetwarzająca formularz jest umieszczona
w funkcji. Proste instrukcje sterujące (umieszczone w nagłówku i stopce HTML) decy-
dują, czy uruchomić pierwszą, czy obie funkcje.
Styl ten nie jest, być może, estetyczny, może nawet wydawać się czasami niejasny, ale
pozwala na pełne wykorzystanie możliwości PHP. PHP jest produktem hybrydowym
— nie jest to C, ani Perl, ani HTML, ale każdy z tych produktów ma na niego wpływ.
Styl hybrydowy jest chyba najlepszy.
274_________________________________________Część l » Podstawy PHP
Funkcje
Na przykładzie średniego PHP pokazaliśmy, że własne funkcje mogą być bardzo ela-
stycznym narzędziem formatowania strony. Jest to jedna z funkcji, które stawiają PHP
ponad językami skryptowymi bazującymi na znacznikach.
W tym przykładzie każda strona ładuje ten sam plik funkcji użytkowych, potem plik
funkcji specyficznych dla strony, następnie definiuje zmienne globalne dla strony. Na
koniec komendy PHP umieszczane są w roboczym HTML. Zawartość strony rozmiesz-
czona jest w kolumnach. Aktualna zawartość zależy od funkcji odpowiednich stron,
które mają zawsze te same nazwy, ale zmieniające się definicje. Zmiana zawartości ko-
lumn oznacza modyfikację funkcji strony lub zmianę zawartości bazy danych. Taka
konstrukcja nie wymaga pracy programisty przy wprowadzaniu niewielkich zmian
układu, jeżeli osoba wprowadzająca zmiany będzie modyfikowała tylko HTML, zosta-
wiając PHP w spokoju.
Powyższy przykład jest tylko jednym spośród wielu sposobów podziału pracy. Wybór
strategii powinien zależeć od typu witryny i stylu pracy współpracowników.
276_________________________________________Część l « Podstawy PHP
Podsumowanie
Inne języki programowania mogą wymagać wielu elementów PHP. Powinieneś pisać
czytelny kod z odpowiednio abstrakcyjnymi funkcjami, z konsekwentnymi wcięciami
i opisowymi komentarzami. Powinieneś też unikać magicznych liczb, powielenia kodu,
nadużywania zmiennych globalnych i sztuczek. Program powinien pracować na znanych
danych i podejmować rozsądne działania w przypadku danych, których się nie spo-
dziewałeś. W przypadku awarii powinien kończyć pracę i opisać powód jej przerwania.
a nie przez:
file://home/httpd/html/mysite/mypage.php
Nieodnaleziony serwer
lub niemożliwe wyświetlenie strony
Jeżeli nie można odszukać serwera, może to być spowodowane problemami z DNS
(serwer nazw) lub konfiguracją serwera WWW.
Jeżeli możesz dostać się do serwera, podając zamiast nazwy jego adres IP, problem jest
najprawdopodobniej związany z DNS. Być może alias serwera nie został prawidłowo
rozesłany. Problem ten występuje, nawet jeżeli witryna długo pracuje. Problem może
też być spowodowany wyłączeniem serwera DNS lub lokalnymi kłopotami z siecią.
Jeżeli nie możesz dostać się do witryny przez adres IP, może nie został prawidłowo po-
łączony z kartą sieciową (lub httpd nie obsługuje wywołań dla określonej domeny, patrz
rozdział 4.).
Rozdział 15. » Podstawowe pułapki PHP_______________________________279
Problemy z wyświetlaniem
W tej części opiszemy problemy, kiedy PHP nie wyświetla komunikatu o błędzie, ale
brak oczekiwanych skutków działania.
Jeżeli tworzyłeś ten plik korzystając z edytora tekstu, sprawdź, czy nie opuściłeś np. znacz-
nika zamykającego </TABLE> lub </FORM>. Jeżeli używałeś edytora wizualnego, problem
może tkwić w dodatkowym elemencie. Plik pochodzący z takiego edytora, zamieszczony na
początku rozdziału 14., posiada o wiele więcej znaczników </TR> niż wierszy tablicy. Róż-
ne przeglądarki mogą to w rozmaity sposób obsługiwać. HTML stworzony przy użyciu
edytora tekstowego o wiele rzadziej jest narażony na takie problemy.
Jeżeli nie możesz znaleźć niczego złego w kodzie HTML — szczególnie jeżeli używasz
maksymalnego stylu PHP — z pewnością moduł PHP nie pracuje. Sprawdź to, ogląda-
jąc inną stronę (musi działać prawidłowo). Jeżeli nie zajmujesz się administracją witry-
ny, będziesz musiał porozumieć się z administratorem systemu.
Obejrzyj źródło HTML w komputerze klienta. Czasami źródło jest złamane w dziwnym
miejscu. Jeżeli źródło nie złamie się, wstaw tymczasowe komunikaty o błędach (w try-
bie HTML) w różnych częściach skryptu, aż do miejsca załamania, jak w przykładzie:
280 Cześć l * Podstawy PHP
Rysunek 15.1.
Niekompletny wynik
HTML
<HTML>
<HEADX/HEAD>
<BODY>Czy występuje zakłócenie na pozycji 1.?
<TABLE><TRXTD>Pozycji 2.? To, co mamy tutaj, to </TDx/TR>
<?php
SProblem = "problem z komunikacja";
print("SProblem"); ?>. Albo pozycja 3.?
</BODY>
</HTML>
Rysunek 15.2.
Użycie tymczasowych
komunikatów o biedach
Rozdział 15. » Podstawowe pułapki PHP_____________________________281
Strona może być niekompletna z powodu braku przetwarzania PHP, jak w następują-
cym skrypcie:
<HTML>
<HEADX/HEAD> t
<BODY>To, co mamy tutaj, to jest
<?php
SProblem = "problem z komunikacją";
print("$Problem"); ?>.
</BODY>
</HTML>
Rysunek 15.3.
Awaria
przy przetwarzaniu
Inaczej mówiąc, cała zawartość części HTML została pokazana, ale nie jest wyświetla-
na część PHP, ani żaden komunikat o błędzie. Jest to objaw niedziałania skryptu PHP
lub nieuaktywnienia PHP w serwerze WWW, na którym strona jest umieszczona (pro-
szę się nie śmiać, to się czasem zdarza).
Łatwo zapomnieć, że PHP traktuje włączane pliki jako HTML, a nie PHP, chyba że
umieścisz na początku tego pliku otwierający znacznik PHP. Załóżmy, że ładujemy na-
stępujący plik PHP:
282___________________________________________Część l » Podstawy PHP
<HTML><HEADX/HEADXBODY>
<?php include("secret.php");
secret_function(} ; ?>
</BODYX/HTML>
Rysunek 15.4.
Włączany PUP
pokazuje się jako HTML
Nieodnaleziona strona
Jeżeli przeglądarka nie może odszukać utworzonej przez ciebie strony, a ostatnio instalo-
wałeś PHP, przejrzyj pierwszą część tego rozdziału. Jeżeli wyświetla się ten komunikat,
a inne pliki PHP otwierają się bez problemu, prawdopodobnie pomyliłeś się podając
ścieżkę lub nazwę pliku.
Rozdział 15. » Podstawowe pułapki PHP_____________________________283
Jak się okazało, jest to komunikat błędu nieodnalezienia strony w NT. PHP nie załado-
wał nawet pierwszego wiersza aktywnego pliku, ponieważ nie ma aktywnego pliku; nie
ma aktywnego pliku, ponieważ nie można odszukać pliku o podanej nazwie.
Rysunek 15.5.
Komunikat
o biedzie skladni
Z definicji błędy te występują w trybie PHP. PHP zwraca bardziej opisowe komunikaty
o błędach niż HTML, szczególnie użyteczny jest numer wiersza, w którym wystąpił błąd.
Najczęstsze przyczyny błędów składni opisane sąw kolejnych częściach tego rozdziału.
Wszystkie z nich mają małe znaczenie i łatwo je usunąć, a PHP ułatwia ich odszukanie.
Jednak wszystkie błędy składni powodują wyświetlenie tego samego komunikatu (poza
nazwą pliku i numerem wiersza). Fragmenty HTML znajdujące się w pliku nie zostaną
wyświetlone, nawet gdy znajdują się przed wierszem powodującym błąd.
284_________________________________________Część l » Podstawy PHP
Brakujący średnik
Jeżeli każda instrukcja PHP nie będzie zakończona średnikiem, wystąpi Wad składni.
W pierwszym wierszu poniższego fragmentu kodu brakuje średnika, dlatego przypisa-
nie do zmiennej nie będzie zakończone.
Mamy tutaj
<?php
^Problem ~ "mały kłopot"
print ("SProblem"); ?>.
Brak znaku $
Innym częstym problemem jest brak znaku $ poprzedzającego zmienną. Jeżeli opusz-
czono $ w wierszu z przypisaniem wartości do zmiennej, wystąpi błąd składni:
Mamy tutaj
<?php
Problem = "mały kłopot";
print ("$Problem"); ?>.
Jeżeli opuścimy go np. podczas wyświetlania wartości zmiennej, błąd się nie pojawi:
Mamy tutaj
<?php
SProblem = "mały kłopot";
print ("Problem"); ?>.
Rysunek 15.6.
Brak znaku $
podczas wyświetlania
zmiennej
Rozdział 15. » Podstawowe pułapki PHP_____________________________285
Ten problem jest szczególnie często spotykany w przypadku krótkich bloków PHP.
Odwrotnie, jeżeli nie otwarłeś prawidłowo bloku PHP, cała jego zawartość zostanie wy-
świetlona jako HTML.
Błędy często pojawiają się przy łączeniu krótkich bloków HTML i PHP:
<FORM>
< I N P U T T Y P E = " T E X T " SIZE=15 N A M E = " F i r s t N a m e " VALUE="<?php p r i n t ( " S F i r s t N a m e " ) ;
t?>">
<INPUT TYPE="TEXT" SIZE=15 NAME="LastName" VALUE="<?php print("$LastName"); ?>">
<INPUT TYPE="TEXT" SIZE=10 NAME="PhoneNumber" VALUE="<?php print(SPhoneNumber");
^?>"
<INPUT TYPE="SUBMIT" NAME="Submit">
</FORM>
Ten fragment kodu pokazuje, jak łatwo można przeoczyć element na skomplikowanej
stronie z wieloma małymi, ale ważnymi elementami. Można zredukować liczbę błędów,
używając dobrego edytora tekstu, bądź testując najpierw HTML, a później dodając do
niego PHP. Można oczywiście stosować oba te sposoby.
Nieoznaczone apostrofy
Innym rodzajem błędów składni, charakterystycznym dla maksymalnego stylu PHP, są
nieoznaczone apostrofy.
<?php
print("Piotr powiedział, /"Mamy tutaj");
$Problem = "mały kłopot\"";
print ("$Problem"); ?>.
286_________________________________________Część l » Podstawy PHP
Czasami błąd składni zawarty jest w wyrażeniu, jednak nie potrafimy sami go zlokali-
zować. Jeżeli błąd sygnalizowany jest w ostatnim wierszu pliku, zwykle apostrof, na-
wias lub klamra nie zostały zamknięte. PHP próbował odnaleźć brakujący element,
przeszukując skrypt do końca.
Uprawnienia do plików
Większość systemów operacyjnych posiada pewne mechanizmy uprawnień, które okre-
ślają dostęp użytkowników do plików i katalogów. Serwer WWW uruchamiany jest ja-
ko jeden z użytkowników systemu, więc musi posiadać prawo do czytania wszystkich
plików, z których korzysta, także HTML i PHP.
Najczęstszym powodem tego błędu jest to, że nie ustawiłeś prawa do wykonywania pli-
ków w tym katalogu (Unix) lub nie uaktywniłeś wykonywania skryptów (Windows).
Pamiętaj, że skrypt PHP będzie uruchamiany przez innego użytkownika niż twój robo-
czy użytkownik. W systemie Unix PHP zwykle korzysta z konta nobody, który jest
ograniczony tylko do usługi HTTP. W systemie Windows każde żądanie HTTP jest re-
alizowane przez anonimowego gościa.
Ł o
Ostrzeżenie iS* * *" Co""*" U* _ _ _ _ _ _ _
przy włączaniu pliku l 4" •*>" 3> _ft ,*. a4 <-* fi & HfSl
J Back '•<•• >'; Rebad Home Seatch Netscape Pnnt Secmiły Shop '- 1 Jjp||
§ -»t' Bookmarks .^ location: [hHp7/localhosl/php/badjndude php J-J (fJjTWhafł Related
» y : .
Komunikat pojawi się również, gdy skrypt spróbuje włączyć plik znajdujący się w in-
nym katalogu, a użytkownik PHP nie posiada uprawnień do tego katalogu. Ogólnie
rzecz ujmując, użytkownik serwera WWW (często nobody w systemie Unix) powinien
mieć uprawnienia do czytania i wykonywania katalogu (możesz wszystkim nadać te
uprawnienia, w systemie Unix 755). Możesz nadać wszystkim uprawnienia do czytania
plików z tego katalogu (np. 744).
Nieprzypisane zmienne
PHP różni się od wielu języków programowania, w których zmienne muszą być dekla-
rowane przed użyciem. Domyślnie PHP nie sprzeciwia się używaniu zmiennych, zanim
zostaną przypisane. Jeżeli zapomnisz przypisać wartości zmiennej, błędy bezpośrednie
nie wystąpią. Przestaw poziom raportowania błędów na poziom 15 poprzez wywołanie
error_reporting (15) albo error_reporting (E_ALL), jeżeli chcesz otrzymywać
ostrzeżenia. Niektóre objawy tego typu problemów przedstawiamy poniżej.
Rysunek 15.8.
Efekt niezainicjowanych
zmiennych
<?php
$pierwszy_clag = "jeden";
$trzeci_ciag = "trzy";
$j eden = 1;
Sdwa = 2;
print("Matematyka jest łatwa jak $pierwszy_ciag, $drugi_ciag,
Strzeci_ciag!<BR>");
print("$pierwszy_ciag równa się $jeden<BR>");
print("$drugi_ciag równa się $dwa<BR>");
print("$trzeci_ciag równa się $trzy<BR>");
print("$pierwszy_ciag podzielone przez $drugi_ciag wynosi".
($jeden / $dwa). "<BR>");
print("$pierwszy_ciag podzielone przez $trzeci_ciag wynosi".
(Sjeden / $trzy). "<BR>");
?>
Problemy z zasięgiem
Dopóki nie korzysta się z definicji funkcji, określenie zasięgu zmiennych w PHP jest
łatwe. Moment zainicjowania zmiennej udostępniają, do czasu zmiany wartości. Jedy-
Rozdział 15. » Podstawowe pułapki PHP_______________________________289
nymi zmiennymi dostępnymi w ciele funkcji są parametry formalne oraz zmienne zade-
klarowane jako globalne. Dla przykładu, w poniższym kodzie zmienna $serial_no
nie została przekazana do funkcji, ani zadeklarowana jako globalna.
Sname = "Bond/ James Bond";
Srank = "Szpieg";
$serial_no = "007";
function Answer($name)
l
global Srank;
print("Nazwisko: Sname; Stopień: Srank; numer: $serial_no<BR>");
}
Answer(Sname);
Ponieważ zmienna $serial_no nie jest przypisana wewnątrz funkcji, wynik działania
tego fragmentu wygląda następująco:
Nazwisko: Bond, James Bond; Stopień: Szpieg; numer:
Problemy z funkcjami
Wiele problemów związanych z wywołaniami funkcji skutkuje błędem krytycznym, co
oznacza przerwanie wykonywania skryptu.
Błędy matematyczne
Wymienione problemy są związane z obliczeniami i numerycznymi typami danych.
Niestety NAN jest liczbą, przynajmniej w sensie typu numerycznego w PHP. W na-
szym przykładzie PHP wskazuje typ NAN jako double, a nie string. Wartość ta „za-
razi" inne wartości, jeżeli zostanie użyta w operacjach arytmetycznych. Jest to
zamierzone działanie, a nie błąd. W przypadku skomplikowanych obliczeń, które ko-
niecznie muszą być prawidłowe, lepiej całe wyrażenie oznaczyć jako niewiarygodnie,
zamiast dopuszczać do błędnej wartości jednej z części wyrażenia. Każde wyrażenie lo-
giczne, w którym bierze udział wartość NAN, jest fałszywe. NAN nie jest ani mniejsze,
ani większe, ani równe żadnej innej liczbie, włączając w to samą siebie. Jest zawsze
różne (!=) od wszystkich liczb (wartość NAN nie jest wyjątkiem PHP, jest części
standardu IEEE dotyczącego arytmetyki zmiennoprzecinkowej, który został zaimplc-
mentowany w funkcjach C, stanowiących podstawę PHP).
Kod ten pokazuje interakcję pętli i pomyłki w wielkości liter — pisany z małej litery
$ index jest zwiększany; nie ma nic wspólnego ze zmienną $ index używaną w wa-
runku końca pętli. Pętla nigdy się nie skończy.
Podsumowanie
W tabeli 15.1 zamieściliśmy podsumowanie pułapek omówionych w tym rozdziale,
wraz z opisem objawów i powodów wystąpienia. Dodaliśmy również kilka sugestii,
w jaki sposób można poprawić najczęściej spotykane błędy.
Tabela 15.1.
Od objawów do przyczyn
Zawartość pliku wyświetlona Nie została wywołana maszyna Upewnij się, że pobierasz plik przez
w przeglądarce (nowa PHP, być może z powodu serwer WWW, zarówno przez localhost
instalacja) otwarcia pliku poprzez system (http://localhost/[ścieżka]), gdy testujesz
plików, a nie przez żądanie do skrypt na serwerze, jak i przez pełny
serwera URL (http://www. strona, com/ [ścieżka])
Rozdział 15. » Podstawowe pułapki PHP_____________________________293
Tabela 15.1.
Od objawów do przyczyn (ciąg dalszy)
Bloki PHP pojawiają się jako PHP jest niewłaściwie Sprawdź konfigurację serwera WWW
tekst w HTML. Przeglądarka wywoływany. Serwer WWW oraz plik konfiguracji PHP (php.ini)
proponuje zapisanie pliku może być niewłaściwie
(nowa instalacja) skonfigurowany dla przetwarzania
określonych typów plików
(np.: .php) przez PHP. Może
występować problem z położeniem
lub zawartością pliku php.ini
Serwer nie jest dostępny Często z powodu błędnej Spróbuj załadować czysty plik HTML
lub strona nie może być konfiguracji serwera WWW, z rozszerzeniem, które nie jest
wyświetlona (nowa Internetu lub DNS, bardzo rzadko skojarzone z PHP (np.: .html), aby
instalacja) z powodu PHP wykluczyć problem z PHP. Spróbuj
pobrać ten sam plik, korzystając
z adresu IP zamiast nazwy. Jeżeli
zadziała, problem jest związany z DNS
Pusta strona Zwykle źle skonstruowany kod Sprawdź źródło HTML
HTML (również tworzony w przeglądarce. Szczególnie zwróć
przez PHP) uwagę na niezamknięte znaczniki
<TABLE>, <FORM> lub formularze
<SELECT>
Niekompletna strona lub Zwykle źle skonstruowany kod Sprawdź źródło HTML
nieoczekiwana zawartość HTML (również tworzony w przeglądarce. Spróbuj odszukać
przez PHP) źródło problemów za pomocą
komunikatów diagnostycznych
(w PHP lub HTML)
Kod PHP pokazuje się Jeżeli maszyna PHP jest Sprawdź znaczniki otwierające
w przeglądarce zainstalowana i działa prawidłowo, i zamykające blok PHP i upewnij się,
jest to zwykle związane z brakiem że włączane pliki z kodem PHP mają
znacznika rozpoczynającego właściwe znaczniki otwierające
blok PHP i zamykające
Nieodnaleziona strona Zwykle oznacza to, że nazwa lub Dokładnie sprawdź nazwę i ścieżkę
ścieżka do pliku PHP są do pliku
nieprawidłowe
Nieudane otwarcie pliku do Jak wyżej, w niektórych Dokładnie sprawdź nazwę i ścieżkę
włączenia konfiguracjach NT do pliku
Komunikaty o błędach Bardzo różne przyczyny: brakujące Odszukaj wiersz z błędem w pliku
składni średniki, zmienne bez znaku $, PHP i przyczynę błędu w tym wierszu
nieoznaczone apostrofy, lub bezpośrednio go poprzedzających.
niezamknięte apostrofy, klamry Jeżeli błąd wskazywany jest
lub nawiasy; HTML w ostatnim wierszu pliku, szukaj
interpretowany jako PHP wcześniej niezamkniętego apostrofu,
nawiasu lub klamry
294_________________________________________Część l » Podstawy PHP
Tabela 15.1.
Od objawów do przyczyn (ciąg dalszy)
Błąd HTTP numer 403 Nieprawidłowe uprawnienia Sprawdź uprawnienia do samego pliku oraz
do pliku katalogów w ścieżce
Ostrzeżenie przy Z jakiegoś powodu PHP nie Sprawdź, czy plik istnieje, poprawność nazwy
włączaniu pliku może załadować pliku oraz (w systemie Unix) wielkość liter
podanego w wyrażeniu w nazwach. Upewnij się, czy uprawnienia
include pozwalają na odczytanie pliku
Wartość zmiennej nie Zmienna nie została Sprawdź, czy zainicjowałeś zmienną przed
pojawia się zainicjowana, więc jej instrukcją print; sprawdź wielkość liter. Upewnij
w wynikowym ciągu wartość jest wstawiona się, że nie wklejasz do ciągu obiektów lub
w wynikowy ciąg jako wielowymiarowych tablic. Możesz również użyć
ciąg pusty wyrażenia e r r o r - r e p o r t i n g ( 1 5 ) , aby
włączyć ostrzeżenia przy użyciu
niezainicjowanej zmiennej
Zmienna numeryczna ma Często, jeżeli zmienna nie Patrz poprzednia porada
niespodziewanie została zainicjowana
wartość zero
Wywołanie Funkcja ( ) nie została Jeżeli próbujesz wywołać własną funkcję,
niezdefiniowanej funkcji zdefiniowana przed sprawdź, czyjej definicja (lub włączenie pliku
funkcja ( ) wywołaniem z jej definicją) znajduje się przed użyciem. Jeżeli
wywołujesz wbudowaną funkcję, sprawdź, czy
poprawnie wpisałeś jej nazwę. Jeżeli jest
prawidłowa, sprawdź, czy „rodzina" funkcji jest
włączona przy konfigurowaniu PHP
(np. wszystkie funkcje XML będą działały,
albo żadna)
Wywołanie Wywołane zostało wyrażenie Jeżeli chciałeś użyć zmiennych nazw funkcji,
niezdefiniowanej w postaci $ f u n k c j a ( } , dodaj (lub popraw) przypisanie do zmiennej
funkcji ( ) a Sfunkcja nie została $ f u n k c j a . Jeżeli chcesz wywołać funkcję
zainicjowana nazwą f u n k c j a ( ) , usuń znak $
zdefiniowanej funkcji
Nie można powtórnie F u n k c j a ( ) jest Szukaj podwójnej definicji funkcji w pliku PHP
zadeklarować funkcji zdefiniowana dwukrotnie lub dwukrotnego włączania pliku z definicją
funkcja ( ) w czasie wykonywania
skryptu
Nieprawidłowa liczba Funkcja (zwykle Porównaj wywołanie funkcji z definicją
parametrów wbudowana) jest wywołana w podręczniku (http://www.php.net)
z nieprawidłową liczbą
argumentów
Ostrzeżenie o dzieleniu Operator / po prawej stronie Zainicjuj zmienną powodującą błąd. Jeżeli
przez zero ma wartość zero. zdarzy wartość mianownika równa zero, dodaj
Niezainicjowana zmienna sprawdzenie i odpowiednio obsługuj tę sytuację
w mianowniku
Rozdział 15. » Podstawowe pułapki PHP_____________________________295
Tabela 15.1.
Od objawów do przyczyn (ciąg dalszy)
Bazy danych i PHP są ze sobą ściśle połączone, jak bułka z masłem, Trynidad i Tobago,
Flip i Flap.
Czym właściwie jest WWW? To obszerny magazyn informacji dostępnej dla mniejszej
bądź większej publiczności. Znajdują się tutaj nie tylko małe „broszurowe" witryny, ale
również większe i częściej uaktualniane źródła danych.
Unikanie redundancji
Tworzenie stron na bieżąco przy użyciu PHP, szablonów i bazy danych jest bardzo
uzależniające. Jeżeli nauczysz się tego, nigdy nie wrócisz do tworzenia statycznych wi-
tryn HTML, niezależnie od rozmiaru. Możesz tworzyć nieskończenie wiele stron nakła-
dem pracy potrzebnym na oprogramowanie jednej strony. Zmieniając jedną stronę,
zmieniasz wszystkie.
Do niedawna bazy danych były zbyt kosztowne dla większości niekomercyjnych zasto-
sowań. Jednak w chwili obecnej mamy dostęp do wielu świetnych i tanich baz danych.
W odpowiedzi na tę zdrową konkurencję zmieniają się warunki licencji niektórych ko-
mercyjnych produktów.
Morał jest następujący: mimo że bazy danych mają ograniczone możliwości programo-
wania, są jednak optymalne dla niektórych zastosowań, więc należy korzystać z tych
możliwości, gdzie tylko się da.
Szukanie
Mimo że jest możliwe przeszukiwanie wielu plików tekstowych w poszukiwaniu okre-
ślonego ciągu (szczególnie w systemie Unix), nie jest to rzecz, którą programiści
WWW robią codziennie. Już w przypadku kilkuset plików proces staje się powolny
i trudny do zarządzania. Bazy danych są stworzone po to, aby ułatwić tego typu zada-
nia. Za pomocą jednej komendy możesz odszukać informację, od identyfikatora nume-
rycznego do dużego bloku tekstu lub obrazka w formacie JPG.
Rozdział 16. » Wybór bazy danych dla PHP______________________________301
Bezpieczeństwo
Baza danych tworzy dodatkowy system bezpieczeństwa, jeżeli skorzystamy z jej hasła
lub haseł.
Architektura wielowarstwowa
Do tej pory skupialiśmy się na tak zwanej architekturze dwuwarstwowej: PHP pobierał
surowe dane z jakiegoś rodzaju systemu pamięci masowej i zmieniał je na postać
HTML. Jednym z założeń PHP było, że ma on służyć jako „klej" w architekturze trój-
warstwowej oraz wielowarstwowej. Jeżeli masz cokolwiek bardziej skomplikowanego
niż zwykła architektura dwuwarstwowa, naprawdę potrzebujesz bazy danych.
Co więcej, system operacyjny, serwer WWW i języki programowania mogą nie pozo-
stawić żadnego wyboru. Aplikacja napisana w języku Java na dużym komputerze
z systemem Unix nie będzie działała dobrze z Microsoft SQL Server (teoretycznie jest
to możliwe, ale w praktyce źle się to skończy i lepiej poszukać innego rozwiązania). Im
większy system dostaniesz, tym bardziej będziesz ograniczony przez czyjeś wcześniej-
sze decyzje.
Jednak mamy dobrą nowinę. PHP został wyposażony w możliwość obsługi wielu baz
danych i innych serwerów usługowych. Pozwala to na połączenie ciągle rosnącej ar-
chitektury w jeden system informacyjny. Niektóre funkcje w PHP istnieją tylko po to,
abyś mógł łatwo przenieść dane do nowocześniejszej bazy.
Za pomocą PHP możesz obsługiwać trzy główne rodzaje baz danych: plikowe, relacyj-
ne i obiektowo-relacyjne.
Plikowe lub mieszające bazy danych, takie jak Gnu DBM i Berkeley DB (tzw. Sleepy-
cat DB2) są zwykle używane przez inne programy, takie jak serwery pocztowe. Po-
zwalają na najszybsze zapisywanie i odszukiwanie danych (pary użytkownik i hasło,
przesyłki e-mail opatrzone datą).
J
Rozdział 16. » Wybór bazy danych dla PHP____________________________303
Bazy danych tego typu nie tworzą same reprezentacji bardziej skomplikowanych
związków pomiędzy danymi. Program korzystający z bazy danych musi to realizować.
Mimo to wyniki mogą być zadziwiające; wszystko zależy od umiejętności programisty.
Obecnie najczęściej stosowane są relacyjne bazy danych. Istnieją różne opinie na temat
warunków, jakie musi spełniać baza danych, aby mogła być nazwana relacyjną, więc nie
damy się wciągnąć w akademicką dyskusję i nie podamy żadnej definicji. W zamian mo-
żemy stwierdzić, że baza danych, z którą można porozumiewać się w języku SQL, jest
bazą relacyjną. Większość popularnych baz obsługiwanych przez PHP to bazy relacyjne.
Jednak relacyjność jest relatywna. Niektóre bardzo popularne komercyjne bazy danych,
takie jak Filemaker Pro czy Microsoft Access, nie były projektowane jako podstawa
witryny WWW. Mimo że posiadaj ą obsługę ODBC, dzięki czemu PHP może teoretycz-
nie pobierać z nich dane, są głównie nastawione na łatwość użycia, a nie na wydajność.
Dużo większym problemem jest to, że użytkownicy tych produktów odrzucają korzyści
płynące z relacyjnego modelu i wolą powtarzać dane w każdej pozycji, zamiast utwo-
rzyć osobną tabelę obrazującą relację. Dodatkowo bazy takie zwykle nie posiadają bar-
dziej zaawansowanych funkcji, jak wielowątkowość czy blokady. Jednak są ludzie
próbujący używać Accessa i PHP, ale nie tworzą publicznych witryn obsługujących
wielu użytkowników. Znamy również programistów, którzy używają programów Ac-
cess lub Filemaker Pro jako narzędzi programistycznych na laptopach; mogą w ten spo-
sób programować w samolocie. Są to jednak zawsze projekty przenoszone później
z tych „półrelacyjnych" baz danych do dużych systemów.
Istniej ą również obiektowe lub obiektowo-relacyjne bazy, nowe i ciągle rozwijające się
modele dostępu do danych. Obiektowe bazy danych mają na celu łatwiejsze współ-
działanie z obiektowymi językami programowania. Obiektowo-relacyjne bazy danych
to hybryda używana do typów danych (astronomicznych lub genetycznych), których
zwykłe bazy nie obsługują dobrze. PHP nie jest i prawdopodobnie nie będzie w pełni
obiektowy, więc trudno wyobrazić sobie powód wyboru PHP jako języka skryptowego
do obsługi takich systemów baz. Dobre omówienie tych typów baz znajduje się pod ad-
resem: http://www.cai.com/products/jasmine/analyst/idc/14821E.htm/.
ODBC i JDBC w mniejszym lub większym stopniu wzajemnie się wykluczają. Istnieje
wprawdzie produkt o nazwie ODBC-JDBC bridge, pozwalający programom napisanym
w Javie na dostęp do baz danych ODBC, ale jest bardzo powolny. Istnieją inne sterow-
niki, które wykonują to zadanie o wiele szybciej.
304____________________________________Część II » PHP i bazy danych
Istniej ą również bazy danych, z którymi programy klienckie komunikują się za pomocą
własnego API, a nie poprzez ODBC czy JDBC. Działa to o wiele szybciej z powodu
mniejszej liczby warstw pośredniczących. Większość baz dostępnych na warunkach
open source działa właśnie w ten sposób. Niektóre z nich posiadają sterowniki ODBC
lub JDBC. PHP może na przykład komunikować się z bazą My SQL poprzez własne
API, natomiast program w Javie poprzez JDBC. Zanim wybierzesz schemat, upewnij
się, że masz odpowiednie, pewne i wydajne sterowniki.
GUI
Bazy danych są uzależnione od narzędzi interfejsu użytkownika. Istnieje duży wybór
rozwiązań: od prostych programów liniowych do potężnych środowisk programistycz-
nych na podstawie języka Java. Za to, co wybierzesz, zapłacisz nie tylko pieniędzmi, ale
także wydajnością, dlatego szukaj najmniejszego produktu spełniającego twoje potrzeby
(środowiska graficzne spowalniaj ą pracę programów).
Podzapytania
Podzapytanie jest wbudowanym wyrażeniem SELECT, np.:
SELECT * FROM tablel WHERE id IN (SELECT id FROM table2);
Rozdział 16. » Wybór bazy danych dla PHP______________________________305
lub
INSERT INTO table2[(co!2, co!3, co!17)]
SELECT lastname, firstname, state FROM tablel WHERE col5 = NULL;
Istnieją sposoby obejścia braku podzapytań, chociaż nie wszyscy ich potrzebują; mogą
zaoszczędzić nieco czasu, jeżeli konsekwentnie będziesz tworzył duże wyrażenia SE-
LECT, INSERT lub DELETE.
Złożone złączenia
Złączenie jest sposobem na szukanie danych w tablicy, z użyciem wspólnych wartości
w wielu tablicach. Najprostszą formą złączenia jest:
SELECT textfield FROM tablel, table2 WHERE tablel.id=table2.id;
Wielowątkowość i blokowanie
Wielowątkowość i blokowanie są bardzo ważne w wielowarstwowych witrynach, są także
przydatne w przypadku witryn dwuwarstwowych. Zapewniaj ą prawo do zapisu tylko jednej
transakcji w danym czasie, powodując, że dwa odwołania do bazy nie zderzą się ze sobą.
Przykładem, który wyraźnie pokazuje wartość blokowania, jest witryna WWW sprze-
dająca bilety na oblegany koncert (z numerowanymi miejscami). Oczywiście dwóch lu-
dzi nie może kupić biletu na to samo miejsce. Baza danych musi w jakiś sposób
rozpoznawać indywidualne żądania i pozwolić tylko jednemu użytkownikowi (lub wąt-
kowi) na wprowadzanie zmian w określonym momencie.
Jeżeli nie jesteś pewien, że w przypadku twojego projektu tylko jeden użytkownik może
modyfikować dane (na przykład dziennik sieciowy), musisz bardzo uważnie zatwier-
dzać dane w jednowątkowych bazach danych.
Transakcje
Jest to schemat operowania na danych zwiększający ich integralność.
bazy danych. Nieudana jest usuwana, przywracany jest stan bazy sprzed transakcji. Sys-
tem handlu elektronicznego może użyć wycofania w przypadku odrzucenia karty kredy-
towej klienta, aby powrócić do stanu przed dokonaniem zakupów. Mechanizm wycofań
jest również użyteczny w przypadku uszkodzenia danych w czasie awarii serwera.
Procedury i wyzwalacze
Procedury są zapisanymi i skompilowanymi zapytaniami lub podprogramami. Typową
procedurą będzie np. procedura wybierająca adresy e-mail wszystkich klientów, którzy
złożyli zamówienie określonego dnia. Jeżeli ciągle używasz tego samego wyrażenia
SELECT, procedura będzie wygodnym rozwiązaniem.
Wybieramy MySQL
Jak wiele innych zespołów, które kiedykolwiek napisały książkę na temat PHP, uwiel-
biamy MySQL i będziemy używać go we wszystkich kolejnych przykładach w części
II. MySQL jest najszybszą, najtańszą, najprostszą i najbardziej stabilną bazą danych,
która posiada większość potrzebnych funkcji. Wyróżnia się ona tym, że posiada prawie
tak samo dobre implementacje dla systemu Unix i Windows. Pomiędzy PHP i MySQL
istnieje niespotykana synergia (szczególnie na platformie Linux — Apache). Oba ze-
społy programistów postanowiły dodatkowo zacieśnić współpracę i zbliżyć do siebie te
produkty.
Tabela 16.1.
Bazy danych obsługiwane przez PHP
Podsumowanie
Największą zaletą WWW jest możliwość szybkiego i taniego opublikowania dużej ilo-
ści danych. Możliwość ta została rozszerzona dzięki zwiększeniu się liczby niedrogich
i pewnych baz.
Rozdział 16. « Wybór bazy danych dla PHP______________________________309
Ponieważ wielu programistów nigdy nie wybierało bazy danych, opisaliśmy niektóre
podstawowe zagadnienia, które należy wziąć pod uwagę przy podejmowaniu decyzji.
Do tych zagadnień zaliczamy podstawową strukturę bazy danych (plikowa, relacyjna
lub obiektowo-relacyjna), sterownik lub API, łatwość późniejszego rozwijania. Dodat-
kowe własności, takie jak transakcje lub interfejs graficzny, mogą również grać pewną
rolę przy wyborze bazy. Ponieważ PHP obsługuje wiele baz danych, masz świetną oka-
zję odnalezienia potrzebnego zestawu funkcji.
310____________________________________Część II » PHP i bazy danych
Rozdział 17.
Samouczek SQL
W tym rozdziale:
* Standardy SQL
* Podstawowe wyrażenia SQL
* Projektowanie baz danych SQL
** Użycie połączeń SQL
4 Uprawnienia i bezpieczeństwo
Rozdział ten stanowi podstawowe wprowadzenie do baz danych SQL. Omówimy tutaj
standardy, projektowania bazy danych, język manipulacji danymi (DDL) oraz wspólne
dla wszystkich baz SQL procedury zabezpieczania bazy.
Rozdział ten nie jest dokładnym opisem języka SQL ani żadnej bazy danych SQL. Aby
korzystać z bardziej zaawansowanych funkcji, musisz zapoznać się z dokumentacją do
bazy oraz sięgnąć do innych książek. Proponujemy jeden z podręczników języka SQL:
Podstawy SQL. Ćwiczenia praktyczne, Autor: Arkadiusz Jakubowski,
ISBN: 83-7197-427-2
SQL. Wydanie drugie, Autor: Martin Gruber, ISBN: 83-7197-301-2
SQL dla każdego, Autor: Rafę Coburn, ISBN: 83-7197-248-2
SQL. Księga eksperta, Autor: Hans Ladanyi, ISBN: 83-7197-120-6
Standardy SQL
Andrew Taylor, twórca SQL twierdzi, że SQL nie pochodzi od „Structured Query Lan-
guage", ale reszta świata tak uważa. SQL reprezentuje i ściślejszą, i bardziej ogólną me-
todę przechowywania danych niż poprzedni standard plikowych baz danych.
312______________________________________Część II » PHP i bazy danych
SQL jest standardem uznawanym zarówno przez ANSI, jak i ECMA. Możesz zapoznać
się z tym standardem po zapłaceniu należności tym organizacjom:
http://www. ansi. org
http://www. ecma. org
Poza ogólnymi wytycznymi standardu istnieją bardzo istotne różnice pomiędzy produ-
ktami pochodzącymi firmowymi oraz tworzonymi na zasadach open-source. W osta-
tnich kilku latach możemy zaobserwować szybki rozwój tzw. obiektowo-relacyjnych
baz danych oraz produktów SQL ukierunkowanych na obsługę WWW.
Aby wybrać właściwą bazę danych, musisz skupić się na własnych potrzebach. Na
pewno słyszałeś opinie, że wiele zaawansowanych funkcji bazy danych (na przykład
transakcje lub połączenia krzyżowe) jest niezbędnych w każdej instalacji SQL. Nie mu-
sisz w to wierzyć. Lepiej wypisz potrzebne ci funkcje, sortując je według ważności
i sprawdź, który produkt najlepiej spełnia te wymagania.
Na szczęście SQL jest dosyć mocno zestandaryzowany. Występuje w nim kilka wrażeń,
których będziesz stale używał, niezależnie od serwera bazy, z którym będzie współpra-
cował program.
Każdy serwer SQL posiada instrukcje manipulujące danymi. Stanowią one większą
część wszystkich operacji na relacyjnej bazie danych. Tymi instrukcjami są: SELECT,
INSERT, UPDATE oraz DELETE (twoi pracownicy i pomocnicy).
SELECT
SELECT jest podstawową instrukcją używaną do odczytywania danych z bazy SQL. Jej
najczęściej używana składnia jest bardzo prosta:
SELECT polel, pole2, pole3 FROM tabela WHERE warunek;
Rozdział 17. » Samouczek SQL_____________________________________313
Istnieją przypadki, gdy potrzebny jest cały wiersz zamiast poszczególnych pól danych.
Praktyka taka nie jest korzystna z wielu powodów (działa wolniej od pobierania potrzebnych
nam danych i może powodować problemy w przypadku zmiany struktury tabeli), jednak
mimo tego jest często stosowana. Cały wiersz jest zwracany po użyciu gwiazdki:
SELECT * FROM tabela WHERE data = 01-05-2000;
Złączenia
W przypadku wyrażenia SELECT istnieje tylko jeden poważniejszy problem: złączenia. Po-
nieważ są jedną z najbardziej użytecznych funkcji języka SQL, opiszemy je szczegółowo.
Baza danych SQL jest jednak z definicji relacyjna. Aby zrozumieć koncepcję relacyj-
nych baz danych, musisz przypomnieć sobie sytuacje, w których musiałeś wypełnić
wiele formularzy — podczas załatwiania kredytu, podczas pierwszej wizyty u lekarza
lub załatwiania innych urzędowych spraw. Gdy po raz piętnasty trzeba było wpisać na-
zwisko, adres, datę urodzenia czy numer PESEL, pewnie pomyślałeś sobie: „Dlaczego
nie wpisuje się tego tylko raz". To jest właśnie zasada działania relacyjnej bazy danych.
możesz zapisać za pomocą relacyjnej bazy danych każdy fragment informacji tylko
raz i odwoływać się do niego w innych częściach, w sposób pokazywany w tabelach
17.1 do 17.3.
Tabela 17.1.
Osoby
Tabela 17.2.
Obawy
IDobawy Obawa
1 czarny kot
2 piątek 13
3 woda
4 wysokość
5 latanie
Tabela 17.3.
Osoba_Obawa
ID IDosoby IDobawy
1 1 1
2 1 2
3 1 5
4 2 4
5 2 5
Taki zapis jest lepszą i szybszą (dla bazy danych) metodą przechowywana informacji.
Jeżeli chcesz pobrać dane w postaci czytelnej dla człowieka, pojawia się problem: mu-
sisz pobrać i skorelować dane z więcej niż jednej tabeli. Jest to zadanie dla złączeń.
Aby sprawdzić, jakie obawy ma pani Nowak, musisz odszukać jej identyfikator.
SELECT IDosoby FROM Osoby WHERE Nazwisko = 'Katarzyna Nowak';
Znając tylko jedną daną, za pomocą złączeń możesz pobrać z bazy wszystkie dane na
jej temat. Złączenie pozwala na traktowanie kilku tabel jako jednej w czasie poszuki-
wania informacji.
Podzapytania
Zanim zajmiemy się innymi wyrażeniami SQL, powinniśmy wspomnieć o podzapyta-
niach. Jest to wyrażenie:
SELECT numer_tel FROM tabela
WHERE nazwisko = 'SELECT nazwisko FROM tabela 2 WHERE ID = l 1 ;
Podzapytania powstały dla wygody. Mogą być bardzo użyteczne, jeżeli pracujesz z du-
żą ilością danych. Możesz otrzymać te same wyniki, używając dwóch prostszych wyra-
żeń SELECT (jednak będzie to działać wolniej, nawet w PHP 4).
INSERT
Poleceniem pozwalającym na wstawianie nowych danych do bazy jest INSERT. Pod-
stawowa składnia jest następująca:
INSERT INTO tabela (coll, co!2, col3) VALUES (vail, va!2, va!3);
Kolumny i wartości muszą się zgadzać, jeżeli pozamieniasz elementy tablic, będziesz
miał problem. Jeżeli nie chcesz, aby niektóre wiersze miały podane wartości w wybra-
nych polach, musisz użyć wartości NULL lub samoczynnie zwiększającej się wartości.
Przed wykonaniem operacji INSERT musisz się upewnić, że pola te mogą pozostać pu-
ste lub mają ustawione samoczynne zwiększanie się wartości. Jeżeli jest to możliwe,
możesz po prostu opuścić pola, w których chcesz pozostawić domyślne wartości w wy-
rażeniu INSERT.
Odmianą podstawowej instrukcji INSERT jest INSERT INTO ... SELECT. Pozwala ona
na wstawienie do tabeli wyników wyrażenia SELECT.
INSERT INTO klient(birthmonth, bitrhflower, birthstone)
SELECT * FROM birthday_info WHERE birthmonth = Sbirthmonth;
316_______________________________________Część II » PHP i bazy danych
Nie każda baza danych SQL posiada tę funkcję. Powinieneś być ostrożny stosując ją,
ponieważ możesz łatwo wpędzić się w kłopoty. Ogólnie rzecz biorąc, pobieranie da-
nych z tej samej tabeli, do której wstawiasz, nie jest korzystne.
UPDATE
Instrukcja UPDATE jest używana do zmieniania danych zawartych w bazie. Możesz za
jej pomocą zmienić niektóre dane w rekordzie bez potrzeby usuwania go i powtórnego
wstawiania. Składnia jest następująca:
UPDATE tabela SET polel='wartl', pole2='wart2', pole3='wart3'
WHERE warunek;
Wyrażenie warunkowe jest identyczne jak dla instrukcji SELECT, na przykład WHERE
I D = 1 5 l u b WHERE p l e c = ' K ' .
DELETE
DELETE jest używane do usuwania danych z bazy. Składnia jest następująca:
DELETE dana FROM tabela WHERE warunek;
Najistotniejszą częścią wyrażenia jest warunek. Jeżeli nie podasz żadnego warunku, zo-
stanie usunięta cała zawartość tabeli. W większości przypadków operacji nie trzeba bę-
dzie potwierdzać.
* wiele do jednego;
* unikalny identyfikator.
Dla przykładu, dla Amerykanów relację jeden do jeden określa tzw.: Social Security
Number (w innych krajach również istnieją podobne numery). Każdy obywatel USA ma
przydzielony identyfikator, a używanie numeru innego człowieka jest przestępstwem.
Projektanci baz danych bazują na takich identyfikatorach, ponieważ prawie wszystkie
informacje o człowieku mogą się zmienić.
Wiele do jednego i jeden do wielu różnią się jedynie sposobem rozmieszczenia kolumn
w bazie danych. Przykład takich relacji, zaczerpnięty ze świata medycznego, to pacjent
i jego wizyty. Jeżeli projektujesz tabelę reprezentującą wizy ty pacjentów, zawsze będzie
wchodziła w skład relacji jeden do wielu.
Relacja wiele do wielu jest dobrze ilustrowana przez autorów i książki. Nie tylko książ-
ka może mieć wielu autorów, ale każdy z autorów może napisać sam lub w zespole róż-
ne książki. Nie jest to relacja, która może być efektywnie przedstawiona w arkuszu
kalkulacyjnym, dla tego rodzaju danych baza jest dużo lepszym rozwiązaniem.
Najprostszą relacją jest jeden do jeden, ponieważ można zebrać wszystkie pola w jednej
tabeli, która może być błyskawicznie przeszukiwana. Tabela przechowująca dane
o kliencie może zawierać np. następujące pola:
ID Klienta
Nazwa Klienta
Kontakt z administracją
Kontakt z działem technicznym
W przypadku relacji jeden do jeden najtrudniej stwierdzić, czy będzie potrzebna relacja
jeden do wielu.
Gdy masz już określone relacje jeden do jeden, jeden do wielu, wiele do wielu, należy
podzielić pojedynczą tabelę na wiele tabel: przechowujących główne dane i relacje. Ta-
bele 17.4 do 17.6 zawierają przykład relacji wiele do wielu.
318_______________________________________Część
Część
II »II »PHP
PHP ii bazy danych
danych
Tabela 17.4.
Klient
ID_klienta Nazwa
1 Acme Bread
2 Baker Construction
3 Coolee Dam
Tabela 17.5.
Kontakty
IDJtontaktu Typ
1 Porada telefoniczna
2 Interwencja u klienta
3 Skarga pisemna
4 Skarga telefoniczna
5 Propozycja
Tabela 17.6.
Klienci kontakty
1 1 1
2 3 5
3 2 4
4 2 3
5 1 2
Gdy zaprojektujesz strukturę bazy, musisz poznać szczegóły jej tworzenia. Głównymi
wyrażeniami SQL, których będziesz używał, to CREATE, ALTER i DROP.
Do utworzenia nowej bazy danych bądź tabeli używamy wyrażenia CREATE. Stworzenie
bazy danych, oprócz nadania jej nazwy, wymaga jeszcze wykonania kilku czynności.
CREATE DATABASE db_name;
Większość pracy to stworzenie tabel i ich kolumn. Najpierw trzeba podać nazwę tabeli,
a następnie szczegółowo określić typy i nazwy kolumn tabeli w wyrażeniu CREATE.
CREATE TABLE tabela (
->id_col INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
->COll TEXT NULL INDEX,
->C012 DATE NOT NULL
Rozdział 17. » Samouczek SQL_____________________________________319
Nie zawsze można łatwo przenosić typy danych i opcje definicji pomiędzy bazami, ze
względu na różnice między serwerami.
DROP jest używany do usuwania tabeli lub bazy danych wraz z wszystkimi danymi.
DROP TABLE tabela;
DROP DATBASE db_name;
Jeżeli chcesz zmienić strukturę tabeli, musisz zastosować wyrażenie ALTER. W tym wy-
rażeniu podajesz nazwę zmienianej tabeli oraz określasz zmiany. W tym przypadku
również mogą wystąpić różnice pomiędzy różnymi bazami SQL.
ALTER TABLE tabela RENAME AS nowa_tabela;
ALTER TABLE nowa_tabela ADD COLUMN Col3 VARCHAR(50);
ALTER TABLE nowa_tabela DROP COLUMN co!2;
Bezpieczeństwo i uprawnienia
Jak napisaliśmy w rozdziale 31. „Bezpieczeństwo i kryptografia", o bezpieczeństwo
w świecie WWW trzeba zadbać tak samo, jak w rzeczywistym. Każdy policjant może
potwierdzić, że nie da się całkowicie zabezpieczyć domu przed włamaniem. Bardziej
realistycznym celem jest utrudnienie potencjalnego włamania.
Użycie PHP wraz z bazą danych może być porównane do zastosowania dwóch zamków
w drzwiach wejściowych. Wyraźnie zwiększa bezpieczeństwo witryny, ale tylko wtedy,
gdy zastosuje się podstawowe zasady higieny bazy.
320____________________________________Część II » PHP i bazy danych
Ustawianie uprawnień
Podstawową zasadą zabezpieczania baz danych jest przydzielanie użytkownikowi lub
grupie minimalnych uprawnień, wymaganych do wykonywanych zadań. Nie pozwolisz
przecież obcemu na przechadzanie się po domu i czytanie pamiętnika leżącego w sy-
pialni. Ta sama zasada będzie obowiązywała w witrynie. Nieco więcej pracy wymaga
zarządzanie wieloma użytkownikami i odpowiednimi dla nich uprawnieniami, ale warto
się wysilić.
,§?^vz. Użytkownicy bazy danych i ich hasła powinny różnić się od użytkowni-
Z^Lx\ ków i haseł systemowych. Nigdy nie ustawiaj w bazie hasła identycz-
nego z hasłem administratora systemu. Jeżeli zdarzy się, że haker
włamie się przez Internet, dasz mu klucz do systemu.
Umieszczenie zmiennych bazy danych poza plikami PHP jest korzystne również z in-
nego powodu. Jeżeli masz wiele skryptów PHP korzystających z tej samej bazy, mogą
używać tego samego pliku haseł. Jeżeli podejrzewasz, że hasło zostało złamane, lub re-
gularnie zmieniasz hasła, wystarczy zmodyfikować tylko jeden plik i wszystkie skrypty
zostaną uaktualnione.
Istnieje jedna wada takiego rozwiązania. Użytkownik serwera Apache musi mieć uprawnie-
nia do odczytania pliku z hasłami. Ponieważ użytkownik Apache i użytkownik bazy danych
toczęsto te same osoby, plik ten będzie dostępny dla wszystkich. To jednak bezpieczniejsze
rozwiązanie, niż przechowywanie haseł w głównym katalogu drzewa witryny WWW.
Jeżeli rzadko używasz zmiennych bazy danych, na przykład tylko w skryptach konfigu-
racyjnych, możesz trzymać go w katalogu niedostępnym dla serwera Apache i zmieniać
prawa do tego katalogu tylko wtedy, gdy konieczny jest dostęp do pliku z hasłami. Na
przykład: stosunkowo rzadko kasujemy pozycje z listy dyskusyjnej dostępnej na naszej
witrynie. Łatwiej jest przechowywać hasła w katalogu niedostępnym dla Apache i na
chwilę ustawiać uprawnienia w celu usunięcia np. obraźłiwego wpisu.
Jeżeli z jakiegoś powodu zdecydujesz się umieścić nazwę użytkownika, hasło, nazwę
komputera i bazy danych w skrypcie PHP, otrzymasz spodziewany wynik. Jeżeli demon
httpd będzie działał prawidłowo, hasła bazy danych będą tak samo bezpieczne jak każ-
dy plik witryny — nie jest to wysoki poziom bezpieczeństwa. Jeżeli demon zatrzyma
się, istnieje możliwość, że plik PHP (włączając w to wartości zmiennych bazy danych)
zostanie dostarczony użytkownikowi w czytelnej postaci. Możesz ograniczyć ryzyko,
korzystając z innego niż „.htmr rozszerzenia dla plików PHP.
Jeżeli nie uaktywniłeś „trybu cichego" w PHP 3, w przypadku braku podłączenia do ba-
zy zobaczysz w przeglądarce następujący napis:
Warning: MySQL Connection Failed: Access denied for user: 'someuser@localhost'
^(using password: NO) in /home/web/html/mysqltest,php3 on line 2
<HTML>
<BODY>
<FORM METHOD=POST ACTION="<?php print("form_check.php");?>">
<P>Użytkownik: <INPUT TYPE="TEXT" SIZE=20 NAME="try_user"X/P>
<P>Hasło: <INPUT TYPE="PASSWORD" SIZE=10
NAME="try_pass"x/P>
<P>Data: <INPUT TYPE="TEXT" SIZE=10 NAME="try_date"X/P>
<P>Wpis:<BR>
<TEXTAREA COLS=50 ROWS=10 NAME="try_entry"X/TEXTAREAX/P>
<PXINPUT TYPE="SUBMIT"X/P>
</BODY>
</HTML>
form_check.php
<?php
// Sprawdź użytkownika bazy danych
include("/home/Webvars.inc");
mysql_connect($hostname, Sdb_user, Spassword);
// Sprawdź użytkownika z formularza
mysql_select_db("Weblog") ;
$query = ("SELECT password FROM finalcheck WHERE user='$try_user'") ;
Sresult = mysql_query(Squery);
Spasscheck = mysql_fetch_array(Sresult);
if($passcheck[0] == Stry_pass)
{
/* Wstaw nową pozycję. */
$query = ("INSERT INTO log (ID, date, entry) VALUES
(NULL, '$try_date', '$try_entry')");
$result = mysql_query(Squery);
print("Wynik wstawienia nowej pozycji to $result."};
)
else
(
mail("security@localhost", "Ostrzeżenie",
"Ktoś z komputera $REMOTE_ADDR próbuje wejść na główną stronę dziennika.");
>
?>
Skrypt ten niczego nie zapisze w bazie danych, jeżeli nazwa użytkownika i jego hasło
nie zostaną podane. Użytkownik musi też prawidłowo załogować się do serwera.
Ostrzeżenie o próbie włamania do systemu można otrzymać e-mailem.
Podsumowanie
SQL to nie technologia kosmiczna. Cztery podstawowe wyrażenia używane do mani-
pulowania danymi są dostępne we właściwie wszystkich bazach SQL. SELECT pobiera
danych z bazy, INSERT umieszcza dane w bazie, UPDATE zmienia fragmenty istnieją-
cych wpisów, a DELETE usuwa wiersze tabeli.
Rozdział 17. » Samouczek SQL_____________________________________323
Baza danych SQL jest tworzona za pomocą wyrażeń struktury danych. Najważniejszymi
wyrażeniami tego typu są CREATE, ALTER i DROP. CREATE DATABASE tworzy nową
bazę danych, a CREATE TABLE definiuje nową tabelę w bazie. ALTER zmienia struktu-
rę tabeli. DROP usuwa całą tabelę lub bazę danych.
Dobry projekt bazy danych obejmuje również zagadnienia bezpieczeństwa. Użycie od-
powiednich zabezpieczeń w bazie wyraźnie zwiększa poziom bezpieczeństwa całego
serwera WWW. Najlepszą obroną przed włamaniami jest ustawienie regularnego two-
rzenia kopii bazy, dlatego każdy administrator SQL powinien poznać najbardziej efek-
tywne sposoby tworzenia takich kopii.
324______________________________________Część II » PHP i bazy danych l
Rozdział 18.
Funkcje PHP i MySQL
W tym rozdziale:
4 Łączenie z MySQL
* Tworzenie zapytań
** Pobieranie danych
* Pobieranie opisu danych
* Korzystanie z wielokrotnych połączeń
* Kontrola błędów
* Tworzenie baz danych
Łączenie z MySQL
Podstawową funkcją pozwalającą na zainicjowanie połączenia z MySQL jest:
mysql_connect($hostname, $user, $password);
jeżeli zamiast zmiennych chcesz użyć ciągów, wywołanie tej funkcji jest następujące:
mysql_connect('localhost', 'root', 'sesame');
Hasło jest opcjonalne. Lepiej, jeżeli baza danych korzysta z hasła. Jeżeli jednak hasło
nie jest potrzebne, można pominąć ten argument. Można również podać port i gniazdo
w serwerze ($hostname: port: gniazdo), ale jeżeli nie wybrałeś innych niż standar-
dowe ustawień portu i gniazda serwera bazy, nie ma powodu, aby stosować takie wy-
wołanie.
326____________________________________Część II » PHP i bazy danych
Jeżeli wolisz stosować jawne ciągi zamiast zmiennych, wywołaj tę funkcję następująco:
mysql_select_db('phpbook') ;
PHP spróbuje wykonać takie zapytanie. Czasem trzeba podzielić tę (i podobne) instruk-
cje na dwa wiersze i użyć dwóch dodatkowych zmiennych, tak jak w przykładzie:
$query = "INSERT INTO personal_info (ID, Surname, Name, Occupation)
VALUES (NULL, 'Adams', 'Sam', 'Pariot Brever')";
$result = mysql_query($query) ;
Podstawowym powodem jest to, że te dodatkowe zmienne daj ą nam dostęp do bardzo
użytecznych danych. Każde zapytanie MySQL w wyniku zwraca informację, czy się
powiodło, czy nie — jak bankomat, kiedy próbujesz pobrać pieniądze. Jeżeli wszystko
pójdzie dobrze, potrzebujesz tylko potwierdzenia. Jeżeli jednak coś pójdzie źle, zwraca-
na wartość podaje powód, dlaczego operacja się nie udała.
Funkcja mysql_query wymaga ciągu z zapytaniem jako parametru (który nie powi-
nien mieć średnika na końcu) i opcjonalnie identyfikatora połączenia. Jeżeli nie korzy-
stasz z wielokrotnych połączeń, identyfikator połączenia nie jest potrzebny. Funkcja
Rozdział 18. » Funkcje PHP i MySQL__________________________________327
zwraca wartość TRUE (różną od zera), jeżeli zapytanie zostało wykonane poprawnie
(nawet jeśli nie zwraca żadnych wierszy). Jeżeli zapytanie było niepoprawne lub nie zo-
stało wykonane z innych powodów, funkcja zwraca wartość FALSE.
Jeżeli zapytaniem było jedno z wyrażeń INSERT, UPDATE, DELETE, CREATE TABLE
lub DROP TABLE, wykonane poprawnie, możesz użyć funkcji mysql_affected_
rows, aby sprawdzić, ile wierszy zostało zmienionych przez zapytanie. Funkcja ta rów-
nież posiada opcjonalny parametr identyfikatora połączenia, który jest potrzebny jedy-
nie w przypadku używania wielokrotnych połączeń.
Saffected_rows = mysql_affected_rows O;
Jeżeli wykonywane zapytanie było wyrażeniem SELECT, zwracana liczba ma nieco inne
znaczenie: zamiast wartości TRUE lub FALSE zwracana jest wartość całkowita zwana
identyfikatorem wyniku. Jest to unikalny identyfikator każdej instrukcji SELECT, zwy-
kle w postaci kolejnych liczb, rozpoczynających się od l dla pierwszej instrukcji
SELECT w każdym skrypcie. Dla SELECT nie można używać funkcji mysql_af fec-
ted_rows, ponieważ wiersze nie są modyfikowane. W zamian można skorzystać z my-
sql_num_rows ( $ r e s u l t ) , aby sprawdzić, ile wierszy zwróciła instrukcja SELECT.
Pobieranie wyniku
Pomysł pobierania danych przez PHP może wydać się dziwny początkującym użyt-
kownikom. Wydaje się logiczne założenie, że wynikiem zapytania powinny być żądane
dane. Wynikiem zapytania w PHP jest liczba określająca pomyślne lub niepomyślne
wykonanie zapytania albo identyfikator wyniku.
Wykonanie powyższego kodu spowoduje wypisanie danych z bazy; każdy wiersz bę-
dzie zawierał dane skojarzone zjednostkowym identyfikatorem.
Pamiętaj, że mysql_f etch_array może być używana również w ten sam sposób, co
mysql_f etch_row — z identyfikatorami numerycznymi zamiast nazw pól. Używając
tej funkcji, możesz dokonać wyboru. Jeżeli chcesz używać numeru albo nazwy pola,
możesz użyć następujących wywołań:
Soffset_row = mysql_fetch_array(Sresult, MYSQL_NUM);
lub
$associative_row = mysql_fetch_array($result, MYSQL_ASSOC);
Rozdział 18. » Funkcje PHP i MySQL__________________________________329
Można również używać MYSQL_BOTH jako drugiej wartości, ale pamiętaj, że jest to
wartość domyślna (byłby to nadmiar funkcji).
Istnieje specjalna funkcja MySQL używana, wraz z dowolną funkcją pobierającą dane,
do dokładnego określenia wymaganego numeru wiersza. Jest to mysql_data_seek,
która wymaga identyfikatora wyniku i numeru wiersza jako argumentu. Przesuwa ona
wewnętrzny wskaźnik wiersza w wyniku do określonego wiersza. Najczęstszym zasto-
sowaniem tej funkcji jest ponowne przeglądanie wyniku od początku — ustawienie
numeru wiersza na zero (analogicznie w funkcji dla tablic). Unika się w ten sposób ko-
lejnego kosztownego wykonywania zapytania przez serwer bazy do pobrania danych,
które już wcześniej zostały przekazane do PHP.
<?php
echo("<TABLE>\n<TRXTH>Tytuły</THX/TR>\n<TR>") ;
$query = "SELECT title, publisher FROM books";
Sresult = mysql_query(Squery);
while($book_row = mysql_fetch_array($result))
(
echo("<TD>$book_row[0]</TD>\n");
)
echo("</TRx/TABLEXBR>\n") ;
echo("<TABLE>\n<TRXTH>Wydawcy</THx/TR>\n<TR>") ;
mysql_data_seek(Sresult, 0 ) ;
while($book_row = mysql_fetch_array(Sresult))
{
echo("<TD>Sbook_row[l]</TQ>\n") ;
1
echo("</TRX/TABLEXBR>\n") ;
330______________________________________Część II » PHP i bazy danych
Pewnie nie chcesz, aby każdy haker mógł poznać budowę i zawartość twojej bazy. Je-
żeli masz skrypty używające powyższych funkcji — na przykład narzędzi do zdalnej
administracji bazy danych — musisz szczególnie zadbać bezpieczeństwo. Upewnij się,
że skryptów może używać tylko administrator MySQL. Zaleca się stosowanie formula-
rzy z hasłami. Można również ograniczyć możliwość stosowania tych skryptów do
określonych adresów IP.
Większość nazw funkcji, zwracających opis danych, wyjaśnia sama siebie. Istnieje kilka
rzeczy o których należy pamiętać podczas ich używania. Po pierwsze większość tych
funkcji działa jedynie we właściwej kombinacji z innymi funkcjami — nie próbuj uży-
wać mysql_af fected_rows po zapytaniu SELECT. Po drugie, zwracaj uwagę na za-
bezpieczenie funkcji, które zwracają dane na temat struktury bazy. Poznanie nazwy
i struktury tabel będzie hakerowi bardzo przydatne. Wiele z tych funkcji stanowi połą-
czenie prostszych funkcji. Jeżeli potrzebujesz kilku fragmentów informacji z wyniku
lub z bazy danych, lepiej używać mysql__fetch_field zamiast kolejnych wywołań
mysql_field.
Kontrola błędów
Ta część powinna nosić tytuł „Die", ponieważ podstawowa funkcja kontroli błędów na-
zywa się die ( ) . Ponieważ nazwa funkcji (pesymistyczna) nie pasuje do książki czyta-
nej przez początkujących programistów, zastosowaliśmy bardziej prozaiczny tytuł.
Die nie jest funkcją MySQL — podręcznik PHP wymienia ją w części „Różne funk-
cje". Funkcja ta zatrzymuje wykonywanie skryptu (lub jego części), zwracając odpo-
wiedni napis.
mysql_query("SELECT * FROM mutual_funds WHERE code = 'Ssearchstring'")
or die ("Proszę poprawić zapytanie i spróbować jeszcze raz.");
Zwróć uwagę na składnię: „or" (mógłbyś użyć również | |, ale „or DIE"' wygląda in-
trygująco...) i tylko jeden średnik.
Aby utworzyć bazę danych za pomocą PHP, użytkownik skryptu powinien posiadać
pełne uprawnienia CREATE /DROP w MySQL. Oznacza to, że każdy, kto użyje takiego
skryptu, może bardzo łatwo usunąć wszystkie twoje bazy wraz z zawartością (co oczy-
wiście jest niekorzystne).
Jeżeli rozważasz tworzenie baz danych za pomocą PHP, przynajmniej nie zapamiętuj
nazw użytkowników bazy danych i ich haseł w pliku tekstowym. Utwórz formularz do
logowania się do bazy i przekaż te zmienne do wszystkich skryptów. Przechowywanie
zmiennych w zewnętrznym pliku poza drzewem witryny nie jest wystarczającym za-
bezpieczeniem.
Funkcje MySQL
Tabela 18.1 zawiera podsumowanie wszystkich funkcji MySQL. Argumenty w nawia-
sach kwadratowych są opcjonalne.
334______________________________________Część II » PHP i bazy danych
Tabela 18.1.
Funkcje PHP-MySQL
Tabela 18.1.
Funkcje PHP-MySQL (ciąg dalszy)
Podsumowanie
Funkcje PHP do obsługi MySQL są bardzo łatwe w użyciu, mimo że czasami ich nazwy
są mylące. W celu interakcji z bazą danych musimy otworzyć połączenie z serwerem
bazy, wybrać bazę i wykonać zapytanie, które zwróci identyfikator wyniku. Wynik ten
jest rodzajem potwierdzenia, które określa, czy operacja się udała, czy nie.
Jeżeli po wykonaniu zapytania SELECT zostaną zwrócone dane, należy użyć jednej
z funkcji pobierającej wynik zapytania. Dane pobrane z bazy danych MySQL przecho-
336______________________________________Część II » PHP i bazy danych
wywane sąw buforze, z którego mogą być pobrane jedną z funkcji. Jeżeli chcesz jesz-
cze raz odczytać wynik, możesz użyć funkcji mysql_data_seek(), aby przestawić
wskaźnik wiersza do pozycji 0.
PHP posiada również wiele funkcji, zwracających informacje na temat samej bazy da-
nych lub ostatniej operacji. Dwie najczęściej używane funkcje tego typu to my-
sql_num__rows ( ) , która zwraca liczbę wierszy wyniku, oraz m y s q l _ i n s e r t _ i d ( ) ,
zwracająca identyfikator ostatniej instrukcji INSERT.
Większość funkcji realizowanych przez PHP stworzona jest po to, aby pomóc progra-
miście przenieść dane z bazy na stronę WWW. Dane te mogą być oglądane, dodawane
oraz usuwane w wyniku reakcji witryny na akcje podejmowane przez użytkownika.
W tym rozdziale ograniczymy się pokazania różnych sposobów użycia PHP do ogląda-
nia zawartości bazy danych bez jej zmieniania. Będziemy używać tylko instrukcji SQL
SELECT, a pobrane dane będziemy wyświetlać w tabelach HTML. Użyjemy jednego
przykładu do pokazania różnych technik, pisząc przy okazji kilka przydatnych funkcji,
których można użyć we własnych programach. Na koniec zajmiemy się tworzeniem
przykładowych danych przy użyciu instrukcji INSERT.
W tym rozdziale używamy tylko bazy danych MySQL i jej funkcji, ale te
techniki wyświetlania można bezpośrednio przenieść do prawie każdej
bazy danych SQL obsługiwanej przez PHP.
Najprostszym przypadkiem jest taki, w którym struktura tabeli bazy danych lub zapyta-
nia odpowiada strukturze tabeli HTML, którą chcemy wyświetlić. Jest to sytuacja, gdy
obiekt bazy danych posiada m kolumn i n wierszy — chcemy wyświetlić prostokątną
siatkę m na n wypełnić wszystkie komórki.
Teraz na podstawie powyższych punktów należy napisać zgrabną funkcję, której będzie
można używać w wielu przypadkach. Nie dodamy pierwszego i ostatniego punktu: two-
rzenia i zamykania połączenia z bazą danych. Możesz użyć tej funkcji więcej niż jeden
raz na stronie i w tej sytuacji nie ma sensu za każdym razem otwierać i zamykać połą-
czenia. Założymy, że mamy otwarte wcześniej połączenie i wraz z nazwą tabeli przeka-
żemy je do funkcji jako argument.
Na wydruku 19.1 pokazujemy taką funkcję wbudowaną w kompletną stronę PHP, która
używa tej funkcji do wyświetlenia zawartości kilku tabel.
print("<TABLE BORDER=l>\n");
while ($row = mysql_fetch_row($result_id))
(
print("<TR ALIGN=LEFT VALIGN=TOP>");
for ($column_num = 0;
$column_num < $column_count;
$column_num++)
print("<TD>$row[$column_num]</TD>\n");
print("</TR>\n");
)
print("</TABLE>\n");
)
?>
<HTML>
<HEAD>
<TITLE>Miasta i kraje</TITLE>
</HEAD>
<BODY>
<TABLEXTRXTD>
<?php display_db_table("country", $global_dbh); ?>
</TD><TD>
<?php display_db_table("city", Sglobal_dbh); ?>
</TDX/TR></TABLE></BODYX/HTML>
340____________________________________Część II » PHP i bazy danych
Przykładowe tabele
Aby zobaczyć wynik działania skryptu zamieszczonego na wydruku 19.1, popatrz na
rysunek 19.1, który pokazuje zawartość przykładowych tabel „country" oraz „city".
Możesz uważać te tabele za wstępny projekt bazy danych, będącej atlasem dostępnym
w Internecie. W tym projekcie zastosowaliśmy konwencję, według której tabela posiada
pole o nazwie ID, które stanowi jej klucz główny, i kolejne wartości numeryczne przy-
pisywane automatycznie podczas wstawiania nowego wiersza. Mimo że nie wynika to
Rozdział 19. » Wyświetlanie zapytań w tabelach 341
Rysunek 19.1.
Wyświetlanie
tabel bazy danych
jasno z opisu, tabele te posiadają wbudowaną jedną relację — pole „countrylD" w ta-
beli „city" odpowiada polu ID z tabeli „country". Reprezentuje to przynależność miasta
do kraju (jeżeli będziesz projektował prawdziwą bazę danych dla atlasu, będziesz mu-
siał podzielić tabelę „country" na tabele „country" i „continent", połączone ze sobą
analogiczną relacją).
Ulepszanie wyświetlania
Nasza pierwsza wersja tej funkcji ma ograniczenia: działa tylko z jedną tabelą, nie za-
pewnia kontroli błędów i wyświetla dane w bardzo prosty sposób. Zajmiemy się kolejno
tymi problemami, naprawiając je w następnej wersji (jeżeli chcesz od razu obejrzeć
ulepszoną wersję funkcji, odszukaj wydruk 19.2).
Kontrola błędów
Pierwsza wersja naszej ftmkcji zakładała, że odpowiednio napisaliśmy zapytania, baza
danych pracuje prawidłowo —jeżeli coś pójdzie nie tak, otrzymasz błędy losowe. Czę-
ściowo można to rozwiązać poprzez dodanie wywołania die ( ) do zapytań bazy danych
— gdy wystąpi błąd, wyświetlony zostanie odpowiedni komunikat.
Kosmetyka
Nasza tabela wygląda ciągle tak samo. Najprościej jest napisać funkcję, która pozwoli
na przekazanej jako argumentu ciągu, który jest wklejany do definicji tabeli HTML.
Jest to bardzo nieeleganckie rozwiązanie, którego na pewno nie polecą zwolennicy ar-
kuszy stylów, ale pozwala na bezpośrednią kontrolę nad niektórymi elementami, odpo-
wiedzialnymi na wygląd tabeli, bez potrzeby pisania całkowicie nowej funkcji.
$column_num++)
(
$field_name =
mysql_field_name($result_id, $column_num);
print("<TH>$field_name</TH>");
)
print("</TR>\n");
(
// Wyświetlenie tabeli
while ($row = mysql_fetch_row($result_id))
{
print("<TR ALIGN=LEFT VALIGN=TOP>");
for ($column_num = 0;
$cołumn_num < $column_count;
Scolumn_num++)
(
print("<TD>Srow[$column_num]</TD>\n");
)
print("</TR>\n");
)
print("</TABLE>\n"); )
<HTMLXHEADXTITLE>Kraje i miasta</TITLEX/HEAD>
<BODY>
<TABLEXTRXTD>
<?php display_db_tabłe("country", $global_dbh,
TRUE, "BORDER=2"); ?>
</TDXTD>
<?php display_db_table("city", $global_dbh,
TRUE, "BORDER=2"); ?>
</TDX/TR></TABLEX/BODYX/HTML>
Wynik użycia tego kodu do tej samej, co poprzednio, zawartości bazy danych pokazany
jest na rysunku 19.2. Jedyną widoczną różnicą są nagłówki kolumn. Jednak dzięki po-
działowi funkcji na części otrzymujemy kolejną funkcję; możemy dzięki niej wyświe-
tlić w ten sam sposób dowolne zapytanie, które łączy dane z wielu tabel.
Złożone odwzorowania
Do tej pory zajmowaliśmy się tylko bardzo prostymi przypadkami zależności pomię-
dzy tabelą HTML i wynikiem zapytania — każdy wiersz wyniku odpowiadał jednemu
wierszowi w tabeli. Kod obsługujący ten przypadek składa się z dwóch zagłębionych
pętli. Czasami struktura tabeli HTML ma skomplikowaną relację z relacyjną strukturą
tabel bazy.
344 Część II » PHP i bazy danych
Rysunek 19.2.
Wyświetlanie wyniku
zapytania
function display_cities($db_connection)
{
/* Wyświetlenie tabeli miast i krajów */
$country_query = "select id, continent, countryname
from country
order by continent, countryname";
$country_result =
mysql_query($country_query, $db_connection);
/* pętla po krajach */
while ($country_row = mysql_fetch_row($country_result)}
f
/* odczytanie danych o kraju */
Scountry_id = $country_row[0];
$continent = $country_row[l];
$country_name = Scountry_row[2];
print("<TD>Scontinent</TD>");
print("<TD>Scountry_name</TD>");
<HTML>
<HEAD>
<TITLE>Miasta w krajach</TITLE>
</HEAD>
<BODY>
<?php
display_cities(Sglobal_dbh);
?>
</BODY>
</HTML>
Strategia jest bardzo prosta: wewnętrzna pętla używa jednego zapytania do przeglądania
wszystkich krajów, zapamiętując nazwę kraju i jego ID. Następnie pole ID jest używane
do odszukania wszystkich miast należących do kraju. Zauważ, w jaki sposób wbudo-
wujemy zmienną $countryid w wewnętrzne zapytanie — ciąg zapytania będzie nieco
inny za każdym przebiegiem pętli po krajach.
<BODY>
<?php d i s p l a y _ c i t i e s ( S g l o b a l ^ d b h ) ;
?>
</BODYX/HTML>
Kod ten jest dosyć sprytny — mimo że odczytuje kolejne wiersze, a wszystkie druko-
wane elementy pochodzą z bieżącego wiersza, drukuje nazwę kraju, jeżeli się zmieniła
(kontynenty powtarzają się). Zmiana kraju jest wykrywana poprzez monitorowanie
wartości pola ID, pochodzącego z tabeli „country". Zmiana kraju jest również sygnałem
do wydrukowania kodu HTML, potrzebnego do zakończenia poprzedniego wiersza
i rozpoczęcia nowego. Kod musi ponadto dodać znaczniki HTML potrzebne do rozpo-
częcia pierwszego i zakończenia ostatniego wiersza tabeli.
(
Scountry__query =
"insert into country (continent, countryname)
values ('$continent', '$countryname')";
$result_id = mysql_query($country_query)
OR die($country_query . mysql_error{});
if ($result_id)
{
ScountrylD = mysql_insert_id($dbh);
for (Scity = current (Scity__array) ;
$city;
$city = next($city_array))
{
$city_query =
"insert into city {countrylD, cityname)
values ($countryID, '$city')";
mysql_query($city_query, $dbh)
OR die($city_query. mysql_error());
)
}
)
function populate_cities_db($dbh)
(
/* Jeżeli tabele istnieją, usuń je - pozwala na wykonanie funkcji
więcej niż raz */
mysql_query("drop table city", $dbh);
Rozdział 19. » Wyświetlanie zapytań w tabelach___________________________349
/* tworzenie tabel */
mysql_query("create table country
(ID int not null auto_increment primary key,
continent varchar(50),
countryname varchar(50))",
$dbh)
OR die(mysql_error());
mysql_query("create table city
(ID int not null auto_increment primary key,
countrylD int not null,
cityname varchar(50})",
Sdbh)
OR die(mysql_error());
Za pomocą tego skryptu możesz utworzyć przykładową bazę danych w twoim kompute-
rze, zakładając oczywiście, że masz skonfigurowane PHP i MySQL. Plik phpbook-
vars.inc, zawierający nazwę użytkownika, hasło i nazwę bazy danych, znajduje się w
odpowiednim katalogu.
Kod ten wysyła zapytania (z wbudowanymi zmiennymi), ale tym razem zapytania za-
wierają instrukcję INSERT, które tworzą wiersze tabeli. Zazwyczaj wstawiane dane są
po prostu ciągami przekazanymi do funkcji, ale do przekazania dowolnej liczby miast
w krajach zastosowaliśmy tablicę.
Jedyną sztuczką użytą przy tworzeniu tych tabel jest tworzenie struktury relacyjnej.
Chcemy, aby każdy wiersz miasta miał odpowiednią wartość pola country ID, która
powinna być taka sama jak wartość ID z odpowiedniego wiersza tabeli krajów. Jednak
identyfikatory kraju są nadawane automatycznie przez MySQL i nie mamy nad nimi
kontroli. W jaki sposób poznać właściwą wartość dla countrylD? Z pomocą przycho-
dzi bardzo użyteczna funkcja mysql_insert_id ( ) , który zwraca identyfikator nadany
w ostatnim zapytaniu INSERT, wykonanym na określonym połączeniu do bazy. Wsta-
wiamy nowy kraj, odczytujemy identyfikator przydzielony do nowego wiersza i uży-
wamy go podczas wykonywania zapytania wstawiającego miasto.
350_______________________________________Część II » PHP i bazy danych
Podsumowanie
Interakcja z bazą danych jest jednym z zadań, w którym PHP naprawdę ma coś do po-
wiedzenia. Częstym zadaniem realizowanym w kodzie współdziałającym z bazą jest
atrakcyjne wyświetlanie zawartości bazy danych. Jedną z możliwości wyświetlania jest
odwzorowanie zawartości bazy danych w odpowiednich elementach tabeli HTML.
Jeżeli odwzorowanie jest dostatecznie proste, możesz stworzyć uniwersalną funkcję po-
bierającą określone nazwy tabel lub całe wyrażenia SELECT, wyświetlające wynik
w tabeli. Jeżeli potrzebujesz bardziej skomplikowanej kombinacji danych z tabel, praw-
dopodobnie będziesz musiał stworzyć wyspecjalizowaną funkcję. Powinieneś zastoso-
wać takie zapytania SQL, które zwracaj ą potrzebne dane w odpowiedniej kolejności, co
pozwoli na selektywne drukowanie niepowtarzających się danych.
Pokazaliśmy też przykład wypełniania zestawu tabel przy użyciu wyrażeń INSERT. Po-
za nim wszystkie pokazane w tym rozdziale techniki były technikami „tylko do odczy-
tu" i nie zmieniały zawartości bazy danych. W kolejnym przykładzie zobaczymy, w jaki
sposób uzyskać ściślejsze związki z bazą danych dzięki łączeniu zapytań SQL i formu-
larzy HTML.
Rozdział 20.
Tworzenie formularzy
z zapytań
W tym rozdziale:
* Statyczne formularze HTML
* Samoprzetwarzanie
* Obsługa formularzy
* Formularze zależne od zmiennych
* Formularze zależne od zapytań
Obsługa formularzy jest jedną z najlepsz ych funkcji realizowanych przez PHP. Połą-
czenie HTML do tworzenia formularzy wprowadzania danych, PHP do obsługi danych
i bazy do ich przechowywania są podstawą różnych bardzo użytecznych zastosowań
WWW.
Formularze HTML
Wiesz już, co jest potrzebne do stworzenia dobrych formularzy obsługiwanych przez
PHP i bazę danych. Poniżej przedstawiamy kilka specyficznych punktów PHP, o których
należy pamiętać.
* Zawsze używaj NAME do wszystkich elementów wprowadzania danych (INPUT,
SELECT, TEXTAREA itd.). Nawy tych atrybutów zostaną przekształcone na nazwy
zmiennych w PHP. Jeżeli twój edytor graficzny nie pozwala na ustawienie tych
nazw, musisz pamiętać o ręcznym wprowadzeniu atrybutów NAME.
* Atrybut NAME na formularzu nie musi być zgodny z nazwą pola w bazie da-
nych, ale powinien.
352____________________________________Część II » PHP i bazy danych
Samoprzetwarzanie
Formularz HTML jest po prostu ładną otoczką graficzną, nałożoną na prostą metodę
przesyłania danych do serwera. W chwili obecnej nie potrafią one nic poza przesłaniem
danych do skryptu obsługi formularza, który musi być napisany w jakimś języku pro-
gramowania.
Jednak za pomocą PHP można połączyć w jednym skrypcie formularz i program go ob-
sługujący. Właściwie to w PHP obsługa samoprzetwarzania jest tak prosta i użyteczna,
że prawdopodobnie nie będziesz stosować oddzielnego formularza i skryptu obsługi.
Rysunek 20.1.
Formularz
z samoprzetwarzaniem
<HTML>
<BODY>
<P>Dziękujemy za użycie naszego systemu obsługi klienta.
Wybierz jedną z poniższych opcji:</P>
<FORM METHOD="POST" ACTION="formhandler.php">
<INPUT TYPE=RADIO NAME="userlevel" VALUE=l>Używałem już tego systemu lub wolę
mało dokumentacj i.<BR>
<INPUT TYPE=RADIO NAME="userlevel" VALUE=2>Nie używałem jeszcze tego systemu
lub wolę więcej dokumentacj i.<BR>
<INPUT TYPE=SUBMIT>
</FORM>
</BODY>
</HTML>
Używając tego formularza chciałbyś przejść do formularza dla eksperta lub dla począt-
kującego, w zależności od wybranej opcji. Możesz napisać skrypt CGI umożliwiający ci
obsługę obu opcji, ale wydaje się zbytkiem uruchamianie nowego procesu do wybrania
jednej z dwóch stron HTML. W PHP 4 samoprzetwarzanie formularza jest realizowane
prawie tak szybko jak zwykłe wywołanie HTML. Jeżeli mechanizm buforowania Zend
jest aktywny, samoprzetwarzanie będzie prawdopodobnie szybsze niż wysłanie danych
do oddzielnego skryptu obsługi formularza.
Obsługa formularzy
Jeżeli formularz i skrypt do jego obsługi są dwiema osobnymi stronami, ich obsługa za
pomocą PHP jest bardzo prosta. Rysunek 20.2 pokazuje prosty formularz do wstawiania
danych do bazy.
354______________________________________Część II » PHP i bazy danych
Rysunek 20.2.
Formularz
wprowadzania
adresu e-mail
<HTML>
<HEAD>
<TITLE>Wprowadzanie adresu e-mail</TITLE>
</HEAD>
<BODY>
<P>Proszę podać w poniższym formularzu :
płeć, imię i nazwisko oraz adres e-mail.</P>
<FORM METHOD="POST" ACTION="emailaddress.php">
Płeć:<BR>
<INPUT TYPE=RADIO NAME="Title" VALUE=l>Pan<BR>
<INPUT TYPE=RADIO NAME="Title" VALUE=2>Pani<BR>
Imię: <INPUT TYPE=TEXT NAME="GivenNeme" SI2E=25XBR>
Nazwisko: <INPUT TYPE=TEXT NAME="FamilyName" SIZE=25XBR>
Adres ę-mail: <INPUT TYPE=TEXT NAME="Email" SIZE=25><BR>
<INPUT TYPE=SUBMIT>
</FORM>
</BODY>
</HTML>
<HTML>
<HEAD>
<TITLE>Obsługa formularza wprowadzania adresu e-mail</TITLE>
</HEAD>
<BODY>
<?php
/* Otwarcie połączenia do bazy danych */
mysql_connect("localhost", "phpuser", "sesame")
or die("Nieudana próba połączenia z bazą danych");
mysql_select_db("phpbook");
/* Wstawienie wartości */
$query = "INSERT INTO addressbook
(ID, Title, GivenName, FamilyName, Email) VALUES
('NULL', '$Title', '$GivenName', 'SFamilyName', '$Email')'
$result - mysql_query($query);
if($result > 0) (
print("Dane zostały zapisane.");
) else {
print("Nieudany zapis danych.");
)
?>
</BODY>
</HTML>
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________355
Najbardziej użyteczną techniką jest ukryta zmienna stanu. Pozwala ona na zapamięta-
nie, ile razy zostały wysłane zmienne formularza. Dzięki temu wiemy, na którym etapie
procesu jesteśmy. Możesz użyć takiej zmiennej (o dowolnej nazwie, powinien być to
raczej ciąg, a nie liczba) do wyboru części formularza lub funkcji obsługi formularza,
która powinna zostać wywołana.
<HTML>
<HEAD>
<TITLE>Wprowadzanie adresu e-mail</TITLE>
</HEAD>
<BODY>
<?php
if ( HsSet (Sstage) )
f
?>
<P>Proszę podać w poniższym formularzu :
pięć, imię i nazwisko oraz adres e-mail.</P>
<FORM METHOD="POST" ACTION="<?php print("$PHP_SELF"); ?>">
PłeĆ:<BR>
•CINPUT TYPE=RADIO NAME="Title" VALUE=l>Pan<BR>
<INPUT TYP =RADIO NAME="Title" VALUE=2>Pani<BR>
Imię: <INPUT TYPE=TEXT NAME="GivenName" SIZE=25XBR>
Nazwisko: <INPUT TYPE=TEXT NAME="FamilyName" SIZE=25XBR>
Adres e-mail: <INPUT TYPE=TEXT NAME="Email" SIZE=25XBR>
<INPUT TYPE=HIDDEN NAME="stage" VALUE=1>
<INPUT TYPE=SUBMIT>
</FORM>
<?php
}
else
f
/* Otwarcie połączenia do bazy danych */
mysql_connect("localhost", "phpuser", "sesame")
or die("Nieudana próba połączenia z bazą danych");
mysql_select_db("phpbook") ;
/* Wstawienie wartości */
$query = "INSERT INTO addressbook
(ID, Title, GivenName, FamilyName, Email) VALUES
('NULL1, 'STitle', 'SGivenName', 'SFamilyName', '5Email')";
$result = mysql_query($query);
if($result > 0)
print("Dane zostały zapisane.");
else
print("Nieudany zapis danych.");
)
?>
</BODY>
</HTML>
TEXT i TEXTAREA
TEXT i TEXTAREA są najprostszymi elementami, ponieważ korzystają z jednoznacznej
relacji jeden do jednego, pomiędzy identyfikatorem i zawartością. Inaczej rzecz ujmu-
jąc, istnieje tylko jedna możliwa wartość dla jednej nazwy (pamiętaj, że w formularzach
HTML liczby całkowite i rzeczywiste muszą używać elementów TEXT i TEXTAREA).
Możesz po prostu pobrać zawartość pola z bazy danych i wyświetlić je w formularzu,
odwołując się do odpowiedniego pola tablicy. Przykład takiego formularza pokazany
jest na rysunku 20.3.
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________357
Rysunek 20.3.
Wyświetlanie tekstu
do edycji
<HTML>
<HEAD>
<TITLE>Edycja zapisu w dzienniku</TITLE>
</HEAD>
<BODY>
<?php
if (!IsSet($stage))
(
?>
<FORM METHOD="POST" ACTION="<?php print("SPHP_SELF"); ?>">
Data wpisu do edycji (RRRR-MM-DD):
< I N P U T T Y P E = T E X T NAME="LogDate" SIZE=10XBR>
<INPUT TYPE=HIDDEN NAME="stage" VALDE=1>
<INPUT TYPE=SOBMIT>
</FORM>
<?php
}
elseif($stage == 1)
(
/* Otwarcie połączenia z bazą danych */
mysql_connect("localhost", "root", "root") or
die("Nieudana próba połączenia z bazą danych");
mysql_select_db("phpbook");
/* Pobranie wartości */
Squery = "SELECT ID, LogText FROM weblog
WHERE LogDate = 'SLogDate'";
$result = mysql_query(Squery);
$LogRow - mysql_fetch_array{Sresult);
SLogID = $LogRow[0];
SLogText = stripslashes($LogRow[1]);
?>
<FORM METHOD="POST" ACTION="<?php print("$PHP_SELF");?>">
Data wpisu:
<INPUT TYPE=TEXT NAME="LogDate" SIZE=10 VALUE="<?php print($LogDate); ?>"><BR>
Tekst:
<TEXTAREA NAME="LogText" COLS=75 ROWS=10X?php print("SLogText");
?></TEXTAREAXBR>
<INPUT TYPE=HIDDEN NAME="LogID" VALUE="<?php echo $LogID; ?>">
<INPUT TYPE=HIDDEN NAME="stage" VALUE=2>
<INPUT TYPE=SUBMIT>
</FORM>
<?php
)
elseif(Sstage == 2);
{
358 _________________________________Część II » PHP i bazy danych
CHECKBOX
Elementy typu CHECKBOX mają dwie możliwe wartości: włączony (zaznaczony) lub bez
wartości (niezaznaczony). Jednak elementy te są często stosowane grupami w celu od-
wzorowania bardziej złożonych wartości. Mimo że ich wartości rzadko są odczytywane
z bazy danych (pola wyboru są często używane do wybierania z małego zakresu warto-
ści, zwykle niezapisywanych w bazie), może być to zrealizowane w sposób pokazany
na rysunku 20.4.
Rysunek 20.4.
Pole wyboru
odczytywane
z bazy danych
<HTML>
<HEAD>
<TITLE>Formularz z polem wyboru</TITLE>
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________359
</HEAD>
<BODY>
<?php
if ( UsSet (Sstage) )
{
?>
<FORM METHOD=POST ACTION="<?php print("$PHP_SELF"); ?>">
<FONT SIZE=+2>Proszę przesyłać mi na mój adres e-mail dużo
^biuletynów!</FONT><BR>
<INPUT TYPE=CHECKBOX CHECKED NAME="OptOut" VALUE="On">
<FONT SIZE=-2>zaznaczajac pole, zgadzasz się z powyższym</FONTXBR>
<INPUT TYPE=HIDDEN NAME="stage" VALUE=1>
<INPUT TYPE=SUBMIT>
</FORM>
<?php
l
elseif(Sstage == l SS !IsSet($0pt0ut))
{
/* otwarcie połączenia z baza danych */
mysql_connect("localhost", "root", "root") or
die("Nieudana próba połączenia z baza danych");
mysql_select_db("phpbook");
/* Zmiana wartości, w tym przypadku z On na NULL */
Squery = "UPDATE checkbox SET BoxValue = NULL WHERE BoxName = 'OptOut'";
$result = mysql_query(Squery);
print("Zostałeś usunięty z naszej listy dystrybucyjnej.");
}
?>
</BODY>
</HTML>
RADIO
Element formularza RADIO (przycisk opcji) pozwala na stworzenie relacji jeden do
wielu, pomiędzy identyfikatorem i wartościami. Inaczej mówiąc, posiadają one wiele
możliwych wartości, ale tylko jeden może zostać wyświetlony lub zaznaczony. Zakres
działania przycisków opcji jest zbliżony do pól wyboru. Najczęściej używa się ich
w małych grupach opcji, zwykle poniżej 10 elementów, do opisania których wystarczy
wyraz lub dwa.
SELECT
Element SELECT jest prawdopodobnie najbardziej interesujący. Może przechowywać
największą liczbę opcji i pozwala również na wybór kilku z nich, które są przekazywa-
ne do bazy danych za pomocą tablic.
Rysunek 20.5.
Wypełnione pole listy
z możliwością
wielokrotnego wyboru
<HTML>
<HEAD>
<TITLE>Prezydenckie bokobrody</TITLE>
</HEAD>
<BODY>
<P>Którzy prezydenci mieli największe bokobrody? Możesz wybrać kilka
nazwisk z listy, przytrzymując klawisz "Control", "Alt", "Apple" lub "Command"
podczas klikania myszą.</P>
<FORM METHOD=POST ACTION="sideburns.php">
<SELECT NAME="PresID[] " SIZE=5 MULTIPLO
<?php
/* Otwarcie połączenia z baza danych */
mysql_connect("localhost", "root", "root") or
~ die ("<OPTION>Błąd bazy danych ! </OPTIONx/SELECT>") ;
mysql_select_db("phpbook");
sideburns.php
<HTML>
<BODY>
<?php
while(list($key, $val) = each($PresID))
{
$queryl = "INSERT INTO votes (ID, PresID, email)
VALUES (NULL, 'Sval', 'Semail')";
Rozdział 20. » Tworzenie formularzy z zapytań_____________________________361
$resultl = mysql_query($queryl);
)
print("Dziękuję za głosowanie! Wyślemy e-mail do zwycięzców naszego konkursu
w dzień wyborów prezydenckich.");
?>
</BODY>
</HTML>
?>
<FORM METHOD=POST ACTION="<?php print("SPHP_SELF"); ?>">
<SELECT SIZE=1 NAME="authorID">
/* Pętla wypełniająca listę autorów */
<?php
while($authorname = mysql_fetch_array(Sresult))
(
print("<OPTION VALUE=\"$authorname[0]\">$authorname[1],
$authorname[2]\n");
}
?>
</SELECT>
<INPUT TYPE=HIDDEN NAME="stage" VALUE=1>
<INPUT T Y P E = S U B M I T VALUE="Wybierz">
</FORM>
<HR ALIGN=CENTER W I D T H = 8 0 % SIZE=1>
<P>Jeżeli jesteś całkowicie pewien, że autora nią ma na liście,
wprowadź informacje o nim do formularza.</P>
<FORM METHOD=POST ACTION="<?php p r i n t ( " $ P H P _ S E L F " ) ; ?>">
I m i ę : < I N P U T TYPE=TEXT N A M E = " f i r s t n a m e " S I Z E = 2 5 X B R >
Nazwisko: <INPUT TYPE=TEXT NAME="lastname" SIZE=25xBR>
< I N P U T T Y P E = H I D D E N NAME="stage" VALUE=2>
<INPUT TYPE=SUBMIT VALUE="Dodaj autora">
</FORM>
</BODY>
</HTML>
Podsumowanie
PHP jest niezwykle wydajnym narzędziem obsługi skryptów, szczególnie we współpra-
cy z bazą danych. Możesz używać PHP do generowania wartości formularza na pod-
stawie danych zapisanych w bazie i oczywiście zapisywać do bazy dane z formularza.
Za pomocą PHP możesz stworzyć osobno formularz HTML i skrypt obsługi w PHP, al-
bo też połączyć oba te elementy w jeden skrypt PHP. Druga opcja jest lepsza, chociaż
może być nieco bardziej kłopotliwa w programowaniu. Powinieneś ustawić zmienną
w formularzu, która wskaże, czy dane zostały już wysłane.
Możesz stworzyć nawet kilka formularzy obsługiwanych przez ten sam skrypt PHP.
Każdy formularz może być wygenerowany na podstawie danych odczytanych z bazy.
Pozwoli to elastycznie korzystać z formularzy, ale trzeba uważać na skomplikowaną,
wieloczęściową strukturę.
Rozdział 21.
Dziennik sieciowy
W tym rozdziale:
+ Dlaczego dziennik?
+ Tworzenie prostego dziennika sieciowego
* Dodanie systemu wprowadzania danych przez HTTP
* Dołączenie bazy danych
* Zmiany i uzupełnienia
Małe samodzielne aplikacje PHP, takie jak głosowanie lub tablice ogłoszeniowe, są
atrakcyjne, ale kompletne witryny są miejscem, gdzie PHP naprawdę błyszczy. W roz-
dziale tym zawarliśmy kompletną instrukcję tworzenia najprostszych samodzielnych
witryn, jakimi są dzienniki sieciowe.
Dlaczego dziennik?
Sieciowy dziennik osobisty jest najprostszym rodzajem dynamicznej witryny. Może być
uważany za dynamiczną wersję osobistej strony macierzystej: zawartość jest zorgani-
zowana chronologicznie, a nie według wymagań biznesowych lub prezentowanej dzie-
dziny. Większość dzienników nie tworzy własnej treści w sensie opisywania nowości
lub tworzenia prac plastycznych. Bazują one raczej na komentarzach na temat pracy in-
nych osób lub wydarzeń. Na najwyższym poziomie tego stylu publiczne dzienniki, jak
na przykład Slashdot, są niezwykle popularnymi miejscami gromadzącymi społeczności
sieciowe i służą im jako forum wymiany doświadczeń lub zainteresowań.
Dzienniki są również zabawne i dzięki temu są wartościowe nawet dla tych, którzy
używają PHP do bardziej poważnych zastosowań. Nie ma większej przyjemności od
prowadzenia intelektualnej debaty, kłótni lub romansu dzięki dziennikowi sieciowemu.
Zapomnij o kinie, muzyce pop i telewizji — dzienniki sieciowe są prawdziwym me-
dium stulecia!
Najprostszy dziennik
Najprostszy dziennik jest po prostu szablonem i kilkoma dołączonymi plikami teksto-
wymi. Jest ograniczony tylko do lokalnych modyfikacji — nie możesz tworzyć wpisów
poprzez HTTP, ale zapisując plik tekstowy po załogowaniu się na serwer jako normalny
użytkownik. Nie możesz również przypisać poziomów uprawnień, więc ten typ dzien-
nika sieciowego jest najbardziej odpowiedni dla czysto prywatnej witryny prowadzonej
przez jednego autora.
Możesz więc spytać, po co to wszystko? Naszym zamiarem jest pokazanie, w jaki spo-
sób działają poszczególne części kodu PHP przed dodaniem funkcji, umożliwiających
połączenie z bazą danych, i modyfikacji zawartości witryny za pomocą danych poda-
nych w formularzach (co będzie tematem następnych dwóch części tego rozdziału). Po-
równanie podejścia, w którym dane są przechowywane w plikach, z integracją z bazą
danych efektywnie pokazuje, dlaczego to drugie wyjście jest lepsze. Możesz również
zaprojektować całkowicie prywatny pamiętnik, który można czytać w przeglądarce we
własnym komputerze i nie umieszczać go w Internecie.
Rysunek 21.1.
Strona dziennika
sieciowego
<BODY BGCOLOR=tFFFFFF>
<TABLE BORDER="0" CELLPADDING="5" WIDTH=100%>
<TR BGCOLOR=#822222>
<TD ALIGN=RIGHTXHl>Dziennik na dzień <?php print ("Sdate") ; ?></Hl>
366____________________________________Część II » PHP i bazy danych
</TDX/TR>
<TRXTD>
<?php include("navbar.inc"); ?>
<TD WIDTH=85%>
<?php
// Dla początkujących nieco ryzykowne jest automatyczne generowanie
// łączy "następny" i "poprzedni", ponieważ możesz łatwo można wpaść
// w nieskończoną pętlę. Dodamy te łączą ręcznie do każdej pozycji z datą.
// Dołącz pozycję dla podanej daty lub wyświetl domyślny komunikat "nie dzisia:"
// jeżeli nie ma żadnej zawartości.
if(file_exists("Sdate.txt"))
(
include("Sdate.txt") ;
)
else
(
include("not_today.txt") ;
)
?>
</TDX/TR>
</TABLE>
</TDX/TR>
</TABLE>
</BODY>
</HTML>
<DIV CLASS="topic">ŚWIETO</DIV>
<P>To jest rok, w którym inżynierowie nie świętują Nowego Roku. Wszyscy śpimy
pod swoimi biurkami, upewniając akcjonariuszy, że nasz szef zwalczył pluskwę
milenijną</P>
<CENTER>
<P>
<A HREF="weblog.php?date=l9991231">Poprzedni</A>
<A HREF="weblog.php?date=20000102">Następny</A>
</P>
</CENTER>
<BODY BGCOLOR=#FFFFFF>
<TABLE BORDER="0" CELLPADDING="10" WIDTH= 100%>
<TR BGCOLOR=»822222>
<TD A L I G N = R I G H T VALIGN=BOTTOMXHl>Moje u l u b i o n e < / H l >
</TDX/TR>
<TRXTD>
<?php i n c l u d e ( " n a v b a r . i n c " } ; ?>
<TD W I D T H = 7 5 % >
<DIV CLASS="topic">KSIĄŻKK/DIV>
<DL>
<DT>Cryptonomicon, autor: Neal Stephenson</DT>
<DD>Majstersztyk dla technicznych — to nasze życie, wejdź w kierat ogromnych
inwestycji. Załaduj esej "Na początku byi wiersz komend" z witryny:
www.crytonomicon.com</DD>
</DL>
<DIV CLASS="topic">MUZYKA</DIV>
<DL>
<DT>When The Pawn..., Fiona Apple</DT>
<DD>Jeszcze jedna latynoska gwiazdka.</DD>
</DL>
</TDX/TR>
</TABLE>
</TDX/TR>
</TABLE>
</BODY>
</HTML>
<STYLE TYPE="text/css">
<!-
BODY {font-family: verdana, arial, sans-serif;
font-size: lOpt; color: #000000; text-align: left)
HI (font-family: verdana, arial, sans-serif;
font-size: 14pt; color: ttFFFFFF)
.sidebar {font-family: verdana, arial, sans-serif;
font-size: 12pt; color: #822222; text-align:right; margin-top: lOpx)
.topic {font-family: verdana, arial, sans-serif;
368______________________________________Część II » PHP i bazy danych
</STYLE>
Ekrany wyświetlania danych są dokładnie takie same jak w tworzonej lokalnie wersji
dziennika. Dodatkowo potrzebuj emy plików zamieszczonych na wydrukach 21.7 do 21.10.
login.php
logentry.php
logentry_handler.php
password, inc
Umieśćmy plik password, inc w katalogu poza drzewem dostępnym przez WWW, na
przykład /home/weblog. Katalog ten musi byś dostępny dla wszystkich, a użytkownik
httpd (Nobody) musi mieć możliwość odczytania pliku. Jeżeli posiadasz prawa admini-
stratora, możesz zmienić właściciela pliku na użytkownika httpd. Jeżeli nie, musisz
nadać uprawnienia do odczytu wszystkim, co stanowi osłabienie systemu bezpieczeń-
stwa. Upewnij się, że hasło to nie jest hasłem użytkownika systemowego.
<?php
include("/home/weblog/password.inc");
if(Stest_username == Susername && $test_password == Spassword)
{
?>
<BODY>
<FORM ACTION="logentry_handler .php" METHOD="POST">
<P>Wprowadż d z i s i e j s z y tekst do d z i e n n i k a : <BRXTEXTAREA NAME="logtext"
COLS=75 ROWS=20 WRAP="VIRTUAL"X/TEXTAREAX/P>
< I N P U T TYPE="hidden" NAME="test_username" VALUE="<?php
p r i n t ( " $ t e s t _ u s e r n a r a e " ) ; ?>">
<PXINPOT TYPE="SUBMIT" NAME="SUBMIT" VALUE="Wyśli j "></P>
</FORM>
<?php
l
else
{
mail("meSlocalhost", "Podglądanie dzinnika", "Ktoś z adresu $REMOTE_ADDR
próbuje wejść na twoją stronę wprowadzania danych do dziennika.");
)
?>
</BODY>
</HTML>
Użycie bazy danych ma jeszcze dwie dodatkowe zalety. Po pierwsze, można łatwo na-
pisać skrypt do edycji i wprowadzania pozycji dziennika za pomocą formularza HTML.
Jeszcze lepsze jest programowe, a nie ręczne tworzenie łączy do nawigacji pomiędzy
pozycjami dziennika.
<?php
/* Podłączenie do bazy danych, sprawdź logowanie. */
include("/home/weblog/db_password.inc"};
mysql_connect(Shostname, $user, $password);
mysql_select_db("weblogs");
Squery = "SELECT password FROM login WHERE username = '$test_username'";
$result - mysql_query{$query);
Spassword_row = mysql_fetch_array(Sresult);
Sdb_password = $password_row[0];
if(Stest_password == Sdb_password)
t
?>
<BODY>
<FORM ACTION="db_logentry_handler.php" METHOD="POST">
< P > T e k s t : < B R X T E X T A R E A NAME="logtext" COLS=75 ROWS=20
WRAP="VIRTUAL"X/TEXTAREAX/P>
< I N P U T TYPE="hidden" NAME="test_username" VALUE="<?php
p r i n t ( " S t e s t _ u s e r n a m e " ) ; ?>">
<PXINPUT TYPE="SUBMIT" NAME="SUBMIT" VALUE="Wyśli j "X/P>
</FORM>
<?php
}
else
{
mail("me@localhost", "Podglądanie dzinnika", "Ktoś z adresu SREMOTE_ADDR
próbuje wejść na twoją stronę wprowadzania danych do dziennika.");
t
?>
</BODY>
</HTML>
<?php
/* Podłączenie do bazy danych, sprawdź logowanie. */
include("/home/weblog/db_password.inc");
mysql_connect(Shostname, Suser, Spassword);
mysql_select_db("weblogs");
$query = "SELECT password FROM login WHERE username = 'Stest_username'";
Sresult = mysql_query(Squery);
$password_row = mysql_fetch_array(Sresult);
Sdb_password = Spassword_row[0];
if ($test_password == $db_jpassword)
372______________________________________Część II « PHP i bazy danych
(
$queryl = "SELECT logtext FROM mylog WHERE date = $edit_date";
$resultl = mysql_query($queryl) ;
Sentry_row = mysql_fetch_array(Sresultl);
/* Po pobraniu tekstu t bazy SQL powinieneś usunąć znaki sterujące. */
$edit_entry = stripsląshes($entry_row[0]);
?>
<BODY>
<FORM ACTION="db_logentry_handler.php" METHOD-"POST">
< P > T e k s t : < B R X T E X T A R E A NAME="logtext" COLS=75 ROWS=20 WRAP="VIRTUAL"X?php
print ("$edit_entry") ; ?></TEXTAREAX/P>
<INPUT TYPE="hidden" NAME="edit_date" VALUE="<?php p r i n t ( " S e d i t _ d a t e " ) ; ?>">
< I N P U T TYPE="hidden" NAME="test_username" VALUE-"<?php
print ( "$test_username" ) ; ?>">
<PXINPUT TYPE="SUBMIT" NAME="SUBMIT" VALUE="Wyśli j "X/P>
</FORM>
<?php
)
else
(
mail("me@localhost", "Podglądanie dzinnika", "Ktoś z adresu $REMOTE_ADDR
Spróbuje wejść na twoja stronę zmiany danych do dziennika.");
)
?>
</BODY>
</HTML>
<?php
$date = datę("Ymd");
if($test_username)
{
include("/home/weblog/db_password.inc") ;
mysql_connect(Shostname, $user, $password);
mysql_select_db("weblogs") ;
4. Oglądasz tą stronę bez podawania daty (wiec domyślnie jest data dzisiejsza)
i jest to nowa pozycja dzisiaj.
5. Oglądasz te stronę bez podawania daty (więc domyślnie jest data dzisiejsza)
i nie jest to nowa pozycja.
Daj domyślny komunikat z łączem do ostatniej pozycji.
*/
/* Pobierz dzisiejszą datę dla przypadków 4 i 5. */
if{llsSet(Sdate))
l
$date = datę("Ymd");
}
?>
<HTML>
<HERD>
<TITLE>Dziennik sieciowy PHP4: <?php print("$date"); ?></TITLE>
<?php include("style.inc"); ?>
</HEAD>
<BODY BGCOLOR=tłFFFFFF>
<TABLE BORDER="0" CELLPADDING="5" WIDTH=100%>
<TR BGCOLOR=#822222>
<TD ALIGN=RIGHTXHl>Mój dziennik na dzień <?php print ("$date"); ?></Hl>
</TDX/TR>
<TRXTD>
<?php include("navbar.inc"); ?>
<TD WIDTH=85%>
<?php
/* Otwórz połączenie do bazy danych. */
include("/home/weblog/db_password.inc");
mysql_connect($hostname, Suser, Spassword);
mysql_select_db("weblogs");
Slast_ID = SlastID_row[0];
/* Pierwsza gałąź tekstu wyświetla pozycje dziennika w przypadkach l - 4... */
$queryl = ("SELECT ID, logtext FROM mylog WHERE date = Sdate");
$resultl = mysql_query($queryl);
$row_test_num = mysql_num_rows(Sresultl);
if($row_test_num > 0)
f
Sentry_row = mysql_fetch_array($resultl);
$entry_ID = $entry_row[0];
$logtext = stripslashes($entry_row[1]);
/* Pobierz poprzednią datę dla przypadków l, 3 i 4. Ten test zakłada,
że baza zwiększa wartości od 1; jeżeli jest inaczej, powinieneś
zmienić liczbę poniżej. */
if($entry_ID > 1)
t
$prev_ID = $entry_ID - 1;
$query2 = ("SELECT date FROM mylog WHERE ID = $prev_ID");
$result2 = mysql_query(Squery2);
$prevdate_row = mysql_fetch_array($result2);
$prev_date = Sprevdate_row[0];
)
else
374____________________________________Część II » PHP i bazy danych
(
$prev_date = "";
)
/* Pobierz następną datę dla przypadków 1 1 2 . */
if($entry_ID != $last_ID)
{
$next_ID = $entry_ID + 1;
Squęry3 = ("SELECT date FROM mylog WHERE ID = $next_ID");
$result3 = mysql_query($query3);
Snextdate_row = mysql_fetch_array($result3);
Snext_date = Snextdate_row[0];
}
else
{
$next_date = "";
(
/* Wyświetl tekst dla... */
/* Przypadek l */
if{$next_date != "" && $prev_date != ""}
(
print ("<CENTERxpxA HREF=\"db_weblog.php?date=$next_date\">Następny</A>
<A HREF=\"db_weblog.php?date=$prev_date\">Poprzedni</A>
</Px/CENTER>\n$logtext\n
<CENTERXPXA HREF=\"db_weblog.php?date=$next_date\">Następny</A>
<A HREF=\"db_weblog.php?date=$prev_date\">Poprzedni</Ax/Px/CENTER>") ;
)
/* Przypadek 2 */
e l s e i f ( $ n e x t _ d a t e != "" & & $prev_date == "")
(
p r i n t ("<CENTERXPXA HREF=\"db_weblog .php?date=$next_date\">Następny</A>
</PX/CENTER>\n$logtext\n<CENTER>
<PXA HREF=\"db_weblog.php?date=$next_date\">
Następny</AX/PX/CENTER>") ;
)
/* P r z y p a d k i 3 1 4 . */
e l s e i f ( $ n e x t _ d a t e ••=• "" 5& $prev_date != "")
{
print ("<CENTERXP>
<A HREF=\"db_weblog.php?date=$prev_date\">Poprzedni</A>
</PX/CENTER>\n$logtext\n<CENTERXP>
<A HREF=\"db_weblog.php?date=Sprev_date\">
Poprzedni</AX/PX/CENTER>") ;
(
}
/* ... ta gałąź obsługuje przypadek 5. */
else
{
/* Pobierz datę ostatniego wpisu. */
$query2 = ("SELECT date FROM mylog WHERE ID = $last_ID");
$result2 = mysql_query($query2) ;
Slastdate_row = mysql_fetch_array(Sresult2);
$last_date = $lastdate_row[0];
print("<P>Niestety dzisiaj nic nie ma! Mój ostatni wpis jest
<A HREF=\"db_weblog.php?date=Slast_date\">tutaj</A>.</P>");
)
?>
</TDX/TR>
</TABLE>
</TDX/TR>
</TABLE>
</BODY>
</HTML>
Rozdział 21. » Dziennik sieciowy____________________________________375
Możliwe rozszerzenia
Możesz zmienić i dodać następujące elementy w przytoczonym kodzie:
+ Zmiana kolorów, stylów i układu.
•* Zmiana częstotliwości uaktualnień (co tydzień, co miesiąc).
•* Zmiana nawigacji na bazującą na kalendarzu, a nie na łączach Następny i Po-
przedni.
+ Zmiana nawigacji na tematyczną, a nie na podstawie daty.
+ Wstrzymanie automatycznego przeterminowania pozycji.
+ Umożliwienie edycji przyszłych pozycji w bazie danych.
• Dodanie wielu autorów, redaktorów z różnymi uprawnieniami.
Oprócz osobistego dziennika, możesz na podstawie tego kodu utworzyć dowolną prostą
chronologiczną witrynę, na przykład:
•* Rodzinny dziennik wakacyjny.
•* Historia projektu.
376____________________________________Część II » PHP i bazy danych
Podsumowanie
Mimo że PHP jest użyteczny w małych oddzielnych projektach, takich jak głosowania,
najbardziej imponujące jest tworzenie kompletnej witryny sterowanej danymi. Naj-
prostszym przykładem takiej witryny jest osobisty dziennik sieciowy. Polecamy prowa-
dzenie takiej witryny każdemu użytkownikowi, ponieważ jest użyteczna do testowania
nowych pomysłów i technik.
Format dziennika sieciowego jest bardzo elastyczny. Może zostać rozbudowany do po-
ważnej publicznej witryny, jak na przykład Slashdot (z wieloma współpracownikami
i fachową redakcją zawartości). Możesz również tworzyć mały sekretny pamiętnik.
Ważne jest, że po stworzeniu za pomocą PHP kompletnej witryny sterowanej danymi
nie wrócisz już do statycznych stron WWW.
Rozdział 22.
Sieciowe głosowanie
W tym rozdziale:
* Przykład zastosowania PHP i bazy danych
+ Zbieranie głosów
* Wyświetlanie głosów
* Nadużycia
Rozdział ten jest przeglądem części kodu PHP, który jest używany w popularnej witrynie
WWW. Jest to dodatkowo fragment programu, który w przeciwieństwie do prezento-
wanych do tej pory przykładów jest ciągle rozwijany. Pokazuje on dosyć skomplikowa-
ną interakcję z bazą danych, sposób zastosowania mechanizmu cookie oraz kilka
interesujących sztuczek i technik.
Zadania systemu
W „wolnym czasie" Autorzy uruchomili kilka witryn WWW z recenzjami i rekomenda-
cjami książek. Jedna z nich jest poświęcona kryminałom (www.mysteryguide.com), dru-
ga opisuje książki popularno-naukowe (www.siencebookguide.com).
378_______________________________________Część II » PHP i bazy danych
Cele systemu
Gdy rozpoczynaliśmy dodawanie tych funkcji do naszej witryny, zamierzaliśmy zreali-
zować kilka celów. Chcieliśmy, aby nasz system:
* pozwalał ocenić książkę na stronie recenzji;
* wyświetlał bieżącą ocenę książki na stronie;
* na głównej stronie wyświetlał ranking książek;
* pozwalał na łatwe jednorazowe głosowanie na książkę;
** nie pozwalał na wielokrotne głosowanie na tę samą książkę.
Struktura
Nasza witryna ma jedną stronę zarezerwowaną dla każdej omawianej książki. We wcze-
śniejszej wersji witryny każda taka strona była statyczną stroną HTML. W wersji póź-
niejszej każda strona jest dynamicznie generowana na podstawie zawartości bazy
danych MySQL.
Sam kod składa się z dwóch części: kodu obsługującego zbieranie i wyświetlanie ocen
oraz kodu obsługującego centralną stronę ocen czytelników. W rozdziale tym te frag-
menty kodu są opisane w częściach „Zbieranie głosów" oraz „Wyświetlanie zbiorczych
wyników".
Rozdział 22. » Sieciowe głosowanie___________________________________379
Tabela 'book'
ID int (NOT NULL, PRIMARY KEY, AUTO INCREMENT)
title varchar(75)
[... wiele kolumn nie używanych w tym kodzie]
Tabela 'ReaderRatings'
ID int (NOT NULL, PRIMARY KEY, AUTO INCREMENT)
bookid int (NOT NULL)
RatingDate timestamp
ClientIP varchar(lOO)
Rating tinyint
BogusBit tinyint
Zbieranie głosów
Pierwszy fragment kodu jest odpowiedzialny za części strony związane z głosowaniem.
Jest to fragment umożliwiający głosowanie oraz ten, który zawiera wyniki głosowania
na książkę. Na rysunku 22.1 pokazano część strony umożliwiającą głosowanie — aby
zobaczyć całą stronę, możesz zajrzeć na witrynę \vww. mysteryguide.com lub www.sien-
cebookguide. com.
Rysunek 22.1.
Fragment strony
umożliwiający
głosowanie
Wyświetlaniem tych fragmentów strony oraz obsługą przesłanych głosów zajmują się
funkcje pokazane na wydruku 22.1. Są one dołączone do pliku „book.php", który zaj-
muje się wyświetlaniem stron poszczególnych książek.
if (IsSet($REMOTE_HOST))
Sremote = $REMOTE_HOST;
else
Sremote = $REMOTE_ADDR;
static $dbh;
if (ilsSet(Sdbh))
Sdbh = mysql_connect($dbhost, $dbuser, $dbpass);
mysql_select_db('scienceguide');
SresultlD = raysql_query(
"select Rating, Count(ID)
from ReaderRatings
where bookid = $book
and BogusBit <> 1
group by Rating
order by Rating desc");
print("<A NAME-ReaderRating>
print ("</TDX/TR>
<TD ALIGN=LEFT BGCOLOR=\"$global_box_color\">
<FONT SIZE=-2 FACE-\"ARIAL, GENEVA, S A N S - S E R X F \ " > " ) ;
if ($Rating)
(
print("<B>Dziekujemy za głosowanie!</B>");
)
else
(
print("<B>Czy czytałeś tę ksiązkę?</B>
Jeżeli tak, poznajmy twoją opinię o niej.
Kliknij jeden z przycisków oceny
i twój głos zostanie zarejestrowany.
<CENTER>
<FORM ACTION=\"Starget_path\">
< I N P U T TYPE=SUBMIT NAME=Rating VALUE=\"5 - Świetna\"XBRXBR>
< I N P U T TYPE=SUBMIT NAME=Rating VALUE=\"4 - Bardzo dobra\"XBRXBR>
<INPUT TYPE=SUBMIT NAME=Rating VALUE=\"3 - Dobra\"XBRXBR>
<INPUT TYPE=SUBMIT NAME=Rating VALUE=\"2 - Średnia\"XBRXBR>
382____________________________________Część II » PHP i bazy danych
function ReaderRating ()
f
global $book, $Rating, SRatingCookieName, SSRatingCookieName;
if (!IsSet(S$RatingCookieName))
(
GetFeedbackl);
}
DisplayRatings();
)
Założyliśmy, że funkcje będą użyte na stronie informacyjnej, która zawiera m.in. zaini-
cjowaną zmienną $book (identyfikator rekordu w tabeli książek), $dbhost, $dbuser
i $dbpass (aby podłączyć się do bazy danych MySQL) oraz $global_box_color
(namiastka arkusza stylu).
DisplayRatings()
Funkcja DisplayRatings ( ) realizuje w postaci prostej tabeli wyświetlanie wyniku
wyrażenia SELECT na tabeli ReaderRatings, w sposób opisany w rozdziale 19.
Rozdział 22. » Sieciowe głosowanie___________________________________383
GetFeedbackQ
Funkcja Get Feedback ( ) jest przeznaczona głównie do prostej prezentacji formularza
dedykowanego do przesyłania wyniku ponownie do tej samej strony book.php i zawiera
jedną niewielką sztuczkę. Jeżeli zmienna $Rating jest zainicjowana, podczas wyświe-
tlania strony po zagłosowaniu na książkę, zamiast zaproszenia do głosowania wyświe-
tlane jest podziękowanie.
HandleRatingsQ
Po spełnieniu odpowiednich warunków, w odpowiedzi na naciśnięcie przycisku myszki
przez użytkownika, funkcja HandleRatings ( ) wstawia wiersz do tabeli ReaderRa-
tings. Chcieliśmy, aby warunki te były następujące:
1. Użytkownik właśnie przesłał głos.
2. Użytkownik nie wysyłał wcześniej takiego głosu (wykrywane przez cookie).
Jeżeli wszystko jest w porządku, wstawiamy nowy wiersz zawierający głos oraz adres
IP głosującego. Nie narusza to prywatności — pozwala nam łatwiej wykryć nadużycia,
jeżeli głosy takie są zgrupowane (zajrzyj do części „Nadużycia" poniżej).
Smax_rating_display = 5;
$rain_votes = 2;
$min_best_votes = 2;
$min_worst_votes = 2;
?>
<HTML>
<HEAD>
<TITLE>Oceny czytelników</TITLE>
<META NAME="description" CONTENT="Strona ocen czytelników ScienceBookGuide.com">
<META NAME="keywords" CONTENT="Oceny czytelników, głosowanie, oceny,
ScienceBookGuide.com">
<!-- Strona ocen czytelników ScienceBookGuide.com -->
</HEAD>
</TD>
</TR>
<TR VALIGN=TOP ALIGN=CENTER>
<TD><H3><FONT FACE=<?php print("Sfontlist");
?»Najczęściej oceniane książki</FONTX/H3>
<TABLE WIDTH=100% CELLPADDING=1>
386____________________________________Część II » PHP i bazy danych
<?php
SresultlD = mysql_query("select avg(Rating) as avgrating,
Count(ReaderRatings.ID) as countrating, book.id, book.title from
ReaderRatings, book where BogusBit <> 1 and book.id = bookid group by
bookid order by countrating desc");
print("<TR ALIGN=LEFT>
<TH>Book</THXTH>Number</THxTH>Average</TH>") ;
$counter = 0;
while((Srow_array = mysql_fetch_row(SresultlD)) &&
(Scounter < $max_rating_display))
(
if ($row_array[4] != $row_array[3])
{
print ("<TR ALIGN=LEFTXTDXFONT SIZE=-1 FACE=$fontlist>");
$bookid = $row_array[2];
$booktitle = stripslashes($row_array[3]) ;
$author_string = book_to_author_string($bookid);
$bookurl = sprintf("book.html?book=%d", Sbookid);
Sbookstring = sprintf("%s, %s", $booktitle, $author_string);
print("<A HREF=\"$bookurl\">$booktitle</A>, $author_string
</TDXTD> <FONT FACE=Sfontlist SIZE=-l>$row_array [ 1) </FONT>
</TDXTD> <FONT FACE=Sfontlist SIZE=-1>"1;
print ("%3.If",$row_array[0]);
print ("</FONTX/TDX/TR>") ;
$counter = $counter + 1;
)
)
?>
</TABLE>
<BR>
</TD>
</TR>
</TABLE>
<BR>
</TDX/TRX/TABLE>
<P ALIGN=RIGHTXFONT FACE=<?php print ("Sfontlist" );?> SIZE=-2 >
&tt!69 1999 Troutworks, Inc. All rights reserved. <BR>Ciagle zmieniane</FONTX/P>
</BODYX/HTML>
Rysunek 22.2
Strona wyświetlania
wyników
Kod ten wyświetla trzy tabele typu „Złota dziesiątka", zawierając najbardziej popularne,
najmniej popularne oraz najczęściej oceniane książki. Stylistycznie rzecz ujmując, powta-
rzanie kodu dla każdej tabeli jest nieładne — właściwie to realizując, powinniśmy napisać
funkcję, która jako argument wymagałaby podanie typu tabeli i kolejności sortowania.
Niestety nie zrobiliśmy tego i pokazujemy kod w takiej postaci, w jakiej istnieje.
Rozdział 22. * Sieciowe głosowanie__________________________________387
Nadużycia i skalowanie
System naszkicowany w tym rozdziale jest wystarczający do zbierania i wyświetlania
głosów w naszych witrynach poświęconych książkom i odpowiada użytkownikom.
W dodatku bazujący na cookies system utrudniający wielokrotne głosowanie na tę samą
książkę wydaje się być wystarczający. Proszę nie mylić utrudniania z zabezpieczaniem
— na pewno nie zgodzisz się na przeprowadzanie w ten sposób wyborów prezydenc-
kich. Istnieje wiele sposobów na ominięcie tego zabezpieczenia. Zauważyliśmy, że
wielu użytkowników zaakceptowało sens użycia tej funkcji. Czasami musimy wyrzucić
część podejrzanych głosów, które są w sposób oczywisty wygenerowane przez skrypt
lub ręcznie. Nie wnosi to niczego oprócz marnowania naszego czasu.
Podsumowanie
W tym rozdziale pokazaliśmy bardziej skomplikowany fragment kodu, który jest uży-
wany przez dosyć popularną witrynę WWW. Zbiera on głosy użytkowników na temat
jakości książek przedstawionych w witrynie, zapisuje głosy w bazie danych i wyświetla
zestawienie ogólne i dla danej książki. Witryna używa systemu cookie utrudniającego
sztuczne windowanie wyników.
Ten niewielki rozdział jest przeznaczony dla osób, tworzących witryny WWW korzy-
stające z bazy danych, które uważają, że robią coś nieodpowiednio lub nieefektywnie.
Być może nie masz doświadczenia z bazami danych, albo twoje strony ładują się zbyt
długo.
Połączenia
— ograniczanie i powtórne użycie
Ważne, abyś pamiętał, że nawiązanie połączenia z bazą danych jest zawsze kosztowną
operacją. Jeżeli twój skrypt nie wykonuje intensywnych obliczeń, to interakcja z bazą
danych będzie najbardziej czasochłonną częścią kodu, pochłaniającą też najwięcej za-
sobów. Najczęściej sprawdza się twierdzenie, że nawiązanie połączenia jest najbardziej
kosztowną częścią kodu, nawet gdy połączenie jest nawiązywane na stronie tylko raz.
Według naszych obserwacji, większość skryptów WWW nie wymaga nakładu na za-
mykanie i powtórne otwieranie połączenia z bazą danych w trakcie wykonywania jed-
nego skryptu. Jeżeli chcesz zminimalizować czas połączenia z bazą danych, otwieraj
połączenie bezpośrednio przed pierwszą operacją na bazie danych i zamykaj natych-
miast po wykonaniu ostatniej.
)
/* kod używający funkcji box__query{) */
?>
Powyższy kod sprawdza się świetnie, jeżeli spodziewamy się wyświetlenia tylko jednej
tabelki na stronie. Jeżeli jednak użyjemy tej funkcji więcej niż raz na stronie, będzie
otwierała i zamykała połączenie z bazą danych za każdym jej wywołaniem, co jest
mniej efektywne niż pozostawienie otwartego połączenia. Jak wcześniej wspomnieli-
śmy, naczelną zasadą jest pozostawianie otwartego połączenia, dopóki jest ono potrzeb-
ne na bieżącej stronie. Zastosowanie tej zasady do przedstawionej funkcji powinno
spowodować jej zmodyfikowanie i pobieranie połączenia jako argumentu (lub niejawne
używanie otwartego połączenia na początku skryptu), a następnie otwarcie jednego po-
łączenia na stronę.
Fragment ten powinien wypisać tytuły książek, odczytanych z tabeli książek, przy uży-
ciu identyfikatorów, odczytanych z tabeli autorów. Przykład ten pokazuje co prawda
skrajnie nieefektywną metodę pobierania danych (spójrz do części „Przenoszenie pracy
na serwer bazy danych"), ale ilustruje fakt, że dwa różne zestawy wyników (identyfi-
kowane przez zmienne $author_result i $book_result) mogą być używane w tym
samym czasie, po ich odczytaniu poprzez to samo połączenie.
Trwałe połączenia
Jeżeli przekonałeś się, że nakłady na otwieranie nowych połączeń do bazy danych za-
bijają wydajność, możesz zapoznać się z otwieraniem trwałych połączeń. W przeci-
wieństwie do zwykłych połączeń z bazą danych połączenia takie nie są automatycznie
392____________________________________Część II » PHP i bazy danych
Przenoszenie pracy
na serwer bazy danych
Jak w przypadku pisania kodu w dowolnym języku programowania, pisanie kodu współ-
działającego z bazą danych jest ćwiczeniem odpowiedniego podziału pracy. Ludzie piszą-
cy języki programowania i bazy danych zgodzili się na automatyzację, standaryzację
i optymalizację niektórych zadań często wykonywanych w czasie programowania, więc
programiści nie muszą ciągle „wynajdywać na nowo koła" w czasie tworzenia kolejnych
aplikacji. Ogólna zasada brzmi: o ile nie chcesz poświęcać wiele energii na optymalizację
kodu dla specjalnego zastosowania, lepiej użyj mechanizmu bazy danych, zamiast wymy-
ślać własne rozwiązanie tego samego zadania.
Gdy przekażemy do tej funkcji nazwisko i połączenie do bazy danych, wypisze ona
związane z nazwiskami imiona z tabeli „author", o ile takie istnieją. Dla przykładu
wywołanie print_first_name_bad( 'Sagan' , $dbconnection) może dać nastę-
pujący wynik
Imię to Carl
Jeżeli istnieje wielu autorów o takim samym nazwisku, zostanie wypisanych wiele
wierszy.
Problemem jest to, że nie potrzebujemy pobierać wszystkich danych z tabeli, „przepy-
chać przez cienką rurę", odczytywać i wybierać odpowiednie rekordy po naszej stronie
połączenia. W zamian powinniśmy użyć w zapytaniu warunku za pomocą klauzuli
WHERE:
function print_first_name_beter( Slastname, $dbconnection )
{
$query = "select firstname, lastname from author
where lastname = $łastname";
$result_id = mysql_query($query, Sdbconnection );
while ($row = mysql_fetch_array($result_id))
(
print("Imie to ". Srow['firstname']);
}
)
Klauzula WHERE zapewnia, że otrzymamy tylko interesujące nas wiersze. Nie tylko po-
zwala na ograniczenie ilości danych przesyłanych przez połączenie SQL, ale kod użyty
do odszukania właściwych wierszy w bazie danych będzie oczywiście szybszy niż twój
kod PHP.
Sortowanie i agregacja
Dokładnie tego samego argumentu powinieneś użyć, gdy będziesz chciał sam napisać
fragment sortujący wyniki zwrócone przez bazę danych, zliczający, liczący średnią (al-
bo w inny sposób agregujący wyniki). Klauzula ORDER BY w SQL pozwala na posor-
towanie pobranych wierszy według dowolnej listy kolumn w zapytaniu; sortowanie to
będzie prawdopodobnie efektywniejsze niż jakikolwiek własny kod lub funkcja PHP
394______________________________________Część II » PHP i bazy danych
sortująca tablicę. Podobnie, zamiast przeglądać wiersze bazy danych w celu zliczenia,
zsumowania lub obliczenia średniej, sprawdź, czy twoja baza danych pozwala na użycie
w SQL konstrukcji GROUP BY oraz funkcji do użycia w zapytaniach: count (), sum ()
i average ( ) . Zwykle wykonanie zapytania:
Squery = "select count(ID) from author";
jest o wydajniejszym sposobem liczenia wierszy tabeli niż pobranie ich i liczenie ich
w pętli PHP.
W chwili obecnej jedynym sposobem na wstawienie lub zmianę pól daty jest podanie
ciągu reprezentującego żądaną datę w formacie czytelnym dla bazy danych. Jeżeli
chcesz np. ustawić na określoną datę pole mydate typu datetime wszystkich wierszy
tabeli, możesz to zrobić za pomocą takiego pytania:
$query = "update mytable set myquery = 'September 4, 2001'";
Poprzednie wyjście jest dobre, dopóki ciąg z datą jest prawidłowy dla twojej bazy da-
nych. Sprawy się komplikują, gdy potrzebujesz takiego ciągu na bieżąco, aby repre-
zentował datę uzależnioną od wartości zmiennych w skrypcie.
Należy jednak pamiętać, że w większości systemów baz danych nie ma potrzeby prze-
chodzenia przez te męki przy ustawianiu pola na bieżącą datę i czas. Zwykle dostępna
jest funkcja zwracająca bieżącą datę; używa się jej bezpośrednio w zapytaniu. Poprzed-
nie zapytanie w wersji dla MySQL, które ustawia odpowiednie pole daty i czasu na datę
bieżącą, wygląda następująco:
Squery = "update mytable set myquery = now()";
Zauważ, że wywołanie funkcji nów ( ) nie jest otoczone apostrofami. Analogiczne za-
pytanie dla Microsoft SQL Server wygląda następująco:
Squery = "update mytable set myquery = getdateU";
Jeżeli data, którą chcesz zapisać, nie jest datą bieżącą, są lepsze sposoby od tworzenia
w skrypcie czytelnego dla serwera ciągu z datą. Wiele wersji SQL ma, oprócz funkcji
zwracających bieżącą datę, funkcje wykonujące operacje na datach. Możesz dodać lata,
miesiące lub godziny do dowolnej daty. W MySQL takąfunkcjąjest:
^ date add(date, date-interval)
Rozdział 23. » Styl i efektywność rozwiązań na podstawie PHP i bazy danych__________395
Problem pojawia się, gdy próbujesz utworzyć nową pozycję w bazie danych, która jest
rozproszona w kilku tabelach (każda z tych tabel ma klucz główny o automatycznie
zwiększającej się wartości). Jako przykład weźmy tabele utworzone za pomocą nastę-
pujących wyrażeń MySQL:
create table author) ID int primary key, auto_increment,
lastname varchar(75),
firstname varchar(75));
create table book (ID int primary key, auto_increment,
authorlD int,
title varchar(100));
Jednym założeniem w przypadku tych wyrażeń jest połączenie tabeli book z tabelą au-
thor w taki sposób, że b o o k . a u t h o r l D = a u t h o r . I D . Kolejnym założeniem jest au-
tomatyczne przypisanie przez bazę danych unikalnych identyfikatorów w tych tabelach.
Niestety połączenie tych założeń prowadzi do problemów. W jaki sposób mamy napisać
kod, który wstawi połączoną parę książka-autor, jeśli zarówno książka, jak i autor są
nowi w bazie danych? Jeżeli wstawimy nowego autora, pole ID wstawionego wiersza
będzie automatycznie utworzone przez bazę danych, więc nie jest częścią twojego wy-
rażenia SQL. W jaki sposób mamy podać właściwą wartość pola authorlD w nowym
wierszu tabeli book?
W kodzie tym tworzymy nowy wiersz autora i używając nazwiska oraz imienia wybie-
ramy ten nowo wstawiony wiersz, zapamiętując przypisany identyfikator nowego wier-
sza. Następnie dodajemy ten identyfikator do wyrażenia wstawiającego nowy wiersz do
tabeli book. Kod ten będzie prawdopodobnie działał w niektórych przypadkach, jeżeli
założymy, że imię i nazwisko autora są wystarczające do jednoznacznej identyfikacji
rekordu. Niestety w wielu bazach danych nie można utworzyć takiego założenia, co jest
oczywiście powodem stosowania unikalnych identyfikatorów.
Podobnym wyjściem, które jest czasami używane, jest wstawianie wiersza (na przykład
to tabeli autorów) i pobieranie wiersza o największym identyfikatorze (teoretycznie jest
wstawiony najpóźniej). Jest to niestety rozwiązanie, które działa tylko wtedy, gdy jest
testowane przez pojedynczego programistę, a przestaje działać na docelowym serwerze
bazy danych, obsługującym wiele połączeń jednocześnie. Problem wystąpi, gdy z inne-
go połączenia przyjdzie żądanie wstawienia rekordu i będzie obsłużone pomiędzy na-
szym wstawieniem a pobraniem największej wartości identyfikatora. Spowoduje to
pobranie nieprawidłowego identyfikatora, przez co nasze drugie wyrażenie wstawienia
rekordu będzie miało niewłaściwy identyfikator autora.
Najlepszym rozwiązaniem, o ile jest ono dostępne, jest pozwolenie bazie danych na pil-
nowanie ostatnio wstawionego identyfikatora i udostępnianie go w bieżącym połącze-
niu. W ten sposób nie wystąpią problemy synchronizacji, opisane w poprzednim
akapicie. PHP oferuje użytkownikom MySQL funkcję mysql_insert_id ( ) , która na
podstawie identyfikatora połączenia pobiera identyfikator ostatnio wstawionego wier-
sza. Możemy użyć tej funkcji do poprawienia poprzedniego przykładowego kodu:
Sauthor_lastname = 'Feynman1;
Sauthor_firstname = 'Richard1;
$book_title = 'The Character of Physical Law';
$author_insert = "insert into author(lastname, firstname)
values! '$author_lastname', 'Sauthor_firstname')";
mysql_query(Sauthor_insert) or die(mysql_error());
SauthorlD = mysql_insert_id{) ;
$book_insert = "insert into book (authorlD, title)
values ($authorID, $book_title)";
mysql_query($author_insert) or die(mysql_error()};
Jak w przypadku wielu funkcji PHP i MySQL, identyfikator połączenia w funkcji my-
sql_insert_id ( } jest opcjonalny i domyślnie przyjmuje ostatnio otwarte połączenie.
Podsumowanie
Ponieważ funkcje związane z bazą danych są pochłaniają najwięcej zasobów, możesz
korzystać z bardziej efektywnych technik kodowania. Jeżeli twoje skrypty sterowane
danymi są ociężałe, powinieneś nauczyć się działać razem z bazą danych, zamiast prze-
ciwko niej.
Brak połączenia
Jeżeli w skrypcie PHP masz odwołanie do bazy danych, a otwarcie połączenia nie udało
się, zobaczysz jeden z dwóch przedstawionych poniżej ostrzeżeń (w zależności od
ustawionego poziomu ostrzeżeń, a także, w pewnym stopniu, od tego, co spowodowało
problem).
400______________________________________Część II » PHP i bazy danych
Rysunek 24.1.
Ostrzeżenie
o braku połączenia
Jeżeli problem leży po stronie PHP, ekran błędu będzie raczej wyglądał jak ten pokaza-
ny na rysunku 24.2.
Rysunek 24.2.
Krytyczny bląd
wywalania
niezdefiniowanej funkcji
Rozdział 24. » Pułapki tandemu PHP-baza danych__________________________401
Z tych dwóch przypadków błąd krytyczny jest prostszy do poprawienia. Jeżeli otrzymasz
błąd wywołania niezdefiniowanej funkcji, która występuje w zestawie funkcji PHP, na
pewno zapomniałeś dodać tego modułu podczas instalowania PHP. W przypadku sys-
temu Unix powinieneś skompilować ponownie PHP z opcją -with-mysql. W przypadku
Windows wszystkie dostępne opcje są już skompilowane w dostarczonym programie,
więc powinna być dostępna odpowiednia instalacja, albo możesz włączać i wyłączać
poszczególne funkcje w p\\kuphp.ini. W przypadku MySQL (lub innej obsługiwanej
bazy danych) wystarczy po prostu odkomentować wiersz zawierający php_mysql.dll
w pliku php.ini i PHP jest już gotowy do pracy (chyba że umieściłeś pliki programu
MySQL w bardzo dziwnym miejscu, chociaż nie powinieneś tego robić).
Łatwo sprawdzić, czy program mysqld pracuje, więc możemy to zrobić na początku.
Można tutaj użyć dowolnej metody, jakiej zwykle używasz do sprawdzania pracujących
procesów. Na Windows NT oznacza to, że trzeba użyć kombinacji Ctrl+Alt+Delete, aby
wywołać Task Managera. W systemach Unix, gdzie swoboda wyboru jest ogromna,
możesz sprawdzić procesy systemu poprzez wywołanie p s lub graficznego programu
monitorowania systemu, albo nawet uruchamiając mysqladmin.
Problemy z uprawnieniami
Komunikaty o błędach, powodowane przez problemy z uprawnieniami, wyglądają po-
dobnie do opisanych przed chwilą problemów z nawiązaniem połączenia.
Rysunek 24.3.
Problemy
z uprawnieniami
Nie są to problemy strukturalne, ale zwykle skutki zapomnienia ważnych dla pracy
systemu parametrów. Błędy te można łatwo naprawić w większości sytuacji.
Nieoznaczone apostrofy
Apostrofy mogą powodować wiele małych, choć dokuczliwych problemów pomiędzy
PHP i MySQL. Sednem problemów jest fakt, że PHP przetwarza ciągi w cudzysłowach
i ignoruje apostrofy, natomiast MySQL przetwarza ciągi w apostrofach i ignoruje cu-
dzysłowy. Prowadzi to do sytuacji, w których musisz dokładnie określić przeznaczenie
każdego z tych znaków. Przykładem takiego wyrażenia jest:
mysql_query("INSERT INTO book (ID, title, year, ISBN)
VALUES ('NULL 1 , '$title', 'Syear1, '$ISBN')");
Niestety musisz być bardzo ostrożny podczas pisania takich wyrażeń. Jest to jeden z po-
wodów, dla których zaleca się podział kwerend MySQL na dwie części, jak na przykład:
Squery = "INSERT INTO book (ID, title, year, ISBN)
VALUES ('NULL1, 'Stitle', '$year', '$ISBN')";
Sresult = mysql_query($query);
Styl taki eliminuje również podwójne nawiasy, które również są częstą przyczyną błę-
dów w kodzie PHP.
Jeszcze większe problemy pojawiają się, jeżeli pracujemy z ciągami, które zawierają
w tekście apostrofy lub cudzysłowy. Pamiętaj, że apostrofy i cudzysłowy są tym samym
dla PHP i MySQL — nie ma żadnej sprytnej opcji, która rozwiązuje te problemy. Za-
pytania wstawiające dane nie będą wykonywane, jeżeli jakiekolwiek nazwisko będzie
posiadało w sobie apostrof (np.: O'Hara):
$query = "INSERT INTO employee (ID, lastname, firstname)
VALUES ('NULL', 'Slastname', 'Sfirstname')";
Sresult = mysql_query($query) ;
Inną, równie częstą przyczyną tych problemów są nazwy firm z apostrofami w nazwie,
takie jak Rosalita's Bar oraz jakiekolwiek angielskie napisy zawierające skrótowce lub
zaimki dzierżawcze (jak na przykład can't, what's lub Mike's)'.
' Ponieważ w języku polskim nie występują tego typu konstrukcje, można być nieco spokojniejszym, ale już
witryna zawierająca recenzje filmowe nie jest bezpieczna.
404_______________________________________Część II » PHP i bazy danych
2. Gdy ciąg jest reprezentowany przez zmienną, możesz użyć funkcji addsla-
shes ( ) , która automatycznie doda niezbędne znaki.
$string -
addslashes("Spiker powiedział: 'Pani O'Hara jest proszona na salę'.");
$wyr = mysql_query("INSERT INTO diary (ID, entry)
VALUES ('NULL', 'Sstring')");
Z niezrozumiałych dla nas przyczyn wielu użytkowników PHP nie lubi używać za każ-
dym razem funkcji addslashes () i jej partnera, funkcji stripslashes (). Raczej
wikłają się w wymienianie cudzysłowów na apostrofy, gdy naprawdę nie muszą tego
robić, jeżeli oznaczą backslashami wszystkie cudzysłowy. To praktyka w złym stylu,
a jest szczególnie niebezpieczna, gdy korzysta się z bazy danych.
Musisz dodać znaki backslash, gdy umieszczasz dane w bazie danych, a usunąć je po
odczytaniu ciągu z bazy danych (chyba że masz włączoną opcję magic-quotes).
Squery = "SELECT passphrase FROM userinfo WHERE username='Susername'";
Sresult = mysql_query(Squery) ;
$query_row = mysql_fetch_array(Sresult );
Spassphrase = stripslashes(Squery_row[0]);
Jeżeli tego nie zrobisz, za każdym wstawieniem danych do MySQL będzie dodawane co-
raz więcej znaków backslash! Jest to dosyć częste w przypadku formularzy na stronach
WWW, które wyświetlaj ą dane pobrane z bazy danych, jak pokazano na rysunku 24.4.
J
Rozdział 24. » Pułapki tandemu PHP-baza danych 405
Rysunek 24.4.
Nieusunięte z danych
znaki backslash
Rysunek 24.5.
Błąd powodowany
przez nieprawidłowe
zapytanie SQL
To, w jaki sposób będzie wyglądał ten błąd w przeglądarce, zależy od wersji PHP, wer-
sji bazy danych, ustawień obsługi błędów oraz sposobu obsługi błędów w skrypcie.
Kluczem do sukcesu jest wczesne wykrycie awarii.
Jeżeli nie umieściłeś procedur obsługi błędów przy wywołaniach zapytań, dostaniesz
pierwsze złe wiadomości, gdy spróbujesz użyć identyfikatora wyniku zapytania w ko-
lejnych odwołaniach do bazy danych. Typowym schematem jest:
$my_result = mysql_query{$baa_query);
// ... instrukcje przetwarzania i wyświetlania
$row = mysql_fetch_row($my_result); // tu ujawnia się błędy
Typowym komunikatem w MySQL jest „O is not a mysql result identifier in ..." (O nie
jest prawidłowym identyfikatorem wyniku mysql). Dzieje się tak dlatego, że zamiast
wykryć wartość O zwracaną przez mysql_query ( ) w przypadku błędu, próbujesz użyć
tej wartości jako prawidłowego identyfikatora wyniku.
Pomyłki w nazwach
Niestety, oprócz skomplikowanych błędów, tkwiących głęboko w środku programu,
istnieje mnóstwo banalnych pomyłek, które jednak bardzo łatwo usunąć.
Komunikat o błędzie zwracany przez bazę danych może być mylący — komunikat ten
mówi o nieznanej kolumnie 'Daniel'. Dzieje się tak, ponieważ ciągi nieotoczone apo-
strofami traktowane sąjako nazwy kolumn, jak w przykładzie:
$query - "select * from author where firstname = lastname";
Zapytanie takie może być użyte do wyszukiwania takich autorów, jak na przykład
„Lisa Lisa".
Niezainicjowane zmienne
Jednym ze sposobów zepsucia zapytania jest umieszczenie w nim niezainicjowanej
zmiennej.
408_______________________________________Część II » PHP i bazy danych
Ponieważ kod ten nie próbuje przechwycić błędu, znowu zobaczysz komunikat o tym,
że O nie jest prawidłowym identyfikatorem wyniku. Problem leży oczywiście w tym, że
wprawdzie przypisaliśmy wartość do zmiennej ($customerlD), ale użyliśmy w zapy-
taniu zupełnie innej ($customer__iD). Zmienna ta jest niezainicjowana, więc w czasie
interpretowania ciągu zachowuje się jak pusty ciąg. W wyniku tego baza danych otrzy-
ma następujące zapytanie, które oczywiście jest nieprawidłowe:
select from customers where ID =;
W tym zapytaniu tak naprawdę chciałeś użyć operatora 'or' a nie 'and', a błąd ten spo-
wodował, że zapytanie nie zwróci nigdy żadnych wierszy, niezależnie od zawartości
bazy danych.
Jeżeli skrypt przegląda w pętli wiersze tabeli i wyświetla ich o wiele zbyt dużo, problem
często leży w tym, że zapytanie ma zbyt mało połączeń. Naczelną zasadą jest, że liczba wa-
runków w klauzuli WHERE nie powinna być mniejsza od liczby łączonych tabel minus je-
den. Zapytanie poniższe posiada np. trzy tabele, ale ma tylko jeden warunek złączenia.
"select book.title, from book, author, country
where author.countrylD - country.ID";
Rozdział 24. » Pułapki tandemu PHP-baza danych__________________________409
Zapytanie to zwróci wszystkie możliwe pary książka — autor, niezależnie od tego, czy
autor napisał tę książkę, czy nie, co nie jest prawdopodobnie oczekiwanym wynikiem.
Kontrola poprawności
Jeżeli kończą ci się pomysły na szukanie błędów związanych z zapytaniami, użyteczne
jest porównywanie wyników zapytań osadzonych w PHP z tymi samymi zapytaniami
wykonanymi bezpośrednio na bazie danych. Jeżeli masz możliwość bezpośredniego
uruchomienia interpretera SQL (na przykład pisząc w wierszu poleceń mysql) oraz
przenoszenia danych pomiędzy programami, możesz wypróbować taki sposób:
1. Wstaw wyrażenie uruchomieniowe do skryptu PHP, które drukuje treść zapy-
tania bezpośrednio przed wykonaniem go w bazie danych (na przykład:
print ($query) ;).
2. Wstaw treść zapytania z przeglądarki (lub źródła HTML) do twojego interpre-
tera SQL.
Jeżeli zapytanie wygląda dobrze, ale powoduje błąd SQL i PHP, musi być w nim jakiś
błąd składniowy lub w nazwie; kod PHP w tym przypadku nic nie zawinił (chyba że
konstruuje to zapytanie). Podobnie w przypadku zbyt małej lub zbyt dużej liczby wier-
szy wyniku —jeżeli zachowanie jest identyczne w obu programach, winne jest zapytanie.
Jeżeli zaś zapytanie zachowuje się w interpreterze SQL tak, jak się tego spodziewasz,
jest prawidłowe, a podejrzenie pada na kod PHP, wysyłający zapytanie i przetwarzający
wynik.
Podsumowanie
Błędy PHP w połączeniu z bazą danych nie są zwykle zbyt głębokie, ale mogą być nie-
łatwo je odszukać. Ogólnie mówiąc, im wcześniej błąd zostanie wykryty w skrypcie,
410_______________________________________Część II » PHP i bazy danych
tym łatwiejsze będzie jego zlokalizowanie. Każde wyrażenie współpracujące z bazą da-
nych powinno posiadać dołączoną klauzulę or die ( ) , zawierającą niosący możliwie
dużo informacji komunikat o błędzie, szczególnie w trakcie uruchamiania.
W innych częściach tej książki spotykałeś się czasami z ostrzeżeniem „nowa funkcja",
które zwykle oznaczało możliwość wprowadzoną w PHP 4.0. Niniejszy rozdział powinien
być w ten sposób oznaczony, ponieważ w PHP 3.x nie było wbudowanego mechanizmu
śledzenia sesji. Jeżeli chcesz korzystać z sesji użytkownika, musisz zainstalować PHP 4,
zastosować popularną bibliotekę PHPLIB lub stworzyć własne rozwiązanie.
Czym są sesje?
Co mamy na myśli, mówiąc o sesjach? Nieformalnie, sesja w przypadku przeglądania
WWW to czas, w którym dana osoba, siedząc przy jednym komputerze, przegląda okre-
śloną liczbę stron, następnie kończy ten proces. Jeżeli prowadzisz witrynę WWW, którą
przykładowy użytkownik właśnie przegląda, sesja biegnie od czasu załadowania pierw-
szej strony, aż do ostatniej. Na przykład witryna hotelu na Karaibach może korzystać
z sesji na czas przeglądania pięciu stron, ale użytkownik naprawdę rozpoczął sesję
w portalu zajmującym się podróżami, a zakończył ją w czasie rezerwacji miejsca w ho- ..
telu konkurencji. u
Co stanowi problem?
Dlaczego więc idea sesji jest na tyle skomplikowana, że zajmujemy się nią dopiero te-
raz? Jest tak dlatego, że protokół HTTP jest bezstanowy. Powoduje to, że twój serwer
WWW ma mniej pamięci długoterminowej niż twój kod. W rezultacie serwer WWW
414___________________________________Część III » Techniki zaawansowane
Alternatywy sesji
Zanim zajmiemy się podejściem PHP do sesji, wymienimy kilka alternatywnych sposo-
bów rozwiązania problemu. Jak się później przekonamy, podejście PHP stanowi kom-
binację kilku z tych technik.
Adres IP
Serwery WWW znają albo nazwę, albo adres IP komputera, z którego przyszło żądanie
załadowania strony. W większości konfiguracji PHP są one dostępne w zmiennych, od-
powiednio $REMOTE_HOST i $REMOTE_NAME. Wydaje się, że identyfikacja komputera
Rozdział 25. » Sesje_________________________________________415
Niestety adres IP twojej przeglądarki nie musi do komputera, z którego korzysta użyt-
kownik. Zarówno AOL, jak również inni dostawcy stosują serwery proxy, które działają
jako pośrednicy. Jeżeli przeglądarka zażąda załadowania URL, wysyła on to żądanie do
docelowego serwera i zwraca stronę użytkownikowi, taka konfiguracja powoduje, że
wielu użytkowników może jednocześnie przeglądać twoją witrynę pozornie z jednego
adresu IP. Adresy IP nie są więc wystarczająco jednoznaczne, aby stanowić podstawę
do śledzenia sesji.
Ukryte zmienne
Każde wywołanie HTTP jest obsługiwane niezależnie, ale za każdym razem gdy użyt-
kownik przejdzie do innej strony witryny, jest zwykle realizowane przez łącze lub
przetworzenie formularza. Jeżeli pierwsza strona, którą użytkownik odwiedza, może
wygenerować identyfikator tej wizyty, następne przejścia ze strony do strony mogą go
przenosić.
Ten fragment kodu kontroluje, czy zmienna $my_s_id została wcześniej zainicjowana.
Jeżeli tak, zakładamy, że została przekazana do bieżącej strony i jesteśmy w środku se-
sji. Jeżeli nie, oznacza to, że jesteśmy na pierwszej stronie nowej sesji i wywołujemy
hipotetyczną funkcję, nazwaną generate_s_id ( ) , w celu utworzenia identyfikatora
(jest to trudne zadanie).
Ta technika jest prostym sposobem odróżniania sesji (dopóki możesz generować identy-
fikatory). Jeśli mamy identyfikatory sesji, możemy zastosować wiele sposobów dołą-
czenia danych do każdej sesji. Jednym z nich jest użycie identyfikatora sesji jako klucza
tabeli bazy danych. Jednak to wyjście jest kłopotliwe — musisz upewnić się, że każde
łącze i obsługa formularza prawidłowo przenoszą opisane informacje. Jeżeli przesyłasz
informacje jako argument GET, maszyna śledzenia sesji będzie widoczna w pasku adresu
przeglądarki — argument może być zmieniony lub przechwycony przez użytkownika.
416________________________________Część III » Techniki zaawansowane
Cookie
Innym sposobem śledzenia sesji jest użycie takiego samego identyfikatora sesji, jak opi-
saliśmy przed chwilą, ale do jego przekazywania pomiędzy stronami stosuje się cookie.
Jakie są wady? Niektóre stare przeglądarki w ogóle nie obsługują cookie, a nowe po-
zwalają użytkownikowi na zablokowanie możliwości tworzenia cookie przez serwery
WWW. Mimo że cookie dostarczają dobre rozwiązanie problemu, nie możemy zakła-
dać, że będą zawsze dostępne.
Sesje PHP działają na bazie kombinacji metody ukrytych zmiennych i cookie. Dla zalet
cookie PHP będzie używał tej metody, jeżeli jest dostępna; w przypadku braku obsługi
cookie skorzysta ze zmiennych. Na szczęście funkcje obsługi sesji działają na bardziej
abstrakcyjnym poziomie i same troszczą się o obsługę cookie i inne szczegóły. Jeżeli
twoja wersja PHP 4 została odpowiednio skonfigurowana do obsługi sesji, możesz użyć
sesji bez martwienia się o to, jaka metoda została zastosowana.
Jeżeli PHP śledzi sesje, można również zachować dane przez „rejestrowanie" określo-
nych zmiennych, w celu wskazania, że ich wartości mają być zapamiętane w połączeniu
z identyfikatorem sesji.
Jeżeli poprzednio nie została zarejestrowana taka zmienna lub zapomnieliśmy wywołać
session_start (), zmienna będzie traktowana jak każda niezainicjowana zmienna.
portowana do zmiennych sesji. Na kolejnych stronach wartość jej powinna być taka sa-
ma, jak po zakończeniu tego skryptu. W naszym kodzie wartość tej zmiennej zmieniła
się na 'Dallas', więc następne strony otrzymają taką właśnie wartość zmiennej.
Możliwe jest takie skonfigurowanie PHP, aby program przechowywał zmienne sesji
w bazie danych zamiast w plikach. Więcej informacji znajdziesz w części „Zagadnienia
konfiguracji" poniżej.
Tabela 25.1.
Zestawienie funkcji do obsługi sesji
Funkcja Opis
session start ( ) Nie wymaga argumentu. Powoduje, że PHP odczytuje identyfikator sesji
przekazany do strony (dzięki cookie lub GET, POST); jeżeli nie może go
odszukać, tworzy nowy identyfikator sesji
Jeżeli odnaleziony został stary identyfikator sesji, PHP odtwarza
przypisania do wszystkich zarejestrowanych zmiennych i udostępnia te
wartości jako zwykłe zmienne globalne
session r e g i s t e r ! ) Wymaga ciągu jako argumentu i rejestruje zmienną o nazwie określonej
przez ciąg — na przykład session register ( ' username ' ) . Nazwa
zmiennej nie powinna zawierać początkowego znaku '$'. Można również
przekazać tablicę ciągów, aby za jednym razem zarejestrować wiele
zmiennych
Efektem rejestracji jest zapamiętanie przypisań do obiektu (po
zakończeniu skryptu zarejestrowane zmienne podlegają serializacji i są
przenoszone w sposób, który zapewnia ich odtworzenie w wywołaniu
session s t a r t ( ) )
Tabela 25.1
Zestawienie funkcji do obsługi sesji (ciąg dalszy)
Funkcja Opis
session is r e g i s t e r e d ( ) Sprawdza, czy zmienna o podanej nazwie jest zarejestrowana w bieżącej
sesji, zwracając TRUE, jeżeli zmienna jest zarejestrowana, FALSE, jeżeli
nie jest
session d e s t r o y ! ) Wywołanie tej funkcji usuwa wszystkie zapamiętane dane o sesji
(identyfikator sesji nie musi się zmieniać po wywołaniu tej funkcji).
Wywołanie tej funkcji nie zmienia wartości żadnych zmiennych
w bieżącym skrypcie
session name ( ) Funkcja wywołana bez parametrów zwraca nazwę bieżącej sesji. Jest to
zwykle ' P H P S E S S I D 1
Wywołana z jednym argumentem session name ( ) ustawia nazwę
bieżącej sesji na podany ciąg. Nazwa tajest używana jako klucz do
odszukania identyfikatora sesji w cookie lub argumentach GET, POST. Aby
odnalezienie sesji się udało, nazwa sesji musi być identyczna jak nazwa
sesji, w trakcie której poddano zmienne serializacji. Zauważ, że nie ma
powodu do zmiany nazwy sesji, chyba że chcesz rozróżniać różne typy
sesji obsługiwanych jednocześnie przez serwer WWW (na przykład
w przypadku wielu witryn korzystających z sesji). Nazwa sesji jest
zmieniana na domyślną za każdym uruchomieniem skryptu, więc zmiana
nazwy sesji musi zostać wykonana w każdym skrypcie przed wywołaniem
innej funkcji obsługi sesji
session module name ( ) Jeżeli nie zostaną podane żadne argumenty, funkcja zwraca nazwę modułu
odpowiedzialnego za obsługę danych sesji. Nazwa tajest domyślnie
ustawiona na ' f ileś ' , co oznacza, że dane sesji po serializacji są
zapisywane do plików w katalogu ustawionym przez funkcję
session save path ( ) . Jeżeli podany zostanie ciąg jako argument,
nazwa modułu jest zmieniana na ten ciąg (np. ' u s e r ' dla zdefiniowanej
przez użytkownika bazy danych sesji; ale nie powinieneś zmieniać tego
parametru, jeżeli nie wiesz dokładnie, co robisz)
session save path ( ) Zwraca (lub ustawia w przypadku podania argumentu) ścieżkę do
katalogu, w którym zapisywane są wartości zmiennych sesji (zwykle jest
to /tmp w systemach Unix). Katalog ten musi mieć odpowiednio
ustawione uprawnienia, aby PHP mógł tam zapisywać pliki
session i d ( ) Nie ma argumentów. Zwraca ciąg, który jest kluczem określającym sesję.
Jeżeli zostanie przekazany argument, funkcja ta ustawi identyfikator sesji
na tę wartość
session encode ( ) Zwraca ciąg z zakodowanym stanem bieżącej sesji, który może zostać
zdekodowany przez funkcję session decode ( ) . Ciąg ten może zostać
użyty do zapamiętania sesji w celu odtworzenia jej za jakiś czas
(np. zapisanie zakodowanego ciągu w pliku lub bazie danych)
session d e c o d e ! ) Pobiera wynik działania funkcji session encoded i odtwarza stan
sesji, zmieniając zmienne sesji na zmienne strony (analogicznie do
session start ( ) )
422___________________________________Część III » Techniki zaawansowane
<?php
if (Spagecount >= Spagecount__limit)
session_destroy();
?>
Rozdział 25. » Sesje____________________________________________423
Ten przykładowy kod realizuje za pomocą wywołania funkcji obsługi sesji następujące
zadania:
1. Wywołuje session_start ( ) w celu nakłonienia PHP do odszukania wcze-
śniej istniejącej sesji —jeżeli nie zostanie odnaleziona żadna sesja, tworzony
jest nowy identyfikator (chociaż kolejną funkcją jest session_register ( ) ,
która troszczy się również o rozpoczęcie sesji, jeżeli nie było wywołania
session_start ()).
2. Użyta jest funkcja session_register ( ) , aby zarejestrować zmienne 'user-
name' i 'pagecount'. Są nadal niezainicjowane po tym wywołaniu.
3. Zwiększamy wartość $pagecount — ponieważ była ona niezainicjowana, jest
interpretowana jako 0; po tej operacji będzie miała wartość 1.
4. Za pomocą funkcji session_id() drukujemy wartość identyfikatora sesji
(w przypadku produkcyjnego serwera jest to działanie niezwykłe — nie jest to
informacja przeznaczona dla użytkownika).
5. Jeżeli strona wysłała do samej siebie zmienną $posted_username (przy uży-
ciu formularza), nazwa jest zapamiętywana w zarejestrowanej zmiennej
Susername. W wyniku Susername jest przenoszone do przyszłych wywołań
skryptu, nawet gdy $posted_username nie zostanie przeniesione.
6. Na koniec ustalamy, że użytkownik może odwiedzić określoną liczbę stron,
zanim sesja zostanie rozpoczęta od nowa. Używamy funkcji session_de-
stroy ( ) , aby usunąć wszystkie dane o zmiennych sesji, co oznacza, że rozpo-
czynamy od nowa zarówno z $pagecount, jak i $username.
Rysunki 25.1, 25.2 i 25.3 pokazują wygląd okna przeglądarki na pierwszym etapie wy-
konywania skryptu z wydruku 25.1, następnie po wysłaniu nazwy przez formularz i po
przejściu przez łącze. W tym przypadku przeglądarka obsługuje cookie, więc w adresie
URL nie pokazują się żadne informacje o sesji.
Zagadnienia konfiguracji
*
Zmienne z tabeli 25.2 należy ustawiać w pliku php.ini, można je również podejrzeć za
pomocą funkcji php_inf o ( ) . W tabeli zamieściliśmy opis i „typowe" wartości (niektó-
re wartości domyślne są zależne od platformy).
Rysunek 25.1.
Strona
testowania sesji
po uruchomieniu
Rysunek 25.2.
Strona
testowania sesji
po przetworzeniu
danych z formularza
Rozdział 25. » Sesje 425
Rysunek 25.3.
Strona
testowania sesji
po przejściu
przez łącze
Tabela 25.2.
Zmienne konfiguracji sesji
Jeżeli sesje nie działają lub powodują błędy, sprawdź ścieżkę zwracaną przez ses-
sion_save_path ( } i upewnij się, że katalog istnieje, a PHP może w nim zapisywać
pliki. Jeżeli nie, powinieneś utworzyć go lub zmienić wartość 'session. save_path'
w pl\kuphp.ini.
Pamiętaj, że funkcje sesji, które jako argumentu wymagają nazwy zmiennej, nie ocze-
kują początkowego $ w nazwie zmiennej.
Jeżeli nagłówki HTTP nie są wysyłane, skrypt może wysyłać tekst (nawet pusty wiersz)
przed wywołaniem funkcji session_start () lub session_register (). Odszukaj
w dołączanych plikach wszystkie puste wiersze lub przesuń funkcje obsługi sesji na po-
czątek pliku.
Podsumowanie
Sesje są użyteczne w sytuacjach, gdy chcesz śledzić działania użytkownika w przypadku
interakcji trwającej dłużej niż wykonanie jednego skryptu. Jeżeli prezentowana zawar-
tość strony zależy od działań podejmowanych na poprzedniej, kod powinien zapamię-
tywać i przenosić te informacje w sposób, który umożliwia odróżnienie poszczególnych
użytkowników. Ponieważ protokół HTTP jest protokołem bezstanowym, prowadzi to
zastosowania technik zastępczych — zwykle albo ukrytych zmiennych (które są nie-
stety kłopotliwe), albo cookies (niektóre przegładarki klientów ich nie obsługują).
W tym rozdziale przedstawimy kilka technik, które zależą od innych elementów inte-
rakcji klient-serwer, takich jak zapisywanie danych w cookie w komputerze klienta lub
jawne zastosowanie nagłówków HTTP do identyfikacji użytkownika lub przekierowa-
nia na inną stronę WWW. Funkcje PHP pozwalające na korzystanie z bezpośredniej
komunikacji HTTP są bardzo proste i jest ich stosunkowo niewiele — problemy wyni-
kaj ą raczej z ograniczeń nałożonych na sam protokół.
Cookie
Cookie to mała cząstka informacji przechowywana w komputerze klienta, albo w pa-
mięci przeglądarki, albo jako mały plik zapisany na dysku. Zawiera on parę nazwa
i wartość. „Ustawianie cookie" oznacza skojarzenie wartości z nazwą i zapisanie tej pa-
ry w komputerze klienta. „Pobieranie" albo „odczytywanie" polega na użyciu nazwy
w celu uzyskania skojarzonej z nią wartości (przeczytaj część „Cookie i prywatność",
w którym opisaliśmy kontrowersje związane z używaniem cookie}.
428 Część III » Techniki zaawansowane
Funkcja setcookie()
Istnieje tylko jedna funkcja związana z obsługą cookie — setcookie ( ) . W tabeli 26.1
zamieszczone są argumenty funkcji. Kolejność w tabeli odpowiada kolejności w wy-
wołaniu funkcji. Wszystkie argumenty oprócz pierwszego są opcjonalne.
Przykłady
Zamieścimy teraz kilka przykładów użycia setcookie ( ) z komentarzem.
setcookie('membername', 'timboy');
Rozdział 26. » Cookie I HTTP______________________________________429
Tabela 26.1.
Argumenty setcookief)
Usuwanie cookie
Usuwanie cookie jest łatwe. Po prostu wywołaj setcookie (nazwa_cookie) bez
podanego drugiego argumentu, które nie przypisuje do wartości cookie pustego ciągu,
ale usuwa je.
Cookies i prywatność
Cookies zawsze budziły kontrowersje z powodu naruszania prywatności.
W czasie pisania tej książki DoubleClick (internetowa agencja reklamowa)
była krytykowana za plany skorelowania danych pochodzących z cookie
z danymi zawartymi w olbrzymiej bazie danych z nazwami konsumentów, ad-
resami i przyzwyczajeniami. Istnieje obawa, że jeżeli klient poda swoje dane
w witrynie, wypełniając formularz, i pozwoli na ustawienie cookie, wszystkie
inne witryny porównujące swoje zapisy z wcześniejszymi witrynami mogą w ten
sposób identyfikować użytkowników (i poznać w ten sposób wiele innych
danych). Jeżeli taka praktyka stałaby się powszechna, każda witryna handlu
elektronicznego, którą odwiedzasz, mogłaby poznać nie tylko twoje nazwi-
sko, adres i zakupione towary, ale również listę innych odwiedzanych stron.
Cookies są również rozsądnym obejściem bezstanowości protokołu HTTP.
Istnieje wiele powodów, żeby rozciągać interakcję pomiędzy klientem i ser-
werem na kilka kolejnych stron, zamiast pakować wszystko do jednego wy-
wołania. Jako projektant witryny możesz zdecydować się na użycie do tego
celu cookie, ponieważ nie występuje tutaj bezpośrednia ingerencja w pry-
watność użytkownika.
Wielu użytkowników tak ustawia swoje przeglądarki, aby odrzucały wszystkie
cookies (pamiętaj, że jest to związane nie tylko z prywatnością, ale również
z dostępem do ich własnych dysków). Każdy kod na serwerze powinien od-
powiednio obsługiwać odrzucenie cookies przez klienta, a witryny powinny
posiadać łatwe do odszukania opisy zasad szanowania prywatności, aby
użytkownik wiedział, czego się może spodziewać.
Rozdział 26. » Cookie I HTTP______________________________________431
Odczytywanie cookie
Cookies automatycznie pojawiają się jako zmienne globalne strony, jeżeli uda się ich
odczytanie. Na przykład ustawienie cookie:
setcookie('membername', 't imboy');
Dzieje się tak, ponieważ, jak wspomnieliśmy w ostatniej wskazówce, cookie nie zostanie
ustawione dopóki odpowiednie nagłówki HTTP nie dotrą do klienta. Ponieważ w przy-
kładzie taka sytuacja nie zaszła, zmienna $membername nie została zainicjowana i bę-
dzie zawierała prawdopodobnie pusty ciąg.
Kolejne skrypty załadowane do tej samej przeglądarki będą mogły odwoływać się do
zmiennej $membername.
Wszystkie wartości zmiennych GET, POST oraz cookie są dostępne jako globalne
zmienne strony już na początku skryptu. Jeżeli dwa lub więcej z tych źródeł posiada
zmienne o tej samej nazwie, jedna lub więcej wartości zmiennej zostanie nadpisana.
Domyślną kolejnością nadpisywania jest: GET, POST, cookie — co oznacza, że argu-
menty POST nadpiszą wszystkie zmienne GET o takiej samej nazwie, a wartości cookie
nadpiszą wartości argumentów POST.
W kodzie tym istnieją trzy możliwe sposoby ustawienia zmiennej globalnej $VarSource:
ze zmiennej POST, ze zmiennej GET oraz z cookie. Nieco pokrętny może się wydawać
sam początek kodu, który ustawia cookie 'VarSource', jeżeli zmienna $ VarSource
Rozdział 26. » Cookie I HTTP______________________________________433
nie jest ustawiona T. cookie, i usuwa cookie, jeżeli jest ustawiona. W przypadku domyśl-
nej kolejności nadpisywania powoduje to kolejne ustawianie i usuwanie cookie w trak-
cie kolejnych przeładowań strony.
Rysunek 26.1.
Pierwsze
załadowanie skryptu
pokazującego
kolejność
nadpisywania
Przy pierwszym załadowaniu strony nie ma ona żadnych argumentów GET ani POST
i mimo że zostało właśnie ustawione cookie, nie jest jeszcze dostępne w tym skrypcie.
W wyniku tego zmienna $VarSource nie została ustawiona.
Na rysunku 26.2 pokazany jest wygląd strony po naciśnięciu przycisku Wyślij za pierw-
szym razem. Tym razem zmienna $VarSource jest zainicjowana przez cookie, ponie-
waż zostało ustawione w czasie pierwszego załadowania strony. Obecnie w rym
konkretnym przypadku wszystkie źródła danych (GET, POST i cookie) dostarczaj ą war-
tości zmiennej $VarSource, nadpisywanej w takiej właśnie kolejności.
Rysunek 26.2.
Wynik dzialania
skryptu
po pierwszym
naciśnięciu Wyślij
434___________________________________Część III » Techniki zaawansowane
Jeżeli jeszcze raz przyciśniemy Wyślij, powinniśmy zobaczyć ekran pokazany na rysun-
ku 26.3. Cookie zostało usunięte w czasie drugiego załadowania, więc wartość argu-
mentu POST nie jest nadpisywana. Zauważ, że w adresie URL znajduje się zmienna
GET, ale za każdym razem jest nadpisywana.
Rysunek 26.3
Wynik dzialania
skryptu po drugim
naciśnięciu Wyślij
Pułapki cookie
Trudno jest zrobić coś źle używając PHP. Ustawienie cookie wymaga wywołania tylko
jednej funkcji (set_cookie ( ) ) , a odczytanie cookie nie wymaga podejmowania żad-
nych działań (ponieważ wartość cookie automagicznie pojawia się jako zmienna glo-
balna). Czy można sobie wyobrazić coś łatwiejszego? Typowe problemy są związane
z samym protokołem HTTP.
Powodem błędnego działania jest to, że HTTP wymaga wysłania nagłówka przed za-
wartością samej strony HTML — nie mogą być wymieszane. Przed wygenerowaniem
jakiejkolwiek zawartości strony PHP musi wiedzieć o wszystkich specjalnych nagłów-
kach i wtedy wysyła je przed rozpoczęciem transmisji zawartości strony HTML. Jeżeli
zdarzy się, że cookie (lub dane o innym nagłówku) wystąpi później, zostanie wygene-
rowany błąd.
Gdy załadujemy ten skrypt, otrzymamy błąd „cannot add header information" (nie
można dodać danych nagłówka). Problem powodowany jest pierwszym znakiem pliku:
spacja przed „<?php". Ponieważ pliki PHP rozpoczynaj ą pracę w trybie HTML, plik ten
spowoduje wygenerowanie jednej spacji i wysłanie jej do klienta, zanim włączony zo-
stanie tryb PHP i będzie wysłane żądanie ustawienia cookie.
Jeżeli kiedykolwiek zdarzy się ten błąd, dosyć łatwo odszukać jego przyczynę. Spróbuj
najpierw przesunąć kod związany z HTTP na początek pliku. Jeżeli mimo tego otrzy-
masz komunikaty o błędach, musisz prześledzić kod wstecz od miejsca, w którym wy-
stąpił błąd, aż do początku pliku. Gdzieś pomiędzy początkiem pliku a miejscem
wystąpienia błędu znajduje się albo kilka znaków interpretowanych w trybie HTML,
albo konstrukcja PHP wypisująca dane. Jeżeli dołączałeś jakieś pliki przed miejscem
wystąpienia błędu, sprawdź, czy występują w nich znaki przed znacznikiem startowym
PHP lub po znaczniku zamykającym blok PHP.
Odrzucenie cookie
Na koniec musimy ostrzec, że setcookie ( ) nie gwarantuje, że cookies będą przyjęte
przez przeglądarkę — setcookieO próbuje tylko wysłać odpowiedni nagłówek
HTTP. To, co stanie się później w komputerze klienta, może zależeć od tego, czy klient
ma odpowiednio nową przeglądarkę, która potrafi obsługiwać cookie, albo że użytkow-
nik z rozmysłem zablokował przyjmowanie cookie.
Składnia funkcji header ( ) jest tak prosta: wymaga jednego argumentu, którym jest
ciąg zawierający nagłówek do wysłania.
Przykład: przekierowanie
Jednym z użytecznych nagłówków HTTP jest nagłówek „Location:", który może słu-
żyć do przekierowania. Należy podać pełny adres URL po ciągu „Location:" i prze-
glądarka rozpocznie odczytywanie danych z tego adresu. Na przykład:
<?php
if ((IsSet(Sgender) SS ($gender == 'kobieta'))
{
header(
"Location: http: //www .troutworks. com/phpbook/ch26/secret. php" ) ;
exit ;
1
?>
<HTMLXHEADXTITLE>Strona dla wszystkich</TITLEX/HEAD>
<BODY>
<H3>Witaj!</H3>
Witamy wszystkich na tej stronie, nawet mężczyzn! Opowiedzcie nam o sobie.
</BODYX/HTML>
ny. Zauważ, że różni się to od selektywnego importowania zawartości przy użyciu wy-
rażenia include — przeglądasz stronę o zupełnie innym adresie URL, niż wpisałeś
w oknie przeglądarki.
Ten rodzaj przekierowaniajest użyteczny w przypadku, gdy chcesz podzielić swoją wi-
trynę WWW na warunkowe „gałęzie" bez konieczności wybierania przez użytkownika
różnych łączy.
if(!isset($PHP_AUTH_USER))
{
header("WWW-Authenticate: Basic realm=\"PHP book\"");
header("HTTP/1.0 401 Unauthorized");
echo "Anulowane przez użytkownika\n";
exit;
)
else
(
if (($PHP_AUTH_USER == 'user') &&
($PHP_AUTH_PASSWD == 'password')) // patrz ostrzeżenie poniżej
print("Możesz wejść<BR>");
else
print("Nie jesteś tu mile widziany<BR>");
}
?>
Powinieneś również wiedzieć, że użycie nagłówków wymaga nie tylko znajomości sa-
mego protokołu HTTP, ale również wiedzy o sposobie dostosowywania się doń różnych
przeglądarek. Jeżeli piszesz skrypty przeznaczone dla szerszego grona, powinieneś wy-
konać więcej testów przeglądarek za pomocą prostych skryptów generujących HTML.
Podsumowanie
PHP oferuje kilka sposobów skorzystania z możliwości protokołu HTTP, oprócz funkcji
tworzenia stron HTML, które są przesyłane dzięki HTTP. Funkcja setcookieO po-
zwala na ustawienie lub usunięcie cookie w przeglądarce użytkownika; wartości takie
będą dostępne na kolejnych stronach jako zwykłe zmienne globalne.
Funkcja header ( ) pozwala na wysłanie dowolnego nagłówka HTTP. Może być uży-
wana na przykład do realizacji uwierzytelniania i przekierowań.
Funkcje HTTP są bardzo proste, a główną przyczyną kłopotów jest sam protokół HTTP.
Jedną z komplikacji jest konieczność wysłania wszystkich nagłówków HTTP przed wy-
słaniem zawartości strony. Jest to bardzo częste źródło błędów w PHP 3 i w wersjach
beta PHP 4. Na szczęście w finalnej wersji PHP 4 wyjście HTTP jest buforowane
w sposób umożliwiający mieszanie nagłówków z zawartością.
440________________________________Część III » Techniki zaawansowane
Rozdział 27.
PHP i JavaScript
W tym rozdziale:
** Tworzenie kodu JavaScript z PHP
* PHP jako zapas dla JavaScript
* JavaScript statyczny kontra dynamiczny
* Dynamiczna generacja formularzy
Jeżeli nigdy nie używałeś języka JavaScript, lektura tego rozdziału nie
wystarczy do nauczenia się go. Zajmujemy się tutaj tylko tymi aspek-
tami JavaScript, które istotnie wpływają na PHP. Jeżeli zastanawiasz
się, co to jest zdarzenie onBiur, polecamy książkę Java Script, Autor:
Arman Danesh, ISBN 83-86718-82-X.
Mimo że PHP potrafi tworzyć dynamicznie strony WWW, jest to jedynie język skryp-
towy serwera. Istnieje duża grupa zadań wykonywanych w witrynie WWW, które
prawdopodobnie nie potrzebuj ą potęgi serwera i najlepiej jeżeli będą wykonane szybko
— na przykład zmiana wyglądu kursora myszy w trakcie przesunięcia nad obiektem.
442___________________________________Część III » Techniki zaawansowane
JavaScript jest językiem skryptów, zorientowanym na klienta (istnieje wersja dla serwe-
ra, ale zakładamy, że już wybrałeś PHP), który może być szybko zintegrowany z PHP.
Również język skryptów klienta JavaScript (znany także jako Javascript, JScript, EC-
MAScript) posiada wiele ograniczeń. Na przykład nie potrafi komunikować się bezpo-
średnio z bazą danych, ani zmieniać się w czasie pracy. Co gorsza, nie można bazować
na technologiach po stronie klienta, ponieważ mogą być zablokowane lub niedostępne
w przeglądarkach użytkowników. Sumienni programiści skryptów muszą albo decydo-
wać o kodowaniu w najbardziej prawdopodobny sposób (i przyjmować narzekania ze
strony mniejszości) lub utrzymywać kilka wersji witryny. PHP może pomóc w obejściu
problemów niespójności technologii klienckich.
Pojedynek obiektów
Prawdopodobnie największą różnicą pomiędzy JavaScript i PHP jest różnica w mode-
lach obiektów. Są one całkowicie różne koncepcyjnie i używaj ą różnych stylów notacji.
Niektórzy mogą uważać to za zaletę, ponieważ nie ma niebezpieczeństwa pomieszania
podobnych obiektów (jest to możliwe na przykład w przypadku ASP i JavaScript).
Wielu będzie uważało to za niekompatybilność, niewygodę lub wadę projektową. Jed-
nak w każdym przypadku nie ma możliwości dostania się do tych samych obiektów za
pomocą PHP i JavaScript.
Wadą modelu obiektowego JavaScript jest jego słaba standaryzacja, mimo że teoretycz-
nie ECMA i W3C wprowadziły międzynarodowe standardy, praktycznie wszyscy pro-
ducenci przeglądarek łamią podstawowe zasady lub dodają nowe. Biegli programiści
JavaScript zużywają wiele energii, śledząc niekompatybilności i rozwiązania proble-
mów dla różnych przeglądarek i platform.
Jak wspominaliśmy w tej książce, klasy PHP są bardziej dodatkiem, modernizacją lub udo-
godnieniem niż istotną częścią języka. Używana notacja jest nazywana stylem „ze strzałką"
lub „wskaźnikiem", styl taki jest spotykany czasami w kodzie C++ ($this->zmienna).
Nie ma sposobu, aby używać notacji z kropką. Zmusza nas to szczerego przyznania, że kla-
sy PHP prawdopodobnie nie rozwiną się do prawdziwego modelu obiektowego.
Pamiętaj, że PHP nie zawsze daje w wyniku PHP — jego końcowym produktem jest
zwykle kod uruchamiany przez inną aplikację, na przykład przeglądarkę.
Istnieje kilka sposobów wysłania kodu JavaScript za pomocą PHP. Najprostszym spo-
sobem jest wyjście z trybu PHP w miejscach, w których musisz umieścić kod wykony-
wany w komputerze klienta. Jest to realizowane dokładnie tak samo, jak przejście do
trybu HTML.
<?php
echo( "Tutaj znajduje się skomplikowany kod PHP."};
?>
<SCRIPT LANGUAGE="JavaScript">
<!-- Ukryj dla przeglądarek nie obsługujących JavaScript
document.write("Oddzielenie skryptów serwera i klienta jest dobrym
^rozwiązaniem. " i
// koniec ukrywania -->
</SCRIPT>
<?php
echo("Kolejny blok PHP");
?>
Nawet ten przykład nie pokazuje pełnej separacji pomiędzy PHP a JavaScript. Wiele
definicji kodu JavaScript jest zwykle umieszczanych na stronie HTML pomiędzy
znacznikami <HEAD> i wywoływanych w części <BODY>, a PHP jest zwykle używany
w tej drugiej części.
Istnieją przypadki, kiedy nie chcemy wyłączać trybu PHP. W takich sytuacjach należy
użyć wyrażeń PHP echo lub print, aby wysłać kod JavaScript.
<?php
echo("Tutaj znajduje się kod PHP");
echo("<SCRIPT ŁANGUAGE=\"JavąScript\">\n");
echo("<!-- Ukryj dla przeglądarek nieobsługujacych JavaScript\n");
echo("document.write(V'Oddzielenie skryptów serwera i klienta jest dobrym
^rozwiązaniem.\\n\")");
echo("// koniec ukrywania -->\n");
echo("</SCRIPT>\n");
echo("Kolejny blok PHP");
?>
Styl taki jest również prawidłowy, ale jest dużo trudniejszy do zrozumienia. Jeżeli nie
jesteś doświadczonym programistą, powinieneś ograniczać stosowanie takiego stylu
tylko do sytuacji, w których wywołujesz predefmiowane funkcje JavaScript, np. zda-
rzenie onSubmit.
<BODY >
<FORM METHOD="post" ACTION="redirect.php">
<SELECT NAME="category" onChange="Browse(this.forra,O)">
<OPTION SELECTED VALUE=0>Wybierz rodząj</OPTION>
<OPTION VALUE="desktop.php">Komputery</OPTION>
<OPTION VALUE="laptop.php">Laptopy</OPTION>
<OPTION VALUE="monitor.php">Monitory</OPTION>
<OPTION VALUE="input.php">Urz. wejściowe</OPTION>
•cOPTION VALUE="storage.php">Pamięci masowe</OPTION>
</SELECT>
<INPUT TYPE="submit" value="Wyślij">
</FORM>
</BODY>
</HTML>
Innym rodzajem operacji na formularzach jest arytmetyka. Tutaj również możesz połą-
czyć JavaScript z PHP w celu zabezpieczenia się z obu stron.
{
var site = form.elements[i].selectedlndex;
if (site>0)
(
top.location = form.elements[i].options[site].value
1
)
// —>
</SCRIPT>
</HEAD>
<BODY >
<FORM METHOD="post" ACTION="redirect.php">
OELECT NAME="category" onChange-"Browse(this.form,0)">
<OPTION S E L E C T E D VALUE=0>Wybierz r o d z a j < / O P T I O N >
<?php
mysql_connect("localhost", "user", "password");
mysql_select_db("site_db");
Squery = "SELECT filename, ray_text FROM categories WHERE display=l";
Sresult = mysql_query(Squery);
while ( list {$f ilename, $my_text) = mysql_fetch__array (Sresult) )
{
print("<OPTION VALUE=\"$filename\">$my_text</OPTION>\n");
)
?>
</SELECT>
<INPUT TYPE="submit" value="Wyślij">
</FORM>
</BODY>
</HTML>
W tym przykładzie będziemy chcieli pomóc użytkownikom odszukać dane na temat róż-
nych samochodów. Lista modeli samochodów wyprodukowanych przez wszystkich pro-
ducentów jest dosyć druga, zbyt długa nawet dla dobrze zaprojektowanej listy rozwijalnej.
Dodatkowo nazwy samochodów są fonetycznie podobne. Jedyną możliwością logicznego
zawężania wyboru jest umieszczenie listy producentów, po wybraniu określonego modelu
ograniczenie ich listy tylko do wyprodukowanych przez wybranego producenta.
Potrzebna nam tabela bazy danych wygląda następująco (nie jest to odpowiedni projekt
relacyjnej bazy danych, ale chcemy skupić się na JavaScript, a nie na bazie danych):
Rozdział 27. » PHP i JavaScript_____________________________________447
1 Producent 1 Model 1
1 Audi 1 A4 1
1 Audi A6 1
| Audi A8 l
1 Audi 1 Quattro 1
1 Chrysler I Cirrus |
1 Chrysler 1 Concorde 1
1 Chrysler 1 PT Cruiser 1
1 Toyota 1 Camry 1
| Toyota 1 Corolla !
1 Toyota 1 Rav4 1
Używając tej bazy danych i skryptów serwera w PHP będziesz ograniczony do dwóch
możliwości wyboru. Możesz stworzyć jedną dosyć dużą listę (albo w postaci listy roz-
wijalnej, albo w postaci strony) producentów i modeli, albo pokazać użytkownikowi
dwa kolejne formularze. Jeżeli dodamy do tego JavaScript, możemy na górze strony
umieścić dwie listy rozwijalne, zmieniając zawartość drugiej listy na podstawie wyboru
w pierwszej.
Nasz projekt podwójnej listy rozwijalnej bazuje na sprytnym kodzie JavaScript, którego
autorem jest Andrew King. Dostępny jest pod adresem www.webreference.com na wa-
runkach licencji Gnu.
Użyliśmy PHP, aby podłączyć się do bazy danych i przepisać dane do dwuwymiarowej
tablicy, na której pracuje kod JavaScript. JavaScript jest odpowiedzialny za zachowanie
się strony.
<HTML>
<HEAD>
<META NAME="save" CONTENT="history">
<STYLE>
.saveHistory (behavior : url ( łdef aultftsavehistory) ; (
</STYLE>
<SCRIPT LANGUAGE="JavaScript">
v=false;
//-->
</SCRIPT>
<SCRIPT LANGUAGE="JavaScript">
< !--
// Uniwersalne menu zależne - Autor Andrew King
// Zmodyfikowane przez Joyce Park
// Dostępne na zasadach licencji Gnu
//
// Universal Related Select Menus - cascading popdown menus
// by Andrew King. vi.34 19990720
// Copyright (c) 1999 internet.com LLC. All Rights Reserved.
// Modified by Joyce Park 20000703
//
// This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later
448___________________________________Część III » Techniki zaawansowane
// version.
//
// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free
// Software Foundation, Inc., 59 Temple Place, Suite 330,
// Boston, MA 02111-1307 USA
//
// Originally published and documented at www.webreference.com
// see www.webreference.com/dev/menus/intro2.html for changelog
if(v)(a=new Array(22);)
// Wypełnienie tablicy
<?php
mysql_connect ("localhost", "db__user" ) ;
mysql_select_db("auto_db");
// Pobranie producentów
Smake_query = "SELECT DISTINCT make FROM cars";
$make_result = mysql_query($make_query);
\
Rozdział 27. » PHP i JavaScript_____________________________________449
$i = 0;
while ($make_row = mysql_fetch_array($make_result)) {
$make[$i] =• Smake_row[0] ;
// Wypełnianie tablicy modelami każdego producenta
echo "newCat();\n";
$model_query = "SELECT model FROM cars WHERE make = '$make[$i)' ORDER BY
^model";
$model_result = mysql_query($model_query);
while(list(Smodel) = mysql_fetch_array($model_result)) (
echo "0 ( V'SmodeiV , V'/Smodel. php\" ) \n" ;
)
echo "\n";
$i + + ;
)
?>
} // if (v)
function relate(formName,elementNum,j) {
if(v)(
var formNum = getFormNum{formName);
if (formNum>=0) (
formNum++; // Referencja do następnego formularza
with (document.forms[forraNum].elements[elementNum]) {
for(i=options.length-1;i>0;i—) options[i] = null; // zerowanie w
odwrotnej kolejności (obejście błędu)
for(i=0;i<a[j].length;i++)(
options[i] = new Option(a[j][i].text,a[j][i].value);
)
options[0].selected = true;
}
t
} else (
jmp(formName,elementNum);
)
)
// — >
</SCRIPT>
</HEAD>
<BODY BGCOLOR="ttff ffff ">
<CENTER>
<TABLE BGCOLOR="#DDCCFF" BORDER="0" CELLPADDING="8" CELLSPACING="0">
<TR VALIGN="TOP">
<TD>Wybierz producenta:<BR>
<FORM NAME="fl" METHOD="POST" ACTION="redirect.php" onSubmit="return false;">
<SELECT NAME="ml" ID="ml" CLASS=saveHistory
onChange="relate(this,form,0,this.selectedlndex)">
<?php
while (list ($key, Sval) =• each ($make) ) (
echo "<OPTION VALUE=\"/Sval.php\">$val</OPTION>\n";
)
?>
</SELECT>
<INPUT TYPE=SUBMIT VALUE="Go" onClick="jmp(this.form,0);">
450___________________________________Część III » Techniki zaawansowane
</FORM>
</TD>
<TD>Wybierz model:<BR>
<FORM NAME="f2" METHOD="POST" ACTION="redirect.php" onSubmit="return false;">
<SELECT NAME="m2" ID="m2" CLASS=saveHistory onChange="jmp(this.form,0)">
// To są wartości zajmujące miejsce przy pierwszym załadowaniu
// strony. Nie zmieniają się przy zmianie wartości w formularzu
// Jeżeli usuniesz je, formularz nadal będzie działał, ale
// druga lista rozwijalna będzie pusta do chwili zmiany.
// Wartości te mogą być generowane dynamicznie, ale chcieliśmy
// pokazać je w miejscu, w którym muszą się znajdować.
<OPTION VALUE="/A4.php">A4</OPTION>
<OPTION VALUE="/A6.php">A6</OPTION?
<OPTION VALOE="/A8.php">A8</OPTION>
<OPTION VALUE="/Quattro">Quattro</OPTION>
</SELECT>
<INPUT TYPE=SUBMIT VALUE="Go" onClick="jmp(this.form,0);">
<INPUT TYPE="hidden" NAME="baseurl" VALUE="http://localhost">
</FORM>
</TD>
</TR>
</TABLEX/CENTER>
</BODY>
</HTML>
Jeżeli zmienisz lub dodasz jakieś dane w bazie danych, automatycznie zostanie zmie-
niona część JavaScript. Dynamiczna integracja nowych danych powoduje, że jest to
świetne narzędzie pozwalające ograniczyć do minimum prace nad utrzymaniem strony.
main.html
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<! —
function confirmOne(boxVals} {
var count = 0;
for (var x = 1; x < boxVals.length; x++) {
if (boxVals[x].checked == false) {
count++;
1
(
if (count == (boxVals.length - 1)) {
boxVals[0].checked = true;
} else (
boxVals[0].checked = false;
)
)
function toArray(boxVals) {
for (var x = 0; x < boxVals.length; x++) {
var valArray = boxVals[x].name+"[]";
boxVals[x].name = valArray;
)
t
// -->
</script>
</HEAD>
<TABLE CELLPADDING=20>
<TR>
<TD VALIGN="top">
< B > Z a m a w i a m kanapkę z . . . < / B >
<BRXBR>
<FORM N A M E = " s e l e c t o r " T A R G E T = " r e s u l t s " METHOD="post" A C T I O N = " r e s u l t S . p h p " >
< B > D o d a t k i ( z a z n a c z jeden lub wiece j ) </BXBRXBR>
< I N P U T TYPE="checkbox" n a m e = " f i l l i n g " value="wszystko" checked
o n d i c k = " d e s e l e c t A l l O t h e r s (document . s e l e c t o r , f i l l i n g ) ;
confirmOne(document.selector.filling); toArray(document.selector.filling);
s u b m i t ( ) ; " > Wszystko
<BR>
<INPUT TYPE="checkbox" name="filling" value="indyk"
onClick="confirmOne(document.selector.filling);
toArray(document.selector.filling); submit));"> Indyk
<BR>
<INPUT TYPE="checkbox" name="filling" value="pieczen"
onClick="confirmOne(document.selector.filling);
toArray(document.selector.filling); submit();"> Pieczeń
<BR>
<INPUT TYPE="checkbox" name="filling" value="wedzonka"
onClick="confirmOne(document.selector.filling);
toArray(document.selector.filling); submit(};"> Wędzonka
<BR>
<INPUT TYPE="checkbox" name="filling" value="jajko"
onclick="confirmOne(document.selector.filling);
toArray(document.selector.filling); submit();"> Jajko
<BR>
</TD>
< T D VALIGN="top"XBRXBR>
<B>Sery</B><BRxBR>
<SELECT NAME="cheese" o n C h a n g e = " s u b m i t ( ) ; " >
<OPTION VALUE="bez sera">Bez sera</OPTION>
<OPTION VALUE="cheddar">Cheddar</OPTION>
<OPTION VALUE="Swiss">Swiss</OPTION>
<OPTION VALUE="camembert">Camembert</OPTION>
<OPTION VALUE="bleu">Blue</OPTION>
<OPTION VALUE="twarożek">Twarożek</OPTION>
</SELECT>
<BRXBR>
</TD>
<TD VALIGN="top"XBRXBR>
<B>Chleb</BXBRXBR>
452___________________________________Część III » Techniki zaawansowane
results.php
<HTML>
<HEADX/HEAD>
Formularz ten nie robi zbyt wiele, ale jego celem było pokazanie sposobu przesyłania
danych z klienta do serwera i odwrotnie. Przedstawia również inne interesujące efekty,
jakie może uzyskać programista PHP eksperymentując z JavaScript.
Podsumowanie
JavaScript jest językiem skryptowym, wykonywanym w przeglądarce klienta, który jest
bardzo efektywny w wielu zastosowaniach niewymagających interakcji z serwerem. Nie
wszyscy chcą używać JavaScript z powodu problematycznej użyteczności i zagadnień
bezpieczeństwa. Jednak ci, którzy się nań zdecydują się, połączenie języków skrypto-
wych serwera i klienta zaowocuje interesującą kombinacją funkcji.
Rozdział 27. » PHP i JavaScript_____________________________________453
PHP i JavaScript mają różne notacje obiektowe. JavaScript używa tak zwanej notacji
„z kropką", natomiast PHP używa stylu C++ „ze strzałką". JavaScript jest językiem
obiektowym, a PHP traktuje obiekty jako funkcję dodatkową. Dobrą no winą jest to, że
nigdy nie pomylisz obiektów PHP i JavaScript. Złą, że nie ma dostępu do tych samych
obiektów za pomocą obu języków.
Możliwe jest stworzenie jakiejś funkcji zarówno z wykorzystaniem serwera, jak i klienta.
Użytkownicy z przeglądarkami obsługującymi JavaScript mogą korzystać z dużej szybko-
ści i wygody, inni nie stracą dostępnych funkcji. Pozwala to na użycie JavaScript, omija-
jąc jego istotną wadę, jakąjest uniemożliwienie pracy części użytkowników.
Najcenniejszą funkcją, jaką PHP może podarować JavaScript, jest możliwość korzysta-
nia z baz danych, co w wyniku daje „dynamiczny JavaScript". JavaScript to technologia
czysto kliencka, która nie może korzystać z danych z bazy w celu wygenerowania za-
wartości strony. Bez pomocnika na serwerze, na przykład PHP, JavaScript ręcznie mo-
dyfikować przy każdej zmianie danych. Zdolność PHP do przekazania aktualnych
zmiennych powoduje, że utrzymanie kodu JavaScript jest nieco mniej pracochłonne.
454________________________________Część III » Techniki zaawansowane
Rozdział 28.
E-mail
W tym rozdziale:
+ Informacje na temat architektury e-mail
* Pobieranie poczty za pomocą PHP
* Wysyłanie poczty za pomocą PHP
4 Więcej na temat aplikacji pocztowych
* Problemy przy korzystaniu z poczty
W tym rozdziale zajmiemy się użyciem PHP (i w niektórych przypadkach bazy danych)
do wysyłania i odbierania poczty. Zakładamy jedynie podstawową znajomość systemu
e-mail i protokołów POP, IMAP oraz SMTP.
Przyjrzyjmy się tym częściom dokładniej. Aby łatwiej zapamiętać zadania poszczegól-
nych części, spróbujemy użyć metafory: rezydencja Mail Server.
Serwer TCP/IP
Dobrym sposobem na przedstawienie serwera TCP/IP jest porównanie go do odźwier-
nego, odpowiadającego na pukanie do drzwi do rezydencji Mail Server. Właściwie je-
żeli porównasz każdy port do drzwi, być może bardziej trafne było by porównanie go
do ochroniarza, który siedzi w sterowni i za pomocą zestawu kamer kontroluje wiele
alejek wejściowych i wyjściowych. Jednak prostsze i bardziej obrazowe jest porówna-
nie do staroświeckiego odźwiernego przy drzwiach wejściowych.
Odźwierny jednak nie rozmawia z gośćmi. Jego zadaniem jest tylko otwarcie drzwi, je-
żeli ktoś puka, i przywołanie właściwej osoby (nazwijmy ją lokajem), który obsłuży go-
ścia. Rolą tej osoby jest dowiedzenie się, czego gość chce.
Rozdział 28. » E-mail___________________________________________457
Otwieranie drzwi nie jest problemem — trzeba wiedzieć, że istnieją drzwi, które można
otworzyć. Serwer TCP/IP posiada listę różnych usług, za które jest odpowiedzialny,
i odpowiada na każde żądanie, wywołując odpowiedni demon — w przypadku poczty
sendmail albo serwer POP/IMAP. Pozwala to na oszczędniejszą gospodarkę zasoba-
mi, ponieważ jeden demon monitoruje wszystkie wywołania, inne demony są urucha-
miane tylko, gdy są potrzebne.
Najważniejszym zadaniem MTA jest pobieranie poczty z innego serwera SMTP i do-
starczanie jej do kolejki poczty dla właściwego adresu. Bierze w tym udział o wiele
więcej części, niż ci się wydaje, ale nie będziemy ich wszystkich tutaj opisywać. MTA
zbiera dodatkowo pocztę wychodzącą i próbuje wysłać ją do innych serwerów SMTP.
Protokół SMTP to język skryptu, według którego jeden serwer pyta się drugiego „Kim
jesteś i czego chcesz", wygląda podobnie jak poniższy (nazwa komputera wysyłającego
i odbierającego):
220 receipthost ESMTP
HELO
250 receipthost
MAIL From:<sender@sendhost>
250 ok
RCPT To:<receiver@receipthost>
250 ok
DATA
345 Go ahead
Body of message.
Kolejka poczty
Srebrna taca, na której lokaj przenosi listy, jest odpowiednikiem kolejki pocztowej lub
skrzynki pocztowej. Różne systemy poczty używają rozmaitych rodzajów skrzynek
pocztowych. Główną różnicą na omawianym przez nas poziomie jest to, że nowa prze-
syłka jest umieszczana na końcu bardzo długiej kolejki (jak kartka papieru z tekstem
wszystkich przesyłek, jakie dotychczas otrzymałeś, zapisanych w kolejności otrzymy-
wania) lub staje się plikiem tekstowym w katalogu.
W wielu przypadkach wybór klienta poczty jest ściśle związany z wyborem MTA oraz
skrzynki pocztowej. Na przykład Mutt ułatwia korzystanie z formatu skrzynki maildir,
używanego przez qmail i dlatego wydaje się najlepszy dla użytkowników systemu Qmail.
Wysyła się wtedy co jakiś czas żądanie do rezydencji Mail Server, aby inny lokaj prze-
słał wszystkie listy zgromadzone do tej pory na srebrnej tacy. Lokaj ten nie przyjmuje
listów —jego zadaniem jest tylko ich przesyłanie.
Ten drugi lokaj musi być bardzo sumienny. Lokaj SMTP przesyła i odbiera tylko listy
adresowane do mieszkańców, ale przecież każdy może wysłać żądanie przesłania
poczty w jakieś odległe miejsca. Dlatego lokaj numer dwa zna tajne hasła wszystkich
mieszkańców rezydencji Mail Server i nie prześle żadnego listu, jeżeli nie otrzyma wła-
ściwego hasła.
Przesyłki e-mail można czytać w zdalnych klientach pocztowych — wiele z nich jest
bazuje na witrynie WWW, istnieją także dostępne w postaci samodzielnych bezpłatnych
programów:
Microsoft Outlook Express http://www.microsoft.com/windows/IE;
Netscape Communicator http://www. netscape, com/download/index, html;
Qualcomm Eudora Light http://www.eudora.com;
Hotmail http://www.hotmail.com;
AOL mail client http://www.aol.com.
Istniej ą różne protokoły służące do pobierania poczty oraz olbrzymia liczba produktów
(często różniących się) korzystających z tych protokołów. Najbardziej popularnymi są
POP3 i IMA P.
Serwer POP sprawdza nazwę użytkownika, hasło i położenie kolejki poczty, porównu-
jąc odpowiedzi użytkownika z zapisanymi u siebie prawidłowymi wartościami. Dialog
z klientem wygląda następująco:
user peter
+OK
pass rabbit
+OK
data
Jeżeli istnieją nowe listy, zostają wysłane do klienta. Niektóre systemy POP mogą zo-
stać tak skonfigurowane, aby zapamiętywać kopie listów wysłanych do klienta, niektóre
pozwalają usuwać wiadomości zdalnie, ale większość zapomina o przesyłkach zaraz po
ich wysłaniu. Użytkownik może dużo czasu spędzić na czytaniu pobranych listów oraz
pisaniu odpowiedzi, potem połączyć się z serwerem SMTP i wysłać odpowiedzi.
IMAP jest nowszym i lepszym protokołem pobierania poczty. Zamiast usuwać wiado-
mości po pobraniu, IMAP symuluje lokalne działanie klient-serwer. Potencjalną wadą
jest to, że każdy klient musi być podłączony do serwera IMAP w czasie, gdy użytkow-
nik czyta pocztę i odpowiada na nią, więc proces ten wymaga większych zasobów.
Serwer IMAP jest również nieco trudniejszy w instalacji, konfiguracji i utrzymaniu.
Mamy teraz cały model serwera poczty. Po dokładnym zdefiniowaniu terminologii mo-
żemy przejść do dalszej części rozdziału.
Nigdy nie myśleliśmy nawet o napisaniu takiego projektu od podstaw; każdy, kto ma
taki zamiar, na pewno nie potrzebuje naszych porad. Dlatego dyskusja o klientach
pocztowych skupi się na tym, jak zrobić to łatwo i przyjemnie (jak mówi nieśmiertelna
Tina Turner).
Rozdział 28. » E-mail___________________________________________461
Jeszcze lepszym produktem jest wspaniały klient IMAP o nazwie IMP, który jest do-
stępny na warunkach licencji GPL. Autorami są ludzie skupieni w Horde.org:
http://w ww. hordę, org/imp
W chwili obecnej IMP posiada również obsługę POP3 z wykorzystaniem funkcji PHP
imap_open ( ) , ale jest to tylko dodatek. Trudno sobie wyobrazić, w jaki sposób można
ulepszyć ten program. W chwili pisania książki przenoszono go na PHP 4, ale nadal jest
dostępna wersja IMP dla PHP 3.
Jest to szczególnie użyteczne dla tych, którzy chcą udostępnić firmowego klienta pocz-
towego przez WWW —jak większość dostawców Internetu. Pamiętaj jednak, że przy-
właszczenie cudzej pracę nie jest w porządku — wystarczy mała informacja w formie
logo lub adresu e-mail.
462___________________________________Część III » Techniki zaawansowane
Konfiguracja Windows
Musisz ustawić dwie zmienne w pliku php. ini:
* SMTP: ciąg zawierający nazwę DNS lub adres IP serwera SMTP, który będzie
wysyłał pocztę z komputera Windows z zainstalowanym PHP. Jeżeli znajduje
się on na tym samym serwerze, należy podać 'localhost';
4 sendmail_f rom: ciąg zawierający adres e-mail domyślnego użytkownika wy-
syłającego pocztę przez PHP (na przykład info@domena.com).
Konfiguracja Unixa
Musisz sprawdzić i być może zmienić zawartość jednej zmiennej vi php.ini: sendma-
il_path. Jest to pełna ścieżka do programu sendmail (/usr/sbin/sendmail lub /usr/lib/
sendmail) lub jego zamiennika (/var/qmail/bin/sendmail).
Jeżeli nie używasz programu sendmail i nie zmienisz opcji konfiguracji tak, aby wska-
zywała na właściwy program, przesyłanie poczty może być bardzo powolne. Serwer
będzie próbował uruchamiać kilka kolejnych alternatywnych programów, czekając jakiś
czas na odpowiedź..
W systemie Unix PHP 4 używa użytkownika root jako domyślnego użytkownika pocz-
ty. PHP 3 korzystał z użytkownika PHP (zwykle Nobody), ale wiele serwerów odrzu-
cało pocztę pochodzącą z tego konta.
Funkcja maił
W PHP istnieje tylko jedna funkcja do wysyłania poczty: maił ( ) . Funkcja ta (typu bo-
olean) próbuje wysłać pocztę, korzystając z danych w nawiasach, i zwraca O lub 1.
Rozdział 28. » E-mail___________________________________________463
Najprostszym użyciem tej funkcji (podany adres jest fikcyjny i nie powinieneś go uży-
wać do testowania) jest:
<?php
mail("receiver@receipthost.com", "Przykładowy temat", "Treść komunikatu\r\nz
"^wierszami oddzielonymi znakami nowego wiersza.");
?>
Jest to domyślny i minimalny format: adres i odbiorca, wiersz tematu i treść. W tym
przypadku PHP doda automatycznie linię From: root@sendhost do nagłówka każdej
wiadomości.
Jeżeli chcesz wysłać przesyłkę do wielu adresatów, wpisz ich adresy rozdzialejąc je
przecinkami (nie wszystkie serwery mają możliwość obsługi):
<?php
$mailsend = maił("jan21@polbox.pl, j23@aol.com", "Przykładowy temat", "Treść
^komunikatu\r\nz wierszami oddzielonymi znakami nowego wiersza.");
print("$mailsend");
?>
Wiele osób chciałoby precyzyjniej kontrolować adres, wygląd i format przesyłek e-mail.
Można to zrealizować dzięki dodaniu nagłówków po trzech domyślnych nagłówkach.
<?php
Smailsend = maił("receiver@receipthost.com", "Przykładowy temat", "Treść
"^komunikatu.", "From: me@sendhost\r\nbcc: phb@sendhost\r\nContent-
"^type:text/plain\r\nX-mailer: PHP/". phpversion());
print("$mailsend");
?>
Pole „dodatkowych nagłówków" jest nieco dziwne, ponieważ zawiera kilka typów da-
nych, które normalnie powinny mieć własne pola. Naszym zadaniem nie jest zastana-
wianie się, dlaczego tak jest, tylko podanie rodzaju informacji, jaki możesz umieścić
w tym polu:
* twój adres e-mail;
* adres reply-to lub bounce-to;
464___________________________________Część III » Techniki zaawansowane
** adresy cc i BCC.
Tego rodzaju formularz do wysyłania poczty może być użyteczny we wielu przypad-
kach: wysyłania głosów, raportowania błędów i korzystania z pomocy technicznej.
<html>
<head>
<title>Formularz: "Co to był za dreszczowiec"</title>
</head>
<body>
<center>
<table width="550">
<tr bgcolor= # F F 9 9 3 3 x t d a l i g n = " c e n t e r " X B R x H 3 > T h e T h r i l l e r G u i d e . c o m
<BR>"Co to był za d r e s z c z o w i e c " < B R > < / H 3 > < / t d x / t r >
<trxtd>
Czy czytałeś kiedyś niezapomniany dreszczowiec, a teraz nie pamiętasz jego
tytułu? Wypełnij możliwie dużo z poniższych pól i kliknij przycisk Wyślij.
Spróbujemy przeszukać nasze zasoby i odeślemy ci odpowiedź.
</tdx/tr></table>
</center>
<FORM METHOD=post ACTION="titlehelp.php">
<P>Imię: <input type="text" size=30 name="FirstName">
<P>Nazwisko: <input type="text" size=30 name="LastName">
<P>Twój adres e-mail: <input type="text" size=30 name="Email">
<P>W którym roku dzieje się akcja? <input type="text" size=4 name="Year">
<P>Czy pamiętasz jakieś fakty? <input type="text" size=30 name="Setting">
<P>Bohaterami książki byli: <br>
<ul>
<input TYPE="radio" NAME="Gender" VALUE=l>Kobieta<br>
<input TYPE="radio" NAME="Gender" VALUE=2>Mężczyzna<br>
<input TYPE="radio" NAME="Gender" VALUE=3>Para<br>
<input TYPE="radio" NAME="Gender" VALUE=4>Dwóch mężczyzn<br>
<input TYPE="radio" NAME="Gender" VALUE=5>Dwie kobiety<br>
</ul>
<P>Gdy książka się ukazała, była: <br>
<ul>
<input TYPE="radio" NAME="Status" VALUE=l>bestselerem<br>
<input TYPE="radio" NAME="Status" VALUE=2>ulubieńcem krytyków<br>
<input TYPE="radio" NAME="Status" VALUE=3>niezauważona<br>
<input TYPE="radio" NAME="Status" VALUE=4>nie wiem<br>
</ul>
<P>Opowiedz, c o p a m i ę t a s z ( a k c j a , b o h a t e r o w i e , w e r s j a filmowa i t p . ) :
<brxtextarea NAME="Other" ROWS=6 COLS=50x/textarea>
<Pxinput type="submit" name="Submit" value="Wyślij ">
</body>
</html>
<html>
<head>
<title>ThrillerGuide: TitleHelp.php</title>
</head>
<body>
<?php
/* Jeżeli chcesz, możesz zapisać te informacje do bazy danych */
$formsent = maił("help@thrillerguide.com", "Jaki tytuł nosił dreszczowiec?",
"Pytanie od: SLastName SFirstName\r\nRok: $Year\r\nFakty:
$Setting\r\nBohaterowie: $Gender\r\nSukces: SStatus\r\nInne szczegóły
identyfikacyjne: SOther", "From: SEmail\r\nBounce-to: help3thrillerguide.com");
if($formsent == 1)
j
print("<P>Cześć, SFirstName. Dostaliśmy twoje wołanie o pomoc. Spróbujemy
odpowiedzieć w ciągu 24 godzin. Dziękujemy za wizytę w ThrillerGuide.com!");
t
else
print("Przepraszamy, wystąpił problem z formularzem. Proszę spróbować
jeszcze raz.");
466___________________________________Część III » Techniki zaawansowane
?>
</body>
</html>
<BODY>
<CENTERXTABLE W I D T H = 5 5 0 X T R X T D >
Rozdział 28. » E-mail___________________________________________467
email_send.php
<html>
<head>
<title>Ekran wysyłania poczty</title>
</head>
<body>
<?php
/* Ta strona wprowadza nazwy, adresy i hasła do bazy danych
oraz wysyła e-maile. */
include("password_maker.inc");
mysql_connect("localhost", "root") ;
mysql_select_db("mailinglist");
$list_length = 0;
/* Zawiera warunek zatrzymania w przypadku wprowadzenia mniej niż 25 nazw. */
for ($list_length = 0; $list_length <= 24 &&
STRLEN(SFirstName[$list_lengthJ) > 0; ++$list_length)
(
/* Wymagamy 8 znaków w haśle. */
SPassword = random^string{Scharset, 8);
$query = "INSERT INTO recipient (FirstName, LastName, Email, Password)
VALUES('$FirstName[Slist_length]', 'SLastName[$list_length]',
'SEmail[$list_length]', $Password)";
$result = mysql_query(Squery);
$formsent = mail($Email[$list_length], "Informacja o użytkowniku i haśle",
"Twoja nazwa użykownika to: SFirstName[$list_length]
$LastName[Slist_length].\r\nUpewnij się, że jest tylko jedna spacja
pomiędzy imieniem i nazwiskiem w trakcie logowania.\r\nHaslo:
SPassword.\nPowodzenia!", "From: mailinglist@sendhost\r\nReply-to:
help@sendhost");
printC'Wynik dla Slist_length jest $f ormsent. \n") ;
(
?>
</body>
</html>
password_maker.inc
<?php
/* Wywoływana jest funkcja random_string. Używa ona random_char */
468___________________________________Część III » Techniki zaawansowane
function random_char($string)
f
$length = strlen($string);
Sposition = mt_rand(0, $length - 1);
return($string[$position]);
}
5charset = "abcdefghijklmnopqrstuvwxyz";
?>
Podsumowanie
Poczta należy do najbardziej użytecznych i atrakcyjnych aplikacji internetowych. PHP
pozwala zarówno na wysyłanie, jak i odbieranie przesyłek e-mail z poziomu strony
WWW. Jednak PHP nie jest wyspecjalizowanym programem, prawdopodobnie nie ob-
służy dużej liczby przesyłek.
Co to jest XML?
XML jest skrótem od extensible Markup Language. XML jest uproszczoną formą
SGML, Standard Generalized Markup Language, nie musisz jednak nic wiedzieć na
temat SGML, aby używać XML. Definiuje on składnię dla strukturalnych dokumentów,
które są czytelne zarówno dla komputera, jak i człowieka.
Najłatwiejszym sposobem zrozumienia XML jest pomyślenie o tym, czego nie potrafi
HTML. HTML jest również językiem bazującym na znacznikach, ale dokumenty
HTML nie są strukturalne. Znaczniki HTML (technicznie nazywane elementami)
i atrybuty są prostymi znacznikami identyfikacyjnymi dla przeglądarki. Na przykład pa-
ra znaczników <H1> i < / H l > oznacza nagłówek najwyższego poziomu. Przeglądarka
interpretuje go jako tekst wyświetlany dużą, grubą i być może pochyłą czcionką. HTML
470___________________________________Część III » Techniki zaawansowane
nie wskazuje jednak, czy tekst pomiędzy tymi znacznikami jest tytułem strony, nazwi-
skiem autora, przywitaniem, obietnicą specjalnych cen sprzedaży. Jest to tylko fragment
tekstu, który ma być napisany dużymi literami.
Nasz opis XML musi być bardzo skrótowy (ponieważ jest to książka
na temat PHP, a nie XML). Osobom, które chcą dowiedzieć się więcej
na ten temat, polecamy książkę XML. Księga eksperta. Autor: Elliote
Rusty Harold, ISBN: 83-7197-275-X. Mimo że książka ta nie jest po-
radnikiem na temat tworzenia aplikacji na bazie XML, przedstawimy
podstawowe zasady i koncepcje.
Jedną z konsekwencji braku struktury HTML jest to, że wyszukiwarki dostają niewiele
wskazówek, co jest ważne na każdej stronie lub jakie są relacje między poszczególnymi
fragmentami tekstu. Aby to odgadnąć, używają różnych metod, ale żadna z nich nie jest
odporna na błędy. Nadużywa się znaczników <META>, witryny o treściach pornogra-
ficznych bardzo często umieszczają popularne, niezwiązane z prawdziwą zawartością
słowa kluczowe w nagłówkach, aby oszukać nieuważnych użytkowników WWW. Je-
żeli XML stanie się wszechobecny, może wyeliminować wiele z tych problemów.
Następnym pytaniem na temat XML jest zwykle „do czego XML jest podobny". Fak-
tycznie jest podobny do HTML. Prosty plik XML, jak ten pokazany na wydruku 29.1,
jest łatwy do zrozumienia dla użytkowników HTML.
Znaki &, <, >, ' oraz " są znakami specjalnymi w XML. Możesz użyć
ich w danych oznaczając je za pomocą kodów, takich jak &,
⁢ lub umieszczając je w sekcji CDATA w sposób opisany poniżej.
Dokumenty XML wy stępuj ą zwykł e w postaci tekstu. Mogą zawierać dane binarne, ale
nie są dla nich przeznaczone. Jeżeli chcesz umieścić dane binarne w dokumencie XML,
musisz je najpierw zakodować i dekodować przy odczytywaniu. Zwróć uwagę, że dołą-
czanie danych binarnych może naruszyć niezależność od platformy czystego XML.
472___________________________________Część III » Techniki zaawansowane
Praca z XML
Prawdopodobnie zadajesz sobie pytanie: co mogę z tym zrobić. Odpowiedź nie jest ła-
twa. Teoretycznie za pomocą XML możesz realizować trzy główne zadania: manipula-
cje i zapisywanie danych, przesyłanie danych pomiędzy aplikacjami lub pomiędzy
firmami oraz wyświetlanie stron XML w przeglądarce, z użyciem arkuszy stylów do
zdefiniowania dyrektyw wyświetlania.
W praktyce prawie nikt nie używa XML jako podstawowej składnicy danych w czasach,
gdy wszechobecny jest SQL. Możliwe jest, choć ciągle skomplikowane, manipulowanie
danymi przy użyciu XML — można edytować dokument tworząc lub zmieniając węzły
XML, zamiast zmiany tekstu. Możliwe jest (ale pracochłonne) wyświetlanie prostego do-
kumentu XML przy użyciu arkuszy stylu. W rzeczywistości niewiele witryn kor2ysta
z plików XML zamiast HTML. Więcej informacji na temat wyświetlania XML znajduje
się w dopisku „Obietnice i pułapki przy wyświetlaniu XML".
PHP może również czytać dane z jakiegoś źródła i zapisywać je do pliku XML. Pomaga
to przy przenoszeniu zawartości witryny WWW do innej. Możesz również skorzystać
z tej funkcji, aby użytkownicy „nietechniczni" mogli tworzyć odpowiednio sformato-
wane dokumenty XML za pomocą formularzy WWW. Obecnie zapisywanie XML jest
najczęstszym zastosowaniem skryptów PHP.
Dokumenty i DTD
Jak poprzednio mówiliśmy, wymagania formatowania XML są minimalne. Ale doku-
menty XML mają dodatkowy poziom kontroli poprawności. Prawidłowy dokument XML
to taki, który spełnia wszystkie zasady znane jako definicja typu dokumentu (DTD).
Rozdział 29. » PHP i XML_________________________________________473
Aby docenić wartość DTD, wyobraź sobie, że jesteś szefem projektu open source two-
rzącego książki i inne dokumenty, dostępne bezpłatnie w Internecie w postaci elektro-
nicznej. XML niezwykle cię interesuje; wydaje się, że spełnia twoje potrzeby jako
format wymiany danych, łatwy do zaadoptowania w powstających właśnie technolo-
giach wyświetlania. Członkowie twojej grupy przegłosowali przekodowanie wszystkich
książek i dokumentów projektu na postać XML oraz szybką XMLizacje wszystkich
rozpoczętych dokumentów.
Oba te pliki XML korzystają z podobnej, ale nie identycznej struktury hierarchicznej,
używając podobnych znaczników. Jest to wada definiowanych przez siebie znaczników:
474___________________________________Część III » Techniki zaawansowane
Definicja typu dokumentu, albo DTD, opisuje strukturę klasy dokumentów XML. DTD
jest pewnego rodzaju formalnym ograniczeniem gwarantującym, że wszystkie doku-
menty tego typu będą spełniały założone reguły struktury i konwencje nazw. DTD po-
zwala dokładnie określić, jakie elementy są dozwolone, jaka jest ich relacja, jaki jest typ
każdego elementu i jaka jest jego nazwa. DTD pozwala również na określenie, które
elementy są obligatoryjne, które opcjonalne, jakie są ich wartości domyślne.
Możesz dać kopię tego pliku każdemu, kto będzie jej potrzebował. DTD jest po prostu
zwięźle zapisanym, dokładnie zdefiniowanym plikiem gramatyki. Jest użyteczne doda-
nie takiej dyscyplinującej opcji do pliku XML, gdyż jego „samodefiniowalna" natura
może wprowadzać chaos. Jeżeli grupa ludzi zgodzi się na taką postać DTD, to dobra
droga do ustanowienia standardu dla wszystkich danych określonego typu.
Plik XML można przepuścić przez tak zwany „analizator poprawności", który stwier-
dzi, czy dokument spełnia wymagania zapisane w pliku DTD. Zamiast sprawdzać każdą
przychodzącą książkę, możesz wrzucić plik do analizatora, który wykona kontrolę po-
prawności formalnej. Nie skontroluje on jakości zawartości dokumentu XML, ale może
zweryfikować go według wymagań.
Struktura DTD
Definicja typu dokumentu jest zbiorem zasad definiujących strukturę określonej grupy
dokumentów XML. DTD może być częścią dokumentu XML (w tym przypadku mó-
wimy o wewnętrznym DTD), może też być umieszczony poza dokumentem w osobnym
pliku na serwerze, albo pod adresem URL w Internecie (w takich przypadkach mówimy
o zewnętrznym DTD).
Rozdział 29. » PHP i XML 475
DTD może istnieć wewnątrz (jako część samego dokumentu XML), ale
lepiej tworzyć zewnętrzne DTD. Wspomnieliśmy już, że DTD definiuje
klasę dokumentów. Dzięki oddzieleniu ich od XML można uniknąć
zmieniania wszystkich dokumentów XML tej klasy w przypadku póź-
niejszej zmiany pliku DTD. Łatwiej pokazać wewnętrzny DTD, użyjemy
więc obu metod w przykładach zawartych w tym rozdziale.
<!DOCTYPE recipe [
Dla łatwiejszego czytania podzieliliśmy dokument XML na trzy części. Pierwszy wiersz
jest zwykłą jednowierszową deklaracją, która powinna rozpoczynać każdy dokument
XML. Druga część jest wewnętrznym DTD, oznaczonym wierszem rozpoczynającym
się od sekwencji < !. Trzecia część jest właściwym plikiem XML. Skupimy się teraz na
drugiej części, na DTD. Część tekstu położona na zewnątrz nawiasów kwadratowych
jest deklaracją typu dokumentu (nie pomyl z „definicją typu dokumentu"): < ! DOCTYPE
recipe [ . . . ] > . Deklaracja typu dokumentu wskazuje, że dokument ten będzie uży-
wał DTD. Ponieważ jest to wewnętrzny DTD, nazwaliśmy go tak samo jak główny
element (recipe) i umieściliśmy resztę deklaracji w nawiasach kwadratowych. Jeżeli
będziesz używał zewnętrznego DTD, powinieneś użyć deklaracji typu dokumentu
wskazującej typ i położenie DTD. Dwoma przykładami deklaracji typu dokumentu od-
wołujących się do zewnętrznych DTD są:
<!DOCTYPE recipe SYSTEM "recipe.dtd">
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML//EN">
Jeśli XML użyty w obu przykładach jest zgodny z wewnętrznym i zewnętrznym DTD,
oba dokumenty powinny być pozytywnie zweryfikowane przez analizator poprawności.
Pokazaliśmy tylko fragment wiedzy na temat DTD i dokumentów XML, ale te podsta-
wy powinny wystarczyć do zrozumienia większości funkcji PHP dla XML.
Analizatory kontrolujące
i nie kontrolujące poprawności
Istnieją dwa rodzaje analizatorów XML: kontrolujące poprawność i je niekontrolujące.
Analizatory niekontrolujące sprawdzają tylko, czy dokument XML jest odpowiednio
Rozdział 29. » PHP i XML_________________________________________477
Analizator PHP 4 SAX o nazwie expat nie jest analizatorem kontrolującym. Nie ozna-
cza to, że można zignorować DTD. Przejście przez proces tworzenia DTD dla każdego
dokumentu jest dobrą praktyką projektową. Zmusza do dokładnego przemyślenia
struktury dokumentu. Wielu ekspertów poleca przepuszczenie wszystkich dokumentów
przez analizator poprawności, nawet wtedy, gdy nie planujesz używać go ponownie.
Możliwe jest również użycie Gnome libxml do kontroli dokumentów XML. Wymaga
więcej pracy. Na witrynie libxml istnieją przykłady kontroli dokumentów z użyciem C
(www.xmlsoft.org).
Możesz używać dowolnego API do analizy dokumentów XML. W celu stworzenia lub
rozbudowy dokumentów XML za pomocą interfejsu PHP (bez tworzenia ręcznego) mu-
sisz skorzystać z DOM.
SAX
SAX jest mniejszy i łatwiejszy do nauki, ale traktuje XML jak przepływający strumień da-
nych. Jeżeli chcesz analizować nasz przepis, możesz skorzystać z analizatora SAX w PHP.
Jednak trudno będzie dodać nowy element lub atrybut, a zmiana wartości wybranego skład-
nika będzie pracochłonna. SAX jest doskonały do powtarzalnych zadań odnoszących się do
wszystkich elementów danego typu — na przykład zamiany znaczników odpowiedniego
elementu na znaczniki <PX/P> (krok w kierunku zamiany XML na HTML nadającego się
do wyświetlenia). SAX analizuje dokument tylko raz, od góry do dołu.
DOMXML buduje model struktury całego dokumentu XML. Pozwala na wybór odpo-
wiedniego węzła i zmianę jego wartości lub dodanie kolejnego węzła do drzewa. Jednak
proces ten wymaga dużych zasobów i dlatego nie nadaje się do obsługi długich doku-
mentów XML. Sprawdza się w nieskomplikowanych przekształceniach.
478________________________________Część III » Techniki zaawansowane
DOM
Document Object Model to kompletne API do tworzenia, edycji i analizy dokumentów
XML. Jest to obiektowe API, więc znajomość programowania obiektowego jest tu
przydatna, choć niekonieczna. DOMXML nie jest, prawdę mówiąc, analizatorem, ale
większość implementacji zawiera analizator.
DOM najlepiej pracuje, gdy dostępny jest cały dokument XML. Jeżeli twój XML jest
bardzo powoli przesyłany, użyj SAX. Ponieważ DOM buduje drzewo elementów
w pamięci, na wczytanie dużych dokumentów potrzeba czasu i zasobów.
Pod maską DOM API tkwi analizator gnome-libxml 2.0 (zwany Gnome xml). Jest do-
stępny na witrynie:
http://www.xmlsoft. org
Implementacja Gnomę DOM pochłania mniej pamięci. Document Object Model jest re-
komendowany przez World Wide Web Consortium. Możesz przeczytać o nim w mate-
riałach W3 dostępnych pod adresem:
http://www. w3. org/DOM/
Aby wywołać funkcję wysokiego poziomu, dodaj do nazwy metody przedrostek domxml
i obiekt na początku listy parametrów. Możesz dowolnie przeplatać obie metody.
W tabeli 29.1 zamieszczono listę funkcji DOM wysokiego poziomu wraz z opisem.
W czasie pisania tego rozdziału DOM API w PHP 4 nie było jeszcze usta-
lone. Obecnie prawdopodobnie dostępnych jest więcej klas i metod.
Tabela 29.1.
Zestawienie funkcji wysokiego poziomu XML DOM
Funkcja Opis
xmldoc ( ) Wymaga ciągu zwierającego dokument XML jako argumentu. Funkcja analizuje
dokument i tworzy obiekt Document
xmldocf ile ( ) Wygodna funkcja wymagająca nazwy pliku jako argumentu; odczytuje ten plik.
Następnie zachowuje się jak xmldoc ( )
xmltree ( ) Wymaga ciągu 7. dokumentem XML jako argumentu. Tworzy drzewo obiektów PHP
i zwraca obiekt DOM
Uwaga: drzewo obiektów zwrócone przez tę funkcję jest tylko do odczytu
new xmldoc ( ) Tworzy nowy pusty obiekt XML. Zwraca obiekt Document. Musisz dodać element
główny przy użyciu metody add root ( )
Tabela 29.2 zawiera listę klas DOM API wraz z opisem sposobu użycia.
Tabela 29.2.
Zestawienie klas DOMXML
Klasa Opis
Document Klasa hermetyzująca dokument XML. Zawiera element główny i DTD, jeżeli istnieje
Node Zamyka węzeł (element). Węzeł może być elementem głównym lub dowolnym innym
elementem. Węzły mogą zawierać inne węzły, dane znakowe i atrybuty
Attribute Klasa hermetyzująca atrybut obiektu. Atrybut jest definiowaną przez użytkownika
cechą węzła
Tabela 29.3 zawiera listę metod klasy Document wraz z opisem działania.
480___________________________________Część III » Techniki zaawansowani
Tabela 29.3.
Lista metod klasy Document
Metoda Opis
root ( ) Zwraca element główny dla dokumentu DOM
add root ( ) Dodaje główny element do dokumentu i zwraca nowy węzeł. Nazwa elementu jest
przekazana przez parametr
dump mem ( ) Zapisuje całe drzewo dokumentu XML do ciągu
Tabela 29.4.
Lista metod klasy Node
Metoda Opis
a t t r i b u t e s() Zwraca tablicę asocjacyjną z nazwami atrybutów i ich wartościami
g e t a t t r() Zwraca wartość atrybutu o podanej nazwie
setattr ( ) Ustawia wartość atrybutu o podanej nazwie. Jeżeli atrybut nie istnieje, zostanie
utworzony
children() Zwraca listę węzłów potomnych
lastchildt) Zwraca ostatni węzeł potomny z listy tych węzłów
parent ( ) Zwraca węzeł rodzica lub FALSE, jeżeli jest to węzeł główny
node ( ) Tworzy nowy węzeł i zwraca go
n e w child ( ) Tworzy nowy węzeł potomny do bieżącego węzła i zwraca go. Wymaga ciągu
zawierającego nazwę nowego węzła i ciągu zawierającego zawartość tego węzła jako
argumentu
Tabela 29.5 zawiera listę metod klasy Attribute wraz z opisem działania.
Tabela 29.5.
Lista metod klasy Attribute
Metoda Opis
attrname ( ) Ustawia nazwę atrybutu
SAX
Simple API for XML jest często używanym analizatorem dokumentów XML. bazuje na
zdarzeniach, co oznacza, że analizator wywołuje odpowiednie funkcje, gdy rozpozna
odpowiedni wyzwalacz w ciągu zdarzeń.
Rozdział 29. » PHP i XML__________________________________________481
SAX posiada interesującą historię, szczególnie w porównaniu z DOM. SAX API nie
jest zatwierdzone przez żaden organ standaryzujący. Tworzone jest przez programistów
grupy dyskusyjnej XML-DEV. Wielu z nich stworzyło już wcześniej analizatory XML,
nieposiadające standardowego API. Możesz się dowiedzieć więcej o twórcach SAX
z ich własnych witryn, np.:
http://www. megginson. com/SAX/index. html
SAX działa na podstawie procedur obsługi zdarzeń. W czasie przeglądania pliku XML
analizator rozpoznaje fragmenty XML, takie jak elementy, dane znakowe i jednostki
zewnętrzne. Każde z nich to zdarzenie. Jeżeli dostarczyłeś analizatorowi funkcję dla
określonego zdarzenia, w momencie napotkania takiego zdarzenia zatrzyma się i wy-
woła funkcję. Dane związane ze zdarzeniem zostaną przekazane do funkcji. Gdy funk-
cja obsługi zdarzenia zakończy się, analizator SAX będzie kontynuował analizę
dokumentu, wywołując funkcje związane ze zdarzeniami aż do końca dokumentu. Pro-
ces ten jest jednokierunkowy, od początku do końca dokumentu — analizator nie może
cofnąć się, ani wykonać pętli.
Używanym przez PHP SAX analizatorem jest expat, którego autorem jest James Clark.
Expat jest często używanym i prawdopodobnie najszybszym analizatorem XML. Wię-
cej informacji na temat tego produktu na stronie WWW autora:
http://www.jclark.com/xml/
Expat jest również rozprowadzany z serwerem Apache. Jeżeli używasz innego serwera
WWW, prawdopodobnie będziesz musiał skopiować go z witryny autora.
Użycie SAX
Sposób użycia SAX zależy od założeń, ale istniej ą części wspólne:
1. Rozpoznanie rodzajów obsługiwanych zdarzeń.
2. Napisanie funkcji obsługi dla każdego zdarzenia, np. funkcja do obsługi da-
nych znakowych oraz elementów początkowych i końcowych.
482___________________________________Część III » Techniki zaawansowane
If Zwolnienie pamięci
xml_parser_free(Ssimpleparser);
?>
Opcje SAX
Analizator XML w SAX API posiada dwie opcje konfiguracji, jedną do wyrównywania
znaków, drugą do kodowania wyniku.
Funkcje obsługi zdarzeń otrzymują dane tekstowe z analizatora XML. Kodowanie tek-
stu przekazywanego do funkcji obsługi zdarzeń jest nazywane „kodowaniem wyniku".
Domyślnie kodowanie jest identyczne z kodowaniem w źródłowym dokumencie, zna-
nym jako „kodowanie źródła". Możesz zmieniać kodowanie wyniku, jeżeli chcesz ana-
lizować tekst z innym kodowaniem niż pierwotnie.
$target_encoding =
xml_parser_get_option(XML_OPTION_TARGET_ENCODING);
Schange_encoding = xml_parser_set_option{$new_jparser,
XML_OPTION_TARGET_ENCODING, 'UTF-8');
Tabela 29.6.
Zestawienie funkcji PHP dla SAX
Funkcja Opis
xml parser create ( ) Funkcja tworzy nową instancję analizatora XML. Możesz używać kilku
osobnych analizatorów w tym samym czasie. Zwraca analizator XML
lub FALSE w przypadku niepowodzenia
Posiada identyfikator kodowania (na przykład UTF-8) jako opcjonalny
argument. Jeżeli nie zostanie podane kodowanie, używane jest
ISO-8859- 1
xral parser f r e e ( ) Zwalnia pamięć używaną przez xmljparser_create ( )
xml parser get o p t i o n ! ) Pobiera wartość opcji analizatora, ustawianych przez
xml_parser set^option ( ) . Dostępne opcje:
XML OPTION TARGET ENCODING i XML OPTION TARGET ENCODING
Tabela 29.6.
Zestawienie funkcji PHP dla SAX (ciąg dalszy)
Funkcja Opis
xml set c h a r a c t e r data Ustawia funkcję obsługi danych tekstowych. Otrzymuje ciąg z danymi
handler ( ) tekstowymi z elementu jako argument
xml set e x t e r n a l e n t i t y Ustawia funkcję obsługi dla odwołania do zewnętrznej jednostki.
r e f handler ( ) Odwołania wewnętrzne są obsługiwane automatycznie. Argumentami
funkcji są: analizator, ciąg zawierający nazwę zapisu, ciąg zawierający
podstawowy URI używany do odczytywania URI dla SYSTEM lub
PUBLIC, ciąg zawierający URL SYSTEM, jeżeli istnieje, i ciąg zawierający
ciąg P U B L I C , jeżeli istnieje. Podstawowy URL jest póki co pusty
xml set p r o c e s s i n g Ustawia funkcję obsługi instrukcji przetwarzania, Funkcja otrzymuje
instruction h a n d l e r ( ) jako argumenty: analizator, cel instrukcji przetwarzania i tekst instrukcji
przetwarzania
Funkcja obsługi musi wykonać instrukcje przetwarzania. Jeżeli cel jest
znany, na przykład PHP, analizator może wykonać funkcję exal ( ) na
tekście. Jest to świetny sposób na wbudowywanie skryptów do XML
Uwaga: wykonuj tylko kod z zaufanego źródła. Musisz być
przygotowany na komunikaty typu „Undefined error: 0 in Unknown on
line 0" (niezdefiniowany błąd: 0 w nieznanym pliku w wierszu 0)
xml set u n p a r s e d e n t i t y Ustawia funkcję obsługi nieanalizowanego elementu. Gdy analizator
decl parser ( ) napotyka na element zadeklarowany jako N DAT A, nie może analizować
tego elementu, ale wywołuje funkcję jego obsługi
xml set notation decl Funkcja ustawia nazwę funkcji obsługi, wywoływaną w przypadku
handler ( ) napotkania przez analizator deklaracji < ! NOTATION. Notacjami są
nazwy i identyfikatory dołączone do nieanalizowanego elementu.
Ponieważ elementy takie nie są analizowane przez analizator XML,
ich właściwe użycie jest domeną aplikacji. Notacje są pomyślane jako
dodatkowe informacje na temat typu i obsługi nieanalizowanych
danych. Argumentami funkcji są: analizator, ciąg zawierający nazwę
notacji, ciąg zawierający podstawowy URI używany do odczytywania
URI dla SYSTEM lub PUBLIC, ciąg zawierający URL SYSTEM, jeżeli
istnieje w deklaracji < ! NOTATION, i ciąg zawierający ciąg PUBLIC,
jeżeli istnieje. Podstawowy URL jest póki co pusty
xml set d e f a u l t h a n d l e r ! ) Ustawia domyślną funkcję obsługi zdarzeń. Jeżeli nie została ustawiona
funkcja obsługi zdarzenia, wywoływana jest domyślna funkcja obsługi
Funkcja otrzymuje analizator i ciąg zawierający nieobsłużone dane jako
argument, na przykład deklarację zapisu bądź odwołanie do zewnętrznej
jednostki
u t f B decode ( ) Funkcja przekodowuje ciąg z UTF-8 na ISO-8859- 1. Wymaga ciągu
UTF-8 jako argumentu i zwraca ciąg ISO-8859- 1
u t f S encode ( ) Funkcja przekodowuje ciąg z ISO-8859- 1 na UTF-8. Wymaga ciągu
ISO-8859-1 jako argumentu i zwraca ciąg UTF-8
486________________________________Część III » Techniki zaawansowane
<BODY>
<CENTERXH3>Tworzenie ankiety</H3x/CENTER>
</BODY>
</HTML>
<body>
<?php
Spollfile = "poll.xml";
<Poll id=\"SPollName\">
\t<StartDate>SPoll_Startdate</StartDate>
\t<EndDate>SPoll_Enddate</EndDate>
\t<name>$PollName</name>
\t<text>SPoll_Question</text>
\t<display type=\"poll_display-Bar-Graph\"/>
\t<responseSet resource=\"SPollID-responseSet\" />
</Poll>
<responseSet id=\"$PollName-responseSet\">
SRespSet</responseSet>
SResps";
//Zapisz do pliku
Sfd = fopen(Spollfile, "w") or die ("Nie mogę otworzyć pliku do zapisu. Sprawdź
uprawnienia.");
Swritestr - fwrite(Sfd, Snewxml);
//Komunikat
echo "zapisano Swritestr do Spollfile.";
?>
</body>
</html>
488___________________________________Część III » Techniki zaawansowane
<body>
<?php
require "xmltree.inc";
$pollfile = "poll.xml";
$pollname = $XMLtree->getEltByPath("/rdf(1)/poll($i)/name(1)");
echo "Nazwa ankiety: <INPUT TYPE=\"text\" SIZE=50 NAME=\"PollNarae\"
VALUE=\"$pollname\"XBR>";
5poll_enddate = $XMLtree->getEltByPath("/rdf(1)/poll($i)/enddate(l)");
echo "Data zakończenia: <INPOT TYPE=\"text\" SIZE=50 NAME=\"Poll_Enddate\"
VALUE=\"$poll_enddate\"XBR>";
$poll_question = $XMLtree->getEltByPath("/rdf(1)/poll(Si)/text(l)");
$poll_question = stripslashes($poll_question);
echo "Pytanie ankiety: <INPUT TYPE=\"text\" SIZE=100 NftME=\"Poll_Question\"
VALUE=\"$poll_question\"XBR>";
St = l;
Slow = ( (Si-1)*6)+l;
$hi = ( (Si-l)*6)+6;
for(Sr=$low; $r<=$hi; $r++) {
$poll_option($t] = SXMLtree->getEltByPath("/rdf(1)/response($r)/text(1) ") ;
echo "Odpowiedzi: <INPUT TYPE=\"text\" SIZE=25 NAME=\"Raw_Poll_Option[St J \"
VALUE=\"Spoll_option[St]\n\"><BR>";
St + + ;
}
fclose(Sfd);
// Pobranie elementów i atrybutów
SXMLtree = new XMLtree;
if ($err = $XMLtree->parse("$harrisfile"))
(
die (Serr);
)
$pollname = $XMLtree->getEltByPath("/rdf(1)/poll(Si)/name(l)");
Spoll_startdate = SXMLtree->getEltByPath("/rdf(1)/poll($i)/startdate(1)");
Spoll_enddate = $XMLtree->getEltByPath("/rdf(1)/poll(Si)/enddate(1)");
Spoll_question = SXMLtree->getEltByPath("/rdf(1)/poll(Si)/text(1)");
St = l;
Slow = ( (Si-1)*6)+l;
$hi = ( (Si-1)*6)+6;
for($r=Slow; Sr<=$hi; Sr++) (
Sraw_poll_option[$t] = SXMLtree-
>getEltByPath("/rdf(1)/response(Sr)/text(1)");
St + + ;
)
//Formatowanie zbioru odpowiedzi.
for(St-l; St<=6; $t + + ) (
if(!empty(Sraw_poll_option[$t]))
{
$poll_option[$t] = "$pollname-$raw_poll_option[$t]";
}
else
1
$poll_option(St] = "";
}
SrespSet .= "\t<response resource=\"Spoll_option[St]\"/>\n";
Sresps .- "<response
id=\"Spoll_option[St]\">\n\t<text>Sraw_poll_option[St]</text>\n</response>\n";
)
\t<responseSet resource=\"$pollname-responseSet\"/>
</Poll>
<responseSet id=\"Spollname-responseSet\">
5respSet</responseSet>
Sresps";
Sdivide = e x p l o d e ( S s e p a r a t o r , S f s t r ) ;
<responseSet id=\"$PollName-responseSet\"> *
$RespSet</responseSet>
SResps";
//Zapis do pliku
Sfd = fopen($pollfile, "w");
$writestr = fwrite($fd, $newxml);
//Komunikat
echo "Zapisano $writestr do $pollfile.";
(
?>
</body>
</html>
<PollList id="raovie-PollList">
<Poll name="Best_Summer_Filra"/>
<Poll name=""/>
</PollList>
<Poll id="">
<StartDatex/StartDate>
<EndDateX/EndDate>
<namex/name>
<textx/text>
<display type="poll_display-Bar-Graph"/>
<responseSet resource="-responseSet"/> •
</Poll>
<responseSet id="-responseSet">
<response resource=""/>
<response resource=""/>
<response resource=""/>
<response resource=""/>
<response resource=""/>
<response resource=""/>
</responseSet>
<response id-"">
<textx/text>
</response>
<response id="">
Rozdział 29. » PHP i XML_________________________________________491
<textx/text>
</response>
<response id="">
<textx/text>
</response>
<response id="">
<textx/text>
</response>
<response id="">
<textx/text>
</response>
<response id="">
<textx/text>
</response>
<Poll id="Best_Summer_Film">
<StartDate>09/01/2000</StartDate>
<EndDate>10/01/2000</EndDate>
<name>Naj lepszy film lata</name>
<text>Jaki był najlepszy film tego lata?</text>
<display type="poll_display-Bar-Graph"/>
<responseSet resource="Best_Summer_Filni-responseSet"/>
</Poll>
<responseSet id="Best_Summer__Film-responseSet">
<response resource="Best__Summer__Fil[n-Shaf t"/>
<response resource="Best_Summer_Film-Xmen"/>
<response resource="Best_Summer__Filn\-Gladiator"/>
<response resource="Best_Summer_Film-Battlefield_Earth"/>
<response resource="Best_Summer_Film-MI2"/>
</responseSet>
<response id="Best_Summer_Film-Shaft">
<text>Shaft</text>
</response>
<response id="Best_Summer_Film-Xmen">
<text>X Men</text>
</response>
<response id="Best_Summer_Film-Gladiator">
<text>Gladiator</text>
</response>
<response id="Best_Summer_Film-Battlefield_Earth">
<text>Battlefield Earth</text>
</response>
<response id="Best_Summer_Film-MI2">
<text>Mission Impossible 2</text>
</response>
</PollDefs>
Jeżeli nie możesz odczytać lub zapisać dokumentów XML na dysku, sprawdź, czy ser-
wer WWW ma uprawnienia do wykonania takiej operacji.
492___________________________________Część III » Techniki zaawansowane
Jeżeli API DOM zwraca błąd „fatal function not found error", oznacza to, że moduł
DOMXML nie został zainstalowany. Uruchom phpinf o ( ) i odszukaj pozycję domxml.
Jeżeli jej nie ma, musisz powtórnie skompilować PHP z modułem DOMXML.
W chwili pisania książki obsługa DOMXML dla Windows nie była jeszcze
dostępna. Biblioteka Gnome XML była już kompilowana dla Windows,
ale tylko dla odważnych programistów C, a niejako .dli dla PHP.
DOM API jest również nowością w PHP 4, więc wiele szczegółów może jeszcze nie
działać prawidłowo. Pamiętaj o tym i sprawdzaj listę błędów w przypadku problemów.
http://bugs.php. net/
Podsumowanie
XML jest niezależnym od aplikacji formatem wymiany danych, który umożliwi łatwiej-
sze i szybsze tworzenie aplikacji WWW w przyszłości. XML i HTML, spadkobiercy
SGML, są na pierwszy rzut oka podobne. Oba formaty posiadają znaczniki i atrybuty.
W XML elementy te można definiować, natomiast struktura HTML jest zdefiniowana
przez standard HTML i nie zawiera informacji o strukturze dokumentu.
PHP z SAX może być używany do zapisania prawidłowego pliku XML na podstawie
wartości podanych w formularzu. Jest również używany do zamiany XML na HTML
w celu łatwego wyświetlania danych w przeglądarce. Innym zadaniem jest pobieranie
danych z bazy i zapisywanie ich do pliku XML w celu wymiany z innymi organizacjami.
Rozdział 30.
Programowanie obiektowe
wPHP
W tym rozdziale:
* Co to jest programowanie obiektowe?
* Klasy, obiekty i typy w PHP
** Wyświetlanie i drukowanie obiektów
+ Kopiowanie obiektów: referencje i aliasy
+ Serializacja obiektów
* Zewnętrzne interfejsy: COM, Java i CORBA
* Przykładowa aplikacja obiektowa
Programowanie obiektowe jest zbiorem technik organizacji kodu wokół jednostek lub
obiektów reprezentowanych przez ten kod. Języki obiektowe zawierają wsparcie dla ta-
kiej organizacji.
również definicje funkcji (metod) operujących na danych. O takiej definicji klasy prze-
pisu można powiedzieć, że zawiera składniki (dane) i etapy (dane) oraz sposób, w jaki
przepis będzie wydrukowany (metoda). Ważne jest, że metody są integralną częścią
klasy — zdefiniowanie sposobu drukowania przepisu nie ma wpływu na sposób druko-
wania czegokolwiek innego.
Aby napisać program obiektowy, musisz zdefiniować klasy, a następnie stworzyć obiekty
za pomocą zdefiniowanych wcześniej klas. Zamiast tworzyć globalne funkcje i zmienne,
łączysz zmienne z funkcjami pozwalającymi na ich użycie i modyfikacje.
Jak dobre
jest programowanie obiektowe?
Programowanie obiektowe nie daje magicznej siły — nie ma programu obiektowego,
którego nie da się przepisać na wersję proceduralną. Programowanie obiektowe zmusza
do określonego zorganizowania kodu. Organizacja może ułatwiać wymyślenie sposobu
rozwiązania problemu, ponieważ prowadzi do naturalnego modelowania procesów za-
chodzących w świecie rzeczywistym.
Jeżeli pisałeś kod PHP, jesteś przygotowany do pisania klas i obiektów. Łatwo stworzyć
prostą klasę:
class myClass
(
var Sattribute = "atrybut";
function show_attribute()
{
echo this->attribute. "<br>\n";
(
l
Definicja klasy jest dosyć prosta. Jej struktura nie zmienia się:
class NAZWAKLASY [extends NAZWAKLASY_BAZOW J]
{
[atrybuty]
[funkcje]
)
Atrybuty
Atrybuty są definiowane w postaci:
var Sattribute = "atrybut";
Definicja atrybutu nie może zawierać inicjalizacji niestatyczną wartością. Oznacza to,
że można ustawić początkową wartość zmiennej za pomocą ciągu, liczby całkowitej lub
zmiennoprzecinkowej, albo tablicy, ale nie można użyć do tego obiektu.
Atrybut może posiadać wartość o jednym z typów PHP 4 lub być obiektem o typie jed-
nej ze zdefiniowanych klas. Możesz deklarować ten sam atrybut wiele razy, ale będzie
miał on wartość nadaną w ostatniej inicjalizacji.
Atrybuty klasy mogą, ale nie muszą, być inicjowane. Jeżeli atrybutowi jest nadawana
wartość w deklaracji klasy, jest on inicjowany.
Funkcje
Funkcje są deklarowane w klasie podobnie jak te spoza klasy. Istnieją tylko dwie głów-
ne różnice:
* Aby dostać się do atrybutów obiektu, należy używać składni $this->attri-
bute. Nie należy używać this->$attribute, ponieważ nie działa. Forma
$this->$attribute działa tylko wtedy, gdy Sattribute jest ciągiem za-
wierającym nazwę atrybutu. Działa również w przypadku funkcji.
Rozdział 30. » Programowanie obiektowe w PHP___________________________497
var $attrl;
$attr_name = "attrl";
echo $this->$attr_name;
Konstruktory
Konstruktor jest specjalizowaną funkcją zdefiniowaną w klasie. Posiada nazwę iden-
tyczną z nazwą klasy i może mieć argumenty. Klasa może posiadać tylko jeden kon-
struktor. Konstruktor jest wywoływany automatycznie w czasie tworzenia obiektu m
podstawie klasy. Używany jest do inicjalizacji atrybutów obiektu za pomocą odpowied-
nich wartości.
Dziedziczenie
Klasa w PHP 4 może dziedziczyć z innej klasy. Klasa podrzędna „dziedziczy" wszyst-
kie funkcje i atrybuty z klasy nadrzędnej, bez potrzeby ich ponownej deklaracji.
class super 9
(
function super_echo()
(
echo "SUPERCLASS. <br> \n";
)
}
Klasa o nazwie „sub" to podklasa bądź klasa dziedzicząca. Dziedziczy (rozszerza) jedną
lub więcej klas. Klasy PHP 4 mogą dziedziczyć tylko z jednej klasy, ale klasa bazowa
może również dziedziczyć z innej klasy bazowej. Można powiedzieć, że klasy PHP
mogą mieć tylko jednego ojca, ale mogą mieć również dziadka.
class dziadek
(
}
class ojciec extends dziadek
(
)
class dziecko extends ojciec
l
)
Przesłanianie
Podklasy automatycznie dziedziczą atrybuty i funkcje ze swoich klas nadrzędnych, ale
czasem po woduj ą konflikt nazw. Klasa nadrzędna może posiadać funkcję o takiej samej
nazwie jak funkcja w podklasie. PHP 4 musi zdecydować, którą z nich należy wywołać:
class super
{
function my_function()
{
echo "SUPERCLASS. <br> \n";
)
>
class sub extends super
{
function my_function()
Jeśli podklasa posiada funkcję o takiej samej nazwie jak nadklasą, podklasa przesiania
funkcję z nadklasy. Jeżeli przesłonięta funkcja jest wywołana w podklasie, wywoływa-
Rozdział 30. » Programowanie obiektowe w PHP_________________________499
Za pomocą tego modyfikatora nie można odwoływać się do atrybutów nadklasy prze-
słoniętych w definicji podklasy.
Przeciążanie
Przeciążaniem w programowaniu obiektowym nazywamy sytuację, gdy w klasie ist-
nieje więcej niż jedna funkcja o tej samej nazwie. Interpreter lub kompilator odróżnia je
po liczbie i typach argumentów — każda z tych funkcji musi mieć unikalną sygnaturę
argumentów. PHP 4 nie obsługuje przeciążania. Poprawną sytuacją jest zdefiniowanie
w klasie więcej niż jednej funkcji o tej samej nazwie, jednak wszystkie definicje, poza
ostatnią, zostaną zignorowane.
Zasięg
Jeżeli pisałeś już funkcje, powinieneś znać pojęcie zasięgu. Zasięg to przestrzeń, w któ-
rej dostępna jest określona zmienna. W żadnej funkcji nie można używać zmiennych
globalnych bez ich wcześniejszego zadeklarowania jako globalne w ciele funkcji, albo
odwoływania się do nich poprzez tablicę GLOBALS [ ].
Zasięg w PHP 4 określa również to, która funkcja zostanie wywołana. Nie można bez-
pośrednio wywołać funkcji w klasie, nie używając modyfikatora zasięgu w celu wska-
zania, gdzie należy szukać tej funkcji.
Funkcje, zmienne i definicje klas pozostają w zasięgu, nawet gdy są dołączane za po-
mocą funkcji i n c l u d e ( ) lub r e q u i r e ( ) .
Alias to zmienna będąca referencją do innej zmiennej. Nie posiada własnej wartości,
lecz wskazuje na wartość innej zmiennej. Może być używana jak każda inna zmienna:
możesz przypisywać jej wartość lub wywoływać funkcje, jeżeli jest obiektem.
$alias = &$zmienna;
echo "<BR>";
echo "Zmiana aliasu...<BR>";
$alias = "Zmieniony pierwszy ciąg testowy - alias";
echo "<BRXBR>";
Zmiana aliasu...
Aliasy mogą odwoływać się do określonych komórek w tablicy lub atrybutów w obiekcie:
$alias = &$object->attributte;
Salias = "Nowa wartość atrybutu";
echo "<pre>";
var_ciump($object) ;
echo "</pre>";
echo "<pre>";
print_r($object);
echo "</pre>";
Przeglądanie
Obiekt jest zespołem danych i funkcji. PHP pozwala na sprawdzenie nazw, typów i war-
tości wszystkich danych w obiekcie. Obiekty mogą być traktowane jak tablice i „przeglą-
dane" w celu odczytania każdej nazwy zmiennej, typu i wartości.
Tabela 30.1.
Zestawienie funkcji sprawdzających typy, zwracających wartości boolean
Funkcja Opis
Tabela 30.1.
Zestawienie funkcji sprawdzających typy, zwracających wartości boolean (ciąg dalszy)
Funkcja Opis
is real ( $ v a r ) Zwraca TRUE, jeżeli $var jest liczbą zmiennoprzecinkową o podwójnej precyzji
Tabela 30.2.
Zestawienie funkcji typów i klas
Funkcja Opis
gettype ( $ v a r ) Zwraca ciąg reprezentujący typ wartości Svar, to znaczy string, object, double
settype ( $ v a r ) Określa typ wartości ?var na array, double, integer, object lub string. Funkcja
może dawać zaskakujące wyniki, więc przetestuj ją przed użyciem
doubleval (Svar) Zwraca liczbę typu double o wartości określanej przez wartość Svar.
Na przykład: "0.045" jest zamieniane na 0.045, 6 staje się 6.0. Funkcja nie działa
na tablicach i obiektach
intval ( $ v a r ) Zwraca liczbę typu integer o wartości określanej przez wartość Svar.
Na przykład: "45" jest zamieniane na 45, 4.0 staje się 4. Funkcja nie działa
na tablicach i obiektach
unset ( S v a l ) Usuwa podaną zmienną. Po wykonaniu funkcji zmienna nie jest dostępna
i ponowne wywołanie isset (Svar) zwróci FALSE
Tabela 30.3.
Zestawienie funkcji wejścia i wyjścia dla klas i typów
Funkcja Opis
print r ( 5 v a r ) Drukuje ciąg będący reprezentacją zmiennej lub obiektu
var dump (Svar ) Drukuje ciąg będący reprezentacją zmiennej lub obiektu. Postać drukowanego
ciągu jest nieco inna niż print r ( )
strval ( $ v a r ) Drukuje ciąg będący reprezentacją zmiennej. Nie podaje szczegółów tablic ani
obiektów
serialize ($var) Zwraca ciąg bajtów reprezentujący zmienną lub obiekt. Ciąg ten jest odpowiedni
do zapisywania w bazie danych lub przesyłania pomiędzy stronami. Do
niektórych celów może wymagać zakodowania funkcjąbase64_encode ( )
unserialize ( $ v a r ) Zwraca zmienną stworzoną na podstawie wyniku funkcji serialize ( )
reset($obj};
while ( list($slot) = each(Sobj) )
{
echo spaceout(Sdepth) ."<b>$slot Szczegóiy:</b> <br>";
echo spaceout($depth) ."Nazwa zmiennej: Sslot<br>";
echo spaceout($depth) ."Typ zmiennej:".gettype(Sobj->$slot}." <br>";
if (is_object($obj->$slot))
echo spaceout($depth). "zmienna klasowa:".get_class(Sobj->$slot). "<br>";
if (gettype(Sobj->$slot) == "string")
echo Sobj->$slot;
else if (gettype($obj->Sslot) == "integer")
echo $obj->$slot;
else if (gettype (Sobj->Sslot) == "double")
echo $cbj->$slot;
else if (gettype(Sobj->Sslot) == "array")
506________________________________Część III » Techniki zaawansowane
print_r(Sobj->$slot);
else if (gettype{$obj->$slot) == "object"}
{
echo spaceout (Sdepth) . "<i>0biekt klasy </ixb>" .
get_class($obj->Sslot).":</b><br>";
display_object($obj->$slot, $depth+l);
continue;
l
echo "<br>";
}
)
Poniżej znajduje się klasa zawierająca zmienne każdego typu oraz obiekt wewnętrzny:
class walkme
{
var $stringOne = "Pierwszy ciąg, wartość oryginalna";
var $stringTwo = "Pierwszy ciąg, wartość oryginalna";
var 3objectOne;
var $integerOne;
var SdoubleOne;
var $arrayOne;
function init()
ł
Sthis->stringOne = "Pierwszy ciąg, nowa wartość"; //ciąg
Sthis->objectOne = new introO; //obiekt
$this->objectOne->objectOne = new introO; //obiekt wewnętrzny
$this->integerOne = 6; //liczba całkowita
$this->doubleOne = 6.5; //liczba zmiennoprzecinkowa
$this->arrayOne = array("a", "b", "c"); // tablica
)
}
stringOne Szczegóły:
Nazwa zmiennej: stringOne
Typ zmiennej: string
wartość: Pierwszy ciąg, nowa wartość
stringTwo Szczegóły:
Nazwa zmiennej: StringTwo
Typ zmiennej: string
wartość: Pierwszy ciąg, wartość oryginalna
objectOne Szczegóły:
Nazwa zmiennej: objectOne
Typ zmiennej: object
zmienna klasowa: intro
wartość: Obiekt klasy intro:
stringOne Szczegóły:
Rozdział 30. * Programowanie obiektowe w PHP___________________________507
Serializacja obiektów
Z kilku różnych powodów możliwość przekształcenia obiektu w liniowy ciąg bajtów
zdekodowanie jej do postaci obiektu mogą być przydatne. Używając tej możliwości
możesz zapisać obiekt na dysku, a następnie ponownie go odczytać. Można również
wysłać go do innego programu, w którym obiekt będzie zrekonstruowany. Operacja ko-
dowania i zdekodowania obiektów nazywana jest serializacją.
Możesz zapisać serializowany obiekt do pliku lub bazy danych, przekazać go do innej
strony WWW.
if ((iisset(Sin_object)}
t
?in_object = new walkmeO;
508___________________________________Część III » Techniki zaawansowane
Jedyną rzeczą, o której musisz pamiętać, jest upewnienie się, że klasa odtwarzanego po
serializacji obiektu będzie zdefiniowana w czasie odtwarzania obiektu. Jeżeli nie, zmienne
obiektowe powstałe po odtwarzaniu będą dostępne, ale wywołanie funkcji obiektu będzie
niemożliwe.
Zewnętrzne interfejsy:
COM, Java i CORBA
Obecnie dostępne są moduły rozszerzeń PHP 4 do połączenia oraz manipulacji obiek-
tami COM, DCOM i Java. W wersji 4.0.0 CORBA nie była dostępna, ale trwały prace
nad co najmniej jednym modułem na bazie ORBit ORB.
Mimo że obsługa Javy wchodzi w skład instalacji PHP 4, nie udało się
nam tego przetestować na wersji beta PHP 4.
Rozdział 30. » Programowanie obiektowe w PHP___________________________509
COM i DCOM
Moduł COM obsługuje zarówno obiekty COM, jak i DCOM posiadające interfejs IDi-
spatch. Moduł COM jest częścią PHP 4 dla Win32. W tabeli 30.4 zamieściliśmy do-
stępne funkcje interfejsu COM.
Jeżeli w pliku php.ini podałeś bibliotekę typów, PHP 4 samoczynnie rozpozna stałe
automatyzacji. Przeczytaj plik README dołączony do modułu, w którym znajduje się
szczegółowy opis wykonania.
DCOM jest również obsługiwany, ale domyślnie jest wyłączony. W celu uaktywnienia
DCOM należy w pliku php.ini umieścić wiersz allow_dcom = 1;. Moduł automatycz-
nie skonwertuje typy pomiędzy COM i PHP, oprócz konwersji tablic i obiektów PHP.
while)!Sresult->EOF)
{
echo $result->Fields["First Name"]->Value;
echo " ";
echo Sresult->Fields("Last Name"]->Value;
echo ", ";
echo $result->Fields["State"]->Value;
echo "<br>";
Sresult->MoveNext();
1
Richard Lynn, CA
Robert G. Willis, CA
Peter Buttler, PA
Mary Steward, CA
Darvid Bergman, CA
Lisa Bates, CA
Tony Smith, NJ
Tabela 30.4.
Zestawienie funkcji interfejsu COM
Funkcja Opis
com load ( ) Parametry: (string nazwa obiektu [, string nazwa serwera])
function connect() { ; }
function error_string() ( ; }
function error_number(} { ; }
function execute_query($query) { ; )
function select_database() { ; }
Rozdział 30. » Programowanie obiektowe w PHP___________________________511
function commit() f ; }
function rollback() { ; }
function fetch_row_array() { ; }
function check_connect()
{
if { $this->connection == 0)
Sthis->connect();
}
function print_error($error_string)
i
if ( ! $this->suppress__errors)
echo "Niekrytyczny błąd bazy danych:".$error_string. "<br>";
}
}
class failover_database extends database
f
var $backup_server_name;
function connect(}
{
if ({$result == 0) && isset{$this->backup_server_name))
t
$original = $this->server_name;
$this->server_name = $this->backup__server_name;
$this->backup_server_name = Soriginal;
// spróbuj zapasowy serwer
$result = database::connect(};
}
}
}
II ***************** + + *********** + ******•*********•* + **********
class mysql_database extends failover_database
{
var Sconnection;
)
function error_string{)
(
return mysql_error();
)
function error_number()
{
return mysql_errno();
}
function execute_query(Squery)
{
Sthis->check_connect();
Sresult = Smysql_query(Squery, Sthis->connection);
if (Sresult == 0)
$this->print_error("Nie można wykonać zapytania
(".$query."). Zwracany błąd: (".$this->error_string().").
Sprawdź skiadnię oraz połączenie i spróbuj jeszcze raz."};
return ($result);
}
function select_database(Sdb_name)
(
Sthis->check_connect() ;
Sresult = @mysql_select_db(Sdb_name);
if (Sresuit == 0)
$this->print_error("Nie mogę wybrać bazy danych (".Sdb_name.").
Zwracany błąd: (".$this->error_string(}.").
Sprawdź składnie oraz połączenie i spróbuj jeszcze raz."};
l
function fetch_row_array(Squery_result)
{
$this->check_connect() ;
if ($query__result == 0)
$this->print_error(
"Nie można pobrać wyniku zapytania. Zwracany błąd:
(" .Sthis->error_string()."). Sprawdź składnie oraz połączenie
i spróbuj jeszcze raz."};
return (3mysql_fetch_array(Squery_result, MYSQL_ASSOC));
)
function commit() { ; } // Commit nie obsługiwany w MySQL
function rollback() t ; } // Rollback nie obsługiwany w MySQL
}
// *,.*,**.—— Ł**.,*.**,**..******..****.*».*.*.*„.,„,„***„*„
// Wszystkie zdefiniowane do tej pory klasy należy umieścić w pliku
// database.inc
// include("database.inc");
$server = "nazwa.serwera";
Sbackupserver = "nazwa.serwera.zapasowego";
Sdatabase = "nazwa_bazy_danych";
Susername = "nazwa_użytkownika";
Spassword = "hasło";
if (!isset(Smydb))
{
Smydb = new mysql_database(Sserver, Sbackupserver, Sdatabase,
Susername, Spassword);
echo "Tworzenie obiektu połączenia z bazą danych...<br>";
(
else
(
echo "Ponowne użycie obiektu połączenia z baza danych...<br>";
Rozdział 30. » Programowanie obiektowe w PHP___________________________513
session_register("mydb");
if (Sresult != 0)
(
while($row = $mydb->fetch_row_array(Sresult))
(
echo "Nazwa: <b>" . Srow [ "name" ] . "</bxbr>";
echo " ID : <b>" . $row [ "id"] . "</bxbr>";
echo "<br>";
I
}
?>
</body>
</html>
Podsumowanie
Programowanie obiektowe to programowanie z klasami. PHP 4 posiada podstawowe
narzędzia do tworzenia aplikacji obiektowych. Nie wszystko, do czego przyzwyczaiłeś
się przy programowaniu obiektowym, jest dostępne w PHP 4. Wielu elementów pro-
gramowania obiektowego brakuje. Jednak utrzymane są podstawowe założenia i za po-
mocą dostępnych narzędzi możesz:
** napisać bardziej elastyczną aplikację;
* połączyć funkcje z danymi, zabezpieczając dane przed zniszczeniem przez
niewłaściwe funkcje;
* zarządzać przestrzenią nazw, ukrywając tysiące funkcji w klasach (nie przesło-
nisz przypadkowo funkcji globalnej);
* pisać programy współpracujące z obiektami COM, DCOM i Java;
* poznać podstawy projektowania i programowania obiektowego.
514 Część
Część
111III »» Techniki zaawansowane
Rozdział 31.
Bezpieczeństwo
i kryptografia
W tym rozdziale:
* Możliwe ataki
* Szyfrowanie
4 Szyfrowanie kluczem publicznym
4 Szyfrowanie pojedynczym kluczem
* Mieszanie
* Secure Server Layer
Drugą zasadą dla bezpiecznej witryny jest: „Ograniczaj szkody". Co się stanie, jeżeli
program ma słabe punkty (mimo że stosowałeś się do zasad bezpieczeństwa przy jego
pisaniu)? Musisz zwrócić uwagę na sposoby ograniczania szkód, jakie może wyrządzić
potencjalny włamywacz, któremu uda się jednak przełamać zabezpieczenia.
jakąś dozę ryzyka. Jako projektant witryny jesteś odpowiedzialny za jego maksymalne
ograniczenie. Oprócz upewnienia się, że dane są bezpieczne na twoim serwerze, powi-
nieneś zapewnić bezpieczeństwo danych na czas ich transmisji pomiędzy komputerami
użytkowników a serwerem.
Pierwsza część tego rozdziału opisuje niektóre sposoby atakowania serwera i obrony
przed nimi. Następnie zajmiemy się użyciem technik kryptograficznych do ochrony da-
nych. Na końcu rozdziału zamieścimy listę witryn WWW zawierających bieżące infor-
macje na temat technik włamywania. Śledząc te witryny, możesz poznać słabe punkty
systemów, zanim ktoś z nich skorzysta.
Możliwe ataki
Podłączenie serwera do Internetu jest podobne do otwarcia sklepu na ruchliwej ulicy. Mimo
że masz stosunkowo niewielu klientów, możesz spodziewać się nieproszonych gości.
Zmiana wyglądu źle zaprojektowanej witryny jest możliwa przy użyciu przeglądarki.
Przyjrzyj się następującemu fragmentowi programu:
<?php
if (IsSet(Svisitor))
(
$ fp ~ fopen("database" , "a");
fwrite (Sfp, "<li> SvisitorW) ;
fclose(S fp);
) ?>
<HTML>
<HEADX/HEAD>
<BODY>
<Hl>Ksiega gosci:</Hl>
<OL>
<?php
$fp = f open ("database", "r");
print(fread($fp, fileśi ze("/sciezka/do/bazy")));
fclose(Sfp); ?>
</OL>
<HR>
<FORMXINPUT T Y P E = " T E X T " NAME="visitor">
<INPUT TYPE="SUBMIT" NAME="submit" VALUE="Wpisz sie!">
</FORM>
</BODY>
</HTML>
Program ten obsługuje podstawową książkę gości. Przeglądając ten kod, powinieneś
poczuć się niepewnie. Program ten pobiera dane z formularza, spodziewając się imienia
gościa (w zmiennej $visitor), i zapisuje je w pliku tekstowym, wyświetlając później
kolejnych gości. W przypadku takich danych nie ma żadnego niebezpieczeństwa.
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________517
Gdy ktoś załaduje książkę gości, przeglądarka zinterpretuje ten znacznik i natychmiast
przejdzie na witrynę włamywacza. Przy odrobinie pomysłowości może on skorzystać
z zaufania, jakim gość obdarza twoją stronę, w celu otrzymania poufnych danych (ha-
sła, numery kart kredytowych).
W przypadku używania PHP w postaci modułu serwera WWW istnieje małe ryzyko
wyświetlenia kodu źródłowego przez serwer, ponieważ każdy plik z określonym rozsze-
rzeniem jest analizowany przez moduł PHP. Jeżeli jednak PHP jest zainstalowany jako
program CGI, sprawy mogą się skomplikować.
Jeżeli nie możesz uruchomić PHP jako modułu serwera, możesz zainstalować PHP tak,
jak inny interpreter skryptów CGI, na przykład Perl lub Python. Po umieszczeniu
wszystkich programów PHP w katalogu cgi-bin serwera lub twojego konta zmień kon-
figurację tak, aby interpreter PHP był wywoływany dla tych plików. Na Uniksie dodaj
do każdego skryptu pierwszy wiersz:
#! /usr/local/bin/php
Jednak PHP jest napisany w sposób, który w przypadku właściwej konfiguracji umoż-
liwia bezpieczne działanie z katalogu cgi-bin. Jeżeli zdecydujesz się na użycie takiej
konfiguracji, przeczytaj dokładnie rozdziały o bezpieczeństwie i konfiguracji w pod-
ręczniku PHP; mogą zawierać istotne informacje, które ominęliśmy w tej książce.
na adresy w postaci
http://serwer/cgi-bin/php/program.php
Dokładne dyrektywy, powodujące takie zachowanie się serwera, zależą od jego rodzaju.
Dla Apache jest to:
Action php-script /cgi-bin/php
AddType php-script .php
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________519
Jeżeli używasz Apache, upewnij się, że PHP jest skompilowany z opcją konfiguracji
—enable-force-cgi-redirect. PHP nie potrafi odróżnić tych dwóch typów adre-
sów URL i będzie dostarczał dokumenty dowolnego typu. Pozwoli to na udostępnianie
użytkownikowi plików z pominięciem ograniczeń serwera WWW. Załóżmy na przy-
kład, że plik pod adresem http://www.secret.com/top/secret/hush.php ma zablokowane
prawa dostępu. Włamywacz może skorzystać więc z adresu URL: http://www.secret.
com/cgi-bin/php/top/secret/hush.php, aby odczytać ten plik.
Program ten wyświetla dostępne wiersze, dając możliwość wyboru z listy rozwijalnej.
Przypomnij sobie teraz zasadę „Nie ufaj Internetowi". Naciśnięcie przycisku Pokaż na
głównej stronie powoduje wygenerowanie adresu URL w postaci ...poezja.php?poem =
graves.html. Włamywacz może zamienić tę nazwę pliku na coś bardziej go interesują-
cego, na przykład poezja.php?poem=/etc/passwd. Program teraz posłusznie wyświetli
plik haseł Unixa, umożliwiając włamanie na przykład przez konto gościa.
break;
case "graves":
$poem_file = "graves.html";
break;
}
1
if (IsSet(Spoem_file))
(
$fp = fopen($poem_file, "r");
print (fread($fp, filesize($poem_file))};
fclose($fp);
}
?>
Zaletą tej metody jest dokładne wyliczenie wszystkich dostępnych pozycji i obsługa
niepożądanych wartości. Jeżeli umożliwiłeś dostęp do wielu wierszy, wyrażenie swi-
tch powinno być zastąpione zapytaniem o miejsce, gdzie wprowadzenie niewłaściwych
danych będzie skutkowało niepowodzeniem wykonania zapytania.
Drugi warunek w tym kodzie sprawdza, czy w podanej nazwie pliku istnieją separatory
ścieżki. Program ten jawnie podaje zestaw nieakceptowanych wartości wejściowych
i uznaje wszystko inne za dopuszczalne. W tym przypadku programista założył, że ża-
den ważny plik nie będzie przechowywany w tym samym katalogu co skrypt.
A jeżeli ten plik zostanie kiedyś użyty na innym serwerze? Istnieje szansa, że z powodu
niedokładnej konfiguracji (prawdopodobnie zrobionej przez inną osobę) lub z powodu
niezauważonej dziury w systemie bezpieczeństwa udostępnimy część lub wszystkie pli-
ki serwera.
PHP pozwala podać zbiór katalogów, z których wolno otwierać pliki. Służy do tego
zmienna konfiguracyjna open_basedir. Może być użyteczna przy ograniczenia dostę-
pu do większości katalogów; jest to dobry sposób niwelowania uszkodzeń. Więcej da-
nych na temat konfigurowania PHP w rozdziale 32.
Jednak istnieje wiele plików, które muszą być otwierane przez programy PHP w trakcie
udostępniania witryny użytkownikom. Przykładem może być plik haseł. Dostęp do tego
pliku nie może być zablokowany za pomocą opcji open_basedir, ale zawarte w nim
ważne dane mogą być zaszyfrowane.
Witryny chronione hasłem muszą sprawdzać hasło podane przez użytkownika. Jednym
ze sposobów realizacji jest przechowywanie hasła w postaci zaszyfrowanej i odszyfro-
wania go w celu porównania z hasłem podanym przez użytkownika. Ale jeżeli potrafi-
my zdekodować hasło, inni również mogą to zrobić. Aby upewnić się, że nikt nie będzie
Rozdział 31. » Bezpieczeństwo i kryptografia_____________________________521
Funkcja crypt (password, salt) szyfruje podane hasło. Parametr salt powinien
być wybrany losowo podczas zapisywania hasła (PHP wybiera losową wartość w przy-
padku braku tego parametru). Funkcja zawraca złączenie wartości parametru salt i za-
szyfrowanego ciągu. Poniższa funkcja tworzy nowe hasło dla gościa:
function new_pw(Sgiven)
t
return crypt(Sgiven);
)
Błędy ludzi
Błędy popełniane przez ludzi są często niedocenianą furtką włamania do
systemów. Czasami łatwiej wyciągnąć dane (szczególnie hasła) od ludzi niż
od komputerów:
Cracker. Cześć, tu Adam z działu IT. Kiedy ostatnio używałeś twojego konta
firmowego?
Pracownik: Hmm, wprowadziłem kilka nowych zamówień około godziny temu.
Cracker. Obawiam się, że do twojego konto ktoś się włamał. Możemy utracić
część danych, co może kosztować firmę kilka milionów dolarów, jeżeli szyb-
ko nie namierzymy włamywacza. Potrzebujemy do tego twojego hasła.
Pracownik: No dobrze, moje hasło to...
Bardzo często użytkownicy notują hasła na kawałkach papieru leżących na
ich biurkach! Zdeterminowany włamywacz może łatwo dostać posadę noc-
nego stróża i przejrzeć te notatki.
522___________________________________Część III » Techniki zaawansowane
Włamywacz musi uzyskać dostęp do serwera przez wiersz komend Uniksa bądź
MS-DOS. Oczywiście jest to najtrudniejsze do wykonania, ale wysiłek opłaca się.
„Wewnątrz" serwera może spowodować najwięcej uszkodzeń, skopiować lub zmodyfi-
kować dane, skorzystać z mocy obliczeniowej serwera do otwarcia kolejnych furtek. Co
gorsza, zaawansowany włamywacz może zatrzeć ślady, zmieniając pliki śladów i usu-
wając pliki tymczasowe.
PHP posiada kilka funkcji umożliwiających uruchamianie programów: s y s t e m ! ) ,
exec ( ) , popen ( ) , passthru (} oraz operator „"". Przykładem użycia jednej z tych
funkcji jest poniższa strona, pokazująca dane, zwracane przez polecenie Unixa finger,
dla podanego poprzez formularz HTML użytkownika:
<HTML>
<HEADX/HEAD>
<BODY>
<FORM>Pobierz dane użytkownika: <INPUT TYPE="TEXT" NAME="username">,
< I N P U T TYPE="SUBMIT" VALUE="OK"X/FORM>
<?php if (IsSet(Susername)) { ?>
<Hl>Wyniki dla <?php echo Susername; ?></Hl>
<prex?php system ( "finger ". $username) ; ?x/pre>
<?php )?>
</BODY>
</HTML>
W systemie Unix polecenie rm -rf / usuwa wszystkie pliki z serwera. Wyobraź sobie
złośliwego gościa, który wpisze do formularza ; rm -rf / i naciśnie OK.
W ten sposób żadna wartość wprowadzona przez użytkownika nie spowoduje urucho-
mienia kolejnego programu. Nie zabezpiecza to przed podaniem niepoprawnych danych
do funkcji finger. W przypadku wprowadzenia niepoprawnych danych finger nie
spowoduje żadnych szkód w systemie, jednak programy mogą działać inaczej.
Bezpieczeństwo poczty
Poczta jest najmniej bezpiecznym protokołem internetowym. W trakcie wędrówki prze-
syłka może być kolejkowana w wielu pośrednich serwerach. Jeżeli serwer jest słabo za-
bezpieczony, włamywacz może łatwo przeczytać przechodzące przesyłki. Przesyłaj
więc pocztąjak najmniej ważnych danych. To znaczy, nigdy nie przesyłaj pocztą nume-
rów kart kredytowych i unikaj wysyłania haseł, chyba że jest to absolutnie niezbędne.
Interesujące, że większość z istniejących witryn nie stosuje się do drugiej zasady.
Administratorzy systemu
Administratorzy systemu, zwani również adminami, to ludzie, którzy dbają
o działanie komputerów i Internetu. Mają w zwyczaju tak programować ser-
wery, aby zawiadamiały natychmiast o każdej niezwykłej sytuacji; podejmują
szybkie i stanowcze akcje przeciwko niebezpiecznym zjawiskom.
Wykładowca na wydziale informatyki jednej ze szkół wyższych zadał studen-
tom jako pracę domową włamanie do jego systemu Linux. Aby ułatwić pracę,
dał im tekst zaszyfrowanego hasła (przeczytaj o funkcji crypt ( ) w części
„Uruchamianie dowolnych programów"). Żadnemu ze studentów nie udało
się włamać do jego systemu. Konta kilku z nich zostały zablokowane, po-
nieważ uruchamiali obciążające system programy o nazwie crack.
Jeżeli nie jesteś administratorem systemu, ale przejmujesz się bezpieczeń-
stwem witryny, zaprzyjaźnij się z twoim lokalnym administratorem. Zasugeru-
je ci sposoby uszczelnienia witryny i będzie w stanie pomóc w odtworzeniu
systemu po awarii.
524___________________________________Część III * Techniki zaawansowane
Jeżeli twoja witryna prosi użytkownika o podanie hasła, powinieneś dokładnie wytłu-
maczyć, w jaki sposób zostanie wykorzystane i do kogo wysłane. Jeżeli na stronie
WWW znajduje się adres e-mail, powinieneś tak go zmodyfikować, aby automatyczne
wyszukiwarki adresów, wykorzystywane do tworzenia SPAMU, nie mogły go łatwo zi-
dentyfikować. Najłatwiejszym sposobem jest zamiana symbolu @ słowem „at".
Jeżeli nie jest to absolutnie niezbędne, unikaj tworzenia łączy mailto:. To świetne
źródła adresów dla spammerów; są niewygodne dla użytkowników, którzy nie używają
przeglądarek WWW do wysyłania poczty.
Szyfrowanie
Szyfrowanie jest procesem zamiany otwartego tekstu w nieczytelny tekst zaszyfrowany.
Bez odpowiedniej informacji (pewnego rodzaju klucza) trudno odtworzyć otwarty tekst
z postaci zaszyfrowanej. Jednak ktoś, kto posiada właściwy klucz, może łatwo odszy-
frować tekst zaszyfrowany, odtwarzając oryginalny tekst (jeżeli funkcja szyfrująca nie
jest jednokierunkowa).
Alicja ma ważną informację dla Bogdana. Wraz z kluczami Alicja dostała instrukcję szy-
frowania przesyłki. Zapiszemy tę operację jako: Phoh(M). Alicja zaszyfrowała tekst klu-
czem publicznym Bogdana i włożyła tajemniczo wyglądającą przesyłkę do torby Kucyka.
Klucze naszych przyjaciół nie zostały wybrane dowolnie. Mają również specjalną
właściwość: jeżeli przekształci się tekst jednym kluczem, a następnie wynik przekształci
się za pomocą drugiego, otrzymamy oryginalny tekst. Oznacza to: S i j (P n j (M)) =
a iC a a C a
Bogdan wie, że nikt inny nie mógł odczytać przesyłki, ponieważ nikt nie ma jego klu-
cza prywatnego. Jednak nie wie, czy przesyłka na pewno pochodzi od Alicji, ponieważ
każdy, kto czytał gazetę, mógł wysłać tę przesyłkę podpisując się jako Alicja.
Teraz Alicja chce wysłać inną przesyłkę do Bogdana; tym razem chce upewnić odbior-
cę, że list pochodzi od niej. Najpierw przekształca tekst za pomocą swojego klucza
prywatnego i dodaje wynik na końcu przesyłki jako sygnaturę: M+SaiiCja(M). Przesyłka
w takiej postaci zostaje wysłana do Bogdana. Po przeczytaniu tekstu przesyłki Bogdan,
stosując się do instrukcji, dekoduje sygnaturę za pomocą jej klucza publicznego:
Paiwja(S<iiicja(M))=M i otrzymuje poprawny oryginalny tekst.
Ponieważ nikt nie zna klucza prywatnego Alicji, jest ona jedyną osobą, która mogła
utworzyć taką sygnaturę, więc adresat jest pewny. Zauważ jednak, że tym razem Alicja
wysłała do Bogdana niezaszyfrowany tekst. Każdy złoczyńca mógłby go przeczytać.
Alicja może najpierw podpisać tekst, a następnie zaszyfrować tekst razem z sygnaturą,
korzystając z pierwszej metody (efektem będzie podpisana i zaszyfrowana przesyłka).
W tym schemacie istnieje jednak słaby punkt. Bez spotkania się z Alicją Bogdan nie
może być pewien, że klucz publiczny znaleziony w gazecie należy na pewno do Alicji.
A jeżeli ktoś inny wydrukuje swój klucz pod tym samym imieniem? Jeżeli Bogdan ko-
responduje z wieloma osobami, nie będzie miał czasu na sprawdzanie klucza z osobą.
Załóżmy, że jest jedna osoba, której wszyscy ufają, Tomek. Przechowuje on zestaw
kluczy i oferuje podpisywanie przesyłek za pomocą jego klucza prywatnego w przy-
padku, gdy właściciel dokumentu potwierdzi swoją tożsamość. Alicja ma swój klucz
publiczny podpisany przez Tomka. Publikuje ten klucz, nazywany certyfikatem, w ga-
zecie. Bob sprawdza sygnaturę pliku za pomocą klucza zamieszczonego w gazecie
i klucza publicznego Tomka. Wie, że Tomek podpisał tę przesyłkę, musiał też zidenty-
fikować Alicję, więc klucz w gazecie na pewno należy do Alicji.
Szyfrowanie pojedynczym kluczem nie jest objęte patentem RSA, istnieje wiele jego
odmian, które nie są objęte embargo. Wersja PHP dla Unixa posiada zestaw funkcji re-
alizujących szyfrowanie pojedynczym kluczem przy użyciu biblioteki o nazwie mcrypt.
Aby użyć tych funkcji, należy ściągnąć i zainstalować mcrypt (łącze do źródła biblio-
teki jest podane w podręczniku PHP) oraz przekompilować PHP z opcją konfiguracji
--enable-mcrypt.
W czasie pisania tej książki PHP nie był zgodny z ostatnią wersją
mcrypt. Ostatnią dobrze działającą wersją była iibmcrypt-2.2. 6.
Jednak kompilując tę wersję mcrypt należy w czasie konfiguracji włą-
czyć opcję —disabie-posix-thread. Zignorowanie powoduje awa-
rię Apache.
Tabela 31.1.
Tryby szyfrowania dostępne w mcrypt
Ostatnie dwa tryby wymagają wektora inicjalizacji, który służy za stan początkowy dla
algorytmu szyfrowania. Różnica pomiędzy tymi trybami jest ważna dla trybu interak-
cyjnego, gdy szyfrowane są pojedyncze znaki. W takim przypadku ważne jest, aby al-
gorytm nie szyfrował 'a' w taki sam sposób za każdym razem. Jednak interfejs PHP do
mcrypt umożliwia tylko szyfrowanie ciągów, więc każdy z trybów jest akceptowalny.
Klucz musi mieć wielkość odpowiednią dla wybranego algorytmu szyfrowania. Aby
sprawdzić, jaka jest odpowiednia wielkość, należy użyć funkcji:
mcrypt_get_key_size(cipher)
Pamiętaj, że dane, na których działa mcrypt, mają postaci ciągów PHP z danymi binar-
nymi. Jeżeli chcesz wyświetlać je w postaci czytelnej lub zapamiętać w ciągu teksto-
wym, musisz je w jakiś sposób skonwertować. PHP posiada odpowiednie do takich
zadań funkcje base64_encode ( ) oraz base64_decode ( ) . Więcej informacji na ich
temat znajdziesz w podręczniku PHP.
Szyfrowanie cookie
Wysyłane przez twoją witrynę do przeglądarki cookies zawierają dane na temat użyt-
kownika. Gdy przeglądarka odsyła cookies z powrotem, witryna korzysta z zapisanych
danych do stworzenia nowej strony. Jednak cookie może być dowolnie zmodyfikowane
przez złośliwego użytkownika w celu zmylenia programu obsługi witryny. Za przykład
posłuży nam bardzo prosty program:
528___________________________________Część III » Techniki zaawansowane
<?php
$visits = Svisits + 1;
setcookie("visits", Svisits};
?>
<HTMLXHEADX/HEAD>
<BODY>
<Hl>Byłeś już u nas <?php echo Svisits; ?> razy</Hl>
</BODY>
</HTML>
Mamy tutaj licznik zliczający liczbę wizyt użytkownika na witrynie. Liczba ta prze-
chowywana jest w cookie visits. Użytkownik może je zmienić, aby wskazywało na
np. 10000. Nasz program oczywiście nie wie, że w rzeczywistości użytkownik ten od-
wiedzał stronę rzadziej, i wypisze „Byłeś już u nas 10000 razy".
Mcrypt operuje na ciągach danych binarnych, więc nie możemy ich bezpośrednio wpisać
lub przesłać do przeglądarki. W tym przypadku użyliśmy funkcji PHP base64. Zanim
uruchomimy ten program, utworzymy klucz DES za pomocą następującego programu:
<?php
$key_size = mcrypt_getKey_size(MCRYPT_DES);
$key = mcrypt_create_iv(Skey_size, MCRYPT_DEV_RANDOM);
echo base64_encode(Skey);
?>
Przykład ten pokazuje, że należy bardzo dobrze chronić pliki źródłowe. Jeżeli włamy-
wacz podejrzy treść programu, wejdzie w posiadanie klucza używanego do szyfrowania
witryny i będzie mógł łatwo odszyfrować wartość cookie.
Mieszanie
Podpisywanie dokumentów kluczem prywatnym powoduje powstawanie sygnatur
wielkości oryginalnego dokumentu. Staje się to problemem w przypadku, gdy chcesz
podpisywać naprawdę długie pliki. Większość oprogramowania związanego z bezpie-
czeństwem systemu (na przykład mcrypt) jest podpisywana cyfrowo, więc użytkownik
wie, że ostatnia wersja była naprawdę napisana przez autora. Administratorzy obawiają
się, że włamywacze będą podrzucać programy, które instalują „tylne wejście" do systemu.
Po zapisaniu tak spreparowanego pliku użytkownik może obliczyć sumę kontrolną, na-
stępnie odszyfrować sygnaturę i sprawdzić, że wyniki są identyczne. Użytkownik bę-
dzie korzystał z tego pliku, przyjmując, że stworzyła go rzeczywiście osoba podpisana.
Jak można się spodziewać PHP posiada zestaw funkcji mieszających. Bazują one na do-
stępnej publicznie bibliotece mhash. W podręczniku PHP znajduje się łącze do najnow-
szej jej wersji.
Funkcja mhash(typ, ciąg) oblicza wynik mieszania wartości parametru ciąg przy
użyciu metody podanej w parametrze typ. Typowymi wartościami tego parametru są
MCRYPT_MD5 i MCRYPT_SHA1. Kompletna lista możliwych wartości znajduje się w pod-
ręczniku PHP.
Protokół Secure Server Layer (SSL) pozwala na zrealizowanie takiej transmisji, unie-
możliwia podsłuchiwanie. Pozwala również, aby witryna udowodniła użytkownikowi,
że jest tą, za którą się podaje. Opcjonalną możliwością jest taka sama identyfikacja
użytkownika. Nie wgłębiając się w kryptograficzne szczegóły, SSL działa na podstawie
szyfrowania kluczem publicznym w celu udowodnienia autentyczności serwera, wy-
mienia się kluczami używanymi do szyfrowania konwersacji. Następnie przełącza się
na szyfrowanie pojedynczym kluczem, ponieważ jest o wiele szybsze.
Porównanie serwerów SSL wykracza poza zakres tej książki. Autorzy bezpłatnego opro-
gramowania wierzą, że takie implementacje są najlepsze i najpewniejsze. Wiele z ko-
mercyjnych serwerów SSL bazuje na bezpłatnych implementacjach! Jeżeli kupisz
komercyjną implementację, będziesz miał możliwość uzyskania pomocy od producenta.
Witryny podejmujące
problematykę bezpieczeństwa
Jeżeli po przeczytaniu tego rozdziału nie możesz zasnąć — nie przejmuj się. Każdy ad-
ministrator i projektant witryny boryka się z tymi samymi problemami. Istnieje wiele
witryn poświęconych bezpieczeństwu komputerów; prawie każda z nich zawiera pełny
opis ostatnio zidentyfikowanych zagrożeń i sposobów zabezpieczenia systemu przed
atakiem. Niektóre są przeznaczone dla „służb bezpieczeństwa", inne dla włamywaczy...
Wszystkie dostarczaj ą użytecznych i interesujących informacji.
532________________________________Część III » Techniki zaawansowane
Podsumowanie
Dla każdej znanej witryny WWW bezpieczeństwo jest jednym z kluczowych zagadnień.
Powinieneś być niezmiernie dokładny przy zabezpieczaniu serwera i prywatnych danych
użytkowników. W czasach rozwoju biznesu sieciowego publikacja artykułu o dużych lu-
kach w systemie bezpieczeństwa witryny może podważyć zaufanie użytkowników i odej-
ście do konkurencji.
Nie ufaj Internetowi. Każdy przychodzący bajt powinien być traktowany jako poten-
cjalnie niebezpieczny. Szczególnymi restrykcjami obejmij tworzenie dostępnych da-
nych wejściowych. Listy dostępnych wartości powinny być najbezpieczniejszym
rozwiązaniem. Upewnij się, że konfiguracja serwera WWW nie pozwala na podejrzenie
kodu źródłowego lub obejście praw dostępu.
Jeżeli prowadzisz własny serwer, spróbuj się do niego włamać. Jeżeli się uda, zidentyfi-
kujesz dziury, które możesz załatać, zanim skorzysta z nich ktoś inny. Jeżeli się nie uda,
odkryjesz tajniki bezpieczeństwa serwera. Jeżeli dzierżawisz serwer, spróbuj poznać
osobę, która może pomóc w zabezpieczaniu witryny.
534 Część III » Techniki zaawansowane
Rozdział 32.
Konfiguracja i dostrajanie
W tym rozdziale:
* Podglądanie zmiennych środowiska
* Konfigurowanie Apache dla PHP
* Konfigurowanie PHP
* Poprawianie wydajności
W rozdziale tym omówimy szczegółowo wiele opcji konfiguracji PHP, szczególnie te,
które są dostępne w wersji PHP będącej modułem serwera Apache. Celem rozdziału jest
to, abyś poznał plusy i minusy włączenia i wyłączenia każdej z opcji. Zapoznamy się
również ze sposobami mierzenia i polepszania wydajności skryptów PHP.
Trudno jest w pełni opisać konfigurację, ponieważ jest bardzo wiele możliwych kombi-
nacji parametrów — ściśle rzecz ujmując, około 25 silnia. W niektórych przypadkach
występuje oczywisty konflikt pomiędzy dyrektywami konfiguracji — musisz wybrać
jedną z nich. W innych przypadkach będziesz potrzebował kilku obejść. Spróbujemy
opisać konsekwencje działań, ile tylko się da. Jednak nikt nie może dać słowa, że prze-
testował wszystkie możliwe kombinacje.
Wersja PHP dla Windows ma niewiele opcji — szczególnie jeżeli wybierzesz firmowy
serwer WWW, taki jak Microsoft IIS. Początkujący użytkownicy PHP mogą uważać ten
brak wyboru za komfortowy; zrzucają całą odpowiedzialność na osobę, która utworzyła
program instalacyjny. Użytkownicy Windows muszą ustawić tylko zmienne dostępne
w pliku php.ini — nie wszystkie z nich mają zastosowanie w aplikacjach Windows.
Można więc bezpiecznie przejść do części rozdziału poświęconej plikowi php. ini.
Użytkownicy Unixa maj ą bogatszą paletę opcji. Aby w pełni skorzystać z tej mocy, mu-
sisz dokładnie poznać różne aspekty, pod kątem których przeanalizujesz i wyregulujesz
instalację PHP. W systemie Unix najważniejsze są:
* opcje kompilacji;
** pliki konfiguracyjne serwera WWW;
* p\\kphp.ini.
Istnieje również kilka opcji, które mogą być: sterowane opcjami ustawianymi w czasie
działania programu, ustawieniami systemu, istnieniem, brakiem i konfiguracją innych
pakietów oprogramowania.
Opcje kompilacji
W czasie konfigurowania kompilacji PHP pozwala ustawić wiele specyficznych znacz-
ników. Powoduje to dołączenie do modułu PHP odpowiednich plików.
-with-apache[=KATALOG]
Jest to najważniejsza opcja, ponieważ powoduje skompilowanie PHP do postaci sta-
tycznego modułu Apache. Mimo że w obecnie najczęściej stosuje się PHP w postaci
modułu Apache, projektanci PHP postanowili ustawić jako domyślne kompilowanie do
postaci CGI. Jeżeli będziesz chciał otrzymać moduł Apache i zapomnisz ustawić tę
opcję, otrzymasz wersję CGI.
Warto ustawić katalog bazowy dla Apache; make ustawia go domyślnie na /usr/local/
etc/httpd. Pamiętaj, że Apache instaluje się w innym katalogu w przypadku kompilowa-
nia ze źródeł niż w przypadku instalowania RPM —jeżeli wcześniej instalowałeś Apa-
che z RPM (na przykład w trakcie instalowania Red Hat Linux), będziesz musiał
najprawdopodobniej usunąć ten pakiet, aby pozostawić wolne miejsce dla wersji, którą
właśnie kompilujesz ze źródeł.
-with-apxs
Opcja ta wskazuje, że moduł PHP będzie utworzony jako dynamiczny moduł Apache.
Oszczędza to nieco miejsca na dysku, a niektórzy uważają, że kompilacja taka jest ła-
twiejsza. Dla większości użytkowników nie ma większej różnicy pomiędzy statycznym
a dynamicznym modułem Apache.
-with-fhttpd[=KATALOG]
Opcja powoduje skompilowanie PHP do postaci modułu serwera fhttpd. Domyślnym
katalogiem jest /usr/local/src/fhttpd.
-with-database[=KATALOG]
Wszystkie bazy danych obsługiwane przez PHP używają podobnych opcji kompilacji.
Podanie katalogu jest potrzebne jedynie w przypadku, gdy instalujesz bazę danych
w innym katalogu niż standardowy. Więcej informacji na temat wyboru bazy danych
dla PHP w rozdziale 16.
Tabela 32.1.
Opcje kompilacji związane z bazami danych
Bazy danych oznaczone w tabeli gwiazdką używaj ą komunikacji przez ODBC. Opcje te
wzajemnie się wykluczają — musisz ograniczyć się tylko do jednej. Dodatkową dy-
rektywą kompilacji, jakiej możesz użyć przy bazach danych, jest --disabie-
unif ied-odbc. Pomaga w unikaniu konfliktów pomiędzy bibliotekami ODBC.
Każda baza danych posiada nieco inne opcje konfiguracji w pliku php.ini lub innych
plikach konfiguracyjnych. Na przykład Oracle ma własne zmienne środowiska, które
omijają ustawienia PHP. Sybase używa innego stylu oznaczania apostrofów, dlatego
wymag opcji plikuphp.ini magic_quotes_sybase. MySQL pozwala podać domyślne
nazwy: komputera z bazą danych, użytkownika i hasło — nie jest to dobry pomysł,
chyba że dokładnie wiesz jakie, mogą być następstwa! Większość z tych opcji jest pro-
sta i łatwa do zrozumienia, ma niewielki wpływ na inne części PHP.
Rozdział 32. » Konfiguracja i dostrajanie________________________________539
-enable-track-vars
Opcja ta uaktywnia predefiniowane zmienne PHP HTTP_GET_VARS, HTTP_POST_VARS
i HTTP_COOKIE_VARS. Może być pomocna w sytuacji, gdy używasz w programach
wielu metod przekazywania danych. Pozwala na upewnienie się, skąd pochodzi każda
wartość. Możesz wyłączyć tę opcję, korzystając ze zmiennej pliku php.ini track_vars.
-with-ldap[=KATALOG]
Opcja ta pozwala PHP na podłączenie się do serwera LDAP (Lightweight Directory
Access Protocol). Domyślnym katalogiem jest /usr/local/ldap. Zanim skompilu-
jesz PHP, będziesz musiał ściągnąć i zainstalować biblioteki klienta LDAP z jednego
z tych źródeł:
http://developer.netscape.com/tech/directory/
http://www. openldap. org
LDAP jest protokołem, podobnym do bazy danych, który pomaga w zarządzaniu profi-
lami użytkownika i uprawnieniami w sieci. Można go zastosować do obsługi scentrali-
zowanej sieciowej książki adresowej, udostępniającej adresy e-mail klientom w sieci.
Pomaga również w zarządzaniu uprawnieniami — można ustawiać je w jednym miejscu
— jeżeli na przykład pracownik opuszcza firmę, możesz mu od razu odebrać wszystkie
uprawnienia.
*
Nie będziemy opisywali LDAP w tej książce, ponieważ obecnie protokół ten jest uży-
wany jedynie w wielkich sieciowych organizacjach, takich jak firmy międzynarodowe
czy rządowe laboratoria badawcze. Zapoznaj się z artykułem wprowadzającym do ko-
rzystania z LDAP, autorstwa Lincolna Steina:
http://www.webtechniques.com/archives/1999/ll/webm/
Teraz, gdy istnieje bezpłatna implementacja LDAP, coraz więcej odbiorców zaczyna
zeń korzystać. Rasmus Lerdorf, ojciec PHP, napisał krótką informację na temat użycia
LDAP w swojej sieci domowej:
http://www.webtechniques.com/archives/1999/05/junk/junk/shtml
540________________________-__________Część III » Techniki zaawansowane
-with-mcrypt[=KATALOG]
Opcja powoduje wbudowanie w PHP biblioteki mcrypt zawierającej wiele popularnych
algorytmów szyfrujących. Mcrypt jest dostępny na serwerze w Grecji:
ftp://argeas.cs-net.gr/pub/unix/mcrypt/
-with-xml
Opcja powoduje dołączenie analizatora XML expat. Jeżeli kompilujesz PHP do postaci
modułu Apache, nie potrzebujesz dodatkowej biblioteki, ponieważ expat jest wbudowa-
ny w Apache. W innym przypadku musisz ściągnąć bibliotekę z:
http://\vww.jclark. com/xml/
Aby używać XML, musisz wyłączyć skrócone znaczniki (<? ?>) za pomocą opcji
kompilacji --disable-short-tags lub dyrektywą w pliku php.ini short_open_tag.
Analizator PHP rezerwuje ten styl znaczników dla własnych potrzeb, więc PHP musi
o tym wiedzieć. Standardowe znaczniki PHP (</php ?>) to najlepszy wybór (poza
niektórymi przypadkami).
-with-dom
Opcja powoduje dołączenie obsługi DOM XML przy użyciu biblioteki GNOMĘ (na-
zywanej również libxml lub gnome-xml). Biblioteka ta wraz z opisem jest dostępna pod
adresem:
http://xxx.xmlsoft. org/
-enable-bcmath
Powoduje dołączenie funkcji pozwalających na obliczenia o dowolnej dokładności.
W plikuphp. ini można ustawić domyślną liczbę miejsc po przecinku.
-with-config-file-path= KATALOG
Opcja pozwala na określenie położenia pliku php. ini. Jest potrzebna jedynie wtedy, gdy
przeniesiesz ten plik do innego katalogu niż /mr/local/lib.
Rozdział 32. » Konfiguracja i dostrajanie________________________________541
-enable-url-includes
Opcja pozwala na dołączanie plików z odległych komputerów za pomocą protokołu
HTTP lub ftp za pomocą funkcji include (http : //remotehost/include . inc).
-disable-url-fopen-wrapper
Opcja wyłącza możliwość otwierania plików z innych serwerów w następujący sposób:
include(http://remotehost/include.inc).
-enable-magic-quotes
Opcja powoduje automatyczne oznaczanie cudzysłowów w tekstach pochodzących
z zewnętrznych źródeł danych, takich jak pliki tekstowe i bazy danych. Jeżeli nie uży-
jesz tej opcji, będziesz musiał dodawać i usuwać ukośniki. Możesz włączać i wyłączać
tę opcję za pomocą dyrektywy w pliku php.ini magic_quotes_runtime. W pliku
php.ini znajdują się dodatkowo ustawienia automatycznego oznaczania cudzysłowów
w zmiennych GET, POST i cookie, oraz dla oznaczania apostrofów dla Sybase.
-enable-debugger
Opcja umożliwia przejście do wbudowanego debuggera PHP. Niestety obecnie jest on
niestabilny i niezgodny z optymal i zatorem Zend.
-disable-syntax-hl
-enable-safe-mode
Tryb bezpieczny był zaprojektowany w postaci programu CGI dla PHP. Użytkownicy
modułu serwera muszą się nieco napracować, aby zapewnić podobne działanie — mu-
szą ustawić odpowiednio dyrektywy open_basedir i include_path w pliku php.ini.
Dla przykładu, do pobrania pliku z repozytorium CVS zwykle używana jest prawdziwa
nazwa użytkownika. Jeżeli bezpośrednio zmieniasz pliki w katalogu, z którego korzysta
serwer WWW, również jej używasz. PHP działający w trybie bezpiecznym nie może
skorzystać z takich plików, trzeba zmienić ich właściciela na nobody. Jeżeli używasz
dużej liczby plików i katalogów, ciągłe zmienianie właściciela może być czasochłonne.
Innym przykładem mogą być pliki z hasłami, umieszczone poza drzewem katalogów
dostępnych przez WWW, na przykład w /usr/local/lib. Ich właścicielem jest określony
użytkownik, a użytkownik PHP musi mieć możliwość je odczytać. W trybie bezpiecz-
nym niestety nie będzie to możliwe — musisz umieścić plik z hasłami w katalogu do-
stępnym dla serwera WWW.
Jeżeli nie masz dostępu do hasła administratora systemu, nie będziesz mógł używać try-
bu bezpiecznego (chyba że administrator tak skonfiguruje PHP, aby uruchamiał się na
prawach określonego użytkownika, korzystając z suExec lub jego odpowiednika). Bez
uprawnień administratora nie da się zmienić właściciela pliku na użytkownika nobody.
-with-exec_dir[=KATALOG]
Kolejną opcją związaną z trybem bezpiecznym jest —with-exec-dir. Powoduje
ustawienie domyślnego katalogu z programami do uruchamiania na /usr/local/bin.
Można go zmienić za pomocą dyrektywy pliku php.ini saf e_mode_exec. Pamiętaj, że
w trybie bezpiecznym możesz uruchamiać programy jedynie z tego katalogu.
-enable-discard-path
Jeżeli chcesz umieścić PHP w wersji CGI poza drzewem katalogów WWW i urucha-
miać go tak, jak skrypty CGI Perl (umieszczając w pierwszym wierszu każdego skryptu
# ! /usr/local/bin/php), musisz skorzystać z tej opcji kompilacji. Musisz też dodać
atrybut wykonywania dla wszystkich plików ze skryptami CGI PHP.
-enable-force-cgi-redirect
Ustawienie tej opcji jest konieczne ze względów bezpieczeństwa. Zabezpiecza ona
przed bezpośrednim uruchamianiem skryptów CGI przez użytkowników, które powo-
duje obejście zabezpieczeń Apache. Ale jeżeli korzystasz z innego serwera WWW, jest
bezużyteczna.
Nowe dyrektywy konfiguracji Apache stosowane sąjedynie do opcji, które można rów-
nież zmienić w p\ikuphp.ini.
Timeout
Wartość tego parametru określa liczbę sekund, zanim upłynie czas ważności wywołania
HTTP. W trybie bezpiecznym parametr ten jest ignorowany; musisz ustawiać go w pli-
ku php.ini.
DocumentRoot
Określa główny katalog dla wszystkich wywołań HTTP na tym serwerze. W systemie
Unix ustawienie to wygląda następująco:
DocumentRoot "/usr/local/apache_l.3.6/htdocs"
AddType
Aby pliki PHP były analizowane, musisz za pomocą tej opcji ustawić typy minie dla
PHP. Nie usuwaj pochopnie komentarza w wierszu ze źródłem PHP (.phps) tylko dlate-
go, że tak napisano w podręczniku instalacji. Pomyśl, w ilu sytuacjach będziesz chciał
ściągnąć źródło PHP z twojego serwera.
Action
Musisz zastosować ten wiersz, korzystając z PHP w postaci CGI dla serwera Apache,
szczególnie gdy używasz Windows.
Action application/x-httpd-php4 "/php/php.exe"
ScriptAlias
Musisz zastosować ten wiersz, korzystając z PHP w postaci CGI dla serwera Apache,
szczególnie gdy używasz Windows.
ScriptAlias /php/ "c:/php/"
Plik php.ini
Plik konfiguracyjny PHP, php.ini, jest ostatnią i najszybszą metodą wpływu na zachowanie
się PHP. Od czasu wprowadzenia wersji beta PHP w strukturze tego pliku zaszły poważne
zmiany, więc jeżeli do tej pory dokładnie mu się nie przyjrzałeś, teraz jest ku temu okazja.
Plik php.ini jest zawsze odczytywany w czasie inicjalizacji PHP — czyli za każdym
ponownym uruchamianiem httpd, w przypadku stosowania modułu serwera, lub za każ-
dym wykonaniem skryptu, w przypadku wersji CGI. Jeżeli zmiany, które wprowadziłeś,
nie przynoszą skutku, zatrzymaj i wystartuj httpd. Jeżeli nadal nie ma efektu, skorzystaj
546___________________________________Część III » Techniki zaawansowane
short_open_tag = Off
Krótkie znaczniki otwierające (nazywane w PHP 3 krótkimi znacznikami) wyglądają
następująco: <? ?>. W chwili wydawania książki nie działały one w PHP 4 i dlatego
użytkownicy PHP zmuszeni są do korzystania ze standardowych znaczników PHP
(<?php ?>). Opcja ta musi być wyłączona, jeżeli chcesz użyć funkcji XML.
safe_mode = Off
Jeżeli opcja ta jest ustawiona na On, prawdopodobnie kompilowałeś PHP z opcją
—enable-safe-mode. Tryb bezpieczny jest stosowany przy korzystaniu z wersji CGI
PHP. Dokładne objaśnienie tego trybu znajduje się we wcześniejszej części tego roz-
działu „Opcje kompilacji dla postaci CGI".
Opcja ta jest stosowana tylko w trybie bezpiecznym. Może być ona ustawiona również
za pomocą opcji kompilacji --with-exec_dir. W trybie bezpiecznym PHP będzie
uruchamiał jedynie programy znajdujące się w tym określonym katalogu. Domyślnym
katalogiem jest /usr/local/bin. Nie wpływa on na proces dostarczania stron HTML.
safe_mode_allowed_env_vars=[PHP_]
Opcja pozwala na wyszczególnienie zmiennych środowiska, które można zmieniać
w trybie bezpiecznym. Wartością domyślną są wszystkie zmienne rozpoczynające się od
PHP_. Jeżeli nie podamy żadnej wartości, będzie można zmieniać większość zmiennych.
Rozdział 32. « Konfiguracja i dostrajanie________________________________547
safe_mode_protected_env_vars=[LD_LIBRARY_PATH]
Opcja pozwala na podanie listy zmiennych środowiska, których nie można zmieniać
w trybie bezpiecznym, nawet gdy opcja saf e_mode_allowed_env_vars na to pozwala.
max_execution_time = 30
W trybie bezpiecznym nie działa funkcja set_time_limit ( ) , więc opcja ta jest pod-
stawowym sposobem na określenie maksymalnego czasu działania skryptu. W Win-
dows musisz określić taki warunek na podstawie ilości zużytej pamięci, a nie na
podstawie czasu. Jeżeli korzystasz z Apache, możesz również użyć analogicznej opcji
konfiguracji Apache (wpływa to na inne typy plików).
warn_plus_overloading = Off
Powoduje wyświetlenie ostrzeżenia w przypadku użycia operatora " + " dla ciągów, ta-
kich jak na przykład wartości pochodzące z formularza.
548___________________________________Część III « Techniki zaawansowane
variables_order = "EGPCS"
Ta opcja konfiguracji nadpisuje gpc_order. Pozwala na ustawienie kolejności inicjo-
wania różnych zmiennych: środowiska, GET, POST, cookie i serwera (zwanych wbudo-
wanymi). Możesz dowolnie zmieniać tę kolejność. Zmienne są inicjowane w kolejności
od lewej do prawej, więc leżąca skrajnie po prawej stronie zawsze „wygrywa". Oznacza
to, że jeżeli pozostawisz domyślne ustawienie i użyjesz tej samej nazwy zmiennych dla
zmiennej środowiska, zmiennej POST i zmiennej cookie, inicjowania zmienna będzie
miała wartość zmiennej cookie. Jednak na całe szczęście sytuacja taka nie zdarza się
zbyt często.
register_globals = On
Pozwala zadecydować, czy rejestrować zmienne EGPCS jako zmienne globalne. Jeżeli
używasz track_vars, prawdopodobnie nie będzie to konieczne.
gpc_order = G PC
Opcja niezalecana. Należy korzystać z nowej, bardziej szczegółowej opcji varia-
bles_order.
magic_quotes_gpc = On
Ustawienie to powoduje oznaczenie apostrofów w danych pochodzących ze zmiennych
GET, POST i cookie. Jeżeli używasz dużo formularzy, możesz ustawić tę dyrektywę na
On lub pamiętać o używaniu funkcji addslashes ( ) do danych tekstowych. Załóżmy,
że mamy formularz z polem, do którego użytkownicy wpisują swoje nazwiska:
<FORM METHOD="post" ACTION="formhandler.php">
<INPUT TYPE="text" NAME="surname" SIZE-25>
</FORM>
Załóżmy teraz, że formularz otworzy pan O'Donnell i wpisze swoje nazwisko. Bez
oznaczania apostrofów — wystąpi paskudny błąd składni. Z włączoną opcją oznaczania
— trzeba użyć funkcji stripslashes ( ) na zmiennej Ssurname przed jej wyświetle-
niem. Skrypt będzie działał bez błędów.
magic_quotes_runtime = Off
Opcja ta powoduje oznaczanie apostrofów w tekstach przychodzących z bazy danych.
Pamiętaj, że SQL oznacza apostrofy i cudzysłowy w czasie zapamiętywania tekstów
i nie usuwa ich podczas ich odczytywania. Jeżeli opcja ta jest ustawiona na off, musisz
skorzystać z funkcji stripslashes ( ) przy wypisywaniu danych pochodzących z bazy
danych SQL. Jeżeli opcja magic_quotes_sybase jest ustawiona na On, ta musi być
ustawiona na Of f.
Rozdział 32. » Konfiguracja i dostrajanie________________________________549
magic_quotes_sybase = Off
Opcja ta powoduje oznaczanie apostrofów w ciągach pochodzących z bazy danych za
pomocą ukośników stosowanych przez Sybase (zamiast lewych ukośników). Jeżeli
opcja magic_quotes_runtime jest ustawiona na On, ta musi być ustawiona na Off.
auto-prepend-file = [scieżka_do_pliku]
Jeżeli podamy tutaj ścieżkę, PHP będzie automatycznie dołączał ją na początku każdego
pliku PHP. Istnieją ograniczenia w dołączaniu plików. Pamiętaj o tym, że nie możesz
dwa razy wysyłać nagłówka HTTP.
auto-append-file = [scieżka_do_pliku]
Jeżeli podamy tutaj ścieżkę, PHP będzie automatycznie dołączał ją na końcu każdego
pliku PHP, chyba że wcześniej użyjesz funkcji exit ().
include_path = [KATALOG]
Jeżeli ustawisz wartość tego parametru, będziesz mógł dołączać jedynie pliki znajdujące
się w tych katalogach. Katalog plików dołączanych znajduje się zwykle w drzewie ka-
talogów WWW — to wymóg pracy w trybie bezpiecznym. Ustawienie tej opcji na . /
spowoduje dołączenie plików jedynie z katalogu, w którym znajduje się skrypt.
doc_root = [KATALOG]
Jeżeli używasz Apache, na pewno ustawiłeś katalog dokumentów serwera w pliku
httpd.conf. Ustawienie tej opcji jest potrzebne w przypadku pracy w trybie bezpiecz-
nym, bądź jeżeli chcesz włączyć obsługę PHP jedynie we fragmencie witryny (na przy-
kład w jednym podkatalogu drzewa WWW).
upload_tmp_dir = [KATALOG]
Nie zmieniaj tego wiersza, zanim nie poznasz wszystkich konsekwencji ładowania pli-
ków na serwer przez HTTP!
session.save-handler = files
Opcja opisana w rozdziale „Sesje".
ignore_user_abort = [On/Off]
Opcja pozwala na kontrolowanie zachowania PHP, gdy użytkownik kliknie przycisk
Stop w przeglądarce. Domyślną wartością parametru jest On, co oznacza, że skrypt bę-
dzie działał nadal, do zakończenia lub przekroczenia dopuszczalnego czasu. Jeżeli
ustawisz opcję na Off, skrypt zostanie przerwany. Opcja ta działa jedynie w przypadku
pracy PHP jako modułu serwera.
550___________________________________Część III » Techniki zaawansowane
Zanim zaczniemy poprawiać wydajność, musimy umieć ją mierzyć. Zend pracuje nad
komercyjnym narzędziem do tego celu, ale w czasie pisania książki nie było jeszcze do-
stępne. Dlatego skorzystamy ze stosowanego od dawna sposobu mierzenia wydajności:
liczenia mikrosekund. Napiszmy małą funkcję:
function exec_time()
(
$mtime = explode( " ", microtime());
5msec = (double)$mtime[0] ;
$sec = (double)$mtime[1];
return Ssec + 5msec;
)
Wstaw ją lub dołącz do skryptu, który chcesz zoptymalizować. Podzielmy teraz treść
skryptu na części i dodajmy w strategicznych miejscach wywołania do funkcji exec_
time():
<?php
$start_db_call = exec_time();
Sresult = mysql_db_query("test", "SELECT * from user WHERE ID=1");
while ($testrow = mysql_fetch_array($result))
echo $testrow[0];
$end_db_call = exec__time () ;
Sruntime = $end_db_call - start_db_call;
echo "Odwołanie do bazy danych i wypisanie wyniku zajęło
Sruntime sekund";
?>
Wywołaj zmodyfikowaną stronę. Skrypt teraz będzie sam podawał czas wykonania.
Teraz wiesz, jak długo trwa wykonywanie poszczególnych części skryptu, więc możesz
rozpocząć poprawianie wydajności. Oczywiście trzeba pamiętać, że wykonywanie
funkcji używające plików lub odwołujące się do innych procesów trwa dłużej niż
skryptów zawartych tylko w jednym pliku. Odwołania do bazy danych, funkcje inclu-
de ( ) i require ( ) , obiekty z dziedziczeniem, analiza XML zajmują więcej czasu niż
Rozdział 32. » Konfiguracja i dostrajanie________________________________551
Mimo że byłoby najlepiej usunąć wszystkie błędy z kodu, można ustawić w Apache lub
PHP maksymalny czas wykonywania skryptu lub maksymalną ilość używanej pamięci.
Żadna strona nie potrzebuje przecież na utworzenie 300 sekund. Inną opcją konfiguracji
pomocną przy bardzo powolnych skryptach jest ignore_user_abort w pliku php. ini.
Optymalizator Zend
Do niedawna fanatycy prędkości mieli niewielki wybór, oprócz własnych
funkcji mierzących czas bazujących na funkcji microtime(). W tej chwili
mamy nowe bezpłatne narzędzie: optymalizator Zend. Program ten wykonuje
kilka przebiegów przez skrypt PHP, zastępując wolniejsze konstrukcje szyb-
szymi, o takim samym działaniu. Zend przewiduje od 40 do 100% poprawy
wydajności w stosunku do zwykłego kodu PHP (im bardziej skomplikowana
logika skryptu, tym więcej ulepszeń).
Produkt wzbudza kontrowersje. Niektórzy nie korzystają z optymalizatora, ani
innych produktów Zend, ponieważ nie są „politycznie poprawne". Bardziej
pragmatyczni użytkownicy nie widzą żadnego problemu w tym, że bogate fir-
my muszą zapłacić za komercyjne dodatki. Każdy użytkownik PHP może wy-
brać odpowiedni dla siebie sposób licencjonowania.
Możesz ściągnąć optymalizator Zend z witryny http://www.zend.com/php/
optimizer.php. W chwili wydawania książki optymalizator i debugger nie były
ze sobą zgodne; optymalizator wymagał kompilacji bez trybu uruchamiania.
W systemie Unix można to osiągnąć, kompilując PHP bez opcji —with-
debugger, w Windows potrzebna jest specjalna wersja, bez informacji dla
debuggera, dostępna na witrynie Zend. Ponieważ niewielu użytkowników
używa tego debuggera, to niewielki problem.
Innym produktem firmy Zend, który dodatnio wpływa na wydajność, jest bu-
for PHP. Kompiluje on i przechowuje w pamięci wywoływane strony, co
zmniejsza liczbę operacji dyskowych i kompilacji skryptów, przyspieszając
działanie witryny. W czasie wydawania książki nie były jeszcze ustalone
szczegóły licencjonowania, ale przewiduje się go dla dużych i mocno obcią-
żonych witryn. Z czasem, gdy PHP będzie bardziej popularny na dużych wi-
trynach, możemy spodziewać się napływu narzędzi optymalizujących z Zend
i innych firm.
552___________________________________Część III » Techniki zaawansowane
Podsumowanie
Zaletą i wadą konfiguracji PHP jest to samo: mamy wiele opcji i wiele sposobów na ich
ustawienie. Moduł Apache w systemie Unix ma szczególnie dużo opcji, ale zespół pro-
jektantów PHP długo pracował nad jego elastycznością.
Istnieją trzy główne sposoby konfigurowania PHP. Pierwszym, dostępnym dla tych, któ-
rzy sami budują PHP, jest użycie opcji kompilacji. Wiele z tych dyrektyw to niezbędne
warunki wstępne (ustawiające warunki domyślne), które należy później potwierdzić lub
odwołać. Drugim sposobem jest skorzystanie z plików konfiguracji Apache (httpd.conf
i .htaccess), które są dostępne jedynie użytkownikom serwera Apache. Trzecią metodą
jest użycie p\ikuphp.ini, który jest zawarty w każdej instalacji PHP.
W PHP 4 wprowadzono wiele zmian w pliku php.ini. Jedną z najważniejszych jest
zdolność do indywidualnego wyłączania funkcji. Niektóre opcje w tym pliku (PHP 3
i PHP 2) stały się przestarzałe, np.: gpc_order (przesłonięta przez variables_order).
Plikphp.ini nie jest już absolutnie niezbędny w systemie Windows — PHP 4 przyjmuje
teraz wartości domyślne, jeżeli nie ma go na ścieżce Windows.
Podobieństwa
W tej części wymienimy kilka cech (na pewno nie są to wszystkie), które powodują, że
PHP jest bardzo podobny do C.
Składnia
Ogólnie mówiąc, składnia PHP jest identyczna ze składnią C: kod jest niezależny od
odstępów, wyrażenia zakończone są średnikami, wywołania funkcji mają taką samą
strukturę ( f u n k c j a (wyrażeniel, wyrażenie2)), nawiasy klamrowe ({ i }) łączą
wyrażenia w bloki. PHP ma komentarze zarówno w stylu C, C++ (/* */ oraz //), jak
i w stylu Perl i skryptów powłoki (#).
Operatory
Operatory przypisania (=, +=, *= itd.), operatory logiczne ( & & , | |, !), operatory porów-
nania (<, >, <=, >=, ==, ! =) oraz podstawowe operatory arytmetyczne (+, -, *, /, %) za-
chowują się tak jak w C.
556___________________________________________________Dodatki
Struktury sterujące
Podstawowe struktury sterujące (if, switch, while, for) działają identycznie jak
w C, łącznie z konstrukcjami break i continue.
Różnice
Mimo że PHP odziedziczył wiele z C, ma również cechy innych przodków (Perl
i skryptów powłoki) oraz kilka unikalnych własności, które nie pochodzą z C.
Znaki $
Wszystkie zmienne są poprzedzone znakiem $. Nie trzeba deklarować zmiennych przed
ich użyciem; nie mają one zdeterminowanego typu. Typ ostatnio przypisanej wartości
jest bieżącym typem zmiennej. Kod w PHP odpowiadający następującemu w C:
double my_number;
ray_numtier - 3 . 1 4 1 5 9 ;
Typy
W PHP istnieją tylko dwa typy numeryczne: integer (odpowiadający typowi long
w C) i double (odpowiadający double w C).
Ciągi mają dowolną długość. Nie ma osobnego typu znakowego (funkcje wymagające
znaku w C jako argumentu, w PHP zwykle oczekują ciągu z jednym znakiem, na przykład
ord ( ) ) . Do PHP 4 wprowadzony został pełnowartościowy typ boolean (wartości TRUE
lub FALSE).
Konwersja typów
Typy nie są sprawdzane w czasie kompilacji, a błędy konwersji typów zwykle nie wy-
stępują w czasie wykonywania. W zamian zmienne i ich wartości są automatycznie
Dodatek A « PHP dla programistów C_________________________________557
Tablice
Tablice są syntaktycznie prawie identyczne do tablic w C, ale ich budowa jest odmienna.
Są one tablicami asocjacyjnymi; „indeks" może być zarówno liczbą, jak i ciągiem. Nie
muszą być wstępnie deklarowane i przydzielane.
Brak struktur
W PHP nie ma typu struct, częściowo dlatego, że tablice oraz typ obiektowy dosko-
nale go zastępuj ą (elementy tablicy PHP nie muszą być tego samego typu).
Obiekty
PHP ma jedynie podstawową składnię obiektową, która umożliwia definiowanie klas
z ich składowymi danymi i funkcjami. Nie ma destruktorów (zajrzyj do części „Zarządza-
nie pamięcią" poniżej).
Brak wskaźników
W PHP nie ma wskaźników, chociaż zmienne pozbawione typu odgrywają podobną
rolę. PHP obsługuje referencje do zmiennych.
Brak prototypów
Funkcje nie muszą być deklarowane, zanim zostaną zdefiniowane. W PHP 3 należy je-
dynie definiować funkcje przed ich użyciem (wywołanie jednej funkcji z ciała innej nie
jest traktowane jako użycie przed definicją). Wystarczy, że funkcja zostanie zdefinio-
wana przed jej wywołaniem. W PHP 4 funkcje nie muszą nawet poprzedzać wywołań,
ponieważ są prekompilowane przed użyciem.
Zarządzanie pamięcią
Maszyna PHP posiada efektywny mechanizm zbierania śmieci (zliczający odwołania);
małe skrypty nie wymagaj ą żadnego zwalniania pamięci. Możesz bez obaw przydzielać
pamięć nowym strukturom — jak na przykład ciągi lub obiekty — ponieważ zostaną
usunięte, gdy tylko skrypt się zakończy. Jeżeli musisz zwolnić pamięć w czasie wyko-
nania skryptu, wywołaj unset ( ) na zmiennej. Zasoby zewnętrzne (jak na przykład
wyniki pochodzące z bazy danych) mogą być jawnie zwalnianie w skryptach; jest to ko-
rzystne jedynie w przypadkach, gdy skrypt używa zbyt dużo pamięci.
558 __________________________________________________Dodatki
Kompilacja i łączenie
W PHP nie ma osobnego etapu kompilacji skryptów — można od razu zobaczyć wynik
po edycji. Błędy i ostrzeżenia wypisywane są w przeglądarce. Dynamiczne ładowanie
bibliotek zwykle nie następuje (choć istnieje taka możliwość) — o tym, jakie grupy
funkcji będą dostępne w skryptach, decydujesz w czasie konfigurowania PHP.
Tolerancyjność
Można powiedzieć, że PHP wybacza więcej niż C (szczególnie w stosowaniu typów).
Dzięki temu w skryptach pojawia się nowa kategoria błędów. Niespodziewane wyniki
występują o wiele częściej niż błędy wykonania. Przy domyślnym ustawieniu poziomu
raportowania błędów PHP nie ostrzega o użyciu niezainicjowanej zmiennej (ale zwraca
rozsądne wyniki zamiast śmieci). Lepiej chyba zobaczyć ostrzeżenie, więc warto usta-
wić wyższy poziom raportowania błędów, używając wyrażenia error-reporting
(E_ALL) (lub error-reporting ( 1 5 ) ) na początku skryptu, albo też ustawiając po-
ziom raportowania błędów na stałe (modyfikacja p\ikuphp.ini).
Przewodnik po książce
Nie zakładaliśmy, pisząc tę książkę, że użytkownik zapoznał się wcześniej z C. Ponie-
waż PHP korzysta w wielu miejscach z C, niektóre rozdziały mogą tyczyć znanej czy-
telnikowi problematyki, (np. część I, która omawia wprowadzenie do języka).
Tabela A.l.
Przewodnik po części I dla programistów C
Podobne koncepcje
Porównując z dalszej perspektywy, PHP i ASP są bardzo podobne (uprzedzając pytanie
przypominamy, że PHP powstał przed ASP, więc nie można mówić o kopiowaniu Micro-
softu). Oba standardy dają się wbudować w HTML, nie opartymi o znaczniki preproce-
sorami skryptów wykonywanych na serwerze. W większości przypadków można je
stosować zamiennie.
Możemy stwierdzić, że wszystko, co potrafi ASP, PHP potrafi zrobić lepiej (może poza
obsługą sesji i zmiennych aplikacji). W porównaniu z ASP PHP w większości przypad-
ków jest zgodne z hasłem NASA: „Lepiej, szybciej, taniej".
Główne różnice
Pomiędzy tymi dwoma językami można znaleźć różnice w składni, modelu obiektowym
i filozofii.
562___________________________________________________Dodatki
Jak widać, składnia C używa większej liczby symboli, wcięć i nawiasów do grupowa-
nia. Składnia PHP jest jeszcze elastyczniejsza od C. W naszym przykładzie nawiasy
klamrowe nie są obowiązkowe, ponieważ korzystamy tylko z jednego wyrażenia po
warunku. Dodatkowo wstawiliśmy gałąź elseif tylko dla celów demonstracyjnych.
W PHP zarówno print, jak i echo (które sąw większości zastosowań wymienne) nie
wymagają nawiasów wokół drukowanych ciągów, więc raz ich użyliśmy, a raz nie.
Jeżeli chcesz poznać składnię C ponad poziom przedstawiany w tabelach na końcu tego
dodatku, skorzystaj z jednego z wielu bezpłatnych samouczków języka C dostępnych
w Internecie. Możesz skorzystać ze świetnej (krótkiej) książki Patricka Henry'ego Win-
stona „On to C" (Addison-Wesley, Nowy Jork, 1994).
Mimo że PHP posiada obsługę obiektów i klas, nie jest całkowicie obiektowy i prawdopo-
dobnie nigdy nie będzie. Każdy może wybrać sobie notację, która będzie najlepiej spełniała
potrzeby. Kod obiektowy najlepiej sprawdza się w dużych projektach wielu programistów,
a do tej pory większość witryn WWW tworzonych w PHP jest relatywnie niewielka i nie ma
tak naprawdę potrzeby stosowania obiektów. Jeżeli firmy częściej będą stosowały PHP,
programowanie obiektowe stanie się na pewno bardziej popularne.
PHP został zaprojektowany jako „klej" internetowy. Bardzo łatwo jest łączyć różne tech-
nologie za pomocą PHP — a w czasach szybkich zmian technologii sieciowych jest to
bardzo pomocne. Możesz więc swobodnie korzystać z wszystkich zalet serwerów usłu-
gowych, wiedząc, że masz możliwość dokonania zmian wtedy, gdy zajdzie taka potrzeba.
PHP jest wyjątkowy, został od podstaw zaprojektowany jako język skryptów serwera
WWW. Nie jest to środowisko obciążone powstałymi już aplikacjami. Projektanci PHP
podejmowali decyzje w oparciu o prawdziwe potrzeby programistów WWW i niezależ-
nie od tego, że języki programowania użyte do zbudowania konkretnej aplikacji reali-
zowały zadania w jakiś określony sposób.
Ponieważ źródła PHP mają otwarty charakter, nowe funkcje są szybko dodawane (zbyt
szybko dla zmęczonych autorów książek technicznych) i wprowadzane przez inne fir-
my, bez ograniczeń. Na przykład w PHP 4 pojawiła się obsługa DOM XML i Gnu Get-
text, COM i CORBA, CyberCash i Shockwave Flash. Cykl produkcyjny Microsoftu jest
wolniejszy. Firma wspiera jedynie własne produkty, poza tymi, które są powszechnie
stosowane (na przykład Oracle). Nie ma nic złego w takiej filozofii, chociaż ogranicza
ona twórców, którzy chcą mieć dostęp do szerokiej gamy najlepszych produktów.
Jeżeli dobrze czujesz się w C, możesz „podnieść maskę" PHP i właściwie dowolnie go
zmodyfikować. Zaawansowani użytkownicy PHP rutynowo wyłączają funkcje, dodają
własne rozszerzenia, próbują stworzyć PHP dla innego serwera WWW lub włączyć nową
bibliotekę. Przeglądają oni również kod PHP w poszukiwaniu nieudokumentowanych
funkcji. Programiści C mogą dodawać własne funkcje do PHP, biorąc udział w projekcie.
Ostatnim, ale nie najmniej ważnym zagadnieniem jest kwestia wsparcia technicznego.
PHP oferuje na swojej witrynie podręcznik, który zawiera wszystkie aspekty PHP
— podaje nawet wersję dla komputerów PalmPilot. Od dawna istnieje lista dyskusyjna,
szeroko znana z aktywności i wzajemnego przyjaznego nastawienia subskrybentów.
Często się zdarza, że odpowiedzi pochodzą od twórców fragmentu, którego dotyczyło
pytanie. Istnieje również wiele dobrze zorganizowanych witryn WWW dotyczących
PHP, zawierających artykuły, tablice ogłoszeniowe i przykłady kodu. Pojawiają się
również firmy oferujące wsparcie dla komercyjnych projektów prowadzonych w PHP.
Przy okazji warto wspomnieć, że PHP nic nie kosztuje. Oprócz wsparcia komercyjnego
i niektórych serwerów (na przykład Oracle i SQL Server), wszystkie funkcje PHP są
dostępne bez opłat. Oczywiście skomercjalizowany rynek dodatków do ASP i narzędzi
dla programistów kwitnie.
Dodatek B » PHP dla programistów ASP________________________________565
Autor tego programu Michael Kohn, który ma własne zdanie na temat ASP i nie wsty-
dzi się go wyrazić na swojej witrynie (jego motto brzmi: „Opór nie jest bezskuteczny").
Żaden program nie będzie jednak w stanie skonwertować kodu do ostatniego apostrofu
— będziesz musiał ręcznie zmieniać skrypty. Ale program ten pomaga w niektórych
powtarzających się czynnościach, które i tak trzeba wykonać.
Tabela B.l.
Operatory ASP i PHP
/ lub \ /
&
.=
Eqv Brak odpowiednika, używaj operatorów bitowych
" Pow()
Imp Brak odpowiednika, używaj operatorów bitowych
Is (referencja obiektu) Niepotrzebny w PHP
Mód %
And And, &&
Not i
566____________________________________________________Dodatki
Tabela B.l.
Operatory ASP i PHP (ciąg dalszy)
Xor xor
= ==, ===
> >
< <
Tabela B.2.
Wyrażenia ASP i funkcje PHP
Tabela B.2.
Wyrażenia ASP i funkcje PHP (ciąg dalszy)
lub
continue;
Option Explicit Brak odpowiednika
Private WazwaZmiennej Brak odpowiednika
Public N a z w a Z m i e n n e j Brak odpowiednika
Randomize [liczba] m t srand( (double) m i c r o t i m e 0 * 1 0 0 0 0 0 0 )
lub
s r a n d l (double) microtime 0 * 1 0 0 0 0 0 0 )
ReDim WazwaZmiennej Brak odpowiednika
Rem [Komentarz] // [Komentarz]
lub lub
' [Komentarz] /* [Komentarz] */
lub
# [Komentarz]
Select Case [ wyrażenietestujące] switch ($ na zwą Zmiennej)
[Case [lista warunków] {
[wyrażenia] ] case [ wartość] : [f unkcje] ;
[Case [ l i s t a warunków] break;
[ wyrażenia-n] ] case [wartość] : [ f u n k c j e ] ;
End Select break;
)
Set NazwaZmiennej = [wyrażenie] $NazwaZmiennej = [wyrażenie]
Public/Private Sub function NazwaFunkcj i ( [argumenty] )
WazwaPodprogramu ( [argumenty] ) f
[ wyrażenie] [funkcje] ;
End Sub }
568___________________________________________________Dodatki
Tabela B.2.
Wyrażenia ASP i funkcje PHP (ciąg dalszy)
lub
while ( [ warunek] ) :
[funkcje] ;
endwhile;
Typy zmiennych
W PHP nie musisz jawnie definiować typów. Lepiej zrobić to w przypadku liczb
zmiennoprzecinkowych, ale w innych nie jest to konieczne, a PHP sam manipuluje ty-
pami. Więcej na ten temat w rozdziale 6.
State
Istnieje niewielki związek pomiędzy starymi używanymi w języku VBScript a stałymi PHP.
PHP posiada niewiele wbudowanych stałych, jak na przykład PHP_VERSION i PHP_OS.
Dodatek B » PHP dla programistów ASP________________________________569
Możesz również definiować stałe w programie, co przydaje się jedynie, gdy dołączasz
wiele plików i definiujesz wiele funkcji.
Klasy i obiekty
Szczegółowy opis klas i obiektów PHP znajduje się w rozdziale 30. W skrócie można
powiedzieć, że klasy w PHP są niczym więcej niż wygodną metodą opakowania często
używanych kombinacji zmiennych i funkcji. Jeżeli naprawdę jesteś fanem programo-
wania obiektowego (choć w tym przypadku Python będzie bardziej odpowiedni dla cie-
bie), upewnij się, że czas, który zaoszczędziłeś, jest wart spadku wydajności. Wielu
zaawansowanych użytkowników PHP (włączając w to twórcę języka Rasmusa Lerdor-
fa) nigdy nie używało obiektów.
Włączanie plików
Mechanizm włączania plików w PHP jest dynamiczny, w przeciwieństwie do ASP. Po-
zwala na łatwe dołączanie fragmentów PHP lub HTML z osobnych plików. Składnia
jest następująca:
ASP
< ! — ł ł i n c l u d e f i l e = "ścieżka" — >
PHP
include("ścieżka");
lub
include_once("ścieżka");
lub
require("ścieżka");
Pomiędzy ASP i PHP występuje jeszcze wiele różnic; ten rozdział porusza najważniej-
sze zagadnienia, które musisz poznać, aby szybko zacząć pracę z PHP.
570________________________ Dodatki
Dodatek C
PHP
dla programistów HTML
Dodatek ten zawiera porady dla projektantów HTML, którzy chcieliby zaprząc do pracy
na serwerze język o większych możliwościach. Chociaż z drugiej strony, jeśli znasz już
ASP, JavaScript lub inny prawdziwy język programowania, nie skorzystasz zbyt wiele
na lekturze tego dodatku.
Dobre nowiny
Jeżeli biegle znasz HTML, korzystanie z PHP nie będzie zbyt dużym problemem. Po-
nieważ PHP jest wbudowywany w HTML, rozszerzanie statycznych stron HTML za
pomocą języka programowania jest naturalnym postępem. Mamy więc wiele powodów,
aby sądzić, że możesz dosyć szybko nauczyć się PHP.
Znajomość HTML
Prawdopodobnie masz sporo praktyki przy uruchamianiu stron HTML. Wiele błędów
powstaje w części HTML skryptów w czasie przełączania pomiędzy trybami, więc umie-
jętność sprawnego czytania i modyfikacji HTML jest tutaj bardzo ważna. Dodatkowo
komunikaty o błędach niosą ze sobą o wiele więcej informacji niż te zawarte w HTML.
Wyniki twojej pracy będą na pewno wyglądały dużo lepiej niż naszej, ponieważ wiesz
o wiele więcej na temat projektowaniu układu strony. Pokaż więc całemu środowisku
PHP, że witryny nie muszą być brzydkie lub ubogie.
572_____________________________________________________Dodatki
I szczerze mówiąc, PHP pozwala nauczyć się tylko tych części, które mogą się przydać.
Jeżeli nie będziesz pisał ogromnych funkcji matematycznych, możesz opuścić rozdział,
który je omawia. A jeżeli kiedyś będziesz potrzebował się tego nauczyć, możesz sko-
rzystać z gotowych funkcji.
Złe nowiny
Zanim przejdziemy dalej, musimy cię lojalnie uprzedzić, że zanim staniesz się zaawan-
sowanym użytkownikiem PHP, po drodze będziesz musiał pokonać kilka przeszkód.
Jeżeli znasz nieco JavaScript lub w szkole miałeś zajęcia z wprowadzenie do C, na pewno
niektóre zagadnienia przypomną ci się w czasie nauki PHP. Zapewne istnieją kursy, na któ-
rych możesz wygodnie nauczyć się PHP, jeżeli oczywiście odpowiada ci taka forma nauki.
Dodatek C » PHP dla programistów HTML_______________________________573
Jeżeli nie jesteś do tego zmuszony, nie próbuj nauczyć się wszystkiego na raz. Najważ-
niejszym zadaniem jest zapoznanie się z serwerem WWW. Apache jest potężny, ale
niestety wymaga dogłębnego poznania. Zapewne będziesz chciał nauczyć się SQL, je-
żeli jeszcze go nie znasz; obsługa poczty również jest bardzo przydatna. Po opanowaniu
tajników tej trójcy kolejne etapy nauki będą łatwiejsze.
Myślenie o programowaniu
Jak wspomnieliśmy, nauczenie się programowania wymaga nieco czasu, praktyki i prze-
glądania wielu przykładów. Nie ma żadnego sposobu na skrócenie tego procesu.
1. Zapisz zadania, jakie strona ma realizować. Ważne jest, żeby opis ten był
kompletny.
Strona powinna wyświetlać formularz wypełniony odpowiedziami, które
zostały poprzednio wprowadzone, z możliwością ich zmiany. Chcę,
aby strona ta była zabezpieczona hasłem, wiec potrzebuję identyfikatora
użytkownika z okna logowania.
2. Podziel opis na etapy i zadania, jak w przypadku przepisu. Jeżeli jest to ko-
nieczne, zmień kolejność.
1. Pobierz identyfikator użytkownika ze strony logowania.
Jeżeli nie ma go, nic nie wyświetlaj.
2. Wyświetl formularz HTML.
3. Wyświetl w formularzu wszystkie stare wartości z bazy danych
a) podłącz się do bazy danych
b) odczytaj dane związane z aktualnym elementem
c) wstaw do formularza HTML jako "value=X".
4. Zmień wartości i wstaw je do bazy danych.
5. Przekaż identyfikator użytkownika do następnej strony
3. Każdy z tych punktów zamień na kod PHP. Zwykle łatwiej jest zacząć od
głównych zadań — na przykład wysyłania poczty lub wypisywania jakichś in-
formacji na ekranie, niż od zadań pobocznych, jak podłączanie się od bazy da-
nych. Jeżeli będziesz potrzebował podłączenia do bazy danych, skorzystaj
chwilowo ze zmiennej, tablicy lub dołączanego pliku.
1. Pobierz identyfikator użytkownika ze strony logowania.
Jeżeli nie ma go, nic nie wyświetlaj.
<HTMLXHEADX/HEAD>
<BODY>
<FORM>
Imię: <input type="text" size=30 name=" FirstName"XBR>
Nazwisko: <input type="text" size=30 name = "LastName"xBR>
E-mail: <input type="text" size=30 name = "Email"XBR>
Możesz przy okazji, wykorzystując program obsługi serwera, utworzyć bazę danych,
nawet jeżeli w PUP istnieją narzędzia realizujące te same zadania. Program phpMy-
Admin jest bardzo sprytnym i wygodnym narzędziem do obsługi bazy danych MySQL,
ale początkujący administrator serwera MySQL nauczy się więcej korzystając z bardziej
prymitywnego interfejsu wiersza poleceń MySQL.
Nigdy nie kasuj wierszy zawierających słowa kluczowe (na przykład if, while lub for).
i
576___________________________________________________Dodatki
Na początek unikaj
Istnieje kilka zagadnień mało znanych koderom HTML, zwykle niepotrzebnych do pi-
sania funkcjonalnego kodu PHP. Przynajmniej na początku unikaj następujących za-
gadnień.
Obiekty
Obiekty mogą mylić nawet doświadczonego programistę „nieobiektowego". Ponieważ
PHP nie jest językiem w pełni obiektowym, „notacja obiektowa" może powodować
problemy w późniejszej pracy.
Prosto napisanym, krótkim podręcznikiem jest „On to C" autorstwa Patricka H. Win-
stona (Addison-Wesley, 1994). Ma niecałe 300 stron; większość materiału związanego
z PHP znajduje się na początku książki. Sztandarowym podręcznikiem jest „Język
ANSI C" Briana W. Kernighana i Dennis M. Ritchie (Prentice Hall, 1988), który jest
uważany za wzorcowy, ale ma charakter skorowidza i dlatego nie odpowiada czasem
projektantom HTML.
Dodatek C » PHP dla programistów HTML_____________________________577
Wadą tego stylu programowania jest to, że strona nie może przekazywać zmiennych
sama do siebie. Jest to szczególnie istotne w przypadku formularzy; możesz nieco
zmienić styl programowania, jeżeli uważasz, że jesteś do tego przygotowany.
Witryna PHP
Oficjalną witryną PHP jest:
http://www.php. net
Możesz znaleźć tam najnowsze informacje, uaktualnienia, opisy wykrytych błędów oraz
wciąż rosnącą listę witryn korzystających z PHP.
Podręcznik
W części Documentation znajdziesz podręcznik PHP, dostępny w kilku wersjach.
* Wersja w języku japońskim i francuskim.
+ Kilka wersji do ściągnięcia w postaci PDF, RTF i HTML, wygodnych, jeżeli
chcesz korzystać z podręcznika nie mając dostępu do Sieci. Wersja HTML jest
rozprowadzana razem z PHP.
* Wersja dla PalmPilota.
* Podstawowa wersja sieciowa w postaci HTML.
Najczęściej jednak mówiąc o podręczniku PHP mamy na myśli najbardziej znaną wer-
sję sieciową z komentarzami. Użytkownicy z całego świata mogą dodawać uwagi i ko-
mentarze do każdej strony tego podręcznika. Są to zwykle objaśnienia niejasnych
punktów głównego tekstu, dodatkowe spostrzeżenia i uwagi na temat działania PHP na
różnych platformach.
580 Dodatki
To listy o średnim lub niskim ruchu, około 100 do 1000 listów miesięcznie. Poruszają
aspekty techniczne. Pewnie niewiele z nich skorzystasz, chyba że jesteś aktywnym
członkiem grupy.
Istnieje również wiele nieoficjalnych list prowadzonych w wielu językach. Ich zestaw
znajduje się w części Documentation na witrynie PHP.
Pojawiły się także listy dotyczące popularnych projektów opartych na PHP, takich jak
Midgard. Możesz się do nich zapisać poprzez ich witryny WWW.
Jeżeli wolisz korzystać z systemu internetowych list dyskusyjnych (wielu nowych użyt-
kowników tego nie potrafi), możesz przyłączyć się do listy PHP za pomocą bramki news:
news.php.net
Ten sposób korzystania z listy ma wielką zaletę — możesz wysyłać listy, nie będąc za-
pisanym. Jednak nowicjusze powinni raczej szukać odpowiedzi w archiwum, zanim
(lub zamiast) zadadząpytanie.
Różnica pomiędzy tymi witrynami jest niewielka: archiwum PCC ma bardzo prosty
układ (w chwili pisania tej książki witryna ta była czarno-biała), natomiast PHPBuilder
intensywniej wykorzystuje HTML. Archiwum PHPBuilder dla PHP 4 rozpoczyna się
od maja 2000, wcześniejsze artykuły dostępne sąw części PHP 3 i PHP 4-beta. Istnieje
również archiwum Geocrawler (http://www.geocrawler.com) zawierające archiwum
PHP 3; być może nie jest uaktualniane.
Jeżeli nigdy nie dostałeś ponad 100 listów, zapychających skrzynkę pocztową, nie wiesz,
jak to przeszkadza. Tylko czytanie i usuwanie może zająć kilka godzin, a odpowiadanie
na listy to już pełnoetatowe zajęcie. W żadnym wypadku nie powinieneś pobierać pełnej
listy użytkowników, jeżeli korzystasz z bezpłatnej poczty na stronie WWW.
Z drugiej strony trudniej brać udział w dyskusji korzystając ze skróconej wersji. Kilku
dzielnych członków społeczności może odpowiedzieć na wszystkie pytania, zanim
otrzymasz skrót listy.
Zapisywanie się
Aby zapisać się na jakąś listę dyskusyjną PHP, przejdź na stronę Support witryny PHP.
W środku strony ukryty jest niepozorny formularz służący do zapisania się na listę.
Wybierz listę, która cię interesuje, wpisz adres i kliknij przycisk Subscribe. Za pomocą
tego formularza możesz również anulować subskrypcję.
Zarządca listy dyskusyjnej PHP nieomal natychmiast wyśle do ciebie e-mail, w którym
poprosi o potwierdzenie subskrypcji. Dopóki nie odeślesz potwierdzenia, nie zostaniesz
zapisany na listę. Anulowanie subskrypcji nie wymaga potwierdzenia.
Sprawdź, czy użyłeś odpowiedniego tematu listu — im bardziej szczegółowy, tym le-
piej. Temat „PHP — pomocy" będzie prawdopodobnie zignorowany przez większość
stałych bywalców listy. Powinieneś wybrać coś bardziej opisowego, na przykład: „Ar-
gumenty mysql_connect nie są przekazy wane w wersji 4.0.0".
Bardzo często zdarza się sytuacja, że ktoś wchodzi na listę dyskusyjną i narzeka, że
PHP nie posiada funkcji, której właśnie jest potrzebna. Programiści zwykle wtedy py-
tają, czy zająłbyś się wykonaniem takiej funkcji. Jeżeli nie jesteś wystarczająco dobry
w C, możesz poprosić kogoś innego o stworzenie takiej funkcji.
Zrób to sam
Bezpłatne oprogramowanie może być używane bez ograniczeń, ale nie zwalnia od od-
powiedzialności. Twoim zadaniem jest zorientowanie się, gdzie i w jaki sposób możesz
spożytkować zdolności. Nie oznacza to, że wszyscy musimy stać się programistami C.
Istnieje wiele innych sposobów „dołożenia swojej cegiełki". Odpowiadanie na pytania
na listach dyskusyjnych i witrynach WWW zawsze jest dobrym pomysłem, ponieważ
zmniejsza obciążenie głównych programistów.
584________________________________________________Dodatki
Zend.com jest witryną poświęconą maszynie skryptowej PHP 4 oraz centrum komer-
cjalizacji PHP. Firma sprzedaje usługi serwisowe i oferuje pisanie programów dla du-
żych firm, jednak większość użytkowników PHP jest zainteresowana dodatkami do
PHP, jakie tworzy się w siedzibie Zend.com w Izraelu. Pierwszymi takimi narzędziami
są: dostępny już optymalizator i oczekiwane przez wszystkich nowości, takie jak bufor
PHP. Witryna ta zawiera jedyne w swoim rodzaju strony, na przykład udostępnia bio-
grafie większości ważnych postaci świata PHP.
Dodatki do PHP
PHP Base Library
http://phplib. netuse. de/index.php3
Wiele plików, klas i funkcji służących do lepszej obsługi sesji i uwierzytelniania. Pod
egidą tej witryny prowadzony jest projekt o nazwie phpslash — wersja PHP znanej wi-
tryny Slashdot.
Dodatek D » Zasoby Sieci na temat PHP_______________________________585
PHP Builder
http://www.phpbuilder. com/
Devshed
http://w\vw. devshed. com/Server _Side/PHP/
PHP Wizard
http://www.phpwizard. net/
PX
h ftp ://px. sklar. com/
Mało jasny projekt witryny, ukrywający dużą liczbą skryptów — w większości nie są to
aplikacje, ale raczej małe skrypty.
Weberdev
http ://www. weberdev. com/
Większość zawartości tej witryny, poświęconej skryptom, jest przeznaczona jedynie dla
zarejestrowanych użytkowników; posiada jedną naprawdę dobrą funkcję: sekcję Smart-
Code, bazę doskonałych sztuczek (w większości autorstwa Rasmusa Lerdorfa).
586________________________________________________Dodatki
Możesz tam również natrafić na poprawki kilku błędów, które umknęły bystrym oczom
i czerwonym elektronicznym ołówkom naszych redaktorów.
Słownik
Abstrakcja proceduralna. Proces przenoszenia powtarzających się linii kodu do proce-
dur lub funkcji.
Analizator poprawności. W kontekście XML jest to taki analizator, który sprawdza do-
kument pod kątem zgodności z jego plikiem definicji typu (DTD). Nie wszystkie anali-
zatory PHP kontrolują poprawność, choć wszystkie sprawdzają, czy dokument ma
poprawną strukturę.
Apache, serwer HTTP. Najpopularniejszy serwer WWW typu open source. Zwany rów-
nież serwerem Apache, Apache httpd lub po prostu Apache.
Apache Software Foundation. Fundacja nie nastawiona na zysk, która nadzoruje kilka
projektów open source, a w szczególności serwer Apache.
Applet. Program napisany w języku Java, który może być szybko załadowany ze strony
WWW i wykonywany w przeglądarce użytkownika.
Application Program Interface (API). Standard opisujący, w jaki sposób powinno się
pisać programy współpracujące z określonym systemem operacyjnym lub usługą. Zwy-
kle w postaci zestawu funkcji, które wywołuje się z kodu aplikacji.
ASP. W kontekście tej książki — Active Server Pages, ale w innym kontekście może
być również skrótem od Application Service Provider (dostawca aplikacji, odżywająca
znów idea umożliwiania klientowi dostępu aplikacji, zwykle poprzez sieć).
588___________________________________________________Dodatki
Binarny. W książce używa się kilku znaczeń: 1). Związany z liczbami o podstawie dwa,
tak jak w arytmetyce binarnej. 2). Opis kodowania typu włączony-wyłączony, używa-
nego w pamięci komputera. 3). Plik używający wszystkich wartości bajtowych, a nie
tylko tych, które są czytelne dla człowieka. Na przykład pliki wykonywane bezpośred-
nio przez komputer są często nazywane binariami, aby odróżnić je od czytelnego dla
człowieka kodu źródłowego (patrz również kompilacja, interpretacja, źródło).
Boolean. Określenie wartości logicznej prawda-fałsz. W PHP typ ten posiada tylko
dwie wartości TRUE i FALSE.
C. Jeden z najważniejszych języków programowania używany do pisania systemów
operacyjnych, języków programowania i aplikacji ogólnego przeznaczenia. Ściśle zwią-
zany z rodzinąjęzyków programowania Unix.
Cookie. Mała porcja danych zapisana przez serwer WWW w przeglądarce lub na dysku
w komputerze użytkownika.
Demon http. Program stale działający na serwerze. Służy do udostępniania stron WWW.
Słownik___________________________________________________589
Document Type Definition (DTD). Definicja typu dokumentu. Dokument w taki sposób
określający strukturę klasy dokumentów XML, że możliwe jest ich automatyczne
sprawdzanie, czy struktura ta została zachowana.
Document Object Model (DOM). Standardowy interfejs (API) dla programów współ-
pracujących z analizatorami XML. W DOM analizatory odczytują cały dokument XML
i tworzą drzewo analizy, a program może przeszukiwać lub manipulować tym drzewem
(patrz SAX, API).
Domain Name Service. Typ programu, który zajmuje się tłumaczeniem nazw domen (na
przykład www.ibm.com) na odpowiadające im adresy IP (patrz adres IP, Nazwa dome-
ny).
Efekt uboczny. W językach programowania: wynik działania wyrażenia inny niż główna
wartość zwracana przez wyrażenie.
fhttpd. Mały, szybki serwer WWW, którego autorem jest Alex Belits.
File Transfer Protocol (FTP). Standardowa metoda przesyłania plików pomiędzy kom-
puterami w Internecie.
General Public License (GPL). Typ licencji na oprogramowania lansowany przez Free
Software Foundation. Zakłada swobodną dystrybucję źródeł i dopuszcza dystrybucję
zmian do oprogramowania objętego licencją GPL, jeżeli te zmodyfikowane wersje są
nadal dostępne jako GPL.
Globalny. Określenie opisujące dane lub wartości dostępne w całym kodzie określonego
programu. Przeciwieństwo określenia „lokalny".
HTML. HyperText Markup Language, podzbiór języka SGML. Powszechna metoda za-
pisu stron WWW.
IMAP. Internet Message Access Protocol. Protokół pobierania poczty, który uzupełnia
lub zastępuje POP.
Integer. W matematyce jest to liczba całkowita, bez części ułamkowej, dodatnia bądź
ujemna. W PHP podstawowy typ danych reprezentujący liczby całkowite.
Java Server Pages (JSP). Język skryptów serwera promowany przez firmę Sun Micro-
systems, w którym fragmenty kodu w języku Java wbudowywane są w kod HTML.
Java Virtual Machine (JVM). Maszyna wirtualna Java. Program wykonujący kod po-
średni języka Java. Moduły JVM są wbudowywane w przeglądarki i umożliwiają uru-
chamianie appletów (patrz Java, Applet, Przeglądarka).
Język programowania. Standard definiujący zbiór konstrukcji, dzięki którym można pi-
sać kod. Jeżeli istnieje różnica pomiędzy specyfikacją języka a konkretną implementa-
cją kompilatora lub interpretera, termin ten odnosi się do specyfikacji. Przykładami
języków programowania są C, C++, Common Lisp, Scheme, Basic, Pascal, Fortran,
Perl, PHP; języki skryptowe również są językami programowania (patrz Język skrypto-
wy, Kompilacja, Interpretacja).
Kod źródłowy. Tekst programu napisany przez programistę. Jego przeciwieństwem jest
kod wynikowy produkowany z kodu źródłowego (patrz Kompilacja, Binarny).
Kolejność operatorów. Zbiór zasad określający, który operator pierwszy pobierze dane,
jeżeli operatory występują po lewej i prawej stronie argumentu.
Kolokacja. Specjalny typ udostępniania sieci, w której klient dostarcza komputer z uru-
chomionym serwerem WWW (administrowanym zdalnie), natomiast dostawca Inter-
netu udostępnia pomieszczenie, zasilanie, dostęp do Internetu, a często inne usługi, na
przykład składowanie.
Konwersja typów. Zamiana typu wartości wyrażenia na inny typ danych. Może być,
choć nie musi, wynikiem rzutowania typów.
Localhost. Urządzenie sieciowe oznaczające ten sam komputer, w którym działa pro-
gram.
Lokalny. Określenie zmiennej lub wartości w programie, która jest dostępna jedynie
w ograniczonym obszarze kodu. Przeciwieństwo do określenia Globalny (patrz Zasięg).
MySQL. Bezpłatny system relacyjnych baz danych często łączony z Linuksem, PHP
i Apache, w celu uzyskania kompletnego zestawu programów dla witryny WWW.
Nazwa domeny. Nazwa komputera lub usługi w Internecie, na przykład www. trout-
works, com. Nazwy domen drugiego poziomu, na przykład ibm.com, mogą grupować
więcej nazw domen.
Słownik____________________________________________________593
Odstęp. Znak w pliku tekstowym lub programie, który nie jest widoczny. Znakami od-
stępu są: spacje, tabulatory, nowe linie i znaki końca linii.
PHP. Bezpłatny język skryptowy wbudowywany w kod HTML. Zawiera również ma-
szynę uruchamiania skryptów.
Plaska baza danych. System bazy danych przechowujący dane w jednej uniwersalnej
tabeli, w przeciwieństwie do relacyjnej bazy danych używającej wielu tabel (patrz Rela-
cyjna baza danych).
Polimorfizm. Dla funkcji lub metod: zdolność różnego zachowywania się w zależności
od liczby i typu argumentów.
Poprawność. W kontekście XML poprawny dokument to taki, który nie tylko jest od-
powiednio zbudowany, ale również zgodny ze opisem typu dokumentu w DTD (patrz
DTD).
Relacyjna baza danych. System bazy danych mający możliwość podziału danych na
wiele tabel, połączonych ze sobą relacjami pomiędzy kolumnami.
RPM. Red Hal Package Manager. System prekompilowanych binariów dla dystrybucji
Linuksa Red Hat i jego naśladowców.
Simple Api for XML (SAX). Interfejs (API) dla programów używających analizatorów
XML. W przypadku SAX w trakcie odczytywania dokumentu XML analizator powia-
damia aplikacje o zdarzeniach. Zdarzeniem takim może być napotkanie znacznika lub
określonej zawartości (patrz DOM, API).
Solaris. Wersja systemu Unix rozwijana i sprzedawana przez firmę Sun Microsystems.
Statyczny. Określenie stron HTML, które nie są tworzone na bieżąco przed wyświetle-
niem lub nie zawierają żadnych rozszerzeń uruchamianych w komputerze klienta.
Ścieżka. Ciąg znaków określający położenie pliku w systemie plików. Zawiera wszyst-
kie niezbędne nazwy katalogów.
Typ danych. Dziedziny wartości w pamięci komputera. Typ danych określa zarówno
przedział dopuszczalnych wartości, a także sposób, w jaki te wartości będą przechowy-
wane w pamięci komputera. Podstawowymi typami danych w PHP są integer, do-
uble, Boolean,string, a r r a y i object.
Universal Resource Indicator (URI). Adres internetowy. Jest to rozszerzenie adresu URL.
Unix. Rodzina systemów operacyjnych, w oparciu o które działa większa część Inter-
netu, z wyjątkiem komputerów użytkowników. Implementacjami Uniksa są Linux, Fre-
eBSD i Solaris.
Wiersz. W relacyjnych bazach danych: pozycja w tabeli bazy danych zawierająca war-
tości z kolumn wymienionych w definicji tabeli.
Zaplecze. Określenie części programu komputerowego bądź usługi, która jest niewi-
doczna dla użytkownika. W programowaniu dla WWW: do tej grupy zalicza się serwer
bazy danych; do części frontowej zalicza się przeglądarkę i programy tworzące HTML.
Zend. Nazwa programu analizującego i wykonującego, który jest podstawą PHP 4. Jest
to również nazwa firmy założonej przez projektantów tego modułu (Zeev Suraski i Andi
Gutmans). Firma oferuje usługi konsultingowe, dodatki i narzędzia dla PHP.
0 pi, 186
plik
Obiekt, 494
/etc/services, 40 1
obiekty, 102
fopen-wrappers.c, 240
OctDec, 184
httpd.conf, 64
ODBC, 303
php.ini, 423, 462
opcja
srm.conf, 64
— enable-discard-path, 518
podzapytania, 3 1 5
— enable-mcrypt, 526
polimorfizm, 103
--enable-track-vars, 4 1 7
połączenie trwałe, 391
— enable-trans-sid, 417
POP, 456, 459
-with-mysql, 401
open source, 32
POP3, 38
pow, 186
openlog, 244
prev, 2 1 2
operator, 77
print, 86, 99
%, 178
printer, 502
*, 178
printf, 171
.=, 157
funkcja C, 87
/, 178
priorytety, 78
+, 178
procedura przechowywana, 34'
++, 179
profiler, 265
=, 159
przeciążanie, 499
===, 181
->, 104
przesłanianie, 498
AND, 112 Q
kropka, 156
logiczny, 1 12 quotemeta, 171
modulo, 178 R
NOT, 112
OR, 112 rand, 190
priorytet, 78 rangę, 204
trójskładnikowy, 116 readfile, 239
XOR, 112 rekurencja, 143
złączenia, 156 replikacja, 306
operatory require, 142
kolejność, 113, 115 reset, 2 1 1
porównania, 1 14 return, 138
skracanie, 1 1 3 round, 108, 182
Oracle, 33 RSA, 525
ord, 140 rsort, 224
ORDER BY, 393 S
P SAX, 477
parametry SELECT, 312
aktualne, 139 sendmail, 462
formalne, 139 serialize, 505, 507
pętla sesja, 4 1 3
do-while. 123 session decode, 42 1
for, 123 session destroy, 421
nieskończona, 128 session_encode, 421
while, 122 session_id, 42 1
pfsockopen, 245 session is registered, 421
PHPLIB, 417 session_module_name, 421
phpMyAdmin, 333 session name, 421
session_register, 417
Skorowidz 603
session_save _path, 42 1 T
session start, 4 1 7
tablica, 100
session_unregister, 420
SGLOBALS, 233
setcookie, 428 SHTTP COOKIE VARS, 200. 233, 434
settype, 106
SHTTP GET VARS, 233, 434
SGML, 469 $HTTP_POST_VARS, 233, 434
shuffle, 220
asocjacyjna, 101,200
sin, 187
indeksy, 101
sito Erastonesa, 266
wielowymiarowa, 206
sizeof, 207
tan, 187
SMTP, 456, 457
tautologia, 1 13
split, 175
time, 246
sprintf, 171
transakcja, 305
srand, 190
trim, 163
srm.conf
TRUE, 96
plik, 64
trwale połączenie, 391
SSL, 531
tryb HTML, 72
stała tryb otwarcia pliku, 236
M PI, 186
tryb PHP, 72
SID.417 typ
stała logiczna, 96, 1 1 2
boolean, 95
static, 141
double, 94
statyczny JavaScript, 445
integer, 93
stos, 215
obiektowy, 102
str_replace, 163
string, 97
strcasecmp, 160
tablicowy, 100
strchr, 160
typy
strcmp, 115, 159
rzutowanie, 177
strcspn, 166
strftime, 247 U
string, 97
uasort, 224
stripjags, 176
ucfirst, 170
stripslashes, 171, 404
ucwords, 170
stristr, 160
uksort, 224
strlen, 157
strpos, 158
unserialize, 505, 507
strrpos, 158
unset, 84, 207
strspn, 165
UPDATE, 316
uprawnienia, 320
strstr, 160
usort, 224
strtok, 167
strtolower, 169 V
strtoupper, 170
strval, 106 var dump, 502
substr. 92, 161 W
ujemne indeksy, 162
substr replace, 163 while, 122
suma kontrolna, 529 składnia alternatywna, 1 29
switch, 120 widok, 344
składnia alternatywna, 129 wielkość liter
syslog, 244 wrażliwość, 77
system, 522 wyrażenia regularne
szyfrowanie, 524 POSIX, 173
wysyłanie załączników, 464
604 Dodatki
X Zend.com, 30
złączenie, 313
XML, 37, 469
zmienna, 82
Attribute, 480
$PHP SELF, 352
Document, 480
SREMOTE HOST, 414
Expat, 6 1
$REMOTE_NAME, 414
Node, 480
nieprzypisana, 83
xml_parse, 484
zmienna globalna, 140
xml _parser_create, 484
zmienna statyczna, 141
xml_parser free, 484
znacznik
xmlj>arser_get_option, 484
<BR>, 88
xml_parser set option, 484
xmldoc, 479 <PRE>, 172,502
xmldocfile, 479
<SCRIPT>, 517
<STYLE>, 274
xmltree, 479
mailto, 464
XSL, 473
znacznik czasu, 245
Z znaczniki
ASP, 71
zagnieżdżanie, 80
kanoniczne, 70
załączniki
krótkie, 70
wysyłanie, 464
zaokrąglanie
kłopty, 96
księgarnia mternetowa http://www.helion.pl
0'Reilly
Wydawnictwo Helion
ul. Chopina 6, 44-100 Gliwice; '. '. skr. poczt. 462
»(32) 230-98-63, (32) 231-22-19; e-mail: helion@helion.pl
księgarnia internatowa http://www.helion.pl
Księga eksperta
ara
Już sama nazwa serii objaśnia treść tych książek. „Księgi eksperta" to prawdziwa skarb-
nica wiedzy. Znajdziesz w nich właściwie wszystko, co jest potrzebne użytkownikom
poszczególnych rodzajów oprogramowania. Już na pierwszy rzut oka można przekonać
się, że „Księgi eksperta" to solidne opracowania, których lektura stanowi kolejny krok
w świat profesjonalnych zastosowań oprogramowania komputerowego. Konkretne zagad-
nienia omówiono w nich w sposób dogłębny i nad wyraz wyczerpujący. Ich szczegółowość
dodatkowo podkreślają odpowiedzi na „z życia wzięte" pytania. Po nabyciu książki z tej
serii nie będziesz już musiał wertować kartek kilku innych książek - znajdziesz w niej
wszystko, co potrzebne.
Wydawnictwo Helion
ul. Chopina 6, 44-100 Gliwice; ' • ' skr. poczt. 462
8(32)230-98-63, (32) 231-22-19; e-mail: helion@helion.pl
księgarnia internatowa http://www.helion.pl
Vademecum profesjonalisty
Wydawnictwo Helion
ul. Chopina 6, 44-100 Gliwice; l i skr. poczt. 462
8(32) 230-98-63, (32) 231-22-19; e-mail: helion@helion.pl