informatyka programowanie obiektowe w php 5 hasin hayder ebook

background image

Programowanie
obiektowe w PHP 5

Autor: Hasin Hayder
ISBN: 978-83-246-1821-7
Tytu³ orygina³u:

Object-Oriented

Programming with PHP5

Format: 170x230, stron: 264

• Naucz siê definiowaæ w³aœciwoœci obiektów
• Stwórz kod, który bêdzie ³atwy w zarz¹dzaniu
• Zbuduj wydajn¹ i bezpieczn¹ aplikacjê

Programowanie obiektowe (OOP) wci¹¿ zyskuje rzesze nowych zwolenników.
Poniewa¿ opiera siê ono na klasach i obiektach, jest znacznie bardziej intuicyjne
ni¿ programowanie strukturalne. Do jego podstawowych zalet zaliczyæ nale¿y tak¿e
³atwoœæ modyfikowania oraz mo¿liwoœæ wielokrotnego wykorzystania klas. PHP 5
udostêpnia wiele ró¿norodnych mechanizmów (na przyk³ad obs³ugê wyj¹tków czy zbiór
interfejsów znacznie rozszerzaj¹cych mo¿liwoœci klas u¿ytkownika) oraz pe³ny modu³
obs³uguj¹cy styl programowania OOP, dziêki czemu jest doskona³ym narzêdziem,
pozwalaj¹cym tworzyæ wydajne, bezpieczne i dynamiczne aplikacje z wykorzystaniem
programowania obiektowego.

Ksi¹¿ka „Programowanie obiektowe w PHP 5” jest doskona³ym Ÿród³em informacji,
które pomo¿e Ci zrozumieæ najistotniejsze koncepcje programowania zorientowanego
obiektowo w PHP 5. Podrêcznik zawiera omówienie zagadnieñ podstawowych
oraz bardziej zaawansowanych, takich jak architektura Model-View-Controller (MVC)
oraz testy jednostkowe. Znajdziesz tu tak¿e praktyczne wskazówki i przyk³ady
dotycz¹ce m.in. u¿ycia biblioteki Standard PHP Library. Dowiesz siê, jak u¿ywaæ
odpowiedniego wzorca, aby zwiêkszyæ wydajnoœæ kodu, czym jest testowanie
jednostkowe i dlaczego stanowi ono zasadnicz¹ czêœæ tworzenia dobrego oraz
stabilnego projektu programu. Nauczysz siê tworzyæ wydajne, bezpieczne i ³atwe
w zarz¹dzaniu aplikacje.

• Praca z OOP — tworzenie obiektów
• Funkcje dostarczaj¹ce informacje o klasie
• Iteratory
• Automatyczne wczytywanie klas
• Serializacja
• Wzorce projektowe
• Refleksja i testy jednostkowe
• Biblioteka Standard PHP Library
• Obs³uga baz danych z u¿yciem stylu OOP
• U¿ywanie architektury MVC

background image

Spis tre

Ăci

O autorze

9

O recenzentach

11

Wprowadzenie

13

Co zawiera ta ksi

Èĝka?

13

Dla kogo jest przeznaczona ksi

Èĝka?

15

Konwencje zastosowane w ksi

Èĝce

15

U

ĝycie przykïadowych kodów

16

Rozdzia

ï 1. Styl OOP kontra programowanie proceduralne

17

Wprowadzenie do PHP

18

Zaczynamy

18

Krótka historia stylu programowania OOP w PHP

19

Proceduralny styl kodowania kontra OOP

19

Zalety u

ĝywania stylu OOP

20

Wnikliwa analiza obiektu

22

ĝnice miÚdzy stylem OOP w PHP 4 i PHP 5

23

Niektóre podstawowe poj

Úcia z zakresu OOP

25

Ogólne konwencje programowania

26

Podsumowanie

27

Rozdzia

ï 2. RozpoczÚcie pracy z OOP

29

Tworzenie obiektów

29

Dost

Úp do wïaĂciwoĂci i metod z wewnÈtrz klasy

31

U

ĝywanie obiektu

31

Modyfikatory dost

Úpu

32

Konstruktory i destruktory

34

Sta

ïe klasy

36

background image

Spis tre

Ğci

4

Rozszerzanie klasy (dziedziczenie)

38

Nadpisywanie metod

40

Uniemo

ĝliwianie nadpisywania

40

Uniemo

ĝliwianie rozszerzania

40

Polimorfizm

41

Interfejs

42

Klasa abstrakcyjna

44

Metody i w

ïaĂciwoĂci statyczne

45

Metody akcesorów

48

U

ĝywanie metod magicznych do pobierania i ustalania wartoĂci wïaĂciwoĂci klasy

49

Metody magiczne s

ïuĝÈce do przeciÈĝania metod klasy

51

Wizualne przedstawienie klasy

52

Podsumowanie

52

Rozdzia

ï 3. Jeszcze wiÚcej OOP

55

Funkcje dostarczaj

Èce informacje o klasie

55

Sprawdzanie, czy dana klasa istnieje

55

Okre

Ălanie aktualnie wczytanej klasy

56

Sprawdzanie, czy istniej

È podane metody i wïaĂciwoĂci

56

Okre

Ălanie rodzaju klasy

57

Okre

Ălanie nazwy klasy

57

Obs

ïuga wyjÈtków

58

Zebranie wszystkich b

ïÚdów PHP jako wyjÈtku

62

Iteratory

63

Obiekt ArrayObject

65

Konwersja tablicy na obiekt

66

Dost

Úp do obiektów z zastosowaniem stylu tablicy

67

Serializacja

68

Metody magiczne w serializacji

70

Klonowanie obiektu

72

Automatyczne wczytywanie klas, czyli klasy na

ĝÈdanie

73

’añcuchowe wiÈzanie metod

74

Cykl

ĝycia obiektu w PHP oraz buforowanie obiektu

75

Podsumowanie

77

Rozdzia

ï 4. Wzorce projektowe

79

Jak to zosta

ïo zrobione wczeĂniej?

79

Wzorzec Strategia

80

Wzorzec Fabryka

82

Wzorzec Fabryka abstrakcyjna

85

Wzorzec Adapter

87

Wzorzec Singleton

91

Wzorzec Iterator

93

Wzorzec Obserwator

96

Wzorzec Proxy, czyli mechanizm Lazy Loading

98

Wzorzec Dekorator

100

background image

Spis tre

Ğci

5

Wzorzec Active Record

103

Wzorzec Fasada

103

Podsumowanie

106

Rozdzia

ï 5. Refleksja i testy jednostkowe

109

Refleksja

109

ReflectionClass

110

Klasa ReflectionMethod

115

Klasa ReflectionParameter

117

Klasa ReflectionProperty

119

Testy jednostkowe

121

Korzy

Ăci pïynÈce z testów jednostkowych

121

Krótkie wprowadzenie do niebezpiecznych b

ïÚdów

122

Przygotowanie do przeprowadzania testów jednostkowych

123

Rozpocz

Úcie przeprowadzania testów jednostkowych

124

Testowanie obiektu EmailValidator

127

Testy jednostkowe dla zwyk

ïych skryptów

130

Podej

Ăcie Test Driven Development (TDD)

134

PHPUnit API

139

Podsumowanie

147

Rozdzia

ï 6. Biblioteka Standard PHP Library

149

Obiekty dost

Úpne w SPL

149

Klasa ArrayObject

150

Klasa ArrayIterator

155

Klasa DirectoryIterator

157

Klasa RecursiveDirectoryIterator

161

Klasa RecursiveIteratorIterator

162

Klasa AppendIterator

162

Klasa FilterIterator

164

Klasa LimitIterator

165

Klasa NoRewindIterator

166

Interfejs SeekableIterator

167

Interfejs RecursiveIterator

168

Obiekt SPLFileObject

169

Obiekt SPLFileInfo

170

Obiekt SPLObjectStorage

172

Podsumowanie

174

Rozdzia

ï 7. Obsïuga baz danych z uĝyciem stylu OOP

175

Wprowadzenie do MySQLi

175

Nawi

Èzywanie poïÈczenia z MySQL w stylu zgodnym z OOP

176

Pobieranie danych w stylu zgodnym z OOP

177

Uaktualnianie danych w stylu zgodnym z OOP

177

Zapytania preinterpretowane

178

U

ĝywanie obiektu BLOB w zapytaniach preinterpretowanych

180

Wykonanie procedury sk

ïadowanej za pomocÈ MySQLi i PHP

182

background image

Spis tre

Ğci

6

PDO

183

Konfiguracja DSN dla ró

ĝnych silników baz danych

185

U

ĝywanie zapytañ preinterpretowanych za pomocÈ PDO

185

Wywo

ïywanie procedur skïadowanych

187

Inne ciekawe funkcje

187

Wprowadzenie do Data Abstraction Layers

188

ADOdb

189

MDB2

197

Wprowadzenie do ActiveRecord

200

Tworzenie nowego rekordu za pomoc

È ActiveRecord

200

Wybór lub uaktualnienie danych

