You are on page 1of 21

PHP 5 w praktyce

Autorzy: Elliott White, Jonathan D. Eisenhamer


Tumaczenie: Piotr Gaczkowski,
Radosaw Meryk, Piotr Pietrzak
ISBN: 978-83-246-0814-0
Tytu oryginau: PHP 5 in Practice
Format: B5, stron: 432

Poznaj potg jzyka PHP 5


Chcesz poprawi dziaanie swoich aplikacji internetowych, jednoczenie wykorzystujc
peni moliwoci oferowanych przez PHP 5? A moe w projekcie natkne si na
problem i szukasz rozwizania? Najnowsza wersja PHP to w peni obiektowy jzyk
programowania, umoliwiajcy tworzenie rozbudowanych i stabilnych aplikacji
internetowych. Dostpny bezpatnie PHP 5 jest podstawowym narzdziem wielu
programistw na caym wiecie, cenicych jego prost skadni, ogromny zakres
zastosowa i szybko dziaania.
PHP 5 w praktyce to zbir porad, dziki ktrym utworzysz ciekawe, wydajne i szybko
dziaajce aplikacje internetowe i witryny WWW. Znajdziesz tu rozwizania najczciej
spotykanych zada programistycznych oraz wskazwki dotyczce stosowania
poszczeglnych elementw jzyka PHP 5. Przeczytasz o przetwarzaniu tekstw, liczb
i dat, programowaniu obiektowym, poczeniach z bazami danych, tworzeniu elementw
interfejsu uytkownika oraz operacjach na plikach i katalogach. Dowiesz si take, jak
korzysta z plikw XML, przetwarza grafik i usuwa bdy z aplikacji PHP.
Dziki ksice poznasz:

Wydawnictwo Helion
ul. Kociuszki 1c
44-100 Gliwice
tel. 032 230 98 63
e-mail: helion@helion.pl

Przetwarzanie danych tekstowych i liczbowych


Operacje na tablicach
Stosowanie funkcji
Praca z systemem plikw
Tworzenie elementw witryn WWW
Obsuga formularzy HTML
Sesje i pliki cookie
Wysyanie poczty elektronicznej
Komunikacja z bazami danych
Przetwarzanie plikw XML
Generowanie grafiki
Szyfrowanie danych

O autorach .................................................................................... 9
Wprowadzenie ............................................................................ 11

I
1

Opis jzyka PHP ....................................................... 19


Cigi znakw .............................................................................. 21
1.1. Dopasowywanie wzorcw (wyraenia regularne)
1.2. Usuwanie biaych znakw
1.3. Rozwijanie tabulacji do spacji oraz konwersja spacji na tabulacje
1.4. Konwersja tekstu w formacie systemw Mac OS, Unix i Windows
1.5. Przetwarzanie dokumentw w formacie CSV
1.6. Obcinanie tekstu, by zmieci si w okrelonej przestrzeni
1.7. Uzupenianie tablic z danymi w celu wywietlenia ich w kolumnach
1.8. Sprawdzanie pisowni sowa
1.9. Dopasowywanie podobnych cigw znakw
1.10. Waciwe stosowanie wielkich liter w nazwach wasnych
1.11. Generowanie unikatowych identyfikatorw
1.12. Zliczanie wystpie okrelonego sowa w tekcie

Liczby .......................................................................................... 41
2.1. Odczytywanie liczby na podstawie wartoci znakowej
2.2. Wywietlanie poprawnych form liczby mnogiej
2.3. Konwersja danych liczbowych na liczby rzymskie
2.4. Obliczanie odsetek
2.5. Symulacja rzutw komi
2.6. Obliczenia dugoci i szerokoci geograficznej
2.7. Konwersja jednostek metrycznych na angielskie
2.8. Konwersja temperatury
2.9. Tworzenie pakietu funkcji statystycznych

24
26
27
28
29
31
32
34
36
37
38
39

43
44
45
46
51
52
56
59
60

Godziny i daty ............................................................................ 63


3.1. Obliczanie rnicy czasu midzy dwiema datami
3.2. Wyznaczanie ostatniego dnia podanego miesica
3.3. Sprawdzenie, czy rok jest przestpny
3.4. Obsuga stref czasowych

66
68
68
69

PHP 5 w praktyce
3.5. Obsuga znacznikw czasu w bazach danych lub plikach
3.6. Wyznaczanie liczby dni roboczych
3.7. Generowanie kalendarza dla okrelonego miesica

Zmienne ...................................................................................... 77
4.1. Sprawdzanie, czy zmienna jest rwna innej zmiennej
4.2. Dostp do zmiennych spoza funkcji (zmienne globalne)
4.3. Utrzymywanie trwaych wartoci w obrbie funkcji (zmienne statyczne)
4.4. Odwoania do zmiennych (referencje)
4.5. Wykorzystywanie zmiennej do zapisania nazwy innej zmiennej
4.6. Deklaracje staych zamiast zmiennych
4.7. Przegldanie listy wartoci w celu znalezienia pierwszej,
ktra ma warto rn od false

85

92
93
95
97
99
103
104
106

Funkcje ..................................................................................... 113


6.1. Ustawianie opcjonalnych parametrw
6.2. Tworzenie funkcji rekurencyjnych
6.3. Wywoywanie funkcji z wykorzystaniem nazwy funkcji zapisanej w zmiennej
6.4. Dynamiczne tworzenie funkcji (styl lambda)
6.5. Wykorzystanie do oblicze tablicy funkcji
6.6. Przekazywanie argumentw i zwracanie wartoci przez referencje
6.7. Uywanie zmiennej liczby argumentw
6.8. Wymaganie okrelonego typu parametru

78
80
81
82
83
84

Tablice ........................................................................................ 87
5.1. Tablice superglobalne i ich zastosowanie
5.2. Implementacja stosu
5.3. Implementacja kolejki
5.4. Sortowanie z wykorzystaniem porwna definiowanych przez uytkownikw
5.5. Sortowanie z wykorzystaniem rnych algorytmw
5.6. Rekurencyjna obsuga tablic wielowymiarowych
5.7. Wykonywanie operacji na zbiorach z wykorzystaniem tablic
5.8. Operacje na macierzach z wykorzystaniem tablic

71
72
74

114
115
117
118
120
121
122
123

Klasy i obiekty .......................................................................... 127


7.1. Automatyczne adowanie plikw rdowych klasy
7.2. Ochrona danych obiektowych (modyfikatory public, private, protected)
7.3. Automatyczne uruchamianie kodu podczas tworzenia lub niszczenia obiektw
7.4. Dostp do skadowych klasy bez tworzenia egzemplarza
7.5. Rozszerzanie definicji klasy
7.6. Tworzenie klas abstrakcyjnych
7.7. Zastosowanie interfejsw obiektw
7.8. Dynamiczne i przecione nazwy zmiennych
7.9. Przecianie metod
7.10. Implementacja powizanych list
7.11. Implementacja drzew binarnych

