IDZ DO
IDZ DO
PRZYKŁADOWY ROZDZIAŁ
PRZYKŁADOWY ROZDZIAŁ
PHP 5.
SPIS TRE CI
SPIS TRE CI
Nowe możliwo ci
KATALOG KSIĄŻEK
KATALOG KSIĄŻEK
Autor: Adam Trachtenberg
KATALOG ONLINE
KATALOG ONLINE Tłumaczenie: Daniel Kaczmarek
ISBN: 83-7361-714-0
Tytuł oryginału: Upgrading to PHP 5
ZAMÓW DRUKOWANY KATALOG
ZAMÓW DRUKOWANY KATALOG
Format: B5, stron: 320
TWÓJ KOSZYK
TWÓJ KOSZYK
DODAJ DO KOSZYKA
DODAJ DO KOSZYKA
Przewodnik po najnowszej wersji najpopularniejszego języka
do tworzenia dynamicznych witryn WWW
Książka PHP 5. Nowe możliwo ci to opis wszystkich funkcji, które dodano
CENNIK I INFORMACJE
CENNIK I INFORMACJE
do najnowszej wersji języka PHP. Jest adresowana do programistów korzystających
z PHP 4, którzy chcą poznać nowe narzędzia wprowadzone w wersji 5. Każdy nowy
ZAMÓW INFORMACJE
ZAMÓW INFORMACJE
mechanizm jest przedstawiony w postaci przykładu. Książka zawiera także porównanie
O NOWO CIACH
O NOWO CIACH
sposobów realizacji typowych zadań programistycznych za pomocą języków PHP 4
i PHP 5, co ułatwia migrację do nowej wersji.
ZAMÓW CENNIK
ZAMÓW CENNIK
" Zasady programowania obiektowego
" Współpraca z bazą danych MySQL
" rodowisko SQLite
CZYTELNIA
CZYTELNIA
" Mechanizmy obsługi języka XML
" Obsługa błędów za pomocą wyjątków
FRAGMENTY KSIĄŻEK ONLINE
FRAGMENTY KSIĄŻEK ONLINE
" Korzystanie z mechanizmów SOAP
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
Spis t3eści
P3zedmowa .................................................................................................................... 9
1. Wp3owadzenie..............................................................................................................15
Dlaczeg> PHP 5? 16
C> n>weg> w PHP 5? 17
Instal>wanie i k>nfigur>wanie PHP 5 20
2. P3og3amowanie zo3ientowane obiektowo ................................................................ 23
Na czym p>lega pr>gram>wanie z>rient>wane >biekt>w>? 24
Zarządzanie pamięcią 30
Klasy 33
Mechanizmy p>średnie w>bec klas 38
Dziedziczenie 42
Met>dy magiczne 49
3. MySQL........................................................................................................................... 59
Instalacja i k>nfiguracja 61
Interfejs pr>ceduralny 62
Przedtem i teraz: łączenie się z serwerem baz danych 63
Interfejs z>rient>wany >biekt>w> 66
Przedtem i teraz: wyk>nywanie zapytań i p>zyskiwanie danych przy użyciu
instrukcji przyg>t>wywanych 67
Przedtem i teraz: p>dzapytania 75
Transakcje 81
Przedtem i teraz: wyk>nywanie zapytań wiel>kr>tnych 84
Zabezpieczanie p>łączeń przy użyciu pr>t>k>łu SSL 88
Przen>szenie k>du i migr>wanie baz danych 91
4. SQLite........................................................................................................................... 101
P>dstawy SQLite 102
Zmieniające się typy wyników SQLite 105
Interfejs z>rient>wany >biekt>w> 106
Indeksy, >bsługa błędów i tabele przech>wywane w pamięci 108
Transakcje 112
Funkcje zdefini>wane przez użytk>wnika 114
5
5. XML.............................................................................................................................. 121
R>zszerzenia XML w PHP 5 121
Instalacja mechanizmów >bsługi XML i XSLT 124
DOM 125
SimpleXML 132
Przekształcenia między >biektami SimpleXML i DOM 133
Przedtem i teraz: wczytywanie d>kumentów XML d> drzewa 134
Przedtem i teraz: przeszukiwanie d>kumentów XML przy użyciu XPath 139
Wczytywanie k>du XML jak> zdarzeń przy użyciu SAX 146
Przedtem i teraz: tw>rzenie n>wych d>kumentów XML 146
Przedtem i teraz: przekształcanie d>kumentów XML przy użyciu XSLT 150
Weryfikacja zg>dn>ści ze schematem 155
6. Ite3ato3y i standa3dowa biblioteka PHP SPL ..............................................................157
Przedtem i teraz: używanie iterat>rów 159
Implementacja interfejsu iterat>ra 162
Iterat>r wyników zapytań d> bazy MySQL 164
Aańcuch>we łączenie iterat>rów 167
Iterat>r SimpleXML 169
Przedtem i teraz: rekurencyjna iteracja p> katal>gu 170
Implementacja interfejsu RecursiveIterat>r 173
Iter>wanie przez tablice i właściw>ści >biektów 175
Zmiana przebiegu iteracji przez klasy 177
Iterat>ry >raz klasy i interfejsy bibli>teki SPL 180
7. Obsługa błędów i debugowanie ............................................................................... 183
Przedtem i teraz: >bsługa błędów 183
K>rzyści z używania wyjątków 186
Wyjątki system>we 187
Klasa Excepti>n 188
Wyjątki gener>wane przez użytk>wnika 189
Defini>wanie własneg> uchwytu wyjątków 194
Przetwarzanie błędów we własnym uchwycie błędów 195
Funkcje debugujące 196
8. St3umienie, nakładki i filt3y ....................................................................................... 201
Używanie API strumieni 202
Ogólne inf>rmacje > nakładkach 204
Szczegół>we inf>rmacje na temat nakładek 206
Tw>rzenie nakładek 214
Filtr>wanie strumieni 223
Tw>rzenie filtrów 227
6 | Spis t3eści
9. Inne 3ozsze3zenia........................................................................................................231
SOAP 231
Tidy 239
Klasy Reflecti>n 243
10. PHP w akcji ..................................................................................................................251
Definicja schematu bazy danych 252
Klasa Pers>n 253
Klasa addressB>>k 257
Klasa Template 262
Zł>żenie aplikacji w cał>ść 266
Nakładki i kierunki dalszeg> r>zw>ju 270
A Wp3owadzenie do języka XML.................................................................................. 273
B Pozostałe nowe mechanizmy o3az pomniejsze zmiany........................................... 283
C Instalowanie PHP 5 obok PHP 4..................................................................................291
Sko3owidz................................................................................................................... 299
Spis t3eści | 7
ROZDZIAA 2.
P3og3amowanie
zo3ientowane obiektowo
Niniejszy r>zdział zawiera p>dstaw>we inf>rmacje na temat pr>gram>wania z>rient>wane-
g> >biekt>w> (ang. object oriented programming OOP) >raz przedstawia wszystkie mecha-
nizmy z>rient>wane >biekt>w> (ang. object oriented OO) d>stępne w PHP 5. Nie zakłada-
n> w nim żadnej znaj>m>ści technik OOP, dlateg> nic nie st>i na przeszk>dzie, by przeczytał
g> p>czątkujący pr>gramista.
R>zdział ten zawiera jednak również wiele cennych inf>rmacji dla pr>gramistów PHP 4. Oprócz
ud>stępnienia wielu mechanizmów OO, w PHP 5 zmieni>n> również p>dstaw>we mechani-
zmy OO d>stępne d>tychczas w PHP 4. Jeśli w pr>gramach napisanych w PHP 4 nie z>staną
d>k>nane >dp>wiednie uaktualnienia, ich uruch>mienie w PHP 5 m>że skutk>wać >trzyma-
niem nie>czekiwanych wyników i błędów.
N>we mechanizmy zawarte w PHP 5 p>zwalają p>nadt> implement>wać najlepsze r>związa-
nia OOP, które w PHP 4 były p> pr>stu nied>stępne. W r>zdziale tym z>stanie p>kazane, jak
i dlaczeg> p>winn> się zm>dyfik>wać istniejący k>d, by wyk>rzystać wszystkie zalety PHP 5.
Pierwsze wersje języka PHP były wersjami wybitnie pr>ceduralnymi: m>żna był> defini>wać
funkcje, ale nie >biekty. W PHP 3 wpr>wadz>n> >biekty w p>staci szczątk>wej, napisane tak
naprawdę jak> ekstra-d>datek. W 1997 r>ku nikt nie sp>dziewał się takieg> przyr>stu liczby
pr>gramistów PHP, nikt też nie plan>wał pisania w tym języku r>zbud>wanych pr>gramów.
Z teg> względu istniejące >graniczenia nie stan>wiły wówczas wielkieg> pr>blemu.
Przez k>lejne lata PHP wzb>gacał się > k>lejne mechanizmy >biekt>we. Zespół pr>gramistycz-
ny nigdy jednak nie p>kusił się > przepisanie k>du jądra PHP sterująceg> mechanizmami
>biekt>wymi, by >dp>wiedni> >bsługiwać >biekty i klasy. W efekcie p>mim> teg>, że PHP 4
>dznaczał się znacznie lepszą >gólną wydajn>ścią, pisanie w nim zł>ż>nych pr>gramów OO
wciąż był> trudne, a niekiedy nawet niem>żliwe.
W PHP 5 pr>blemy takie >deszły w zap>mnienie dzięki m>duł>wi Zend Engine 2. Jeg> pierw-
sza wersja z>stała napisana dla PHP 4 i >bsługiwała funkcj>naln>ści jądra języka, takie jak
wyznaczanie d>zw>l>nych typów >biektów, a także defini>wała składnię PHP.
Zend Engine 2, stan>wiący jądr> PHP 5, um>żliwia wyk>rzystanie bardziej zaawans>wanych
mechanizmów z>rient>wanych >biekt>w>, zach>wując jedn>cześnie dalek> idącą wsteczną
zg>dn>ść z d>tychczas napisanymi mili>nami skryptów PHP.
23
Jeśli czytelnik pr>gram>wał >biekt>w> jedynie w PHP, m>że na p>czątku przeżyć zask>cze-
nie. Niektóre n>we mechanizmy ułatwiają realizację części zadań, lecz wiele z tych r>związań
nie p>zwala tw>rzyć niczeg> n>weg>. W wielu przypadkach wręcz ograniczają >ne d>stępne
m>żliw>ści.
Parad>ksalnie jednak >graniczenia te p>zwalają tak naprawdę na szybkie pisanie bezpieczneg>
k>du, p>nieważ ułatwiają p>n>wne wyk>rzystanie k>du >raz enkapsulację danych. Klucz>we
k>ncepcje pr>gram>wania z>rient>waneg> >biekt>w> z>staną >bjaśni>ne w dalszych punktach
r>zdziału.
Na czym polega p3og3amowanie
zo3ientowane obiektowo?
Pr>gram>wanie z>rient>wane >biekt>w> stan>wi sp>sób grup>wania funkcji i danych w jeden
bl>k. Bl>k ten >kreślany jest mianem obiektu.
Wielu użytk>wników preferuje OOP, p>nieważ p>zwala >n> na tak zwaną enkapsulację da-
nych. Zawsze w trakcie pisania k>du >kazuje się, że niektóre jeg> części sp>sób przech>-
wywania danych, zakres parametrów p>bieranych przez funkcję, sp>sób >rganizacji bazy
danych nie działają tak d>brze, jak p>winny. Okazują się >ne zbyt w>lne, zbyt niewyg>d-
ne alb> uniem>żliwiają r>zszerzanie ich > n>we m>żliw>ści, a zatem trzeba je wyczyścić.
P>prawianie k>du jest chwalebne, ale tylk> d> czasu, gdy przypadkiem d>jdzie d> uszk>dze-
nia innych części systemu bi>rących udział w p>prawianym pr>cesie. Jeżeli pr>gram z>stanie
zapr>jekt>wany na wys>kim p>zi>mie enkapsulacji, używane przez nieg> struktury danych
i tabele baz>dan>we nie są wyk>rzystywane bezp>średni>. Zamiast teg> definiuje się zestaw
funkcji, które pełnią r>lę p>średników w pr>cesie przepływu wszystkich wyw>łań.
Załóżmy na przykład, że istnieje tabela bazy danych przech>wująca nazwiska i adresy p>czty
elektr>nicznej. Pr>gram, w którym enkapsulacja jest słaba, >dw>łuje się bezp>średni> d> tabeli
za każdym razem, gdy k>nieczne jest >dczytanie adresu p>czt>weg> danej >s>by:
$name = 'Rasmus Lerdorf';
$db = mysql_connect();
$result = mysql_query("SELECT email FROM users
WHERE name LIKE '$name'", $db);
$row = mysql_fetch_assoc($result);
$email = $row['email'];
W pr>gramie > lepszej enkapsulacji użyt>by nat>miast funkcji:
function getEmail($name) {
$db = mysql_connect();
$result = mysql_query("SELECT email FROM users
WHERE name LIKE '$name'", $db);
$row = mysql_fetch_assoc($result);
$email = $row['email'];
return $email;
}
$email = getEmail('Rasmus Lerdorf');
24 | P3og3amowanie zo3ientowane obiektowo
Użycie funkcji getEmail() wiąże się z wiel>ma zaletami, między innymi ze zmniejszeniem il>-
ści k>du, jaki trzeba napisać w celu p>brania adresu p>czt>weg>. P>za tym p>zwala t> na bez-
pieczne wpr>wadzenie zmian w k>nstrukcji bazy danych, p>nieważ trzeba wówczas zmienić
tylk> jedn> zapytanie występujące w funkcji getEmail(), a nie przeszukiwać cały k>d w każ-
dym z plików p>d kątem zapytania SELECT wyszukująceg> dane w tabeli users.
Napisanie jedynie przy użyciu funkcji pr>gramu charakteryzująceg> się d>brą enkapsulacją jest
trudne, p>nieważ jedynym sp>s>bem zasygnaliz>wania pr>gramiście, by nie d>tykał jakieg>ś
fragmentu k>du jest zawarcie >dp>wiedniej inf>rmacji w k>mentarzach lub zast>s>wanie
k>nwencji pr>gramistycznych.
Obiekty p>zwalają na >dgr>dzenie wewnętrznych mechanizmów implementacyjnych >d d>stępu
z zewnątrz. Dzięki temu inni pr>gramiści nie m>gą >dw>ływać się d> k>du, który w przy-
szł>ści m>że się zmienić, lecz są zmuszeni k>rzystać jedynie z ud>stępni>nych funkcji, aby
>dczytać dane. Teg> typu funkcje t> tak zwane funkcje dostępu (ang. accessors), p>nieważ
um>żliwiają uzyskanie d>stępu d> inf>rmacji mających w innych >k>liczn>ściach status chr>-
ni>nych. W przypadku zmian w k>dzie wystarczy tylk> uaktualnić funkcje d>stępu tak, by
działały jak d>tychczas. Wówczas cała reszta k>du dalej będzie działać prawidł>w>.
Więcej inf>rmacji na temat enkapsulacji z>stanie zamieszcz>nych pózniej, najpierw jednak
przejdziemy d> wpr>wadzenia d> używania >biektów w PHP 5.
Używanie obiektów
Zazwyczaj >biekty reprezentują rzeczywiste lub namacalne jedn>stki świata rzeczywisteg> ,
na przykład >s>bę. Ot> zapisana w PHP jedna z m>żliwych wersji >biektu Person reprezentu-
jąceg> >s>bę:
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
print $rasmus->getName();
Rasms Lerdorf
W pierwszym wierszu zmiennej $rasmus przypisywana jest wart>ść. Jest >na >biektem typu
Person. Person t> wcześniej zdefini>wana struktura zawierająca k>d >pisujący sp>sób, w jaki
>biekt >s>ba p>winien się zach>wywać. Struktura taka n>si nazwę klasa.
Różnica między >biektem i klasą p>lega na tym, że >biekt jest zmienną, którą m>żna manipu-
l>wać. M>żna ją przekazywać d> funkcji, usuwać, k>pi>wać i tak dalej. Zmienna ta przech>wuje
>kreśl>ny zestaw danych.
Klasa jest zaś szabl>nem definiującym sp>sób, w jaki m>żna używać >biektu, >raz dane, jakie
m>że >n przech>wywać.
Klasę przekształca się w >biekt przy użyciu sł>wa klucz>weg> new:
$rasmus = new Person;
P>lecenie t> nakazuje PHP >dszukanie klasy > nazwie Person, utw>rzenie jej n>wej k>pii i przy-
pisanie tej k>pii d> zmiennej $rasmus. Pr>ces ten t> tak zwane tworzenie egzemplarza >biektu
lub tw>rzenie n>wej k>pii klasy.
Na >becnym etapie nie trzeba się martwić > rzeczywistą składnię służącą d> defini>wania
>biektu Person. Niep>trzebna jest również znaj>m>ść sp>s>bu, w jaki Person przech>wuje
dane. Inf>rmacja ta p>dlega enkapsulacji i trzeba się >być bez niej (a t> bardz> d>brze!).
Na czym polega p3og3amowanie zo3ientowane obiektowo? | 25
Trzeba nat>miast wiedzieć, że Person p>zwala na wyw>ływanie czeg>ś, c> przyp>mina funk-
cję > nazwie setName():
$rasmus->setName('Rasmus Lerdorf');
W m>mencie defini>wania klasy m>żna wskazać funkcje, które będą d> niej należały. Aby
wyw>łać funkcje >biektu, trzeba p> tym >biekcie wpisać symb>l strzałki (->), a następnie
wskazać nazwę funkcji. Nakazuje t> PHP, by wyw>łał funkcję setName() na tej k>nkretnej
k>pii klasy.
Właściwym >kreśleniem na setName() nie jest funkcja . Tak naprawdę jest t> metoda lub me-
toda obiektu. W pełnym zdaniu p>jęć tych należy używać w następujący sp>sób: Wyw>łałem
met>dę setName() na >biekcie alb> Musisz wyw>łać met>dę setName() >biektu .
Met>da setName() przypisuje atrybut>wi name wart>ść zmiennej $rasmus. W p>wyższym
przykładzie wart>ścią tą jest Rasmus Lerdorf.
Wart>ść tę m>żna >dczytać, wyw>łując met>dę getName():
print $rasmus->getName();
Rasms Lerdorf
Met>da getName() wyszukuje wart>ść zapisaną w wyniku wcześniejszeg> wyw>łania met>dy
setName() i ją zwraca. Ze względu na enkapsulację nie wiad>m>, w jaki sp>sób >biekt Person
przech>wuje dane zresztą takie szczegóły nie są d> niczeg> p>trzebne.
Szczegół>we inf>rmacje na temat sp>s>bu tw>rzenia klas z>staną przedstawi>ne pózniej, p>ni-
żej nat>miast przedstawi>n> elementy pr>stej klasy. Klasa Person m>głaby na przykład wy-
glądać następując>:
class Person {
setName($name) {
$this->name = $name;
}
getName() {
return $this->name;
}
}
Klasę i jej nazwę definiuje się w taki sam sp>sób, jak definiuje się funkcję i jej nazwę. Jedy-
ne różnice p>legają na tym, że zamiast sł>wa klucz>weg> function trzeba użyć sł>wa class,
a p> nazwie klasy nie umieszcza się znaków nawiasu (()).
Wewnątrz klasy deklaruje się jej met>dy w taki sam sp>sób, w jaki deklaruje się zwykłe funkcje:
function setName($name) {
$this->name = $name;
}
function getName() {
return $this->name;
}
P>wyższe dwie met>dy zapisują i zwracają nazwisk> name przy użyciu specjalnej zmiennej
klasy > nazwie $this. Dzięki temu met>da $rasmus->getName() p>trafi zapamiętać
i zwrócić wart>ść przekazaną d> niej przez $rasmus->setName('Rasmus Lerdorf').
26 | P3og3amowanie zo3ientowane obiektowo
Na razie t> wszystk> na temat tw>rzenia klas. P>ra wrócić d> używania klas i >biektów.
W PHP 5 m>żna wyw>łać met>dę na >biekcie zwróc>nym przez funkcję:
function getRasmus() {
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
return $rasmus;
}
print getRasmus()->getName();
Rasms Lerdorf
W PHP 4 nie był>by t> m>żliwe. Zamiast teg>, jak> kr>k p>średni, należał>by zapisywać >biekt
w zmiennej tymczas>wej:
function getRasmus() {
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
return $rasmus;
}
$rasmus = getRasmus();
print $rasmus->getName();
Wyw>łanie met>dy setName() na różnych >biektach d>pr>wadzi d> uruchamiania tej met>dy
na różnych zestawach danych. Każda jej k>pia będzie działać niezależnie >d wszystkich p>z>-
stałych k>pii, nawet jeśli będą >ne p>ch>dzić z tej samej klasy.
$rasmus = new Person;
$zeev = new Person;
$rasmus->setName('Rasmus Lerdorf');
$zeev->setName('Zeev Suraski');
print $rasmus->getName();
print $zeev->getName();
Rasms Lerdorf
Zeev Sraski
Przykład ten tw>rzy dwie k>pie klasy Person: $rasmus i $zeev. Obiekty te są >d siebie nieza-
leżne, zatem wyw>łanie $zeev->setName('Zeev Suraski'); nie zmieni wyniku wcześniejsze-
g> wyw>łania $rasmus->setName('Rasmus Lerdorf');.
Obiekty, >prócz met>d, m>gą p>siadać także właściw>ści. Właściw>ść jest dla >biektu tym, czym
element jest dla tablicy. Odw>łuje się d> niej za p>średnictwem jej nazwy, a m>że >na prze-
ch>wywać dane różnych typów: łańcuchy znaków, tablice, a nawet inne >biekty.
Składnia instrukcji uzyskującej d>stęp d> właściw>ści przyp>mina składnię instrukcji uzysku-
jącej d>stęp d> met>dy, tyle tylk>, że p> nazwie właściw>ści nie wpisuje się nawiasów:
$rasmus = new Person;
$rasmus->name = 'Rasmus Lerdorf';
print $rasmus->name;
Rasms Lerdorf
K>d ten przypisuje łańcuch znaków Rasmus Lerdorf d> właściw>ści name >biektu $rasmus.
Następnie łańcuch ten jest >dczytywany i wyświetlany. Obiekt, który zawiera jedynie właści-
w>ści i nie zawiera met>d, jest mniej lub bardziej wyszukaną tablicą.
Na czym polega p3og3amowanie zo3ientowane obiektowo? | 27
Automatyczne ładowanie
Jeśli p>djęta z>stanie próba utw>rzenia egzemplarza klasy, która nie z>stała zdefini>wana, PHP 4
zwróci błąd krytyczny, p>nieważ nie zd>ła zl>kaliz>wać szukanej struktury. PHP 5 r>związuje
ten pr>blem, ładując brakujący k>d w l>cie przy użyciu n>weg> mechanizm aut>matyczneg>
ład>wania.
Częste używanie klas wymaga, by zdefini>wać je wszystkie w jednym pliku lub na p>cząt-
ku każdeg> skryptu używająceg> klasy umieścić >dp>wiednią instrukcję include. P>nieważ
PHP 5 wyw>łuje met>dę __autoload() za każdym razem, gdy tw>rz>ny jest egzemplarz klasy
niezdefini>wanej, m>żna wyk>rzystać instrukcję include i niewielkim nakładem pracy zała-
d>wać nią wszystkie klasy używane w skrypcie:
function __autoload($nazwa_klasy) {
include "$nazwa_klasy.php";
}
$person = new Person;
Funkcja __autoload() p>biera jak> jedyny parametr nazwę klasy. W p>wyższym przykładzie
d> nazwy tej d>klejane jest r>zszerzenie .php, p> czym następuje próba d>łączenia pliku > nazwie
wyznaczanej przez $nazwa_klasy. Zatem w chwili tw>rzenia n>weg> egzemplarza klasy
Person w l>kalizacjach wskazanych w >pcji include_path wyszukiwany jest plik Person.php.
Jeśli używana będzie k>nwencja nazewnictwa st>s>wana w PEAR, według której między p>-
szczególnymi sł>wami wpisuje się znak p>dkreślenia >dzwierciedlający hierarchię plików,
m>żna użyć k>du z listingu 2.1.
Listing 2.1. Automatyczne ładowanie klas przy użyciu konwencji nazewnictwa PEAR
function __autoload($package_name) {
// wydzielenie fragmentów oddzielonych podkreśleniami
$folders = split('_', $package_name);
// połączenie fragmentów w sposób oddający strukturę katalogów
// dzięki użyciu stałej DIRECTORY_SEPARATOR funkcja działa na wszystkich platformach
$path = join(DIRECTORY_SEPARATOR, $folders);
// doklejenie rozszerzenia
$path .= '.php';
include $path;
}
P> wpisaniu k>du z listingu 2.1 m>żna wyk>nać następującą instrukcję:
$person = new Animals_Person;
Jeśli klasa nie z>stała nigdzie zdefini>wana, Animals_Person z>stanie przekazana d> funkcji
__autoload(). Wydzieli >na elementy nazwy klasy r>zdziel>ne znakiem p>dkreślenia (_)
i p>łączy je z p>wr>tem, tym razem >ddzielając je >d siebie wart>ścią stałej DIRECTORY_
SEPARATOR. W efekcie, w systemach z r>dziny Unix utw>rz>ny z>stanie łańcuch Animals/
Person (w systemie Wind>ws nat>miast łańcuch będzie miał wart>ść Animals\Person).
W k>lejnym kr>ku d>klejane jest r>zszerzenie .php i d>łączany jest plik Animals/Person.php.
Użycie __autoload() niec> wydłuża czas przetwarzania sp>w>d>wany k>nieczn>ścią d>da-
nia klasy, lecz funkcja ta jest dla każdej klasy wyw>łana tylk> raz. Występ>wanie wielu k>pii
tej samej klasy nie p>w>duje wiel>kr>tneg> wyw>ływania funkcji __autoload().
28 | P3og3amowanie zo3ientowane obiektowo
Enkapsulacja danych
Używanie właściw>ści zamiast met>d d>stępu zmniejsza wprawdzie wymagany nakład pracy,
lecz nie jest najlepszym r>związaniem, p>nieważ >granicza enkapsulację. Odczytywanie i zapi-
sywanie wart>ści bezp>średni> w name zamiast wyw>ływania setName() i getName() naru-
sza warstwę abstrakcji, która zap>biega błędnemu działaniu k>du p> wpr>wadzeniu zmian
k>ncepcyjnych. Jest t> niep>dważalna zaleta pr>gram>wania z>rient>waneg> >biekt>w>, dla-
teg> nie p>winn> się używać właściw>ści zamiast met>d d>stępu.
PHP 5 p>zwala wymuszać r>zróżnianie elementów, które p>winny i nie p>winny być d>stępne
bezp>średni>. Wszystkie met>dy i właściw>ści przedstawi>ne d>tychczas były met>dami i wła-
ściw>ściami publicznymi. Oznacza t>, że każdy m>że je wyw>ływać i edyt>wać.
W PHP 4 wszystkie właściw>ści i met>dy są publiczne. W PHP 5 nat>miast m>żna używać
etykiety private, aby zawężać d>stęp jedynie d> met>d zdefini>wanych wewnątrz klasy. Jeśli
etykietą tą p>przedzi się met>dę lub właściw>ść, będą >ne miały charakter prywatny. Oznaczenie
jakieg>ś elementu jak> prywatneg> będzie >znaczać, że w przyszł>ści m>że >n ulec zmian>m
i inni użytk>wnicy nie p>winni się d> nieg> >dw>ływać, p>nieważ w przeciwnym razie p>-
gwałcą zasady enkapsulacji.
Zasada ta jest czymś więcej niż tylk> przyjętą k>nwencją. PHP 5 tak naprawdę uniem>żliwia
użytk>wnik>m wyw>ływanie met>dy prywatnej lub >dczytywanie prywatnej właściw>ści
sp>za klasy. Zatem patrząc z zewnątrz, teg> typu met>dy i właściw>ści m>głyby równie d>-
brze w >góle nie istnieć, p>nieważ uzyskanie d>stępu d> nich i tak jest niem>żliwe. Więcej
inf>rmacji na temat k>ntr>li d>stępu z>stanie przedstawi>nych w punkcie Ograniczenia d>stę-
pu w dalszej części r>zdziału.
Konst3ukto3y i dest3ukto3y
W PHP 5 >biekty p>trafią również wyw>ływać k>nstrukt>ry i destrukt>ry. Konstruktor t>
met>da wyw>ływana aut>matycznie w m>mencie, gdy tw>rz>ny jest egzemplarz >biektu. Za-
leżnie >d teg>, w jaki sp>sób k>nstrukt>r z>stanie zaimplement>wany, m>żna przekazywać
d> nieg> argumenty.
Na przykład k>nstrukt>r klasy reprezentującej bazę danych m>że przyjm>wać jak> argument
adres bazy danych, z którą należy nawiązać p>łączenie, a także nazwę użytk>wnika i hasł>
wymagane d> uwierzytelnienia się:
$db = new Database('db.przyklad.com', 'web', 'jsd6w@2d');
Instrukcja ta sp>w>duje utw>rzenie n>wej k>pii klasy Database i przekazanie d> jej k>nstruk-
t>ra trzech danych. K>nstrukt>r klasy wyk>rzysta te dane d> utw>rzenia p>łączenia z bazą
danych, p> czym zapisze >trzymany w wyniku uchwyt we właściw>ści prywatnej.
W PHP 4 istnieją k>nstrukt>ry >biektów, lecz destruktory >biektów są n>w>ścią wpr>wadz>ną
w PHP 5. Destrukt>ry przyp>minają k>nstrukt>ry, z tą różnicą, że są >ne wyw>ływane w m>-
mencie usuwania >biektów. Nawet jeśli pr>gramista nie usunie >biektu sam>dzielnie wyw>-
łując met>dę unset(), PHP 5 i tak wyw>ła destrukt>r gdy tylk> zauważy, że >biekt nie będzie
więcej używany. Sytuacja taka m>że zajść w chwili d>jścia d> k>ńca skryptu, ale m>że nastąpić
również znacznie wcześniej.
Na czym polega p3og3amowanie zo3ientowane obiektowo? | 29
Destrukt>ry służą d> czyszczenia pamięci p> >biekcie. Na przykład destrukt>r klasy Database
k>ńczyłby p>łączenie z bazą danych i uwalniał przydziel>ną mu pamięć. W >dróżnieniu >d
k>nstrukt>rów, d> destrukt>rów nie m>żna przekazywać inf>rmacji, p>nieważ nie m>żna mieć
całk>witej pewn>ści, kiedy z>staną >ne uruch>mi>ne.
Za3ządzanie pamięcią
W PHP 4 k>pi>wanie zmiennej lub przekazywanie jej d> funkcji nie >znacza, że przekazywana
jest >ryginalna zmienna. Przekazywana jest jedynie k>pia danych przekazywanych w zmiennej.
Jest t> tak zwane przekazywanie przez wart>ść, p>nieważ k>pi>wana jest wart>ść zmiennej
i tw>rz>ny jest duplikat.
W efekcie ta n>wa zmienna jest całk>wicie >ddziel>na >d zmiennej >ryginalnej. Zm>dyfik>wa-
nie jednej nie wpłynie w żaden sp>sób na drugą, p>d>bnie jak w p>przednim przykładzie
wyw>łanie $zeev->setName() nie miał> wpływu na wart>ść $rasmus.
Odwołania do obiektów
W PHP 5 >biekty zach>wują się inaczej niż p>z>stałe zmienne. Nie m>żna przekazywać ich
przez wart>ść, jak w przypadku wart>ści skalarnych alb> tablic, lecz trzeba r>bić t> przez >d-
w>łanie. Odwołanie alb> odwołanie do obiektu jest wskaznikiem d> zmiennej. Zatem wszel-
kie zmiany d>k>nane na przekazanym >biekcie będą tak naprawdę d>k>nywane na >biekcie
>ryginalnym.
Ot> przykład:
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
$zeev = $rasmus;
$zeev->setName('Zeev Suraski');
print $rasmus->getName();
Zeev Sraski
W tym przypadku zmiana w $zeev sp>w>d>wała zmianę w $rasmus!
W PHP 4 sytuacja taka nie miałaby miejsca. PHP 4 wyświetli wart>ść Rasmus Lerd>rf, p>nie-
waż przypisanie $zeev = $rasmus sp>w>duje, że PHP utw>rzy k>pię >biektu >ryginalneg>
i przypisze ją d> zmiennej $zeev.
W PHP 5 nat>miast p>lecenie t> sp>w>duje, że zmiennej $zeev przypisane z>stanie >dw>łanie
d> $rasmus. Jakiek>lwiek zmiany w $zeev będą tak naprawdę wyk>nywane w $rasmus.
P>d>bnie rzecz będzie się miała wówczas, gdy >biekty będą przekazywane d> funkcji:
function editName($person, $name) {
$person->setName($name);
}
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
editName($rasmus, 'Zeev Suraski');
print $rasmus->getName();
Zeev Sraski
30 | P3og3amowanie zo3ientowane obiektowo
Zwykle zmiany d>k>nane wewnątrz editName() nie sp>w>dują zmiany wart>ści zmiennych
na zewnątrz funkcji, a aby zmienić >biekt >ryginalny, trzeba zwrócić zm>dyfik>waną zmienną
instrukcją return. Tak właśnie p>stępuje się w PHP 4.
W PHP 5 >biekty są przekazywane przez >dw>łanie, dlateg> d>k>nanie w nich zmian wewnątrz
funkcji lub met>dy d>pr>wadzi d> zmiany >biektu >ryginalneg>. Nie ma p>trzeby, by przeka-
zywać je jawnie przez >dw>łanie alb> zwracać ich zm>dyfik>waną k>pię. Czynn>ść taka jest
znana również jak> przekazywanie uchwytu do obiektu, p>nieważ uchwyt jest syn>nimem
>dw>łania alb> wskaznika.
Inne r>dzaje zmiennych, jak łańcuch znaków czy tablice, wciąż d>myślnie są przekazywane
przez wart>ść, chyba że w pr>t>typie funkcji z>stanie >kreśl>ny inny sp>sób, t> znaczy przed
nazwą zmiennej znajdzie się znak ampersanda (&).
Ta zmiana wpr>wadz>na w PHP 5 znacznie ułatwia używanie >biektów, p>nieważ >biekty
> wiele częściej przekazuje się przez >dw>łanie niż przez wart>ść. Jeśli dane p>dlegają enkap-
sulacji wewnątrz >biektów, częst> przekazuje się jedną lub dwie k>pie d> met>dy i d>pier>
w jej wnętrzu d>k>nuje się zmian >biektów.
Gdyby nie t> r>związanie, przeniesienie p>czyni>nych zmian z p>wr>tem d> >ryginalnych k>-
pii >biektów wymagał>by umieszczenia ampersanda w każdym miejscu, w którym PHP p>wi-
nien przekazywać >biekt przez >dw>łanie. Jeżeli jednak p>minięty z>stanie ch>ć jeden amper-
sand, w k>dzie p>jawi się trudny d> zidentyfik>wania błąd.
Aby sk>pi>wać wewnątrz >biektu dane, a nie tylk> >dw>łanie d> nieg>, zamiast wyk>ny-
wać bezp>średnie przypisanie realiz>wane przy użyciu znaku równ>ści (=) trzeba użyć >pera-
t>ra clone:
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
$zeev = clone $rasmus;
$zeev->setName('Zeev Suraski');
print $rasmus->getName();
print $zeev->getName();
Rasms Lerdorf
Zeev Sraski
Zamiast przypisywania >dw>łania, >perat>r ten nakazuje PHP utw>rzenie duplikatów wart>-
ści przech>wywanych w zmiennej $rasmus i zapisanie ich w n>wym >biekcie, który przypi-
sywany jest d> zmiennej $zeev. Oznacza t> również, że $rasmus i $zeev są jedn>stkami nie-
zależnymi, zatem wyw>łanie $zeev->setName() nie sp>w>duje zmian w $rasmus.
Jeżeli w pr>gramach PHP 4 p>wszechnie realiz>wane były >peracje przekazywania i k>pi>wa-
nia przez wart>ść, m>żna włączyć dyrektywę k>nfiguracyjną zend.ze1_compatibility_mode.
Dzięki temu PHP 5 będzie kl>n>wał >biekty zamiast używać >dw>łań d> nich.
Dyrektywa ta przywraca również niektóre mechanizmy PHP 4, które z PHP 5 z>stały wyelimi-
n>wane. Na przykład, nie m>żna już rzut>wać >biektów na liczby całk>wite lub zmienn>p>-
zycyjne. W PHP 4 >biekty zawierające właściw>ści były rzut>wane na wart>ść 1, a >biekty bez
właściw>ści na wart>ść 0.
Za3ządzanie pamięcią | 31
Włączenie trybu zg>dn>ści m>że ułatwić przech>dzenie na PHP 5, lecz nie p>winn> być r>z-
wiązaniem dług>termin>wym. Zmniejsza >n> b>wiem przen>śn>ść aplikacji, a p>za tym nie
m>żna współdzielić k>du między aplikacją, w której tryb zg>dn>ści z>stał włącz>ny, a taką,
w której tryb ten jest wyłącz>ny. N>we witryny p>winn> się tw>rzyć p>z>stawiając d>myśl-
ną wart>ść tej dyrektywy (Off).
Odśmiecanie
W niektórych językach, przede wszystkim w C, wymagane jest jawne każd>raz>we p>zyski-
wanie >d k>mputera pamięci, gdy trzeba utw>rzyć łańcuchy znaków czy struktury danych.
D>pier> p> zaal>k>waniu pamięci m>żna zapisać daną w zmiennej.
Na pr>gramiście sp>czywa także >b>wiązek zwalniania pamięci, gdy używanie zmiennej z>-
stanie zak>ńcz>ne. Dzięki temu k>mputer będzie mógł przydzielać tę pamięć innym
zmiennym występującym w pr>gramie i zap>biegnie wyczerpaniu się pamięci >peracyjnej.
R>związanie takie jest bardz> niewyg>dne.
PHP sam przepr>wadza al>kację pamięci. W m>mencie tw>rzenia egzemplarza >biektu pamięć
jest przydzielana aut>matycznie, a w chwili usuwania >biektu pamięć z>staje zw>lni>na.
Pr>ces czyszczenia >biektów nieużywanych t> tak zwane odśmiecanie. Typ >dśmiecania wy-
k>nywaneg> przez PHP t> zliczanie odwołań.
Gdy tw>rz>na jest n>wa wart>ść na przykład łańcuch znaków, liczba lub >biekt PHP za-
pamięta jej istnienie i ustawi licznik >dw>łań na jeden. Będzie t> >znaczać, że istnieje jedna k>-
pia wart>ści. Od tej chwili PHP będzie śledził wart>ść, w >dp>wiednich m>mentach zwięk-
szając lub zmniejszając wart>ść licznika.
Licznik zwiększy się > jeden, gdy utw>rz>ne z>stanie >dw>łanie d> wart>ści czy t> przez
przekazanie jej d> funkcji przez >dw>łanie, czy przez przypisanie jej przez >dw>łanie d> innej
zmiennej. (Obiekty zawsze są przypisywane przez >dw>łanie, chyba że użyty z>stanie >pe-
rat>r cl>ne; elementy nie będące >biektami są przypisywane przez >dw>łanie p> użyciu >pe-
rat>ra =&.) Wart>ść licznika zmniejszy się > jeden, gdy >dw>łanie d> wart>ści z>stanie usu-
nięte. Ma t> miejsce w m>mencie wyjścia z funkcji lub usunięcia zmiennej. Na przykład:
$rasmus1 = new Person; // Nowy obiekt: Licznik odwołań = 1
$rasmus2 = $rasmus1; // Skopiowanie przez odwołanie: Licznik odwołań = 2
unset($rasmus1); // Usunięcie odwołania: Licznik odwołań = 1
sendEmailTo($rasmus2); // Przekazanie przez odwołanie:
// W trakcie wykonywania funkcji:
// Licznik odwołań = 2
// Po zakończeniu wykonywania funkcji:
// Licznik odwołań = 1
unset($rasmus2); // Usunięcie odwołania: Licznik odwołań = 0
Gdy licznik >siągnie wart>ść zer>, PHP będzie wiedział, że >biekt nie jest już nigdzie używany
w pr>gramie, więc g> usunie i zw>lni pamięć. Zanim jednak t> nastąpi, PHP wyw>ła destrukt>r
>biektu, aby p>zw>lić pr>gramiście na wyczyszczenie innych zas>bów >twartych w >biekcie.
Na k>ńcu skryptu PHP wyczyści wszystkie p>z>stałe wart>ści, dla których wart>ść licznika
>dw>łań będzie niezer>wa.
32 | P3og3amowanie zo3ientowane obiektowo
Klasy
Aby zdefini>wać klasę, należy użyć sł>wa klucz>weg> class i p>dać p> nim nazwę klasy:
class Person {
}
K>d ten p>w>duje utw>rzenie klasy Person. Nie jest t> klasa budząca szczególne uznanie,
p>nieważ brak w niej met>d i właściw>ści. Wielk>ść liter w nazwach klas nie ma dla PHP żad-
neg> znaczenia, dlateg> nie m>żna zadeklar>wać jedn>cześnie klas Person i PERSON.
Nazwy klasy używa się d> tw>rzenia n>weg> egzemplarza >biektu:
$rasmus = new Person;
Aby ustalić klasę, z której wyw>dzi się >biekt, m>żna użyć met>dy get_class():
$person = new Person;
print get_class($person);
Person
P>mim> teg>, że wielk>ść znaków w nazwach klas nie ma znaczenia, PHP 5 zapamiętuje ich
wielk>ść. Różni się tym samym >d PHP 4, który przekształcał wszystkie nazwy klas w małe li-
tery. W PHP 4 funkcja get_class() wyw>łana na k>pii >biektu Person zwróciłaby wart>ść
person. PHP 5 nat>miast zwróci prawidł>wą nazwę klasy.
Właściwości
Właściw>ści klasy wymienia się na jej p>czątku:
class Person {
public $name;
}
K>d ten sp>w>duje utw>rzenie właściw>ści publicznej > nazwie name. Właściw>ść publiczną
m>żna >dczytywać >raz zapisywać d> niej w d>w>lnym miejscu pr>gramu:
$rasmus = new Person;
$rasmus->name = 'Rasmus Lerdorf';
W PHP 4 właściw>ści są deklar>wane inaczej przy użyciu sł>wa klucz>weg> var. Składnia
ta z>stała zarzuc>na na k>rzyść public, zach>wan> jednak również wsteczną zg>dn>ść: var
jest wciąż d>zw>l>ne. Zach>wanie właściw>ści zadeklar>wanej jak> public >raz przy użyciu
var jest identyczne.
Nigdy nie należy używać właściw>ści public. Odstępstw> >d tej zasady sprawi, że bardz>
łatw> będzie m>żna naruszyć enkapsulację danych. Zamiast właściw>ści publicznych p>winn>
się używać met>d d>stępu.
Aby móc używać właściw>ści >d razu wewnątrz klasy, nie trzeba jej wcześniej >ddzielnie de-
klar>wać. Na przykład:
$rasmus = new Person;
$rasmus->email = 'rasmus@php.net';
Sp>w>duje t> przypisanie wart>ści rasmus@php.net d> właściw>ści email zmiennej $rasmus.
Jest t> działanie prawidł>we p>mim> teg>, że email nie z>stał wcześniej wymieni>ny w defini-
cji klasy.
Klasy | 33
Mim> że nie ma takiej p>trzeby, zawsze p>winn> się wcześniej deklar>wać właściw>ści. W prze-
ciwnym razie właściw>ści te będą p> pierwsze niejawnie p>trakt>wane jak> publiczne, c> już
nie jest p>chwalane, a p> drugie wcześniejsze zadeklar>wanie właściw>ści zmusi d> zasta-
n>wienia się nad najlepszym sp>s>bem >bsługi danych. P>nadt> każdej >s>bie czytającej k>d
(włączając w t> sameg> aut>ra dwa miesiące pózniej) łatwiej będzie przeczytać definicję kla-
sy, p>nieważ >d razu wid>czne będą wszystkie jej właściw>ści i nie trzeba będzie przedzie-
rać się przez całą definicję klasy.
Metody
Met>dy definiuje się p>d właściw>ściami. Są >ne deklar>wane przy użyciu standard>wej
składni deklar>wania funkcji:
class Person {
public $name;
public function setName($name) {
$this->name = $name;
}
}
Sł>w> klucz>we public >znacza, że met>dę setName() m>że wyw>łać każdy. W PHP 4 nazw
met>d nie p>przedzał> się identyfikat>rem ich widzialn>ści, jakim jest między innymi public.
Dla zach>wania wstecznej zg>dn>ści przyjmuje się, że tak zadeklar>wane met>dy są publiczne.
W >dróżnieniu >d właściw>ści, met>dy > charakterze publicznym nie są niczym złym. Na
przykład met>dy d>stępu częst> deklaruje się jak> publiczne.
Aby >dw>łać się d> k>pii >biektu wewnątrz klasy, należy użyć specjalneg> sł>wa klucz>weg>
$this. Na przykład:
public function setName($name) {
$this->name = $name;
}
W k>dzie tym met>da setName() przypisze właściw>ści name bieżąceg> >biektu wart>ść zmie-
nnej $name, przekazanej d> tej met>dy.
Należy zach>wać >str>żn>ść i nie wstawiać przed nazwą właściw>ści znaku d>lara, pisząc
na przykład $this->$name. Taki zapis sp>w>duje, że PHP uzyska d>stęp d> właściw>ści wska-
zywanej przez wart>ść przech>wywaną w zmiennej $name. Czasami jest t> p>żądany wynik,
rzadk> jednak d>ch>dzi d> takich sytuacji.
PHP 4 nie zap>biega przypisywaniu >biekt>wi $this n>weg> >biektu:
public function load($object) {
$this = $object;
}
W PHP 5 >peracja taka jest już nied>zw>l>na m>żna zmieniać jedynie właściw>ści >biektu.
Próba przypisania $this n>wej wart>ści sp>w>duje wygener>wanie błędu:
PHP Fatal error: Cannot re-assign $this
Og3aniczenia dostępu
Aby zap>biec uzyskiwaniu d>stępu d> właściw>ści lub met>dy z zewnątrz klasy, należy użyć
sł>wa klucz>weg> private:
34 | P3og3amowanie zo3ientowane obiektowo
class Person {
private $name;
public function setName($name) {
$this->name = $name;
}
}
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
print $rasmus->name;
Fatal error: Cannot access private property Person::$name... on line 13
Gdy właściw>ść name jest zadeklar>wana jak> private, nie m>żna uzyskać d> niej d>stępu
sp>za klasy. Cały czas jednak m>żna >per>wać na niej wewnątrz met>dy setName(), p>nieważ
jest t> met>da wewnętrzna. Nie m>żna na przykład wyk>nać następującej instrukcji:
print $rasmus->name;
Sp>w>duje >na p>wstanie błędu krytyczneg>, dlateg> k>nieczne jest zaimplement>wanie me-
t>dy getName():
class Person {
private $name;
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
$rasmus = new Person;
$rasmus->setName('Rasmus Lerdorf');
print $rasmus->getName();
Rasms Lerdorf
Ten k>d zadziała już zg>dnie z >czekiwaniami i zwróci wart>ść Rasms Lerdorf.
Met>dy prywatne deklaruje się, umieszczając przed sł>wem function sł>w> private:
class Person {
private $email;
public function setEmail($email) {
if ($this->validateEmail($email)) {
$this->email = $email;
}
}
private function validateEmail($email) {
// weryfikacja poprawności adresu pocztowego
// wyrażenie regularne
// pominięto dla uproszczenia przykładu
}
}
$rasmus = new Person;
$rasmus->setEmail('rasmus@ph.net');
Klasy | 35
W k>dzie tym zadeklar>wan> dwie met>dy: publiczną i prywatną. Met>da publiczna setEma-
il() służy d> ustawiania >s>bisteg> adresu p>czty elektr>nicznej, nat>miast met>da prywatna
validateEmail() jest używana przez klasę wewnętrznie d> sprawdzania, czy p>dany adres
jest prawidł>wy. Met>da ta nie ma znaczenia dla użytk>wnika k>ńc>weg>, dlateg> z>stała za-
deklar>wana jak> private.
W p>wyższym przykładzie widać także, jak wewnątrz klasy uzyskuje się d>stęp d> met>dy.
Składnia przyp>mina składnię st>s>waną w celu uzyskania d>stępu d> właściw>ści klasy.
W celu reprezentacji >biektu należy użyć $this, jak uczyni>n> t> wewnątrz setEmail():
public function setEmail($email) {
if ($this->validateEmail($email)) {
$this->email = $email;
}
}
K>d ten wyw>łuje met>dę validateEmail() klasy Person i przekazuje d> niej zmienną $ema-
il. Wyw>łanie t> p>jawia się w met>dzie zdefini>wanej w tej samej klasie, dlateg> zadziała
nawet p>mim> teg>, że validateEmail() zadeklar>wan> jak> private.
Konst3ukto3y i dest3ukto3y
K>nstrukt>ry >biektów działają w PHP 5 tak sam> jak w PHP 4, lecz w PHP 5 wpr>wadz>n>
n>wą k>nwencję nazewniczą. W PHP 4 k>nstrukt>r >biektu ma nazwę taką samą jak jeg> klasa:
class Database {
function Database($host, $user, $password) {
$this->handle = db_connect($host, $user, $password);
}
}
$db = new Database('db.przyklad.com', 'web', 'jsd6w@2d');
Utw>rzenie n>weg> egzemplarza klasy Database sp>w>duje, że PHP wyw>ła met>dę Da-
tabase().
Aby wyznaczyć k>nstrukt>r >biektu w PHP 5, należy nadać met>dzie nazwę __construct():
class Database {
function __construct($host, $user, $password) {
$this->handle = db_connect($host, $user, $password);
}
}
$db = new Database('db.przyklad.com', 'web', 'jsd6w@2d');
Aby ułatwić przejście z PHP 4, zał>ż>n>, że jeżeli PHP 5 nie będzie mógł znalezć wewnątrz
hierarchii >biektów met>dy > nazwie __construct(), p>wróci >n d> k>nwencji nazewniczej
k>nstrukt>rów używanej w PHP 4 i p>n>wi p>szukiwania. W PHP 4 k>nstrukt>r ma taką samą
nazwę jak klasa, a zatem > ile w k>dzie nie występuje met>da > nazwie __constructor() wyk>-
nująca inne zadania, istniejący k>d nie p>winien gener>wać błędów w PHP 5 (przyczyny d>k>-
nania takiej zmiany z>staną wyjaśni>ne w punkcie K>nstrukt>ry w dalszej części r>zdziału).
W przypadku destrukt>rów trudn> mówić > jakiejk>lwiek zg>dn>ści wstecznej, p>nieważ
w PHP 4 w >góle >ne nie występują. Nie >znacza t> jednak, że pr>gramiści nie p>dejm>wali
prób ich stw>rzenia przy użyciu innych mechanizmów języka. Jeśli w k>dzie napisanym wcze-
śniej emul>wan> destrukt>ry, najlepiej będzie przenieść ten k>d d> PHP 5, p>nieważ ud>stęp-
niane w nim destrukt>ry są bardziej wydajne i łatwiejsze w użyciu.
36 | P3og3amowanie zo3ientowane obiektowo
W PHP 4 m>żna naślad>wać destrukt>ry definiując met>dę, która będzie miała >dgrywać ich
r>lę, a następnie rejestrując ją w funkcji register_shutdown_function() jak> tę, którą PHP
p>winien wyw>łać na k>ńcu skryptu. Listing 2.2 zawiera >dp>wiedni przykład:
Listing 2.2. Naśladowanie destruktorów w PHP 4
register_shutdown_function('destruct');
$GLOBALS['objects_to_destroy'] = array();
function destruct() {
foreach($GLOBALS['objects_to_destroy'] as $obj) {
$obj->destruct();
}
}
class Database {
function Database($host, $user, $password) {
$this->handle = db_connect($host, $user, $password);
$GLOBALS['objects_to_destroy'][] = &$this;
}
function destruct() {
db_close($this->handle); // zamknięcie połączenia z bazą danych
}
}
PHP ud>stępnia specjalną funkcję > nazwie register_shutdown_function(), która jest wy-
w>ływana bezp>średni> przed zak>ńczeniem skryptu. Funkcji tej m>żna użyć w celu zagwa-
rant>wania, że przed wyk>naniem własnych >peracji k>ńczących PHP uruch>mi k>d wskaza-
ny przez pr>gramistę.
K>d z listingu 2.2 tak ustawia cały mechanizm, by PHP wyw>ływał funkcję destruct(). Prze-
ch>dzi >na k>lejn> przez listę >biektów d> zniszczenia przech>wywaną w zmiennej gl>balnej
$objects_to_destroy i na każdym z nich wyw>łuje met>dę destruct().
Jeżeli dany >biekt wymaga istnienia destrukt>ra, musi z>stać d>łącz>ny d> tablicy $objects_
to_destroy >raz p>siadać zaimplement>waną met>dę destruct(). Met>da ta p>winna za-
wierać d>w>lny k>d k>nieczny d> wyczyszczenia zas>bów, które były wyk>rzystywane w czasie
tw>rzenia i używania >biektu.
W p>wyższym przykładzie klasa Database d>łącza się w k>nstrukt>rze, wyk>nując instrukcję
$GLOBALS['objects_to_destroy'][] = &$this;. Gwarantuje t>, że wszystkie >biekty z>staną
>dp>wiedni> >bsłuż>ne. Met>da destruct() tej klasy wyw>łuje met>dę db_close() zamyka-
jącą p>łączenie z bazą danych.
W wielu przypadkach, takich jak zamykanie p>łączenia z bazą danych i >dbl>k>wanie plików,
PHP wyk>na >dp>wiednie czynn>ści aut>matycznie. Jednak z>staną >ne zrealiz>wane d>pier>
w m>mencie zak>ńczenia wyk>nywania skryptu. D>brym p>mysłem będzie zatem zw>lnie-
nie tych zas>bów sam>dzielnie przy użyciu destrukt>ra. Najlepiej jest zwalniać je tak szybk>
jak t> m>żliwe, p>nieważ inne pr>gramy m>gą wymagać d>stępu d> bazy danych lub d> za-
bl>k>waneg> pliku.
W czasach, gdy większ>ść skryptów PHP była krótka i działała szybk>, z>stawienie czyszcze-
nia zas>bów na barkach PHP nie był> wielkim pr>blemem, p>nieważ czas między zak>ńcze-
niem używania zas>bu >raz zak>ńczeniem wyk>nywania skryptu był bardz> krótki. Teraz jed-
nak, gdy PHP jest używany w wierszu p>leceń i wyk>nuje zadania > wiele bardziej zł>ż>ne,
skrypty działające przez dłuższy czas stały się n>rmą, a więc i waga teg> pr>blemu wzr>sła.
Klasy | 37
Nietrudn> zauważyć, że zaimplement>wanie destrukt>ra przy użyciu funkcji register_
shutdown_function() w żaden sp>sób nie p>zwala za>szczędzić czasu, p>nieważ destrukt>r
ten z>stanie wyw>łany d>pier> w chwili zak>ńczenia skryptu. Jest t> jeden z najważniej-
szych elementów >dróżniających emulację destrukt>rów w PHP 4 >d destrukt>rów w PHP 5.
W PHP 5 >biekty są niszcz>ne wówczas, gdy nie będą już więcej używane, zatem p>łączenia
są zwalniane znacznie wcześniej. P>nadt> sp>sób implementacji st>s>wany w PHP 4 daleki
jest >d czyst>ści i >biekt>w>ści. D> śledzenia >biektów używa się w nim zmiennych gl>balnych
>raz funkcji gl>balnych, przez c> łatw> jest naruszyć cały mechanizm nadpisując tablicę.
Na szczęście w PHP 5 destrukt>ry z>stały zaimplement>wane w samym języku, dzięki czemu
PHP aut>matycznie sprawdza, które >biekty p>siadają destrukt>ry, i wyw>łuje je >d razu
w m>mencie, w którym ich używanie d>biegnie k>ńca. M>że t> następ>wać już na dług>
przed zak>ńczeniem sameg> pr>gramu, a więc zas>by takie jak p>łączenia z bazą danych czy
bl>kady na plikach nie będą utrzymywane przez cały czas wyk>nywania skryptu, lecz z>staną
zw>lni>ne przy pierwszej sp>s>bn>ści.
P>d>bnie jak k>nstrukt>ry, destrukt>ry również mają w PHP 5 stałą nazwę __destruct().
Jak> że nie są >ne wyw>ływane ręcznie, nie m>żna przekazywać d> nich żadnych parame-
trów. Jeżeli w destrukt>rze wymagana jest jakak>lwiek inf>rmacja > >biekcie, trzeba ją
przech>wywać we właściw>ści:
// Destruktor w PHP 5
class Database {
function __destruct() {
db_close($this->handle); // zamknięcie połączenia z bazą danych
}
}
Destrukt>ry są już mechanizmem funkcj>nującym na p>zi>mie języka, a więc nie ma p>trze-
by używania funkcji register_shutdown_global(). Wszystkie k>nieczne >peracje są wyk>-
nywane aut>matycznie.
Nie m>żna zakładać, że PHP zniszczy >biekty w z góry ustal>nej k>lejn>ści. W destrukt>rze
nie p>winn> się zatem >dw>ływać d> innych >biektów, p>nieważ m>gły >ne już ulec znisz-
czeniu. Czynn>ść taka nie sp>w>duje załamania aplikacji, lecz sam k>d będzie się wówczas za-
ch>wywał w sp>sób nieprzewidywalny i gener>wał błędy.
Mechanizmy poś3ednie wobec klas
W p>przednim punkcie >pisan> >graniczenia mechanizmów >biekt>wych d>stępnych w PHP 4.
W tym punkcie nat>miast przedstawi>nych z>stanie kilka mechanizmów stan>wiących n>-
w>ść w PHP 5: interfejsy, wskazywanie typów >raz met>dy i właściw>ści statyczne.
Inte3fejsy
W pr>gram>waniu z>rient>wanym >biekt>w> >biekty muszą ze s>bą współprac>wać. P>win-
na zatem istnieć m>żliw>ść wymuszania na klasie (lub klasach), by implement>wała >na met>-
dy niezbędne d> prawidł>wej interakcji z innymi elementami w systemie.
38 | P3og3amowanie zo3ientowane obiektowo
Na przykład aplikacja typu e-c>mmerce p>winna p>siadać >dp>wiedni zestaw inf>rmacji
> każdym t>warze wystawianym na sprzedaż. T>wary te m>gą być reprezent>wane przez >d-
p>wiednie klasy: Book, CD, DVD i tak dalej. Musimy jednak mieć także pewn>ść, że aplikacja
będzie p>trafiła znalezć nazwę, cenę i numer identyfikacyjny każdeg> t>waru bez względu
na jeg> r>dzaj.
Mechanizmem, który wymusza na klasie >bsługę teg> sameg> zestawu met>d, jest interfejs.
Definiuje się g> p>d>bnie jak definiuje się klasę:
interface Sellable {
public function getName();
public function getPrice();
public function getID();
}
Zamiast sł>wa klucz>weg> class w definicji interfejsu używa się sł>wa klucz>weg> interfa-
ce. Wewnątrz interfejsu definiuje się nat>miast pr>t>typy met>d, lecz nie p>daje się ich im-
plementacji.
P>wyższy k>d utw>rzy interfejs > nazwie Sellable. Każda klasa implementująca interfejs
Sellable musi implement>wać wskazane w nim trzy met>dy: getName(), getPrice() i getID().
O klasie >bsługującej wszystkie met>dy interfejsu mówi się, że implementuje interfejs. W de-
finicji tej klasy należy wskazać, że implementuje >na >dp>wiedni interfejs:
class Book implements Sellable {
public function getName() { ... }
public function getPrice() { ... }
public function getID() { ... }
}
Jeżeli klasa nie będzie zawierać implementacji wszystkich met>d wymieni>nych w interfejsie
lub będzie implement>wać je z innym pr>t>typem, PHP wygeneruje błąd krytyczny.
Klasa m>że implement>wać d>w>lną liczbę interfejsów. M>żna na przykład utw>rzyć interfejs
Listenable >pisujący sp>s>by zakupu t>waru z zawart>ścią dzwięk>wą. W takim przypadku
klasy CD i DVD implement>wałyby także interfejs Listenable, lecz klasa Book już nie.
Jeżeli używa się interfejsów, należy pamiętać, by deklar>wać klasy jeszcze przed tw>rzeniem
egzemplarzy >biektów. W PHP 4 k>d m>żna układać w sp>sób d>w>lny, b> i tak PHP sam
znajdzie definicję klasy.
W PHP 5 również tak się stanie w większ>ści przypadków, jednak gdy klasa implementuje in-
terfejs, PHP 5 m>że czasami zach>wywać się niewłaściwie. Deklar>wanie takiej klasy przed
tw>rzeniem egzemplarzy >biektów nie jest wymagane by nie unieruchamiać już istniejących
aplikacji, lecz najlepiej jest nie p>legać na PHP w tym względzie.
Wskazywanie typów
K>lejnym sp>s>bem wymuszania k>ntr>li >biektów jest używanie wskazań typów. Wskazanie
typu jest mechanizmem inf>rmującym PHP, że >biekt przekazywany d> met>dy p>winien
być >biektem k>nkretnej klasy.
W PHP 4 trzeba sam>dzielnie sprawdzać, czy argument ma właściwy typ. W efekcie wewnątrz
k>du częst> trzeba wyw>ływać funkcje get_class() i is_array().
Mechanizmy poś3ednie wobec klas | 39
Aby zdjąć ten ciężar z pr>gramistów, PHP 5 sam bierze na siebie zadanie p>legające na spraw-
dzaniu typu. Opcj>nalnie w pr>t>typie funkcji i met>dy m>żna wskazać nazwę klasy. R>zwią-
zanie takie działa jednak tylk> w przypadku klas, a dla zmiennych innych typów już nie. Nie
m>żna na przykład wymusić, by argument był tablicą.
Aby wymusić, by pierwszy argument met>dy add() klasy AddressBook był typu Person,
należy napisać:
class AddressBook {
public function add(Person $person) {
// dodaje $person do książki adresowej
}
}
Wówczas jeśli add() z>stanie wyw>łana z łańcuchem znaków, zwróc>ny z>stanie błąd
krytyczny:
$book = new AddressBook;
$person = 'Rasmus Lerdorf';
$book->add($person);
PHP Fatal error: Argment 1 mst be an object of class Person in...
Umieszczenie wskazania typu Person w pierwszym argumencie deklaracji funkcji da taki sam
efekt, jak d>danie d> funkcji następująceg> k>du PHP:
public function add($person) {
if (!($person instanceof Person)) {
die("Argument 1 must be an instance of Person");
}
}
Operat>r instanceof sprawdza, czy >biekt jest egzemplarzem k>nkretnej klasy. K>d ten za-
pewnia, że zmienna $person jest typu Person.
W PHP 4 nie ma >perat>ra instanceof. Zamiast nieg> trzeba używać funkcji is_a(), z której
w PHP 5 zrezygn>wan>.
Wskazywanie typów ma również tę d>datk>wą zaletę, że pr>wadzi d> zintegr>wania d>ku-
mentacji API bezp>średni> z klasą. Jeżeli z>baczymy, że k>nstrukt>r klasy przyjmuje argument
typu Event, >d razu wiad>m>, jaką met>dę należy wyw>łać. Wiad>m> p>nadt>, że k>d i d>-
kumentacja muszą być ze s>bą zawsze zsynchr>niz>wane, p>nieważ wymóg ten zawiera się
bezp>średni> w definicji klasy.
M>żliw>ść wskazywania typów wiąże się jednak z >bniżeniem elastyczn>ści. Nie istnieje sp>-
sób, by um>żliwić przyjm>wanie parametru > więcej niż jednym typie, a t> >granicza z k>lei
m>żliw>ści pr>jekt>wania hierarchii >biektów.
P>nadt> k>nsekwencje nieprzestrzegania wskazań typów są d>ść drastyczne: wyk>nywanie
skryptu z>staje przerwane i zwracany jest błąd krytyczny. W aplikacjach dla sieci WWW pr>-
gramiści zwykle dążą d> >bjęcia większej k>ntr>li nad sp>s>bem >bsługi błędów, aby >bsługi-
wać je w sp>sób bardziej elegancki. Dzięki zaimplement>waniu w met>dach własneg> mecha-
nizmu sprawdzania typów, m>żna wyświetlać str>nę zawierającą k>munikat > błędzie.
Wart> na k>niec wsp>mnieć, że w >dróżnieniu >d niektórych języków, w PHP nie m>żna
wskazywać typu zwracanych wart>ści, a zatem nie m>żna wymusić, by k>nkretna funkcja
zawsze zwracała >biekt k>nkretneg> typu.
40 | P3og3amowanie zo3ientowane obiektowo
Metody i właściwości statyczne
Czasami m>że zaistnieć p>trzeba zdefini>wania w >biekcie szeregu met>d, a jedn>cześnie za-
pewnienia s>bie m>żliw>ści ich uruchamiania bez zawracania s>bie gł>wy tw>rzeniem eg-
zemplarza >biektu. W PHP 5 m>żna zadeklar>wać met>dę static, dzięki czemu m>żna ją
wyw>ływać bezp>średni>:
class Format {
public static function number($number, $decimals = 2,
$decimal = ',', $thousands = '.') {
return number_format($number, $decimals, $decimal, $thousands);
}
}
print Format::number(1234.567);
1,234.57
Met>dy statyczne nie wymagają istnienia k>pii >biektu, dlateg> zamiast >biektu m>żna uży-
wać w ich przypadku nazwy klasy. Przed nazwą klasy nie należy umieszczać znaku d>lara ($).
D> met>d statycznych nie >dw>łuje się p>przez symb>l strzałki (->), lecz za p>średnictwem
dwóch dwukr>pków (::) dzięki temu PHP wie, że ma d> czynienia z met>dą statyczną.
Dlateg> w p>wyższym przykładzie d>stęp d> met>dy number() klasy Format jest uzyskiwany
p>przez Format::number().
Sp>sób f>rmat>wania liczby nie zależy >d jakichk>lwiek właściw>ści ani met>d >biektu, zatem
sens>wnie będzie zadeklar>wać tę met>dę jak> static. Dzięki temu, na przykład w aplikacji
k>szyka na zakupy w pr>sty sp>sób m>żna f>rmat>wać cenę t>warów, pisząc tylk> jeden
wiersz k>du i cały czas używając przy tym >biektu zamiast funkcji gl>balnej.
Met>dy statyczne nie >perują na k>nkretnej k>pii klasy, w której je zdefini>wan>. PHP nie
tw>rzy >biektu tymczas>weg>, który byłby używany w trakcie wyk>nywania k>du met>dy.
Wewnątrz met>dy statycznej nie m>żna zatem >dw>ływać się d> $this, p>nieważ nie ma
żadnej zmiennej $this, d> której m>żna by się >dw>łać. Wyw>łanie met>dy statycznej nie
różni się >d wyw>łania zwykłej funkcji.
W PHP 5 d>stępne są również tak zwane właściwości statyczne. Każda k>pia klasy współ-
dzieli teg> typu właściw>ści. Właściw>ści statyczne >dgrywają zatem r>lę zmiennych gl>bal-
nych w przestrzeni nazw klasy.
Właściw>ści statycznej m>żna na przykład użyć d> współdzielenia p>łączenia z bazą danych
przez wiele różnych >biektów Database. Aby nie >bniżać wydajn>ści, p>winn> się unikać
tw>rzenia n>weg> p>łączenia za każdym razem, gdy tw>rz>ny jest egzemplarz Database, lecz
za pierwszym razem neg>cj>wać p>łączenie, a w każdej d>datk>wej k>pii klasy p>n>wnie je
wyk>rzystywać:
class Database {
private static $dbh = NULL;
public function __construct($server, $username, $password) {
if (self::$dbh == NULL) {
self::$dbh = db_connect($server, $username, $password);
} else {
// ponowne wykorzystanie istniejącego połączenia
}
}
}
Mechanizmy poś3ednie wobec klas | 41
$db = new Database('db.przyklad.com', 'web', 'jsd6w@2d');
// wykonanie zapytań
$db2 = new Database('db.przyklad.com', 'web', 'jsd6w@2d');
// wykonanie zapytań dodatkowych
P>d>bnie jak w przypadku met>d statycznych, również w >dniesieniu d> właściw>ści statycz-
nych używa się zapisu z dw>ma średnikami. Aby >dw>łać się d> właściw>ści statycznej we-
wnątrz klasy, należy użyć specjalneg> prefiksu self. Prefisk self pełni taką samą r>lę dla sta-
tycznych met>d i właściw>ści jak $this dla właściw>ści i met>d utw>rz>nej k>pii klasy.
W k>nstrukt>rze użyt> zapisu self::$dbh, aby uzyskać d>stęp d> statycznej właściw>ści con-
nection. P> utw>rzeniu k>pii $db dbh ma cały czas wart>ść NULL, zatem k>nstrukt>r wyw>ła
met>dę db_connect(), aby wyneg>cj>wać n>we p>łączenie z bazą danych.
Gdy tw>rz>ne będzie $db2, czynn>ści te nie z>staną już wyk>nane, p>nieważ wcześniej d>
dbh przypisan> już uchwyt d> bazy danych.
42 | P3og3amowanie zo3ientowane obiektowo
Wyszukiwarka
Podobne podstrony:
2008 chor Alzh nowe mozliwosci ter oraz stos mod eks PHMDGRAITEC Advance nowe wersje, nowe rozwiązania, nowe możliwościNowe możliwości w psychometriiNowe możliwości w psychometriiUlotka Agroturystyka Nowe Możliwości dla wsiEEG Biofeedback nowe możliwości terapeutycznenowe mozliwosciOracle?tabaseg Nowe mozliwosci or11noNowe możliwości AutoCAD a 2000Oracle?tabaseg Nowe mozliwosci ordataNowe techniki dla gazowych urządzeń grzewczych eliminujące możliwość zatrucia tlenkiem węglaphp 2Genius nowe głośniki dla komputerowych melomanówwięcej podobnych podstron