201

Podsumowanie

201

Rozdzia

ï 8. Uĝywanie jÚzyka XML w stylu zgodnym z OOP

203

Format dokumentu XML

203

Wprowadzenie do SimpleXML

204

Przetwarzanie dokumentów

205

Uzyskiwanie dost

Úpu do atrybutów

206

Przetwarzanie

ěródeï Flickr za pomocÈ SimpleXML

206

Zarz

Èdzanie sekcjami CDATA za pomocÈ SimpleXML

209

XPath

210

DOM API

212

Modyfikacja istniej

Ècych dokumentów

213

Inne u

ĝyteczne funkcje

214

Podsumowanie

214

Rozdzia

ï 9. Uĝywanie architektury MVC

215

Co to jest MVC?

215

Rozplanowanie projektu

216

Projekt pliku rozruchowego

216

Dodanie obs

ïugi bazy danych

232

Sterowniki

235

Tworzenie aplikacji na podstawie gotowej struktury

245

Kontroler uwierzytelniania

246

Podsumowanie

252

Skorowidz

253

background image

2

Rozpocz

Úcie pracy z OOP

W tym rozdziale czytelnik dowie si

Ú, w jaki sposób tworzyÊ obiekty, definiowaÊ ich atrybuty

(czyli w

ïaĂciwoĂci) oraz metody. W jÚzyku PHP obiekty zawsze sÈ tworzone za pomocÈ sïowa

kluczowego

class

. Po lekturze rozdzia

ïu czytelnik znacznie rozszerzy wiedzÚ z zakresu klas,

w

ïaĂciwoĂci i metod. Ponadto w rozdziale tym zostanÈ podjÚte tematy zwiÈzane z zasiÚgiem

metod, modyfikatorami metod oraz zostan

È przedstawione zalety pïynÈce z uĝywania interfej-

sów. Niniejszy rozdzia

ï jest takĝe wprowadzeniem do innych podstawowych funkcji programo-

wania zorientowanego obiektowo w PHP. Bior

Èc to wszystko pod uwagÚ, moĝna zaryzykowaÊ

stwierdzenie,

ĝe ten rozdziaï jest jednym z lepszych zasobów pozwalajÈcych na rozpoczÚcie

pracy z OOP w j

Úzyku PHP.

Tworzenie obiektów

Jak wcze

Ăniej wspomniano, obiekt w jÚzyku PHP jest tworzony za pomocÈ sïowa kluczowe-

go

class

. Wymieniona klasa sk

ïada siÚ z wïaĂciwoĂci i metod (publicznych bÈdě prywatnych).

Przyjrzyjmy si

Ú klasie

Emailer

, która zosta

ïa juĝ przedstawiona w rozdziale 1. Teraz przeanali-

zujemy sposób dzia

ïania klasy

Emailer

:

<?
// class.emailer.php
class Emailer
{
private $sender;
private $recipients;
private $subject;
private $body;
function __construct($sender)
{
$this->sender = $sender;

background image

Programowanie obiektowe w PHP 5

30

$this->recipients = array();
}
public function addRecipients($recipient)
{
array_push($this->recipients, $recipient);
}
public function setSubject($subject)
{
$this->subject = $subject;
}
public function setBody($body)
{
$this->body = $body;
}
public function sendEmail()
{
foreach ($this->recipients as $recipient)
{
$result = mail($recipient, $this->subject, $this->body,
"From: {$this->sender}\r\n");
if ($result) echo "WiadomoĂÊ zostaïa wysïa do
{$recipient}<br/>";
}
}
}
?>

Powy

ĝszy kod rozpoczyna siÚ poleceniem

class Emailer

, które oznacza,

ĝe nazwa tworzonej kla-

sy to

Emailer

. Podczas nadawania nazw klasom nale

ĝy stosowaÊ tÚ samÈ konwencjÚ nazw, któ-

ra jest u

ĝywana w stosunku do zmiennych, na przykïad nazwy nie rozpoczynamy od cyfry, itd.

Nast

Úpnie wiersze kodu odpowiadajÈ za deklaracjÚ wïaĂciwoĂci klasy. Moĝemy wiÚc wyodrÚb-

ni

Ê cztery —

$sender

,

$recipient

,

$subject

oraz

$body

. Warto zwróci

Ê uwagÚ, ĝe kaĝda z wy-

mienionych w

ïaĂciwoĂci zostaïa zadeklarowana z uĝyciem sïowa kluczowego

private

(prywat-

na). W

ïaĂciwoĂÊ prywatna to taka, która jest dostÚpna jedynie w danej klasie. Trzeba jeszcze

doda

Ê, ĝe wïaĂciwoĂci to po prostu zmienne wewnÈtrz klasy.

Jak czytelnik zapewne pami

Úta, metoda jest po prostu funkcjÈ zdefiniowanÈ wewnÈtrz klasy.

W klasie przedstawionej na powy

ĝszym kodzie znajduje siÚ piÚÊ funkcji —

__construct()

,

addRecipient()

,

setSubject()

,

setBody()

oraz

sendEmail()

. Warto zwróci

Ê uwagÚ, ĝe ostatnie

cztery metody zosta

ïy zadeklarowane z uĝyciem sïowa kluczowego

public

(publiczne). Ozna-

cza to,

ĝe kaĝdy, kto utworzy egzemplarz tego obiektu, posiada równieĝ dostÚp do jego metod

publicznych.

Metoda

__construct()

jest metod

È specjalnego znaczenia w klasie i jest nazywana konstrukto-

rem

. W trakcie tworzenia nowego obiektu na podstawie klasy metoda konstruktora jest auto-

matycznie wywo

ïywana. Dlatego teĝ, jeĂli podczas tworzenia obiektu trzeba na nim wykonaÊ

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

31

okre

Ălone zadania, to najlepszym rozwiÈzaniem jest zdefiniowanie ich w konstruktorze. Przykïa-

dowo, metoda konstruktora klasy

Emailer

powoduje zdefiniowanie pustej tablicy

$recipients

oraz danych nadawcy.

Dost

Úp do wïaĂciwoĂci i metod z wewnÈtrz klasy

Czytelnik zapewne zastanawia si

Ú, w jaki sposób funkcje mogÈ uzyskaÊ dostÚp do wïaĂciwoĂci

klasy z poziomu danej klasy? Do tego celu s

ïuĝy nastÚpujÈca konstrukcja kodu:

public function setBody($body)
{
$this->body = $body;
}

W klasie znajduje si

Ú wïaĂciwoĂÊ prywatna o nazwie

$body

. Je

ĝeli zachodzi potrzeba uzyska-

nia dost

Úpu do niej z wewnÈtrz funkcji, wtedy naleĝy uĝyÊ sïowa kluczowego

$this

. Wymie-

nione s

ïowo kluczowe

$this

oznacza odniesienie do bie

ĝÈcego egzemplarza obiektu. Dlatego

te

ĝ, aby uzyskaÊ dostÚp do wïaĂciwoĂci

body

, trzeba zastosowa

Ê polecenie

$this->body

. Warto

zwróci

Ê uwagÚ, ĝe w celu uzyskania dostÚpu do wïaĂciwoĂci (na przykïad zmiennych) klasy

trzeba u

ĝyÊ operatora „

->

”, a nast

Úpnie nazwy egzemplarza.

Podobnie jak w przypadku w

ïaĂciwoĂci, takĝe dostÚp do metod klasy z poziomu innej metody

klasy odbywa si

Ú z pomocÈ przedstawionej powyĝej konstrukcji. Przykïadowo, wywoïanie me-

tody

setSubject

nast

Úpuje w przedstawiony sposób:

$this->setSubject()

.

Warto zwróci

Ê uwagÚ, ĝe sïowo kluczowe $this jest poprawne tylko w zasiÚgu metody, która nie zo-

sta

ïa zadeklarowana jako static (statyczna). Sïowa kluczowego $this nie moĝna uĝyÊ z zewnÈtrz kla-

sy. Wi

Úcej informacji na temat modyfikatorów static, private i public zostanie przedstawionych

w podrozdziale Modyfikatory znajduj

Ècym siÚ w dalszej czÚĂci rozdziaïu.

U

ĝywanie obiektu

Nadesz

ïa pora na uĝycie nowo utworzonego obiektu

Emailer

w kodzie PHP. W tym miejscu

trzeba doda

Ê, ĝe przed uĝyciem obiektu naleĝy wykonaÊ pewne przygotowania. Przede wszyst-

kim, zanim obiekt b

Údzie mógï zostaÊ uĝyty, wczeĂniej musi byÊ utworzony jego egzemplarz.

Po utworzeniu egzemplarza obiektu programista zyskuje dost

Úp do jego wszystkich publicz-

nych w

ïaĂciwoĂci i metod poprzez uĝycie operatora „

->

” po nazwie obiektu. W poni

ĝszym

fragmencie kodu przedstawiono przyk

ïadowe uĝycie obiektu:

<?
$emailerobject = new Emailer("hasin@pageflakes.com");
$emailerobject->addRecipients("hasin@somewherein.net");

background image

Programowanie obiektowe w PHP 5

32

$emailerobject->setSubject("To tylko test");
$emailerobject->setBody("CzeĂÊ Hasin,Jak siÚ miewasz?");
$emailerobject->sendEmail();
?>

Na powy

ĝszym fragmencie kodu pierwszym krokiem jest utworzenie egzemplarza klasy

Emailer

i przypisania go zmiennej o nazwie

$emailerobject

. Warto w tym miejscu zapami

ÚtaÊ bardzo

wa

ĝnÈ reguïÚ: podczas tworzenia nowego obiektu

Emailer

nale

ĝy podaÊ adres nadawcy. Caïy

wiersz jest wi

Úc nastÚpujÈcy:

$emailerobject = new Emailer("hasin@pageflakes.com");

Wynika to z faktu,

ĝe metoda konstruktora jest zdefiniowana w postaci

__construct($sender)

.

Jak wspomniano wcze

Ăniej, podczas tworzenia egzemplarza obiektu nastÚpuje automatyczne

wywo

ïanie konstruktora. Dlatego teĝ w trakcie ustanawiania klasy

Emailer

trzeba poda

Ê pra-

wid

ïowe argumenty, zgodnie z definicjÈ zawartÈ w metodzie konstruktora. Przykïadowo, wy-

konanie poni

ĝszego kodu spowoduje wygenerowanie komunikatu ostrzeĝenia:

<?
$emailer = new emailer();
?>

Po wykonaniu powy

ĝszego kodu na ekranie zostanie wyĂwietlony komunikat ostrzeĝenia:

Warning: Missing argument 1 for emailer::__construct(),
called in C:\OOP_PHP5\Kody\rozdzial1\class.emailer.php on line 42
and defined in <b>C:\OOP_PHP5\Kody\rozdzial1\class.emailer.php</b>
on line <b>9</b><br />

Teraz ró

ĝnica powinna byÊ doskonale widoczna. Jeĝeli klasa nie posiada metody konstruktora

b

Èdě konstruktor nie zawiera argumentów, wtedy egzemplarz obiektu moĝna utworzyÊ za po-

moc

È powyĝszego kodu.

Modyfikatory dost

Úpu

W omówionej wcze

Ăniej klasie zastosowano kilka sïów kluczowych, miÚdzy innymi

private

i

public

. Powstaje wi

Úc pytanie, co oznaczajÈ te sïowa kluczowe i dlaczego ich zastosowanie

w klasie jest konieczne? Ogólnie rzecz bior

Èc, wymienione sïowa kluczowe sÈ nazywane mody-

fikatorami dost

Úpu i zostaïy wprowadzone w PHP 5. Modyfikatory dostÚpu nie wystÚpowaïy

w PHP 4. Te s

ïowa kluczowe pomagajÈ programiĂcie w definiowaniu ograniczeñ w dostÚpno-

Ăci do zmiennych i wïaĂciwoĂci dla uĝytkowników danej klasy. Przekonajmy siÚ, w jaki sposób
mo

ĝna wykorzystaÊ dostÚpne modyfikatory dostÚpu.

Private

. W

ïaĂciwoĂci lub metody zadeklarowane z uĝyciem sïowa kluczowego

private

(pry-

watne) nie mog

È byÊ wywoïane z zewnÈtrz klasy. JednoczeĂnie dowolne metody wewnÈtrz tej

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

33

samej klasy mog

È bez problemu uzyskaÊ dostÚp do elementów prywatnych. W omawianej kla-

sie

Emailer

wszystkie w

ïaĂciwoĂci zostaïy zdefiniowane jako prywatne, dlatego teĝ wykonanie

poni

ĝszego kodu spowoduje wygenerowanie komunikatu bïÚdu:

<?
include_once("class.emailer.php");
$emobject = new Emailer("hasin@somewherein.net");
$emobject->subject = "Witaj Ăwiecie";
?>

Po wykonaniu powy

ĝszego kodu zostanie wygenerowany bïÈd krytyczny:

<b>Fatal error</b>: Cannot access private property emailer::$subject
in <b>C:\OOP_PHP5\Kody\rozdzial1\class.emailer.php</b> on line
<b>43</><br />

Oznacza to,

ĝe z zewnÈtrz klasy nie moĝna uzyskaÊ dostÚpu do jakiejkolwiek prywatnej wïaĂci-

wo

Ăci bÈdě metody.

Public

. Ka

ĝda wïaĂciwoĂÊ lub metoda, która nie zostaïa wyraěnie zdefiniowana z uĝyciem sïów

kluczowych

private

(prywatna) b

Èdě

protected

(chroniona), jest metod

È publicznÈ (

public

).

Dost

Úp do metod publicznych jest moĝliwy równieĝ z zewnÈtrz klasy.

Protected

. To jest kolejny modyfikator dost

Úpu, który ma znaczenie specjalne w programowa-

niu zorientowanym obiektowo. Je

ĝeli jakakolwiek wïaĂciwoĂÊ lub metoda zostanie zdefiniowa-

na z u

ĝyciem sïowa kluczowego

protected

, to dost

Úp do niej moĝna uzyskaÊ tylko z poziomu

podklasy. Wi

Úcej informacji dotyczÈcych podklas zostanie przedstawionych w dalszej czÚĂci

rozdzia

ïu. Aby zademonstrowaÊ, w jaki sposób dziaïa chroniona metoda lub wïaĂciwoĂÊ, po-

s

ïuĝymy siÚ kolejnym przykïadem.

Rozpoczynamy od otwarcia pliku class.emailer.php (czyli klasy

Emailer

) i zmieniamy deklara-

cj

Ú zmiennej

$sender

. Po zmianie definicja zmiennej powinna by

Ê nastÚpujÈca:

protected $sender

Nast

Úpnie tworzymy kolejny plik o nazwie class.extendedemailer.php, w którym powinien znaj-

dowa

Ê siÚ poniĝszy fragment kodu:

<?
class ExtendedEmailer extends emailer
{
function __construct(){}
public function setSender($sender)
{
$this->sender = $sender;
}
}
?>

background image

Programowanie obiektowe w PHP 5

34

Kolejny krok to u

ĝycie w nastÚpujÈcy sposób nowo utworzonego obiektu:

<?
include_once("class.emailer.php");
include_once("class.extendedemailer.php");
$xemailer = new ExtendedEmailer();
$xemailer->setSender("hasin@pageflakes.com");
$xemailer->addRecipients("hasin@somewherein.net");
$xemailer->setSubject("To tylko test ");
$xemailer->setBody("CzeĂÊ Hasin,Jak siÚ miewasz?");
$xemailer->sendEmail();
?>

Po dok

ïadnym przyjrzeniu siÚ kodowi klasy

ExtendedEmailer

czytelnik zauwa

ĝy, ĝe nastÚpuje

próba uzyskania dost

Úpu do wïaĂciwoĂci

$sender

jej klasy nadrz

Údnej (którÈ w rzeczywistoĂci

jest klasa

Emailer

). Dost

Úp do wymienionej wïaĂciwoĂci jest moĝliwy, poniewaĝ zostaïa zade-

klarowana jako chroniona. Dodatkow

È zaletÈ jest fakt, ĝe wïaĂciwoĂÊ

$sender

nadal pozostaje

bezpo

Ărednio niedostÚpna poza zasiÚgiem obu wymienionych klas. Oznacza to, ĝe próba wy-

konania poni

ĝszego fragmentu kodu spowoduje wygenerowanie bïÚdu krytycznego:

<?
include_once("class.emailer.php");
include_once("class.extendedemailer.php");
$xemailer = new ExtendedEmailer();
$xemailer->sender = "hasin@pageflakes.com";
?>

Po wykonaniu powy

ĝszego kodu zostanie wygenerowany bïÈd krytyczny:

<b>Fatal error</b>: Cannot access protected property
extendedEmailer::$sender in <b>C:\OOP_PHP5\Kody\rozdzial1\test.php
</b> on line <b>5</b><br />

Konstruktory i destruktory

We wcze

Ăniejszej czÚĂci rozdziaïu wspomniano o metodzie konstruktora. Wymieniony kon-

struktor to metoda specjalna, która jest automatycznie wykonywana podczas tworzenia egzem-
plarza klasy. W j

Úzyku PHP 5 istniejÈ dwa sposoby napisana metody konstruktora wewnÈtrz

klasy. Pierwszy z nich to po prostu zdefiniowanie w klasie metody o nazwie

__construct()

.

Natomiast drugim sposobem jest utworzenie metody o nazwie identycznej jak nazwa klasy.
Przyk

ïadowo, jeĂli klasa nosi nazwÚ

Emailer

, to nazw

È metody konstruktora bÚdzie

Emailer()

.

Przyjrzyjmy si

Ú poniĝszej klasie, której zadaniem jest obliczanie silni dowolnej liczby:

<?
// class.factorial.php
class factorial

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

35