129
132
133
134
135
137
139
140
144
147
149

Spis treci

Pliki i katalogi .......................................................................... 155


8.1. Wygenerowanie penej zawartoci katalogu
8.2. Wywietlanie rozmiaru plikw w naturalny sposb
8.3. Zmiana nazwy wszystkich plikw w katalogu
8.4. Wyszukiwanie nazw plikw w drzewie katalogw
8.5. Wzgldne i bezwzgldne cieki dostpu do plikw
8.6. Czytanie pliku za porednictwem protokow HTTP lub FTP
8.7. Obserwacja zawartoci pliku w miar wzrostu jego objtoci
(symulacja uniksowego polecenia tail -f)
8.8. Generowanie raportu z rnic midzy dwoma plikami
8.9. Blokowanie pliku w celu uzyskania wycznoci
8.10. Lokalne buforowanie zdalnych plikw
8.11. Kompresowanie i dekompresowanie plikw
8.12. Automatyczne wczanie okrelonych plikw z drzewa nadrzdnego

II
9

168
170
174
178
181
183

Tworzenie stron WWW / XHTML / CSS ................................. 187


189
194
195
198
200
202
204
207
207

Obsuga formularzy internetowych ......................................... 211


10.1. Dostp do danych formularzy
10.2. Wykorzystanie tablic wielowymiarowych z danymi
wprowadzanymi za porednictwem formularzy
10.3. Jednoczesne korzystanie z danych formularzy
przesyanych metod GET i POST
10.4. Obsuga przesyania plikw na serwer
10.5. Generowanie listy opcji do wyboru
10.6. Wykorzystanie pl obowizkowych
10.7. Przetwarzanie tekstu do wywietlenia na forach (BBCode)
10.8. Pobieranie od uytkownika danych
i wywietlanie ostrzee dotyczcych czy
10.9. Zapobieganie wielokrotnemu przesyaniu formularzy

11

158
159
162
164
165
167

Aplikacje PHP ......................................................... 185


9.1. Tworzenie wielowarstwowego, rozwijanego menu
9.2. Wyrnianie sekcji witryny WWW aktualnie przegldanej przez uytkownika
9.3. Wywietlanie dynamicznych paskw postpu
9.4. Symulacja graficznych wykresw z wykorzystaniem XHTML / CSS
9.5. Podzia wynikw na strony
9.6. Optymalizacja dziaania serwera
poprzez buforowanie generowanych stron WWW
9.7. Lokalizacja strony WWW dla rnych jzykw
9.8. czne wykorzystanie technologii Ajax i PHP
do tworzenia interaktywnych stron WWW
9.8. czne wykorzystanie technologii Ajax

10

212
213
215
216
218
221
223
224
226

Sprawdzanie poprawnoci danych i ich standaryzacja ........... 229


11.1. Numery telefonw
11.2. Kody pocztowe
11.3. Numery ubezpieczenia spoecznego

231
233
234

PHP 5 w praktyce
11.4. Liczby
11.5. Numery kart kredytowych
11.6. Daty
11.7. Adresy e-mail
11.8. Adresy URL

12

Sesje i ledzenie uytkownikw ............................................... 243


12.1. Uywanie plikw cookies do przechowywania danych
12.2. Zapisywanie danych uytkownika za pomoc sesji
12.3. Dostosowywanie wygldu strony
12.4. Tworzenie bibliotek przechowujcych dane
o dziaaniach podjtych w serwisie przez uytkownika
12.5. Tworzenie prostego koszyka na zakupy
12.6. Przekazywanie danych z sesji midzy serwerami
12.7. Analizowanie danych o przegldarkach na podstawie pliku dziennika

13

267
268
269
272
273
278
279

284
286
287
289
290
292
293
294
296

Inne metody przechowywania danych .................................... 301


15.1. Tworzenie i odczyt plikw CSV
15.2. Zapisywanie danych w plikach tekstowych
15.3. Dostp do baz danych DBM i ich aktualizacja
15.4. Zapisywanie danych za porednictwem operacji serializacji i deserializacji
15.5. Automatyczne tworzenie i aktualizacja wczanych plikw PHP

16

253
255
258
260

Relacyjne bazy danych ............................................................. 283


14.1. Komunikacja z baz danych MySQL
14.2. Komunikacja z baz danych Oracle
14.3. Komunikacja z baz danych PostgreSQL
14.4. Komunikacja z baz danych Sybase
14.5. Komunikacja z baz danych Microsoft SQL Server
14.6. Komunikacja z baz danych SQLite
14.7. Komunikacja z bazami danych za porednictwem ODBC
14.8. Wykorzystanie warstwy abstrakcji do komunikacji z bazami danych (PDO)
14.9. Implementacja blogu z wykorzystaniem bazy danych SQLite

15

244
247
250

Usugi sieciowe i inne protokoy .............................................. 265


13.1. Przesyanie da HTTP metod POST
z wykorzystaniem rozszerzenia cURL
13.2. Komunikacja z serwerami LDAP
13.3. Wykorzystanie usug sieciowych za pomoc protokou SOAP
13.4. Nawizywanie poczenia z serwerem FTP
13.5. Wykorzystanie PHP do utworzenia klienta FTP
13.6. Wykorzystanie gniazd do nawizywania pocze
z serwerami internetowymi
13.7. Utworzenie prostego serwera WWW

14

235
236
238
239
240

302
303
305
307
308

Poczta elektroniczna ................................................................ 311


16.1. Wysyanie wiadomoci e-mail (tekst, HTML, format podwjny,
wstawiane ilustracje, zaczniki)
16.2. Sprawdzanie, czy istnieje konto pocztowe
16.3. Utworzenie automatu do masowego wysyania wiadomoci e-mail

312
321
323

Spis treci
16.4. Implementacja prostego programu obsugi list mailingowych
16.5. Ochrona adresw e-mail przed automatami pobierajcymi je
w celu przesyania spamu
16.6. Utworzenie procesu Watchdog wysyajcego wiadomo e-mail
w przypadku modyfikacji strony

17

20

III
A

330

335
336
338
341
343
344

350
354
356
357
364

Zgaszanie bdw i debugowanie ........................................... 367


19.1. Definicja niestandardowej procedury obsugi bdw
19.2. Wykorzystanie wyjtkw do obsugi bdw

369
371

19.3. Pomiar czasu wykonania skryptu

374

19.4. Wykorzystanie funkcji zamykajcych w celu obsugi awarii skryptw

376

19.5. Generowanie szczegowego ladu w celu zgaszania bdw

