Idź do
• Spis treści
• Przykładowy rozdział
• Katalog online
• Dodaj do koszyka
• Zamów cennik
• Zamów informacje
o nowościach
• Fragmenty książek
online
Helion SA
ul. Kościuszki 1c
44-100 Gliwice
tel. 32 230 98 63
e-mail: helion@helion.pl
© Helion 1991–2010
Katalog książek
Twój koszyk
Cennik i informacje
Czytelnia
Kontakt
• Zamów drukowany
katalog
PHP 5. Narzędzia
dla ekspertów
Autor: Dirk Merkel
Tłumaczenie: Jarosław Dobrzański
ISBN: 978-83-246-2860-5
Tytuł oryginału:
Expert PHP 5 Tools
Format: 170×230, stron: 450
Osiągnij wyższy poziom zaawansowania w PHP!
• Jak tworzyć efektywny, łatwy w utrzymaniu kod PHP?
• Jak automatycznie tworzyć dokumentację techniczną?
• Jak debugować aplikację z Xdebug?
Język PHP to ulubione środowisko wielu programistów tworzących aplikacje i strony internetowe.
Jego wykorzystanie pozwala na błyskawiczne osiągnięcie efektów, a nauka nie przysparza
trudności. Trudno wskazać moment, w którym PHP zdobył tak ogromną popularność. Chwilami
można odnieść wrażenie, jakby w sieci był obecny od zawsze. Piąta wersja tego języka zawiera
wszystko to, co powinien posiadać nowoczesny język programowania – możliwość programowania
obiektowego, wsparcie dla formatu XML oraz rozbudowane mechanizmy wejścia-wyjścia. PHP 5
może z powodzeniem konkurować z „dużymi” rozwiązaniami, dostępnymi od lat na rynku
aplikacji internetowych.
Niniejsza książka to pozycja przeznaczona dla programistów, którzy znają już podstawy tego języka.
To unikalny podręcznik, dzięki któremu nauczysz się tworzyć efektywny, profesjonalny i łatwy
w utrzymaniu kod. W trakcie lektury zdobędziesz wiedzę na temat systemów kontroli wersji,
testów jednostkowych, szkieletów aplikacji oraz narzędzi wspomagających proces debugowania.
Ponadto dowiesz się, w jaki sposób tworzyć dokumentację z wykorzystaniem phpDocumentor,
jak wybrać najlepszy szkielet aplikacji oraz wdrożyć aplikację w środowisku produkcyjnym.
Dzięki tej książce osiągniesz wyższy poziom zaawansowania w programowaniu w języku PHP!
• Standardy pisania kodu PHP
• Opracowywanie własnych standardów
• Przygotowanie profesjonalnego środowiska programistycznego
• Dokumentowanie kodu za pomocą phpDocumentor
• Zarządzanie kodem źródłowym i jego wersjami
• Debugowanie aplikacji
• Szkielety aplikacji PHP
• Testy jednostkowe – tworzenie niezawodnego kodu
• Wdrażanie aplikacji
• Projektowanie aplikacji z wykorzystaniem UML
• Proces ciągłej integracji
Ta książka pomoże Ci stać się lepszym programistą!
Spis tre
Ăci
O autorze
9
O recenzentach
11
Przedmowa
13
O czym jest ta ksi
Èĝka?
13
Co jest potrzebne, aby skorzysta
Ê z ksiÈĝki?
14
Dla kogo jest ta ksi
Èĝka?
14
Konwencje
15
Uwagi Czytelników
15
Przyk
ïadowy kod
16
Rozdzia
ï 1. Standardy i styl pisania kodu
17
Co uwzgl
ÚdniÊ przy tworzeniu standardów?
17
Zalety
18
Wady
19
Standard pisania kodu PHP
19
Formatowanie
20
Konwencje nazewnicze
25
Metodologia
29
Weryfikacja zgodno
Ăci ze standardami pisania kodu
35
Automatyczna kontrola zgodnoĂci za pomocÈ narzÚdzia PHP_CodeSniffer
35
Podsumowanie
46
Rozdzia
ï 2. Dokumentowanie za pomocÈ narzÚdzia phpDocumentor
49
Dokumentacja w tre
Ăci kodu
50
Poziomy szczegóïowoĂci
51
Wprowadzenie do programu phpDocumentor
52
Instalacja programu phpDocumentor
52
Bloki DocBlock
54
Szablony DocBlock
55
Spis tre
Ğci
4
Samouczki
56
Dokumentowanie projektu
59
Opcje programu phpDocumentor
74
Katalog tagów
78
Tagi stosowane w PHP4
94
Tagi uĝytkownika
94
Podsumowanie
95
Rozdzia
ï 3. Eclipse — zintegrowane Ărodowisko programistyczne
97
Dlaczego Eclipse?
98
Wprowadzenie do PDT
100
Instalacja Eclipse
100
Wymagania
100
Wybór pakietu
102
Dodawanie pluginu PDT
102
Podstawowe poj
Úcia zwiÈzane z Eclipse
104
Przestrzeñ robocza (Workspace)
104
Widoki (Views)
105
Perspektywy
107
Przyk
ïadowy projekt PDT
108
Mo
ĝliwoĂci funkcjonalne pluginu PDT
111
Edytor
111
Inspekcja
115
Debugowanie
117
Preferencje PDT
120
Inne moĝliwoĂci funkcjonalne
127
Pluginy Eclipse
128
Zend Studio dla Eclipse
129
Wsparcie
131
Refaktoring
131
Generowanie kodu
131
Testowanie za pomocÈ PHPUnit
131
Obsïuga programu phpDocumentor
132
Integracja ze szkieletem Zend Framework
133
Integracja z serwerem Zend
133
Podsumowanie
133
Rozdzia
ï 4. ZarzÈdzanie kodem ěródïowym i wersjami
135
Typowe przypadki u
ĝycia
135
Krótka historia kontroli kodu
ěródïowego
136
CVS
139
Wprowadzenie do Subversion
141
Instalacja klienta
141
Konfiguracja serwera
142
PojÚcia zwiÈzane z Subversion
143
Lista poleceñ Subversion
147
Tworzenie projektu Subversion
157
Spis tre
Ğci
5
Sposób pracy z systemem kontroli wersji
164
Bliĝsze spojrzenie na repozytorium
169
OdgaïÚzienia i scalanie
171
Aplikacje klienckie
177
Konwencje i najlepsze praktyki przy pracy z Subversion
183
Przystosowywanie Subversion do wïasnych potrzeb
184
Powiadamianie programistów o zatwierdzonych plikach
za pomocÈ skryptu post-commit
187
Podsumowanie
187
Rozdzia
ï 5. Debugowanie
189
Pierwsza linia obrony — kontrola sk
ïadni
189
Dzienniki
191
Opcje konfiguracyjne
192
Dostosowywanie opcji konfiguracyjnych i panowanie nad nimi — PhpIni
194
Wy
Ăwietlanie informacji diagnostycznych
201
Funkcje
201
„Magiczne” staïe
205
Tworzenie wïasnej klasy diagnostycznej
205
Wprowadzenie do Xdebug
221
Instalacja Xdebug
221
Konfiguracja Xdebug
224
Natychmiastowe korzyĂci
225
Zdalne debugowanie
228
Podsumowanie
235
Rozdzia
ï 6. Szkielety aplikacji PHP
237
Pisanie w
ïasnego szkieletu
237
Ocena i wybór szkieletów
238
SpoïecznoĂÊ i akceptacja
239
MoĝliwoĂci funkcjonalne
239
Dokumentacja
240
JakoĂÊ kodu
240
Stosowanie i zgodnoĂÊ ze standardami pisania kodu
241
Dopasowanie do projektu
241
atwoĂÊ w nauce i adaptacji
242
DostÚpnoĂÊ kodu ěródïowego
242
ZnajomoĂÊ szkieletu
243
Ich zasady
243
Popularne szkielety aplikacji PHP
243
Zend
244
CakePHP
244
CodeIgniter
245
Symfony
245
Yii
246
Spis tre
Ğci
6
Aplikacja w szkielecie Zend Framework
247
Lista cech i funkcji
247
KrÚgosïup aplikacji
248
Usprawnienia
253
Podsumowanie
272
Rozdzia
ï 7. Testowanie
273
Metody testowania
273
Czarna skrzynka
274
Biaïa skrzynka
274
Szara skrzynka
275
Typy testowania
276
Testowanie jednostkowe
276
Testowanie integracyjne
277
Testowanie regresyjne
277
Testowanie systemowe
278
Testy akceptacji uĝytkowników
278
Wprowadzenie do PHPUnit
279
Instalacja PHPUnit
279
Przeszukiwanie ciÈgu tekstowego (przykïadowy projekt)
281
Analiza pokrycia kodu
306
Podklasy klasy TestCase
307
Podsumowanie
308
Rozdzia
ï 8. Wdraĝanie aplikacji
309
Cele i wymagania
309
Wdra
ĝanie aplikacji
311
Wymeldowywanie plików i wysyïanie ich na serwer
312
WyĂwietlanie informacji o niedostÚpnoĂci serwisu
313
Aktualizacja i instalacja plików
313
Aktualizacja schematu i zawartoĂci bazy danych
314
Rotacja plików dziennika i aktualizacja dowiÈzañ symbolicznych
314
Weryfikacja wdroĝonej aplikacji
315
Automatyzacja procesu wdro
ĝenia
315
Phing
315
Podstawowa skïadnia i struktura pliku
317
Typy
321
Wdraĝanie serwisu
322
Podsumowanie
339
Rozdzia
ï 9. Projektowanie aplikacji za pomocÈ jÚzyka UML
341
Metamodel i notacja a nasze podej
Ăcie do UML
342
Poziom szczegó
ïowoĂci i przeznaczenie
343
Narz
Údzia jedno- i dwukierunkowe
344
Podstawowe typy diagramów UML
345
Diagramy
346
Spis tre
Ğci
7
Diagramy klas
347
Diagramy sekwencji
359
Przypadki uĝycia
364
Podsumowanie
368
Rozdzia
ï 10. CiÈgïa integracja
369
Systemy satelitarne
371
Kontrola wersji — Subversion
371
Testowanie — PHPUnit
372
Automatyzacja — Phing
373
Styl pisania kodu — PHP_CodeSniffer
374
Dokumentowanie — PhpDocumentor
375
Analiza pokrycia kodu — Xdebug
375
Przygotowanie
Ărodowiska
376
Czy potrzebujÚ dedykowanego serwera CI?
376
Czy potrzebujÚ narzÚdzia CI?
376
Narz
Údzia CI
377
XINC
377
phpUnderControl
377
Ci
Ègïa integracja z phpUnderControl
378
Instalacja
378
Konfiguracja CruiseControl
382
PrzeglÈd procesu i komponentów ciÈgïej integracji
382
Podsumowanie
404
Skorowidz
405
8
Wdra
ĝanie aplikacji
Po zako
ñczeniu pisania aplikacji i zadbaniu o to, by inwestorzy zatwierdzili jej odbiór, przychodzi
czas na jej wdro
ĝenie. W zasadzie w tym momencie powinniĂmy juĝ mieÊ za sobÈ kilkukrotne jej
wdro
ĝenie i caïy proces powinien byÊ w mniejszym lub wiÚkszym stopniu zautomatyzowany.
Wi
ÚkszoĂÊ projektów, w które byïem ostatnio zaangaĝowany, skorzystaïo na tym, ĝe aplikacja byïa
wielokrotnie wdra
ĝana w róĝnych Ărodowiskach, takich jak programistyczne, testowe i docelo-
we. Automatyzacja tego procesu umo
ĝliwia szybkie uruchamianie kolejnych egzemplarzy
aplikacji.
Jest to nie tylko dobry sposób na wyeliminowanie wszelkich potencjalnych problemów przy
wdro
ĝeniu, ale takĝe duĝy krok w kierunku poprawienia produktywnoĂci nowych programi-
stów. Je
ĝeli proces wdroĝenia zostaï zoptymalizowany i dobrze udokumentowany, nowi czïonko-
wie zespo
ïu programistycznego nie bÚdÈ musieli poĂwiÚcaÊ wiele czasu na utworzenie wïa-
snego
Ărodowiska programistycznego. Zamiast tego mogÈ wykonaÊ kilka prostych kroków, aby
uzyska
Ê aplikacjÚ dziaïajÈcÈ i gotowÈ do dalszego rozwoju.
Cele i wymagania
Zastanówmy si
Ú, jakie powinny byÊ nasze cele podczas wdraĝania lub aktualizowania aplika-
cji. Innymi s
ïowy, co jest miarÈ sukcesu w tym procesie? Moĝe nam przyjĂÊ do gïowy twier-
dzenie,
ĝe to, jak dobrze dziaïa aplikacja, jest konsekwencjÈ tego, jak dobrze przeprowadzone
zosta
ïo wdroĝenie. To jednak byïoby mylÈce. Na tym etapie nie interesuje nas juĝ projekt
funkcjonalny, programowanie ani testowanie. Dzia
ïamy przy zaïoĝeniu, ĝe dysponujemy w peïni
funkcjonuj
ÈcÈ aplikacjÈ, która musi zostaÊ wdroĝona. To, czy aplikacja bÚdzie dziaïaÊ zgodnie
z oczekiwaniami, mo
ĝe, ale nie musi byÊ naszym problemem i nie ma nic wspólnego z samym
jej wdra
ĝaniem.
PHP 5. Narz
Ċdzia dla ekspertów
310
Powstaje wi
Úc pytanie, na jakiej podstawie ustaliÊ, czy wdroĝenie przebiegïo pomyĂlnie? Do
czego powinni
Ămy dÈĝyÊ, tworzÈc plan wdroĝenia? Jak moĝna siÚ spodziewaÊ, mam kilka
przemy
Ăleñ na ten temat.
Po pierwsze, wdro
ĝenie powinno odbyÊ siÚ szybko. Wszyscy ci, którzy wdraĝajÈ aplikacje
r
Úcznie, bÚdÈ zaskoczeni, ile z tego, co robiÈ, moĝna zautomatyzowaÊ przy odpowiednim pla-
nowaniu. Kiedy wdra
ĝamy aplikacjÚ po raz pierwszy, zwykle nie musimy siÚ spieszyÊ i mo-
ĝemy upewniÊ siÚ, ĝe wszystko dziaïa, jak trzeba, zanim powiadomimy klienta o tym, iĝ staïa
si
Ú dostÚpna. OczywiĂcie szybkie wdroĝenie staje siÚ o wiele waĝniejsze, kiedy aktualizujemy
aplikacj
Ú, bÚdÈcÈ juĝ w staïym uĝyciu. W takiej sytuacji naszym celem powinno byÊ zmini-
malizowanie lub najlepiej unikni
Úcie wszelkich niedogodnoĂci lub przerw w dostawie usïug
Ăwiadczonych przez aplikacjÚ. WïaĂnie wówczas szybkoĂÊ staje siÚ istotna.
Drugim celem wdro
ĝenia jest jego peïna odwracalnoĂÊ. Najlepiej zaczÈÊ traktowaÊ aktualizacje
aplikacji jako transakcj
Ú znanÈ z systemów bazodanowych. Jeĝeli coĂ siÚ nie powiedzie w trakcie
wdro
ĝenia, powinniĂmy byÊ w stanie cofnÈÊ wszystkie wykonane dotychczas kroki i przywróciÊ
aplikacj
Ú dokïadnie do stanu sprzed rozpoczÚcia wdroĝenia. O ile wydaje siÚ to sensowne i logicz-
ne, czasami nie
ïatwo osiÈgnÈÊ to w praktyce.
We
ěmy na przykïad sytuacjÚ, kiedy musimy zmodyfikowaÊ istniejÈcÈ tabelÚ bazy danych.
Mo
ĝe to wymagaÊ uruchomienia kilku zapytañ, aby zmieniÊ strukturÚ tabeli i pomanipulowaÊ
danymi. Je
ĝeli popeïnimy bïÈd lub pojawi siÚ jakiĂ nieprzewidziany problem, to jak cofniemy
zmiany? Je
ĝeli mamy plan, bÚdziemy mogli przywróciÊ bazÚ z kopii zapasowej, którÈ zrobiliĂmy
przed rozpocz
Úciem wdraĝania. Jednak w zaleĝnoĂci od rozmiaru bazy ïadowanie wszystkich da-
nych do wszystkich tabel mo
ĝe potrwaÊ dobre kilka minut. Moĝemy wiÚc przywróciÊ z kopii
zapasowej tylko naruszon
È tabelÚ, ale do tego potrzebujemy specjalnego narzÚdzia. Jeĝeli
zrobili
Ămy kopiÚ zapasowÈ w formie monolitycznego pliku zrzutu, znalezienie jednej tabeli
i przywrócenie jej mo
ĝe byÊ trudne. InnÈ opcjÈ jest uruchamianie specjalnie przygotowanych
zapyta
ñ cofajÈcych zmiany, które doprowadziïy do problemu. W tym przypadku zapytania te
trzeba b
Údzie przygotowaÊ wczeĂniej i nie bÚdziemy mieÊ na to czasu w sytuacji kryzysowej
przy du
ĝych naciskach na szybkie rozwiÈzanie problemu.
Jak wida
Ê, jest wiele sposobów na to, by uczyniÊ aktualizacje odwracalnymi. Konieczne jest
dok
ïadne planowanie, aby proces byï odwracalny na kaĝdym etapie.
Podsumujmy nasze wymagania dotycz
Èce wdroĝenia lub aktualizacji:
1. SzybkoĂÊ i automatyzacja w celu minimalizacji niedogodnoĂci dla uĝytkowników
i b
ïÚdów ludzkich.
2. Peïna odwracalnoĂÊ wszystkich czynnoĂci.
Powtórzy
ïem to w punktach, poniewaĝ dalsze rozwaĝania poĂwiÚcone planom wdroĝenia, po-
szczególnym czynno
Ăciom i pisaniu kodu, który pozwoli to zrealizowaÊ, bÚdÈ osnute wokóï
konsekwencji tych wymogów. To na ich podstawie b
Údziemy oceniaÊ, na ile udaïo siÚ osiÈgnÈÊ cel.
Rozdzia
á 8. • WdraĪanie aplikacji
311
Wdra
ĝanie aplikacji
W poprzednim punkcie wspominali
Ămy o moĝliwoĂci odwracania poszczególnych kroków w trak-
cie wdra
ĝania, ale co wïaĂciwie kryje siÚ pod pojÚciem kroku? Przyjrzyjmy siÚ kilku czynnoĂciom,
które s
È typowe podczas wdraĝania lub aktualizowania aplikacji. W dalszej czÚĂci rozdziaïu
przejdziemy do implementacji tych zada
ñ i próby ich maksymalnego zautomatyzowania.
Poni
ĝszy diagram ilustruje kroki w procesie wdraĝania aplikacji lub serwisu internetowego.
Oczywi
Ăcie w zaleĝnoĂci od aplikacji moĝemy jakieĂ kroki dodaÊ, a jakieĂ zignorowaÊ, ale
kroki tu pokazane stanowi
È dobrÈ podstawÚ w przypadku wiÚkszoĂci wdroĝeñ.
PHP 5. Narz
Ċdzia dla ekspertów
312
Zanim omówi
Ú szczegóïowo kaĝdy z powyĝszych kroków, zastanówmy siÚ nad ogólnym prze-
biegiem procesu ukazanego na diagramie. Zaczynamy od wymeldowania ca
ïego projektu
z systemu kontroli wersji. W celu dostosowania si
Ú do Ărodowiska, w którym aplikacja bÚdzie
wdra
ĝana, niektóre pliki, a w szczególnoĂci pliki konfiguracyjne bÚdÈ musiaïy zostaÊ zmodyfi-
kowane. Nast
Úpnie w istniejÈcym serwisie internetowym publikujemy komunikat informujÈcy
u
ĝytkowników o trwajÈcych pracach konserwacyjnych. Po zainstalowaniu plików, dokonaniu od-
powiednich zmian w bazie danych i rotacji plików dziennika usuwamy komunikat o przerwie
w funkcjonowaniu serwisu, restartujemy serwer i ponownie udost
Úpniamy stronÚ uĝytkownikom.
Na koniec dokonujemy inspekcji serwisu, aby sprawdzi
Ê, czy aplikacja dziaïa poprawnie.
Wymeldowywanie plików i wysy
ïanie ich na serwer
Zak
ïadajÈc, ĝe nasz projekt rezyduje w jakimĂ systemie kontroli wersji, bÚdziemy musieli naj-
pierw „wydoby
Ê” go stamtÈd w takiej postaci, która bÚdzie nadawaÊ siÚ do wdroĝenia na do-
celowy serwer. Je
ĝeli jako systemu kontroli wersji uĝywamy narzÚdzia Subversion, omówio-
nego w innym rozdziale tej ksi
Èĝki, musimy wykonaÊ polecenie
export
, aby uzyska
Ê kopiÚ
pozbawion
È wszystkich metadanych, jakie Subversion przechowuje zwykle w plikach ukry-
tych w ka
ĝdym z katalogów projektu. W innych systemach kontroli wersji moĝe obowiÈzywaÊ
inna terminologia i inne polecenia, ale idea pozostaje taka sama. Konieczne jest utworzenie
kopii projektu pozbawionej metadanych i wszelkich innych plików, które nie s
È przeznaczone
do wdro
ĝenia na serwer.
Czasem jednak b
Údzie nam zaleĝeÊ na takim wdroĝeniu, w ramach którego wdroĝony kod
ěródïowy pozostaje powiÈzany z repozytorium. PosïugujÈc siÚ terminologiÈ Subversion, moĝemy
wymeldowa
Ê kod do lokalnej lub zdalnej kopii roboczej. KorzyĂci z takiego podejĂcia sÈ dwoja-
kie. Po pierwsze mo
ĝemy uĝywaÊ informacji z repozytorium do przyspieszenia przyszïych
procesów aktualizacji. Mo
ĝemy na przykïad zaktualizowaÊ wymeldowanÈ wersjÚ do bieĝÈcej
wersji z repozytorium jednym poleceniem. Po drugie, je
ĝeli nasz proces wdroĝeniowy obsïu-
guje wymeldowania z repozytorium, mo
ĝna dziÚki niemu szybko tworzyÊ Ărodowiska progra-
mowania i w ten sposób poprawia
Ê produktywnoĂÊ nowych czïonków zespoïu. Z tego wzglÚdu
proces, który opracujemy w tym rozdziale, b
Údzie umoĝliwiaï zarówno wdroĝenie docelowe,
jak i programistyczne.
Prawdopodobnie b
Údziemy musieli równieĝ zmieniÊ jeden z kilku plików konfiguracyjnych,
wprowadzaj
Èc takie ustawienia, które odpowiadajÈ Ărodowisku docelowemu aplikacji. Bywa,
ĝe trzeba ustawiÊ w ten sposób katalog gïówny projektu, parametry uwierzytelniajÈce dla bazy
danych, bazowy adres URL itp.
Warto w tym momencie powróci
Ê na chwilÚ do rozdziaïu poĂwiÚconego szkieletom aplikacji,
poniewa
ĝ stworzona tam przykïadowa aplikacja zostaïa specjalnie zaprojektowana tak, by
dzia
ïaÊ w róĝnych Ărodowiskach z uĝyciem stosownych sekcji w pliku konfiguracyjnym. Takie
rozwi
Èzanie przewiduje istnienie zautomatyzowanego procesu wdroĝeniowego, obsïugujÈ-
cego ró
ĝne Ărodowiska.
Rozdzia
á 8. • WdraĪanie aplikacji
313
Dysponuj
Èc wdraĝanÈ wersjÈ projektu, moĝemy przesïaÊ go na serwer. Zwykle nastÚpuje to
za po
Ărednictwem protokoïów FTP, SFTP lub SCP (SSH). Zalecam korzystanie z któregoĂ
z bezpiecznych wariantów, aby nie eksponowa
Ê danych uwierzytelniajÈcych.
Je
ĝeli pracujemy na instalacji rezydujÈcej na serwerze docelowym, moĝemy poïÈczyÊ dwa
kroki, czyli eksportowanie i wysy
ïanie plików na serwer, wykorzystujÈc fakt, ĝe wszystkie nowo-
czesne systemy kontroli wersji obs
ïugujÈ operacje zdalne. Wystarczy wymeldowaÊ projekt
z repozytorium wprost na docelowy komputer.
Wy
Ăwietlanie informacji o niedostÚpnoĂci serwisu
Zanim naci
Ăniemy metaforyczny guzik „wyïÈczajÈcy” serwis, powinniĂmy powiadomiÊ uĝyt-
kowników,
ĝe prowadzimy prace konserwacyjne na serwerze. Ja sam zwykle kierujÚ uĝytkowni-
ków do standardowej strony HTML informuj
Ècej o trwajÈcych pracach i sugerujÈcej ponownÈ
prób
Ú poïÈczenia za kilka minut.
Je
ĝeli dobrze zaplanujemy i zautomatyzujemy aktualizacje, przerwa w dostÚpnoĂci usïugi dla
u
ĝytkowników powinna byÊ minimalna lub wrÚcz ĝadna. W tym momencie moĝe paĂÊ pyta-
nie, po co wobec tego w ogóle wstawiamy informacj
Ú o niedostÚpnoĂci serwisu. Otóĝ infor-
macja ta nagle mo
ĝe staÊ siÚ bardzo istotna w sytuacji, gdy coĂ przeszkodzi nam w pïynnej
realizacji dobrze zaplanowanego wdro
ĝenia. W takiej chwili bÚdziemy desperacko próbowaÊ
ustali
Ê ěródïo problemu i jak najszybciej go wyeliminowaÊ. DuĝÈ ulgÈ bÚdzie wówczas to, ĝe
nie musimy przejmowa
Ê siÚ informowaniem uĝytkowników i zamiast tego moĝemy skupiÊ siÚ
na tym, co naprawd
Ú wymaga naszej uwagi, czyli na rozwiÈzywaniu problemu.
Po zako
ñczeniu aktualizacji strona informujÈca o przerwie musi zostaÊ usuniÚta. To równieĝ
nale
ĝy zautomatyzowaÊ, poniewaĝ jest to coĂ, o czym naprawdÚ nie chcielibyĂmy zapomnieÊ.
Aktualizacja i instalacja plików
Kiedy pliki s
È juĝ na serwerze, musimy udostÚpniÊ je dla uĝytkowników. ZastÚpowanie ist-
niej
Ècych plików nowymi nie jest zgodne z naszÈ zasadÈ odwracalnoĂci. Moĝna by zmieniÊ
nazwy plików i przenie
ĂÊ je do katalogu ze starymi plikami, po czym zastÈpiÊ oryginaïy no-
wymi wersjami, ale istnieje szybsza metoda.
W systemach, które dopuszczaj
È stosowanie dowiÈzañ symbolicznych (zwanych teĝ aliasami
lub skrótami), dobra praktyka polega na utworzeniu dowi
Èzania symbolicznego wskazujÈcego
katalog zawieraj
Ècy pliki aplikacji. Gdy przychodzi czas aktualizacji, wystarczy przekierowaÊ
dowi
Èzanie na inny katalog, a nowe pliki stanÈ siÚ z miejsca dostÚpne.
PHP 5. Narz
Ċdzia dla ekspertów
314
Aktualizacja schematu i zawarto
Ăci bazy danych
Wi
ÚkszoĂÊ wspóïczesnych aplikacji korzysta z jakiejĂ formy bazy danych, wĂród których prym
wiedzie MySQL z uwagi na swoj
È popularnoĂÊ. Podczas pierwszego wdroĝenia aplikacji zwykle
jeste
Ămy takĝe odpowiedzialni za prawidïowe utworzenie i udostÚpnienie bazy danych. W przy-
padku aktualizacji istniej
Ècej instalacji musimy znaleěÊ jakiĂ sposób modyfikacji schematu
i zawarto
Ăci bazy. NajczÚĂciej polega to na utrzymywaniu pliku tekstowego, do którego pro-
grami
Ăci dodajÈ zapytania wprowadzajÈce w bazie zmiany wymagane przez zaktualizowany
kod. W trakcie wdra
ĝania administrator bazy lub programista jest wówczas odpowiedzialny za
wykonanie tych zapyta
ñ w tym czasie, gdy wykonywane sÈ wszystkie inne zadania zwiÈzane
z wdro
ĝeniem.
Mimo
ĝe takie podejĂcie moĝe siÚ sprawdzaÊ w przypadku aktualizacji lub tworzenia baz, nie
spe
ïnia ono postawionych wczeĂniej wymogów. Otóĝ nie umoĝliwia ïatwej odwracalnoĂci,
chyba
ĝe z góry to zaplanujemy. Poza tym wdraĝajÈc kolejne aktualizacje, napotykamy kolejne
zmiany w bazie danych i wówczas przywrócenie poprzednich stanów staje si
Ú coraz bardziej
skomplikowane.
Jedno z rozwi
Èzañ moĝe stanowiÊ narzÚdzie umoĝliwiajÈce definiowanie, zarzÈdzanie i wyko-
nywanie krokowych zmian w bazie danych. Owe krokowe zmiany zwane s
È migracjami, a na-
rz
Údzie, za którego pomocÈ bÚdziemy zarzÈdzaÊ migracjami, to DbDeploy. Poniewaĝ kaĝde-
mu krokowi aktualizuj
Ècemu bazÚ odpowiada krok wstecz, powracajÈcy do poprzedniej
wersji, mo
ĝliwe jest przeskakiwanie od stanu do stanu bazy w dowolnym kierunku. Co wiÚ-
cej, zmiany mo
ĝna aplikowaÊ w sposób zautomatyzowany, co zaspakaja obydwa nasze wymogi
dotycz
Èce udanego wdroĝenia.
W szczegó
ïy programu DbDeploy zagïÚbimy siÚ w dalszej czÚĂci rozdziaïu, przy okazji defi-
niowania migracji bazy danych na potrzeby naszego przyk
ïadu.
Rotacja plików dziennika
i aktualizacja dowi
Èzañ symbolicznych
W zale
ĝnoĂci od tego, gdzie i w jaki sposób nasza aplikacja przechowuje informacje, koniecz-
na albo przynajmniej zalecana mo
ĝe byÊ rotacja plików dziennika. Przede wszystkim trzeba
si
Ú upewniÊ, czy serwer moĝe zapisywaÊ do plików dziennika, co nie jest pewne w przypad-
ku, gdy pliki dziennika znajduj
È siÚ w podkatalogu naszej aplikacji i uaktywniliĂmy je na ser-
werze docelowym, edytuj
Èc dowiÈzanie symboliczne.
Bywa,
ĝe do Ăwieĝo wdroĝonej aplikacji trzeba skopiowaÊ inne biblioteki lub aplikacje wspoma-
gaj
Èce. Ja na przykïad utrzymujÚ interfejs WWW do czytania poczty, który jest dostÚpny z po-
ziomu katalogu g
ïównego dokumentów serwera mojej strony WWW. Nie jest on jednak czÚ-
ĂciÈ projektu w repozytorium Subversion i trzeba go skopiowaÊ lub przekierowaÊ dowiÈzanie
symboliczne po ka
ĝdym wdroĝeniu nowej wersji strony.
Rozdzia
á 8. • WdraĪanie aplikacji
315
Weryfikacja wdro
ĝonej aplikacji
Krok ten mo
ĝe siÚ wydawaÊ oczywisty, ale jednoczeĂnie jest to coĂ, o czym nie wypada zapo-
mnie
Ê. PowinniĂmy sprawdziÊ, czy wszystko, co zostaïo wdroĝone, dziaïa tak, jak oczekujemy.
W tym jednym kroku musimy dopu
ĂciÊ pewne odstÚpstwo od zaïoĝonych wymogów. Co
prawda mo
ĝemy i chcemy pewne testy zautomatyzowaÊ, np. testowanie nagïówków odpo-
wiedzi HTTP, ale niektóre rzeczy po prostu trzeba sprawdzi
Ê rÚcznie. Czasami najprostszym
sposobem na sprawdzenie, czy wszystko dzia
ïa, jest otwarcie przeglÈdarki i skorzystanie
z aplikacji tak, jak b
ÚdÈ korzystaÊ z niej uĝytkownicy. DziÚki temu z miejsca moĝemy wykryÊ
wiele powa
ĝnych problemów.
Automatyzacja procesu wdro
ĝenia
Skoro wiemy ju
ĝ, co chcemy osiÈgnÈÊ, przejděmy do omawiania narzÚdzi, za których pomocÈ
dokonamy implementacji i automatyzacji naszego planu wdro
ĝenia. Jest kilka pomniejszych
narz
Údzi, które pomogÈ nam wykonaÊ to zadanie, ale gïównym narzÚdziem pozwalajÈcym
wszystko zautomatyzowa
Ê i wykonujÈcym wiÚkszoĂÊ zadañ jest Phing.
Phing
Phing to system konsolidacji projektów. Nazwa to rekurencyjny akronim, którego pe
ïne
brzmienie w j
Úzyku angielskim to Phing Is ot Gnu Make (Phing to nie GNU Make). Phing
umo
ĝliwia wykonywanie róĝnych zadañ zwiÈzanych z konsolidacjÈ oprogramowania. Szcze-
gólnie dobry jest w automatyzacji zada
ñ, która, jak moĝna wnioskowaÊ po dotychczasowej
lekturze tego rozdzia
ïu, jest dla nas bardzo waĝna.
Co prawda twórcy narz
Údzia Phing aktywnie zaprzeczajÈ wszelkim zwiÈzkom pomiÚdzy tym
programem a narz
Údziem make, ale moĝna bezpiecznie stwierdziÊ, ĝe make leĝy w jakiejĂ
cz
ÚĂci u podstaw Phing. Phing zostaï jednak bardziej oparty na narzÚdziu Ant, które jest naj-
cz
ÚĂciej stosowanym systemem konsolidacyjnym w jÚzyku Java.
W naszym przypadku przewaga Phing nad narz
Údziem Ant polega na tym, ĝe obsïuguje on
ró
ĝne zadania specyficzne dla programowania w PHP. Poza tym Phing jest napisany w PHP,
co u
ïatwia programistom tego jÚzyka rozszerzanie funkcjonalnoĂci systemu.
Phing jest sterowany tzw. celami (targets) zdefiniowanymi w pliku XML. Cele s
È w gruncie
rzeczy dzia
ïaniami wykonywanymi przez Phing. Plik XML definiujÈcy owe cele i zaleĝnoĂci
mi
Údzy nimi zwykle ma nazwÚ build.xml. Cele skïadajÈ siÚ z kolei z jednego lub kilku zadañ.
Wi
Úcej o celach i zadaniach bÚdzie w dalszej czÚĂci rozdziaïu.
PHP 5. Narz
Ċdzia dla ekspertów
316
Takie rozwi
Èzanie uïatwia odrÚbne wykonywanie któregoĂ ze zdefiniowanych celów z auto-
matyczn
È obsïugÈ zaleĝnoĂci. Przykïadowe cele zdefiniowane przez uĝytkownika to:
Q
create-skeleton — tworzy katalogi potrzebne na serwerze.
Q
checkout-site — wymeldowuje projekt z systemu Subversion.
Q
update-db — wykonuje wst
Úpnie zdefiniowane zapytania w celu aktualizacji
struktury i zawarto
Ăci bazy danych.
Powy
ĝszych przykïadowych celów uĝyjemy teĝ miÚdzy innymi w naszym projekcie.
Instalacja narz
Údzia Phing
Narz
Údzie Phing moĝna zainstalowaÊ na kilka sposobów. Najprostszy i najbardziej bezbolesny
sposób polega na wykorzystaniu instalatora Pear. Z repozytoriów i narz
Údzia Pear korzystali-
Ămy juĝ wiele razy w tej ksiÈĝce. Przyczyna jest prosta — to dziaïa i staïo siÚ ogólnie przyjÚte
do tego stopnia,
ĝe wiÚkszoĂÊ narzÚdzi, które tutaj omawiam, moĝna w ten sposób pobraÊ
i zainstalowa
Ê.
Zamiast od razu uruchamia
Ê narzÚdzie pear, chciaïbym zwróciÊ uwagÚ, jak Ăwietny przykïad
stanowi ono w niniejszym rozdziale. Chwila zastanowienia i okazuje si
Ú, ĝe wpisujÈc
pear in-
stall phing/phing
, robimy dok
ïadnie to, czemu poĂwiÚcony jest ten rozdziaï — wdraĝamy
(instalujemy) aplikacj
Ú, a konkretnie Phing. Innymi sïowy, zaleĝnie od typu aplikacji rozprowadza-
nie jej za po
Ărednictwem kanaïu Pear moĝe stanowiÊ jeszcze jedno podejĂcie do wdraĝania.
Teraz mo
ĝemy przejĂÊ do praktyki i zainstalowaÊ Phing za pomocÈ Pear. Oto przebieg i re-
zultat instalacji Phing z wiersza polece
ñ:
Tak naprawd
Ú sÈ tu tylko dwa polecenia. Pierwsze,
pear channel-discover pear.phing.info
,
informuje instalator Pear,
ĝe pod adresem pear.phing.info znajduje siÚ repozytorium Pear.
Drugie polecenie,
pear install phing/phing
, instaluje pakiet o nazwie phing (drugie wyst
È-
pienie „phing”) poprzez kana
ï o nazwie phing (pierwsze wystÈpienie „phing”).
Rozdzia
á 8. • WdraĪanie aplikacji
317
Inn
È metodÈ instalacji Phing jest bezpoĂrednie wymeldowanie go z repozytorium CVS pro-
jektu. Zalet
È jest to, ĝe otrzymujemy w ten sposób najĂwieĝszÈ i najlepszÈ bazÚ kodu, w tym
nieopublikowane jeszcze poprawki i ulepszenia. Z tej w
ïaĂnie metody musimy skorzystaÊ, jeĝeli
chcemy w
ïoĝyÊ wïasnÈ pracÚ w rozwój projektu, poniewaĝ bÚdziemy wówczas mogli zatwierdzaÊ
zmiany z powrotem do repozytorium. Oczywi
Ăcie zakïadajÈc, ĝe otrzymamy uprawnienia do
dokonywania zmian.
Podstawowa sk
ïadnia i struktura pliku
Plik konsolidacyjny zawiera kod XML definiuj
Ècy wszystkie dziaïania i cele dostÚpne dla uĝyt-
kownika. Zgodnie z konwencj
È plik ten otrzymuje nazwÚ build.xml. Jeĝeli jednak korzystamy
z opcji
–buildfile [nazwa_pliku]
w wierszu polece
ñ, moĝemy zmieniÊ tÚ nazwÚ na dowolnÈ
inn
È. W naszym przykïadzie pozostaniemy przy przyjÚtej konwencji nazewniczej.
Przyjrzyjmy si
Ú ogólnej strukturze pliku konsolidacyjnego Phing. Poniĝszy szkielet takiego
pliku nie definiuje
ĝadnych dziaïañ. Jego celem jest jedynie zilustrowanie podstawowej struktu-
ry takich plików. Wraz z post
Úpem pracy nad naszym przykïadem bÚdziemy uzupeïniaÊ ko-
lejne cz
ÚĂci tego pliku, zmierzajÈc do opracowania w peïni zautomatyzowanego procesu
wdro
ĝeniowego.
<l?xml version="1.0"?>
<project name="nazwaProjektu" description="Opcjonalny opis pliku
´konsolidacyjnego." default="nazwaCeluDomyĂlnego">
<property name="jakaĂWïaĂciwoĂÊGlobalna" value="wartoĂÊ" override="true" />
<type>
<!-- globalna definicja typu -->
</type>
<target name="nazwaCeluDomyĂlnego" depends="celPomoniczy" description="Opis
´domyĂlnego zadania.">
<property name="wïaĂciwoĂÊLokalna" value="wartoĂÊ" override="true" />
<type>
<!-- lokalna definicja typu -->
</type>
<task>
<!-- definicja zadania -->
</task>
<!-- nast
Ċpne zadania (opcjonalnie) -->
</target>
<target name="celPomocniczy" description="Opis celu pomocnicznego.">
<task>
<!-- definicja zadania -->
</task>
PHP 5. Narz
Ċdzia dla ekspertów
318
<!-- nast
Ċpne zadania (opcjonalnie) -->
</target>
</project>
Jako programista zapewne znasz dobrze format XML, dlatego omówi
Ú szkielet pliku konsoli-
dacyjnego bardzo krótko. Wydaje mi si
Ú teĝ, ĝe najlepiej zaczÈÊ omawianie hierarchii znacz-
ników od wewn
Ètrz.
Zadania
Sercem pliku konsolidacyjnego s
È zadania ujmowane w znacznikach
<task>
. Znaczniki te od-
powiadaj
È wprost dziaïaniom. To tutaj wykonywana jest caïa rzeczywista praca. Znaczniki
<task>
mo
ĝna traktowaÊ jako najdrobniejszÈ jednostkÚ wykonywanych dziaïañ. Dokumentacja
Phing definiuje te podstawowe zadania jako te, które s
È niezbÚdne, by skonsolidowaÊ projekt.
Dla odró
ĝnienia zadania opcjonalne to te, które nie sÈ niezbÚdne dla skonsolidowania pro-
jektu. Moim zdaniem rozró
ĝnienie to jest nieco sztuczne, szczególnie w zwiÈzku z faktem, ĝe
PHP to j
Úzyk interpretowany, co oznacza, ĝe proces konsolidacji nie zawiera w sobie fazy
kompilacji.
Oto przyk
ïadowe zadania:
Q
CopyTask
— kopiuje pliki lub grupy plików albo katalogów z jednego miejsca w systemie
plików w inne, z mo
ĝliwoĂciÈ zmiany nazwy.
Q
ForeachTask
— przechodzi przez list
Ú i umoĝliwia ujÚcie jednego lub kilku zadañ
w p
Útli i wykonanie kaĝdego z nich dla kaĝdego elementu z listy.
Q
InputTask
— prosi u
ĝytkownika o wprowadzenie danych, z których moĝna skorzystaÊ
przy wykonywaniu nast
Úpnych zadañ.
A oto przyk
ïady zadañ opcjonalnych:
Q
SvnExportTask
— eksportuje projekt z repozytorium Subversion do lokalnego
katalogu.
Q
ZipTask
/
UnzipTask
— dwa uzupe
ïniajÈce siÚ zadania tworzÈce archiwum ZIP z grupy
plików lub rozpakowuj
Èce pliki z istniejÈcego archiwum.
Q
PHPUnit2Task
— uruchamia przypadki testowania lub zestawy testów za
po
Ărednictwem systemu testujÈcego PHPUnit2.
Zadania przypominaj
È nieco funkcje w tym sensie, ĝe mogÈ przyjmowaÊ argumenty. Gdy tylko
zaczniemy tworzy
Ê nasz przykïadowy skrypt konsolidacyjny do wdraĝania aplikacji, zobaczy-
my praktyczne przyk
ïady zadañ.
Zamiast podawa
Ê listÚ wszystkich dostÚpnych zadañ wraz z moĝliwymi opcjami, odsyïam Czytel-
ników do bardzo dobrze napisanej dokumentacji online, zawieraj
Ècej najĂwieĝszÈ i najlepszÈ
list
Ú zadañ wraz z opisami, dostÚpnej pod adresem http://phing.info/docs/guide/.
Rozdzia
á 8. • WdraĪanie aplikacji
319
Wreszcie, je
ĝeli zestaw zadañ udostÚpniany przez Phing nie zaspakaja naszych wymagañ, mo-
ĝemy bez problemu dodawaÊ wïasne zadania. Jako narzÚdzie open source Phing jest z zaïoĝe-
nia rozszerzalny. Fakt, i
ĝ zostaï napisany w PHP, potencjalnie uïatwi wiÚkszoĂci Czytelników tej
ksi
Èĝki dopisywanie kodu wïasnych zadañ. Tak naprawdÚ dodanie wïasnego zadania w formie
klasy przyjmuj
Ècej argumenty i wykonujÈcej poĝÈdane operacje jest zaskakujÈco proste.
Cele
Cele (targets) to logicznie powi
Èzane grupy zadañ. Zadania grupowane sÈ w formie celów, aby
osi
ÈgnÈÊ okreĂlony rezultat. Moĝemy na przykïad mieÊ cel o nazwie
backup-db
, który grupuje
zadanie tworz
Èce kopiÚ zapasowÈ bazy danych, zadanie kompresujÈce otrzymany plik zrzutu
bazy oraz zadanie przesy
ïajÈce kopiÚ poprzez FTP do miejsca, w którym zwykle przechowu-
jemy kopie zapasowe.
Zadania zawarte pomi
Údzy otwierajÈcym i zamykajÈcym znacznikiem
<target>
s
È wykonywa-
ne w kolejno
Ăci wystÚpowania. Cele majÈ trzy atrybuty — sÈ to
name
(nazwa),
description
(opis) oraz
depends
(powi
Èzania). DziÚki atrybutowi
name
mo
ĝliwe jest wykonanie danego celu
z wiersza polece
ñ. Oto przykïadowe wywoïanie celu
upgrade-db
w domy
Ălnym pliku konsoli-
dacyjnym build.xml:
$ phing upgrade-db
Nazwa celu jest opcjonalna w powy
ĝszym wywoïaniu i jeĝeli nie zostanie podana, wykonywany
jest domy
Ălny cel zdefiniowany w znaczniku
<project>
, który omówiony zostanie za chwil
Ú.
Atrybut
description
znacznika
<target>
zawiera krótkie podsumowanie dzia
ïañ wykonywanych
w ramach celu.
Wreszcie atrybut
depends
pozwala wskaza
Ê inne cele, które muszÈ zostaÊ wykonane przed da-
nym celem. Phing
Ăledzi, które z celów zostaïy juĝ wykonane, i automatycznie wywoïuje cele,
które s
È konieczne, aby speïniÊ ten wymóg. W przedstawionym wczeĂniej przykïadowym
szkielecie pliku build.xml cel o nazwie
domyĂlnaNazwaCelu
jest uzale
ĝniony od celu
celPomocniczy
.
Je
ĝeli wywoïamy cel
domyĂlnaNazwaCelu
, Phing zadba o to, by
celPomocniczy
zosta
ï wykonany
wcze
Ăniej. W atrybucie
depends
mo
ĝna podaÊ wiÚcej niĝ jeden cel zaleĝny, oddzielajÈc je
przecinkami. Podobnie jak zadania, cele s
È wykonywane w kolejnoĂci wystÚpowania.
W
ïaĂciwoĂci i plik wïaĂciwoĂci
W terminologii narz
Údzia Phing wïaĂciwoĂci to odpowiedniki zmiennych. Moĝna definiowaÊ
je w globalnej przestrzeni nazw lub w lokalnej dla okre
Ălonego celu. Globalne definicje wïa-
ĂciwoĂci muszÈ nastÚpowaÊ poza obrÚbem znaczników
<target>
, a definicje lokalne w obr
Ú-
bie znacznika
<target>
, którego maj
È dotyczyÊ.
PHP 5. Narz
Ċdzia dla ekspertów
320
Nieco dalej pojawi si
Ú kilka globalnych definicji wïaĂciwoĂci i typów. WïaĂciwoĂci to w gruncie
rzeczy zmienne, z których wi
ÚkszoĂÊ nie zmienia wartoĂci w trakcie wykonywania skryptu. SÈ
jednak równie
ĝ wïaĂciwoĂci tworzone dynamicznie i uĝywane przez skrypt konsolidacyjny do
zachowania stanu w obr
Úbie celu lub pomiÚdzy wykonaniem poszczególnych celów.
W
ïaĂciwoĂci sÈ definiowane i uĝywane w pliku build.xml w nastÚpujÈcy sposób:
<property name="svn.url" value= "https://${svn.server}/home/svn/${svn.project}"
´override="true" />
W tym przyk
ïadzie definiujemy wïaĂciwoĂÊ o nazwie
svn.url
. Warto
ĂÊ przypisywana tej wïa-
ĂciwoĂci to adres URL, który z kolei jest konstruowany z kilku ciÈgów tekstowych i dwóch
zdefiniowanych wcze
Ăniej wïaĂciwoĂci:
svn.server
i
svn.project
. Jak wida
Ê, aby posïuĝyÊ siÚ
warto
ĂciÈ przypisanÈ do danej wïaĂciwoĂci, naleĝy uĝyÊ skïadni z symbolem dolara, po którym
nast
Úpuje nazwa wïaĂciwoĂci w nawiasach klamrowych:
${nazwa_wïaĂciwoĂci}
.
Mo
ĝliwe jest (i bardzo wygodne) przechowywanie wïaĂciwoĂci w odrÚbnych plikach, zawie-
raj
Ècych wyïÈcznie pary nazwa-wartoĂÊ. Pliki te sÈ zgodne z konwencjÈ nazewniczÈ, nakazujÈcÈ,
by nazwa ko
ñczyïa siÚ przyrostkiem .properties. Oto przykïad prostego pliku wïaĂciwoĂci:
# Subversion
svn.server=waferthin.com
svn.proto=https://
# … definicje wielu innych w
áaĞciwoĞci …
# ustawienia i parametry uwierzytelniaj
ące dla bazy danych
db.server=localhost
db.user=root
db.password=psstdonttell
db.name=state_secrets
Jak wida
Ê, skïadnia jest bardzo prosta. WartoĂci sÈ przypisywane nazwom wïaĂciwoĂci za po-
moc
È znaku równoĂci i w kaĝdym wierszu musi wystÚpowaÊ tylko jedna para nazwa-wartoĂÊ.
Importowanie takiego pliku w
ïaĂciwoĂci jest moĝliwe dziÚki atrybutowi
file
zadania
property
:
<property file="propfile.properties"/>
To wystarczy, by ustawi
Ê wszystkie wïaĂciwoĂci wymienione w pliku propfile.properties dla
przestrzeni nazw, w której wyst
Úpuje zadanie
property
.
U
ĝywanie plików wïaĂciwoĂci ma co najmniej dwie zalety. Po pierwsze, dziÚki niemu plik
build.xml staje si
Ú krótszy i bardziej przejrzysty. Skïadnia XML jest doĂÊ rozwlekïa, wiÚc
utrzymywanie w
ïaĂciwoĂci w odrÚbnym pliku poprawia czytelnoĂÊ i uïatwia zrozumienie sa-
mego pliku konsolidacyjnego. Po drugie, pliki w
ïaĂciwoĂci wprowadzajÈ kolejny poziom
abstrakcji, podobnie jak centralny plik lub obiekt konfiguracyjny dodaje poziom abstrakcji do
aplikacji PHP. Aby wdro
ĝyÊ aplikacjÚ gdzieĂ indziej, wystarczy dokonaÊ edycji pliku wïaĂci-
wo
Ăci bez naruszania pliku build.xml.
Rozdzia
á 8. • WdraĪanie aplikacji
321
Rozwijaj
Èc tÚ ideÚ, moĝemy zapewniÊ obsïugÚ róĝnych Ărodowisk. Jak zobaczymy póěniej w na-
szym przyk
ïadzie, moĝemy po prostu wskazaÊ Phing Ărodowisko, w jakim ma nastÈpiÊ wdro-
ĝenie, a reszta ustawieñ bÚdzie realizowana poprzez doïÈczenie pliku wïaĂciwoĂci odpowia-
daj
Ècemu danemu Ărodowisku. Bardzo czÚsto spotyka siÚ pliki wïaĂciwoĂci o nazwach w stylu
dev.properties, staging.properties lub production.properties, odzwierciedlaj
Èce Ărodowisko, dla
którego konfigurowany jest proces konsolidacji lub wdro
ĝenia.
Typy
Typy mog
È reprezentowaÊ dane bardziej zïoĝone niĝ wïaĂciwoĂci. Na przykïad typ moĝe byÊ
odno
Ănikiem do plików w danym katalogu, którego nazwa musi pasowaÊ do podanego wyra-
ĝenia regularnego. Oto przykïad typu
fileset
, który zawiera odno
Ăniki do wszystkich plików
.properties w katalogu build projektu, poza plikiem o nazwie deprecated.properties.
<fileset dir="${project.home}/build" >
<include name="*.properties" />
<exclude name="deprecated.properties" />
</fileset>
Oto wbudowane typy Phing:
Q
FileList
— uporz
Èdkowana lista plików w systemie plików. Pliki nie muszÈ istnieÊ
w systemie plików.
Q
FileSet
— nieuporz
Èdkowana lista plików, które istniejÈ w systemie plików.
Q
Path / ClassPath
— s
ïuĝy do reprezentowania zbiorów Ăcieĝek do katalogów.
Dok
ïadny opis funkcjonalnoĂci i atrybutów tych typów moĝna znaleěÊ w dokumentacji narzÚ-
dzia Phing.
Filtry
Jak sugeruje nazwa, filtry pozwalaj
È filtrowaÊ i przeksztaïcaÊ w jakiĂ sposób zawartoĂÊ pliku.
Gdy pisa
ïem tÚ ksiÈĝkÚ, byïo dostÚpnych 14 gïównych filtrów, pozwalajÈcych wykonywaÊ tak
ró
ĝnorodne dziaïania, jak:
Q
rozwijanie w
ïaĂciwoĂci w pliku,
Q
usuwanie znaków przej
Ăcia do nastÚpnego wiersza, komentarzy w wierszu lub
komentarzy PHP,
Q
usuwanie lub dodawanie wierszy w pliku w zale
ĝnoĂci od ich lokalizacji w danym
pliku lub usuwanie zawarto
Ăci wiersza.
Filtry musz
È byÊ zawarte pomiÚdzy znacznikami otwierajÈcym i zamykajÈcym
filterchain
.
Nasz plik konsolidacyjny w podsekcji mappers równie
ĝ zawiera przykïad zastosowania filtru.
W dalszej cz
ÚĂci rozdziaïu zobaczymy jeszcze jeden przykïad zastosowania filtrów w celu
zmiany zawarto
Ăci jednego lub kilku plików.
PHP 5. Narz
Ċdzia dla ekspertów
322
Mapery
O ile filtry operuj
È na zawartoĂci pliku, mapery dziaïajÈ podobnie, ale na nazwach plików.
Obecnie istnieje w Phing pi
ÚÊ podstawowych maperów, które pozwalajÈ wykonywaÊ na Ăcieĝ-
kach i nazwach plików nast
ÚpujÈce operacje:
Q
FlattenMapper
— usuwa katalogi z podanej
Ăcieĝki, pozostawiajÈc jedynie nazwy
plików.
Q
GlobalMapper
— przemieszcza pliki, nie zmieniaj
Èc ich nazw.
Q
IdentityMapper
— nie zmienia niczego.
Q
MergeMapper
— zmienia kilka plików tak, by mia
ïy tÚ samÈ nazwÚ.
Q
RegexpMapper
— zmienia nazw
Ú plików, posïugujÈc siÚ wyraĝeniami regularnymi.
Oto przyk
ïad zmiany nazw plików szablonów na nazwy rzeczywistych plików PHP z wyko-
rzystaniem filtru
expandproperties
oraz zmiany nazw plików za pomoc
È filtru
GlobalMapper
:
<copy todir="/includes">
<filterchain>
<expandproperties />
</filterchain>
<mapper type="glob" from="*.php.tpl" to="*.php"/>
<fileset dir="templates">
<include name="*.php.tpl" />
</fileset>
</copy>
Jak zwykle, pe
ïna lista wszystkich filtrów i maperów oraz dokïadne opisy ich zastosowania
i atrybutów s
È dostÚpne w doskonaïej dokumentacji online narzÚdzia Phing.
Znacznik project
Najbardziej zewn
Útrzny znacznik to znacznik
<project>
, który zawiera atrybuty definiuj
Èce
nazw
Ú projektu, jego opis oraz nazwÚ celu, jaki ma byÊ wykonywany domyĂlnie. Jak siÚ za
chwil
Ú przekonamy, zawsze istnieje moĝliwoĂÊ nakazania Phing wykonania celu innego niĝ
zdefiniowany tutaj domy
Ălny. Poza tym Phing korzysta z nazwy projektu, przekazujÈc infor-
macje u
ĝytkownikom.
Wdra
ĝanie serwisu
Spróbujmy teraz wykorzysta
Ê wïaĂnie zdobytÈ wiedzÚ na temat zadañ, celów, wïaĂciwoĂci,
typów, filtrów, maperów oraz projektów i utworzy
Ê plik konsolidacyjny, który pïynnie wdroĝy
aktualizacj
Ú serwisu internetowego. Utworzymy takĝe kilka szablonów i danych, które pozwolÈ
nam dowolnie aktualizowa
Ê i cofaÊ aktualizacje bazy danych. Zamiast eksperymentowaÊ z czyjÈĂ
stron
È, zdecydowaïem siÚ zautomatyzowaÊ wdroĝenie mojej wïasnej strony internetowej
waferthin.com. Oto struktura katalogów wdro
ĝonego serwisu:
Rozdzia
á 8. • WdraĪanie aplikacji
323
Separowanie zewn
Útrznych zaleĝnoĂci
Sensowne wydaje si
Ú odseparowanie zewnÚtrznych zaleĝnoĂci, które nie rezydujÈ w naszym
systemie kontroli wersji, od reszty projektu. S
È to zwykle pliki i katalogi, które niekoniecznie
musz
È byÊ aktualizowane za kaĝdym razem, gdy przeprowadzamy wdroĝenie. SeparujÈc te
zale
ĝnoĂci, nie bÚdziemy musieli martwiÊ siÚ o to, ĝe przypadkowo je nadpiszemy lub uszko-
dzimy. W przypadku mojej strony jest kilka katalogów oraz plików, które zosta
ïy po prostu
skopiowane na serwer podczas pierwotnej r
Úcznej instalacji, takich jak Zend Library, Mantis
(narz
Údzie do Ăledzenia problemów) i RoundCube (przeglÈdarkowy czytnik e-maili). Katalogi
te b
ÚdÈ musiaïy zostaÊ albo przeniesione ze starej wersji serwisu do nowej, albo zastÈpione
dowi
Èzaniem symbolicznym. Z tego samego powodu katalog logs bÚdzie musiaï byÊ przenie-
siony poza katalog projektu. Po uko
ñczeniu naszego skryptu konsolidacyjnego i udanym
wdro
ĝeniu serwisu przyjrzymy siÚ, jak zmieniïa siÚ jego struktura w porównaniu ze stanem
sprzed wdro
ĝenia.
PHP 5. Narz
Ċdzia dla ekspertów
324
Tworzenie skryptu konsolidacyjnego
Zacznijmy od utworzenia prostego skryptu konsolidacyjnego. Na szcz
ÚĂcie cele dzielÈ skrypt
na
ïatwiejsze do ogarniÚcia czÚĂci. BÚdziemy tworzyÊ kolejno po jednym celu, do momentu gdy
wszystkie elementy tej „uk
ïadanki” bÚdÈ gotowe i moĝliwe stanie siÚ wdroĝenie serwisu jednym
poleceniem.
¥rodowisko i wïaĂciwoĂci
Zwykle pracuj
Ú na lokalnej kopii projektu, ale na koniec zdalnie wdraĝam wersjÚ testowÈ
i ostateczn
È. Tutaj kaĝdy programista moĝe preferowaÊ inny sposób pracy, ale niemal wszyscy
spotkamy si
Ú z sytuacjÈ, kiedy musimy wdroĝyÊ tÚ samÈ aplikacjÚ w wielu róĝnych Ărodowi-
skach i serwerach. Przyda
ïoby siÚ, aby skrypt Phing byï na tyle elastyczny, by uwzglÚdniaï
owe ró
ĝne wymagania w sposób nieangaĝujÈcy uĝytkownika. Poniewaĝ wiÚkszoĂÊ, jeĝeli nie
wszystkie czynno
Ăci, jakie trzeba wykonaÊ, jest przy kaĝdym wdroĝeniu taka sama, napiszemy
skrypt pozwalaj
Ècy wdraĝaÊ aplikacjÚ w róĝnych Ărodowiskach poprzez prostÈ zmianÚ kilku
w
ïaĂciwoĂci, takich jak nazwa domeny, Ăcieĝka do projektu na serwerze, ustawienia bazy
danych itp.
Typowe rozwi
Èzanie tego problemu polega na utworzeniu plików wïaĂciwoĂci, które odpo-
wiadaj
È róĝnym Ărodowiskom, które chcemy obsïuĝyÊ. NastÚpnie moĝemy utworzyÊ cele ïa-
duj
Èce odpowiednie pliki wïaĂciwoĂci lub wrÚcz pytajÈce uĝytkownika, z którego pliku wïa-
ĂciwoĂci naleĝy skorzystaÊ.
Oto plik dev.properties, zawieraj
Ècy ustawienia dla wdroĝenia wersji mojej strony w Ărodowi-
sku programistycznym na moim lokalnym komputerze:
# wdro
Īenie
site.fqdn=dev.waferthin.com
site.fqdn.secure=dev.secure.waferthin.com
site.home=/Users/dirk/Sites/${site.fqdn}
site.root=/Users/dirk/Sites/${site.fqdn}/${site.fqdn}
# system Subversion
svn.bin=/usr/bin/svn
svn.fqdn=svn
svn.user=dirk
svn.repo=/svn/
svn.proto=https://
svn.project=waferthin.com/trunk
svn.password=donttellanybody
# ustawienia po
áączenia z bazą i parametry uwierzytelniające
db.user=root
db.password=itsasecret
db.name=waferthin
db.fqdn=localhost
Rozdzia
á 8. • WdraĪanie aplikacji
325
db.port=3306
db.bin=/usr/local/mysql/bin/mysql
db.backup.dir=${site.home}/backups
# lokalizacja pliku dziennika aplikacji
log=${site.home}/logs/waferthin.log
# modu
á szablonów Smarty
smarty.templates_dir=${site.root}/smarty/templates
smarty.compile_dir=${site.root}/smarty/templates_c
smarty.configs_dir=${site.root}/smarty/configs
smarty.cache_dir=${site.root}/smarty/cache
smarty.plugins_dir=${site.root}/smarty/plugins
smarty.plugins2_dir=${site.root}/includes/libraries/Smarty/plugins
smarty.force_compile=true
# zewn
Ċtrzne narzĊdzia
extern.apachectl=/usr/sbin/apachectl
extern.sudo=/usr/bin/sudo
extern.ln=/bin/ln
extern.mysqldump=/usr/local/mysql/bin/mysqldump
# biblioteki
zend_dir=/usr/local/lib/php/Zend
Jak wida
Ê, plik skïada siÚ z szeĂciu sekcji definiujÈcych nastÚpujÈcy podziaï logiczny:
1. WïaĂciwoĂci z przedrostkiem
site.
odnosz
È siÚ do lokalizacji na serwerze, w jakiej
ma zosta
Ê wdroĝony serwis.
2. WïaĂciwoĂci z przedrostkiem
svn.
odnosz
È siÚ do dostÚpu do repozytorium
Subversion przechowuj
Ècego kod ěródïowy.
3. WïaĂciwoĂci z przedrostkiem
db.
odnosz
È siÚ do parametrów poïÈczenia i parametrów
uwierzytelniaj
Ècych umoĝliwiajÈcych poïÈczenie z bazÈ danych.
4. WïaĂciwoĂci z przedrostkiem
smarty.
odnosz
È siÚ do konfiguracji moduïu
szablonów Smarty.
5. WïaĂciwoĂci z przedrostkiem
extern.
odnosz
È siÚ do lokalizacji zewnÚtrznych
plików wykonywalnych, wymaganych przez skrypt konsolidacyjny.
6. WïaĂciwoĂci
log
i
zend_dir
s
ïuĝÈ do zachowania jeszcze innych zewnÚtrznych
zale
ĝnoĂci poprzez utworzenie dowiÈzañ symbolicznych (wiÚcej na ten temat
w dalszej cz
ÚĂci rozdziaïu).
Mam te
ĝ podobne pliki dla Ărodowiska docelowego (prod.properties) oraz testowego
(test.properties). Wszystkie trzy pliki znajduj
È siÚ w tym samym katalogu co plik build.xml. Po
zaimplementowaniu obs
ïugi plików wïaĂciwoĂci i wielu róĝnych Ărodowisk moĝemy dodawaÊ
dowoln
È liczbÚ Ărodowisk wdroĝeniowych poprzez utworzenie stosownych plików wïaĂciwoĂci.
PHP 5. Narz
Ċdzia dla ekspertów
326
Zacznijmy teraz od utworzenia pliku build.xml, który na razie inicjalizuje jedynie
Ărodowisko:
<?xml version="1.0"?>
<project name="waferthin.com" description="Realizuje utrzymanie i wdroĝenie
´serwisu waferthin.com." default="deploy">
<!-- Inicjalizuje datownik, który b
Ċdzie uĪywany przy nadawaniu nazw róĪnym plikom
´
i katalogom.
-->
<tstamp/>
<target name="deploy" depends="get-env,create-skeleton,svn-export,
´stamp-config,disp-maint,backup-db,deploy-db,publish-site"
´description="Wdraĝa serwis na serwer WWW i wykonuje niezbÚdne zadania
´konsolidacyjne i aktualizacyjne.">
</target>
<target name="get-env" description="Pobiera Ărodowisko, do jakiego ma
´nastÈpiÊ wdroĝenie.">
<!-- Czy
Ğrodowisko zostaáo juĪ ustawione? -->
<if>
<not>
<isset property="environment" />
</not>
<then>
<!-- Prosi u
Īytkownika o wybranie Ğrodowiska z listy obsáugiwanych
´
Ğrodowisk. -->
<input propertyname="environment" validargs="dev,test,prod"
´promptChar=":">Podaj Ărodowisko </input>
</then>
</if>
<!-- Sprawdza, czy istnieje plik w
áaĞciwoĞci dla danego Ğrodowiska. -->
<available file="${environment}.properties" property="env_prop_exists"
´type="file" />
<if>
<equals arg1="${env_prop_exists}" arg2="true" />
<then>
<!-- Odczytuje pliki w
áaĞciwoĞci. -->
<property file="${environment}.properties"/>
</then>
<else>
<!-- Przerywa konsolidacj
Ċ i ukazuje komunikat dotyczący báĊdu. -->
<fail message="Nie znaleziono pliku wïaĂciwoĂci dla wybranego
´Ărodowiska (${environment}.properties)" />
</else>
</if>
</target>
Rozdzia
á 8. • WdraĪanie aplikacji
327
<target name="deploy-dev" description="Wdraĝa serwis w Ărodowisku
´programowania.">
<property name="environment" value="dev" override="true" />
<phingcall target="deploy" />
</target>
<target name="deploy-prod" description="Wdraĝa serwis w Ărodowisku
´docelowym.">
<property name="environment" value="prod" override="true" />
<phingcall target="deploy" />
</target>
<target name="deploy-test" description="Wdraĝa serwis w Ărodowisku
´testowym.">
<property name="environment" value="test" override="true" />
<phingcall target="deploy" />
</target>
</project>
Znacznik
<project>
zawiera opis celów pliku build.xml oraz identyfikuje cel
deploy
jako domy
Ălny.
Jedyn
È interesujÈcÈ rzeczÈ zwiÈzanÈ z celem
deploy
jest jego atrybut
depends
, który w tym
przypadku informuje Phing,
ĝe wczeĂniej wykonany musi zostaÊ cel
get-env
. Przyjrzyjmy si
Ú wiÚc
celowi
get-env
, który jak na razie jest jedynym celem wykonuj
Ècym jakieĂ konkretne zadania.
Oto, co si
Ú stanie, gdy uruchomimy wstÚpnÈ wersjÚ pliku build.xml z wiersza poleceñ:
W pliku wyst
ÚpujÈ równieĝ cele
deploy-dev
,
deploy-test
i
deploy-prod
. Ustawiaj
È one wïa-
ĂciwoĂÊ definiujÈcÈ Ărodowisko na
dev
,
prod
lub
test
w zale
ĝnoĂci od tego, czy wdraĝamy
aplikacj
Ú odpowiednio w Ărodowisku programowania, docelowym lub testowym, po czym
wywo
ïujÈ cel
deploy
. Dzi
Úki temu moĝliwe jest wdraĝanie aplikacji w kaĝdym z tych Ărodo-
wisk bez konieczno
Ăci wpisywania jego nazwy rÚcznie.
PHP 5. Narz
Ċdzia dla ekspertów
328
Szkielet katalogów
Sposób, w jaki wdra
ĝamy naszÈ aplikacjÚ, zakïada istnienie okreĂlonej struktury katalogów.
Je
ĝeli wdraĝamy aplikacjÚ po raz pierwszy, musimy utworzyÊ katalogi, które bÚdÈ potrzebne
w nast
Úpnych krokach. Jeĝeli dokonujemy aktualizacji, wciÈĝ musimy utworzyÊ wszelkie ka-
talogi, których wcze
Ăniej brakowaïo.
Oto cel, który zajmuje si
Ú tworzeniem katalogów:
<!-- Tworzy katalogi;
Īadne istniejące katalogi nie zostaną nadpisane. -->
<target name="create-skeleton" description="Tworzy podstawowÈ strukturÚ
´katalogów dla serwisu.">
<mkdir dir="${site.home}" />
<mkdir dir="${site.home}/build" />
<mkdir dir="${site.home}/backups" />
<mkdir dir="${site.home}/logs" />
<mkdir dir="${site.home}/tmp" />
</target>
...
Zadanie
mkdir
tworzy katalog okre
Ălony za pomocÈ atrybutu
dir
.
Eksportowanie i wymeldowywanie z Subversion
Teraz przysz
ïa pora na pobranie kodu z systemu kontroli wersji. Jako ĝe w ksiÈĝce tej skupili-
Ămy siÚ na Subversion jako przykïadzie takiego systemu, bÚdziemy tu trzymaÊ siÚ tego przy-
k
ïadu. Jeĝeli jednak nasz projekt rezyduje w CVS, Git, Perforce lub jakimkolwiek innym sys-
temie, opisane tu kroki b
ÚdÈ wyglÈdaÊ bardzo podobnie. Tak siÚ skïada, ĝe Phing ma pewne
wbudowane zadania opcjonalne, pozwalaj
Èce na interakcjÚ z Subversion. Jeĝeli jednak korzy-
stamy z nieco mniej popularnego typu repozytorium, mo
ĝemy utworzyÊ wïasne zadanie Phing lub
u
ĝyÊ zadania
ExecTask
, które pozwala uruchamia
Ê pliki wykonywalne w wierszu poleceñ.
Oto fragment pliku build.xml definiuj
Ècy cel
svn-export
:
...
<target name="svn-export" description="Eksportuje pliki serwisu z Subversion do
´lokalnego katalogu docelowego.">
<!-- Konstruuje poprawne URL dla Subversion -->
<property name="svn.url" value="${svn.proto}
´${svn.fqdn}${svn.repo}${svn.project}" override="true" />
<!-- Czy has
áo dostĊpu do Subversion zostaáo podane w pliku wáaĞciwoĞci? -->
<if>
<not>
<isset property="svn.password" />
</not>
Rozdzia
á 8. • WdraĪanie aplikacji
329
<then>
<!-- Prosi u
Īytkownika o podanie hasáa dostĊpu do Subversion. -->
<input propertyname="svn.password" promptChar=":">Podaj hasïo
´dla uĝytkownika ${svn.user}, aby pobraÊ projekt
´${svn.project} z repozytorium Subversion
´${svn.fqdn}${svn.repo}</input>
</then>
</if>
<!-- Wymeldowuje projekt do
Ğrodowiska programowania. -->
<if>
<equals arg1="${environment}" arg2="dev" />
<then>
<echo>Wymeldowywanie z svn zostaïo rozpoczÚte...</echo>
<svncheckout svnpath="${svn.bin}"
repositoryurl="${svn.url}"
todir="${site.root}.${DSTAMP}${TSTAMP}"
username="${svn.user}"
password="${svn.password}" />
</then>
<!-- Eksportuje projekt na potrzeby wdro
Īenia. -->
<else>
<echo>Eksport svn zostaï rozpoczÚty ...</echo>
<svnexport svnpath="${svn.bin}"
repositoryurl="${svn.url}"
todir="${site.root}.${DSTAMP}${TSTAMP}"
username="${svn.user}"
password="${svn.password}" />
</else>
</if>
</target>
Na pocz
Ètku za pomocÈ zadania
property
konstruowany jest prawid
ïowy ciÈg URL dla Su-
bversion wskazuj
Ècy nasz projekt, po czym zostaje on zapisany we wïaĂciwoĂci
svn.url
.
Nast
Úpnie sprawdzamy, czy wïaĂciwoĂÊ
svn.password
zosta
ïa ustawiona. Dobra praktyka na-
kazuje nie wpisywa
Ê haseï do plików wïaĂciwoĂci, ale przerywa to peïnÈ automatyzacjÚ. Na-
sze rozwi
Èzanie obsïuguje obydwie moĝliwoĂci — jeĝeli nie podano w pliku wartoĂci
svn.password
, Phing poprosi u
ĝytkownika za poĂrednictwem znacznika
inputTask
o r
Úczne
wpisanie has
ïa.
Je
ĝeli nie chcemy za kaĝdym razem wpisywaÊ nazwy uĝytkownika i hasïa SSH, zawsze moĝemy
zainstalowa
Ê swój publiczny klucz SHH na serwerze, na którym rezyduje repozytorium Subver-
sion, i zmodyfikowa
Ê plik build.xml tak, by nie prosiï o podanie parametrów uwierzytelniajÈcych.
Zastosowany sposób pobrania kodu z repozytorium zale
ĝy od tego, co mamy zamiar z nim
zrobi
Ê. UĝyliĂmy tutaj instrukcji warunkowej
if-then-else
, poniewa
ĝ wymagane kroki sÈ nieco
inne w przypadku
Ărodowiska programowania. Jeĝeli pracujemy w Ărodowisku programowania,
PHP 5. Narz
Ċdzia dla ekspertów
330
dokonujemy wymeldowania z Subversion za pomoc
È zadania
svncheckout
, które pozwoli nam
zatwierdzi
Ê zmiany z powrotem do repozytorium. Jeĝeli z kolei wdraĝamy aplikacjÚ, zastosu-
jemy zadanie
svnexport
, usuwaj
Èce wszelkie dane, które w strukturze katalogów przechowuje
na w
ïasne potrzeby system Subversion.
Tworzenie plików na podstawie szablonów
Ka
ĝdy serwis lub aplikacja zawiera jakieĂ dane konfiguracyjne i istnieje wiele róĝnych sposo-
bów przechowywania tych informacji i udost
Úpniania ich na potrzeby aplikacji. Moĝna siÚ
spotka
Ê ze stosowaniem na potrzeby konfiguracji plików wïaĂciwoĂci, plików XML lub glo-
balnych zmiennych PHP. W moim serwisie korzystam z klasy
Config
zdefiniowanej w pliku
Config.php, gdzie ustawienia konfiguracyjne s
È przechowywane albo jako staïe klasy albo jako
prywatne w
ïaĂciwoĂci statyczne. Normalnie oznaczaïoby to koniecznoĂÊ rÚcznej edycji takiego
pliku, aby ustawi
Ê parametry odpowiednie dla Ărodowiska, do którego nastÚpuje wdroĝenie.
Skoro jednak staramy si
Ú zautomatyzowaÊ proces wdroĝeniowy, musimy znaleěÊ jakieĂ inne
rozwi
Èzanie.
Rozwi
Èzanie to bÚdzie polegaÊ na utworzeniu szablonu pliku Config.php, na którego podsta-
wie tworzony b
Údzie plik Config.php dostosowany do danego Ărodowiska. Oto kilka
pierwszych wierszy szablonu pliku Config.php:
class Config
{
// ustawienia i parametry uwierzytelniaj
ące dla bazy danych
const DB_VENDOR = ‘mysql';
const DB_HOSTNAME = ‘${db.fqdn}';
const DB_PORT = ${db.port};
const DB_USERNAME = ‘${db.user}';
const DB_PASSWORD = ‘${db.password}';
const DB_DATABASE_NAME = ‘${db.name}';
// lokalizacja pliku dziennika aplikacji
const LOG_FILE = ‘${log}';
...
Bardzo
ïatwo rozpoznaÊ powyĝsze bloki, które zostanÈ zastÈpione wartoĂciami przypisanymi do
sta
ïych klasy, poniewaĝ sÈ to po prostu wïaĂciwoĂci Phing. NastÚpujÈcy fragment pliku build.xml
pobiera szablon pliku Config.php i zast
Úpuje owe bloki wartoĂciami, jakie reprezentujÈ, po-
chodz
Ècymi wprost z pliku wïaĂciwoĂci.
<target name="stamp-config" description="Zapeïnia klasÚ Config.php
´wïaĂciwoĂciami konfiguracyjnymi.">
<copy todir="${site.root}.${DSTAMP}${TSTAMP}/includes/classes">
<filterchain>
<expandproperties />
</filterchain>
Rozdzia
á 8. • WdraĪanie aplikacji
331
<fileset dir="${site.root}.${DSTAMP}${TSTAMP}/config/templates">
<include name="Config.php" />
</fileset>
</copy>
</target>
Zadanie
copy
przenosi szablon Config.php do podkatalogu includes/classes katalogu zdefinio-
wanego jako katalog g
ïówny aplikacji. W zadaniu tym jednak dzieje siÚ jeszcze kilka innych
rzeczy wartych omówienia.
S
È tam dwa zagnieĝdĝone znaczniki. Pierwszy to zadanie
filterchain
, które pozwala przetwa-
rza
Ê kopiowane pliki. W tym przypadku za pomocÈ zadania
expandproperties
zast
Úpujemy
wszystkie bloki reprezentuj
Èce wïaĂciwoĂci ich wartoĂciami. Zadanie
fileset
pozwala tworzy
Ê li-
sty plików poprzez wy
ïÈczanie i wïÈczanie róĝnych plików na podstawie róĝnych kryteriów,
takich jak wyra
ĝenia regularne, dopasowujÈce pliki do Ăcieĝki lub nazwy pliku. W naszym
przypadku lista zawiera tylko jeden plik, Config.php, który do
ïÈczamy na podstawie nazwy.
Strona z komunikatem o niedost
ÚpnoĂci serwisu
W tym momencie zako
ñczyliĂmy wszystkie kroki przygotowawcze zwiÈzane z aktualizacjÈ
strony. Na potrzeby nast
Úpnych dziaïañ musimy zadbaÊ o to, by uĝytkownicy odwiedzajÈcy
stron
Ú nie zakïócali procesu aktualizacji. StÈd teĝ konieczne jest poinformowanie uĝytkowni-
ków o tymczasowej niedost
ÚpnoĂci serwisu poprzez przekierowanie caïego ruchu na specjal-
n
È stronÚ, która peïni takÈ wïaĂnie funkcjÚ. W moim serwisie jest to strona maintenance.html
w g
ïównym katalogu publicznie dostÚpnej Ăcieĝki /htdocs/.
Korzystam z Apache w roli serwera WWW, który pozwala tworzy
Ê pliki konfiguracyjne dedy-
kowane dla konkretnego katalogu, zwykle nazywane .htaccess. Aby opisywana tu metoda za-
dzia
ïaïa, naleĝy siÚ upewniÊ, czy stosowanie plików .htaccess jest uaktywnione. Poniĝszy kod
wymaga tak
ĝe wïÈczenia na serwerze moduïu mod_rewrite, za którego pomocÈ modyfikowane
jest
ĝÈdanie URL i przekierowywana jest przeglÈdarka uĝytkownika. Krótko mówiÈc, tworzymy
lokalny plik konfiguracyjny Apache, który za pomoc
È mod_rewrite tymczasowo przekierowuje
wszystkie
ĝÈdania na stronÚ maintenance.html.
<target name="disp-maint" description="Eksportuje pliki serwisu z Subversion do
´lokalnego katalogu docelowego.">
<!-- Sprawdza, czy plik .htaccess ju
Ī istnieje. -->
<available file="${site.root}/htdocs/.htaccess"
´property="htaccess_exists" type="file" />
<if>
<equals arg1="${htaccess_exists}" arg2="true" />
<then>
<!-- .htaccess istnieje; zmienia jego nazw
Ċ. -->
<move file="${site.root}/htdocs/.htaccess"
tofile="${site.home}/htdocs/.htaccess.bck"
overwrite="false" />
</then>
PHP 5. Narz
Ċdzia dla ekspertów
332
</if>
<!-- nowy plik .htaccess na potrzeby komunikatu o przerwie w dost
ĊpnoĞci serwisu -->
<echo file="${site.root}/htdocs/.htaccess" append="false">
Options +FollowSymlinks
RewriteEngine on
RewriteCond %{REQUEST_URI} !/maintenance.html$
RewriteCond %{REMOTE_HOST} !^127\.0\.0\.1
RewriteRule $ /maintenance.html [R=302,L]
</echo>
</target>
Powy
ĝszy kod zamiast od razu tworzyÊ plik .htaccess, najpierw sprawdza, czy plik taki juĝ ist-
nieje. Je
ĝeli istnieje, zmienia jego nazwÚ za pomocÈ zadania
move
. Nast
Úpnie za pomocÈ za-
dania
echo
z atrybutem
file
zapisuje niezb
Údne dyrektywy Apache w nowo utworzonym pliku
.htaccess.
Kopia zapasowa bazy danych
Poniewa
ĝ zablokowaliĂmy uĝytkownikom dostÚp do serwisu, moĝemy mieÊ pewnoĂÊ, ĝe baza
danych, z której korzysta aplikacja, nie b
Údzie uĝywana. Jeĝeli serwis, który wdraĝamy, ma
jakie
Ă zautomatyzowane zadania, korzystajÈce z bazy danych, najprawdopodobniej bÚdziemy
musieli tymczasowo je wy
ïÈczyÊ.
Nast
Úpnym krokiem jest sporzÈdzenie kopii zapasowej bazy danych. Mimo ĝe narzÚdzie, z które-
go korzystamy do migracji, obs
ïuguje moĝliwoĂÊ aktualizowania i cofania aktualizacji do dowolnej
wersji, dobr
È praktykÈ jest tworzenie kopii zapasowej bazy zawsze, gdy coĂ ulega zmianie. Na
szcz
ÚĂcie mamy procedurÚ, która tworzy kopiÚ zapasowÈ bazy oraz caïego serwisu.
Oto fragment kodu tworz
Ècy kopiÚ zapasowÈ bazy danych:
<target name="backup-db" description="Tworzy kopiÚ zapasowÈ bazy danych przez
´jej aktualizacjÈ.">
<!-- Czy has
áo do bazy zostaáo podane w pliku wáaĞciwoĞci? -->
<if>
<not>
<isset property="db.password" />
</not>
<then>
<!-- Prosi u
Īytkownika o podanie hasáa do bazy danych. -->
<input propertyname="db.password" promptChar=":">Podaj hasïo
´uĝytkownika ${db.user} dla bazy ${db.name}</input>
</then>
</if>
<!-- Wykonuje zewn
Ċtrzne polecenie mysqldump, aby utworzyü kopiĊ zapasową bazy
´
danych.
-->
Rozdzia
á 8. • WdraĪanie aplikacji
333
<exec command="${extern.mysqldump} --quick --password=${db.password} –
´user=${db.user} ${db.name} > ${db.name}.${DSTAMP}${TSTAMP}.sql"
dir="${db.backup.dir}"
escape="false" />
<!-- kompresja pliku zrzutu bazy -->
<zip destfile="${db.backup.dir}/${db.name}.${DSTAMP}${TSTAMP}.sql.zip">
<fileset dir="${db.backup.dir}">
<include name="${db.name}.${DSTAMP}${TSTAMP}.sql" />
</fileset>
</zip>
<!-- Usuwa oryginalny plik zrzutu, aby zaoszcz
Ċdziü miejsce. -->
<delete file="${db.backup.dir}/${db.name}.${DSTAMP}${TSTAMP}.sql" />
</target>
Zaczynamy od sprawdzenia, czy has
ïo do bazy danych zostaïo podane w pliku wïaĂciwoĂci.
Je
ĝeli nie, uĝytkownik jest proszony o jego wpisanie w trybie interaktywnym z wiersza pole-
ce
ñ. RozwiÈzanie to powinno wydawaÊ siÚ juĝ znajome, poniewaĝ podobne zostaïo zastoso-
wane przy pobieraniu has
ïa do systemu Subversion.
Nast
Úpnie za pomocÈ zadania
exec
uruchamiane jest zewn
Útrzne polecenie, konkretnie na-
rz
Údzie
mysqldump
, eksportuj
Èce schemat i zawartoĂÊ bazy danych do pliku tekstowego. Plik
ten jest kompletnym obrazem stanu bazy i mo
ĝe byÊ uĝyty do przywrócenia bazy dokïadnie
do stanu z chwili jego utworzenia. Ponownie do bazy pliku do
ïÈczany jest datownik, aby byïo
wiadomo, kiedy dok
ïadnie zostaï utworzony.
Atrybut
command
zadania
exec
zawiera polecenie, jakie ma zosta
Ê wykonane w wierszu poleceñ
po przej
Ăciu do katalogu wskazanego atrybutem
dir
. Atrybut
escape
to warto
ĂÊ logiczna, która
precyzuje, czy znaki specjalne pow
ïoki systemowej majÈ zostaÊ poprzedzone znakiem uciecz-
ki przed wykonaniem polecenia. Opis innych atrybutów obs
ïugiwanych przez zadanie
exec
mo
ĝna znaleěÊ w dokumentacji.
Pliki zrzutu bazy danych s
È po prostu plikami tekstowymi i jako takie doskonale nadajÈ siÚ do
kompresji, która pozwoli zaoszcz
ÚdziÊ miejsce na dysku. Na szczÚĂcie Phing udostÚpnia za-
danie kompresuj
Èce pliki za pomocÈ algorytmu ZIP. Podobnie jak wczeĂniejsze zadanie
copy
,
zadanie
zip
zawiera w sobie znacznik
fileset
okre
ĂlajÈcy, jakie pliki majÈ zostaÊ wïÈczone do
archiwum. W naszym przypadku kompresji poddajemy jeden plik.
Wreszcie po skompresowaniu pliku zrzutu bazy danych mo
ĝemy usunÈÊ oryginalny (nie-
skompresowany) plik, u
ĝywajÈc zadania
delete
. Co prawda zadanie
delete
obs
ïuguje kilka
innych atrybutów, ale tutaj u
ĝywamy jedynie atrybutu
file
wskazuj
Ècego plik przeznaczony
do usuni
Úcia.
Warto te
ĝ zwróciÊ uwagÚ, ĝe katalog, w którym przechowujemy kopie zapasowe, to jeden
z katalogów utworzonych wcze
Ăniej w celu
create-skeleton
.
PHP 5. Narz
Ċdzia dla ekspertów
334
Migracje bazy danych
Po utworzeniu kopii zapasowej bazy danych mo
ĝemy zastosowaÊ wszelkie zmiany do jej
schematu i zawarto
Ăci. W tym celu Phing udostÚpnia bardzo przydatne zadanie
dbdeploy
. Po-
zwala ono utworzy
Ê pliki zawierajÈce zmiany w bazie danych. W plikach tych wstawia siÚ kod
SQL potrzebny do zaktualizowania bazy oraz kod SQL potrzebny, by wycofa
Ê wprowadzone
zmiany. Te dwie sekwencje kodu SQL s
È oddzielone sekwencjÈ
-- //@UNDO
.
Nazwa pliku powinna opisywa
Ê jego dziaïanie. Musi takĝe zaczynaÊ siÚ od liczby, która wska-
zuje na kolejno
ĂÊ, w jakiej poszczególne pliki migracji majÈ byÊ przetwarzane. Pliki o niĝszym
numerze s
È wykonywane wczeĂniej.
Aby „pami
ÚtaÊ”, która migracja zostaïa zastosowana, narzÚdzie
dbdeploy
wymaga w
ïasnego
mechanizmu
Ăledzenia:
CREATE TABLE `changelog` (
`change_number` bigint(20) NOT NULL,
`delta_set` varchar(10) NOT NULL,
`start_dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
´CURRENT_TIMESTAMP,
`complete_dt` timestamp NULL DEFAULT NULL,
`applied_by` varchar(100) NOT NULL,
`description` varchar(500) NOT NULL,
PRIMARY KEY (`change_number`,`delta_set`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Aby wygenerowa
Ê tabelÚ
users
, utworzy
ïem plik db/deltas/1-create-users.sql o nastÚpujÈcej
zawarto
Ăci:
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`login` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`email` varchar(100) DEFAULT ‘',
`active` tinyint(1) NOT NULL DEFAULT ‘1',
`date_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
´CURRENT_TIMESTAMP,
`date_added` timestamp NOT NULL DEFAULT ‘0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_login` (`login`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
-- //@UNDO
DROP TABLE IF EXISTS `users`;
Instrukcja
CREATE TABLE
reprezentuje aktualizacj
Ú, a
DROP TABLE
wycofanie tej aktualizacji —
tworzy to dwukierunkow
È ĂcieĝkÚ migracyjnÈ.
Rozdzia
á 8. • WdraĪanie aplikacji
335
Zadanie
dbdeploy
nie wykonuje zapytania SQL, tylko je tworzy. To dlatego potrzebne jest za-
danie
exec
, które wykona wygenerowane zapytanie aktualizuj
Èce za poĂrednictwem klienta
tekstowego MySQL. Oto kod celu aktualizuj
Ècego bazÚ danych:
<target name="deploy-db" description="Uruchamia migracje SQL aktualizujÈce
´schemat i zawartoĂÊ bazy danych.">
<!--
àaduje zadanie dbdeploy. -->
<taskdef name="dbdeploy"
´classname="phing.tasks.ext.dbdeploy.DbDeployTask"/>
<!-- Generuje SQL aktualizuj
ący bazĊ danych do najnowszej migracji. -->
<dbdeploy url="mysql:host=${db.fqdn};dbname=${db.name}"
userid="${db.user}"
password="${db.password}"
dir="${site.root}.${DSTAMP}${TSTAMP}/db/deltas"
outputfile="${site.home}/build/db-upgrade-${DSTAMP}${TSTAMP}.sql"
undooutputfile="${site.home}/build/
´db-downgrade-${DSTAMP}${TSTAMP}.sql" />
<!-- Wykonuje kod SQL za pomoc
ą tekstowego klienta mysql. -->
<exec
command="${extern.mysql} -h${db.fqdn} -u${db.user} -p${db.password}
´${db.name} < ${site.home}/build/db-upgrade-
´${DSTAMP}${TSTAMP}.sql"
dir="${site.home}/build"
checkreturn="true" />
</target>
...
Udost
Úpnianie serwisu
To ju
ĝ prawie koniec. WymeldowaliĂmy i zmodyfikowaliĂmy serwis, utworzyliĂmy kopiÚ zapa-
sow
È bazy i dokonaliĂmy jej aktualizacji. Pozostaïo teraz „przeïÈczyÊ” siÚ ze starej wersji ser-
wisu na nowo utworzon
È. Tym zajmuje siÚ cel
publish-site
:
<target name="publish-site" description="Aktywuje nowÈ wersjÚ serwisu
´i restartuje serwer Apache, aby uaktywniÊ wszystkie zmiany.">
<!-- Dowi
ązanie symboliczne do zewnĊtrznej biblioteki. -->
<exec command="${extern.ln} -s ${zend_dir}"
dir="${site.root}.${DSTAMP}${TSTAMP}/includes/libraries"
escape="false" />
<!-- Usuwa dowi
ązanie symboliczne do aktywnej kopii serwisu. -->
<delete file="${site.root}" />
<!-- Dowi
ązanie symboliczne do najnowszej wersji serwisu. -->
<exec command="${extern.ln} -s ${site.fqdn}.${DSTAMP}${TSTAMP}
´${site.fqdn}"
dir="${site.home}"
escape="false" />
PHP 5. Narz
Ċdzia dla ekspertów
336
<!-- Przeprowadza p
áynny restart serwera Apache, aby uwzglĊdniü wszystkie zmiany.
´
Nowa wersja serwisu staje si
Ċ dostĊpna!!! -->
<exec command="${extern.sudo} ${extern.apachectl} graceful"
´escape="false" />
</target>
Na pocz
Ètku, ponownie za pomocÈ zadania
exec
, tworzymy dowi
Èzanie symboliczne do kopii
szkieletu Zend, która jest wymagana, by serwis móg
ï dziaïaÊ. Potem zadaniem
delete
usu-
wamy dowi
Èzanie symboliczne, które wskazuje na poprzedniÈ wersjÚ serwisu. NastÚpnie,
znowu zadaniem
exec
, tworzymy nowe dowi
Èzanie symboliczne do wersji serwisu, która wïa-
Ănie zostaïa wymeldowana z Subversion i przygotowana do wdroĝenia.
W ostatnim kroku nakazujemy serwerowi Apache prze
ïadowanie plików konfiguracyjnych,
aby upewni
Ê siÚ, ĝe wszystkie zmiany zostanÈ od razu zastosowane. Poniewaĝ serwer Apache
nie dzia
ïa w przypadku mojego uĝytkownika, muszÚ zastosowaÊ polecenie
sudo
, które spowo-
duje wy
Ăwietlenie proĂby o podanie hasïa administratora w celu wykonania tej czynnoĂci.
Ostateczna wersja pliku konsolidacyjnego
Skoro skonstruowali
Ămy poszczególne cele, moĝemy je poïÈczyÊ w finalnÈ wersjÚ pliku build.xml.
Kompletny listing mo
ĝna zobaczyÊ w doïÈczonych do ksiÈĝki przykïadach kodów z tego rozdziaïu.
Po uruchomieniu tego pliku w
Ărodowisku programowania z wiersza poleceñ otrzymujÚ na-
st
ÚpujÈcy rezultat, który rozbiïem na dwa zrzuty, aby wszystko pomieĂciÊ.
Rozdzia
á 8. • WdraĪanie aplikacji
337
Ca
ïkiem niezïy wynik. W nieco ponad dziewiÚÊ sekund udaïo mi siÚ wymeldowaÊ projekt
z Subversion, utworzy
Ê kilka plików z szablonów, wstawiÊ stronÚ z informacjÈ o niedostÚpnoĂci
serwisu, utworzy
Ê kopiÚ zapasowÈ bazy i zaktualizowaÊ bazÚ, utworzyÊ róĝne dowiÈzania symbo-
liczne i katalogi, po czym zrestartowa
Ê serwer WWW. Wliczam w to czas poĂwiÚcony na wpi-
sanie has
ïa administratora przed restartem serwera.
Spójrzmy teraz na zmienion
È strukturÚ katalogów serwisu. Moĝemy cofnÈÊ siÚ o parÚ stron i po-
równa
Ê go do stanu sprzed zmiany jego struktury w celu uïatwienia aktualizacji.
Aby dowie
ĂÊ, jak ïatwo moĝemy teraz wdraĝaÊ serwis, doïÈczyïem w listingu skrypty aktualizujÈce
baz
Ú danych i cofajÈce jej aktualizacjÚ (db-upgrade.xxxx.sql i db-upgrade.xxxx.sql), a takĝe gïówne
katalogi wcze
Ăniej wdroĝonych aplikacji, które zostaïy teraz zarchiwizowane (dev.waferthin.
com.xxxx). Niestety, listing jest tak d
ïugi, ĝe muszÚ podzieliÊ go na dwa zrzuty ekranowe (rysunki
na nast
Úpnej stronie).