{
private $result = 1;// Inicjalizacj

Ċ moĪna przeprowadziü bezpoĞrednio z zewnątrz.

private $number;
function __construct($number)
{
$this->number = $number;
for($i=2; $i<=$number; $i++)
{
$this->result *= $i;
}
}
public function showResult()
{
echo "Silnia liczby {$this->number} wynosi {$this->result}. ";
}
}
?>

Na powy

ĝszym fragmencie kodu do zdefiniowania konstruktora wykorzystano metodÚ

__con-

struct()

. Dzia

ïanie kodu pozostanie bez zmian, jeĂli nazwa metody

__construct()

zostanie

zmieniona na

factorial()

.

W tym miejscu mo

ĝe zrodziÊ siÚ pytanie, czy w klasie dopuszczalne jest uĝycie konstruktorów

zdefiniowanych za pomoc

È obu omówionych stylów? Oznacza to istnienie w klasie funkcji

o nazwie

__construct()

oraz funkcji o nazwie identycznej z nazw

È klasy. Który z konstruktorów

zostanie u

ĝyty w takim przypadku? A moĝe zostanÈ wykonane obie te funkcje? To sÈ bardzo

trafne i ciekawe pytania. Warto zapami

ÚtaÊ, ĝe w rzeczywistoĂci jednak nie ma moĝliwoĂci

wykonania obu funkcji. Je

ĝeli w klasie bÚdÈ zdefiniowane dwie metody konstruktora, to PHP

5 daje pierwsze

ñstwo funkcji

__construct()

, natomiast druga metoda konstruktora b

Údzie zi-

gnorowana. Spójrzmy na poni

ĝszy fragment kodu:

<?
// class.factorial.php
class Factorial
{
private $result = 1;
private $number;
function __construct($number)
{
$this->number = $number;
for($i=2; $i<=$number; $i++)
{
$this->result*=$i;
}
echo "Wykonano metodÚ __construct(). ";
}
function factorial($number)
{
$this->number = $number;

background image

Programowanie obiektowe w PHP 5

36

for($i=2; $i<=$number; $i++)
{
$this->result*=$i;
}
echo " Wykonano metodÚ factorial(). ";
}
public function showResult()
{
echo " Silnia liczby {$this->number} wynosi {$this->result}. ";
}
}
?>

Je

ĝeli powyĝsza klasa zostanie uĝyta w nastÚpujÈcy sposób:

<?
include_once("class.factorial.php");
$fact = new Factorial(5);
$fact->showResult();
?>

to na ekranie zostanie wy

Ăwietlony poniĝszy komunikat:

Wykonano metodÚ __construct().Silnia liczby 5 wynosi 120.

Podobnie do metody konstruktora w klasie wyst

Úpuje równieĝ metoda destruktora, która jest

wykonywana w trakcie niszczenia obiektu. Programista mo

ĝe wyraěnie utworzyÊ destruktora

poprzez zdefiniowanie metody o nazwie

__destruct()

. Wymieniona metoda zostanie automa-

tycznie wywo

ïana przez PHP na samym koñcu wykonywania danego skryptu. Aby sprawdziÊ,

w jaki sposób dzia

ïa destruktor, moĝna w omówionej powyĝej klasie dodaÊ metodÚ destruktora:

function __destruct()
{
echo "Obiekt zostaï zniszczony.";
}

Nast

Úpnie, po ponownym wykonaniu skryptu obliczajÈcego silniÚ, na ekranie zostanie wyĂwie-

tlony nast

ÚpujÈcy komunikat:

Wykonano metodÚ __construct(). Silnia liczby 5 wynosi 120. Obiekt zostaï
zniszczony.

Sta

ïe klasy

Czytelnik prawdopodobnie wie,

ĝe w skryptach PHP definiowanie staïej odbywa siÚ za po-

moc

È sïowa kluczowego

define

(definiowanie nazwy sta

ïej oraz jej wartoĂci). Jednak w celu

zdefiniowania sta

ïej w klasie uĝywa siÚ sïowa kluczowego

const

. W rzeczywisto

Ăci te staïe

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

37

funkcjonuj

È na zasadzie zmiennych statycznych, a jedyna róĝnica miÚdzy nimi polega na tym,

ĝe sÈ tylko do odczytu. Przykïad tworzenia i uĝywania staïych w klasie zostaï przedstawiony
na poni

ĝszym fragmencie kodu:

<?
class WordCounter
{
const ASC=1; // Przed sta

áą nie trzeba stosowaü znaku dolara ($).

const DESC=2;
private $words;
function __construct($filename)
{
$file_content = file_get_contents($filename);
$this->words =
(array_count_values(str_word_count(strtolower
($file_content),1)));
}
public function count($order)
{
if ($order==self::ASC)
asort($this->words);
else if($order==self::DESC)
arsort($this->words);
foreach ($this->words as $key=>$val)
echo $key ." = ". $val."<br/>";
}
}
?>

Powy

ĝsza klasa

WordCounter

powoduje zliczanie cz

ÚstotliwoĂci wystÚpowania sïów w podanym

pliku. W kodzie zdefiniowano dwie sta

ïe

ASC

i

DESC

o warto

Ăciach odpowiednio

1

i

2

. Aby we-

wn

Ètrz klasy uzyskaÊ dostÚp do staïej, naleĝy odnieĂÊ siÚ do niej za pomocÈ sïowa kluczowego

self

. Warto zwróci

Ê uwagÚ, ĝe dostÚp do staïej nastÚpuje za pomocÈ operatora

::

, a nie ope-

ratora

->

. Wynika to z faktu,

ĝe staïe dziaïajÈ na zasadzie podobnej do elementów statycznych.

W celu u

ĝycia powyĝszej klasy trzeba wykorzystaÊ przedstawiony poniĝej fragment kodu. Za-

prezentowano w nim równie

ĝ sposób dostÚpu do staïej:

<?
include_once("class.wordcounter.php");
$wc = new WordCounter("words.txt");
$wc->count(WordCounter::DESC);
?>

Warto zwróci

Ê uwagÚ, ĝe dostÚp do staïej klasy nastÚpuje z zewnÈtrz klasy za pomocÈ operato-

ra

::

umieszczonego tu

ĝ za nazwÈ klasy, a nie za nazwÈ egzemplarza klasy. Kolejnym krokiem,

który trzeba wykona

Ê w celu przetestowania omówionego skryptu, jest utworzenie pliku tek-

stowego words.txt. Wymieniony plik musi znajdowa

Ê siÚ w tym samym katalogu, w którym

umieszczono skrypt:

background image

Programowanie obiektowe w PHP 5

38

Plik: words.txt

Wordpress jest silnikiem bloga dostepnym na licencji open source. Czytelnikom
nieznajacym blogów wyjasniamy, ze blog pozwala uzytkownikowi na prowadzenie
dziennika w Internecie. Wordpress jest zupelnie bezplatny i zostal wydany
na licencji GPL.

Po wykonaniu skryptu z podanym powy

ĝej plikiem zostanÈ wyĂwietlone nastÚpujÈce dane

wyj

Ăciowe:

na = 3
licencji = 2
wordpress = 2
blog = 2
w = 2
jest = 2
internecie = 1
dziennika = 1
prowadzenie = 1
zupelnie = 1
bezplatny = 1
gpl = 1
wydany = 1
zostal = 1
i = 1
uzytkownikowi = 1
pozwala = 1
source = 1
open = 1
bloga = 1
czytelnikom = 1
nieznajacym = 1
ze = 1
wyjasniamy = 1
silnikiem = 1
dostepnym = 1

U

ĝyteczny skrypt, nieprawdaĝ?

Rozszerzanie klasy (dziedziczenie)

Jedn

È z najistotniejszych funkcji programowania zorientowanego obiektowo jest moĝliwoĂÊ

rozszerzenia klasy oraz utworzenie zupe

ïnie nowego obiektu. Ten nowo utworzony obiekt

b

Údzie posiadaï wszystkie funkcje obiektu nadrzÚdnego, które bÚdÈ mogïy zostaÊ rozbudowa-

ne b

Èdě nadpisane. Nowy obiekt moĝe takĝe zawieraÊ zupeïnie nowe funkcje. Na poniĝszym

fragmencie kodu rozszerzono przedstawion

È wczeĂniej klasÚ

Emailer

oraz nadpisano funkcj

Ú

sendEmail

, która obecnie ma mo

ĝliwoĂÊ wysyïania wiadomoĂci e-mail w formacie HTML.

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

39

<?
class HtmlEmailer extends emailer
{
public function sendHTMLEmail()
{
foreach ($this->recipients as $recipient)
{
$headers = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=iso-8859-2' .
"\r\n";
$headers .= 'From: {$this->sender}' . "\r\n";
$result = mail($recipient, $this->subject, $this->body,
$headers);
if ($result) echo "WiadomoĂÊ w formacie HTML zostaïa wysïana do
{$recipient}<br/>";
}
}
}
?>

Poniewa

ĝ nowa klasa rozszerza klasÚ

Emailer

oraz wprowadza now