378

Uwierzytelnianie uytkownikw i szyfrowanie ....................... 381


20.1. Generowanie losowych hase

382

20.2. Wykorzystanie szyfrowania do ochrony danych

383

20.3. Zastosowanie technik CAPTCHA


do wykrywania rzeczywistych uytkownikw

385

20.4. Uwierzytelnianie uytkownikw

388

Dodatki ................................................................... 391


Migracja do PHP 5 ................................................................... 393
Model obiektowy

329

Grafika ...................................................................................... 347


18.1. Generowanie obrazw poczonych z tekstem
18.2. Wskazwki i sztuczki dotyczce rysowania
18.3. Wykorzystanie przezroczystego ta w ilustracjach
18.4. Utworzenie biblioteki obsugi wykresw
18.5. Automatyczne tworzenie galerii fotografii
z plikw z aparatu cyfrowego (dane Exif)

19

327

XML .......................................................................................... 333


17.1. Przetwarzanie dokumentu XML w celu odczytania danych
17.2. Wyszukiwanie danych w dokumencie XML z wykorzystaniem jzyka XPath
17.3. Weryfikacja dokumentw XML
17.4. Przeksztacanie dokumentw XML na XHTML z wykorzystaniem XSLT
17.5. Tworzenie dokumentw RSS
17.6. Tworzenie skryptu do wywietlania kanaw RSS w witrynach WWW

18

394

Modu obsugi bazy danych MySQL

394

CLI i CGI

395

Uwzgldnianie wielkoci liter w nazwach klas, metod i funkcji

395

Funkcje zwracajce wartoci przez referencj

396

SPL ............................................................................................ 397


Podstawowy interfejs

397

Tworzenie wasnych iteratorw

400

Rozszerzanie iteratorw i iteratory poczone

402

PHP 5 w praktyce

Zagadnienia bardziej zoone

404

Iteratory rekurencyjne
Definiowanie klasy umoliwiajcej rekurencyjn iteracj

404
405

Czste komunikaty o bdach .................................................. 407


Poziomy bdw
Wynik dziaania skryptu: pusta strona

407
408

Skorowidz ................................................................................. 415

PLIKACJE, KTRE WYKONUJ operacje zwizane z czasem, s szczeglnie czsto uywane w internecie. Godziny przesania formularza, dane wprowadzane przez uytkownikw, na
przykad data urodzenia, a take aktualizacja i usuwanie stron w przypadku, gdy s przestarzae to zaledwie kilka przykadw. Chocia obliczenia dotyczce dat w PHP mog
wydawa si skomplikowane, w rzeczywistoci s do proste.
W tym rozdziale gwnie opisano podstawowe funkcje przetwarzania dat i godzin.
Funkcje te zapewniaj wykonywanie wszystkich podstawowych operacji na datach i godzinach, takie jak pobieranie, formatowanie i konwersja dat. W PHP jest dostpne take
inne rozszerzenie Calendar, ktre suy do konwersji midzy rnymi systemami kalendarzy, na przykad midzy kalendarzem gregoriaskim i ydowskim. S to na przykad
funkcje cal_to_jd() i cal_from_jd() opisane w punkcie Szybkie wskazwki.
Zagadnienie planowania zada na przykad tworzenie harmonogramu spotka i zdarze wykracza poza zakres tego rozdziau. Wasnoci te s dostpne dziki rozszerzeniu
MCAL. Wicej informacji na ten temat mona znale pod adresem http://php.net/mcal.
Aby w peni wykorzysta moliwoci przetwarzania dat i godzin w PHP, trzeba wiedzie,
w jaki sposb w rodowisku PHP mierzy si czas. Interpreter PHP odlicza czas w postaci
liczby sekund, jakie upyny od pnocy 1 stycznia 1970 roku tzw. epoki Unix. W zwizku z tym kady moment w czasie jest zapamitywany w postaci prostej liczby typu integer.
Czas zapisany w ten sposb to tzw. znacznik czasu (ang. time stamp). Jak si przekonamy, wiele funkcji albo zwraca znacznik czasu albo wykorzystuje go jako argument. Naley
jednak zachowa ostrono w przypadku czytania dokumentacji lub komentarzy w kodzie. W zalenoci od kontekstu, termin znacznik czasu moe by uywany take w sensie
bardziej oglnym: jako czas, kiedy co si wydarzyo. Gdy mowa na przykad o plikach
dziennikw serwera WWW, terminem znacznik czasu okrela si czas, w ktrym zarejestrowano zdarzenie.

Szybkie wskazwki
Odczytanie znacznika czasu dla biecej godziny:
$timestamp = time();

Funkcja nie pobiera parametrw. Zwraca biecy czas, tzn. czas, w ktrym wywoano funkcj time().
Pena dokumentacja: http://php.net/time.

64

Rozdzia 3. Godziny i daty


Odczytanie biecego znacznika czasu z dokadnoci do mikrosekund:
$result = microtime($format);

Funkcja zwraca uniksowy znacznik czasu z dokadnoci do mikrosekund. Jeli


argument $format ma warto false (domylnie), funkcja zwraca czas w postaci
cigu znakw "sekundy mikrosekundy", gdzie sekundy maj t sam warto,
co zwrcona przez funkcj time(), a mikrosekundy oznaczaj uamek sekund.
Jeli argument $format ma warto true, funkcja zwraca czas w postaci liczby
zmiennoprzecinkowej.
Pena dokumentacja: http://php.net/microtime.
Utworzenie znacznika czasu dla okrelonej daty i godziny:
$timestamp = mktime($godzina, $minuta, $sekunda, $miesiac, $dzien,
$rok);

Zwraca znacznik czasu odpowiadajcy okrelonej dacie i godzinie.


Pena dokumentacja: http://php.net/mktime.
Formatowanie okrelonego czasu:
$string = date($format, $timestamp);

Zwraca znakow reprezentacj podanego znacznika czasu z wykorzystaniem zdefiniowanego formatu. Jeli nie okreli si argumentu $timestamp, funkcja wykorzystuje biecy czas.
Pena dokumentacja: http://php.net/date.
Sformatowanie daty i godziny. Dostosowanie wynikw do formatu Greenwich
Mean Time (GMT):
$string = gmdate ($format, $timestamp);

Funkcja rwnowana do date(). Jednak czas jest przedstawiany jako reprezentacja GMT podanej godziny.
Pena dokumentacja: http://php.net/gmdate.
Odczytanie rnych informacji na temat podanego czasu:
$time_array = getdate($timestamp);

Zwraca tablic rnych wartoci dotyczcych podanego znacznika czasu. Indeksami tablicy s seconds, minutes, hours, mday, wday, mon, year, yday, weekday
i month.
Pena dokumentacja: http://php.net/getdate.
Odczytywanie i ustawianie strefy czasowej uywanej
w funkcjach czasowych:
$string = date_default_timezone_get();
date_default_timezone_set($string);

