Darmowy fragment publikacji:
PHP i MySQL. Projekty
do wykorzystania
Autorzy: Timothy Boronczyk, Martin E. Psinas
T³umaczenie: Daniel Kaczmarek
ISBN: 978-83-246-2069-2
Tytu³ orygina³u: PHP and MySQL: Create - Modify - Reuse
Format: 172×245, stron: 360
ZaoszczêdŸ swój czas – korzystaj z najlepszych gotowców!
(cid:129) Korzystaj z najlepszych mechanizmów!
(cid:129) Wzbogaæ stronê o praktyczne funkcjonalnoœci!
(cid:129) Szybko twórz profesjonalne serwisy!
Ile czasu zajmuje Ci przygotowanie formularza rejestracyjnego? Czy jest on
wykorzystywany tylko raz? Popularnoœæ tandemu PHP-MySQL sprawi³a, ¿e mnóstwo
powszechnie stosowanych mechanizmów ileœ razy napisa³o wielu programistów.
A wœród nich s¹ i tacy, którzy te same mechanizmy tworzyli wiêcej ni¿ raz!
Czy¿ nie jest to klasyczny przyk³ad marnotrawienia czasu?
Dziêki ksi¹¿ce „PHP i MySQL. Projekty do wykorzystania” nie zmarnujesz ju¿ ani jednej
cennej minuty. Stanowi ona zbiór najpopularniejszych mechanizmów, u¿ywanych na co
dzieñ przy tworzeniu serwisów WWW. Dziêki niej ³atwo (a co najwa¿niejsze – szybko)
zaimplementujesz funkcjonalnoœæ rejestracji u¿ytkownika, listy dystrybucyjnej czy te¿
wyszukiwarki. Dowiesz siê, w jaki sposób stworzyæ forum dyskusyjne, osobisty
kalendarz, galeriê zdjêæ czy te¿ mened¿er plików, korzystaj¹cy z technologii AJAX.
Po przewertowaniu tego podrêcznika nie bêdzie stanowi³a dla Ciebie problemu
rejestracja zdarzeñ oraz wykonywanie skryptów pow³oki. Pozwoli Ci to na szybkie
tworzenie nowych serwisów WWW za pomoc¹ sprawdzonych i elastycznych
mechanizmów. Je¿eli cenisz swój czas – oto Twoja lektura obowi¹zkowa!
(cid:129) Rejestracja u¿ytkowników w serwisie
(cid:129) Zabezpieczenie przed spamem – mechanizm CAPTCHA
(cid:129) Implementacja forum dyskusyjnego
(cid:129) Zastosowanie listy dystrybucyjnej
(cid:129) Wyszukiwanie informacji w serwisie
(cid:129) Tworzenie kalendarza
(cid:129) Zarz¹dzanie plikami – mened¿er plików, korzystaj¹cy z AJAX
(cid:129) Prezentacja zdjêæ – galeria online
(cid:129) Statystyki serwisu WWW
(cid:129) Rejestracja zdarzeñ
(cid:129) Wykonywanie skryptów pow³oki
Nie traæ czasu – korzystaj ze sprawdzonych projektów!
Spis treÊci
O autorze ................................................................................................................................................... 7
O wspóÄautorze ......................................................................................................................................... 9
Wprowadzenie ..........................................................................................................................................11
Dla kogo jest ta ksiñĔka? .............................................................................................. 11
UĔywane technologie .................................................................................................... 12
Struktura ksiñĔki .......................................................................................................... 12
Czego potrzeba w trakcie lektury tej ksiñĔki? .................................................................. 13
UĔyte konwencje .......................................................................................................... 14
Kody Ēródäowe .............................................................................................................. 14
RozdziaÄ 1. Rejestracja uÑytkowników ...................................................................................................15
Plan struktury katalogów ............................................................................................... 15
Plan struktury bazy danych ............................................................................................ 16
Kod wspóäuĔytkowany ................................................................................................... 17
Klasa User .................................................................................................................. 20
CAPTCHA ..................................................................................................................... 24
Szablony ...................................................................................................................... 25
Rejestracja nowego uĔytkownika .................................................................................... 27
Wysyäanie e-maila z äñczem do weryfikacji ....................................................................... 32
Logowanie i wylogowywanie ........................................................................................... 35
Zmiana danych ............................................................................................................ 39
Zapomniane hasäa ........................................................................................................ 42
Podsumowanie ............................................................................................................ 44
RozdziaÄ 2. Forum spoÄecznoÊciowe ......................................................................................................45
Wymagania funkcjonalne wobec forum ........................................................................... 45
Projekt bazy danych ...................................................................................................... 46
Uprawnienia i operatory bitowe ...................................................................................... 47
Zmiany w kodzie klasy User .......................................................................................... 49
Kod Ēródäowy i objaĈnienia do kodu ............................................................................... 54
Dodawanie forów .......................................................................................................... 54
Dodawanie wiadomoĈci ................................................................................................ 57
4
PHP i MySQL. Projekty do wykorzystania
WyĈwietlanie forów i wiadomoĈci ................................................................................... 60
Stronicowanie ......................................................................................................... 67
Awatary ....................................................................................................................... 69
BBCode ....................................................................................................................... 72
Podsumowanie ............................................................................................................ 75
RozdziaÄ 3. Lista dystrybucyjna .............................................................................................................77
Projekt listy dystrybucyjnej ............................................................................................ 77
Wybór serwera POP3 .................................................................................................... 78
Projekt bazy danych ...................................................................................................... 80
Kod Ēródäowy i objaĈnienia kodu .................................................................................... 80
Klient POP3 ............................................................................................................ 81
Plik konfiguracyjny ................................................................................................... 87
Zarzñdzanie kontem ................................................................................................ 88
Przetwarzanie wiadomoĈci ....................................................................................... 94
Przetwarzanie wiadomoĈci z podsumowaniem ........................................................... 97
Konfiguracja listy dystrybucyjnej ..................................................................................... 98
Podsumowanie .......................................................................................................... 100
RozdziaÄ 4. Wyszukiwarka ....................................................................................................................103
Projekt wyszukiwarki ................................................................................................... 103
Problemy z wyszukiwaniem peänotekstowym ................................................................. 104
Projekt bazy danych .................................................................................................... 106
Kod Ēródäowy i objaĈnienia kodu .................................................................................. 108
Interfejs administracyjny ........................................................................................ 108
Robot i indekser ................................................................................................... 114
Interfejs uĔytkownika ............................................................................................. 120
Podsumowanie .......................................................................................................... 126
RozdziaÄ 5. Osobisty kalendarz .............................................................................................................129
Projekt aplikacji .......................................................................................................... 129
Projekt bazy danych .................................................................................................... 131
Kod Ēródäowy i objaĈnienia kodu .................................................................................. 131
Widok miesiöczny kalendarza ................................................................................. 132
Kalendarz w ukäadzie dnia ...................................................................................... 136
Dodawanie i prezentowanie zdarzeþ ....................................................................... 137
Wysyäanie przypomnieþ .......................................................................................... 145
Eksport danych z kalendarza .................................................................................. 146
Podsumowanie .......................................................................................................... 150
RozdziaÄ 6. MenedÑer plików Ajax ........................................................................................................153
Projekt menedĔera plików Ajax .................................................................................... 153
JavaScript i Ajax ......................................................................................................... 155
Obiekt XMLHttpRequest ........................................................................................ 156
Kod Ēródäowy i objaĈnienia kodu .................................................................................. 159
Gäówny interfejs .................................................................................................... 159
Funkcje dziaäajñce po stronie klienta ...................................................................... 163
Funkcje dziaäajñce po stronie serwera ..................................................................... 176
Podsumowanie .......................................................................................................... 191
Spis treÊci
5
RozdziaÄ 7. Album fotograficzny online .................................................................................................193
Projekt albumu fotograficznego online ............................................................................... 193
Kod Ēródäowy i objaĈnienia kodu .................................................................................. 194
Widoki .................................................................................................................. 194
Pliki pomocnicze ................................................................................................... 202
Miniatury QuickTime ................................................................................................... 206
Zapisywanie miniaturek w pamiöci podröcznej .............................................................. 208
Podsumowanie .......................................................................................................... 209
RozdziaÄ 8. Koszyk na zakupy .................................................................................................................211
Projekt koszyka na zakupy .......................................................................................... 211
Projekt bazy danych .................................................................................................... 212
Kod Ēródäowy i objaĈnienia kodu .................................................................................. 213
Klasa ShoppingCart .............................................................................................. 213
Sposób uĔycia koszyka na zakupy .......................................................................... 217
Interfejs uĔytkownika ............................................................................................. 225
Dodawanie produktów ........................................................................................... 233
Podsumowanie .......................................................................................................... 253
RozdziaÄ 9. Statystyki witryny internetowej ......................................................................................255
Zakres gromadzonych danych ...................................................................................... 255
Projekt bazy danych .................................................................................................... 256
Gromadzenie danych .................................................................................................. 258
Kod Ēródäowy i objaĈnienia kodu .................................................................................. 260
Wykres koäowy ...................................................................................................... 261
Wykres säupkowy ................................................................................................... 264
Raport .................................................................................................................. 268
Podsumowanie .......................................................................................................... 278
RozdziaÄ 10. System grup dyskusyjnych lub blogów ...........................................................................281
Tabele ....................................................................................................................... 282
Dodawanie wpisów ..................................................................................................... 283
Generowanie kanaäu RSS ............................................................................................ 294
WyĈwietlanie wpisów .................................................................................................. 298
Dodawanie komentarzy ............................................................................................... 300
Podsumowanie .......................................................................................................... 304
RozdziaÄ 11. Skrypty powÄoki ................................................................................................................307
Projekt skryptu ........................................................................................................... 308
Ogólne wskazówki dotyczñce implementacji skryptów powäoki ........................................ 309
Kod Ēródäowy i objaĈnienia kodu .................................................................................. 311
Klasa CommandLine ............................................................................................. 311
Skrypt startproject ................................................................................................ 320
Szkielet struktury ....................................................................................................... 329
Podsumowanie .......................................................................................................... 330
RozdziaÄ 12. BezpieczeÆstwo i rejestracja zdarzeÆ ............................................................................331
Cross-site scripting ..................................................................................................... 332
Przeglñdanie ĈcieĔek .................................................................................................. 334
Wstrzykiwanie ............................................................................................................ 336
Wstrzykiwanie kodu jözyka SQL .............................................................................. 336
Wstrzykiwanie poleceþ .......................................................................................... 340
6
PHP i MySQL. Projekty do wykorzystania
Säabe uwierzytelnianie ................................................................................................ 342
Rejestrowanie zdarzeþ ................................................................................................ 344
Zapobieganie przypadkowemu usuniöciu rekordów ........................................................ 346
Podsumowanie .......................................................................................................... 348
Skorowidz ............................................................................................................................................349
1
Rejestracja uÑytkowników
UmoĪliwienie rejestracji kont i logowania siĊ przez uĪytkowników pozwala nadawaü witry-
nom indywidualny charakter i udostĊpniaü zawartoĞü dostosowaną do konkretnych oczekiwaĔ.
Tego rodzaju mechanizm uwierzytelnienia jest centralnym punktem wielu witryn spoáeczno-
Ğciowych i e-commerce. Ze wzglĊdu na tak duĪą wagĊ mechanizmów uwierzytelniania pierw-
szą prezentowaną aplikacją jest system rejestracji uĪytkowników.
Gáówną funkcją systemu jest umoĪliwienie uĪytkownikom tworzenia kont. Czáonkowie sys-
temu muszą podaü adres poczty elektronicznej, który posáuĪy do weryfikacji poprawnoĞci
rejestracji. UĪytkownicy bĊdą równieĪ mogli zmieniaü hasáa i uaktualniaü adresy pocztowe,
a takĪe resetowaü hasáa, gdy je zapomną. Są to caákowicie standardowe funkcje, oczekiwane
przez uĪytkowników witryn internetowych.
JeĞli chodzi o architekturĊ, katalog przechowujący kod Ĩródáowy powinien mieü logiczną
strukturĊ. Na przykáad pliki pomocnicze i doáączane powinny znajdowaü siĊ w innym katalogu
niĪ pliki dostĊpne publicznie. Ponadto dane na temat uĪytkowników powinny byü przecho-
wywane w bazie danych. PoniewaĪ na rynku dostĊpnych jest wiele narzĊdzi przeznaczonych
do przeglądania i przetwarzania danych przechowywanych w relacyjnych bazach danych takich
jak MySQL, áatwo jest zapewniü przezroczystoĞü i elastycznoĞü rozwiązania.
Plan struktury katalogów
W pierwszym kroku naleĪy zaplanowaü strukturĊ katalogów aplikacji. Zaleca siĊ utworzenie
trzech gáównych folderów: public_files bĊdzie przechowywaá wszystkie pliki dostĊpne publicz-
nie, w lib przechowywane bĊdą pliki doáączane, wspóáuĪytkowane przez dowolną liczbĊ
innych plików Ĩródáowych, w templates zaĞ znajdą siĊ pliki odpowiedzialne za prezentacjĊ
stron. Choü PHP moĪe siĊ odwoáywaü do plików poáoĪonych w dowolnych lokalizacjach, ser-
wer WWW powinien udostĊpniaü wyáącznie pliki z folderu public_files. Przechowywanie pli-
ków pomocniczych poza katalogiem udostĊpnianym publicznie zwiĊksza poziom bezpieczeĔ-
stwa witryny.
16
PHP i MySQL. Projekty do wykorzystania
W folderze public_files zostanie utworzony folder css przechowujący katalogi stylów, folder
js dla plików Ĩródáowych JavaScript oraz img do przechowywania plików graficznych. MoĪna
utworzyü jeszcze dodatkowe foldery, aby zorganizowaü strukturĊ katalogów wedáug wáasnych
potrzeb. Dodatkowymi folderami mogą byü na przykáad sql do przechowywania plików serwera
MySQL, doc dla dokumentacji systemu i dokumentów implementacyjnych oraz tests do prze-
chowywania plików dla testów wstĊpnych i testów jednostkowych.
Plan struktury bazy danych
Oprócz zaplanowania struktury katalogów konieczne jest równieĪ pochylenie siĊ nad struk-
turą bazy danych systemu. Zakres zapisywanych informacji na temat uĪytkowników bĊdzie
zaleĪaá od rodzaju usáug Ğwiadczonych na witrynie. To z kolei bĊdzie wyznaczaü wygląd tabel
bazy danych. Minimalnym wymaganiem jest, by w bazie danych przechowywaü przynajmniej
unikatowy identyfikator uĪytkownika, nazwĊ uĪytkownika, zaszyfrowane hasáo i adres poczty
elektronicznej. Trzeba bĊdzie teĪ zaimplementowaü funkcje sprawdzające, które konta zostaáy
juĪ zweryfikowane, a które dopiero oczekują na weryfikacjĊ.
DROP TABLE IF EXISTS HELION_PENDING;
DROP TABLE IF EXISTS HELION_USER;
CREATE TABLE HELION_USER (
USER_ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
USERNAME VARCHAR(20) NOT NULL,
PASSWORD CHAR(40) NOT NULL,
EMAIL_ADDR VARCHAR(100) NOT NULL,
IS_ACTIVE TINYINT(1) DEFAULT 0,
PRIMARY KEY (USER_ID)
)
ENGINE=MyISAM DEFAULT CHARACTER SET latin1
COLLATE latin1_general_cs AUTO_INCREMENT=0;
CREATE TABLE HELION_PENDING (
USER_ID INTEGER UNSIGNED NOT NULL,
TOKEN CHAR(10) NOT NULL,
CREATED_DATE TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (USER_ID)
REFERENCES HELION_USER(USER_ID)
)
ENGINE=MyISAM DEFAULT CHARACTER SET latin1
COLLATE latin1_general_cs;
W tabeli HELION_USER na przechowywanie zaszyfrowanego hasáa przewidziano kolumnĊ o sze-
rokoĞci 40 znaków, poniewaĪ do szyfrowania haseá uĪywana bĊdzie funkcja sha1(), zwraca-
jąca wáaĞnie 40-znakowy szesnastkowy ciąg znaków. Nigdy nie powinno siĊ przechowywaü
w bazie danych haseá w oryginalnej postaci — jest to podstawowa zasada bezpieczeĔstwa.
Zasadą dziaáania zastosowanego rozwiązania jest zaszyfrowanie hasáa, gdy zostanie ono podane
przez uĪytkownika po raz pierwszy. To samo hasáo wpisywane póĨniej teĪ podlega szyfro-
waniu, a wynik szyfrowania funkcją sha1() jest porównywany z zaszyfrowanym hasáem prze-
chowywanym w bazie danych.
RozdziaÄ 1. Q Rejestracja uÑytkowników
17
Jako maksymalną dáugoĞü ciągu znaków przechowującego adres poczty elektronicznej wyzna-
czono 100 znaków. Z technicznego punktu widzenia obecnie obowiązujące standardy pozwa-
lają na definiowanie adresów pocztowych o maksymalnej dáugoĞci 320 znaków (64 znaki na
nazwĊ uĪytkownika, 1 znak na symbol @ i 255 znaków na nazwĊ komputera). Trudno jed-
nak znaleĨü kogokolwiek, kto uĪywaáby tak dáugiego adresu pocztowego, dlatego w schema-
tach baz danych adresy pocztowe standardowo przechowuje siĊ w kolumnach o szerokoĞci
100 znaków.
W bazie danych moĪna by dodatkowo przechowywaü imiĊ i nazwisko uĪytkownika, jego
adres, miasto zamieszkania, województwo, kod pocztowy, numery telefonów i tak dalej.
W tabeli HELION_PENDING znajduje siĊ inicjalizowana automatycznie kolumna znacznika czasu.
DziĊki temu w dowolnym momencie moĪna usunąü z bazy wszystkie zarejestrowane konta
uĪytkownika, które przez okreĞlony czas nie doczekaáy siĊ weryfikacji. Kolumny tabeli moĪna
by poáączyü z kolumnami tabeli HELION_USER, jednak ze wzglĊdu na to, Īe znacznik wskazujący
koniecznoĞü weryfikacji konta jest uĪywany tylko jeden raz, zdecydowano siĊ na ich wydzie-
lenie do odrĊbnej tabeli. Dane uĪytkowników są przechowywane znacznie dáuĪej, a dziĊki
zastosowanemu rozwiązaniu tabela HELION_USER nie jest zaĞmiecana danymi tymczasowymi.
Kod wspóÄuÑytkowany
Kod, który jest wspóáuĪytkowany przez wiĊkszą liczbĊ plików, powinien zostaü wyáączony
z pliku docelowego i doáączony do niego przy uĪyciu instrukcji include lub require. DziĊki
temu ten sam kod nie bĊdzie duplikowany i áatwiej bĊdzie utrzymywaü aplikacjĊ. Tam, gdzie
to moĪliwe, kod potencjalnie przydatny w przyszáych aplikacjach (taki jak funkcje albo klasy)
powinien byü przechowywany oddzielnie. Dobrym zaáoĪeniem jest pisanie kodu z myĞlą o tym,
by móc go ponownie wykorzystaü w przyszáoĞci. Plik common.php zawiera kod wspóáuĪytko-
wany, który bĊdzie doáączany do innych skryptów aplikacji, aby ustanowiü w ten sposób
jednolite Ğrodowisko fazy wykonania. Kod tego rodzaju nigdy nie powinien byü wywoáy-
wany przez uĪytkowników bezpoĞrednio, dlatego naleĪy go umieĞciü w katalogu lib.
?php
// true, jeĞli Ğrodowisko produkcyjne; w przeciwnym razie false
define ( IS_ENV_PRODUCTION , true);
// ustawienie opcji raportowania báĊdów
error_reporting(E_ALL | E_STRICT);
ini_set( display_errors , !IS_ENV_PRODUCTION);
ini_set( error_log , log/phperror.txt );
// ustawienie strefy czasowej, by uniknąü ostrzeĪeĔ
// w przypadku uĪycia funkcji czasu i daty
date_default_timezone_set( Europe/Warsaw );
// uwzglĊdnienie „magic quotes” w razie koniecznoĞci
if (get_magic_quotes_gpc())
{
function _stripslashes_rcurs($variable, $top = true)
{
$clean_data = array();
18
PHP i MySQL. Projekty do wykorzystania
foreach ($variable as $key = $value)
{
$key = ($top) ? $key : stripslashes($key);
$clean_data[$key] = (is_array($value)) ?
stripslashes_rcurs($value, false) : stripslashes($value);
}
return $clean_data;
}
$_GET = _stripslashes_rcurs($_GET);
$_POST = _stripslashes_rcurs($_POST);
// $_REQUEST = _stripslashes_rcurs($_REQUEST);
// $_COOKIE = _stripslashes_rcurs($_COOKIE);
}
?
Nie zawsze ma siĊ kontrolĊ nad konfiguracją uĪywanego serwera, dlatego dobrze jest zdefi-
niowaü kilka podstawowych dyrektyw, dziĊki którym przenoszenie aplikacji bĊdzie znacznie
áatwiejsze. Na przykáad zdefiniowanie opcji raportowania báĊdów pozwoli na wyĞwietlanie
báĊdów w Ğrodowisku rozwojowym lub przekierowanie ich w Ğrodowisku produkcyjnym, tak
by wewnĊtrzne komunikaty o báĊdach nie byáy widoczne dla uĪytkownika.
Magiczne apostrofy (ang. magic quotes) to opcja konfiguracyjna, dziĊki której PHP moĪe
automatycznie poprzedzaü znakami ucieczki symbole apostrofu, cudzysáowu i ukoĞników
odwrotnych zawarte w danych wejĞciowych. Funkcja ta moĪe siĊ wydawaü przydatna, jednak
nigdy z góry nie powinno siĊ przyjmowaü zaáoĪenia, Īe na danym serwerze jest ona wáączona
lub nie, poniewaĪ moĪe to doprowadziü do káopotów. Lepiej jest najpierw znormalizowaü
dane, a nastĊpnie w razie koniecznoĞci poprzedzaü je znakami ucieczki przy uĪyciu funkcji
addslashes() lub mysql_real_escape_string() (jeĪeli dane mają byü przechowywane w bazie
danych, zalecane jest zastosowanie drugiej ze wspomnianych funkcji). ZastĊpowanie magicz-
nych apostrofów zapewni, Īe znaki ucieczki zostaną zastosowane w danych odpowiednio i we
wáaĞciwym momencie, bez wzglĊdu na sposób konfiguracji PHP. DziĊki temu dalsza imple-
mentacja bĊdzie prostsza i zmniejszy siĊ zagroĪenie popeánieniem báĊdów.
Ustanawianie poáączenia z serwerem bazy danych MySQL jest czynnoĞcią wykonywaną
powszechnie, dlatego warto przenieĞü wykonujący ją kod do oddzielnego pliku. Plik o nazwie
db.php przechowuje staáe konfiguracyjne oraz kod zestawiający poáączenie z bazą danych.
RównieĪ ten plik ma byü doáączany do innych plików, a nie naleĪy go wywoáywaü w sposób
bezpoĞredni, dlatego naleĪy zapisaü go w katalogu lib.
?php
// staáe bazy danych i schematów
define( DB_HOST , localhost );
define( DB_USER , uľytkownik );
define( DB_PASSWORD , hasđo );
define( DB_SCHEMA , HELION_DATABASE );
define( DB_TBL_PREFIX , HELION_ );
// ustanowienie poáączenia z serwerem bazy danych
if (!$GLOBALS[ DB ] = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD))
{
die( Bđæd: Nie udađo siú nawiæzaè pođæczenia z bazæ danych. );
}
if (!mysql_select_db(DB_SCHEMA, $GLOBALS[ DB ]))
{
RozdziaÄ 1. Q Rejestracja uÑytkowników
19
mysql_close($GLOBALS[ DB ]);
die( Bđæd: Nie udađo siú wybraè schematu bazy danych. );
}
?
Staáe DB_HOST, DB_USER, DB_PASSWORD i DB_SCHEMA reprezentują wartoĞci niezbĊdne do sku-
tecznego zestawienia poáączenia z bazą danych. JeĪeli kod zostanie przeniesiony do Ğrodo-
wiska produkcyjnego, w którym serwer bazy danych pracuje na innym komputerze niĪ PHP
i serwer WWW, moĪna wówczas dodatkowo zdefiniowaü wartoĞü DB_PORT i odpowiednio
zmodyfikowaü wywoáanie funkcji mysql_connect().
Uchwyt poáączenia z bazą danych jest nastĊpnie zapisywany w tablicy superglobalnej $GLOBALS,
aby staá siĊ dostĊpny w dowolnym zasiĊgu kaĪdego pliku doáączającego plik db.php (albo
doáączanego do pliku, który odwoáuje siĊ do db.php).
DziĊki poprzedzeniu nazw tabel prefiksami moĪna uniknąü konfliktu z tabelami uĪywanymi
przez inne aplikacje, przechowywanymi w tym samym schemacie. Ponadto, jeĪeli prefiks zosta-
nie zdefiniowany w postaci staáej, áatwiej bĊdzie uaktualniü kod Ĩródáowy póĨniej, gdy zaj-
dzie koniecznoĞü zmiany prefiksu, poniewaĪ bĊdzie on definiowany tylko w jednym miejscu.
Wspólne funkcje równieĪ moĪna umieszczaü w oddzielnym, przeznaczonym dla nich pliku.
W projekcie uĪyta zostanie funkcja random_text(), której zadaniem bĊdzie wygenerowanie
ciągu znaków CAPTCHA i znacznika weryfikacji. FunkcjĊ random_text() moĪna zatem zapi-
saü w pliku functions.php.
?php
// zwrócenie ciągu losowych znaków o okreĞlonej dáugoĞci
function random_text($count, $rm_similar = false)
{
// utworzenie listy znaków
$chars = array_flip(array_merge(range(0, 9), range( A , Z )));
// usuniĊcie podobnie wyglądających znaków, aby uniknąü pomyáek
if ($rm_similar)
{
unset($chars[0], $chars[1], $chars[2], $chars[5], $chars[8],
$chars[ B ], $chars[ I ], $chars[ O ], $chars[ Q ],
$chars[ S ], $chars[ U ], $chars[ V ], $chars[ Z ]);
}
// wygenerowanie ciągu losowych znaków
for ($i = 0, $text = ; $i $count; $i++)
{
$text .= array_rand($chars);
}
return $text;
}
?
Bez wzglĊdu na to, w jakim jĊzyku implementuje siĊ kod Ĩródáowy, zawsze trzeba prze-
strzegaü podstawowej zasady, by nigdy nie ufaü danym wpisywanym przez uĪytkowników.
UĪytkownicy mogą (i bĊdą) wpisywaü wszelkiego rodzaju bezsensowne i niezrozumiaáe dane.
Czasami przez przypadek, niekiedy jednak jest to dziaáanie zamierzone. Za pomocą funkcji
20
PHP i MySQL. Projekty do wykorzystania
filter_input() i filter_var() jĊzyka PHP moĪna oczyĞciü dane wejĞciowe, jednak niektórzy
programiĞci wciąĪ wolą implementowaü wáasne procedury, poniewaĪ rozszerzenie udostĊp-
niające filtry moĪe nie byü dostĊpne w wersjach PHP wczeĞniejszych niĪ 5.2.0. Kod Ĩró-
dáowy tego rodzaju wáasnych procedur równieĪ warto umieĞciü w pliku functions.php.
Klasa User
Znakomitą wiĊkszoĞü kodu utrzymującego konta uĪytkowników moĪna zawrzeü w ramach
jednej struktury danych, którą bĊdzie moĪna póĨniej rozszerzaü albo ponownie wykorzystywaü
w kolejnych aplikacjach. Struktura bĊdzie implementowaü logikĊ interakcji z bazą danych,
a przez to uáatwiaü operacje zapisywania i odczytywania danych. PoniĪej przedstawiono
zawartoĞü pliku User.php.
?php
class User
{
private $uid; // identyfikator uĪytkownika
private $fields; // inne pola rekordu
// inicjalizacja obiektu User
public function __construct()
{
$this- uid = null;
$this- fields = array( username = ,
password = ,
emailAddr = ,
isActive = false);
}
// nadpisanie metody odczytującej wáaĞciwoĞci
public function __get($field)
{
if ($field == userId )
{
return $this- uid;
}
else
{
return $this- fields[$field];
}
}
// nadpisanie metody ustawiającej wáaĞciwoĞci
public function __set($field, $value)
{
if (array_key_exists($field, $this- fields))
{
$this- fields[$field] = $value;
}
}
RozdziaÄ 1. Q Rejestracja uÑytkowników
21
// sprawdzenie, czy nazwa uĪytkownika ma wáaĞciwy format
public static function validateUsername($username)
{
return preg_match( /^[A-Z0-9]{2,20}$/i , $username);
}
// sprawdzenie, czy adres e-mail ma wáaĞciwy format
public static function validateEmailAddr($email)
{
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
// zwrócenie obiektu wypeánionego na podstawie identyfikatora uĪytkownika
public static function getById($uid)
{
$u = new User();
$query = sprintf( SELECT USERNAME, PASSWORD, EMAIL_ADDR, IS_ACTIVE .
FROM sUSER WHERE USER_ID = d , DB_TBL_PREFIX, $uid);
$result = mysql_query($query, $GLOBALS[ DB ]);
if (mysql_num_rows($result))
{
$row = mysql_fetch_assoc($result);
$u- username = $row[ USERNAME ];
$u- password = $row[ PASSWORD ];
$u- emailAddr = $row[ EMAIL_ADDR ];
$u- isActive = $row[ IS_ACTIVE ];
$u- uid = $uid;
}
mysql_free_result($result);
return $u;
}
// zwrócenie obiektu wypeánionego na podstawie nazwy uĪytkownika
public static function getByUsername($username)
{
$u = new User();
$query = sprintf( SELECT USER_ID, PASSWORD, EMAIL_ADDR, IS_ACTIVE .
FROM sUSER WHERE USERNAME = s , DB_TBL_PREFIX,
mysql_real_escape_string($username, $GLOBALS[ DB ]));
$result = mysql_query($query, $GLOBALS[ DB ]);
if (mysql_num_rows($result))
{
$row = mysql_fetch_assoc($result);
$u- username = $username;
$u- password = $row[ PASSWORD ];
$u- emailAddr = $row[ EMAIL_ADDR ];
$u- isActive = $row[ IS_ACTIVE ];
$u- uid = $row[ USER_ID ];
}
mysql_free_result($result);
return $u;
}
22
PHP i MySQL. Projekty do wykorzystania
// zapisanie rekordu w bazie danych
public function save()
{
if ($this- uid)
{
$query = sprintf( UPDATE sUSER SET USERNAME = s , .
PASSWORD = s , EMAIL_ADDR = s , IS_ACTIVE = d .
WHERE USER_ID = d , DB_TBL_PREFIX,
mysql_real_escape_string($this- username, $GLOBALS[ DB ]),
mysql_real_escape_string($this- password, $GLOBALS[ DB ]),
mysql_real_escape_string($this- emailAddr, $GLOBALS[ DB ]),
$this- isActive, $this- userId);
mysql_query($query, $GLOBALS[ DB ]);
}
else
{
$query = sprintf( INSERT INTO sUSER (USERNAME, PASSWORD, .
EMAIL_ADDR, IS_ACTIVE) VALUES ( s , s , s , d) ,
DB_TBL_PREFIX,
mysql_real_escape_string($this- username, $GLOBALS[ DB ]),
mysql_real_escape_string($this- password, $GLOBALS[ DB ]),
mysql_real_escape_string($this- emailAddr, $GLOBALS[ DB ]),
$this- isActive);
mysql_query($query, $GLOBALS[ DB ]);
$this- uid = mysql_insert_id($GLOBALS[ DB ]);
}
}
// oznaczenie rekordu jako nieaktywnego i zwrócenie znacznika aktywacji
public function setInactive()
{
$this- isActive = false;
$this- save(); // zapewnienie, Īe rekord jest zapisany
$token = random_text(5);
$query = sprintf( INSERT INTO sPENDING (USER_ID, TOKEN) .
VALUES ( d, s ) , DB_TBL_PREFIX, $this- uid, $token);
mysql_query($query, $GLOBALS[ DB ]);
return $token;
}
// wyczyszczenie tymczasowego statusu uĪytkownika i oznaczenie rekordu jako aktywnego
public function setActive($token)
{
$query = sprintf( SELECT TOKEN FROM sPENDING WHERE USER_ID = d .
AND TOKEN = s , DB_TBL_PREFIX, $this- uid,
mysql_real_escape_string($token, $GLOBALS[ DB ]));
$result = mysql_query($query, $GLOBALS[ DB ]);
if (!mysql_num_rows($result))
{
mysql_free_result($result);
return false;
}
else
{
RozdziaÄ 1. Q Rejestracja uÑytkowników
23
mysql_free_result($result);
$query = sprintf( DELETE FROM sPENDING WHERE USER_ID = d .
AND TOKEN = s , DB_TBL_PREFIX, $this- uid,
mysql_real_escape_string($token, $GLOBALS[ DB ]));
mysql_query($query, $GLOBALS[ DB ]);
$this- isActive = true;
$this- save();
return true;
}
}
}
?
W klasie zdefiniowano dwie wáaĞciwoĞci prywatne: $uid, która odpowiada kolumnie USER_ID
tabeli HELION_USER, oraz tablicĊ $fields, która odpowiada pozostaáym kolumnom. Obydwie
wáaĞciwoĞci są udostĊpniane intuicyjnie, poprzez nadpisanie metod __get() i __set(). Jednak
wáaĞciwoĞü $uid jest nadal chroniona przed przypadkową zmianą.
Statyczne metody getById() i getByUsername() zawierają kod odpowiedzialny za odczyty-
wanie rekordu z bazy danych i wypeánianie obiektu danymi. Metoda save() zapisuje rekord
w bazie danych i jest na tyle inteligentna, Īe rozpoznaje, kiedy naleĪy wykonaü zapytanie
INSERT, a kiedy zapytanie UPDATE, zaleĪnie od tego, czy ustawiony jest identyfikator uĪyt-
kownika. W celu utworzenia nowego konta uĪytkownika wystarczy stworzyü nową instancjĊ
obiektu User, zdefiniowaü wartoĞci pól w rekordzie i wywoáaü metodĊ save().
?php
$u = new User();
$u- username = timothy ;
$u- password = sha1( sekret );
$u- emailAddr = timothy@helion.pl ;
$u- save();
?
W taki sam sposób przebiega czynnoĞü zmiany danych konta. Najpierw odczytywane są dane
aktualne, nastĊpnie w danych wprowadzane są zmiany, po czym nastĊpuje ich zapisanie w bazie
danych przez metodĊ save().
?php
$u = User::getByUsername( timothy );
$u- password = sha1( nowe_hasđo );
$u- save();
?
Metody setInactive() i setActive() obsáugują proces aktywacji konta. W wyniku wywo-
áania metody setInactive() konto zostaje oznaczone jako nieaktywne, nastĊpuje wygenero-
wanie znacznika aktywacji, informacja o tym fakcie zostaje zapisana w bazie danych i znacz-
nik jest zwracany. Gdy uĪytkownik uaktywni konto, znacznik aktywacji jest przekazywany
do metody setActive(). Metoda setActive() usuwa rekord ze znacznikiem aktywacji i ozna-
cza konto jako aktywne.
24
PHP i MySQL. Projekty do wykorzystania
CAPTCHA
WyraĪenie CAPTCHA to skrót od angielskich sáów Completely Automated Public Turing Test
to Tell Computers and Humans Apart, co w wolnym táumaczeniu moĪe oznaczaü Caákowicie
Zautomatyzowany Publiczny Test Turinga Wskazujący Komputerom i Ludziom, by Trzymali
siĊ z Daleka. CAPTCHA, oprócz tego, Īe jest trudnym do rozszyfrowania akronimem, czĊsto
bywa uĪywany jako narzĊdzie powstrzymującego spamerów i innych záoĞliwych uĪytkow-
ników przed automatycznym rejestrowaniem kont uĪytkowników.
W tym celu uĪytkownikowi stawia siĊ zadanie, które czĊsto ma postaü obrazka zawierają-
cego litery i cyfry. UĪytkownik musi odczytaü z niego tekst i przepisaü do pola tekstowego.
JeĪeli obydwie wartoĞci są identyczne, moĪna zaáoĪyü, Īe system ma do czynienia z inteligentną
istotą ludzką, a nie z komputerem próbującym automatycznie zaáoĪyü konto w systemie.
Nie jest to jednak rozwiązanie idealne. CAPTCHA moĪe sprawiaü problemy osobom z wadami
wzroku, a poza tym niektóre programy potrafią juĪ odczytywaü tekst zawarty na obrazkach
CAPTCHA (wiĊcej na ten temat pod adresem www.cs.sfu.ca/~mori/research/gimpy/). Zadania
CAPTCHA stawiane przed uĪytkownikami mogą mieü takĪe inną postaü. Istnieją na przykáad
zadania CAPTCHA w postaci dĨwiĊkowej — uĪytkownik musi wówczas wpisywaü litery
i cyfry usáyszane po odtworzeniu pliku audio. Niektóre zadania mają nawet postaü prostych
zadaĔ matematycznych.
Zadania CAPTCHA naleĪy traktowaü jako jedno z tych narzĊdzi w arsenale administratorów,
które sáuĪą do odstraszania leniwych záoczyĔców, nie powinny natomiast zastĊpowaü stan-
dardowych metod monitorowania i zabezpieczeĔ. NiedogodnoĞci dla uĪytkowników wzrastają
wraz ze stopniem skomplikowania zadania CAPTCHA, dlatego w tym projekcie ograniczymy
siĊ do najprostszego przykáadu, polegającego na wykorzystaniu obrazka.
?php
include ../../lib/functions.php ;
// naleĪy utworzyü lub kontynuowaü sesjĊ i zapisaü ciąg znaków CAPTCHA
// w $_SESSION, by byá dostĊpny w ramach innych wywoáaĔ
if (!isset($_SESSION))
{
session_start();
header( Cache-control: private );
}
// utworzenie obrazka o wymiarach 65x20 pikseli
$width = 65;
$height = 20;
$image = imagecreate(65, 20);
// wypeánienie obrazka kolorem táa
$bg_color = imagecolorallocate($image, 0x33, 0x66, 0xFF);
imagefilledrectangle($image, 0, 0, $width, $height, $bg_color);
// pobranie losowego tekstu
$text = random_text(5);
RozdziaÄ 1. Q Rejestracja uÑytkowników
25
// ustalenie wspóárzĊdnych x i y do wyĞrodkowania tekstu
$font = 5;
$x = imagesx($image) / 2 - strlen($text) * imagefontwidth($font) / 2;
$y = imagesy($image) / 2 - imagefontheight($font) / 2;
// wypisanie tekstu na obrazku
$fg_color = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
imagestring($image, $font, $x, $y, $text, $fg_color);
// zapisanie ciągu znaków CAPTCHA do póĨniejszego porównania
$_SESSION[ captcha ] = $text;
// zwrócenie obrazka
header( Content-type: image/png );
imagepng($image);
imagedestroy($image);
?
Skrypt najlepiej jest zapisaü w folderze public_files/img (poniewaĪ musi on byü publicznie
dostĊpny i zwracaü obrazek graficzny), w pliku o nazwie captcha.php. Skrypt tworzy obrazek
PNG o wymiarach 65 na 20 pikseli, z táem koloru niebieskiego oraz biaáym, losowym cią-
giem piĊciu znaków, jak na rysunku 1.1. Ciąg znaków musi byü przechowywany w zmiennej
$_SESSION, aby nieco póĨniej moĪna byáo sprawdziü, czy uĪytkownik przepisaá go prawi-
dáowo. Aby bardziej skomplikowaü obrazek, moĪna uĪyü róĪnych czcionek, kolorów oraz
zastosowaü obrazki w tle.
Rysunek 1.1
Szablony
DziĊki szablonom programistom áatwiej jest utrzymywaü spójny wygląd i ukáad poszczegól-
nych stron witryny. Szablony nadają organizacjĊ kodu oraz przenoszą logikĊ prezentacji poza
wáaĞciwy kod Ĩródáowy, dziĊki czemu pliki PHP oraz HTML stają siĊ bardziej czytelne.
Na rynku dostĊpnych jest wiele rozwiązaĔ do obsáugi szablonów — niektóre rozbudowane
(na przykáad Smarty: http://smarty.php.net), inne niepozorne (TinyButStrong: www.tinybut
´strong.com). KaĪde z tych rozwiązaĔ ma wáasne wady i zalety, bez wzglĊdu na to, czy jest
to produkt komercyjny o otwartym dostĊpie do kodu Ĩródáowego, czy tworzony na uĪytek
domowy. W wielu przypadkach ostateczna decyzja o tym, którego z tych rozwiązaĔ uĪyü, jest
podejmowana na podstawie wáasnych upodobaĔ programisty.
JeĞli chodzi o osobiste upodobania, moĪna caáym sercem popieraü sam pomysá wykorzystania
szablonów, a jednoczeĞnie nie przepadaü za wiĊkszoĞcią implementacji tej idei. Pomimo
niewątpliwych zalet obecnie dostĊpne rozwiązania do obsáugi szablonów wiele rzeczy kom-
plikują. W niektórych stosowana jest ich wáasna, specjalna skáadnia, której trzeba siĊ nauczyü;
poza tym praktycznie wszystkie wydáuĪają proces przetwarzania kodu. PrawdĊ mówiąc,
w wiĊkszoĞci projektów wykorzystanie oddzielnego mechanizmu obsáugi szablonów nie jest
w ogóle potrzebne, poniewaĪ PHP sam moĪe byü uwaĪany za moduá obsáugi szablonów,
26
PHP i MySQL. Projekty do wykorzystania
zdolny obsáugiwaü nawet witryny o Ğredniej wielkoĞci, tworzone przez wiĊkszą liczbĊ pro-
gramistów. Wystarczy zastosowaü w takich przypadkach odpowiednie planowanie i orga-
nizacjĊ prac.
Rozwiązanie, które wydaje siĊ najlepsze, polega na wydzieleniu najwaĪniejszych elemen-
tów prezentacji w plikach HTML w folderze templates. Folder ten zwykle umieszczany jest
poza folderem dostĊpnym publicznie (choü pliki CSS, JavaScript i graficzne przywoáywane
w kodzie HTML muszą juĪ byü publicznie dostĊpne), aby uniknąü sytuacji, w której uĪyt-
kownik lub wyszukiwarka znajduje pliki w praktyce pozbawione treĞci.
PoniĪej przedstawiono podstawowy szablon, speániający wymagania naszego projektu.
!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Strict//EN
http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd
html xmlns= http://www.w3.org/1999/xhtml xml:lang= en lang= en
head
meta http-equiv= Content-Type content= text/html; charset=iso-8859-2 /
title
?php
if (!empty($GLOBALS[ TEMPLATE ][ title ]))
{
echo $GLOBALS[ TEMPLATE ][ title ];
}
?
/title
link rel= stylesheet type= text/css href= css/styles.css /
?php
if (!empty($GLOBALS[ TEMPLATE ][ extra_head ]))
{
echo $GLOBALS[ TEMPLATE ][ extra_head ];
}
?
/head
body
div id= header
?php
if (!empty($GLOBALS[ TEMPLATE ][ title ]))
{
echo $GLOBALS[ TEMPLATE ][ title ];
}
?
/div
div id= content
?php
if (!empty($GLOBALS[ TEMPLATE ][ content ]))
{
echo $GLOBALS[ TEMPLATE ][ content ];
}
?
/div
div id= footer Copyright #169; ?php echo date( Y ); ? /div
/div
/body
/html
RozdziaÄ 1. Q Rejestracja uÑytkowników
27
Zgodnie z powszechnie przyjĊtymi zasadami naleĪy ustaliü pewne konwencje. TreĞü bĊdzie
przechowywana w tablicy $GLOBALS w skrypcie wywoáującym, dziĊki czemu bĊdzie równieĪ
dostĊpna w dowolnym zasiĊgu wewnątrz doáączonego pliku szablonu. Zwykle uĪywane są
nastĊpujące klucze:
Q title — Tytuá strony.
Q description — Opis strony.
Q keywords — Sáowa kluczowe dla strony (tytuá strony, opis i sáowa kluczowe mogą
byü przechowywane w bazie danych).
Q extra_head — Klucz do wstawiania dodatkowych nagáówków HTML lub kodu
JavaScript do kodu strony.
Q content — Gáówna treĞü strony.
Czasami uĪywa siĊ równieĪ kluczy dla menu lub ramek, zaleĪnie od ukáadu, jaki zaplanowano
dla strony. Za kaĪdym razem jednak konkretne nazwy zmiennych bĊdą zaleĪeü od szablonu.
JeĞli tylko zdefiniuje siĊ i zapisze standardowe konwencje, które potem bĊdą konsekwentnie
stosowane, zespóá implementacyjny dowolnej wielkoĞci moĪe z powodzeniem wykorzysty-
waü tak opracowany mechanizm obsáugi szablonów.
Rejestracja nowego uÑytkownika
Zdefiniowano juĪ strukturĊ katalogów, zaimplementowano takĪe odpowiednią czĊĞü kodu
pomocniczego, moĪna siĊ wiĊc teraz skupiü na rejestrowaniu nowego uĪytkownika. Kod Ĩró-
dáowy przedstawiony poniĪej moĪna zapisaü w folderze public_files, w pliku o nazwie regi-
ster.php. Na rysunku 1.2 przedstawiono tĊ samą stronĊ wyĞwietloną w przeglądarce.
Rysunek 1.2
28
PHP i MySQL. Projekty do wykorzystania
?php
// doáączenie kodu wspóáuĪytkowanego
include ../lib/common.php ;
include ../lib/db.php ;
include ../lib/functions.php ;
include ../lib/User.php ;
// rozpoczĊcie lub kontynuacja sesji, by udostĊpniü
// test CAPTCHA przechowywany w zmiennej $_SESSION
session_start();
header( Cache-control: private );
// przygotowanie formularza HTML do rejestracji
ob_start();
?
form method= post
action= ?php echo htmlspecialchars($_SERVER[ PHP_SELF ]); ?
table
tr
td label for= username Nazwa uľytkownika /label /td
td input type= text name= username id= username
value= ?php if (isset($_POST[ username ]))
echo htmlspecialchars($_POST[ username ]); ? / /td
/tr tr
td label for= password1 Hasđo /label /td
td input type= password name= password1 id= password1
value= / /td
/tr tr
td label for= password2 Powtórzenie hasđa /label /td
td input type= password name= password2 id= password2
value= / /td
/tr tr
td label for= email Adres email /label /td
td input type= text name= email id= email
value= ?php if (isset($_POST[ email ]))
echo htmlspecialchars($_POST[ email ]); ? / /td
/tr tr
td label for= captcha Weryfikacja /label /td
td Wpisz tekst widoczny na obrazku br/
img src= img/captcha.php?nocache= ?php echo time(); ? alt= / br /
input type= text name= captcha id= captcha / /td
/tr tr
td /td
td input type= submit value= Zarejestruj / /td
td input type= hidden name= submitted value= 1 / /td
/tr tr
/table
/form
?php
$form = ob_get_clean();
// wyĞwietlenie formularza, jeĞli strona jest wyĞwietlana po raz pierwszy
if (!isset($_POST[ submitted ]))
{
$GLOBALS[ TEMPLATE ][ content ] = $form;
}
RozdziaÄ 1. Q Rejestracja uÑytkowników
29
// w przeciwnym razie przetworzenie danych wejĞciowych
else
{
// weryfikacja hasáa
$password1 = (isset($_POST[ password1 ])) ? $_POST[ password1 ] : ;
$password2 = (isset($_POST[ password2 ])) ? $_POST[ password2 ] : ;
$password = ($password1 $password1 == $password2) ?
sha1($password1) : ;
// weryfikacja tekstu CAPTCHA
$captcha = (isset($_POST[ captcha ])
strtoupper($_POST[ captcha ]) == $_SESSION[ captcha ]);
// jeĞli wszystkie dane są prawidáowe — dodanie rekordu
if ($password
$captcha
User::validateUsername($_POST[ username ])
User::validateEmailAddr($_POST[ email ]))
{
// sprawdzenie, czy uĪytkownik juĪ istnieje
$user = User::getByUsername($_POST[ username ]);
if ($user- userId)
{
$GLOBALS[ TEMPLATE ][ content ] = p strong Przepraszamy, .
takie konto juľ istnieje. /strong /p p Prosimy podaè .
innæ nazwú uľytkownika. /p ;
$GLOBALS[ TEMPLATE ][ content ] .= $form;
}
else
{
// utworzenie nieaktywnego rekordu uĪytkownika
$u = new User();
$u- username = $_POST[ username ];
$u- password = $password;
$u- emailAddr = $_POST[ email ];
$token = $u- setInactive();
$GLOBALS[ TEMPLATE ][ content ] = p strong Dziúkujemy za .
zarejestrowanie siú. /strong /p p Naleľy pamiútaè o .
zweryfikowaniu konta i kliknæè đæcze a href= verify.php?uid= .
$u- userId . token= . $token . verify.php?uid= .
$u- userId . token= . $token . /a /p ;
}
}
// dane nieprawidáowe
else
{
$GLOBALS[ TEMPLATE ][ content ] .= p strong Podano nieprawidđowe .
dane. /strong /p p Prosimy prawidđowo wypeđniè .
wszystkie pola, abyħmy mogli zarejestrowaè konto uľytkownika. /p ;
$GLOBALS[ TEMPLATE ][ content ] .= $form;
}
}
// wyĞwietlenie strony
include ../templates/template-page.php ;
?
30
PHP i MySQL. Projekty do wykorzystania
Najpierw kod z pliku register.php importuje pliki z kodem wspóáuĪytkowanym, który bĊdzie
póĨniej wykorzystywany. Niektórzy programiĞci wolą umieszczaü wszystkie instrukcje include
w jednym wspólnym pliku nagáówkowym, a nastĊpnie doáączaü sam plik nagáówkowy —
dziĊki temu kod wáaĞciwy jest krótszy. W projekcie bĊdziemy jednak oddzielnie doáączaü
pojedyncze pliki, poniewaĪ wydaje siĊ, Īe takie rozwiązanie jest prostsze w utrzymaniu.
Inni programiĞci wykorzystują funkcjĊ chdir() do zmiany katalogu roboczego PHP, dziĊki
czemu nie trzeba za kaĪdym razem odwracaü ĞcieĪek w systemie w celu doáączenia pliku.
RównieĪ tutaj decydują osobiste upodobania. Jednak w przypadku wyboru takiego rozwiązania
trzeba zwróciü szczególną uwagĊ na starsze instalacje PHP, w których uĪywany jest tryb
bezpieczny. Wykonanie funkcji chdir() moĪe bowiem siĊ nie udaü i nie zwróci Īadnego
komunikatu o báĊdzie, jeĪeli wskazany katalog bĊdzie niedostĊpny.
?php
// doáączenie kodu wspóáuĪytkowanego
chdir( ../ );
include lib/common.php ;
include lib/db.php ;
include lib/functions.php ;
include lib/User.php ;
…
?
Po zaimportowaniu plików z kodem wspóáuĪytkowanym wywoáywana jest metoda session_
´start(). Wywoáania HTTP są bezstanowe, co oznacza, Īe serwer WWW zwraca kaĪdą
stronĊ bez Ğledzenia, co dziaáo siĊ z nią wczeĞniej, i bez przewidywania, co moĪe siĊ z nią
zdarzyü w przyszáoĞci. Mechanizm Ğledzenia sesji dostĊpny w PHP umoĪliwia utrzymywanie
w prosty sposób stanów miĊdzy kolejnym wywoáaniami oraz przenoszenie wartoĞci z jed-
nego wywoáania do nastĊpnego. Wykorzystanie sesji jest niezbĊdne, aby utrzymaü wartoĞü
CAPTCHA wygenerowaną w pliku captcha.php.
Gdy przygotowywane są wiĊksze bloki kodu HTML, na przykáad kod formularza do reje-
stracji, warto jest je buforowaü dla zwiĊkszenia czytelnoĞci kodu Ĩródáowego. Niektórzy pro-
gramiĞci preferują natomiast definiowanie zmiennej buforowej i cykliczne doklejanie do niej
kolejnych fragmentów HTML, na przykáad:
?php
$GLOBALS[ TEMPLATE ][ content ] = form action= .
htmlspecialchars(currentFile()) . method= post ;
$GLOBALS[ TEMPLATE ][ content ] .= table ;
$GLOBALS[ TEMPLATE ][ content ] .= tr ;
$GLOBALS[ TEMPLATE ][ content ] .= td label for= username Nazwa
´uľytkownika /label . /td ;
…
?
Takie podejĞcie wydaje siĊ jednak doĞü niewygodne i stosunkowo czasocháonne. W przypadku
buforowania danych wynikowych wystarczy rozpocząü buforowanie wywoáaniem funkcji
ob_start(), odczytaü zawartoĞü bufora funkcją ob_get_contents() i zatrzymaü buforowanie
funkcją ob_end_clean(). Funkcja ob_get_clean() áączy w sobie wywoáanie dwóch funkcji:
ob_get_contents() i ob_end_clean(). Ponadto dla interpretera áatwiej jest wáączaü i wyáączaü
tryb PHP, dlatego tak skonstruowany kod obsáugi duĪych bloków danych wyjĞciowych powi-
nien dziaáaü szybciej niĪ w metodzie z dopisywaniem danych do bufora.
RozdziaÄ 1. Q Rejestracja uÑytkowników
31
Gdy uĪytkownik po raz pierwszy wywoáuje stronĊ, nie powinno byü zdefiniowanych Īadnych
wartoĞci $_POST, dlatego kod po prostu zwraca formularz rejestracji. Gdy uĪytkownik zatwierdzi
formularz, ustawiana jest zmienna $_POST[ submitted ], dziĊki czemu wiadomo, Īe naleĪy
rozpocząü przetwarzanie danych wejĞciowych.
Kod, którego zadaniem jest weryfikacja poprawnoĞci nazwy uĪytkownika i hasáa, naleĪy do
klasy User. Obydwa hasáa wpisane w formularzu są ze sobą porównywane, a nastĊpnie hasáo
jest szyfrowane i juĪ w postaci zaszyfrowanej zapisywane w bazie danych do póĨniejszego
uĪycia. Na koniec wartoĞü wpisana przez uĪytkownika na podstawie obrazka CAPTCHA
jest porównywana z wartoĞcią wczeĞniej zapisaną w sesji przez kod z pliku captcha.php.
JeĪeli wszystkie dane są poprawne, rekord zostaje zapisany w bazie danych.
Skrypt verify.php przywoáywany w kodzie HTML odpowiada za odczytanie identyfikatora
uĪytkownika i znacznika aktywacji, sprawdzenie odpowiednich danych w bazie i uaktyw-
nienie konta uĪytkownika. Skrypt ten równieĪ musi zostaü zapisany w katalogu dostĊpnym
publicznie.
?php
// doáączenie kodu wspóáuĪytkowanego
include ../lib/common.php ;
include ../lib/db.php ;
include ../lib/functions.php ;
include ../lib/User.php ;
// sprawdzenie, czy otrzymano identyfikator uĪytkownika i znacznik
if (!isset($_GET[ uid ]) || !isset($_GET[ token ]))
{
$GLOBALS[ TEMPLATE ][ content ] = p strong Otrzymane informacje .
sæ niepeđne. /strong /p p Prosimy spróbowaè ponownie. /p ;
include ../templates/template-page.phptemplate_page.php ;
exit();
}
// weryfikacja identyfikatora uĪytkownika
if (!$user = User::getById($_GET[ uid ]))
{
$GLOBALS[ TEMPLATE ][ content ] = p strong Podane konto nie
´istnieje. /strong .
/p p Prosimy spróbowaè ponownie. /p ;
}
// upewnienie siĊ, Īe konto jest nieaktywne
else
{
if ($user- isActive)
{
$GLOBALS[ TEMPLATE ][ content ] = p strong Konto .
zostađo juľ zweryfikowane. /strong /p ;
}
// uaktywnienie konta
else
{
if ($user- setActive($_GET[ token ]))
{
$GLOBALS[ TEMPLATE ][ content ] = p strong Dziúkujemy .
za zweryfikowanie konta. /strong /p p Moľna siú .
teraz a href= login.php zalogowaè /a . /p ;
32
PHP i MySQL. Projekty do wykorzystania
}
else
{
$GLOBALS[ TEMPLATE ][ content ] = p strong Podano .
nieprawidđowe dane. /strong /p p Prosimy spróbowaè
´ponownie. /p ;
}
}
}
// wyĞwietlenie strony
include ../templates/template-page.php ;
?
WysyÄanie e-maila z ľczem do weryfikacji
Aktualnie skrypt register.php wyĞwietla bezpoĞrednie áącze sáuĪące do weryfikacji konta,
natomiast w Ğrodowisku produkcyjnym zwykle odpowiednie áącze wysyáa siĊ pocztą elek-
troniczną na adres wpisany przez uĪytkownika. Wychodzi siĊ przy tym z zaáoĪenia, Īe praw-
dziwy uĪytkownik poda prawidáowy adres pocztowy i samodzielnie potwierdzi zaáoĪenie
konta, czego nie robi znakomita wiĊkszoĞü spamerów.
Funkcja mail() sáuĪy do wysyáania poczty elektronicznej przez PHP. Pierwszym argumentem
funkcji jest adres pocztowy uĪytkownika, drugim jest temat wiadomoĞci pocztowej, trzecim
zaĞ — treĞü samej wiadomoĞci. Zazwyczaj zaleca siĊ, by nie wstrzymywaü wyĞwietlania
ostrzeĪeĔ przy uĪyciu symbolu @, w tym przypadku jednak jest to konieczne, poniewaĪ
w razie niepowodzenia funkcja mail() zwróci wartoĞü false oraz wygeneruje komunikat
z ostrzeĪeniem.
Kod, który naleĪy umieĞciü w pliku register.php, aby zamiast wyĞwietlaü áącze do weryfikacji
konta w przeglądarce, wysyáaü je w wiadomoĞci pocztowej, moĪe mieü nastĊpującą postaü:
?php
…
// utworzenie nieaktywnego rekordu uĪytkownika
$u = new User();
$u- username = $_POST[ username ];
$u- password = $password;
$u- emailAddr = $_POST[ email ];
$token = $u- setInactive();
$message = Dziúkujemy za zarejestrowanie siú! Przed zalogowaniem siú .
naleľy pamiútaè o zweryfikowaniu konta. W tym celu trzeba wejħè .
na stronú http://www.przyklad.com/verify.php?uid= .
$u- userId . token= . $token . . ;
if (@mail($u- emailAddr, Aktywacja nowego konta , $message))
{
$GLOBALS[ TEMPLATE ][ content ] = p strong Dziúkujemy za .
zarejestrowanie siú. /strong /p p Wkrótce otrzymasz .
wiadomoħè pocztowæ z instrukcjami na temat sposobu .
aktywowania konta. /p ;
RozdziaÄ 1. Q Rejestracja uÑytkowników
33
}
else
{
$GLOBALS[ TEMPLATE ][ content ] = p strong Wystæpiđ bđæd .
w trakcie wysyđania đæcza aktywacyjnego. /strong /p .
p Prosimy skontaktowaè siú z administratorem .
pod adresem a href= mailto:admin@przyklad.com .
admin@przyklad.com /a , aby uzyskaè pomoc. /p ;
}
…
?
Na rysunku 1.3 przedstawiono wiadomoĞü pocztową z potwierdzeniem, odczytaną w progra-
mie pocztowym.
Rysunek 1.3
Wysyáanie wiadomoĞci pocztowej zawierającej zwykáy tekst jest prostym zadaniem, nato-
miast wysyáanie wiadomoĞci sformatowanej w jĊzyku HTML wymaga nieco wiĊcej pracy.
Obydwa rodzaje wiadomoĞci mają niewątpliwe zalety: wiadomoĞci tekstowe są bardziej czy-
telne, a prawdopodobieĔstwo ich zablokowania przez filtry antyspamowe jest stosunkowo
niskie, natomiast wiadomoĞci w jĊzyku HTML są bardziej przyjazne dla uĪytkowników,
mniej sterylne, a poza tym mogą zawieraü w treĞci hiperáącza, dziĊki którym áatwiej jest skie-
rowaü uĪytkownika na stronĊ odpowiadającą za weryfikacjĊ konta.
WiadomoĞü pocztowa w jĊzyku HTML moĪe wyglądaü nastĊpująco:
html
p Dziúkujemy za zarejestrowanie siú! /p
p Przed zalogowaniem siú naleľy pamiútaè o zweryfikowaniu konta.
34
PHP i MySQL. Projekty do wykorzystania
W tym celu trzeba wejħè na stronú
a href= http://www.przyklad.com/verify.php?uid=### amp;token=xxxxx
http://www.przyklad.com/verify.php?uid=### amp;token=xxxxx /a . /p
p Jeľeli uľywany program pocztowy nie pozwala na klikniúcie hiperđæczy
obecnych w tej wiadomoħci, naleľy skopiowaè hiperđæcze i wkleiè je w pasku
adresów przeglædarki, aby wyħwietliè stronú do weryfikacji konta. /p
/html
Gdyby jednak powyĪszą wiadomoĞü wysáaü w sposób przedstawiony w poprzednim przy-
káadzie, wówczas i tak dotaráaby ona do adresata w postaci zwykáego tekstu, pomimo Īe
zawiera przecieĪ znaczniki jĊzyka HTML. Aby wskazaü klientowi pocztowemu prawidáowy
sposób wyĞwietlania wiadomoĞci, konieczne jest równieĪ przesáanie odpowiednich nagáów-
ków MIME i Content-Type. Dodatkowe nagáówki stanowią opcjonalny czwarty parametr funk-
cji mail().
?php
// treĞü sformatowanej wiadomoĞci przechowywana w zmiennej $html_message
// sformatowany e-mail wymaga podania nagáówków MIME i Content-Type
$headers = array( MIME-Version: 1.0 ,
Content-Type: text/html; charset= iso-8859-2 );
// dodatkowe nagáówki są przekazywane jako czwarty argument funkcji mail()
mail($user- emailAddr, Prosimy aktywowaè nowe konto , $html_message,
join( \n , $headers));
?
MoĪliwe jest poáączenie zalet obu rodzajów wiadomoĞci pocztowych — wystarczy w tym
celu wysáaü wiadomoĞü w formacie mieszanym. WiadomoĞü w formacie mieszanym zawiera
tak naprawdĊ dwie wiadomoĞci: tekstową i sformatowaną w jĊzyku HTML, a juĪ do decyzji
klienta pocztowego pozostaje, która czĊĞü wiadomoĞci zostanie wyĞwietlona. PoniĪej przed-
stawiono wiadomoĞü mieszaną:
--==A.BC_123_XYZ_678.9
Content-Type: text/plain; charset= iso-8859-2
Dziúkujemy za zarejestrowanie siú!
Przed zalogowaniem siú naleľy pamiútaè o zweryfikowaniu konta.
W tym celu trzeba wejħè na stronú
´http://www.przyklad.com/verify.php?uid=### amp;token=xxxxx.
--==A.BC_123_XYZ_678.9
Content-Type: text/plain; charset= iso-8859-2
html
p Dziúkujemy za zarejestrowanie siú! /p
p Przed zalogowaniem siú naleľy pamiútaè o zweryfikowaniu konta.
W tym celu trzeba wejħè na stronú
a href= http://www.przyklad.com/verify.php?uid=### amp;token=xxxxx
http://www.przyklad.com/verify.php?uid=### amp;token=xxxxx /a . /p
p Jeľeli uľywany program pocztowy nie pozwala na klikniúcie hiperđæczy
obecnych w tej wiadomoħci, naleľy skopiowaè hiperđæcze i wkleiè je w pasku
adresów przeglædarki, aby wyħwietliè stronú do weryfikacji konta. /p
/html
--==A.BC_123_XYZ_678.9--
RozdziaÄ 1. Q Rejestracja uÑytkowników
35
Aby wysáaü tak skonstruowaną wiadomoĞü, trzeba uĪyü nastĊpujących nagáówków:
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary= ==A.BC_123_XYZ_678.9
Warto zwróciü uwagĊ, Īe poszczególne segmenty wiadomoĞci pocztowej są od siebie od-
dzielone specjalnym ciągiem znaków. Ciąg znaków w postaci ==A.BC_123_XYZ_678.9 nie
ma tak naprawdĊ konkretnego znaczenia; wystarczy, by byá to losowy ciąg znaków, który
nie pojawi siĊ nigdzie w treĞci któregoĞ z elementów wiadomoĞci. Ciąg znaków oddzielają-
cy od siebie poszczególne bloki wiadomoĞci jest zawsze poprzedzany dwoma myĞlnikami,
a przed nim wystĊpuje pusty wiersz. MyĞlniki na koĔcu tego ciągu wskazują jednoczeĞnie
koniec caáej wiadomoĞci.
Logowanie i wylogowywanie
MoĪna juĪ tworzyü nowe konta uĪytkowników i weryfikowaü je jako konta zaáoĪone przez
prawdziwych uĪytkowników dziĊki wykorzystaniu podanego adresu poczty elektronicznej.
Kolejnym krokiem jest zatem opracowanie mechanizmu, który bĊdzie dostĊpny dla uĪyt-
kowników i pozwoli im na logowanie siĊ i wylogowywanie z systemu. WiĊkszoĞü uciąĪli-
wych zadaĔ związanych ze Ğledzeniem sesji bĊdzie wykonywana przez PHP, dlatego nam
pozostaje tylko zapisywanie danych identyfikacyjnych w zmiennej $_SESSION. PoniĪszy kod
naleĪy zapisaü w pliku login.php.
?php
// do
Pobierz darmowy fragment (pdf)