È funkcjÚ o nazwie

send-

HTMLEmail()

, to programista nadal posiada dost

Úp do wszystkich metod obecnych w klasie nad-

rz

Údnej. Oznacza to, ĝe przedstawiony poniĝej fragment kodu jest jak najbardziej prawidïowy:

<?
include_once("class.htmlemailer.php");
$hm = new HtmlEmailer();
// ...miejsce na inne zadania...
$hm->sendEmail();
$hm->sendHTMLEmail();
?>

Je

ĝeli zachodzi potrzeba uzyskania dostÚpu do dowolnej metody klasy nadrzÚdnej (inaczej

nazywanej superklas

È), to moĝna uĝyÊ sïowa kluczowego

parent

. Przyk

ïadowo, jeĝeli progra-

mista chce uzyska

Ê dostÚp do metody o nazwie

sayHello

, to nale

ĝy wydaÊ polecenie

parent::

sayHello();

.

Warto zwróci

Ê uwagÚ, ĝe w klasie

HtmlEmailer

nie zosta

ïa zdefiniowana funkcja o nazwie

send-

Email()

. Natomiast wymieniona metoda dzia

ïa z klasy nadrzÚdnej, czyli

Emailer

.

W omówionym powy

ĝej przykïadzie klasa HtmlEmailer jest podklasÈ klasy Emailer, natomiast klasa

Emailer to superklasa dla klasy HtmlEmailer. Trzeba zapamiÚtaÊ, ĝe jeĂli podklasa nie posiada kon-
struktora, to zostanie u

ĝyta metoda konstruktora klasy nadrzÚdnej. W trakcie pisania niniejszej ksiÈĝki

wielokrotne dziedziczenie na poziomie klasy nie by

ïo obsïugiwane. Oznacza to, ĝe nie moĝna jednocze-

Ănie dziedziczyÊ z wiÚcej niĝ tylko jednej klasy. Jednak wielokrotne dziedziczenie jest obsïugiwane w in-
terfejsach. Dlatego te

ĝ interfejs moĝe rozszerzaÊ dowolnÈ liczbÚ interfejsów.

background image

Programowanie obiektowe w PHP 5

40

Nadpisywanie metod

W rozszerzanym obiekcie mo

ĝna nadpisaÊ dowolnÈ metodÚ (zdefiniowanÈ jako chronionÈ lub

publiczn

È) i dowolnie zmieniaÊ sposób jej dziaïania. W jaki wiÚc sposób moĝna nadpisaÊ do-

woln

È metodÚ? Wystarczy po prostu utworzyÊ funkcjÚ o takiej samej nazwie jak ta, która ma

zosta

Ê nadpisana. Przykïadowo, po utworzeniu w klasie

HtmlEmailer

funkcji o nazwie

sendEmail

spowoduje ona nadpisanie metody

sendEmail()

zdefiniowanej w klasie nadrz

Údnej

Emailer

.

Je

ĝeli w podklasie zostanie zdefiniowana zmienna, która istnieje takĝe w superklasie, to pod-

czas dost

Úpu do zmiennej zostanie uĝyta ta zdefiniowana w podklasie.

Uniemo

ĝliwianie nadpisywania

Je

ĝeli metoda zostanie zdefiniowana z uĝyciem sïowa kluczowego

final

, to nie b

Údzie mogïa

zosta

Ê nadpisana w ĝadnej podklasie. Dlatego teĝ, jeĝeli programista nie chce, aby dana meto-

da by

ïa nadpisywana, to wystarczy zdefiniowaÊ jÈ jako

final

. Poni

ĝej pokazano definicjÚ me-

tody z u

ĝyciem sïowa kluczowego

final

:

<?
class SuperClass
{
public final function someMethod()
{
// ...miejsce na dowolny kod...
}
}
class SubClass extends SuperClass
{
public function someMethod()
{
// ...miejsce na dowolny kod, ale i tak nie zostanie on wykonany...
}
}
?>

Je

ĝeli powyĝszy kod zostanie wykonany, to spowoduje wygenerowanie bïÚdu krytycznego, po-

niewa

ĝ klasa

SubClass

próbuje nadpisa

Ê metodÚ z klasy nadrzÚdnej

SuperClass

, która zosta

ïa

zdefiniowana z u

ĝyciem sïowa kluczowego

final

.

Uniemo

ĝliwianie rozszerzania

Podobnie jak w przypadku metody zdefiniowanej jako

final

, tak

ĝe klasÚ moĝna zdefiniowaÊ

z u

ĝyciem sïowa kluczowego

final

, które uniemo

ĝliwi jej rozszerzanie. Dlatego teĝ po zde-

finiowaniu klasy w sposób przedstawiony na poni

ĝszym listingu nie bÚdzie moĝna jej dalej

rozszerza

Ê:

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

41

<?
final class aclass
{
}
class bclass extends aclass
{
}
?>

Po wykonaniu powy

ĝszego kodu zostanie wygenerowany nastÚpujÈcy bïÈd krytyczny:

<b>Fatal error</b>: Class bclass may not inherit from final class
(aclass) in <b>C:\OOP_PHP5\Kody\rozdzial1\class.aclass.php</b> on
line <b>8</b><br />

Polimorfizm

Jak ju

ĝ wspomniano we wczeĂniejszej czÚĂci ksiÈĝki, polimorfizm jest procesem tworzenia

kilku obiektów z okre

Ălonych klas bazowych. Przykïadowo, warto spojrzeÊ na poniĝszy przy-

k

ïad, w którym wykorzystano wszystkie trzy klasy omówione dotychczas w rozdziale

Emailer

,

ExtendedEmailer

oraz

HtmlEmailer

:

<?
include("class.emailer.php");
include("class.extendedemailer.php");
include("class.htmlemailer.php");
$emailer = new Emailer("hasin@somewherein.net");
$extendedemailer = new ExtendedEmailer();
$htmlemailer = new HtmlEmailer("hasin@somewherein.net");
if ($extendedemailer instanceof emailer)
echo "Klasa Extended Emailer wywodzi siÚ z klasy Emailer.<br/>";
if ($htmlemailer instanceof emailer)
echo "Klasa HTML Emailer równieĝ wywodzi siÚ z klasy Emailer.<br/>";
if ($emailer instanceof htmlEmailer)
echo "Klasa Emailer wywodzi siÚ z klasy HTMLEmailer.<br/>";
if ($htmlemailer instanceof extendedEmailer)
echo "Klasa HTML Emailer wywodzi siÚ z klasy Emailer.<br/>";
?>

Po wykonaniu powy

ĝszego fragmentu kodu zostanÈ wyĂwietlone nastÚpujÈce dane wyjĂciowe:

Klasa Extended Emailer wywodzi siÚ z klasy Emailer.
Klasa HTML Emailer równieĝ wywodzi siÚ z klasy Emailer.

To jest przyk

ïad polimorfizmu.

background image

Programowanie obiektowe w PHP 5

42

Dzi

Úki zastosowaniu operatora instanceof zawsze istnieje moĝliwoĂÊ sprawdzenia, czy klasa wywodzi

si

Ú z innej klasy.

Interfejs

Interfejs jest pust

È klasÈ, która zawiera jedynie deklaracje metod. Dlatego teĝ kaĝda klasa im-

plementuj

Èca dany interfejs musi zawieraÊ deklaracje zawartych w nim funkcji. Interfejs jest

wi

Úc jedynie zbiorem ĂciĂle okreĂlonych reguï, które pomagajÈ w rozszerzaniu dowolnej klasy

oraz

Ăcisïej implementacji wszystkich metod zadeklarowanych w interfejsie. Klasa moĝe sto-

sowa

Ê dowolny interfejs, uĝywajÈc sïowa kluczowego

implements

. Warto zwróci

Ê uwagÚ, ĝe

w interfejsie mo

ĝna jedynie zadeklarowaÊ metody, ale nie moĝna umieĂciÊ w nim definicji

tych

ĝe metod. Oznacza to, ĝe w interfejsie czÚĂci gïówne wszystkich metod pozostajÈ puste.

Powstaje wi

Úc pytanie, do czego moĝe sïuĝyÊ interfejs? Jednym z powodów jego stosowania

jest mo

ĝliwoĂÊ implementacji ĂciĂle okreĂlonych reguï podczas definicji klasy. Przykïadowo,

programista wie,

ĝe musi utworzyÊ klasy pewnego sterownika dla programu, które bÚdÈ za-

wiera

ïy operacje zwiÈzane z bazÈ danych. Dla bazy danych MySQL bÚdzie to jedna klasa, dla

PostgreSQL b

Údzie to kolejna klasa, dla SQLite kolejna, itd. W takim przypadku zespóï pro-

gramistów mo

ĝe liczyÊ trzy osoby, z których kaĝda bÚdzie oddzielnie tworzyïa wskazanÈ klasÚ.

Mo

ĝna teraz zadaÊ sobie pytanie, jaki byïby wynik pracy tych programistów, gdyby kaĝdy

z nich implementowa