3.1. Obliczanie rnicy czasu midzy dwiema datami

65

Powysza para funkcji suy do odczytywania (ustawiania) identyfikatora strefy


czasowej.
Pena dokumentacja: http://php.net/date_default_timezone_set
i http://php.net/date_default_timezone_get.
Zamiana czasu z postaci tekstowej w jzyku angielskim na znacznik czasu:
$timestamp = strtotime($english_time);

Funkcja pobiera tekstowy opis daty i godziny w jzyku angielskim i zwraca odpowiadajcy jej znacznik czasu.
Pena dokumentacja: http://php.net/strtotime.
Sprawdzenie poprawnoci daty wedug kalendarza gregoriaskiego:
$isvalid = checkdate($month, $day, $year);

Zwraca true, jeli okrelon dat mona znale w kalendarzu gregoriaskim.


Przydaje si do sprawdzania, czy czas wprowadzany przez uytkownika bd czas
wyliczony s poprawne.
Pena dokumentacja: http://php.net/checkdate.
Godzina wschodu i zachodu soca we wskazanej lokalizacji:
$time_rise = date_sunrise($day_ts, SUNFUNCS_RET_TIMESTAMP, $latitude,
$longitude);
$time_set = date_sunset($day_ts, SUNFUNCS_RET_TIMESTAMP, $latitude,
$longitude);

Powysza para funkcji zwraca godziny wschodu i zachodu soca dla wskazanej
lokalizacji. Uycie staej SUNFUNCS_RET_TIMESTAMP powoduje, e funkcja zwraca godzin w postaci znacznika czasu.
Pena dokumentacja: http://php.net/date.sunrise i
http://php.net/date.sunset.
Konwersja daty midzy rnymi kalendarzami a kalendarzem juliaskim:
$jday count = cal_to_jd($calendar, $rnonth, $day, $year);
$date_array = cal_from_jd($jday_count, $calendar);

Powysza para funkcji umoliwia konwersj daty wyraonej w okrelonym systemie


kalendarza na kalendarz juliaski i odwrotnie. Funkcje obsuguj nastpujce typy kalendarzy: CAL_GREGORIAN, CAL_JULIAN, CAL_JEWISH oraz CAL_FRENCH.
Pena dokumentacja: http://php.net/cal_to_jd i http://php.net/cal_from_jd.

66

Rozdzia 3. Godziny i daty

W programach bardzo czsto trzeba znale liczb tygodni, dni, a nawet sekund dzielcych dwie daty. W niektrych systemach baz danych s dostpne procedury umoliwiajce porwnywanie dat i wykonywanie dziaa arytmetycznych na datach. Jednak w PHP
takie operacje trzeba wykonywa samodzielnie.
Jak powiedziano we wprowadzeniu, w PHP daty s reprezentowane w formacie epoki
UNIX. Oznacza to, e s zapisywane w postaci liczby sekund, jaka upyna od pnocy
1 stycznia 1970 roku. Dziki temu mona z atwoci obliczy liczb sekund, jaka upyna midzy dwoma punktami w czasie. Wystarczy odj od siebie dwa znaczniki czasu.
Wykonanie popularniejszego dziaania obliczenie liczby dni wymaga jednak dodatkowych czynnoci.
Podczas dziaa arytmetycznych na datach trzeba pamita o latach przestpnych,
miesicach o rnej liczbie dni, miesicach obejmujcych pi tygodni, latach z 53. tygodniem, zmianie czasu z letniego na zimowy i innych tego rodzaju zagadnieniach. W przeciwnym razie algorytm bdzie obsugiwa jedynie wikszo przypadkw nie wszystkie.
Rozpocznijmy od omwienia czstego problemu wyznaczenia liczby dni dzielcych
dwie daty. Zwrmy uwag, e w kodzie z listingu 3.1.1 do utworzenia znacznikw czasu
wykorzystano funkcj strtotime(). Funkcja umoliwia pobranie wielu rnych postaci
tekstowej reprezentacji daty i przeksztacenie jej na znacznik czasu.
Uwaga
Poczwszy od wydania PHP w wersji 5.1, przed wywoaniem jakiejkolwiek funkcji operacji na datach trzeba wywoa funkcj date_default_timezone_set(),
ktra suy do okrelenia strefy czasowej, dla jakiej maj by wykonane obliczenia. W innym przypadku generowany jest bd trybu STRICT, poniewa interpreter prbuje odgadn stref czasow. Wicej informacji na temat stref
czasowych mona znale w podrozdziale 3.4, Obsuga stref czasowych w dalszej czci tego rozdziau. Jeli tworzony kod ma dziaa zarwno w rodowisku
PHP 5.0, jak i PHP 5.1, powstaje problem, poniewa w PHP 5.0 funkcja date_
default_Timezone_set() jest niedostpna. Z tego wzgldu mona uy funkcji
function_exists() i za jej pomoc sprawdzi, czy funkcja date_default_
Timezone_set() istnieje. Jeli tak, mona j wywoa. Alternatywnie mona
ustawi domyln stref czasow w pliku konfiguracyjnym na serwerze PHP 5.1.
W takiej sytuacji nie trzeba si martwi ustawieniami strefy czasowej.
Listing 3.1.1. Liczba dni midzy dwoma datami
<?php
// Ustawienie domylnej strefy czasowej na Europe/Warsaw.
date_default_timezone_set('Europe/Warsaw');
// Funkcja zwraca liczb dni midzy dwoma przekazanymi datami.
function count_days($a, $b) {
// Najpierw naley przeksztaci daty na czci skadowe:
$a_dt = getdate($a);
$b_dt = getdate($b);

3.1. Obliczanie rnicy czasu midzy dwiema datami

67

// Odtworzenie znacznikw czasu z wykorzystaniem poudnia kadego dnia.


// Dokadny czas nie ma znaczenia, ale musi to by ta sama godzina w kadym dniu.
$a_new = mktime(12, 0, 0, $a_dt['mon'], $a_dt['mday'], $a_dt['year']);
$b_new = mktime(12, 0, 0, $b_dt['mon'], $b_dt['mday'], $b_dt['year']);
// Odjcie dwch liczb i podzielenie przez liczb sekund przypadajc na dzie.
// Zaokrglenie wyniku, poniewa przekroczenie granicy zmiany czasu z letniego na zimowy (lub odwrotnie)
// spowoduje rnic w czasie o godzin lub dwie.
return round(abs($a_new - $b_new) / 86400);
}
// Przygotowanie kilku dat
$date1 = strtotime('12/3/1973 8:13am');
$date2 = strtotime('1/15/1974 10:15pm');
$date3 = strtotime('2/14/2005 1:32pm');
// Obliczenie rnic. Powinnimy uzyska wyniki 43 i 11353
echo "<p>Upyno ", count_days($date1, $date2), " dni.</p>";
echo "<p>Upyno ", count_days($date2, $date3), " dni.</p>";
?>