ï w klasie wïasny styl? Inni programiĂci, którzy chcieliby wykorzystaÊ te

klasy sterowników, musieliby pozna

Ê definicje uĝytych metod, a nastÚpnie stosowaÊ taki sam

styl, aby móc je wykorzysta

Ê we wïasnym kodzie. Takie rozwiÈzanie staje siÚ wyjÈtkowo trud-

ne w obs

ïudze. Dlatego teĝ moĝna po prostu ustaliÊ, ĝe kaĝda klasa sterownika musi posiadaÊ

dwie metody o nazwach

connect()

i

execute()

. W takim przypadku programi

Ăci nie muszÈ

przejmowa

Ê siÚ wewnÚtrznÈ strukturÈ sterownika, poniewaĝ doskonale wiedzÈ, ĝe wszystkie

klasy posiadaj

È takie same definicje metod. Interfejs stanowi wiÚc duĝe uïatwienie podczas

pracy nad tego rodzaju projektem. Poni

ĝej przedstawiono kod przykïadowego interfejsu:

<?
// interface.dbdriver.php
interface DBDriver
{
public function connect();
public function execute($sql);
}
?>

Czy czytelnik zwróci

ï uwagÚ na fakt, ĝe w interfejsie definicje funkcji sÈ puste? Kolejny krok to

utworzenie klasy

MySQLDriver

, która b

Údzie implementowaïa przedstawiony powyĝej interfejs:

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

43

<?
// class.mysqldriver.php
include("interface.dbdriver.php");
class MySQLDriver implements DBDriver
{
}
?>

Je

ĝeli powyĝszy kod zostanie uruchomiony, to spowoduje wygenerowanie poniĝszego ko-

munikatu b

ïÚdu. Wynika to z faktu, ĝe w klasie

MySQLDriver

nie zosta

ïy zdefiniowane funkcje

connect()

i

execute()

, które s

È zadeklarowane w interfejsie. Warto wiÚc uruchomiÊ kod i od-

czyta

Ê komunikat bïÚdu:

<b>Fatal error</b>: Class MySQLDriver contains 2 abstract methods
and must therefore be declared abstract or implement the remaining
methods (DBDriver::connect, DBDriver::execute) in
<b>C:\OOP_PHP5\Kody\rozdzial1\class.mysqldriver.php</b> on line <b>5</b><br />

Kolejny krok to dodanie do klasy

MySQLDriver

dwóch metod. Po wprowadzeniu zmian kod

przedstawia si

Ú nastÚpujÈco:

<?
include("interface.dbdriver.php");
class MySQLDriver implements DBDriver
{
public function connect()
{
// Nawi

ązanie poáączenia z bazą danych.

}
public function execute()
{
// Wykonanie zapytania i wy

Ğwietlenie jego wyników.

}
}
?>

Po uruchomieniu powy

ĝszego kodu na ekranie ponownie zostanie wyĂwietlony komunikat

b

ïÚdu:

<b>Fatal error</b>: Declaration of MySQLDriver::execute() must be
compatible with that of DBDriver::execute() in
<b>C:\OOP_PHP5\Kody\rozdzial1\class.mysqldriver.php</b> on line <b>3</b><br />

Ten komunikat informuje u

ĝytkownika, ĝe metoda

execute()

nie jest zgodna ze struktur

È me-

tody

execute()

, która zosta

ïa zadeklarowana w interfejsie. Po dokïadnym przyjrzeniu siÚ inter-

fejsowi czytelnik zauwa

ĝy, ĝe metoda

execute()

powinna posiada

Ê jeden argument. Oznacza

to,

ĝe w trakcie implementacji interfejsu w tworzonych klasach kaĝda struktura metody musi

by

Ê dokïadnie taka sama jak zadeklarowana w interfejsie. Po przepisaniu klasy

MySQLDriver

jej

kod przedstawia si

Ú nastÚpujÈco:

background image

Programowanie obiektowe w PHP 5

44

<?
include("interface.dbdriver.php");
class MySQLDriver implements DBDriver
{
public function connect()
{
// Nawi

ązanie poáączenia z bazą danych.

}
public function execute($query)
{
// Wykonanie zapytania i wy

Ğwietlenie jego wyników.

}
}
?>

Klasa abstrakcyjna

Klasa abstrakcyjna jest niemal tak

È samÈ konstrukcjÈ jak interfejs, za wyjÈtkiem faktu, ĝe de-

klarowane w niej metody mog

È posiadaÊ definicje. Ponadto klasa abstrakcyjna musi byÊ „roz-

szerzana”, a nie „implementowana”. Dlatego te

ĝ, jeĝeli rozszerzane klasy posiadajÈ metody

o takich samych funkcjach, to te funkcje mo

ĝna zdefiniowaÊ w klasie abstrakcyjnej. Poniĝej

przedstawiono przyk

ïad klasy abstrakcyjnej:

<?
// abstract.reportgenerator.php
abstract class ReportGenerator
{
public function generateReport($resultArray)
{
// Miejsce na kod przetwarzaj

ący wielowymiarową tablicĊ wynikową oraz

// generuj

ący raport w postaci kodu HTML.

}
}
?>

W powy

ĝszej klasie abstrakcyjnej znajduje siÚ metoda o nazwie

generateRaport

, która jako ar-

gument pobiera wielowymiarow

È tablicÚ, a nastÚpnie na jej podstawie generuje raport w po-

staci kodu HTML. Powstaje zatem pytanie, dlaczego ta metoda zosta

ïa umieszczona w klasie

abstrakcyjnej? Odpowied

ě jest prosta — poniewaĝ generowanie raportu bÚdzie wspólnÈ funk-

cj

È wszystkich sterowników baz danych. Sama funkcja nie wpïywa równieĝ na kod sterowni-

ka, poniewa

ĝ jako argument pobiera tablicÚ i nie ma nic wspólnego z bazÈ danych. Dlatego

te

ĝ w przedstawionym poniĝej kodzie klasy

MySQLDriver

zastosowano klas

Ú abstrakcyjnÈ. Warto

zwróci

Ê uwagÚ, ĝe caïy kod odpowiedzialny za generowanie raportu zostaï juĝ wczeĂniej na-

pisany. Nie trzeba wi

Úc umieszczaÊ go ponownie w klasie sterownika, jak miaïoby to miejsce

w przypadku interfejsu.

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

45

<?
include("interface.dbdriver.php");
include("abstract.reportgenerator.php");
class MySQLDriver extends ReportGenerator implements DBDriver
{
public function connect()
{
// Nawi

ązanie poáączenia z bazą danych.

}
public function execute($query)
{
// Wykonanie zapytania i wy

Ğwietlenie jego wyników.

}
// Nie trzeba w tym miejscu ponownie definiowa

ü lub umieszczaü metody generateReport

// poniewa

Ī ta klasa bezpoĞrednio rozszerza klasĊ abstrakcyjną.

}
?>

Warto zwróci

Ê uwagÚ, ĝe jednoczeĂnie moĝna zarówno uĝywaÊ klasy abstrakcyjnej, jak i im-

plementowa

Ê interfejs. Zostaïo to przedstawione na powyĝszym fragmencie kodu.

Klas

Ú abstrakcyjnÈ (abstract) nie moĝna zdefiniowaÊ za pomocÈ sïowa kluczowego final, poniewaĝ

klasa abstrakcyjna musi by

Ê rozszerzana. Natomiast sïowo kluczowe final uniemoĝliwia rozszerzenie

tak zdefiniowanej klasy. Dlatego te

ĝ jednoczesne uĝycie tych dwóch wymienionych sïów kluczowych jest

bezsensowne i j

Úzyk PHP na to nie pozwala.

Oprócz zdefiniowania klasy jako abstrakcyjnej tak

ĝe i metodÚ moĝna zdefiniowaÊ z uĝyciem

s

ïowa kluczowego

abstract

. Zdefiniowanie metody abstrakcyjnej oznacza,

ĝe podklasy muszÈ

nadpisywa

Ê tÚ metodÚ. W deklaracji metody abstrakcyjnej nie powinna znajdowaÊ siÚ jej de-

finicja. Przyk

ïad deklaracji metody abstrakcyjnej zostaï przedstawiony poniĝej:

abstract public function connectDB();

Metody i w

ïaĂciwoĂci statyczne

S

ïowo kluczowe

static

jest istotne w programowaniu zorientowanym obiektowo. Metody i w

ïa-

ĂciwoĂci statyczne peïniÈ bardzo waĝnÈ rolÚ zarówno w projekcie programu, jak i wzorcach
projektowych. Czym wi

Úc sÈ metody i wïaĂciwoĂci statyczne?

Jak wcze

Ăniej przedstawiono, w celu uzyskania dostÚpu do dowolnej metody bÈdě atrybutu

klasy wcze

Ăniej trzeba utworzyÊ jej egzemplarz (na przykïad za pomocÈ sïowa kluczowego

new

, czyli

$object = new emailer()

). W przeciwnym razie nie b