Problem z pozoru wydaje si prosty: odjcie dwch liczb i podzielenie przez liczb sekund
przypadajc na dzie. To prawda i w ten sposb uzyskamy prawidowe wyniki, jeli interesuje nas cile matematyczna definicja dnia. Wemy jednak za przykad dwie daty: 11
listopada, godz. 23.00 i 12 listopada, godz. 1.00. Zgodnie z oglnie przyjt interpretacj
dat, rni je jeden dzie. Jednak jeli odejmiemy od siebie znaczniki czasowe odpowiadajce obu datom, okae si, e wymienione daty dziel tylko dwie godziny.
Rozwizanie polega na sprowadzeniu dat do wsplnej godziny na przykad poudnia. Teraz mona odj od siebie znaczniki czasu. Aby kolejno dat nie miaa znaczenia,
mona posuy si wartoci bezwzgldn. Aby otrzyma liczb dni, uzyskany wynik
naley podzieli przez liczb sekund przypadajc na dzie (86 400). Byoby to niemal
doskonae rozwizanie, gdyby nie to, e nie uwzgldnia zmiany czasu z letniego na zimowy bd odwrotnie. Jeli midzy datami, dla ktrych wykonujemy obliczenia, nastpia zmiana czasu, dokadny wynik moe si rni o jedn lub dwie godziny. Aby pozby si bdu spowodowanego zmian czasu, wystarczy zaokrgli wynik do najbliszej
liczby cakowitej.
Podobn strategi mona rwnie wykorzysta do obliczenia liczby tygodni, miesicy
i lat dzielcych dwie daty. Spotyka si jednak dwa rne punkty widzenia na te wiksze
jednostki czasu. Na przykad mwimy czasami, e co miao miejsce tydzie temu, majc
na myli siedem dni. W takim przypadku, by obliczy liczb tygodni midzy dwiema datami, wystarczy skorzysta z utworzonej przed chwil funkcji count_days() i podzieli
uzyskany wynik przez 7. Innym razem pod tym samym pojciem rozumie si zdarzenie,
ktre miao miejsce w ubiegym tygodniu. W takiej sytuacji naley skorzysta z podobnego
mechanizmu, jak w przypadku obliczania rnicy w liczbie dni, kiedy sprowadzilimy oba
dni do tej samej godziny. Mona sprowadzi daty do niedzieli danego tygodnia i wtedy
wykona obliczenia. Przykad podobnej operacji zamieszczono w podrozdziale 3.6 Wyznaczanie liczby dni roboczych, w dalszej czci tego rozdziau.

68

Rozdzia 3. Godziny i daty

Czasami trzeba sprawdzi, jaki jest ostatni dzie okrelonego miesica. Do sprawdzenia
dnia mona wykorzysta tabel przegldow (cho w dalszym cigu trzeba uwzgldni lata przestpne dla lutego), ale czsto potrzebny jest znacznik czasu szukanego dnia. Na
szczcie dziki funkcji mktime() uzyskanie znacznika czasu nie jest trudne. Funkcja pobiera argumenty okrelajce kolejno godzin, minut, sekund, miesic, dzie i rok. Zalet funkcji mktime() jest automatyczna prawidowa obsuga argumentw ujemnych lub
zerowych.
Tak wic, jeli wprowadzimy argument dla miesica rwny 3 (marzec), a dla dnia
rwny 1, uzyskamy prawidowy znacznik czasu dla 1 marca. W przypadku wprowadzenia parametru dnia rwnego 0 otrzymamy znacznik czasu poprzedniego dnia, czyli ostatni dzie lutego. Na listingu 3.2.1 pokazano, jak mona wykorzysta funkcj mktime() do
obliczenia znacznikw czasu ostatniego dnia miesica.
Listing 3.2.1. Wyznaczanie znacznika czasu ostatniego dnia miesica
<?php
// Ustawienie domylnej strefy czasowej na Europe/Warsaw.
date_default_timezone_set('Europe/Warsaw');
// Funkcja zwraca znacznik czasu ostatniego dnia miesica podanego roku
function last_day($month, $year) {
// Wykorzystanie funkcji mktime do utworzenia znacznika czasu dla nastpnego miesica, ale o jeden dzien
// wczeniej.
// Ustawienie godziny na blisk pnocy, tak by
// uzyskany znacznik mona byo wykorzysta jako granic miesica.
return mktime(23, 59, 59, $month + 1, 0, $year);
}
// Wyznaczenie znacznika czasu dla ostatniego dnia lutego 2006 roku.
$stamp = last_day(2, 2006);
// Wywietlenie wynikw: 28
echo '<p>Ostatni dzie lutego 2006 roku przypada na: ', date('d', $stamp) ,'</p>';
?>

Czsto wykonywanym zadaniem, dla ktrego w PHP nie ma wbudowanej funkcji, jest
sprawdzenie, czy okrelony rok jest przestpny. Cho mona to wyliczy z prostego wzoru, wiele osb zapomina o niektrych jego elementach. Rok jest przestpny tylko wtedy,
jeli dzieli si przez 4, ale nie dzieli si przez 100, chyba e dzieli si przez 400. Funkcj
sprawdzajc, czy okrelony rok jest przestpny, zaimplementowano na listingu 3.3.1.

3.4. Obsuga stref czasowych

69

Listing 3.3.1. Sprawdzanie, czy rok jest przestpny


<?php
// Funkcja sprawdza, czy okrelony rok jest przestpny, czy nie:
function is_leap_year($y) {
// Jeli rok dzieli si przez 4, ale nie dzieli si przez 100 lub dzieli si przez 400:
return ((($y % 4) == 0) && ((($y % 100) != 0) || (($y % 400) == 0)));
}
// Sprawdzenie, czy lata z podanego zakresu s przestpne:
foreach (range(1999, 2009) as $year) {
// Wywietlenie wynikw:
echo "<p>{$year} = ", is_leap_year($year) ? 'Rok przestpny' : 'rok zwyky',
'</p>';
}
?>

Obsuga stref czasowych moe sprawia wiele problemw. Na szczcie w PHP dostpne
s mechanizmy, ktre j uatwiaj. Wiele z nich wykorzystuje wasno rodowiska PHP
w wersji 5.1 polegajc na wykorzystaniu funkcji date_default_timezone_get() w celu
okrelenia strefy czasowej, dla ktrej s wykonywane obliczenia. W tym punkcie omwimy
kilka innych metod. Kada z nich doskonale nadaje si do wykonywania rnych zada.
We wszystkich tych przykadach zaoymy, e bieca strefa czasowa to Europe/Warsaw
(GMT+1:00), a strefa czasowa, dla ktrej wykonujemy obliczenia, to Asia/Tokyo (GMT+9:00).
Aby dowiedzie si, jaki jest odpowiednik okrelonego czasu w innej strefie czasowej,
mona skorzysta z kombinacji funkcji ustawiajcej domyln stref czasow i funkcji
mktime(), ktra umoliwia wygenerowanie czasu w innej strefie czasowej i wywietlenie
go w strefie domylnej (listing 3.4.1).
Listing 3.4.1. Obliczanie czasu w rnych strefach czasowych
<?php
// Sprawdzenie, ktra godzina jest w Warszawie w czasie, gdy w Tokio jest 8 rano.
// Ustawienie domylnej strefy czasowej na czas tokijski.
date_default_timezone_set('Asia/Tokyo');
// Wygenerowanie znacznika czasu w tej strefie czasowej dla 1 stycznia 2000 roku.
$stamp = mktime(8, 0, 0, 1, 1, 2000);
// Ustawienie domylnej strefy czasowej z powrotem na Europe/Warsaw.
date_default_timezone_set('Europe/Warsaw');
// Wywietlenie daty w formacie standardowym (RFC1123). Funkcja wywietli nastpujcy cig:
// Fri, 31 Dec 1999 18:00:00 EST
echo '<p>', date(DATE_RFC1123, $stamp) ,'</p>';
?>

70

Rozdzia 3. Godziny i daty

Take funkcja strtotime() uatwia wykonywanie konwersji midzy strefami czasowymi.


W cigach znakw opisujcych czas mona wprowadzi modyfikatory stref czasowych.
Funkcja strtotime() analizuje skrty stref czasowych, na przykad EST (ang. Eastern
Standard Time). Nie zaleca si jednak ich stosowania, poniewa mog wystpi trudnoci
z ich interpretacj w rnych jzykach. Akceptuje rwnie przesunicie strefy wzgldem
czasu UTC, na przykad "+0100" dla strefy Europe/Warsaw oraz "+0900" dla strefy Asya/
Tokyo. Przykad uycia tych mechanizmw do wykonywania operacji na strefach czasowych pokazano na listingu 3.4.2.
Listing 3.4.2. Wyznaczanie czasu w rnych strefach czasowych z wykorzystaniem funkcji
strtotime()
<?php
// Ustawienie domylnej strefy czasowej na Europe/Warsaw.
date_default_timezone_set('Europe/Warsaw');
// Wygenerowanie znacznika czasu dla 1 stycznia 2000 o 8 rano w Tokio.
// Uycie notacji ze skrtem 'JST' (Japan Standard Time)
$stamp1 = strtotime('1/1/2000 8am JST');
// Uycie modyfikatora okrelajcego relacj wzgldem GMT: +9 godzin w stosunku do czasu UTC.
$stamp2 = strtotime('Jan 1 2000 08:00 +0900');
// Wywietlenie daty w formacie standardowym (RFC1123). Funkcja wywietli nastpujcy cig:
// Fri, 31 Dec 1999 18:00:00 EST
echo '<p>', date(DATE_RFC1123, $stamp1) ,'</p>';
echo '<p>', date(DATE_RFC1123, $stamp2) ,'</p>';
?>

Wykonywanie konwersji w ten sposb moe okaza si kopotliwe. Czsto si zdarza, e


kto przechowuje dane w postaci lokalnego czasu i chce wywietli go w innych strefach
czasowych. Aby to zrobi, wystarczy skorzysta z tabeli konwersji zawierajcej waciwe
przeliczniki. Takie tabele nie uwzgldniaj jednak wielu nietypowych sytuacji, takich jak
zmiana czasu z letniego na zimowy, ktra w rnych miejscach wystpuje w rnym czasie. Na listingu 3.4.3 do zmiany stref czasowych wykorzystano tabel przegldow.
Listing 3.4.3. Wyznaczanie czasu w rnych strefach czasowych z wykorzystaniem tabeli
przegldowej
<?php
// Ustawienie domylnej strefy czasowej na Europe/Warsaw.
date_default_timezone_set('Europe/Warsaw');
// Utworzenie tabeli przegldowej wzgldem strefy Europe/Warsaw.
$locals = array('Seattle' => -7, 'Londyn' => 1, 'Tokyo' => 10);
// Wygenerowanie daty i godziny we wskazanych lokalizacjach na podstawie czasu lokalnego.
$now = time();
foreach ($locals as $place => $offset) {
// Wywietlenie daty i godziny po konwersji.
echo "<p>Czas w miejscowoci {$place} to: ", date('m/d/Y H:i',
$now + 3600 * $offset) ,'</p>';
}
?>

3.5. Obsuga znacznikw czasu w bazach danych lub plikach

71

Znaczniki czasu czsto zapisuje si w plikach. Ich format jest poza nasz kontrol, jednak
trzeba je odczyta w rodowisku PHP i zinterpretowa jako daty. Daty mona przetwarza za pomoc wyrae regularnych, jednak w wielu przypadkach format czasu powinien by zgodny z funkcj strtotime(). TakZdarza si tak bardzo czsto, poniewa funkcja
strtotime() obsuguje wiele formatw czasu. Na listingu 3.5.1 zamieszczono przykad je
uycia do przetwarzania daty odczytanej z pliku dziennika serwera Apache:
127.0.0.1 - - [02/Nov/2005:22:04:41 -0500] "GET / HTTP/1.1" 200 41228

Listing 3.5.1. Przetwarzanie wierszy z pliku dziennika serwera Apache za pomoc funkcji
strtotime()
<?php
// Ustawienie domylnej strefy czasowej na Europe/Warsaw.
date_default_timezone_set('Europe/Warsaw');
// Symulacja odczytu wiersza z pliku dziennika serwera Apache.
$logline =
'127.0.0.1 - - [02/Nov/2005:22:04:41 -0500] "GET / HTTP/1.1" 200 41228';
// Odczytanie fragmentu dotyczcego daty za pomoc wyraenia regularnego.
$matches = array();
preg_match('/\[(.*?)\]/', $logline, $matches);
// Pobranie daty i jej konwersja.
$timestamp = strtotime($matches[1]);
// Ponowne wywietlenie daty w celu potwierdzenia jej prawidowego odczytania:
echo date(DATE_RFC1123, $timestamp);
?>