Údzie moĝna uzyskaÊ dostÚpu

do metod i w

ïaĂciwoĂci danej klasy. Istnieje jednak odstÚpstwo od tej reguïy i dotyczy metod

i w

ïaĂciwoĂci statycznych. Do metody lub wïaĂciwoĂci statycznej programista moĝe uzyskaÊ

background image

Programowanie obiektowe w PHP 5

46

dost

Úp bezpoĂrednio bez potrzeby tworzenia egzemplarza danej klasy. Element statyczny jest

wi

Úc podobny do elementu globalnego danej klasy i wszystkich jej egzemplarzy. Ponadto wïa-

ĂciwoĂci statyczne zachowujÈ stan z ostatniego przypisania, co w niektórych sytuacjach jest
bardzo u

ĝyteczne.

Czytelnik mo

ĝe zadaÊ pytanie, dlaczego ktokolwiek chciaïby uĝywaÊ metod statycznych? Cóĝ,

s

È one bardzo podobne do metod pomocniczych. WykonujÈ wiÚc ĂciĂle okreĂlone zadanie lub

zwracaj

È ĂciĂle okreĂlony obiekt. (WïaĂciwoĂci i metody statyczne sÈ intensywnie uĝywane we

wzorcach projektowych, co zostanie przedstawione w dalszej cz

ÚĂci rozdziaïu). Z tego powodu

deklarowanie nowego obiektu za ka

ĝdym razem do wykonania takiego zadania moĝe byÊ uzna-

ne za niepotrzebne zu

ĝywanie zasobów. Spójrzmy wiÚc na przykïad uĝycia metod statycznych.

Wró

Êmy do omawianego wczeĂniej programu, który zajmuje siÚ obsïugÈ trzech baz danych

— MySQL, PostgreSQL i SQLite. Zak

ïadamy, ĝe w danej chwili zachodzi potrzeba uĝywania

tylko jednego sterownika. W tym celu tworzymy klas

Ú

DBManager

, której zadaniem jest utwo-

rzenie egzemplarza dowolnego sterownika oraz jego zwrócenie programi

Ăcie.

<?
// class.dbmanager.php
class DBManager
{
public static function getMySQLDriver()
{
// Utworzenie nowego egzemplarza obiektu sterownika bazy danych MySQL i jego
// zwrócenie.
}
public static function getPostgreSQLDriver()
{
// Utworzenie nowego egzemplarza obiektu sterownika bazy danych PostgreSQL i jego
// zwrócenie.
}
public static function getSQLiteDriver()
{
// Utworzenie nowego egzemplarza obiektu sterownika bazy danych SQLite i jego
// zwrócenie.
}
}
?>

W jaki sposób mo

ĝna uĝyÊ powyĝszÈ klasÚ? DostÚp do dowolnej wïaĂciwoĂci statycznej odby-

wa si

Ú poprzez operator

::

zamiast operatora

->

. Przyk

ïad uĝycia klasy

DBManager

zosta

ï przed-

stawiony poni

ĝej:

<?
// test.dbmanager.php
include_once("class.dbmanager.php");
$dbdriver = DBManager::getMySQLDriver();
// Miejsce na kod przetwarzaj

ący operacje bazy danych za pomocą obiektu $dbdriver.

?>

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

47

Warto zwróci

Ê uwagÚ, ĝe w kodzie nie nastÚpuje tworzenie nowego egzemplarza obiektu

DBManager

, na przyk

ïad za pomocÈ polecenia

$dbmanager = new DBManager()

. Zamiast tego,

u

ĝywajÈc operatora

::

programista uzyskuje bezpo

Ăredni dostÚp do jednej z metod wymienio-

nego obiektu.

Co zyskuje programista, stosuj

Èc tego typu rozwiÈzanie? Ogólnie rzecz biorÈc, skoro po pro-

stu potrzebny jest obiekt sterownika, to nie ma potrzeby tworzenia nowego obiektu

DBManager

i zu

ĝywania przez niego pamiÚci aĝ do chwili zakoñczenia dziaïania skryptu. Metoda statycz-

na zwykle wykonuje swoje zadanie, a nast

Úpnie koñczy dziaïanie.

Trzeba zapami

ÚtaÊ jednÈ bardzo waĝnÈ kwestiÚ. WewnÈtrz metody statycznej nie moĝna uĝy-

wa

Ê pseudoobiektu

$this

. Poniewa

ĝ nie jest tworzony egzemplarz klasy, to sïowo kluczowe

$this

nie istnieje wewn

Ètrz metody statycznej. Zamiast niego naleĝy stosowaÊ sïowo kluczo-

we

self

.

Spójrzmy na poni

ĝszy fragment kodu, w którym zademonstrowano rzeczywisty sposób dziaïa-

nia w

ïaĂciwoĂci statycznej:

<?
// class.statictester.php
class StaticTester
{
private static $id=0;
function __construct()
{
self::$id +=1;
}
public static function checkIdFromStaticMehod()
{
echo "BieĝÈce Id z metody statycznej wynosi ".self::$id."\n";
}
public function checkIdFromNonStaticMethod()
{
echo " BieĝÈce Id z metody niestatycznej wynosi ".self::$id."\n";
}
}
$st1 = new StaticTester();
StaticTester::checkIdFromStaticMehod();
$st2 = new StaticTester();
$st1->checkIdFromNonStaticMethod(); // Zwrot warto

Ğci $id jako 2.

$st1->checkIdFromStaticMehod();
$st2->checkIdFromNonStaticMethod();
$st3 = new StaticTester();
StaticTester::checkIdFromStaticMehod();
?>

Po uruchomieniu powy

ĝszego kodu zostanÈ wyĂwietlone nastÚpujÈce dane wyjĂciowe:

background image

Programowanie obiektowe w PHP 5

48

BieĝÈce Id z metody statycznej wynosi 1
BieĝÈce Id z metody niestatycznej wynosi 2
BieĝÈce Id z metody statycznej wynosi 2
BieĝÈce Id z metody niestatycznej wynosi 2
BieĝÈce Id z metody statycznej wynosi 3

Kiedy tylko zostanie utworzony nowy egzemplarz obiektu, b

Údzie on wpïywaï na pozostaïe

egzemplarze, poniewa

ĝ zmienna zostaïa zdefiniowana jako statyczna. Uĝywanie tej moĝliwoĂci,

czyli specjalnego wzorca projektowego o nazwie „Singleton”, doskonale sprawdza si

Ú w PHP.

Ostrze

ĝenie dotyczÈce uĝywania elementów statycznych

Elementy statyczne powoduj

È, ĝe obiekty zachowujÈ siÚ w sposób podobny do proceduralnego stylu

dzia

ïania. Bez tworzenia egzemplarzy programista moĝe bezpoĂrednio wywoïaÊ dowolnÈ funkcjÚ, po-

dobnie jak w programowaniu proceduralnym. Z tego powodu metody statyczne powinny by

Ê uĝywane

z zachowaniem ostro

ĝnoĂci. Nadmierne korzystanie z metod statycznych jest nieuĝyteczne. O ile nie za-

chodzi taka konieczno

ĂÊ, to naleĝy unikaÊ uĝywania elementów statycznych.

Metody akcesorów

Metody akcesorów to po prostu metody, których zadaniem jest pobieranie i ustalanie warto

Ăci

dowolnej w

ïaĂciwoĂci klasy. Dobrym nawykiem jest uzyskiwanie dostÚpu do wïaĂciwoĂci kla-

sy za pomoc

È metod akcesorów zamiast bezpoĂredniego ustalania lub pobierania ich wartoĂci.

Chocia

ĝ metody akcesorów sÈ takie same jak inne metody, to jednak istniejÈ pewne konwen-

cje ich tworzenia.

Dost

Úpne sÈ dwa rodzaje metod akcesorów. Pierwszy z nich nosi nazwÚ

getter

, a celem tej

metody jest pobranie warto

Ăci dowolnej wïaĂciwoĂci klasy. Drugi rodzaj metody nosi nazwÚ

setter

i s

ïuĝy do ustalania wartoĂci dowolnej wïaĂciwoĂci klasy. Poniĝej zaprezentowano przy-

k

ïadowe metody

getter

i

setter

u

ĝywane do operacji na wïaĂciwoĂciach klasy:

<?
class Student
{
private $name;
private $roll;
public function setName($name)
{
$this->name= $name;
}
public function setRoll($roll)
{
$this->roll =$roll;
}

background image

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

49

public function getName()
{
return $this->name;
}
public function getRoll()
{
return $this->roll;
}
}
?>

W powy

ĝszym fragmencie kodu zastosowano po dwie metody typu

getter

oraz

setter

. To jest

konwencja pisania metod akcesorów. Metoda typu

setter

powinna rozpoczyna

Ê siÚ sïowem

kluczowym

set

, a nast

Úpnie zawieraÊ nazwÚ wïaĂciwoĂci, której pierwsza litera jest duĝa. Po-

dobnie, metoda typu

getter

powinna rozpoczyna

Ê siÚ sïowem kluczowym