Podobnym problemem, z jakim czsto spotykaj si programici, jest zapisywanie dat w bazach danych i odczytywanie ich z baz danych. Kada baza danych charakteryzuje si wasn
preferowan metod prezentacji dat.
Na szczcie wikszo formatw dat mona przetworzy za pomoc funkcji strtotime()
(w bazach danych wystpuj rwnie specyficzne komendy pozwalajce na zwracanie dat
w formatach wskazanych przez uytkownikw). Format dat rni si w zalenoci od baz
danych. W zwizku z tym szczegowych informacji dotyczcych formatu baz danych
naley szuka w dokumentacji okrelonej bazy danych.
Prawdziwy problem wystpuje podczas zapisywania dat ze rodowiska PHP do bazy
danych. W kadej bazie danych obowizuje specyficzny format dat. Naley jednak zwrci uwag, e niemal we wszystkich typach baz danych domylny format daty mona
zmieni na serwerze. Z tego wzgldu bezpieczniej korzysta z wasnych funkcji konwersji
cigw znakw na daty. W ten sposb zyskuje si pewno, e za kadym razem zapiszemy do bazy danych prawidow warto daty. Poniej wyszczeglniono kilka przykadw
instrukcji date() w PHP, ktre formatuj cigi znakw na domylny format dat w rnych typach baz danych:

72

Rozdzia 3. Godziny i daty


t Oracle date('d-M-Y H:i', $timestamp);
t MySQL date('Y-m-d H:i', $timestamp);
t Sybase and SQL Server date('m/d/Y H:i', $timestamp).

Cho w podrozdziale 3.1 Obliczanie rnicy czasu midzy dwiema datami zaprezentowano sposb obliczania liczby dni midzy dwiema datami, skrypt ten na niewiele si zda
w wiecie biznesu, w ktrym wane s dni robocze w wikszoci miejsc na wiecie s to
dni od poniedziaku do pitku. Dni robocze czasami uwzgldniaj wita, a jeli ich nie
uwzgldniaj, szczegowe zasady s rne dla rnych firm. Z tego wzgldu skoncentrujemy si na podstawowej definicji.
Jednym ze sposobw obliczania liczby dni roboczych midzy dwiema datami jest
przetwarzanie w ptli wszystkich dni midzy datami i sprawdzanie, czy okrelony dzie
nie naley do weekendu. Metoda ta jest jednak nieefektywna. Istniej lepsze rozwizania,
na przykad to, ktre zaimplementowano w listingu 3.6.1. Wiemy, e tydzie roboczy ma
pi dni. W celu obliczenia liczby dni roboczych wystarczy obliczy liczb penych tygodni
midzy dwiema datami i pomnoy przez 5. Nastpnie naley doda liczb dni w pierwszym i ostatnim tygodniu.
Listing 3.6.1. Obliczanie liczby dni midzy dwiema datami
<?php
// Ustawienie domylnej strefy czasowej na Europe/Warsaw.
date_default_timezone_set('Europe/Warsaw');
// Funkcja zwracajca liczb dni roboczych midzy dwiema datami.
function count_business_days($a, $b) {
// Sortowanie dat. Trzeba si dowiedzie, ktra z dat przypada wczeniej.
if ($a < $b) {
$first = $a;
$second = $b;
} else {
$first = $b;
$second = $a;
}
// Rozoenie znacznikw czasu na skadowe.
$f = getdate($first);
$s = getdate($second);
// Obliczenie liczby pozostaych dni roboczych w pocztkowym tygodniu.
// W tym celu naley odj numer dnia w tygodniu od piciu.
$f_days = 5 - $f['wday'];
// W przypadku soboty lub niedzieli uzyskamy wynik -1 lub 5, ale wtedy przyjmujemy do oblicze warto 0.
if (($f_days == 5) || ($f_days < 0)) { $f_days = 0; }
// Wykonanie podobnej operacji dla kocowego tygodnia, z t rnic, e liczymy od
// pocztku tygodnia. Trzeba si tylko upewni, e sobota liczy si jako 5.
$s_days = ($s['wday'] > 5) ? 5 : $s['wday'];
// Obliczenia znacznika czasu dla poudnia w niedziel pocztkowego tygodnia.

3.6. Wyznaczanie liczby dni roboczych

73

$f_sunday = mktime(12, 0, 0, $f['mon'],


$f['mday'] + ((7 - $f['wday']) % 7), $f['year']);
// Obliczenia znacznika czasu dla poudnia w niedziel poprzedzajc dat kocow.
$s_sunday = mktime(12, 0, 0, $s['mon'],
$s['mday'] - $s['wday'], $s['year']);
// Obliczenie liczby penych tygodni midzy dwiema datami poprzez odjcie od siebie
// znacznikw czasu i podzielenie przez liczb sekund w tygodniu. Uzyskany wynik naley
// zaokrgli po to, by zawsze uzyska liczb cakowit. W innym przypadku z powodu zmiany czasu
// z letniego
// na zimowy (lub odwrotnie) uzyskany wynik moe by niedokadny.
$weeks = round(($s_sunday - $f_sunday) / (3600*24*7));
// Zwrcenie liczby dni poprzez pomnoenie liczby tygodni przez 5 i dodanie
// dni roboczych z pocztkowego i kocowego tygodnia.
return ($weeks * 5) + $f_days + $s_days;
}
// Kilka przykadw:
$date1 = strtotime('12/3/1973 8:13am');
$date2 = strtotime('1/15/1974 10:15pm');
$date3 = strtotime('2/14/2005 1:32pm');
// Obliczenie liczby dni roboczych. Prawidowe wyniki to: 31 & 8109.
echo "<p>Daty dzieli ", count_business_days($date1, $date2), " dni roboczych.</p>";
echo "<p>Daty dzieli ", count_business_days($date2, $date3), " dni roboczych.</p>";
?>

Jak wida z powyszego listingu, najpierw wyznaczono liczb dni midzy niedzielami
przypadajcymi po dacie pocztkowej i przed dat kocow. Na tej podstawie obliczono
liczb penych tygodni midzy dwiema datami. Aby to zrobi, wystarczy podzieli liczb
sekund, jakie dziel obie niedziele, przez liczb sekund w tygodniu. Wynik naley zaokrgli do najbliszej liczby cakowitej. Zaokrglenie jest potrzebne, jeli midzy dat pocztkow a kocow wystpia zmiana czasu z zimowego na letni lub odwrotnie. Jei nie
przeprowadzono by operacji zaokrglania, wystpiby bd godziny lub dwch.
Na koniec naley doda dni robocze z pierwszego i ostatniego tygodnia i otrzymujemy
wynik. Wzr jest poprawny nawet wtedy, gdy oba dni nale do tego samego tygodnia.
Co prawda, najpierw s liczone dni pozostae do koca tygodnia i dni od pocztku tygodnia, co powoduje liczenie niektrych dni kilka razy. W zwizku z tym wynik zawsze jest
wikszy o 5 dni. Jednak z oblicze liczby penych tygodni uzyskuje si -1. W zwizku z tym
odejmujemy nadmiarowe 5 dni, uzyskujc poprawny wynik.
Do zaimplementowanego algorytmu mona wprowadzi modyfikacj polegajc na
uwzgldnieniu dni witecznych. Naleaoby za pomoc odpowiedniego algorytmu obliczy liczb dni witecznych midzy podanymi datami, a nastpnie odj j od kocowego
wyniku.