get

, a nast

Úpnie

zawiera

Ê nazwÚ zmiennej, w której pierwsza litera jest duĝa. Oznacza to, ĝe jeĂli nazwÈ wïa-

ĂciwoĂci jest

email

, to metoda typu

getter

powinna by

Ê nazwana

getEmail

, natomiast metoda

typu

setter

powinna mie

Ê nazwÚ

setEmail

. I to tyle!

Czytelnik mo

ĝe w tym miejscu zapytaÊ, dlaczego ktokolwiek mógïby chcieÊ wykonywaÊ do-

datkow

È pracÚ, definiujÈc te metody, skoro zmienne moĝna zdefiniowaÊ jako publiczne, a resztÚ

pozostawi

Ê bez zmian? Czy to nie bÚdzie miaïo takiego samego efektu? Ogólnie rzecz ujmujÈc,

nie. U

ĝywajÈc metod akcesorów, programista otrzymuje dodatkowe korzyĂci. Przede wszyst-

kim zachowuje pe

ïnÈ kontrolÚ nad ustalaniem i pobieraniem wartoĂci dowolnej wïaĂciwoĂci.

„I co z tego?” — móg

ïby zapytaÊ czytelnik. Zaïóĝmy, ĝe zachodzi potrzeba zastosowania filtrów

danych wej

Ăciowych uĝytkownika przed ustawieniem wartoĂci wïaĂciwoĂci. W takim przypad-

ku metoda typu

setter

pozwala na filtrowanie danych wej

Ăciowych przed ich ustawieniem

i u

ĝyciem w programie.

Czy je

Ăli w klasie znajduje siÚ 100 wïaĂciwoĂci, to programista musi napisaÊ po sto metod typu

getter

i

setter

? To bardzo dobre pytanie. J

Úzyk PHP jest na tyle elegancki, ĝe wyrÚcza pro-

gramist

Ú z takiego ĝmudnego zadania. W jaki sposób? Odpowiedě na to pytanie znajduje siÚ

w kolejnym podrozdziale, w którym zostan

È omówione metody magiczne sïuĝÈce do dynamicz-

nego pobierania i ustalania warto

Ăci wïaĂciwoĂci. Uĝywanie tego rodzaju metod powoduje re-

dukcj

Ú o okoïo 90% pracy zwiÈzanej z koniecznoĂciÈ ĝmudnego pisania kodu metod akcesorów.

A

ĝ trudno w to uwierzyÊ, nieprawdaĝ? JeĂli tak, to warto siÚ o tym przekonaÊ samodzielnie.

U

ĝywanie metod magicznych do pobierania

i ustalania warto

Ăci wïaĂciwoĂci klasy

Jak wspomniano w poprzednim podrozdziale, pisanie du

ĝej liczby metod akcesorów dla wïa-

ĂciwoĂci klasy moĝe byÊ prawdziwym koszmarem. Aby uniknÈÊ tego nudnego zadania, moĝna
wykorzysta

Ê metody magiczne. Taki proces nosi nazwÚ przeciÈĝania metody.

background image

Programowanie obiektowe w PHP 5

50

W PHP 5 wprowadzono w klasach kilka metod magicznych, które znacznie u

ïatwiajÈ pracÚ

w niektórych zadaniach wykonywanych w OOP. Dwie z tych metod s

ïuĝÈ do dynamicznego

pobierania i ustalania warto

Ăci w klasie. Wspomniane metody noszÈ nazwy

__get()

oraz

__set()

.

Przyk

ïad ich uĝycia zostaï przedstawiony w poniĝszym fragmencie kodu:

<?
// class.student.php
class Student
{
private $properties = array();
function __get($property)
{
return $this->properties[$property];
}
function __set($property, $value)
{
$this->properties[$property]="AutoSet {$property} jako: ".$value;
}
}
?>

Kolejny krok to u

ĝycie tego kodu w programie. Powyĝsza klasa zostaje wiÚc zastosowana w po-

ni

ĝszym skrypcie:

<?
$st = new Student();
$st->name = "Afif";
$st->roll=16;
echo $st->name."\n";
echo $st->roll;
?>

Po wykonaniu kodu PHP natychmiast rozpozna,

ĝe w klasie nie istniejÈ wïaĂciwoĂci

name

i

roll

.

Poniewa

ĝ nazwy wïaĂciwoĂci istniejÈ, to nastÈpi wywoïanie metody

__set()

, która nast

Úpnie

przypisze warto

ĂÊ nowo utworzonej wïaĂciwoĂci klasy. Na ekranie zostanÈ wiÚc wyĂwietlone

nast

ÚpujÈce dane wyjĂciowe:

AutoSet name jako: Afif
AutoSet roll jako: 16

Wygl

Èda to caïkiem interesujÈco, nieprawdaĝ? UĝywajÈc metod magicznych, programista wciÈĝ

zachowuje pe

ïnÈ kontrolÚ nad ustawianiem i pobieraniem wartoĂci wïaĂciwoĂci klasy. Stosowa-

nie metod magicznych wi

Èĝe siÚ jednak z jednym ograniczeniem. Podczas uĝywania Reflection

API nie ma mo

ĝliwoĂci badania wïaĂciwoĂci klasy (wymienione Reflection API zostanie przed-

stawione w jednym z kolejnych rozdzia

ïów). Ponadto, sama klasa traci nieco ze swojej „czytel-

no

Ăci” oraz „ïatwoĂci obsïugi”. Dlaczego? Warto spojrzeÊ na poprzedniÈ i nowÈ klasÚ

Student

,

aby samodzielnie odpowiedzie

Ê sobie na to pytanie.

background image

Czytaj dalej...

Rozdzia

á 2. • RozpoczĊcie pracy z OOP

51

Metody magiczne
s

ïuĝÈce do przeciÈĝania metod klasy

Podobnie jak w przypadku przeci

Èĝania i uĝywania metod akcesorów dostÚpne sÈ równieĝ

metody magiczne s

ïuĝÈce do przeciÈĝania wywoïania dowolnej metody klasy. Jeĝeli czytelnik

nadal nie rozumie poj

Úcia przeciÈĝania metody, to warto przypomnieÊ, ĝe jest to proces uzy-

skiwania dost

Úpu do dowolnej metody, która nawet nie istnieje w klasie. Brzmi niewiarygod-

nie, nieprawda

ĝ? Przyjrzyjmy siÚ bliĝej temu zagadnieniu.

Istnieje metoda magiczna, która pomaga w przeci

Èĝaniu dowolnego wywoïania metody w kon-

tek

Ăcie klasy jÚzyka PHP 5. Nazwa tej metody magicznej to

__call()

. Pozwala ona na zdefi-

niowanie dzia

ïañ lub wartoĂci zwrotnej w sytuacji, gdy w obiekcie nastÚpuje wywoïanie nie-

zdefiniowanej metody. Mo

ĝe to byÊ uĝywane do symulowania przeciÈĝania metody lub nawet

zapewnienia eleganckiej obs

ïugi bïÚdów, gdy niezdefiniowana metoda jest wywoïywana w obiek-

cie. Metoda

__call()

pobiera dwa argumenty — nazw

Ú metody oraz tablicÚ argumentów prze-

kazywanych niezdefiniowanej metodzie.

Poni

ĝej przedstawiono przykïad uĝycia metody

__call()

:

<?
class Overloader
{
function __call($method, $arguments)
{
echo "Wywoïano metodÚ o nazwie {method} z nastÚpujÈcymi
argumentami <br/>";
print_r($arguments);
echo "<br/>";
}
}
$ol = new Overloader();
$ol->access(2,3,4);
$ol->notAnyMethod("boo");
?>

Jak wida

Ê w powyĝszym kodzie, w klasie nie ma definicji metod

access

oraz

notAnyMethod

.

Dlatego te

ĝ próba ich wywoïania powinna zakoñczyÊ siÚ wygenerowaniem komunikatu bïÚ-

du, nieprawda

ĝ? Jednak technika przeciÈĝania metody pomaga w sytuacji, gdy nastÚpuje wy-

wo

ïanie nieistniejÈcej metody. Po wykonaniu powyĝszego kodu czytelnik otrzyma nastÚpujÈce

dane wyj

Ăciowe:

Wywoïano metodÚ o nazwie access z nastÚpujÈcymi argumentami
Array
(
[0] => 2


Wyszukiwarka

Podobne podstrony:
konspekt-Dydaktyka Informatyki, Edukacja techniczno informatyczna, Programowanie obiektowe, sieci la
Programowanie obiektowe w PHP 5
informatyka-programowanie obiektowe, podstawy
Sprawozdanie pliki, Edukacja techniczno informatyczna, Programowanie obiektowe
Sprawozdanie rekordy(1), Edukacja techniczno informatyczna, Programowanie obiektowe
informatyka programowanie w jezyku clojure stuart halloway ebook
informatyka smarty szablony w aplikacjach php h hayder ebook
informatyka pear programowanie w php stephan schmidt ebook

więcej podobnych podstron