74

Rozdzia 3. Godziny i daty

Daty s czeci naszego ycia, a z nimi nierozerwalnie wi si kalendarze. Ludzie ceni


sobie moliwo patrzenia na kalendarz, ktry umoliwia wizualn ocen relacji midzy
okrelonymi datami. Z tego powodu kalendarz jest dostpny w wielu aplikacjach internetowych. Funkcja zamieszczona na listingu 3.7.1 pobiera parametry w postaci miesica i roku
i wywietla kalendarz w formacie prostej tabeli HTML.
Zamieszczony algorytm mona atwo zmodyfikowa w celu uwzgldnienia specyficznych wymaga. W obecnej postaci wprowadzono jedn dyrektyw konfiguracyjn umoliwiajc wskazanie numeru dnia, od ktrego rozpocz tabel. Jej domylna warto wynosi 0
(niedziela).
Listing 3.7.1. Generowanie kalendarza w formacie HTML
<?php
// Funkcja wywietla kalendarz w formacie HTML dla wskazanego miesica i roku.
function print_calendar($month, $year, $weekdaytostart = 0) {
// O miesicu, dla ktrego generujemy kalendarz, potrzebne s pewne informacje, na przykad znacznik
// czasu ostatniego dnia.
$last = idate('d', last_day($month, $year));
// Potrzebna jest rwnie informacja o tym, w jaki dzie tygodnia przypada pierwszy dzie miesica.
// Nazw miesica uzyskamy automatycznie.
$firstdaystamp = mktime(0, 0, 0, $month, 1, $year);
$firstwday = idate('w', $firstdaystamp);
$name = date('F', $firstdaystamp);
// Aby uatwi realizacj funkcji 'rozpoczcie od dowolnego dnia tygodnia', potrzebna jest
// tablica numerw dni tygodnia w uywanym porzdku drukowania.
$weekorder = array();
for ($wo = $weekdaytostart; $wo < $weekdaytostart + 7; $wo++) {
$weekorder[] = $wo % 7;
}
// Pocztek tabeli HTML.
echo "<table><tr><th colspan=\"7\">{$name} {$year}</th></tr>\n";
// Wywietlenie wiersza z nazwami dni.
// Wykorzystanie systemu do uzyskania nazw dni tygodnia.
echo '<tr>';
// Przetwarzanie w ptli penego tygodnia poczwszy od dnia numer 1.
foreach ($weekorder as $w) {
$dayname = date('D',
mktime(0, 0, 0, $month, 1 - $firstwday + $w, $year));
echo "<th>{$dayname}</th>";
}
echo "</tr>\n";
// Zainicjowanie licznikw i wykonanie oblicze.
$onday = 0;
$started = false;
// Ptla do ostatniego dnia miesica.

3.7. Generowanie kalendarza dla okrelonego miesica

75

while ($onday <= $last) {


// Rozpoczcie nastpnego wiersza w tabeli.
echo '<tr>';
// Ptla od 0 do 6 dla dni tygodnia, ale w wybranym porzdku;
// do tego celu wykorzystano operator mod.
foreach ($weekorder as $d) {
// Jeli jeszcze nie rozpoczto:
if (!($started)) {
// Czy dzie biecy odpowiada pierwszemu dniu tygodnia, od ktrego naley rozpocz?
if ($d == $firstwday) {
// Ustawienie zmiennej wskazujcej na fakt rozpoczcia generowania kalendarza
// i inkrementacja licznika.
$started = true;
$onday++;
}
}
// Wywietlenie pustej komrki, jeli numer dnia wynosi zero lub
// jest wikszy od numeru ostatniego dnia.
if (($onday == 0) || ($onday > $last)) {
echo '<td>&nbsp;</td>';
} else {
// W innym przypadku wywietlenie numeru dnia i inkrementacja licznika.
echo "<td>{$onday}</td>";
$onday++;
}
}
// Zakoczenie wiersza tabeli.
echo "</tr>\n";
}
// Zakonczenie tabeli.
echo '</table>';
}
// Wygenerowanie dyrektyw formatujcych.
echo '<style>table, td, th { border: 1px solid black; }</style>';
// Wygenerowanie kilku przykadowych tabel.
// Listopad 2005 z niedziel jako pierwszym dniem.
print_calendar(11, 2005);
echo '<br />';
// Utworzenie caorocznego kalendarza dla roku 2006 z poniedziakiem jako pierwszym dniem.
foreach(range(1, 12) as $m) {
print_calendar($m, 2006, 1);
echo '<br />';
}
?>

Skrypt rozpoczyna si od pobrania potrzebnych informacji. Do wyznaczenia ostatniego


dnia miesica wykorzystano funkcj last_day() utworzon na listingu 3.2.1. Potrzebne
s rwnie informacje na temat dnia tygodnia pierwszego dnia miesica.
Poniewa funkcja ma przetwarza w ptli dni tygodnia, potrzebny jest atwy sposb
wykonywania takiej operacji. Gdyby funkcja zawsze przetwarzaa dni od niedzieli do so-

76

Rozdzia 3. Godziny i daty

boty (0 do 6), nie byoby problemu. Poniewa jednak kalendarz mona rozpocz od dowolnego dnia tygodnia, funkcja musi okreli porzdek przetwarzania ptli. Na przykad,
jeli kalendarz ma si zacz od rody, dni tygodnia powinny by przetwarzane w nastpujcej kolejnoci: 3, 4, 5, 6, 0, 1, 2.
Aby to byo moliwe, funkcja tworzy w ptli tablic zawierajc te liczby, poczwszy
od wskazanego dnia pocztkowego. Ptla wykonuje si 7 razy. W kadej iteracji wykonywana
jest operacja licznika ptli modulo 7.
Do odczytania nazw miesicy i dni tygodnia wykorzystano wbudowane funkcje.
Dziki temu funkcja zawsze drukuje kalendarz we waciwej wersji jzykowej. Pozostaa
jeszcze ptla przetwarzajca tygodnie i wywietlajca kolejne dni.
Trzeba zadba o to, by pierwszy dzie wywietli na waciwej pozycji. Wczeniej wywietlamy puste komrki. Odpowiedni liczb pustych komrek trzeba take wywietli
w ostatnim wierszu.

You might also like