You are on page 1of 84

AKTUALNOŚCI 6

Spis treści
Przedstawiamy garść najciekawszych wiadomo-
ści dla developerów PHP.

Opis CD 8
W Egipcie nas nie lubią!
W redakcji uważamy, że lepiej jest robić pismo Dla deweloperów PHP, niż Prezentujemy zawartość płyty i sposób działania
pismo O PHP. Różnica między Dla i O jest dość istotna. W pierwszym przy- najnowszej wersji naszej dystrybucji PHP Solu-
padku pismo traktuje o wszystkim, co może potencjalnie zainteresować tions Live.
i przydać się programiście PHP. Natomiast pismo O PHP jest poświęcone
wyłącznie (lub prawie) tej technologii. WYWIAD
My tworzymy magazyn w pierwszym wariancie. Dlatego w obecnym
Wywiad z Tobiasem Schlittem,
numerze znajdziecie artykuł o video streamingu, w którym omawiamy jak
jednym z głównych deweloperów
opublikować własną galerię filmów we Flashu na WWW, na wzór popular-
platformy eZ components 10
nego Google Video. Dowiecie się też, jak zarobić na PHP i czy opłaca się
zostać Freelancerem. Dariusz Pawłowski
Jeśli interesuje Was, co IBM ma wspólnego z PHP, zachęcam do artyku- Tobias pracuje jako deweloper w firmie eZ sys-
łu o łączeniu DB2 i PHP. Ten mariaż to dowód na ogromne zainteresowanie tems. Obecnie zajmuje się rozwijaniem platfor-
gigantów informatycznych naszą technologią. Przedstawimy też bardzo faj- my eZ components. Jest dobrze znanym eksper-
nego i profesjonalnego CMS-a – TYPO3. System w nowej odsłonie oferuje tem PHP. Udziela się też m.in. w projekcie PE-
naprawdę ogromne możliwości. Jeśli szukasz CMS-a do stworzenia własnej AR, gdzie rozwija kilka pakietów.
witryny, koniecznie musisz zapoznać się z TYPO3! Bardzo prawdopodobne,
że to właśnie na niego padnie wybór.
Nie zabraknie też artykułów dla bardziej zaawansowanych: pokażemy, DLA POCZĄTKUJĄCYCH
jak budować elastyczne aplikacje w oparciu o kontenter IoC, czy tworzyć
Łączymy DB2 i PHP 12
własne rozszerzenia dla PHP z wykorzystaniem Zend API.
Na koniec ciekawostka: mapka zamieszczona poniżej przedstawia wy- Artur Wroński, Piotr Pietrzakk
korzystanie PHP w różnych regionach świata. Kolor zielony oznacza naj- Tworzysz rozbudowaną aplikację, którą bę-
większe (np. Ukraina – 69%), a czerwony najmniejsze zainteresowanie (np. dziesz często rozbudowywał i chcesz uniknąć
Egipt 3,93 %) technologią PHP. Kolor żółty oznacza średnią. Z rysunku wyni- żmudnych, czasochłonnych i podatnych na błę-
ka, że najbardziej lubią nas na Ukrainie i w Rosji. Kto wie, może zaczniemy dy modyfikacji struktury tabel i relacji bazodano-
wydawać w tych językach). wych. Przedstawiamy Ci DB2: solidną i rozbu-
dowaną bazę, która umożliwia przechowywanie i
Przyjemnej lektury, operacje na danych w postaci XML-owej.
Redakcja PHP Solutions
BEZPIECZEŃSTWO
Zaawansowane ataki SQL
Injection 20
Mike Shema
Ataki SQL Injection są wymierzone w trzon apli-
kacji webowej – bazę danych – i umożliwiają in-
truzowi zdobycie, modyfikację lub usunięcie do-
wolnych danych. Zrozumienie zasad działania
SQL Injection jest konieczne do wypracowania
odpowiednich metod obrony.

NARZĘDZIA
TYPO3 od kuchni, czyli wymarzony
portal w zasięgu ręki 28
Jean-Gael Rouchon
Źródło: http://www.nexen.net/ Zarządzasz witryną internetową, która ma zhie-
rarchizowaną strukturę stron, a ich zawartość
jest uzupełniana przez wielu redaktorów. Zależy
Ci na pełnej swobodzie projektowania tego sajtu
oraz łatwości jego tworzenia i rozbudowy. Przed-
stawiamy TYPO3: potężny, elastyczny i solidny
system CMS klasy Enterprise, który jest łatwy w
rozbudowie, co umożliwia ciągłe dostosowywa-
Jeśli jesteś zainteresowany zakupem licencji na wydawanie naszych pism prosimy o kontakt: nie go do Twoich potrzeb.
Monika Godlewska monikag@software.com.pl tel.: 48 22 887 12 66, fax: 48 22 887 10 11

4 www.phpsolmag.org PHP Solutions Nr 5/2006


KASA DLA WEBMASTERA
Spis treści
Freelancing – zostań wolnym
strzelcem 38
Krzysztof Trynkiewicz
Jesteś programistą i chcesz wziąć udział w cie-
kawym projekcie i zarobić trochę pieniędzy? A Pytania dotyczące Strona WWW/Forum
może potrzebujesz kogoś, kto wykona dla Cie-
prenumeraty strona www: www.phpsolmag.org
tel. (22) 887 14 44 Tu znajdą Państwo informacje
bie witrynę internetową, aplikację dla księgowo- e-mail: pren@software.com.pl dotyczące aktualnych i przyszłych
ści czy grafikę? Dzięki serwisom freelancingo- Software Wydawnictwo Sp. z o.o. numerów magazynu PHP Solutions.
wym każdy z Was znajdzie to, czego potrzebuje dział prenumeraty
przy minimalnym lub żadnym ryzyku. ul. Piaskowa 3 Forum: www.phpsolmag.org/newforum
01-067 Warszawa Zachęcamy do dyskusji na naszym
forum. Czekamy na propozycje
DLA ZAAWANSOWANYCH CD tematów, które chcieliby Państwo
tel. (22) 887 14 44
Zend API – tworzymy własne znaleźć w najbliższym numerze pisma.
e-mail: cd@software.com.pl
Zapraszamy także do wymiany
rozszerzenie dla PHP 42 Software Wydawnictwo Sp. z o.o.
poglądów z innymi fanami PHP.
Defekty CD/DVD
Marcin Staniszczak ul. Piaskowa 3 Cena
Twój skrypt działa zbyt wolno? Wydaje Ci się, 01-067 Warszawa Prenumerata: 135 zł
że przyczyna tkwi w wydajności PHP? A mo- Przelew na konto nr:
Zamówienia 46 1440 1299 0000 0000 0391 8238
że chcesz połączyć się z inna aplikacją lub wy- /Numery archiwalne
korzystać swoją ulubiona bibliotekę z C? Roz-
Nordea Bank Polska S.A.
tel. (22) 887 14 44 II Oddział w Warszawie
wiązaniem Twoich problemów może okazać się e-mail: pren@software.com.pl
Zend API. sklep on-line: www.shop.software.com.pl
Kontakt z redakcją
Budujemy własny e-mail: redakcja@phpsolmag.org
kontener IoC, czyli jak to się Software Wydawnictwo Sp. z o.o.
robi w Hollywood? 54 Redakcja PHP Solutions
ul. Piaskowa 3
Piotr Szarwas 01-067 Warszawa
Wyobraźmy sobie, że firma, dla której stworzy-
liśmy aplikację, po jakimś czasie powiększyła Listingi wszystkich opisywanych programów zostały zamieszczone na naszej stronie
internetowej www.phpsolmag.org/pl.
się znacznie i poprosiła nas o migrację baz da-
nych do jednej centralnej pracującej w oparciu o
LDAP. Niestety, jeśli architektura naszej aplika-
cji nie została zaprojektowana prawidłowo, cze- PHP Solutions jest wydawany przez Software-Wydawnictwo Sp. z o.o.
ka nas wyjątkowo żmudna i ciężka praca. Dyrektor Wydawniczy: Jarosław Szumski
Product Manager: Adam Urbanowski adam.urbanowski@software.com.pl
Redaktorzy współpracujący: Dariusz Pawłowski dpawlowski@phpsolmag.org, Krzysztof Sobolewski
XML i PHP w praktyce 62 Stali współpracownicy: Paweł Kozłowski pkozlowski@phpsolmag.org, Krzysztof Trynkiewicz
Kierownik produkcji: Marta Kurpiewska marta@software.com.pl
Projekt okładki: Agnieszka Marchocka
Guillaume Ponçon Skład i łamanie: Sławomir Zadrożny slawekz@software.com.pl
Bazy danych, dokumenty biurowe, RSS: co- Dział reklamy: adv@software.com.pl
Prenumerata: Marzena Dmowska pren@software.com.pl
raz wicej formatów gromadzenia i przesyania Nakład: 6 000 egz.
danych opiera si na XML-u. Jego gówn zalet
Adres korespondencyjny: Software-Wydawnictwo Sp. z o.o.,
jest atwo tworzenia i przetwarzania dokumen- ul. Piaskowa 3, 01-067 Warszawa, Polska
tów XML niezalenie od platformy sprzętowej i tel. +48 22 887 10 10, fax +48 22 887 10 11
www.phpsolmag.org cooperation@software.com.pl
systemowej.
Dołączoną do magazynu płytę CD przetestowano programem AntiVirenKit firmy G DATA Software Sp. z o.o.

Własne Google Video, czyli video Redakcja dokłada wszelkich starań, by publikowane w piśmie i na towarzyszących mu nośnikach informacje
streaming w PHP 72
i programy były poprawne, jednakże nie bierze odpowiedzialności za efekty wykorzystania ich; nie gwarantuje
także poprawnego działania programów shareware, freeware i public domain.
Uszkodzone podczas wysyłki płyty wymienia redakcja.
Rafał Malinowski Wszystkie znaki firmowe zawarte w piśmie są własnością odpowiednich firm
i zostały użyte wyłącznie w celach informacyjnych.
Zastanawiałeś się, jak działa odtwarzanie fil-
mów z poziomu WWW? Podoba Ci się Go- Redakcja używa systemu automatycznego składu
Do tworzenia wykresów i diagramów wykorzystano program firmy
ogle Video? Poznaj video streaming od kuchni i
Osoby zainteresowane współpracą prosimy o kontakt: cooperation@software.com.pl
stwórz własną, webową galerię filmów. Wystar-
Druk: ArtDruk
czy podstawowa znajomość PHP. To wszystko!
Wysokość nakładu obejmuje również dodruki. Redakcja nie udziela pomocy technicznej w instalowaniu
i użytkowaniu programów zamieszczonych na płytach CD-ROM dostarczonych razem z pismem.

Zapowiedzi 82
Sprzedaż aktualnych lub archiwalnych numerów pisma po innej cenie niż wydrukowana na okładce
– bez zgody wydawcy – jest działaniem na jego szkodę i skutkuje odpowiedzialnością sądową.

Pismo ukazuje się w następujących wersjach językowych:


Zapowiedzi, artykułów, które planujemy do na- polskiej , francuskiej , niemieckiej oraz włoskiej .
stępnego wydania naszego pisma.

PHP Solutions Nr 5/2006 www.phpsolmag.org 5


Aktualności

IBM dla PHP


IBM w coraz większym stopniu zauważa po- phpMyVisites
tencjał PHP i możliwości jego wykorzystania w
oprogramowaniu przeznaczonym dla biznesu.
Projekt PHP Integration Kit for WebSphere Ap-
plication Server (WAS) Community Edition (CE)
ma za zadanie zintegrowanie PHP z aplikacja-
p hpMyVisites to darmowe (GNU/GPL)
narzędzie do sporządzania i wyświe-
tlania statystyk. Trudno systemowi cokol-
mi J2EE, w szczególności z architekturą SOA
(service-oriented architecture). WAS Commu- wiek zarzucić: oferuje wszystkie najważ-
nity Edition to lekki serwer aplikacji Java opar-
ty o opensourcowy serwer Apache Geronimo. niejsze i przydatne informacje dotyczące
Darmowy PHP Integration Kit pozwoli programi- oglądalności witryny. Interfejs jest prak-
stom PHP swobodnie rozwijać swoje aplikacje
tyczny i intuicyjny phpMyVisites, a insta-
na bazie WAS. Pozwoli to na wykorzystanie w
jednym miejscu zalet PHP i Javy, wpływając na lacja nie stanowi najmniejszego proble-
elastyczność i lepszy rozwój projektów. mu. Działają tak, jak większość tego typu
http://www.alphaworks.ibm.com/tech/
phpintwasce rozwiązań: na wybranej witrynie umiesz-
czamy prosty kod JavaScript zliczający
phundament – 100% czyste- wizyty. Informacje prezentowane użyt-
go PRADO
To framework oferujący komponenty do budo- kownikowi są przedstawione w prosty i długość/częstość przebywania na stro-
wy aplikacji WWW napisane w PRADO 3. Pro- praktyczny sposób. Do programu może- nie, strony odsyłające do naszej witryny
jekt narodził się z idei stworzenia lekkiego, sta-
bilnego i zorientowanego obiektowo kodu, który my dodawać dowolną liczbę stron, pod czy nawet słowa kluczowe jakie były po-
może być łatwo rozszerzany i wielokrotnie uży- które podepniemy statystyki. dawane podczas korzystania z wyszuki-
wany. Poza PRADO phundament wykorzystu-
W phpMyVisites możemy zarządzać warki na stronie. Dodatkowo, raport mo-
je takie narzędzia jak: Propel (narzędzie ORM,
o którym pisaliśmy w numerze 4/2005), Creole, użytkownikami, którzy otrzymują pra- że zostać wysłany mailem lub rozprowa-
phing, ImageMagic i oczywiście PHP5. Na stro- wa do odczytu/administracji dla konkret- dzany za pomocą RSS.
nie projektu znajdziemy dokumentację, wersję
demo, a nawet tutoriale video. nych stron. Program dostępny jest w 29 wersjach
Statystki przedstawiają wiele in- językowych.
ApacheCon Europe 2006 teresujących informacji o odwiedzają-
W dniach 29-30 czerwca 2006 w Dublinie, w Ir-
landii, odbyła się kolejna edycja konferencji po- cych nasze witryny, m.in: lokalizacja Licencja: GNU/GPL
święconej Apache'owi. Jej sponsorami byli tacy geograficzna, dane techniczne (system http://www.phpmyvisites.us/
giganci jak Microsoft, Sun, Intel czy Google.
Poza tematami stricte związanymi z Apache, operacyjny, rozdzielczość komputera),
poruszano też zagadnienia związane z budo-
waniem aplikacji w oparciu o PHP (np. PHP 6
& Unicode: The Tower of Babel - Next Gene-
ration, Building a Fast and Rich Web App with Zend Studio 5
PHP 5, Agile PHP Testing).
http://www.eu.apachecon.com/

DC PHP Conference 2006


W dniach 18-20 października w Waszyngtonie,
N ieprzerwany sukces języka PHP po-
ciąga za sobą ciągły rozwój coraz
bardziej zaawansowanych środowisk pro-
stworzyć funkcję, aby potem była podpo-
wiadana przez system), czy dokumenta-
cją (która dostępna będzie właśnie pod-
w USA odbędzie się konferencja poświecona
w całości PHP. Przeglądając sesje tematyczne, gramistycznych (rozbudowanych edyto- czas podpowiadania nazw funkcji). Drze-
można odnieść wrażenie, że poruszone zosta-
ną niemal wszystkie najważniejsze zagadnie-
rów) zwanych IDE (Integrated Develop- wiasta struktura plików w oknie IDE po-
nia związane z tworzeniem aplikacji w PHP (np. ment Environment). Wymagania wobec zwala na przeciąganie ich myszką np. z
bezpieczeństwo, wydajność i skalowalność, na- tych narzędzi stale rosną, dlatego PHP- FTP do dowolnie wybranej lokalizacji. Na
rzędzia, biblioteki, techniki oraz zastosowania
biznesowe PHP). owe edytory rozwijają się w dość szyb- uwagę zasługuje też integracja Zend Stu-
http://dcphpconference.com/ kim tempie. dio z Web Serwisami (generowanie pli-
Forum PHP 2006 Wśród IDE dla PHP prym wiedzie ków WSDL bezpośrednio z kodu PHP).
W dniach 9-10 listopada 2006 w Paryżu, odbę- nieprzerwanie Zend Studio. Nowe wcie- Nie ma jednak róży bez kolców: nowe
dzie się już piąta edycja spotkania dewelope-
lenie tego IDE przynosi nam kilka bardzo IDE w wersji professional kosztuje $299
rów PHP.
Wśród speakerów wystąpią między innymi Ra- ważnych funkcji i udogodnień. Już pod- (cena wersji standardowej to $99).
smus Lerdorf (twórca PHP), czy Wez Furlong czas uruchamiania aplikacji możemy ją
(jeden z deweloperów PDO).
http://www.afup.org/pages/forumphp/ debugować i podglądać efekt działania w Licencja: komercyjna
english.php zintegrowanym z IDE Internet Explorerze. http://www.zend.com/
Żeby programiści nie wchodzili sobie na products/zend_studio/
eZ Newsletter – coś więcej
niż system newslettera głowę, mogą korzystać nie tylko z CVS- professional_edition
To bardzo rozbudowany, kompletny system do a, ale również z Subversion, czyli z syste-
email marketingu stworzony przez firmę eZ sys-
tems. Program kontroluje wszystko, co związa-
mów kontroli wersji (Subversion jest now-
ne jest z wysyłką mailową: od przygotowania szym i bardziej rozbudowanym narzę-
kampanii, przez samą wysyłkę, aż po integrację
dziem). Dodano również możliwość połą-
z systemami sprzedaży online czy ERP. Takie
podejście ma zmaksymalizować efektywność czenia sFTP (secure FTP z wykorzysta-
marketingu bezpośredniego, zwiększając tzw. niem SSL) i polepszono funkcję podpo-
feedback rate (który obecnie wynosi ok. 5 %),
a w konsekwencji sprzedaż na WWW. Dodat- wiadania składni języka PHP. Zend pod-
kowo system współpracuje z telefonami komór- powiada praktycznie wszystko to, co zna-
kowymi, faksem i PDA.
http://ez.no/products/solutions/newsletter
leźć można w manualu PHP, łącznie z na-
zwami własnych funkcji (wystarczy raz

6 www.phpsolmag.org PHP Solutions Nr 5/2006


Aktualności

eZ publish
Nowości w Google Lab Online Editor
– teraz za darmo!

P rojekty Google ciągle zaskakują Edytor WYSIWYG, za który jeszcze nie dawno
trzeba było zapłacić $99, teraz dostaniemy zu-
nas innowacyjnością. Przykładowo pełnie za darmo. System początkowo sprzeda-
Google Notebook pozwala na wygodne wany był jako dodatkowo płatny komponent do
CMS-a eZ publish.
zapisywanie treści (teksty, obrazki, lin- Produkt oferuje wszystkie funkcje dobrego WY-
ki) podczas przeglądania stron WWW w SIWYG-a. Obsługuje kilka języków w tym pol-
specjalnym miejscu, na stronie Google. ski, francuski, hiszpański i włoski. Pracuje z In-
ternet Explorerem od wersji 5.5, jak również z
W każdym momencie możemy po nie przeglądarkami opartymi na Mozilli. Na stronie
sięgać, wystarczy posiadać konto Go- projektu można wypróbować wersje DEMO.
http://ez.no/products/add_ons/ez_publish_on-
ogle, np. Gmail. Dodatkowo mamy możli- line_editor
wość publikacji zbieranych przez nas in- lacyjnych na WWW. Arkusze mogą być
formacji na swojej stronie WWW. aktualizowane przez wiele osób jedno- DutchPIPE
To projekt, który oferuje nowe podejście two-
Google Trends z kolei pokazuje, jak cześnie. Bez problemu można też im- rzenia stron dla większej grupy użytkowników.
bardzo popularne są szukane informa- portować i eksportować dokumenty do Strona staję się abstrakcyjnym światem zbu-
dowanym na obiektach, które mogą być swo-
cje, np. możemy podać przyjkładową formatów CSV i XLS. Jedna kopia do- bodnie przemieszczane między poszczególny-
frazę XOOPS, Drupal, aby zobaczyć ja- kumentu na stronie WWW przyda się na mi sajtami.
Jednemu światu odpowiada jeden określony
kie było zainteresowanie tymi produkta- pewno, nawet jeśli mielibyśmy pracować zestaw obiektów. Jeśli umieścisz jakiś obiekt
mi na przestrzeni ostatnich miesięcy, a nad nim w pojedynkę. Bardzo przydatne na stronie, ktoś inny może z niego skorzystać i
przenieść go do siebie.
nawet lat (co przedstawia Rysunek). Ko- i niezmiernie praktyczne rozwiązanie!
DutchPIPE może być wykorzystany do budo-
lejnym bardzo ciekawym pomysłem jest wania różnych internetowych społeczności,
Google Spreadsheets, rozwiązanie któ- http://labs.google.com dla chatroomów, gier RPG czy sklepów online.
Ogranicza nas tylko wyobraźnia.
re pozwala na tworzenie arkuszy kalku- Licencja: MIT
http://dutchpipe.org/

Zend
poMMo – dobry email marketing za darmo Framework 0.1.4
Pojawiła się kolejna wersja rozwojowa Zend

p
Frameworka. Dodano nowe komponen-
oMMo to prosty i praktyczny system ty oraz dokumentację w 10 językach. Na stro-
newslettera dla niewymagających. In- nie projektu stworzono między innymi Com-
munity Wiki, system do zgłaszania własnych
stalacja jest zupełnie bezbolesna, a sam
pomysłów/zmian, czy miejsce do śledzenia
program nie przytłacza ilością opcji (jak zmian (Issue tracker) w projekcie. Naprawiono
na przykład PHPList), przez co administra- też wiele błędów oraz dokonano wiele ulepszeń
np. możliwość stosowania czcionki TrueType w
cja jest intuicyjna i łatwa. Narzędzie pomi- generowanych dokumentach PDF czy wspra-
mo że nie skomplikowane, znakomicie cie dla bazy DB2.
Licencja: BSD
sprawdza się w email marketingu. Moż- http://devzone.zend.com/node/view/id/606
na stworzyć formularz z kilkoma newslet-
terami i pytać użytkowników, które z nich PHP-QT
Dostępne jest już trzecie wydanie PHP-Qt, roz-
chcą otrzymywać. Bez problemu zintegru- szerzenia dla PHP5, które pozwala na wykorzy-
jemy też poMMo ze sklepem online, że- stanie narzędzia Qt (Qt4 Framework) do two-
rzenia aplikacji w PHP. Pakiet z nową wersją
by powiadamiać użytkowników o naszych ili na minutę). Mamy możliwość zatrzyma- zawiera siedem tutoriali i przykładową aplikację.
produktach. Użytkownicy mogą sami zmie- nia, a następnie wznowienia procesu wy- Została zaimplementowana też między innymi
lepsza obsługa błędów.
niać swój email, wypisywać się lub aktuali- syłki, a po utracie połączenia i jego wzno-
Licencja: GNU GPL
zować preferencje. wieniu, wysyłka jest kontynuowana. Im- http://php-qt.berlios.de/
Maile wysyłane są bardzo wydajnie. port i eksport użytkowników odbywa się z
Portale
System może wykorzystywać 4 użytkow- wykorzystaniem formatu CSV. Wysyłając
rankingowe
ników serwera SMTP i kontrolować ruch mailing, mamy do dyspozycji WYSIWYG Software-Wydawnictwo, wydawca magazy-
podczas wysyłki (np. ilość wysłanych ma- (w przypadku maili w formacie HTML), a nu PHP Solutions, uruchomiło pierwsze porta-
le newsowo-rankingowe w języku angielskim.
raz puszczony mail, może posłużyć ja- Obecnie działają już distrorankings.com oraz
ko szablon przy kolejnych wysyłkach. Do PDFdev.com.
Pierwszy z nich to ranking dystrybucji Linuksa,
wad programu na pewno trzeba zaliczyć drugi to portal dla deweloperów używających
brak opcji zbierania odbić (ang. bounces) standardu PDF.
Już niedługo, przy współpracy z naszym maga-
i jeden poziom dostępu – Administrator.
zynem, uruchomione zostaną kolejne projekty
Zanim zdecydujemy się na instalację, (m.in. poświecone systemom CMS) i to w kilku
najlepiej zacząć od wersji DEMO dostęp- wersjach językowych.
http://distrorankings.com
nej na stronie projektu. http://PDFdev.com

Licencja: GPL
http://www.iceburg.net/pommo/

PHP Solutions Nr 5/2006 www.phpsolmag.org 7


Opis CD

PHP Solutions Live

T ę wersję PHP Solutions Live zbudo-


waliśmy, opierając się o dystrybu-
cję Aurox 12 i skrypty automatycznej ge-
z repozytorium Auroxa za pomocą progra-
mu yum.
Teraz, oprócz kompletnego środowi-
neracji (www.aurox.org/pl/live). Narzędzia ska LAMP (Linux, Apache, MySQL, PHP)
dystrybucji, które nie znajdują się na do- w systemie gotowe do użycia są środo-
łączonej do pisma płycie, instalowane są wiska programistyczne, takie jak: Eclip-

Rysunek 2. Nowy atrakcyjny wygląd


se, Nvu, Quanta, BlueFish oraz wiele in-
nych charakterystycznych dla KDE i Li-
nuksa narzędzi.Na dołączonej do pisma
płycie znajduje się PHP Solutions Live
– bootowalna dystrybucja Auroxa, zawie-
rająca przydatne narzędzia, dokumenta-
cję, tutoriale i materiały dodatkowe do ar-
tykułów. Aby zacząć pracę z PHP Solu-
tions Live, wystarczy uruchomić kompu-
ter z CD. Po uruchomieniu systemu zosta-
niemy automatycznie zalogowani w syste-
mie, a po krótkiej chwili powinna urucho-
mić się przeglądarka Mozilla Firefox wraz
z Menu przedstawiającym zawartość płyty
związaną z bieżącym numerem.

Rysunek 1. Jeszcze więcej przydatnych narzędzi

Materiały dodatkowe

M ateriały dodatkowe zostały umiesz-


czone w następujących katalogach:
zawodnemu debagerowi dbg, produk-
tywnemu klientowi połączeń do baz da-
nych oraz szybkiemu i bezpiecznemu
PHP Edit jest to edytor służący do
przygotowywania skryptów PHP. Narzę-
dzie koloruje składnię, ale użytkownik
• install – hity numeru, w bieżącym nu- systemowi wdrożeniowemu. Oferuje- ma możliwość zdefiniowania własnych
merze: PhpED - zintegrowane środo- my Wam wersje 120-dniową NuSphere schematów. Możliwe jest także usta-
wisko programowania dla języków: PhpED 5.5.1. wienie nowych skrótów klawiaturowych
PHP, HTML, CSS, XML, SMARTY, Maguma Workbench wychodzi po- oraz reguł automatycznego poprawia-
XHTML; Maguma Workbench - IDP za typowe IDE tworząc IDP dla PHP i nia skryptów.
dla PHP i Python-a; PHP Edit 0.8 Python. Szybkość, solidność i moduło- Limbas jest aplikacją typu klient/
- edytor służący do przygotowywania wanie powoduję że dostępne IDE jest serwer pozwalającą nam, na szybkie
skryptów PHP; Limbas jest aplikacją bardziej dostosowane do potrzeb. Ma- tworzenie aplikacji bez potrzeby progra-
typu klient/serwer pozwalającą nam, guma Workbench włącza modułową ar- mowania. Używa tylko tabel, form i i ta-
na szybkie tworzenie aplikacji bez po- chitekturę plug-in, dzięki której flagowy kich modułów jak zarządzanie użytkow-
trzeby programowania produkt jest szybki, elastyczny i funk- nikami, manager plików czy interfejs
• ebook – książki i inne dokumenty w cjonalny. SOAP.
formacie PDF Dzięki współpracy z użytkownika- Praca z Limbas jest wybitnie bezbo-
• materialy – materiały do artykułu Ty- mi MC, wydobywamy z naszych pro- lesna – do tworzenia aplikacji po stronie
po3 duktów to, co najlepsze, przyczyniając klienta potrzebna jest tylko przeglądar-
się tym samym do owocnej współpra- ka WWW.
PhpED jest zintegrowanym środowi- cy z klientami. Dzięki swojej elastyczno- W przypadku przeglądania płyty z
skiem programowania dla języków: PHP, ści, MW wychodzi naprzeciw oczekiwa- poziomu uruchomionego PHP Solutions
HTML, CSS, XML, SMARTY, XHTML niom użytkowników, czytając niejako w Live wymienione aplikacje i wszystkie
i pozostałych. PhpED jest uniwersal- ich myślach. materiały dostępne są z podkatalogu
nym edytorem zaspakajającym najbar- Żeby pobrać wersję 90-dnio- /mnt/cdrom.
dziej wyszukane potrzeby programistów wą Magumy Workbench Core nale-
dzięki zastosowanym rozwiązaniom: za- ży zarejestrować się na stronie http://
awansowanemu edytorowi kodów, nie- www.phpsolmag.org/maguma.

8 www.phpsolmag.org PHP Solutions Nr 5/2006


Na CD
Przetestuj dowolne skrypty
bez instalacji!

2 nowe książki elektroniczne


Cross-Platform GUI Programming with wxWidgets
Subversion Version Control
HIT
Pełna 120-dniowa wersja NuSphere PhpED
5.5.1.; Maguma Workbench 90 dniowa pełna
wersja świetnego edytora; PHPEdit 0.8 – pełna
wersja bardzo popularnego edytora; Limbas 1.8.8
– pełna wersja

Rozwiązanie z artykułu
w PHP Solutions Live
Narzędzia w dystrybucji
Typo3
Kompletne środowisko do programo-
wania w PHP Studio Developer
na naszej stronie internetowej pod adresem www.phpsolmag.org/pl

na naszej stronie internetowej pod adresem www.phpsolmag.org/pl


Wszystkie listingi z artykułów zostały zamieszczone

Wszystkie listingi z artykułów zostały zamieszczone


Wywiad

Wywiad z Tobiasem Schlittem,


jednym z głównych deweloperów
platformy eZ components

Tobias pracuje jako deweloper w firmie eZ


systems. Obecnie zajmuje się rozwijaniem
platformy eZ components. Jest dobrze znanym
ekspertem PHP. Udziela się też m.in. w projekcie
PEAR, gdzie rozwija kilka pakietów.

Dariusz Pawłowski: Jak rozpocząłeś swo- ły czas szukamy dobrych programistów, ciażby dzięki pracy nad różnymi projek-
ją przygodę z eZ systems? Czy mógłbyś więc śmiało przysyłajcie swoje aplikacje. tami Open Source w przeszłości. Jakkol-
dać kilka wskazówek dla młodych progra- DP: Jak wygląda Twoja współpraca wiek, dwa razy do roku (czasami częściej)
mistów PHP marzących o pracy dla takiej z eZ systems? Masz jakieś długotermi- przyjeżdżam do Norwegii na kilka tygodni,
firmy jak Twoja? nowe zadania? Jak się komunikujecie na aby porozmawiać i podyskutować na ży-
Tobias Schlitt: Hehe, to był w pew- co dzień? wo o nowych zadaniach i projektach.
nym sensie przypadek. Kiedy przeprowa- TS: W chwili obecnej prawie wyłącz- DP: Jak rozpoczął się projekt eZ com-
dziłem się do Dortmund, mój przyjaciel nie pracuję dla eZ systems. Na początku ponents? Jaka przyświecała mu idea i jak
Sandro zasugerował mi, że powinienem kontynuowałem jeszcze prowadzenie mo- pracujecie nad nim obecnie?
starać się o pracę w eZ. Tak też zrobiłem. jej małej firmy konsultingowej, ale po ja- TS: Idea stworzenia eZ components
Dostałem pracę. Na początku zapowiada- kimś czasie poczułem się tak związany i wzięła się z planu przepisania naszego
ło się na to, że będę pracował nad projek- zidentyfikowany z eZ, że postanowiłem CMS-a eZ publish z PHP4 na PHP5. eZ
tami klientów eZ systems, ale potem De- zawiesić własną działalność i w całości publish stał się bardzo rozbudowanym
rick Rethans skierował mnie do eZ com- poświęcić się pracy dla eZ systems. Oczy- systemem, ze złożoną strukturą i więk-
ponents. I tak się tu znalazłem. wiście, mam długoterminowe zadania. Je- szość jego wewnętrznych bibliotek ma
Jeśli chcielibyście pracować dla eZ stem członkiem teamu odpowiedzialnego dziwne API, dodatkowo bez dobrej doku-
system, musicie spełniać trzy warunki: a) za eZ components i zapowiada się na to, mentacji. Tak więc postanowiliśmy prze-
powinniście kochać PHP, bo jeśli kochasz że tak przez najbliższy czas zostanie. Po- nieść kod wszystkich bibliotek do jedne-
to co robisz, robisz to dobrze, b) powinni- nieważ mieszkam w Dortmund, a prawie go niezależnego projektu – eZ compo-
ście kochać Open Source i filozofię, któ- cała reszta zespołu w Skien, w Norwegii, nents. W trakcie tworzenia komponen-
ra się za tym kryje; eZ systems jak najbar- przeważnie komunikujemy się przez email tów dokonywana jest refaktoryzacja ko-
dziej opiera się na tej idei, c) powinniście lub skype'a. Ten sposób współpracy działa du i tworzona nowa dokumentacja. Pro-
umieć pracować w zespole, zgranie się z całkiem dobrze, ponieważ cały team przy- jekt eZ components ma teraz na celu:
całą firmą jest dla nas bardzo ważne. Ca- wykł do tego rodzaju komunikacji, cho- a) stanowić fundament dla eZ publish,

10 www.phpsolmag.org PHP Solutions Nr 5/2006


Wywiad

b) stanowić solidną i profesjonalną plat- nej integracji. Poza standardowymi kom- na jeśli chodzi o sam język programowa-
formę dla innych. ponentami, które znajdziemy też w Zend nia. Tak naprawdę to nie widzę specjal-
Tworzenie kodu oparte jest o testy i Framework i w wielu innych framewor- nie nowych funkcjonalności, jakie miały-
dokumentację. Oznacza to, że najpierw kach, posiadamy wiele fajnych rozwiązań, by się pojawić w PHP w przyszłości. Ok,
projektujemy wszystkie API, potem je do- które są jeszcze unikalne w świecie PHP. można pomyśleć o przestrzeni nazw (ang.
kumentujemy, następnie piszemy testy Jako przykład możemy podać ImageCo- name spaces), co było dyskutowane już w
jednostkowe, a na koniec przechodzimy nversion component (w przypadku trans- przeszłości czy o przeciążaniu operatorów
do realnej implementacji. W chwili obec- formacji Thumbnail dokonywane jest ska- (ang. operator overloading), co w końcu
nej mamy wersję 1.1, będącą już ulepsze- lowanie obrazków, kompresowanie, kon- nie zostanie wdrożone. Dodanie obsługi
niem poprzedniej. wersja i zapisanie w formacie JPEG) Unicode w PHP6 jest bardzo ważnym kro-
Po ukazaniu się kolejnej wersji ca- Innym ciekawym rozwiązaniem jest kiem, podobnie jak usunięcie takich relik-
ły zespół spotyka się. Wtedy decyduje- Mail component. Nie tylko tworzy on zło- tów jak register_globals. Ale samo PHP6
my, które z ulepszeń zostaną zaimple- żone maile (załączniki czy kilka typów nie będzie jakimś rewolucyjnym posunię-
mentowane w następnych wersjach (wła- MIME mails), wysyła je za pomocą PHP- ciem, tak jak to miało miejsce pomiędzy
śnie takie spotkanie miało miejsce nie- owej funkcji mail() lub serwera SMTP), PHP4 a PHP5, czy też PHP3 i PHP4. Nie
dawno podczas konferencji eZ, gdzie dys- ale także odczytuje maile z serwerów chciałbym się też wypowiadać w kwestii
kutowaliśmy o wersji 1.2). Ustalamy nowe POP / IMAP. Mamy naprawdę wiele świet- PHP7, gdyż byłoby to wróżenie z fusów.
funkcjonalności i ulepszenia dla obecnych nych komponentów, tak więc zachęcamy Jestem pewien, że praca nad języ-
oraz nowych komponentów. Taki proces do zapoznania się z nimi. kiem będzie przebiegała w większym
tworzenia oprogramowania działa bardzo DP: Lubisz Open Source, czy preferu- stopniu na tworzeniu fajnych rozszerzeń
dobrze biorąc pod uwagę, że stawiamy na jesz swoje własne rozwiązania? niż na rozwijaniu samego języka. Ale po-
elastyczność i rozszerzalność podczas TS: Osobiście kocham Open Source. czekajmy, jak zdecydują ludzie odpowie-
implementacji. Jeśli macie jakieś pomysły Od ponad 2 lat używam wyłącznie Linuksa dzialni za rozwój PHP.
i propozycje dla eZ components, śmiało i jestem z niego bardzo zadowolony. Głów- DP: Jaki jest roadmap dla eZ compo-
możecie je zgłaszać na naszej liście ma- ny plus oprogramowania Open Source po- nents i Twoje plany na najbliższy rok, dwa?
ilingowej. Zawsze jesteśmy otwarci na no- lega na tym, że jest tworzone przez ludzi, TS: Zakres prac nad eZ components
we pomysły. którzy faktycznie go używają i wiedzą, ja- ostatnio nieco się zmienił i poszerzył wraz
DP: Czy według Ciebie Zend Frame- kich funkcjonalności potrzebują. Dzięki te- z ulepszaniem eZ platform, które zapewni
work stanowi konkurencję dla eZ compo- mu powstaje dużo lepsze oprogramowa- w przyszłości kompletną platformę do two-
nents? Czy mógłbyś opowiedzieć o kil- nie w porównaniu do wytwarzanego przez rzenia aplikacji w PHP. eZ platform będzie
ku najważniejszych funkcjach eZ compo- zamkniętą grupę kilku deweloperów, któ- podstawą do zbudowania nowej wersji eZ
nents i odnieść się do Zend Framework? rzy nie są zainteresowani późniejszym wy- publish Telemark. eZ components będą
TS: Nie wydaje mi się, że Zend Fra- korzystaniem efektu swojej pracy. Poza stanowiły najniższy poziom rozwijania apli-
mework stanowi konkurencję dla eZ com- tym lubię dewizę, która mówi: spraw, aby kacji. Na komponentach zbudowany zo-
ponents. Oczywiście, mają trochę podo- najlepsze oprogramowanie przetrwało, w stanie eZ application server – kompletna
bieństw, ale tak naprawdę każde z nich przeciwieństwie do spraw, żeby przetrwała platforma do budowania aplikacji. Na jego
działa inaczej. eZ components to nie fra- firma z najlepszym cash-flow. Ponadto ko- bazie powstanie właśnie nowe eZ publish.
mework, podczas gdy nazwa Zend Fra- rzystając z Open Source mogę swobodnie Oznacza to, że eZ components będą cią-
mework wskazuje już na framework. eZ samemu dodać brakujące funkcjonalności. gle rozwijane i już teraz spodziewajcie się
components to kolekcja luźno powiąza- Już kilkukrotnie zdarzało mi sie dodawać wielu nowych i potrzebnych komponentów
nych, przeważnie niezależnych kompo- taką brakującą funkcjonalność. (takich jak eZ Graph czy eZ Feed).Ciągle
nentów do budowy aplikacji. Korzystając DP: Czy wzorujesz się na rozwiąza- też rozwijamy obecne komponenty doda-
z typowego frameworka, w mniejszym lub niach Javy czy ASP w swoich projektach? jąc określone funkcjonalności (jak wspie-
większym stopniu, jesteśmy zmuszeni do TS: Nie, właściwie to nie lubię obu ranie relacji dla PersistentObject).
budowania aplikacji z wykorzystaniem je- tych technologii. Kodowałem dużo w Ja- Ogólnie rzecz biorąc, chcemy zrobić
go specyficznego podejścia itd. My daje- vie na uniwersytecie, ale nie przypadła mi kompletną platformę dla każdego, gdzie
my Ci możliwość wyboru i sposobu uży- ona do gustu. Jeśli chodzi o ASP, to nie ludzie będą mogli wybrać, czego chcą
cia odpowiednich komponentów z naszej mam potrzeby korzystania z tego języka, używać: eZ components jako część na-
kolekcji. Możesz łączyć je z innymi roz- od kiedy znam i kocham PHP. Obecnie szych bibliotek, eZ application server ja-
wiązaniami takimi jak PEAR. Dla przykła- pracuję trochę w C# (i Mono). Język ten ko cała platforma lub eZ publish jako kom-
du: jeśli chciałbyś użyć nasz system sza- jest naprawdę fajny i używam go głów- pletny CMS. Moje osobiste plany na naj-
blonów, ale nie podoba ci się komponent nie do tworzenia GUI, nie dla aplikacji sie- bliższe 2 lata to: a) skończenie studiów, b)
do internacjonalizacji, nie musisz stoso- ciowych. pomaganie eZ systems w robieniu świet-
wać ich razem. DP: Jaki będzie według Ciebie kolejny nych produktów. Myślę, że oba punkty są
Tak więc, najważniejsze cechy obu krok w rozwoju języka PHP? do wykonania.
platform różnią się, gdyż w eZ compo- TS: To jest dość trudne pytanie. Obec- DP: Dzięki za rozmowę!
nents nie wymuszamy praktycznie żad- na wersja PHP jest już prawie komplet- TS: To była przyjemność!

PHP Solutions Nr 5/2006 www.phpsolmag.org 11


Dla początkujących

Łączymy DB2 i PHP


Stopień trudności: lll
Artur Wroński, Piotr Pietrzak

Tworzysz rozbudowaną aplikację, którą będziesz


często rozbudowywał i chcesz uniknąć żmud-
nych, czasochłonnych i podatnych na błędy
modyfikacji struktury tabel i relacji bazodano-
wych. Przedstawiamy Ci DB2: solidną i rozbu-
dowaną bazę, która umożliwia przechowywanie
i operacje na danych w postaci XML-owej, a jej
współpraca z PHP jest bezproblemowa i dobrze
udokumentowana.

B
aza danych IBM DB2 była za- godniu. Co ważne, nie nakłada ograni-
wsze kojarzona z obsługą du- czeń na rozmiar bazy danych czy licz-
żych, krytycznych dla działalno- bę obsługiwanych połączeń. Możemy
ści firmy systemów. Przez ostatnie lata również wykorzystywać DB2 Express-
IBM położył bardzo duży nacisk na do- C do celów komercyjnych, a także roz-
pracowanie funkcji autonomicznych, któ- powszechniać go razem z naszymi apli-
re umożliwiają stosowanie bazy także w kacjami. Razem z bazą dostarczane
małych i średnich systemach, w których są narzędzia graficzne do administra-
W SIECI głównym priorytetem jest bezobsługowa cji. Najnowsza wersja DB2 zawiera tak-
i niezawodna praca. Edycja DB2 Express że DB2 Developer Workbench – opar-
jest przykładem takiego silnika baz da-
• http://ibm.com/db2/v9/ nych – w pełni funkcjonalnego, nadają-
– strona domowa DB2 9 cego się do zastosowania na kompute- Co powinieneś wiedzieć...
• http://ibm.com/ Potrzebna będzie znajomość skład-
developerworks/db2 – rach mających maksymalnie po dwa pro-
ni SQL-a i podstaw programowania w
portal dla deweloperów cesory i działających pod systemami Li- PHP (4 i 5) z wykorzystaniem baz da-
korzystających z DB2
• http://www.redbooks.
nux i Windows. nych. Przydatna będzie również wie-
ibm.com/abstracts/ Dla wszystkich deweloperów aplika- dza na temat PDO i prepared state-
sg247218.html – książka o ments.
cji PHP szczególnie interesujący jest dar-
tworzeniu aplikacji PHP dla
IBM DB2 mowy odpowiednik DB2 Express: DB2 Co obiecujemy...
• http://www.ibm.com/ Pokażemy, jak się łączyć z bazą da-
Express for Community, zwany również
developerworks/db2/library/ nych DB2 Express oraz korzystać z
techarticle/dm-0511singh/ a DB2 Express-C. nowego typu danych pozwalające-
– tutorial o używaniu funkcji Darmowa edycja różni się od komer- go na przechowywanie dokumentów
XML-owych DB2 XML.
• http://www.zend.com/core/
cyjnej głównie brakiem całodobowego
ibm/ – Zend Core for IBM wsparcia technicznego przez 7 dni w ty-

12 www.phpsolmag.org PHP Solutions Nr 5/2006


Łączymy DB2 i PHP Dla początkujących

tą o platformę Eclipse darmową apli-


kację do tworzenia procedur składowa-
nych. Ułatwia ona tworzenie procedur
składowanych i funkcji, a także pozwa-
la na graficzne budowanie zapytań SQL
oraz XQuery, przygotowywanie schema-
tów XML, tworzenie obiektów bazodano-
wych, czy podgląd przykładowej zawar-
tości bazy danych.
W artykule omówimy metody łącze-
nia się z bazami DB2 8.2 w aplikacjach
PHP z oraz przybliżymy możliwości hie-
rarchicznego silnika do obsługi danych
XML, zaimplementowanego w nowej
wersji DB2 9.

Szybki start
Do rozpoczęcia pracy z DB2 i PHP po-
trzebne są:

• działający serwer HTTP (Apache lub


IIS),
• parser PHP (najlepiej w wersji 5.x),
• zainstalowany na serwerze klient DB2,
• działający serwer DB2.
Rysunek 1. Konsola konfiguracyjna Zend Core for IBM – strona powitalna

Użycie Zend Core for IBM (http:// przy użyciu konsoli dostępnej pod adre- kół DRDA (Distributed Relational Data-
www.zend.com/core/ibm/) pozwala na sem http://server:port/ZendCore/ (Ry- base Architecture).
automatyzację całego procesu instala- sunek 1). Komunikacja interfejsu bazodanowe-
cji i konfiguracji. Zend Core for IBM jest Interfejs konsoli administracyjnej go po stronie PHP z DB2 następuje po-
zintegrowanym środowiskiem do budo- Zend Core for IBM jest czytelny i intu- przez wykorzystanie innego, natywnego
wy aplikacji z użyciem PHP i baz DB2 icyjny (Rysunek 2). Pozwala na zmia- interfejsu CLI (Call Level Interface), któ-
lub Cloudscape (wersja opensourco- nę wszystkich niezbędnych parametrów ry jest rozszerzeniem języka PHP napi-
wa jest udostępniana pod nazwą Apa- konfiguracyjnych silnika PHP (php.ini) sanym w C, skompilowanym z biblioteka-
che Derby). Jest to certyfikowana przez przy użyciu przeglądarki internetowej mi obsługi DB2 i należącym do repozyto-
Zend i IBM wersja PHP dla serwerów oraz włączanie i wyłączanie dostępnych rium PECL.
baz danych IBM, zawierająca wszelkie w PHP rozszerzeń, np. obsługi SOAP Dla PHP 5.x istnieje kilka rozsze-
niezbędne dodatki i sterowniki potrzeb- czy XML-RPC. rzeń, które pozwalają na komunikację
ne do komunikacji z wymienionymi ba- Przydatna jest również możliwość z silnikiem DB2 oraz korzystanie z da-
zami danych. Zend Core for IBM jest podglądu bieżącego obciążenia serwera nych i obiektów. Są to:
środowiskiem darmowym. Opcjonalnie HTTP i silnika PHP oraz uzyskania infor-
można zamówić płatne wsparcie tech- macji o liczbie błędów wygenerowanych • Unified ODBC,
niczne firmy Zend lub dokupić dodatko- w poszczególnych warstwach działania • PDO_ODBC,
we narzędzia do tworzenia oprogramo- naszej aplikacji (Rysunek 3). • IBM_DB2.
wania w PHP. Serwer DB2 Express-C może zostać
Jeżeli nie posiadamy działającego automatycznie pobrany przez program Unified ODBC
serwera HTTP, możemy zainstalować instalacyjny Zend Core for IBM. Możemy Zanim powstały rozszerzenia IBM_DB2
Apache 2.0.55 w trakcie instalacji Zend go też ściągnąć ze strony http://ibm.com/ i PDO_ODBC, jedynie Unified ODBC
Core for IBM. db2/express. umożliwiało łączenie się z DB2 lub Clo-
Po instalacji DB2 na naszym serwe- udscape z poziomu aplikacji PHP. Tak
rze umieszczona zostanie również przy- Dostęp do danych DB2 samo, jak w przypadku nowszych roz-
kładowa aplikacja o nazwie DB2 Sam- z poziomu aplikacji PHP szerzeń, obsługa DB2 w Unified ODBC
ple Application for PHP, która będzie Dostęp aplikacji PHP-owych do baz opiera się na natywnych wywołaniach
dostępna pod adresem http://server: DB2 lub Cloudscape jest możliwy dzię- CLI; nie jest jednak możliwe uruchamia-
port/ZendCore/DB2Sample/. Natomiast ki rozszerzeniom korzystającym z API nie procedur składowanych z parametra-
zarządzanie i konfiguracja środowi- klienta DB2. Klient ten łączy się z silni- mi OUT/INOUT. Niezależnie od bazy da-
ska Zend Core for IBM będzie możliwe kiem bazy danych wykorzystując proto- nych, z którą się łączymy (DB2 czy Clo-

PHP Solutions Nr 5/2006 www.phpsolmag.org 13


Dla początkujących Łączymy DB2 i PHP

Rysunek 2. Dostępne panele administracyjne Zend Core for IBM

udscape), używamy tych samych metod tomiast zastosowanie połączenia ska- Z bazą danych DB2 możemy się
dostępu i korzystania z danych. Zamiast talogowanego może być przydatne, gdy również łączyć korzystając ze starego,
Unified ODBC zaleca się obecnie korzy- chcemy wykorzystać specyficzne funkcje nieopartego na PDO interfejsu bazoda-
stanie z rozszerzeń IBM_DB2 lub PDO_ możliwe do realizacji przy pomocy klienta nowego. Używamy w tym celu dwóch
ODBC. DB2 (np. szyfrowanie komunikacji pomię- funkcji: db2 _ connect() i db2 _ pcon-
dzy klientem, a serwerem DB2). nect(). W przypadku db2 _ connect()
PDO_ODBC
PDO_ODBC jest sterownikiem (ang. dri- Listing 1. Łączenie się z bazą DB2 przy użyciu PDO_ODBC
ver) baz zgodnych z ODBC, wykorzy-
stywanym przez będące już standardem <?php
rozszerzenie PDO (PHP Data Objects).
try {
PDO, o którym pisaliśmy już w artykule // przygotowanie danych potrzebnych do nawiązania połączenia z DB2
PDO – przyszły standard dostępu do baz $constrng = 'odbc:DSN={IBM DB2 ODBC DRIVER};HOSTNAME=localhost;
danych w PHP Solutions 5/2005, umożli- PORT=50000;DATABASE=dlrshp;PROTOCOL=TCPIP;UID=db2inst1;PWD=123;';
wia zunifikowany dostęp do różnych źró-
// utworzenie obiektu klasy PDO, z którego będziemy korzystać
deł danych z wykorzystaniem tych sa-
$dbh = new PDO($constrng);
mych metod. W połączeniu z oprogramo- echo 'Connected';
waniem klienta DB2, PDO_ODBC umoż- }
liwia dostęp do baz DB2, Cloudscape i catch (PDOexception $exp) { // jeśli połączenie się nie udało
Apache Derby. Podobnie, jak w pozosta- echo 'Exception: '.$exp->getMessage();
}
łych przypadkach, dostęp ten jest możli-
?>
wy dzięki CLI. Na Listingu 1 przedstawia-
my przykład użycia PDO_ODBC do połą- Listing 2. Łączymy się z DB2 przy użyciu db2_connect
czenia się z bazą DB2.
<?php

IBM_DB2 // definiujemy parametry połączenia


Interfejs IBM_DB2 umożliwia współpra- $database = 'SAMPLE';
cę z bazami DB2, Cloudscape i Apa- $user = 'db2inst1';
che Derby. Obsługuje dwa sposoby łą- $password = 'ibmdb2';
czenia się z bazą danych: skatalogowa- $hostname = 'localhost';
$port = 50000;
ny (cataloged) i nieskatalogowany (un-
cataloged). // tworzymy łańcuch przy użyciu tych parametrów
W przypadku dostępu skatalogowa- $conn_string="DRIVER={IBM DB2 ODBC DRIVER};DATABASE=$database;".
nego wymagana jest obecność klienta "HOSTNAME=$hostname;PORT=$port;PROTOCOL=TCPIP;UID=$user;PWD=$password;";
DB2 na maszynie, na której działa ser-
// łączymy się z bazą danych
wer obsługujący PHP (jeżeli serwer ten
$conn = db2_connect($conn_string, '', '');
jest zainstalowany na innym komputerze
niż serwer DB2). Musimy też zdefinio- // jeżeli połączenie się udało
wać odpowiednie aliasy do serwera DB2, if ($conn) {
z którego będziemy korzystać. Możemy echo 'Połączono z bazą danych.';
db2_close($conn);
je utworzyć wpisując w linii poleceń klien-
}
ta DB2 CLP (od Command Line Proces-
sor) komendy: catalog tcpip node oraz // jeżeli połączenie się nie udało
catalog database. else {
Wygodniejsze jest jednak zastoso- echo 'Połączenie nie powiodło się.';
echo 'wartość SQLSTATE: ' . db2_conn_error();
wanie połączenia nieskatalogowanego,
echo 'komunikat błędu: ' . db2_conn_errormsg();
w którym wszelkie niezbędne informacje }
przekazujemy przy użyciu skryptu otwie- ?>
rającego połączenie z bazą danych. Na-

14 www.phpsolmag.org PHP Solutions Nr 5/2006


Łączymy DB2 i PHP Dla początkujących

Rysunek 3. Monitorowanie aplikacji PHP ze środowiska Zend Core for IBM

połączenie (link) z bazą danych jest Parametry połączenia przekazujemy ko- durami składowanymi, widokami, itp. Co
automatycznie zamykane po wykona- rzystając z $conn _ string. Obsługę błę- więcej, jest dostępny również dla wer-
niu się skryptu, jeżeli natomiast stosu- dów zapewni nam sprawdzenie, czy ist- sji PHP wcześniejszych niż PHP5. Jak
jemy db2 _ pconnect(), połączenie bę- nieje zmienna $conn – jeśli tak, to połą- można się domyślić, interfejs IBM_DB2
dzie cały czas aktywne (nie można go czenie się powiodło. opiera się więc na tradycyjnym progra-
zamknąć nawet korzystając z polecenia IBM_DB2 umożliwia nie tylko wy- mowaniu proceduralnym. Ma również
db2 _ close()). Na Listingu 2 przedsta- syłanie kwerend SQL-owych, ale rów- wiele wbudowanych funkcji służących
wiamy przykład użycia db2 _ connect(). nież pracę z dokumentami XML, proce- m.in. do pobierania informacji o stanie i
konfiguracji serwera DB2, co następuje
Listing 3. Wstawiamy opis nowego produktu z aplikacji PHP poprzez wysyłanie do serwera zapytań
dotyczących tabel słownika systemowe-
// nawiązujemy połączenie z bazą danych DB2 go DB2 (system catalog tables).
$conn =db2_connect($dbname, $dbuser, $dbpass);
Niewątpliwą nowością w IBM_DB2
// odczytujemy plik p1.xml i tworzymy na jego podstawie dokument XML $dom
jest możliwość natywnej obsługi doku-
$fileContents = file_get_contents("products/p1.xml"); mentów i danych składowanych w for-
$dom = simplexml_load_string($fileContents); macie XML.

// odczytujemy id produktu (atrybut pid) z pola pid dokumentu XML


IBM DB2 9: obsługa
$prodID = (string) $dom["pid"];
XML przez serwer
// przygotowujemy i wykonujemy prepared statement umieszczający bazodanowy
// zawartość dokumentu $dom w bazie danych Pod koniec lipca tego roku (2006) po-
jawi się nowa wersja DB2 nosząca nu-
$stmt =db2_prepare($conn, "INSERT INTO product VALUES (?, ?)");
mer 9 i nazwę kodową Viper. Zastoso-
db2_execute($stmt, array($prodID, $fileContents);
wano w niej hierarchiczny silnik do ob-
Listing 4. Utworzenie widoku Categories sługi danych XML (Rysunek 4), co jest
zupełnie nowym podejściem w stosunku
CREATE VIEW Categories(Category) AS SELECT DISTINCT(XMLCAST( do dotychczasowych rozwiązań używa-
XMLQUERY('for $i in $t/product/description/category return $i'
nych w relacyjnych bazach danych. War-
PASSING BY REF T.DESCRIPTION AS "t" RETURNING SEQUENCE)
AS VARCHAR(128))) FROM to nadmienić, iż już obecnie można po-
product AS t brać darmową wersję DB2 9, która rów-
nież nosi nazwę Express-C, z tym, że jest
Listing 5. Użycie zapytania SQL w celu dostępu do danych XML-owych (XQuery) to wersja trial, z której możemy korzystać
$stmt = db2_exec($conn, "SELECT * FROM Categories");
przez 90 dni.
W większości baz danych, doku-
while(list($cat) = db2_fetch_array($stmt)) { menty XML są przechowywane w po-
echo "<a href=\"catalog.php?category=".urlencode($cat). staci dużych obiektów binarnych LOB
"\">$cat</a><br/>"; }
(od ang. large objects) lub dekompono-
wane do tabel relacyjnych (ang. shred-

PHP Solutions Nr 5/2006 www.phpsolmag.org 15


Dla początkujących Łączymy DB2 i PHP

ding). Zastosowanie obiektów LOB po- Załóżmy, że chcemy utworzyć sklep rzystaniu instrukcji INSERT, tak samo, jak
zwala na szybkie wstawienie i pobranie internetowy, który będzie przechowywał w przypadku zwykłych danych relacyj-
całego dokumentu XML, ale ma poważ- informacje o produktach w bazie danych nych. Ponieważ identyfikator produktu
ną wadę, którą jest konieczność dyna- korzystającej z silnika DB2 9. Taka tabela jest przechowywany w oddzielnym polu
micznego parsowania dokumentu, któ- mogłaby wyglądać następująco: tej tabeli (pid), musimy go wyodrębnić z
ra bardzo negatywnie odbija się na wy- dokumentu XML. Możemy to zrobić wy-
dajności zapytań. Dokumenty XML są w create table product ( korzystując interfejs PHP SimpleXml lub
całości umieszczane w polu binarnym. pid int, wykorzystując mechanizmy DB2, takie
Struktura dokumentu jest przetwarzana info xml jak funkcja XMLTABLE , polecenie DECOM-
dopiero w momencie realizacji zapyta- ) POSE XML DOCUMENT czy procedura xdb-
nia, w wyniku którego zwracane są ele- DecompXML .
menty tego XML-a. Atrybut pid byłby identyfikatorem pro- Zwróćmy uwagę na użycie tech-
Wydajniejszą metodą przeszukiwa- duktu, podczas gdy info zawierałby niki prepared statements (db2 _ pre-
nia dokumentów XML jest uprzednia de- szczegółowy opis produktu, będący ze- pare()) przy wstawianiu danych do ba-
kompozycja każdego z nich do posta- stawem danych specjalnego typu XML . zy: występujące we wzorcu znaki zapy-
ci relacyjnej. Jest ona przeprowadza- Opisy produktów dla omawianego syste- tania zostaną zastąpione przez id pro-
na przez odpowiedni kod bazy danych mu byłyby dostarczane w postaci doku- duktu ($prodid) oraz zserializowaną za-
podczas przesyłania dokumentu XML mentów XML. Wstawienie opisu nowego wartość odczytanego pliku XML ($file-
do bazy. Polega na wyodrębnianiu ele- produktu z aplikacji PHP mogłoby prze- contents). W bazie danych dokument
mentów dokumentu XML oraz ich wsta- biegać jak na Listingu 3. ten będzie miał postać hierarchiczne-
wianiu do odpowiednio przygotowanych Zawartość dokumentu XML została go drzewa odzwierciedlającego struk-
tabel relacyjnych (dokument XML zosta- wstawiona do tabeli product przy wyko- turę XML.
je pocięty na fragmenty, które stają się
rekordami w bazie; dobrze oddaje to an- Listing 6. Przetwarzanie listy pobranej z dokumentu XML na format HTML
gielskie określenie shredding). (XQuery)
Zasadniczą wadą shreddingu jest
$xquery =for $i in $t/product
natomiast brak elastyczności, wynikają-
let $thumb := $i/description/images/image[@type="thumbnail"]
cy z konieczności uprzedniej znajomo-
where $i/description/category = " . htmlentities($category) . "
ści struktury przesyłanego dokumen- return
tu, utworzenia tabel bazodanowych za-
wierających odpowiednie atrybuty oraz <div class="float">
<a href="product.php?pid={$i/@pid}">
przypisania tych ostatnich do poszcze-
<img border="0" src="data/images/{$thumb}.jpg" height="100" width="100"/>
gólnych elementów XML-a. Łatwo so-
</a>
bie wyobrazić, jak skomplikowana sta- <p>
je się wtedy prosta modyfikacja struk- <a href="product.php?pid={$i/@pid}">{$i/description/name}</a>
tury formularza na stronie WWW: razem </p>
</div>;>
z nim musimy zmieniać strukturę doku-
mentu XML i bazy danych – to ostatnie
$stmt = db2_prepare($conn, "SELECT XMLSERIALIZE(XMLQUERY(
jest bardzo uciążliwym i czasochłonnym $xquery' PASSING BY REF T.DESCRIPTION AS \"t\"
procesem. RETURNING SEQUENCE) AS CLOB(32K)) FROM xmlproduct AS t");
Ograniczeń obu metod (obiektów bi- db2_execute($stmt);
narnych oraz shreddingu) możemy unik-
while(list($product) = db2_fetch_array($stmt)){echo $product;}
nąć korzystając z dedykowanych baz
XML-owych. Listing 7. Deklarujemy zbiór wynikowy c_cur z klauzulą WITH RETURN
W DB2 9 zaimplementowano sil-
nik pureXML®, który jest oparty o hierar- CREATE PROCEDURE getProduct(IN id VARCHAR(10))
DYNAMIC RESULT SETS 1
chiczną bazę danych i przechowuje dane
LANGUAGE SQL
XML w wewnętrznym formacie odpowia- BEGIN
dającym strukturze dokumentu XML. Ar- DECLARE c_cur CURSOR WITH RETURN FOR
chitekci DB2 położyli bardzo duży nacisk SELECT XMLSERIALIZE(XMLQUERY /* treść zapytania XQuery */
nie tylko na wydajną pracę bazy XML- OPEN c_cur;
END
owej, ale także na jej integrację z silni-
kiem relacyjnym. Język zapytań doku- Listing 8. Uruchamiamy procedurę składowaną przy użyciu instrukcji CALL
mentów XML XQuery/XPath jest obsłu-
giwany na równi z językiem SQL, a kom- $stmt = db2_prepare($conn, "CALL getProduct(?)");
db2_execute($stmt, array($pid));
pilator kwerend bazodanowych DB2 po-
zwala na łączenie ze sobą zapytań do list($product) = db2_fetch_array($stmt);
danych relacyjnych z tymi kierowanymi do echo $product;
danych XML.

16 www.phpsolmag.org PHP Solutions Nr 5/2006


Łączymy DB2 i PHP Dla początkujących

PHP Solutions Nr 5/2006 www.phpsolmag.org 17


Dla początkujących Łączymy DB2 i PHP

re można wybrać odpowiednim wyraże-


niem XPath, np.:

CREATE UNIQUE INDEX prod_pid ON


product(description) GENERATE KEY
USING XMLPATTERN /product/@pid'
AS SQL VARCHAR(10)

Odpowiednio przygotowane wyraże-


nie XPath pozwoli jednym poleceniem
utworzyć indeks, który może obejmować
wszystkie elementy dokumentu XML.
Zastosowanie hybrydowego, relacyjno-
XML-owego silnika danych sprawia, że
obsługa danych XML jest równie wygod-
na, jak operacje na tabelach i relacjach
między nimi w tradycyjnych bazach.

Podsumowanie
System bazodanowy DB2 daje ogrom-
ne możliwości, z których najważniejsze
to te wprowadzone w wersji 9, które do-
tyczą przechowywania danych w posta-
ci dokumentów XML.
Korzystając z nich możemy znacznie
uprościć i przyspieszyć tworzenie struk-
tur danych oraz uczynić ich przetwarza-
nie w naszych aplikacjach elastycznym.
Będziemy o nich jeszcze pisali w następ-
nych artykułach.
Funkcjonalność i solidność poprzed-
niej, całkowicie darmowej wersji DB2 jest
również warta uwagi. Biorąc pod uwa-
Rysunek 4. Dokumenty XML przechowywane są w DB2 9 w postaci hierarchicznej gę te cechy oraz łatwość łączenia DB2
z PHP, warto się poważnie zastanowić
Korzystamy z XQuery Dla wygody możemy umieścić zapy- nad jej zastosowaniem w swoich projek-
Do pobrania i prezentacji kategorii każde- tanie XQuery w procedurze składowanej tach. n
go produktu, przechowywanej jako ele- i przekazać jego wynik w postaci zbioru
ment dokumentu XML, możemy użyć pro- wynikowego.
stego zapytania XQuery: Procedura powinna być zadeklaro-
wana z klauzulą DYNAMIC RESULT SETS,
for $i in db2-fn:xmlcolumn( która oznacza liczbę zwracanych zbio-
'PRODUCT.INFO')/product/description rów wynikowych. By zwrócić zbiór wy- O autorach
return $i/category/text() nikowy, kursor c _ cur musi zostać za-
deklarowany z klauzulą WITH RETURN (Li- Artur Wroński: Jest pracownikiem
Pamiętajmy, że przedstawiony kod jest sting 7). działu oprogramowania IBM i jest od-
wykonywany na serwerze baz danych Jedynym, co nam pozostało do zro- powiedzialny za rozwiązania z zakre-
i nie jest fragmentem skryptu PHP (mimo bienia w aplikacji PHP jest uruchomienie su baz danych. Od 12 lat specjalizuje
się w silnikach baz danych, migra-
podobnego zapisu nazw zmiennych). procedury składowanej przy użyciu in- cjach oraz narzędziach dla hurtowni
Możemy utworzyć widok Categories, strukcji CALL oraz odczytanie wyników danych.
który będzie odpowiadał zapytaniu XQu- naszej kwerendy (Listing 8). Po raz ko- Kontakt: artur.wronski@pl.ibm.com
ery – pozwoli nam to używać zapytania lejny, zwróćmy uwagę na użycie techniki
Piotr Pietrzak: Jest architektem sys-
SQL w celu wykonania operacji dostępu prepared statements. temów w dziale Systems & Technology
do danych XML (Listingi 4 i 5). Group w polskim oddziale firmy
W DB2 9 XQuery pozwala nie tylko Indeksy w XML-u IBM. Od ponad 11 lat związany jest
na prezentację listy pobranej z dokumen- Aby działanie i korzystanie z zapytań by- z branżą IT. Jest autorem wielu tech-
nicznych publikacji dotyczących archi-
tu XML, ale także na przetwarzanie jej w ło wydajne, potrzebne są indeksy. DB2 9 tektury sprzętu oraz programowania.
klauzuli return do formatu HTML na eta- pozwala na tworzenie indeksów dotyczą- Kontakt: piotr.pietrzak@pl.ibm.com
pie zwracania wyników (zob. Listing 6). cych elementów dokumentu XML, któ-

18 www.phpsolmag.org PHP Solutions Nr 5/2006


Wydania specjalne SOFTWARE 2.0 EXTRA, SDJ EXTRA i LINUX+ EXTRA!
Dla prenumeratorów Linux+, Linux+ Extra! oraz SDJ
tel. (22) 887-14-44, fax (22) 887-10-11
trzy dowolne archiwalne numery
SDJ Extra bądź Linux+ Extra tylko za
Oferta ważna do 31 maja 2006 r. lub do wyczerpania zapasów
25 PLN pren@software.com.pl www.buyitpress.com

ASP.NET STARTER KIT SDJ EXTRA

Pismo przygotowane na premierę dwóch


Uczymy programować w ASP.NET 2.0. grup najważniejszych produktów Microsoft.
W piśmie zamieściliśmy aż Omawia nowości a zatem TSQl, SQL service
14 artykułów instruktażowych. Broker, XML oraz aspekty bezpieczeństwa
Nauczysz się jak stworzyć w SQL Server 2005. Dodatkowo zawiera krótki
29,80 zł 29,80 zł
pierwszą aplikację a w przyszłości kurs Visual Studio 2005. Pismo zawiera 2 pły-
może stworzysz profesjonalny portal. ty DVD.

PHP STARTER KIT PROGRAMOWANIE W JAVIE

PHP Starter Kit krok po kroku wprowadzi Cię


w świat PHP. Dowiesz się, jak skonfigurować
pełne środowisko do tworzenia aplikacji
w PHP, napisać pierwszy program, stworzyć Java staje się coraz bardziej popularna.
portal i sklep internetowy. Na DVD: pełny W piśmie prezentujemy technologie,
zestaw narzędzi do tworzenia aplikacji WWW narzędzia oraz biblioteki związane z tym
29,80 zł oraz bootowalna dystrybucja Linuksa z kom- 29,80 zł językiem programowania. Na DVD pełne
pletnym środowiskiem do testowania wersje komercyjnych programów: Yoxos
i tworzenia programów w PHP. i Awoma oraz ciekawe książki elektroniczne.

ORACLE DEBIAN 3.1

Oto baza danych Oracle 10g.


Przedstawiamy jej możliwości i wbudowane Linux+ Extra! z kultową dystrybucją Debian
narzędzia. Dodatkowo: jak prawidłowo GNU/Linux 3.1 Sarge. Na 3 płytach DVD
zainstalować Oracle’a na swoim komputerze dystrybucja Debian 3.1 + dodatki (pełna
29,80 zł w systemie Windows i Linux. 37,00 zł wersja Pixel 32 i 6 komercyjnych aplikacji,
Na DVD pełna wersja bazy Oracle 10g w pełnych wersjach ograniczonych czasowo
z dodatkowymi narzędziami! + 3 książki w PDF).

AUROX 11.1 FREEBSD 6.0

Aurox to stabilny system operacyjny


dedykowany dla posiadaczy urządzeń FreeBSD to system, który dzięki swojemu bez-
mobilnych. Celem zespołu rozwijającego pieczeństwu cieszy się ogromnym powodze-
Auroksa jest zapewnienie dobrego niem wśród administratorów sieci, a dzięki pro-
35,00 zł działania urządzeń takich jak bezprzewodowe 35,00 zł stej instalacji oprogramowania, zjednał so-
karty sieciowe (WiFi), modemy bie także sympatię użytkowników domowych.
ADSL, karty telefonii komórkowej (Orange). Sześć płyt CD + liczne dodatki.

GENTOO LINUX 2005.1 MANDRIVA LINUX

Gentoo Linux to najszybsza ze wszystkich Jeśli chcesz rozpocząć swoją przygodę


dostępnych dystrybucji Linuksa. W Linux+ z Linuksem, Linux+ Extra! z Mandrivą będzie
Extra! przygotowaliśmy dla Was specjalną idealnym wyborem. Mandriva uważana jest
35,00 zł edycję tego systemu, która podczas instalacji 35,00 zł za dystrybucję najprostszą w instalacji
nie wymaga połączenia z Internetem. i użytkowaniu. Na 7 płytach CD znajdziecie
Dwie płyty DVD + dużo dodatków. kompletny system + dodatki.
Bezpieczeństwo

Zaawansowane ataki SQL


Injection
Stopień trudności: lll
Mike Shema

Ataki SQL Injection są wymierzone w trzon


aplikacji webowej – bazę danych – i umożliwiają
intruzowi zdobycie, modyfikację lub usunięcie
dowolnych danych. Zrozumienie zasad działania
SQL Injection jest konieczne do wypracowania
odpowiednich metod obrony.

K
ażdy administrator serwera WWW do baz MySQL, techniki da się zasto-
powinien znać techniki, które mo- sować na każdej platformie bazodano-
gą być wykorzystane do identyfi- wej – w większości przypadków bez
kacji podatności na SQL Injection (patrz modyfikacji. Techniki te w swojej istocie
Artykuł Tobiasa Glemsera Ataki SQL In- są wymierzone w język SQL. Określo-
jection na PHP i MySQL, hakin9 2/2005) ne rozszerzenia baz danych zwyczaj-
W SIECI i być świadomy ryzyka, jakie niosą. Pod- nie ułatwiają zastosowanie omawia-
stawowa metodologia SQL Injection pole- nych technik.
ga na określeniu wektora ataku, a następ-
• http://www.spidynamics.com/ nie jego wykorzystaniu za pomocą zmo-
papers/SQLInjectionWhiteP
aper.pdf – bardzo dobry ar- dyfikowanych zapytań SQL – wszystko Co należy wiedzieć...
Musisz bardzo dobrze znać składnię ję-
tykuł o atakach SQL Injec- przez przeglądarkę internetową. zyka SQL, do tego powinieneś znać ję-
tion,
• http://msdn.microsoft.com/ Określenie potencjału luki jest waż- zyk PHP na średnio zaawansowanym
msdnmag/issues/04/09/ ne, ale jeszcze ważniejsza jest moż- poziomie.
SQLInjection/ – Stop SQL
liwość oceny jego wpływu. W niektó-
Injection Attacks Befo-
re They Stop You (strona rych przypadkach wektor SQL Injec- Co obiecujemy...
MSDN), tion może nie wykraczać poza możli- Po przeczytaniu artykułu nauczysz się
• http://www.sqlsecurity.com/ w jaki sposób atakować składnię zapy-
– wszystko o słabych punk- wość wygenerowania błędów składni,
tań SQL, dowiesz się jak przeprowa-
tach SQL, takich jak próby przekształcenia łań- dzane są ataki na składnię języka SQL,
• http://www.imperva.com/
application_defense_center/ cuchów (strings) w wartości numerycz- poznasz również metody agresji na lo-
white_papers/sql_injection_ ne. W innych sytuacjach wektor umoż- gikę SQL, nauczysz się kilku dodatko-
signatures_evasion.html wych sztuczek SQL Injection oraz po-
liwia atakującemu pełne przejęcie kon-
– automatyczne, samopro- znasz ogólne zasady obrony przed ata-
gramujące ataki na SQL. troli nad informacjami z bazy danych. kami SQL Injection.
Chociaż nasze przykłady odnoszą się

20 www.phpsolmag.org PHP Solutions Nr 5/2006


Zaawansowane SQL Injection Bezpieczeństwo

Dla odświeżenia wodu źle sformatowanej składni (nieza- ASCII. Atakujący może wstrzyknąć cudzy-
pamięci mknięty pojedynczy cudzysłów): słowy przez zastosowanie parzystej lub
Testy SQL Injection można podzielić na nieparzystej liczby powtórzeń łańcuchów
trzy kategorie w oparciu o to, w który • SELECT foo FROM bar WHERE a = CHAR(0x27) – szesnastkowa wartość 0x27
aspekt zapytań SQL są wymierzone: ''';, reprezentuje kod ASCII apostrofu. To waż-
• SELECT foo FROM bar WHERE a = '/ ne, ponieważ atak składa się ze znaków
• atak na składnię zapytania – wstawia- *;, alfanumerycznych i nawiasów. W związ-
nie typowych znaków SQL mające na • SELECT foo FROM bar WHERE a = ';-- ku z tym monitorowanie danych wejścio-
celu wygenerowanie błędów ułatwiają- ;, wych pod kątem cudzysłowów nie zareje-
cych identyfikację potencjalnego wek- • SELECT foo FROM bar WHERE a = '#;. struje ani nie zablokuje ataku.
tora ataku,
• atak na składnię języka – wycelowany Chociaż apostrof (ASCII 0x27) jest najpo- Znaczenie zmiennych
w sam język SQL, zamierzeniem jest pularniejszym przykładem, wiele znaków Błędy baz danych mogą być także wywo-
wygenerowanie błędów bazy danych może być użytych do zakłócenia składni, łane przez ataki na typy zmiennych. Jest
lub wykonanie prostych kwerend po- między innymi: to najskuteczniejsze w przypadku warto-
przez manipulację konstruktami języ- ści numerycznych, ale podatne są także
ka i tożsamościami semantycznymi, • niepasujący cudzysłów, zmienne czasu czy daty. Oto przykładowa
• atak na logikę zapytania – przepisanie • średnik, lista różnych wartości, na których można
zapytania mające na celu otrzymanie • znak komentarza – /*, #, lub --. wypróbowywać parametry spodziewające
dowolnych danych z tabel, do których się liczb dziesiętnych:
twórcy nie przewidzieli dostępu. Filtry poprawności, które zabraniają wy-
łącznie znaków pojedynczego cudzysłowu • 8-, 16-, 32- i 64-bitowe wartości – 256,
Techniki te mogą być łączone w celu oce- (lub tylko małego zestawu znaków) mogą 65536, itd.,
nienia aplikacji webowej i określenia jej zabezpieczać przed pełnym wykorzysta- • przepełnienia całkowite – 2^8 + 1,
podatności na ataki SQL Injection. niem podatności, ale zwykle są niewystar- 2^16 +1, 2^32 + 1, or 2^64 + 1,
W następnych sekcjach przykłado- czające. Mogą po prostu zaciemniać bar- • wartości nienazwane kontra nazwane
we ładunki (ang. payload) SQL Injec- dziej podstawowe problemy z architekturą – wstawianie wartości ujemnych,
tion będą prezentowane bez całego ad- połączeń aplikacji z bazą danych. • przepełnienia zmiennoprzecinkowe – na
resu (URL). Ułatwi to zrozumienie tech- przykład 3.40282346638528860e+38,
nik bez niewygodnych parametrów i tek- Cudzysłowy kontra ukośniki 1.79769313486231570e+308,
stu. Innym powodem jest to, że wstrzyk- Podczas tworzenia silnych filtrów popraw- • alternatywne przedstawienia – dwój-
nięcie tych ładunków jest całkiem proste. ności programiści PHP stają przed kilkoma kowe, ósemkowe, szesnastkowe lub
Przy założeniu, że mamy URL w postaci wyzwaniami i potencjalnie mylącymi za- notacja naukowa.
http://witryna/page.cgi?a=foo&b=bar, atak leceniami. Funkcja magic_quotes() auto-
SQL Injection zastępuje wartość podat- matycznie kasuje (ang. escape) wszystkie Te ataki liczbowe często się udają (gene-
nego parametru swoim ładunkiem: http: apostrofy znakiem odwróconego ukośnika rują błędy), ponieważ zmienne używane
//witryna/page.cgi?a=<ładunek SQL Injec- (\). Jeśli jednak ta możliwość jest połączo- do śledzenia tych wartości nie mają ści-
tion>&b=bar. Dla dalszego przypomnie- na z wywołaniem funkcji strip_slashes(), śle określonego typu. W języku PHP ty-
nia: trzeba pamiętać o kodowaniu spacji i znaki kasowania zostaną usunięte: pem parametru dla wszystkich zmiennych
innych znaków w ładunku, aby nie zakłó- $_REQUEST jest łańcuch (string). Ozna-
cały one składni adresu. • SELECT foo FROM bar WHERE a = '\ cza to, że – mimo możliwości wykonywa-
''; – apostrof wykasowany, nia operacji na zmiennych ($a = 1; $a++)
Ataki na składnię • SELECT foo FROM bar WHERE a = '''; – typ zmiennej powinien być traktowa-
zapytań – odwrócony ukośnik wycięty, zapyta- ny jako łańcuch liczbowy. Zmienna mo-
Pojedynczy cudzysłów, zwany apostrofem nie źle sformatowane. że być nawet po cichu zmieniona z licz-
(') – choć jest zdecydowanie najpopular- by na łańcuch liczbowy, przy którym war-
niejszym znakiem stosowanym do iden- Innym niebezpieczeństwem nadmiernej tość zwykle skutkowałaby przepełnieniem,
tyfikacji wektorów SQL Injection – nie jest koncentracji na pojedynczym cudzysłowie wartością inf (ang. infinity – nieskoń-
w żadnym wypadku jedynym znakiem po- jest fakt, że programiści mogą nie znać czoność) lub NaN (ang. not a number
trzebnym do wygenerowania błędu bazy pełnego zakresu znaków i technik, które – nie jest liczbą). Na przykład funkcja
danych. Ta technika obejmuje najbardziej atakujący mógłby wykorzystać przy atako- PHP is_numeric("1e308") zwróci war-
podstawowe testy potencjalnych luk po- waniu zapytania SQL. Intruz może łączyć tość prawdziwą (bo jest to liczba), ale
przez użycie metaznaków lub znaków for- funkcje SQL, by generować błędy skład- is_numeric("1e309") zwróci fałsz – nie
matujących języka MySQL do zakłócenia ni zapytania. jest to ani liczba, ani łańcuch liczbowy, po-
składni oryginalnego zapytania. Na przy- Do wywołania błędów można również nieważ znajduje się poza zakresem typu
kład następujące wyrażenia nie mogą być użyć wbudowanych funkcji SQL. Funkcja PHP double float. Zmienna musi być prze-
przetworzone do poprawnej postaci z po- CHAR() drukuje odpowiednik argumentu w stawiona w tryb numeryczny bezpośred-

PHP Solutions Nr 5/2006 www.phpsolmag.org 21


Bezpieczeństwo Zaawansowane SQL Injection

zmieniają. Oznacza to także, że filtrowa-


nie oparte na składni – takie jak w firewal-
lach warstwy aplikacji – musi być bardzo
dokładne, aby zabezpieczyć przed tego
typu atakami. W rzeczywistości kombina-
cja alternatywnego schematu kodowania
(kodowanie URL, Unicode) i kreatywnego
SQL ominie większość filtrów bazujących
na wzorcach. Pamiętajmy – CHAR(0x27)
jest tym samym, co cH%41r(0x68-0x41).

Ataki na składnię języka


W języku SQL zdanie Szekspira o różach
wyglądałoby zupełnie niepoetycko:

SELECT name FROM roses


WHERE scent='sweet';

Czy róża zostanie nazwana butem,


trzmielem, czy zegarkiem, jej atrybut pięk-
Rysunek 1. Oryginalny przykładowy URL
nego zapachu pozostanie bez zmian.
SQL posiada bogaty zestaw funkcji, któ-
re mogą być użyte do tworzenia seman-
tycznych ekwiwalentów zapytań, teksto-
wo prezentujących się zupełnie inaczej.
Ta zdolność pozwala atakującemu ziden-
tyfikować i wykorzystać podatność na
wstrzyknięcia, nawet gdy serwer nie po-
kazuje informacji o błędach czy innych
podobnych danych.
Podczas gdy psucie zapytań jest uży-
teczne przy znajdowaniu potencjalnych
podatności, warto także atakować zapy-
tania za pomocą semantyki wbudowa-
nych funkcji SQL. Z tego powodu, za-
miast atakować interpreter języka aplikacji
(PHP, JSP czy innych), agresja koncentru-
je się na samym języku SQL. Ma to dodat-
kowe zalety – nie tylko identyfikuje wek-
tory ataku, lecz także dostarcza więcej
informacji o filtrach poprawności wykorzy-
Rysunek 2. Zmodyfikowany łańcuch z adresem stywanych przez aplikację. Innym skut-
kiem ubocznym tej techniki jest możliwość
nio za pomocą funkcji settype(), ale uwa- za chwilę, filtry poprawności danych mogą przeprowadzania ślepych ataków SQL In-
ga – duże wartości mogą zwracać wartość być nieodpowiednie. Na przykład przeko- jection lub ataków, które nie wymagają ge-
inf, co także może prowadzić do błędów w naliśmy się już, że wartość 1e309 nie jest nerowania błędów do identyfikacji lub wy-
zapytaniu, jeśli spodziewa się ono wyra- liczbą (dla większości języków i baz da- korzystania.
żeń liczbowych. nych SQL) i wywoła błąd w słabo zabez-
pieczonych aplikacjach, choć nie zawiera Numeryczne typy danych
Zwalczając synonimy żadnych typowo szkodliwych znaków. To Numeryczne typy danych to pierwsi kan-
Solidne filtry poprawności danych wejścio- wartość wyłącznie alfanumeryczna. dydaci do przetestowania tej techniki. Ry-
wych mogą być skuteczną obroną przed Pamiętajmy, że SQL to bogaty ję- sunek 1 przedstawia oryginalny przykłado-
tymi technikami, ale niestety są niewy- zyk, który pozwala atakującemu tworzyć wy URL, zaś Rysunki 2 i 3 zawierają zmo-
starczające. Błędy baz danych i inne wy- wiele synonimicznych przekształceń. Na dyfikowane adresy. Korzystamy ze starej,
jątki powinno się wyłapywać i zapobiegać przykład CHAR(0x27) jest odpowiednikiem niezabezpieczonej wersji sklepu interne-
ich wysyłaniu do przeglądarki. Szczegó- ASCII(0x27), które można też zapisać ja- towego FreznoShop – wersje nowsze niż
łowe informacje o błędach zwykle dostar- ko x'27. Koncentrujemy się na łańcuchu 1.4 są odporne na te błędy.
czają cennych danych złośliwym użytkow- CHAR(0x27), by uniknąć gołych cudzy- Rozważmy następującą listę par
nikom atakującym bazę. Jak zobaczymy słowów, ale szczegóły każdego testu się nazwa/wartość:

22 www.phpsolmag.org PHP Solutions Nr 5/2006


Zaawansowane SQL Injection Bezpieczeństwo

• rowid = 111,
• rowid = 0x6f,
• rowid = 0157 (reprezentacja ósem-
kowa),
• rowid = 110+1 (w praktyce używajmy
110%2b1, ponieważ znak + w adresie
oznacza spację),
• rowid = 112–1,
• rowid = MOD(111,112),
• rowid = REPEAT(1,3),
• rowid = COALESCE(NULL,NULL,111).

Z punktu widzenia bazy danych, każde


z tych żądań daje w rezultacie taką sa-
mą wartość: 111. Zauważmy też, że żad-
ne z nich nie korzysta z apostrofu. Pierw-
sze trzy wyglądają na łańcuchy numerycz-
ne lub alfanumeryczne, następne dwa ma-
ją pozornie nieszkodliwe znaki dla symbo-
li dodawania i odejmowania, zaś ostatnie Rysunek 3. Ten sam łańcuch zmodyfikowany za pomocą funkcji MOD()
trzy wartości zawierają nawiasy i przecin-
ki. Jeśli filtry poprawności koncentrowały- danych, śmiało możemy wziąć na muszkę Znów świadomie postanowiliśmy unikać
by się na wycinaniu apostrofów, takie za- składnię funkcji SQL. Na przykład: cudzysłowów, ponieważ zwykle wszczy-
bezpieczenie aplikacji nie miałoby żadne- nają alarm lub lub mogą być blokowane.
go efektu. • BIN(-1), Nie przeszkadza nam to jednak tworzyć
• LIMIT a (przydatne, bo nie wymaga złożonych łańcuchów. Funkcje REVERSE(),
Surowe parametry nawiasów), LEAST(), i GREATEST() wymagają tylko na-
Ta technika, używająca semantycznych • MOD(0,a). wiasów i przecinków. Poniższe przykłady
sobowtórów (ang. doppelganger) pozwa- są identyczne znaczeniowo:
la użytkownikowi na identyfikację wekto- Oczywiście wartości liczbowe również po-
rów SQL Injection. Jeśli rezultat każdego z winny być przetestowane pod kątem wa- • page.cgi?category=music,
żądań jest identyczny, można założyć, że runków granicznych, jak wspomniano w • page.cgi?category=REVERSE(cisum),
silnik aplikacji przetworzył surową wartość poprzedniej sekcji. • page.cgi?category=GREATEST(0x61,0x
parametru i umieścił ją w odpowiednim za- 6d75736963),
pytaniu SQL. Rozważmy na przykład takie Znaki przedwczesnego • page.cgi?category=LEAST(0x6d757369
zapytanie o numer wiersza: zakończenia 63,0x6e75736963).
Ta technika służy do tworzenia indywidual-
SELECT foo FROM table nych zapytań SQL. Takie zapytania często Obrona
WHERE rowid = 110+1; nie potrzebują znaków cudzysłowu, ale Najlepszą obroną przed tymi atakami jest
zwykle wymagają znaków przedwczesne- korzystanie z filtrów poprawności danych
Baza danych oblicza 110+1 = 111 przed go zakończenia. W związku z tym żądanie wejściowych i używanie ścisłych typów da-
przetworzeniem reszty zapytania, zgod- mogłoby wykorzystać znaki /* lub -- do nych przy przypisywaniu parametrom za-
nie ze swoim porządkiem operacji. Da- przycięcia wyrażenia do żądanej postaci. pytań danych dostarczanych przez użyt-
je to taki sam rezultat, jak oryginalne za- Łańcuch SELECT foo FROM table WHERE kownika. Mimo że 0x27 jest poprawną war-
pytanie: rowid = MOD(111,112)+UNION+SELECT+USE tością szesnastkową, aplikacja powinna
R()/*; jest dobrym przykładem. zabraniać jej stosowania (lub cicho prze-
SELECT foo FROM table Łańcuchy stanowią większe wyzwanie, mienić w dziesiętne 27) – surowa wartość
WHERE rowid = 111; ponieważ tutaj istnieje mniej funkcji języka zawiera znak nienumeryczny. Na tej samej
SQL dostarczających pomocnych sobo- zasadzie wartość dziesiętna 0157 powin-
Zanim wyjaśnimy jak rozbudować ten atak wtórów semantycznych. W takich przypad- na być albo odrzucona z powodu poprze-
w celu otrzymania dowolnych danych, kach może się przydać funkcja CONCAT(). dzającego liczbę zera, albo obcięta tak,
zbadajmy najpierw kilka innych przypad- Kiedy argument zawiera jedynie litery od a by stała się wartością dziesiętną 157, co
ków, które można wykorzystać do wyge- do f, można użyć funkcji HEX(): da zupełnie inny numer wiersza. Wresz-
nerowania błędów. Chociaż ta technika nie cie, programiści powinni znać alternatywne
wymaga od nas wywołania błędów bazy • op=add, systemy liczbowe i wiedzieć, w jaki sposób
danych, takie dane są użyteczne do okre- • op=HEX(2781), są interpretowane zarówno w języku apli-
ślenia wersji i nazw tabel lub kolumn. Jeśli • op=REVERSE(dda), kacji, jak i w bazie danych.
filtry danych wejściowych aplikacji wytną • LEAST(0x6d75736963,0x6e75736963), Traktowanie wszystkich danych użyt-
cudzysłowy lecz nie wyłapią błędów bazy • GREATEST(0x61,0x6d75736963). kownika jako łańcuchy jest bardzo łatwe,

PHP Solutions Nr 5/2006 www.phpsolmag.org 23


Bezpieczeństwo Zaawansowane SQL Injection

• wyrażenie UNION powinno kończyć za-


pytanie, aby składnia była poprawna
– dodatkowa logika musi być usunię-
ta,
• wyrażenia UNION wymagają odpowied-
niej liczby kolumn w każdym wyraże-
niu SELECT.

Pierwszy problem da się rozwiązać


względnie łatwo. Wystarczy użyć jedne-
go z typowych znaków zakończenia opisy-
wanych w poprzedniej sekcji. Może to być
ogranicznik komentarza (#, /*, --), połą-
czony w razie potrzeby ze średnikiem lub
apostrofem.

Liczenie kolumn
Drugi problem także nie jest wielkim
wyzwaniem, ale wymaga kilku powta-
rzalnych kroków. Wstrzyknięte wyraże-
Rysunek 4. Udany atak UNION SELECT
nie UNION będzie miało albo za mało,
ale jeśli dane mają być umieszczone w za- Następnie można odwrócić zapytanie albo zbyt dużo kolumn, nam zaś potrze-
pytaniu, powinny być bezpośrednio przypi- tak, aby było poprawne, w ten sposób ba poprawnej liczby. Jeśli mamy możli-
sane (rzutowane) do odpowiedniego typu sprawdzając wektor wstrzyknięcia – / wość obserwacji komunikatów o błędach
danych. W przypadku języków interpreto- *!32302+AND+1+*//* (może być konieczne generowanych przez bazę, zobaczymy
wanych jak PHP, Perl, C# czy Visual Basic zakończenie zapytania). coś w rodzaju The used SELECT sta-
przypisanie powinno być bezpieczne lub tements have a different number of co-
generować błąd konwersji. Jeśli aplikacja UNION SELECT lumns (Użyte wyrażenia SELECT mają
webowa używa języka kompilowanego (C, Po identyfikacji parametru jako wektora różną liczbę kolumn).
C++), to rzutowanie typów musi być trak- dla ataków SQL Injection, następnym kro- Niedoszacowanie kolumn można po-
towane ostrożnie i sprawdzane pod kątem kiem jest określenie tego, na co podatna prawić poprzez dodanie do wyrażenia
wyjątków – uczulamy na ataki z wykorzy- jest baza danych. Osiągamy to przez ma- SELECT dodatkowych kolumn lub miejsc na
staniem łańcuchów formatujących (patrz nipulowanie logiką oryginalnego zapyta- nie (Rysunek 4), na przykład za pomocą
Artykuł Nadużycia z wykorzystaniem cią- nia. Większość podstawowych zapytań ma następujących wyrażeń:
gów formatujących, hakin9 5/2004). postać SELECT foo FROM bar WHERE a=b;,
gdzie b z równania a=b jest parametrem, • SELECT user FROM mysql.user;
Ataki na logikę którym można manipulować. Co za tym • SELECT 1,user FROM mysql.user;
zapytania idzie, nowe zapytanie musi brać pod uwa- • SELECT 1,1,user FROM mysql.user;
Manipulacje składnią zapytań są użytecz- gę wcześniejsze SELECT. Najszybszą tech- • SELECT user,user,user,user FROM
ne przy identyfikacji podatności na SQL niką jest użycie słowa kluczowego UNION. mysql.user;
Injection, ale tylko wskazują na istnienie Wyrażenie UNION łączy wiele wy-
problemu. Prawdziwym ryzykiem związa- rażeń SELECT i jest obsługiwane przez Każde z tych zapytań pobiera nazwę
nym z atakami SQL Injection jest dostęp większość systemów bazodanowych. Je- (lub nazwy) użytkownika z domyślnej ta-
do dowolnych danych. go podstawowa forma ma postać SELECT blicy mysql.user. W każdym z powyż-
MySQL obsługuje charakterystyczne foo FROM bar WHERE a=b UNION SELECT szych przykładów liczba kolumn wzrasta
makro komentarzowe, które wyświetla wer- foo2 FROM bar2 WHERE c=d;. Użytecznym z 1 do 4. W praktyce lepiej powtórzyć na-
sję bazy danych: /*!<wersja> */, gdzie aspektem UNION jest wyświetlanie nazwy zwę kolumny aby upewnić się, że wartość
<wersja> jest pięciocyfrową wartością re- użytkownika, który nawiązał połączenie z jest wyświetlana przez aplikację. Pierw-
prezentującą wersję MySQL. Na przykład bazą danych. W MySQL robi się to za po- sze miejsce na kolumnę działa, ale trud-
wersja 3.23.02 wygląda jak 32302, wer- mocą funkcji SELECT USER(). Żądanie mo- no powiedzieć, którą kolumnę wyświetli
sja 4.1.10 to 40110, zaś 5.0.3 wygląda jak głoby wyglądać następująco: aplikacja.
50003. Najszybszym sposobem sprawdze- Przeszacowania kolumn mogą być
nia możliwości wbudowanych ataków na SELECT text FROM articles poprawione za pomocą wyrażenia CONCAT.
MySQL jest połączenie komentarza z wyra- WHERE id=0 Przeszacowania pojawiają się, gdy pierw-
żeniem skutkującym błędem zapytania: UNION SELECT USER(); sze wyrażenie SELECT spodziewa się
mniejszej liczby kolumn niż zawarto w za-
• /*!32302+AND+0+*/, Przy stosowaniu wyrażeń UNION w ata- pytaniu. Wyrażenie CONCAT rozwiązuje ten
• /*!32302+AND+0+*//* (może być ko- kach SQL Injection pojawia się kilka pro- problem poprzez połączenie każdej ko-
nieczne zakończenie zapytania). blemów: lumny w pojedynczy łańcuch – większa

24 www.phpsolmag.org PHP Solutions Nr 5/2006


Zaawansowane SQL Injection Bezpieczeństwo

dur – oddzielają one logikę zapytań od da-


Dodatkowe sztuczki SQL nych. W konsekwencji ataki SQL Injection
Naszym głównym celem jest zidentyfikowanie podatności na SQL Injection przez twórcze będą mogły uszkodzić oryginalne zapyta-
użycie znaków formatowania (składnię) lub funkcji SQL (semantykę), a potem wykorzysta- nie SQL.
nie tych podatności za pomocą ataku na logikę SQL. Chociaż ten proces koncentruje się
Potencjalną wadą takich zabezpie-
na manipulacjach łańcuchami i liczbami, do wygenerowania błędów można użyć (a raczej
nadużyć) innych funkcji: czeń jest to, że wymagają one dodatko-
wych zmian w aplikacji. To może prowa-
• INET _ ATON(), dzić do spadku wydajności, jednak z dru-
• INET _ NTOA(),
giej strony negatywny wpływ może być
• SOUNDEX().
minimalny, a wzrost bezpieczeństwa tak
Enumeracja jest kolejnym ważnym elementem SQL Injection, choć w tym artykule się nią czy inaczej będzie ogromny.
nie zajmujemy. Mimo to, poniżej znajduje się kilka prostych zapytań, które można wykorzy-
stać do dokładniejszego określenia informacji o bazie danych:
Separowanie
• SHOW VARIABLES, Filtry poprawności danych wejściowych
• SHOW STATUS, są nieodłączną częścią systemów obrony
• SHOW DATABASES,
przed atakami SQL Injection, ale takie dane
• SHOW TABLES,
• DESCRIBE <table>, nie zawsze stanowią największy problem.
• EXPLAIN <table>, Bardziej podstawowym problemem
• EXPLAIN SELECT <foo> FROM <table>, związanym z SQL Injection jest brak se-
• SHOW FULL COLUMNS FROM <table>,
paracji między logiką zapytań a danymi.
• SELECT USER(),
• SELECT SESSION _ USER(), Logika jest definiowana przez programi-
• SELECT CURRENT _ USER(), stę i powinna pozostać statyczna, zaś da-
• SELECT SYSTEM _ USER(), ne podaje użytkownik. Kiedy dane i logika
• SELECT SUBSTRING _ INDEX(USER(),'@',1),
się mieszają, tak jak w przypadku łączenia
• SHOW CHARACTER SET,
• SELECT CURDATE(), łańcuchów przy tworzeniu zapytań, da-
• SELECT CURTIME(). ne dostarczane przez użytkownika mogą
wpływać na logikę zapytania. Wiąże się z
tym większe ryzyko niż to związane z po-
liczba kolumn zostaje więc połączona w Istnieje o wiele łatwiejsza metoda, pole- prawnością danych wejściowych, ponie-
jedną kolumnę. Na przykład: gająca na wykorzystaniu offsetów w wy- waż zmodyfikowane zapytanie umożliwia
rażeniu LIMIT. Można oczywiście ograni- dostęp do danych zawartych w bazie. Zło-
SELECT foo FROM table czyć wyniki do jednego wiersza za pomo- śliwie umieszczony w zapisanej procedu-
WHERE a=b cą LIMIT 1, jednak wyrażenie umożliwia rze znak formatowania może skutkować
UNION SELECT CONCAT(*) też sterowanie wyświetlanymi wierszami błędem bazy, a nie wyświetleniem właści-
FROM mysql.user; przez dodanie opcjonalnego offsetu za- wych danych. Nie chodzi o to, że spraw-
czynającego się od 0. Na przykład: dzanie poprawności danych nie jest istot-
Jeśli istnieje potrzeba, można to połączyć z ne – chodzi o to, że każdy system obro-
techniką używaną przy niedoszacowaniach: • SELECT foo FROM table WHERE a=b ny powinien koncentrować się zarówno na
UNION (SELECT CONCAT(*) FROM mysql. budowie, jak i na wykonywaniu zapytań.
SELECT foo,bar FROM table user LIMIT 0,1);, Z punktu widzenia testów audytorzy,
WHERE a=b • SELECT foo FROM table WHERE a=b którzy niepoprawnie testują podatności
UNION SELECT 1,CONCAT(*) UNION (SELECT CONCAT(*) FROM mysql. na SQL Injection, prezentują niewłaściwe
FROM mysql.user; user LIMIT 1,1);, spojrzenie na ryzyko dla aplikacji. Jeśli te-
• SELECT foo FROM table WHERE a=b sty polegają wyłącznie na wstrzyknięciach
Najpoważniejszym ograniczeniem jest to, UNION (SELECT CONCAT(*) FROM mysql. opartych na apostrofach, będą bezuży-
że wartość NULL w jednej z kolumn za- user LIMIT 2,1);. teczne – do ataków SQL Injection wyko-
mieni łańcuch CONCAT na NULL. rzystuje się wiele różnych znaków. n
Możemy tak postępować do momen-
Wiersze na muszce tu, kiedy zapytanie zwróci wiersz NULL.
Jeśli liczba kolumn w wyrażeniu już się W przeciwieństwie do wcześniejszych
zgadza, następnym krokiem jest zwy- przykładów prostych zapytań trzeba objąć
kle określenie wiersza tablicy, który chce- nawiasami wyrażenie zawierające LIMIT O autorze
my pobrać. Kiedy zapytanie zwraca wie- – inaczej będzie ono niepoprawnie osa- Mike Shema pracuje jako dyrektor ds.
le wierszy, często wyświetlany jest tylko dzone w zapytaniu. bezpieczeństwa w zajmującej się bezpie-
czeństwem aplikacji webowych firmie NT
pierwszy z nich. Dobrze skonstruowane
Objectives.
wyrażenie WHERE może w pewnym stopniu Obrona przez wyrażenia
pomóc w zaatakowaniu konkretnego wier- Efektywne sposoby obrony przed omawia- Kontakt z autorem:
sza, ale tylko wtedy, gdy znamy wcześniej nymi technikami to wykorzystanie sprepa- mikeshema@yahoo.com
ogólną strukturę (nazwy kolumn) tablicy. rowanych wyrażeń lub zapisanych proce-

PHP Solutions Nr 5/2006 www.phpsolmag.org 25


www.buyitpress.com

Zaprenumeruj swoje ulubione magazyny


i zamów archiwalne numery!

Już teraz w kilka minut możesz zaprenumerować swoje ulubione pismo.


Gwarantujemy:
- preferencyjne ceny
- bezpieczną płatność on-line
- szybką realizację Twojego zamówienia
Bezpieczna prenumerata on-line wszystkich tytułów Wydawnictwa Software!
zamówienie prenumeraty

Prosimy wypełnić czytelnie i przesłać faksem na numer: (22) 887 10 11 lub listownie na adres: Software-Wydawnictwo Sp. z o.o.,
Piaskowa 3, 01-067 Warszawa, e-mail: pren@software.com.pl. Przyjmujemy też zamówienia telefoniczne: (22) 887 14 44

Imię i nazwisko............................................................................................ ID kontrahenta..........................................................................................

Nazwa firmy................................................................................................. Numer NIP firmy.......................................................................................

Dokładny adres....................................................................................................................................................................................................................

Telefon (wraz z numerem kierunkowym)................................................... Faks (wraz z numerem kierunkowym) ....................................................

E-mail (niezbędny do wysłania faktury)............................................................................................................................................................................


automatyczne przedłużenie prenumeraty

Tytuł
Ilość Od numeru Opłata
Ilość
zamawianych pisma lub w zł
numerów
prenumerat miesiąca z VAT
Software Developer’s Journal (1 płyta CD)
– dawniej Software 2.0 12 250/1801
Miesięcznik profesjonalnych programistów
SDJ Extra (od 1 do 4 płyt CD lub DVD)
– dawniej Software 2.0 Extra! 6 150/1352
Numery tematyczne dla programistów

Linux+ (2 płyty CD)


Miesięcznik o systemie Linux
12 199/1791

Linux+DVD (2 płyty DVD)


Miesięcznik o systemie Linux
12 199/1791

Linux+Extra! (od 1 do 7 płyt CD lub DVD)


Numery specjalne z najpopularniejszymi dystrybucjami Linuksa
8 232/1982

PHP Solutions (1 płyta CD)


Dwumiesięcznik o zastosowaniach języka PHP
6 135

Hakin9, jak się obronić (1 płyta CD)


Dwumiesięcznik o bezpieczeństwie i hakingu
6 135

.psd (1 płyta CD + film instruktażowy)


Dwumiesięcznik użytkowników programu Adobe Photoshop
6 140

Aurox Linux (4 płyty CD + 1 płyta DVD)


Magazyn z najpopularniejszym polskim Linuksem
4 1193

Suma

Jeżeli chcesz zapłacić kartą kredytową, wejdź na


stronę naszego sklepu internetowego:
1
Cena prenumeraty rocznej dla osób prywatnych
2
Cena prenumeraty rocznej dla osób prenumerujących już Software Developer’s Journal lub Linux+
3
Cena prenumeraty dwuletniej Aurox Linux
www.buyitpress.com
Narzędzia

TYPO3 od kuchni,
czyli wymarzony portal
w zasięgu ręki
Stopień trudności: lll
Jean-Gael Rouchon

Zarządzasz witryną internetową, która ma


zhierarchizowaną strukturę stron, a ich
zawartość jest uzupełniana przez wielu
redaktorów. Zależy Ci na pełnej swobodzie
projektowania tego sajtu oraz łatwości jego
tworzenia i rozbudowy. Przedstawiamy TYPO3:
potężny, elastyczny i solidny system CMS klasy
Enterprise, który jest łatwy w rozbudowie, co
umożliwia ciągłe dostosowywanie go do Twoich
potrzeb.

T
YPO3 jest potężnym i elastycz- pozwalający na łatwe dodawanie kolejnych
nym systemem zarządzania treścią możliwości.
(CMS, ang. Content Management Historia TYPO3 zaczęła się od wspo-
System) napisanym w PHP. Jest aplikacją magania rozwoju portali korporacyjnych
opensourcową, dostępną na licencji GNU w Internecie (głównie B2C, ang. Busi-
GPL (General Public License). Jest CMS- ness-to-Customer, czyli przeznaczonych
em wielojęzycznym (48 wersji językowych do kontaktu między firmą a jej klientami).
w maju 2006) i wielowitrynowym (potrafią- Dzięki systemowi rozszerzeń i wspomnia-
cym obsłużyć wiele witryn z jednej instala- nemu frameworkowi, system może rów-
cji). TYPO3 daje użytkownikowi ogromną nież obsługiwać witryny B2B, intranety,
swobodę tworzenia strony, a jego architek- extranety, a także wspomagać tworzenie
tura jest zorientowana na strony (ang. page- pojedynczych aplikacji.
W SIECI centric paradigm). Oferuje rozwinięte moż-
liwości zarządzania grupami oraz dosto-
Co powinieneś
sowywania funkcjonalności pod kątem au-
wiedzieć...
• http://www.typo3.org – głów- torów i gości. Posiada również wbudowa- Przydatna będzie podstawowa znajo-
na strona TYPO3
ny system wersjonowania oraz funkcje mość konfigurowania PHP oraz wiedza
• http://wiki.typo3.org – oficjal-
na encyklopedia TYPO3 przekierowywania URL-i (ang. URL rewri- na temat systemów CMS.
• http://web.inselhof-triemli.ch/ ting). Jedną z największych zalet TYPO3
index.php?id=98&type=3
– wprowadzenie do Typo3 jest system rozszerzeń, który pozwala do- Co obiecujemy...
Testsite dawać nowe funkcje bez modyfikacji rdze- Pokażemy, jak przy pomocy TYPO3
• http://opensourcecms.com stworzyć od podstaw kompletną witrynę
– witryna poświęcona CMS-
nia systemu. Obecnie dostępnych jest po-
internetową, zawierającą forum dyskusyj-
om nad 1400 rozszerzeń (maj 2006). Dodatko- ne i moduł newsów.
wo, twórcy TYPO3 udostępniają framework

28 www.phpsolmag.org PHP Solutions Nr 5/2006


Narzędzia

Zaczniemy od instalacji umieszczo-


nej na naszych stronach internetowych
(www.phpsolmag.org) witryny demonstra-
cyjnej oraz dostosowania BackEndu. Na-
stępnie dowiemy się, jak dodawać i mody-
fikować zawartość witryny. Jeszcze póź-
niej zobaczymy, w jaki sposób utworzyć
własną witrynę i zainstalować na niej apli-
kacje (forum dyskusyjne i moduł newsów).
Na końcu uwypuklimy niektóre spośród
zaawansowanych możliwości Typo3.

Łączymy się z
BackEndem
Podobnie jak większość systemów CMS,
TYPO3 został podzielony na FrontOffi-
ce (witryna widoczna dla użytkownika)
i BackOffice (interfejs administracyjny).
W TYPO3 witryna jest nosi nazwę Fron-
tEnd (FE), a interfejs administracyjny, w Rysunek 1. BackEnd TYPO3 (istotne kroki zostały podświetlone na czerwono)
którym modyfikujemy zawartość – Bac-
kEnd (BE). Między tymi warstwami istnie- ich ustawień. Na górze strony z ustawie- rą chcemy wyedytować (w naszym przy-
je wyraźny podział, gdyż dane użytkowni- niami możemy zmienić język (mamy do padku Test).
ków FE i BE znajdują się w osobnych ta- wyboru wiele wersji językowych, włącznie Po prawej stronie ekranu pokażą się
belach bazodanowych (istnieje możliwość z angielską, polską, niemiecką, francuską przyciski i menu ułatwaiające nam wyko-
połączenia tych tabel, ale nie jest to dla i włoską). Dostępność każdego z języków nywanie rozmaitych operacji na wybranej
nas ani niezbędne, ani pożądane). zależy od tego, czy zainstalowaliśmy Lan- stronie: oglądanie historii jej zmian, edycja
Zaczniemy od zalogowania się jako guage Pack (możemy to zrobić korzysta- właściwości, przenoszenie strony, doda-
zwykły redaktor zawartości witryny, który jąc z opcji Ext Manager w sekcji User tego wanie nowej strony lub jej zawartości oraz
korzysta z konta o nazwie redactor (hasło samego menu). Na stronie konfiguracyj- przeszukiwanie strony.
redactor), który ma ograniczone możliwo- nej możemy też zmienić ustawienia zwią- Wyedytujmy teraz treść strony: w tym
ści modyfikacji sajtu. UWAGA: nazwa kon- zane z rekurencyjnym kopiowaniem i ka- celu musimy po prostu kliknąć przykła-
ta redactor obowiązuje jedynie dla witryny sowaniem stron (określa ono, do którego dowy tekst pokazany poniżej nagłówka
demonstracyjnej i nie działa w przypadku poziomu mają być kopiowane lub kasowa- NORMAL lub przycisk Edit in Rich Text
instalacji tradycyjnej; w tej drugiej sytuacji ne podstrony wybranej strony). Zmienimy Editor umieszczony tuż pod tym tekstem.
korzystaj z konta administratora (login: ad- tu też nasze dane osobowe i dane konta Najlepiej wybrać pierwszy sposób, gdyż
min, hasło: password; w zastosowaniach (włącznie z hasłem). Aby zaakceptować po kliknięciu na Edit in Rich Text Editor nie
produkcyjnych należy zmienić hasło). zmiany, musimy przewinąć formularz i na wszystkie opcje i funkcje edytora będą do-
Interfejs BE jest podzielony na 3 czę- jego końcu kliknąć przycisk Save Confi- stępne (m.in. nie będziemy mogli zmienić
ści umieszczone w 2 ramkach (zob. Ry- guration, po czym się wylogować i zalogo- tytułu i typu treści ani przełączyć się na
sunek 1). Po lewej mamy główne menu, wać ponownie: to wszystko. tryb pełnoekranowy). Dobrym pomysłem
z którego wybieramy czynność do prze- jest kliknięcie przycisku Open in new win-
prowadzenia (edycja treści, zmiana usta- Zarządzanie treścią dow: jest on umieszczony na samym dole
wień użytkownika, zarządzanie plikami, Następnie, nadal zalogowani jako redac- całej ramki i ma kształt rombu.
itd.). W środkowej ramce (jeżeli jest ona tor lub admin, dodamy nową stronę do wi- W prawej części ekranu widzimy teraz
wyświetlana), pokaże się rozwijalne drze- tryny, po czym umieścimy w niej zawar- zawartość strony Test: znajduje się ona w
wo przedstawiające hierarchię zawartości tość. Później utworzymy 3 kolejne strony edytorze HTML-owym typu WYSIWYG,
naszej witryny. Wreszcie, w prawej czę- i zainstalujemy aplikację (forum dyskusyj- będącym aplikacją pochodną wobec edy-
ści ekranu ujrzymy efekty działań wybra- ne) na jednej z nich. tora HTMLArea, zmodyfikowaną poprzez
nych w części środkowej. Wrócimy do te- dodanie funkcji TYPO3 do obsługi linków
go później – zacznijmy od zmiany języka Modyfikacja treści i obrazów. Edytor ten pozwala nam na
używanego w BackEnd. Zacznijmy pracę z witryną demonstracyj- swobodne wprowadzanie tekstu, bieżą-
ną. Zalogowani jako redaktor, klikniemy cą zmianę ustawień czcionki, wstawianie i
Zmiana języka interfejsu na opcję Page w menu po lewej stronie zarządzanie tabelami oraz listami numero-
BackEnd ekranu (mała ikona oznacza tryb edy- wanymi i wypunktowanymi, cofanie i przy-
W menu po lewej stronie ekranu wybiera- cji). Następnie, korzystając z drzewa na wracanie przeprowadzonych działań, wy-
my opcję User–>Setup, aby dojść do swo- środku ekranu, wybieramy stronę, któ- szukiwanie i zastępowanie tekstu, itd. Kli-

PHP Solutions Nr 5/2006 www.phpsolmag.org 29


Narzędzia

kając wyglądający jak <> przycisk Toggle


HTML Source możemy również zobaczyć Wymagania
TYPO3 wymaga parsera PHP (PHP4), bazy danych MySQL-a i serwera Apache lub IIS.
wersję HTML (źródło) edytowanego tek-
Dodatkowo, musisz zwiększyć ilość pamięci przydzielanej procesom PHP. W tym celu wy-
stu, a wciskając przycisk Remove format- edytuj plik php.ini (/etc/php4/apache/php.ini) i ustaw zmienną memory limit na 32M (memo-
ting (wyglądający jak połowa litery A) usu- ry_limit = 32M) zamiast na 8M.
niemy formatowanie tekstu (np. wklejone-
go z aplikacji biurowej do okienka nasze- Instalacja tradycyjna
go edytora). Aby zainstalować TYPO3 w sposób tradycyjny, pobierz ze strony produktu (www.typo3.org)
jego źródło (typo3_src-x.y.tar.gz lub typo3_src-x.y.zip, gdzie x.y oznacza numer wersji, w
Pobawmy się trochę tym edytorem
naszym przypadku 4.0) oraz przykładową, pustą witrynę (dummy-x.y.tar.gz lub dummy-
i wprowadźmy jakiś tekst. Jeśli potrzebuje- x.y.zip). Następnie stwórz folder dla TYPO3 (np. typo3) w katalogu dostępnym dla serwera
my więcej miejsca do edycji, możemy klik- WWW i rozpakuj do niego oba archiwa (możesz nadpisać pliki powtarzające się pliki).
nąć przycisk Full Screen po prawej stronie Następnie w przeglądarce WWW wpisz adres, pod którym rozpakowałeś TYPO3 (np.
http://localhost/typo3) i postępuj zgodnie z instrukcjami instalatora 1-2-3 Install. Będziesz
obszaru edycyjnego. Pamiętajmy jednak,
musiał podać informacje dotyczące bazy danych (nazwę użytkownika i jego hasło (jeżeli
że pozbawia nas to niektórych pewnych jest) oraz nazwę hosta) oraz nazwę nowej bazy, z której będzie korzystał TYPO3 (lub wy-
opcji edytora. Na koniec, możemy zapisać brać istniejącą, jeśli chcesz ją nadpisać) i utworzyć tę bazę. To wszystko, co jest wymaga-
zmodyfikowany tekst korzystając ze znaj- ne: możesz przejść do BackEndu lub FrontEndu witryny, choć warto najpierw przeczytać
uwagi instalatora dotyczące bezpieczeństwa. Aby przejść do BackEndu, wejdź do katalo-
dujących się na szczycie strony naszego
gu http://localhost/typo3/typo3/.
edytora ikon symbolizujących dyskietki. UWAGA: nie należy używać pakietów TYPO3 oznaczonych jako testsite, gdyż nie są
one od dawna aktualizowane i zawierają stare wersje TYPO3, w których brakuje wielu z
Dodawanie obrazków do tekstu opisywanych przez nas możliwości.
Możemy również wstawiać obrazy: tuż pod Korzystamy z witryny demonstracyjnej
obszarem edycyjnym mamy narzędzie po- Zamiast instalować TYPO3 w sposób tradycyjny, możesz użyć specjalnej witryny demon-
stracyjnej, którą omawiamy w tym artykule. Zawiera ona wstępnie zdefiniowaną treść, parę
zwalające nam wybierać i uploadować pli-
zainstalowanych rozszerzeń i domyślny szablon. Dołączyliśmy do niej również plik HTML,
ki graficzne. Jest tam również lista umożli- którego użyjemy, aby utworzyć design strony internetowej.
wiająca nam wybranie obrazu znajdującego Możesz uruchomić witrynę demonstracyjną z PHP Solutions Live CD lub zainstalować
się już na serwerze (np. Typo3V4_box.gif) ją na swoim komputerze. Zakładamy, że Apache, PHP i MySQL są zainstalowane i urucho-
mione. Tworzymy więc folder dla TYPO3 w przestrzeni serwera WWW (w Linuksie jako ro-
lub uploadowanie obrazu znajdującego się
ot wpisujemy polecenie mkdir /var/www/t3test i przechodzimy do tego katalogu komen-
na dysku lokalnym bezpośrednio do prze- dą cd /var/www/t3test. Ścieżka serwera WWW może się różnić od /var/www, trzeba
strzeni plikowej konta redactor lub admin. oczywiście podać obowiązującą). Następnie rozpakowujemy do tego katalogu archiwum
To ostatnie wykonamy klikając ikonę sym- witryny demonstracyjnej (typo3demosite.zip). Aby kontynuować instalację, w przeglądar-
ce WWW przechodzimy pod adres http://localhost/3test/install/index.php. Uruchomi się na-
bolizującą folder i umieszczoną po prawej
rzędzie 1-2-3 Install, które opisywaliśmy dla instalacji tradycyjnej i przeprowadzi nas przez
stronie listy. Tuż pod opisaną przeglądarką pozostałą część procesu instalacyjnego, który wygląda niemal identycznie jak poprzednio.
plików mamy formularz pozwalający nam Instalacja rozpoczyna się od konfiguracji i utworzenia bazy danych. Istotna różnica polega
edytować właściwości wybranego obrazu: na tym, że na etapie inicjalizacji bazy danych, zamiast wybrać opcję Create the default my-
sql database table, decydujemy się na import bazy ze skryptu database_demo.sql. Po za-
ustawiać jego pozycję na stronie, a także
kończeniu instalacji można zacząć korzystać z witryny: jej FrontEnd znajduje się pod adre-
szerokość, wysokość, nagłówek go opisują- sem http://localhost/t3test/index.php, a BackEnd pod http://localhost/t3test/typo3/.
cy, tekst alternatywny (ukazuje się, gdy nie
można wyświetlić obrazu i odpowiada wła- Uwaga na temat specyfiki dema
ściwości alt HTML-owego znacznika <img W wersji demonstracyjnej wykorzystaliśmy szablon strony WWW o nazwie sunflower
pobrany z witryny oswd.org. Znajduje się on w domenie publicznej (ang. public doma-
src>), tytuł obrazu oraz URL do pełnego
in). Zmieniliśmy go nieznacznie w stosunku do oryginału, m.in. poprzez dodanie CSS-a
opisu grafiki. dla drugiego menu i poprawienie brakującego clear:both w stopce strony. Ponadto w in-
Poniżej opcji dotyczących obrazu wi- dex.html umieściliśmy znaczniki (tagi) wymagane przez TYPO3. Poprawilismy też rozsze-
dzimy General Options – ustawienia do- rzenie TYPO3 o nazwie tt_board, aby było zgodne z css_styled_content.
WAŻNE: wersja demonstracyjna nie jest przeznaczona do zastosowań produkcyjnych,
tyczące całej zawartości i pozwalające
a jedynie do eksperymentów i testów.
na ustawianie dat Start i Stop, określają-
cych okres, w którym zdefiniowana zawar-
tość ma być widoczna. Możemy też zde- ków TYPO3, za pomocą której możemy Po kliknięciu lewym przyciskiem my-
cydować, czy dana strona będzie mogła utworzyć odnośnik do strony (lub treści) szy na ikonie zawartości (pierwsza ikona
zawierać podstrony i ustawiać jej widocz- wewnętrznej, którą można wybrać korzy- w lewym górnym rogu ramki edycyjnej)
ność dla zalogowanych i niezalogowanych stając z drzewa, do pliku w przestrzeni pli- ukaże sie menu kontekstowe pozwalające
użytkowników. Opcje te obowiązują dla kowej TYPO3 lub do zewnętrznego URL-a na dokonanie wybranych operacji na za-
wszystkich typów zawartości. czy adresu emailowego: wszystko, co mu- wartości, takich jak skopiowanie lub wycię-
simy zrobić, to kliknięcie w odpowiednią cie bloku. Jest to prosty sposób na prze-
Wstawianie hiperlinków zakładkę i wprowadzenie wymaganych noszenie zawartości lub stron w ramach
Aby wstawić hiperlinka w tekście, musi- danych. Domyślnie linki zewnętrzne uka- hierarchii witryny. Wykonywanie tych dzia-
my zaznaczyć w edytorze słowo lub ob- zują się w osobnym okienku, a wewnętrz- łań nie zmienia linków pomiędzy stronami
raz i kliknąć przycisk Insert Web Link (iko- ne w tym samym oknie. Ważne jest to, że ani zakładek w przeglądarce WWW, gdyż
na symbolizująca Ziemię) w pasku narzę- możemy ukryć adresy emailowe przed ro- TYPO3 używa stałych identyfikatorów (ID)
dzi edytora. Ukaże się przeglądarka lin- botami spamerskimi. dla każdej strony. Możemy też przenosić

30 www.phpsolmag.org PHP Solutions Nr 5/2006


Narzędzia

strony w hierarchii drzewa metodą prze-


ciągnij i upuść. Dokumentacja TYPO3
TYPO3 ma rozbudowaną dokumentację, zamieszczoną pod adresem http://typo3.org/
Używanie i czyszczenie cache'a documentation. Jest ona dostępna w formatach PDF i SXW oraz w postaci plików wideo
(ok. 5 godzin). Na początek polecamy manuale:
TYPO3 ma zintegrowany system cache'a,
używany w celu minimalizacji obciążenia • Getting Started (doc_tut_quickstart – podręcznik dla redaktora witryny)
serwera. Gdy dokonujemy aktualizacji, • TS by example (przykłady TypoScriptu dla początkujących)
musimy również wyczyścić cache. W tym • Modern Template Building 1 (tworzenie szablonów)
• Futuristic Template Building (tworzenie szablonów przy użyciu TemplaVoila)
celu na liście rozwijalnej znajdującej się
na szczycie ramki edycyjnej, w której pra- Istnieje również książka, która została napisana przez członków zespołu tworzącego pod-
cujemy, wybieramy [Menu] –> [Clear Ca- stawy TYPO3. Jest ona dostępna w niemieckiej, angielskiej i francuskiej wersji językowej
i pozwala na bardzo dobre poznanie TYPO3.
che] –> This page.

Dodawanie strony ve document and view page): strona jest cego obrazy. Moglibyśmy ustawić pozycję,
TYPO3 jest CMS-em zorientowanym na gotowa. rozmiar, nagłówek i tytuł każdego obraz-
strony (ang. page-centric CMS). Ozna- Zanim umieścimy jakąkolwiek treść na ka. Aby zobaczyć więcej opcji, przewijamy
cza to, że pojedyncza strona internetowa naszej stronie, warto wiedzieć kilka rze- formularz i na jego samym dole zaznacza-
jest w nim podstawą całej witryny, umoż- czy. Po pierwsze, istnieje wiele sposo- my checkbox Show secondary options: w
liwiając nawigację i przechowując zawar- bów na dodawanie stron, m.in. użycie iko- formularzu pojawią się takie opcje, jak ja-
tość. Jest to zupełnie inne rozwiązanie niż ny Create new page w Page view czy też kość obrazu, efekty (np. obroty czy regula-
stosowane w takich CMS-ach jak Mambo, opcji new w menu uruchamianym po klik- cja kontrastu) czy click to enlarge. Użycie
gdzie zawartością zarządzamy przy uży- nięciu dowolnej ikony w ramach drzewa ostatniej opcji spowoduje, że po kliknięciu
ciu osobnego modułu, a potem linkuje- pośrodku ekranu. Po drugie, istnieją in- na obraz na stronie TYPO3 wyświetli go w
my ją do wybranych stron. Koncepcja i co- ne typy stron (wybieramy je z listy rozwi- nowym oknie, co jest bardzo przydatne w
dzienne zarządzanie witryną są więc zu- jalnej Type w opisywanym Wizardzie), ta- prostej galerii zdjęć.
pełnie inne niż w tamtych CMS-ach. kie jak skróty do innych części witryny, lin- Zmiana jakości (resampling) czy de-
Zależnie od swoich uprawnień, redak- ki zewnętrzne lub strony zaawansowane korowanie obrazka zachodzi automatycz-
tor może dodać stronę w wybranej części (advanced mode; dodajemy w nich słowa nie wobec wszystkich obrazów z zestawu
witryny. Drzewo stron jest całkowicie nie- kluczowe, opisy, itd). Możemy też dodać przy użyciu kombinacji bibliotek GD oraz
zależne od innych czynników, takich jak stronę typu Sysfolder: gromadzimy na niej ImageMagick.
zainstalowane moduły (w przeciwieństwie dane (zawartość, newsy, ustawienia, da- Kolejnym typem zawartości jest File-
np. do phpNuke i pochodnych). Każda no- ne użytkowników FE, itd), których będzie- links, którego używamy w celu wyświe-
wa strona jest automatycznie dodawana my mogli użyć w dowolnej części witryny. tlenia listy plików, z których każdy zawie-
do menu (jeżeli istnieje menu odpowied- Po trzecie, możemy tworzyć wiele stron ra krótki opis. Typ ten jest użyteczny, jeśli
niego poziomu). jednocześnie korzystając z menu Func- chcemy umieścić na stronie listę plików do
Dodajmy teraz parę stron do naszej tions po lewej stronie ekranu (jest to bar- pobrania (np. dokumentację, tutoriale czy
witryny: będzie to trochę podobne do mo- dzo przydatne, gdy budujemy witrynę od skompresowane aplikacje). Możemy zde-
dyfikacji zawartości strony. podstaw, gdyż skraca czas). finiować wygląd takiej listy korzystając z
W tym celu klikamy opcję Page w kilku gotowych wzorów.
menu po lewej stronie ekranu i wybie- Dodawanie bloku tekstowego Wracając do klasycznej zawartości
ramy stronę Test w znajdującym się po- Dobrze, mamy już naszą stronę, do któ- strony, mamy Menu Site Map, czyli ma-
środku ekranu drzewie stron. W ramce rej chcemy dodać zawartość: zróbmy to. pę witryny. Używamy jej, aby umieścić na
po prawej klikamy przycisk Create New W tym celu wybieramy Page –> Mypage stronie mapę całego sajtu lub jego czę-
Record: pojawi się w niej Wizard doda- –> Create new content. W wizardzie, któ- ści (korzystając z pola Starting Point: ma-
wania stron. Wybieramy w nim opcję Pa- ry się pojawi, wybieramy Page Content my do dyspozycji drzewo, które pozwa-
ge(Inside) – Click here for Wizard, a na- –> Click Here For Wizard. Otrzymamy li- la nam wybrać miejsce witryny, od które-
stępnie wybieramy docelowe położenie stę typów zawartość dostępnych dla użyt- go zaczniemy tworzenie mapy). Możemy
strony. kownika redactor (lub admin). Gdybyśmy też umieścić menu podstrony na poziomie
Pojawi się formularz dodawania stro- wybrali Regular Text, moglibyśmy ustalić, o jeden niższym niż sama strona. Jeżeli
ny, w którym wpiszemy jej tytuł (np. Mo- w której kolumnie chcemy umieścić za- podstrony są typu Advanced, i wypełnisz
ja Strona) – będzie on używany w ta- wartość (wybierzemy kolumnę Normal). pole opisu, możesz sporządzić np. pod-
gu <title> strony WWW oraz jako na- Na koniec widzimy ten sam formularz, któ- sumowanie (resume) podstron. Inną po-
zwa strony w drzewie. Jak widzimy, strona rego używaliśmy podczas modyfikacji ist- żyteczną sztuczką, którą możemy zasto-
jest domyślnie ukryta (poprzez zaznacze- niejącej treści. Przejdźmy teraz do listy sować na mapie witryny jest sporządze-
nie checkboksa Hide page), gdyż nie ma rozwijalnej Type i wybierzmy header: po- nie spisu treści stron na stronie aktywnej.
jeszcze żadnej zawartości: chcemy, aby zwala nam to na wstawienie samego na- Przydatne jest również zaznaczenie bok-
była widoczna na witrynie i w menu, więc główka, bez treści strony. su To top w formularzu edycyjnym każ-
odhaczymy tego checkboksa. Następnie Użycie typu Text w/ Images pozwoli- dego elementu strony (opcja secondary
klikamy na drugiej ikonie z dyskietką (Sa- łoby nam na wstawienie tekstu zawierają- options, umieszczona tuż poniżej listy roz-

PHP Solutions Nr 5/2006 www.phpsolmag.org 31


Narzędzia

nych innych pól, również tego oznaczo-


nego jako CODE, które w obecnej wersji
TYPO3 jest poprostu przestarzałe. Zapi-
sanie forum poprzez kliknięcie przycisku
Save document and view (ikona dyskiet-
ki) kończy naszą pracę nad nim. Mamy
gotowe, małe forum dyskusyjne na naszej
stronie, które nadaje się świetnie jako na-
rzędzie do komentowania zawartości stro-
ny przez użytkowników.
Jeżeli wybierzemy opcję List z menu
po lewej stronie ekranu, a następnie stro-
nę My Page w drzewie pośrodku ekra-
nu, zobaczymy listę wypowiedzi (postów)
z forum zamieszczonych na stronie. Plu-
gin tt_board pobiera wypowiedzi z danej
strony, a następnie je segreguje i wyświe-
tla w postaci drzewa. Udostępnia również
formularz, za pomocą którego dopisujemy
nowe wiadomości. Listę zamieszczonych
wiadomości zobaczymy w BE, tuż poniżej
Rysunek 2. Szablon, który utworzymy
zawartości strony.
wijalnej umożliwiającej wybór typu treści, tt_board nie jest załadowane, istnieje szan-
musi być włączona). W ten sposób umoż- sa, że mamy je gotowe do instalacji w sys- Potęga szablonów
liwimy nawigację pomiędzy blokami tek- temie TYPO3: aby to sprawdzić, wybierz- Możemy już tworzyć i zarządzać zawarto-
stowymi a spisem treści. my opcję Install extensions. Jeżeli nadal ścią strony – świetnie. Jeszcze lepiej by-
Ostatnim klasycznym typem zawarto- nie możemy znaleźć tt_board, to znaczy, że łoby jednak, gdybyśmy mogli tworzyć wła-
ści, który omówimy, jest Insert Record. Po- nie jest ono zainstalowane – musimy więc sną witrynę w oparciu o szablon. Chcemy,
zwala on na wyświetlanie treści zgroma- je pobrać i zainstalować w TYPO3. W tym aby zawierał on 2 menu, z których pierw-
dzonej w innej części witryny. Znaczy to, celu wchodzimy do repozytorium rozsze- sze jest poziome, a drugie jest pionowe
że zawartość ta będzie się cały czas znaj- rzeń TYPO3 (TYPO3 Extensions Reposi- i istnieje na drugim i trzecim poziomie wi-
dowała w oryginalnym położeniu, a każ- tory), które znajduje się pod adresem http: tryny. Chcemy, aby zawartość była wy-
da jej modyfikacja będzie natychmiastowo //www.typo3.org/extensions/, klikamy na za- świetlana w dwóch strefach: pierwsza
i automatycznie widoczna na naszej stro- kładkę Full list, odnajdujemy tt_board i po- z nich będzie duża i ulokowana w cen-
nie. Insert Record jest więc nie tyle typem bieramy je na dysk lokalny. Następnie wra- trum, a druga mała i umieszczona po le-
treści, co referencją do innego bloku. camy do Extension Managera i wybiera- wej, przeznaczona do zamieszczania nie-
my opcję Import extensions. Zwróćmy uwa- wielkich notatek.
Dodajemy aplikację gę na nagłówek UPLOAD EXTENSION FI- Użyjemy szablonu sunflower (zob. Ry-
Jedną z najpotężniejszych cech TYPO3 LE DIRECTLY (.T3X). Tuż pod nim znajdu- sunek 2) – szablon znajdujący się w dome-
jest system rozszerzeń. Umożliwia on je się przycisk przeglądarki plików: klikamy nie publicznej i pobrany z www.oswd.org.
nam łatwe dodawanie nowych funkcji. go, wybieramy pobrany właśnie plik .t3x i Nieznacznie zmodyfikowaliśmy plik HTML,
Każde rozszerzenie może dodawać no- klikamy Upload extension file. Zostaniemy dodając do niego znaczniki TYPO3 (zob.
we typy zawartości lub funkcje BackEn- poprowadzeni przez Wizarda i będziemy Listing 1). Strukturą naszego sajtu będą
du albo zawierać gotowe aplikacje (np. fo- musieli jedynie postępować zgodnie z je- strony Test: zmienimy tylko ich wygląd.
rum dyskusyjne). Te ostatnie będą widocz- go instrukcjami. W ten sposób zainstaluje- Aby tego dokonać, musimy się przełączyć
ne we FrontEndzie i noszą nazwę wtyczek my tt_board. do profilu admin (jeżeli dotychczas byliśmy
(pluginów, ang. plugins). Dla TYPO3, fo- Aby umieścić zainstalowane w TYPO3 zalogowani jako redactor). Wylogujmy się
rum dyskusyjne jest złożonym typem za- tt_board na naszej stronie, przechodzimy więc z BE klikając opcję w menu po le-
wartości korzystającym z wielu rekordów do Page -> My Page -> Create new con- wej stronie ekranu i zalogujmy się na kon-
(którymi są wypowiedzi) oraz narzędzia tent, przewijamy listę typów treści i w sek- to admin (domyślne hasło to password).
wyświetlającego (samo forum). Zainstalu- cji Plugins wybieramy Discussion forum Nasz interfejs ma teraz więcej opcji: został
jemy rozszerzenie tt_board, które jest bar- (na samym końcu listy). Ta ostatnia ope- wzbogacony o te, do których dostęp ma
dzo prostym forum dyskusyjnym. racja jest identyczna z wybraniem General tylko administrator.
Zacznijmy od sprawdzenia, czy tt_bo- plugin w tej samej sekcji, następującej po
ard jest zainstalowane w TYPO3. W tym nim selekcji Insert plugin jako typu zawar- Jak działają szablony w TYPO3?
celu przechodzimy do Extension Mana- tości, a następnie wyborem Board, tree z Istnieją dwie główne metody wykorzysta-
gera (Ext manager w menu po lewej stro- listy rozwijalnej Plugin. Możemy też (jeśli nia szablonów w TYPO3: Modern Templa-
nie ekranu) i w liście rozwijalnej Menu: wy- chcemy) wprowadzić nagłówek forum (np. te Building (nazwa pochodzi z manuala)
bieramy Loaded extensions. Nawet, jeśli Dyskusje). Nie musimy wypełniać żad- oraz Templavoila. Pierwsza z nich, której

32 www.phpsolmag.org PHP Solutions Nr 5/2006


Narzędzia

użyjemy na naszej witrynie, jest klasycz- Rejestr Windows). Język został o pisany w nich (Constant i Setup) informują TYPO3,
na i opiera się na umieszczaniu specjal- osobnym manualu (TS Reference manual), że mamy do czynienia z nowym szablo-
nych znaczników w plikach HTML. Meto- a także w instrukcjach załączanych do każ- nem, który nie dziedziczy z głównego sza-
da ta jest prosta do zrozumienia i znajduje dego rozszerzenia (gdzie opis dotyczy kon- blonu (dziedziczenie po poprzednich sza-
zastosowanie przy tworzeniu rozszerzeń. figuracji tego rozszerzenia). blonach w linii root kodu TypoScript zosta-
Użycie MTB pozwoli nam się przyzwycza- je wykasowane). Trzecia opcja (Rootlevel)
ić do języka TypoScript. Natomiast druga Tworzymy własny szablon określa, że szablon jest korzeniem (ro-
metoda, Templavoila, pozwala na stoso- Aby utworzyć swój własny szablon sko- otem) witryny internetowej (główną stro-
wanie szablonów przy użyciu interfejsu ty- rzystamy z menu List po lewej stronie, wy- ną), a więc strona, na której zostanie uży-
pu point-and-click. bierzemy stronę Test w drzewie pośrodku ty, będzie stroną domową całej witryny.
Dla szablonów w TYPO3 przeznaczo- ekranu, klikniemy Create new record i ja- Możemy też dołączać (inkludować)
no osobny typ zawartości o nazwie Tem- ko typ treści wybierzemy Template. Uka- elementy innych szablonów i ustawień
plate oraz specjalny język TypoScript. Nie że nam się formularz dotyczący szablo- składowanych w bazie danych (Include
jest on językiem programowania, lecz ję- nu, w którym wypełnimy jedynie tytuł (bę- static) lub w plikach (Include static (from
zykiem opisowym, za pomocą którego dący nazwą szablonu). Możemy też usta- extensions)). Dodatkowo, możemy dołą-
definiujemy, jak szablony powinny dzia- wić nazwę witryny: zostanie ona użyta w czać całe szablony używając pola Include
łać – określamy sposób renderowania wi- tagu <title> przy nazwie strony (np. Moja- basis template field. My skorzystamy z po-
tryny, opisujemy obiekty dynamiczne Witryna : Strona1). la Include static (from extensions), wybie-
(np. menu) i konfigurujemy niektóre pluginy. Następnie w tym samym formularzu rając CSS Styled Content. Rezultatem tej
TypoScript używa notacji obiektowej w celu odhaczymy checkboksy Clear: Constant, operacji będzie dołączenie podstawowych
przedstawienia logiki drzewa (podobnie jak Clear: Setup i RootLevel. Dwa pierwsze z ustawień dotyczących renderowania, co
zaoszczędzi nam sporo pracy. Na koniec
Listing 1. Znaczniki TYPO3 wstawione do szablonu sunflower zapisujemy szablon korzystając ze znanej
już nam doskonale ikony z dyskietką.
<body>
<!--###DOCUMENT_BODY### start--> Zabawa z TypoScript
<div class="thebox">
Jeżeli zechcemy zobaczyć, jak wygląda
<div class="logo"><a href="#">###SITENAME###</a></div>
<!--###MENU1### start--> nasza witryna (np. http://localhost/typo3/),
<ul class="header"> zobaczymy komunikat o błędzie Error!.
<li><a href="#">Diary</a></li> Dzieje się tak, gdyż nie ustaliliśmy jeszcze
<li><a href="#">The Idea</a></li> metody renderowania i TYPO3 nie wie ani
<li><a href="#">Contact</a></li>
jakiej zawartości powinien użyć, ani w jaki
</ul>
<!--###MENU1### end--> sposób ją wyświetlić – poprawmy to.
<div class="side"> W naszym szablonie musimy wpisać
<!--###MENU2### start--> odpowiednie informacje w polach Con-
<h2>Archives</h2> stant i Setup. Constant to zmienna w ję-
<a href="#">March 2006</a><br/>
zyku TypoScript oraz miejsce, w którym
<!--###MENU2### end-->
<!--###SIDECONTENT### start--> można wygodnie umieszczać wartości wy-
<h2>Links</h2> korzystywane w polu Setup: później poka-
<a href="http://validator.w3.org/check/referer">XHTML</a><br/> żemy, jak to zrobić, a na razie zajmijmy się
<form method="get" action="#"> polem Setup.
<p><input size="12" style="border:1px solid #eee" type="text" id="q"
Pamiętajmy, że TypoScript ma skład-
name="q" /></p>
</form> nię obiektową, której używamy do konfigu-
<!--###SIDECONTENT### start--> rowania silnika renderującego (ang. ren-
</div> dering engine) oraz umieszczania obiek-
<div class="content"> tów na stronach WWW. Wprowadzimy
<!--###CONTENT### start-->
kod przedstawiony na Listingu 2 do po-
<h2><a href='#'>Sunflower</a></h2><h4>22 March 2006</h4><p>Sunflower validates
as XHTML la Setup. Zaczniemy od inicjalizacji stro-
1.1<br /><br />It has a liquid design or it will fit the available screen ny wpisując mypage = PAGE;, wskutek cze-
width.<br />If go powstanie obiekt mypage typu (klasy)
you like this theme, submit your comments in <a href=" PAGE. Nadamy mu domyślny typ typeNum.
http://www.kumi.co.nr/">www.kumi.co.nr</a><br /><br />Thank you^o^<br /></p>
Zauważmy, że pozwala nam to na zarzą-
<a href='#'>0&nbsp;Comments</a><br/><p>&nbsp;</p>
<!--###CONTENT### end--> dzanie wieloma obiektami stron (np. ram-
</div><div class="footer"> kami) w jednym skrypcie TypoScript, a
&copy; Copyright you | Design by <a href="http://www.kumi.co.nr">Kumiko</a> dzięki typeNum możemy korzystać z róż-
</div> nych metod renderowania (np. druk, PDF
</div><!--###DOCUMENT_BODY### end-->
czy WAP). Następnie zdefiniujemy ścież-
</body>
kę CSS do obiektu strony mypage (mypa-
ge.stylesheet). Jest to konieczne, gdyż

PHP Solutions Nr 5/2006 www.phpsolmag.org 33


Narzędzia

ścieżka CSS zdefiniowana w szablonie


HTML jest błędna.
Kolejnym krokiem będzie załadowanie
szablonu HTML i poinformowanie silnika
TYPO3 o tym, aby w miejsce oznaczonych
(otagowanych) stref w tym pliku wsta-
wił odpowiednią treść. Pierwszym obiek-
tem na tej stronie (page.10), który utwo-
rzymy, będzie obiekt typu TEMPLATE (mypa-
ge.10 = TEMPLATE). Określimy następnie
plik do załadowania (template = FILE i
template.file = fileadmin/templates/
sunflower/index.html).
W pliku HTML-owym musimy ozna-
czyć strefy stosując pary znaczników
<!--###MYZONE##-->, które określają pod-
części (ang. subparts). Każda podczęść
Rysunek 3. Zarządzanie zawartością strony zmodyfikowanej przez zastosowanie określona pomiędzy dwoma tagami może
TemplaVoila następnie zostać zastąpiona treścią wyge-
nerowaną przez silnik TYPO3.
Listing 2. Ustawienia naszego szablonu Możemy też wstawiać oznaczenia
składające się tylko z jednego tagu: zo-
# definiujemy pierwsze menu staną one zastąpione w ten sam spo-
temp.menu1 = HMENU sób. Następnym krokiem będzie zdefinio-
temp.menu1.special = list
wanie części używanej w szablonie jako
temp.menu1.special.value = 8, 2, 1
temp.menu1.1= TMENU workOnSubpart = DOCUMENT_BODY. Pozwala
temp.menu1.1.NO.linkWrap = <li>|</li> to TYPO3 na stosowanie wielu szablonów
temp.menu1.wrap = <ul class="header">|</ul> zgromadzonych w jednym pliku HTML.
Ostatnia część naszego kodu zastępu-
# definiujemy drugie menu
je oznaczenia i podczęści treścią. Znacze-
temp.menu2 = HMENU
temp.menu2.wrap = <ul class="sidemenu">|</ul> nie obiektu TEXT jest oczywiste, wyjaśnimy
temp.menu2.1 = TMENU więc inny obiekt, styles.content.get. Jest
temp.menu2.1 { on skrótem zdefiniowanym w szablonie sta-
NO.linkWrap = <li> |</li> tycznym CSS styled content, który wcze-
ACT = 1
śniej załadowaliśmy. Jego zadaniem jest za-
ACT.linkWrap = <li> |</li>
ACT.ATagParams = style="color:#e69b0a;" rządzenie, aby TYPO3 pobierał zawartość
} z kolumny NORMAL i wyświetlał ją. Linia
temp.menu2.2 = TMENU subparts.CONTENT < styles.content.get
temp.menu2.2 < temp.menu2.1 wstawia więc kolumnę NORMAL do sza-
temp.menu2.2.wrap = <ul>|</ul>
blonu HTML w miejscu określonym przez
## inicjalizujemy stronę
mypage = PAGE znacznik <!--###CONTENT###-->. Tej samej
mypage.typeNum = 0 techniki używamy w celu wstawienia menu1
mypage.stylesheet = fileadmin/templates/sunflower/style.css i menu2 w odpowiednich dla nich strefach.

## ładujemy szablon HTML-owy


mypage.10 = TEMPLATE
Tworzymy menu
mypage.10 { Przyjrzyjmy się teraz bliżej menu. Obiekt
template = FILE menu o nazwie temp.menu1 jest typu HMENU
template.file = fileadmin/templates/sunflower/index.html (H jak hierarchiczny). Zawiera po jednym
obiekcie podrzędnym dla każdego pozio-
## odczytujemy część zawartą pomiędzy znacznikami <body> i </body>
mu drzewa witryny, który chcemy wyświe-
workOnSubpart = DOCUMENT_BODY
## zastępujemy poszczególne strefy tlić. Przyjrzyjmy się sposobowi numera-
marks.SITENAME = TEXT cji menu: jest on ścisły, ponieważ indeks
marks.SITENAME.value = Our Site każdej pozycji w menu określa jednocze-
subparts.MENU1 < temp.menu1 śnie jej głębokość. Każda pozycja w me-
subparts.MENU2 < temp.menu2
nu (tzn. każdy poziom menu) może być in-
subparts.CONTENT < styles.content.get
subparts.SIDECONTENT = TEXT nego typu. W naszym przykładzie mamy
subparts.SIDECONTENT.value = Here the side content tylko menu tekstowe, można jednak two-
} rzyć również menu graficzne (przy użyciu
Gdlib) lub graficzno-tekstowe, menu za-

34 www.phpsolmag.org PHP Solutions Nr 5/2006


Narzędzia

PHP Solutions Nr 5/2006 www.phpsolmag.org 35


Narzędzia

wierające się w polu <select> lub menu


rozwijalne. Ponieważ dla nas te możliwo-
ści nie mają wielkiego znaczenia, aby się
o nich dowiedzieć zajrzyj do instrukcji TS
by example manual oraz TS Reference.
Aby utworzyć pozycję menu teksto-
wego używamy konstrukcji temp.menu1.1=
TMENU. Jedyną właściwością, którą musimy
zdefiniować jest status normal tego menu,
który ustawimy wpisując temp.menu1.1.NO.
linkWrap. Konstrukcja ta nakazuje TYPO3
umieścić link pomiędzy tagami <li>. Usta-
wianie statusu NO jest obowiązkowe.
Istnieje wiele ustawień menu, które
zmieniają sposób jego zachowania w ska-
li globalnej (całej witryny) albo modyfiku-
ją jedynie wybraną pozycję. Przykładem
na to pierwsze jest konstrukcja special =
list in temp.menu, która zmienia typ me-
nu z dynamicznego na statyczne, którego
pozycje mają postać listy.
Istnieje też parametr umożliwiający
tworzenie zestawów przycisków Previo-
us | UP | Next (Poprzedni | DO GÓRY |
Następny), nawigacja typu breadcrumb
(menu pozwalające przełączać się mię-
Rysunek 4. Directory Flexcontent – domyślny formularz oparty na (X/HT)ML
dzy kilkoma poziomami stron, np. newsy-
>kraj>najnowsze czy języki programowa- content.getLeft, co spowoduje, że TYPO3 te Building. Opiera się na wykorzystywaniu
nia>php>nowe projekty) lub katalog stron będzie pobierał zawartość z lewej kolumny technik graficznych, w większości typu po-
(wypróbuj temp.menu1.special=directory i umieszczał ją w podczęści SIDECONTENT. int-and-click. Tym, co czyni TemplaVoila, jest
czy temp.menu1.special.value = 13). Musimy tylko dodać treść w lewej kolum- konwersja HTML-a do XML-a. TV zastępuje
Menu o nazwie menu2 jest przykła- nie używając sekwencji Page –> TEST –> istotne tagi HTML-owe (div, td, dd, itd.) wę-
dem konfigurowania poszczególnych pozy- LEFT -> Create content (w ostatnim kroku złami XML, a następnie określa, czy dany
cji menu. Oprócz statusu normal, możemy zwyczajnie klikamy ikonę Create content znacznik ma zarządzać treścią i jaką. Ma-
używać innych statusów, takich jak RO (rollo- icon umieszczoną tuż pod nagłówkiem powanie zawartości do elementów HTML
ver – stan pozwalający na zdefiniowanie te- LEFT). Można tak dostosować BE, aby wy- jest bardzo proste i dokonujemy go korzy-
go, jak ma wyglądać pozycja menu, gdy na- świetlane były tylko kolumny Left i Normal, stając z formularzy, w których wprowadza-
jedziemy na nią kursorem myszy), ACT (acti- ale nie będziemy się tym zajmować. my potrzebne dane. Liczba mapowanych
ve page – stan ten oznacza, że pozycja me- elementów (treści) odpowiada ilości pól lub
nu odnosi się do aktualnie oglądanej strony) Inne silniki szablonów kolumn w docelowym HTML-u. Przykłado-
lub IFSUB (pozycja menu posiadająca ten Istnieją również inne niż opisane przez wo, jeżeli utworzymy 2 mapowania treści
status jest aktywna, jeżeli ma podstrony). nas metody wprowadzenia i wykorzysta- w TV, otrzymamy 2 kolumny zamiast 4, któ-
W przypadku menu2 włączymy status ACT nia szablonów w Typo3. Skupiliśmy się na re mieliśmy używając metody Modern Tem-
(ACT=1), dodamy nowy styl wewnątrz tagu Modern Template Building, ponieważ jest plate Building (zob. Rysunek 3). Dobre jest
<a> używając ACT.ATagParams i zawrzemy ono najlepszym sposobem, aby obserwo- to, że w szablonie również możemy używać
link pomiędzy tagami <li>. Przeglądając wać efekty użycia TypoScriptu. Unowo- pól, określających np. kolor strony. Wymie-
później witrynę we FrontEndzie, zobaczy- cześniona wersja tej metody działa dzię- nione pole będzie dodane do właściwości
my, że strony aktywne (włącznie z główną) ki wykorzystaniu rozszerzenia Automa- strony, a odpowiedni kolor zostanie zastą-
są podświetlone w menu. ke Template, a znaczniki umieszczane w piony przez silnik renderujący.
szablonie są automatycznie generowane Metody TemplaVoila możemy używać
Ostatnie szlify naszego szablonu przez TYPO3. Używając wymienionego zarówno wobec szablonu całej strony, jak
Jedną z ostatnich czynności, jakie prze- rozszerzenia pomijamy jeden krok w pro- i jego części. Możliwe jest tworzenie treści
prowadzimy wobec tego szablonu jest za- cesie projektowania witryny, ponieważ nie stanowiącej strukturę (np. opis filmu) bez
stąpienie podczęści SIDECONTENT praw- musimy modyfikować plików HTML. tworzenia tabeli bazodanowej, konieczne
dziwą zawartością. Zawartość lewej Ponad rok temu pojawiło się rozsze- w takim wypadku jest jednak zastosowa-
kolumny umieścimy w odpowiedniej pod- rzenie TemplaVoila. Jest ono bardzo potęż- nie końcowego renderowania HTML. Na
części. W tym celu podmienimy po- ną metodą zarządzania szablonami w TY- Rysunku 4 widzimy elastyczny spis treści
prostu linie subparts.SIDECONTENT po- PO3. Została opisana w należącym do ma- utworzony na podstawie struktury danych
przez subparts.SIDECONTENT < styles. nuala TYPO3 rozdziale Futuristic Templa- zapisanej w XML-u, a nie w bazie danych.

36 www.phpsolmag.org PHP Solutions Nr 5/2006


Narzędzia

Metoda TemplaVoila daje bardzo duże zodanową i da możliwość zmiany opcji. W cji lub liczbę jednocześnie wyświetlanych
możliwości, aby je lepiej poznać, powinie- tym przypadku poprostu przewijamy stronę wiadomości, itd. My tylko pokazaliśmy, jak
neś przeczytać manual na temat Futuristic i zgadzamy się na instalację: moduł News go uruchomić i jak dodawać wiadomości.
template building. Najmocniejszą stroną jest gotowy do pracy. Do swojej dyspozycji mamy również
tej metody jest na pewno mapowanie przy UWAGA: ten sposób instalacji może narzędzie ułatwiające tworzenie rozszerzeń
użyciu narzędzi graficznych: wystarczy, okazać się niemożliwy przy powolnym po- (asystenta) o nazwie Extension Kickstarter.
że wybierzemy pożądany typ zawartości łączeniu z Internetem: skrypt może prze- Jego zadaniem jest automatyczne tworze-
(Content Column, Typoscript, Text Field, kroczyć maksymalny czas wykonywania nie wymaganych w każdym rozszerzeniu
itd), klikniemy na znaczniku, którego chce- (nastąpi timeout), który jest standardowo plików oraz tworzenie pustego kodu PHP
my użyć na danej stronie et voilà! – nasz ustawiony na 30 sekund. Aby temu zapo- (który później zastąpimy własnym). Może-
szablon jest gotowy. biec, musimy albo zmienić zawarte w pliku my użyć tego narzędzia do wstawiania no-
Działanie TemplaVoila zostało przed- php.ini ustawienie max_execution_time na wych pól do bazy danych. Co ciekawe, po-
stawione na filmie zrobionym we Flashu, 0 (skrypt będzie wykonywany w nieskoń- la te dodajemy używając oznaczeń typo-
dostępnym pod adresem: http://typo3.org/ czoność, aż do przerwania jego pracy), al- wych dla formularzy (textarea, selector,
documentation/articles/minute-website/. bo też ręcznie pobrać rozszerzenie, a na- file), a nie typów SQL-owych (np. varchar
Dla Twojej wygody, umieściliśmy ten stępnie zainstalować je z dysku lokalnego czy integer). Wydaje się to trochę dziwne,
film na naszej witrynie demonstracyjnej – identycznie, jak w przypadku opisanej lecz jest wydajne.
(TYPO3 –> English -> Cool Features). przez nas instalacji tt_board.
Mając już moduł newsów, dodamy go Podsumowanie
Dodawanie nowych do strony Test. W tym celu przechodzi- Opisaliśmy najbardziej użyteczne moż-
aplikacji my na stronę Test i klikamy Create new re- liwości TYPO3, dając Ci solidne podsta-
Jak już wiemy, jedną z najlepszych cech cord -> Page Content -> Click for Wizard. wy do podjęcia decyzji, czy chcesz ko-
TYPO3 jest system rozszerzeń, o któ- Następnie musimy wybrać plugin o nazwie rzystać z tego CMS-a, czy nie. Aby się o
rym już wspominaliśmy. Podobnie, jak in- News, który jest umieszczony na końcu li- tym upewnić, możesz zajrzeć pod adres
ne systemy wtyczek (np. z Firefoksa), po- sty w sekcji Plugins. Klikamy go i umiesz- http://typo3.com, gdzie znajduje się Eva-
zwala on na wzbogacanie naszej witryny czamy w kolumnie Normal. W formularzu luation Center – miejsce, w którym zgro-
o nowe funkcje bez modyfikowania silnika edycyjnym wpisujemy nagłówek (np. Moje madzone sa rozmaite informacje na te-
leżącego u jej podstaw (rdzenia). Działa- newsy) i wybieramy LATEST w What to di- mat TYPO3. Możliwości TYPO3 są dodat-
nie każdego rozszerzenia może dotyczyć splay: moduł będzie wyświetlał tytuł i pierw- kowo zilustrowane przy użyciu przykładów
FrontEndu, BackEndu lub obu z nich. sze słowa każdego newsa. Zapiszmy nasze we Flashu (http://typo3.com/Feature_
Rozszerzenia TYPO3 możemy pobrać modyfikacje korzystając (jak zwykle) z ikony list.1243.0.html).
z repozytorium znajdującego się pod ad- przedstawiającej dyskietkę. Niezwykłą funkcją TYPO3, która poja-
resem http://typo3.org/extensions (ponad Musimy jeszcze zaktualizować sza- wiła się w wersji 4.0, jest logika przestrzeni
1400 dostępnych rozszerzeń) lub zapisać blon, aby uwzględnić w nim newsy. W tym roboczych (ang. workspace logic). Pozwa-
na serwerze, na którym są tworzone i prze- celu przechodzimy do menu List i edytuje- la ona na określenie przestrzeni, w której
słać je na serwer produkcyjny. Każde roz- my szablon Test. Następnie do listy roz- będzie zapisawana treść, np. brudnopis
szerzenie jest zarchiwizowane w postaci szerzeń Include static from extensions do- czekający na akceptację redaktora.
pojedynczego pliku .t3x, który zawiera mo- dajemy CSS-based tmpl (tt_news) i zapi- Zachęcamy Cię do poznawania TY-
dyfikacje bazy danych, pliki konfiguracyjne i sujemy przeprowadzone zmiany. PO3 krok po kroku: zacznij od urucho-
samą aplikację (jej logikę biznesową). Możemy teraz umieścić wiadomości mienia małej witryny, naucz się Typo-
na naszej witrynie: klikamy więc Page –> Script, wypróbuj TemplaVoila i zainstaluj
Dodajemy i uruchamiamy Test –> Create new record i wybieramy rozszerzenia, których potrzebujesz. Dzięki
system newsów typ rekordu newsów korzystając z formu- własnej praktyce szybko staniesz się mi-
Zainstalujmy system newsów z repozy- larza, który się pojawi po prawej. Następ- strzem obsługi TYPO3. n
torium TYPO3 (TER). Będziemy do tego nie wpisujemy tytuł, odhaczamy checkbox
potrzebowali połączenia z Internetem. Po- hide i wpisujemy treść wiadomości w polu
dobnie, jak miało to miejsce przy instala- Text (jeżeli checkbox hide jest niewidocz-
cji tt_board, musimy zacząć od kliknięcia ny (nie ma go pod polem tytułu), to prze- O autorze
opcji Ext Manager w menu po lewej stro- wijamy stronę do samego końca i zazna-
nie ekranu (sekcja Tools). W ramce, która czamy box Show secondary options). Na- Jean-Gael Rouchon jest dyrektorem
pojawi się po prawej stronie ekranu, wybie- stępnie zapisujemy naszą wiadomość kli- badań i rozwoju (R&D director) we
rzemy Menu: Import extensions. Po przeła- kając ikonę dyskietki z lupą (Save docu- francuskiej firmie ONEXT, która spe-
dowaniu strony, wpisujemy tt_news w polu ment and view page) – gotowe. System cjalizuje się w opensourcowych syste-
Lookup i klikamy przycisk Lookup. Powin- newsów w środowisku produkcyjnym (np. mach CMS tworzonych w PHP. Pracu-
na się pojawić pozycja News – kliknijmy na na portalu korporacyjnym) powinien mieć je nad TYPO3 od 2002 i współadmini-
czerwoną strzałkę z lewej strony tej nazwy. osobną stronę do wyświetlania wszystkich struje portalem typo3.fr
Od tej chwili musimy postępować zgodnie wiadomości, archiwum, itd. Moduł new- Kontakt:
z instrukcjami programu instalacyjnego, sów w TYPO3 pozwala na te operacje, jean-gael.typo3@rouchon.org
który zapyta o to, czy utworzyć tabelę ba- możemy też zmieniać sposób prezenta-

PHP Solutions Nr 5/2006 www.phpsolmag.org 37


Kasa dla
Webmastera

Freelancing – zostań
wolnym strzelcem
Stopień trudności: lll
Krzysztof Trynkiewicz

Jesteś programistą i chcesz wziąć udział w cie-


kawym projekcie i zarobić trochę pieniędzy?
A może potrzebujesz kogoś, kto wykona dla
Ciebie witrynę internetową, aplikację dla księgo-
wości czy grafikę? Dzięki serwisom freelancin-
gowym każdy z Was znajdzie to, czego potrze-
buje przy minimalnym lub żadnym ryzyku.

N
ajogólniej mówiąc, freelancing Wszystkie oferty są przydzielone do za-
to rynek zleceń. Choć istniał już kresów cenowych, począwszy od drob-
wcześniej, do jego rozwoju w naj- nych zadań za dziesięć dolarów, do du-
większym stopniu przyczyniły się wyspe- żych zleceń wykonania całych portali za
cjalizowane serwisy freelancingowe, w kwoty sięgające tysięcy dolarów. Mno-
których jedni zgłaszają swoje pomysły, gość i różnorodność ofert powoduje, że
a inni je wykonują. Nad przebiegiem re- nawet mgliste pojęcie o jakimś zagadnie-
alizacji projektu i przekazania wynagro- niu wystarczy, by podołać niektórym za-
dzenia czuwają administratorzy tych wi- daniom. Przykładowo, jeśli nasza wiedza
tryn, którzy są tym samym pośrednikami na temat PHP wystarcza do zrozumienia
w kontakcie zleceniodawców z wykonaw- cudzego kodu, możemy się śmiało podjąć
cami (zleceniobiorcami). zaktualizowania już zainstalowanej apli-
kacji (np. osCommerce czy XOOPS) lub
Różnorodność ofert
W SIECI Freelancing to nie tylko programowanie
w PHP lub innych językach. Przegląda- Co należy wiedzieć...
jąc witryny pośredników natkniemy się Przydatna będzie podstawowa znajo-
• http://allfreelance.com - mość terminologii biznesowej.
– kompendium linków na multum dziedzin, w których możemy
freelancingowych składać oferty oraz podejmować zada-
• http://guru.com
• http://rentacoder.com nia. Znajdziemy więc zlecenia na pisanie Co obiecujemy...
• http://getafreelancer.com programów, artykułów, tłumaczenie tek- Pokażemy, jak działa freelancing na
• http://zlecenia.przez.net
stów, dostosowywanie skryptów do po- przykładzie najpopularniejszych ser-
– polskojęzyczny serwis wisów, które go organizują.
freelancingowy trzeb klienta, tworzenie grafiki i reklam, a
nawet rozwiązywanie zadań domowych.

38 www.phpsolmag.org PHP Solutions Nr 5/2006


Freelancing

przetłumaczyć na język obcy tekst zapi-


sany w kodzie (ang. hard-coded text), np.
komentarze czy łańcuchy wyświetlane na ����������� �����������
������������� ���������
stronie WWW.

Jak to działa ������������������� ����������������������


Większość serwisów freelancingowych ���������������������� �������������������

oprócz spisu ofert udostępnia system pro- ��������������


wadzący krok po kroku przez cały pro- ���������

ces realizacji projektu, którego stadia są


w większości serwisów podobne. Załóż- �������������������� ����������������
my, że chcemy zlecić komuś wykona- ��������������������� ��������� ����������������
�������������
nie panelu administracyjnego naszej wi-
tryny internetowej. Określamy więc na-
sze wymagania (opcjonalnie także osta- ������������������
������������������
teczny termin zakończenia pracy, tzw. de-
������������ ����������������
adline) i umieszczamy zlecenie na wybra- ����������������� ���������������������
���������������
nym serwisie freelancingowym. Na naszą �����������������
ofertę odpowiadają potencjalni wykonaw-
cy, często proponując konkretną cenę lub
zakres cenowy, dodając własne pomysły i
określając czas wykonania zadania. Jeśli ������������������
któryś z nich spełni nasze wymagania, to ������������

akceptujemy jego propozycję i wpłacamy


ustaloną sumę na konto pośrednika (ang.
put to escrow). Mając takie zabezpiecze- Rysunek 1. Kolejne stadia współpracy pomiędzy wykonawcą a zleceniobiorcą
nie, wykonawca może rozpocząć swoją (proponowane przez pośredników)
pracę. Postępy są odnotowywane u po-
średnika – programista musi zdawać co- nie i współpraca są korzystne dla obu wprowadzony np. w Rent a Coder depo-
tygodniowe relacje poparte odpowiedni- stron: zleceniodawca może przedsta- zyt bezpieczeństwa zleceniodawcy (ang.
mi plikami. W razie niewywiązywania się wić swój projekt tylko nam (aukcja typu Seller Guarantee Deposit).
z umowy, możemy się zwrócić o mediację One on One), dzięki czemu zaoszczę- Będąc wykonawcą projektów, mo-
do pośrednika (może o nią poprosić rów- dzi, gdyż w takiej sytuacji opłata pobie- żemy zdeponować pewien ustalony pro-
nież wykonawca) – obie strony są wtedy rana przez pośrednika jest często niż- cent wartości całego zlecenia na kon-
zobowiązane do zaakceptowania decy- sza: w przypadku Rent a Coder (http:// to pośrednika. Jeżeli nie wywiążemy się
zji arbitra, który rozsądzi racje stron. Je- www.rentacoder.com) różnica wynosi aż z umowy, nasze pieniądze zostaną prze-
śli projekt zostanie zakończony pomyśl- 2,5% ceny całego zlecenia. znaczone na pokrycie kosztów manipula-
nie, pośrednik przekaże wynagrodzenie cyjnych zleceniodawcy oraz na cele cha-
wykonawcy (możemy mu też, jako zleca- Jak się wybić z tłumu rytatywne (lecz nie samemu zlecenio-
jący, przekazywać części kwoty w trakcie Często zleceniodawcy trudno ocenić, czy dawcy, by uniemożliwić wyłudzanie pie-
wypełniania kolejnych postanowień umo- osoba stawiająca korzystną propozycję niędzy). Jeśli natomiast wszystko pójdzie
wy). Do naszych powinności należało bę- cenową tylko sprawdza grunt, czy też jest zgodnie z planem, zostanie nam zwróco-
dzie również wystawienie opinii o wyko- to prawdziwa okazja. Jeśli zaakceptujemy ne około 98% zdeponowanej kwoty. De-
nawcy, z którą będziemy się mogli zapo- taką ofertę, zaś później okaże się, że wy- pozyt stanowi więc zabezpieczenie dla
znać my oraz przyszli klienci. Na Rysun- konawca nie wywiązał się z umowy, mo- zleceniodawcy i pozwala nam podnieść
ku 1 przedstawiamy schemat kolejnych żemy stracić dużo więcej, niż tylko czas zaufanie do naszych ofert.
etapów współpracy. (pieniądze z depozytu zostaną nam zwró-
cone), przykładowo gdy nie będziemy Chrońmy swój pomysł
Zadowolenie przede mogli się terminowo wywiązać z naszych Często miewamy pomysły, których sa-
wszystkim zobowiązań wobec innych osób lub firm. mi nie potrafimy zrealizować z przyczyn
Wykonując swoją pracę sumiennie, bę- Profesjonalista może też mieć pro- technicznych. Taką ideę (a czasem goto-
dziemy stopniowo zyskiwali renomę i blem, aby przekonać zleceniodawców wy do wdrożenia projekt) możemy sprze-
zaufanie zleceniodawców. Zadowolo- do relatywnie wysokiej, choć adekwat- dać komuś, kto zrobi z niej użytek. Pa-
ny klient chętniej skorzysta z naszych nej do jakości świadczonych usług ceny, miętajmy jednak, że nie każdy nabywca
usług ponownie – może wystawić tzw. jeśli musi konkurować z atrakcyjnymi ce- jest uczciwy i może po zapoznaniu się z
aukcję prywatną, do której zaprosi jedy- nowo ofertami początkujących freelance- naszymi pomysłami uniknąć zapłaty lub
nie wybrane przez siebie osoby. Zaufa- rów. Rozwiązaniem tego problemu jest odsprzedać je komuś innemu. Aby unik-

PHP Solutions Nr 5/2006 www.phpsolmag.org 39


Kasa dla Webmastera

nąć takich sytuacji, czyli uniemożliwić po- wątpliwości. Jeżeli jednak wciąż pojawia- nień. Niewątpliwą zaletą takiego rozwią-
dejrzenie szczegółów projektu przez nie- ją się niejasności, możemy poprosić o ra- zania jest mniejsza konkurencja w przy-
uczciwych wykonawców, niektóre serwi- dę facilitatora – przedstawiciela specjal- padku składania przez nas ofert. Jeżeli
sy freelancingowe wprowadziły tzw. Umo- nego oddziału serwisu freelancingowego, jednak to my szukamy wykonawcy, war-
wę o nieujawnianiu informacji (ang. Non- przedstawiając naszą sytuację i podając to wziąć pod uwagę różne, nie tylko naj-
disclosure Agreement). Jeśli dajemy zle- link do naszej aukcji. większe serwisy – liczy się bowiem także
cenie w trybie zgodnym z tą umową, wy- prowizja, jaką będziemy musieli zapłacić
konawcy będą widzieć jedynie podstawo- Wybór serwisów pośrednikowi. Z pewnością mniejsza po-
we informacje o naszym projekcie – jego pośredniczących pularność nie oznacza gorszych wyko-
typ, zakres cenowy i wprowadzone przez W sieci znajdziemy wiele ofert pośredni- nawców, więc jeśli nie jesteśmy zbyt wy-
nas publicznie dostępne wytyczne. By uj- ków. Ważne jest, by wybrać serwis z re- bredni, możemy podjąć niewielkie ryzyko
rzeć szczegóły, wykonawca musi wyrazić nomą, działający legalnie i obsługują- i spróbować zaoszczędzić.
chęć ich poznania przez podpisanie po- cy opisane w artykule zabezpieczenia
wyższej umowy, my zaś możemy udo- przed nieuczciwością i niesolidnością. Freelancing w Polsce
stępnić mu tajne dane lub nie. Rent a Coder (http://rentacoder.com) ofe- Niestety, w chwili pisania artykułu freelan-
ruje wszystkie wspomniane udogodnienia cing w naszym kraju był słabo rozwinię-
Właściwy wybór i z pewnością ma wielką renomę. W chwi- ty. W zasadzie tylko jeden polski serwis
wykonawcy li pisania artykułu posiadał 2 tys. otwar- freelancingowy, http://zlecenia.przez.net,
Będąc zleceniodawcą, powinniśmy się tych projektów oczekujących na oferty, był wart uwagi i to wyłącznie ze wzglę-
dobrze zastanowić przed wyborem wyko- 150 tys. zarejestrowanych wykonawców du na ilość aukcji, gdyż niestety nie ofe-
nawcy naszego projektu. Naszej decyzji i 60 tys. zleceniodawców. Prym we fre- ruje on wielu opisanych przez nas zabez-
nie powinniśmy podejmować wyłącznie w elancingu wiedzie jednak inny serwis – pieczeń i udogodnień. Miejmy nadzieję,
oparciu o cenę, zwłaszcza w informatyce, Guru.com (http://www.guru.com/). Liczba że w najbliższym czasie sytuacja ta ule-
gdzie przykładowo koszty stron WWW zarejestrowanych tam wykonawców jest gnie zmianie.
oscylują między 50zł a tysiącami dola- dziesięciokrotnie większa niż w przypad-
rów. Dokonując wyboru najlepiej zwró- ku Rent a Coder (568 tysięcy). Co cieka- Podsumowanie
cić uwagę na to, jak wiele zadań wykonał we, zleceniodawców jest dwukrotnie mniej Jako programistom, freelancing daje nam
nasz potencjalny zleceniobiorca oraz ja- niż w poprzednim serwisie (30 tysięcy), ogromne możliwości zarobienia pieniędzy,
kie opinie wyrażali o nim poprzedni zlece- natomiast ilość otwartych projektów jest nawiązania kontaktów biznesowych, wyro-
niodawcy i jakie oceny mu wystawili. Po- kilkukrotnie większa. Serwis ten oferuje bienia sobie renomy i doskonalenia swoich
winniśmy też zajrzeć do portfolio i resume również kluczowe udogodnienia, jednak umiejętności. Jeśli natomiast potrzebujemy
wykonawcy, gdzie dowiemy się o umiejęt- w chwili pisania artykułu nie funkcjono- wykonawców naszego projektu, to dzię-
nościach i doświadczeniu nabytym przez wał omawiany w artykule depozyt bezpie- ki rozbudowanym serwisom freelancingo-
niego poza serwisem freelancingowym. czeństwa zleceniodawcy (Seller Guaran- wym mamy dużą szansę znaleźć najbar-
Warto też zwrócić uwagę na języki, któ- tee Deposit). Możemy się spodziewać, że dziej odpowiednie osoby. W obu przypad-
rymi posługuje się nasz potencjalny zle- zabezpieczenie to zostanie bardzo szybko kach, warto się dobrze zapoznać z różny-
ceniobiorca – jeśli wykonanie naszego wprowadzone. Z kolei w serwisie Elance mi ofertami oraz opiniami o zamawiających
zadania będzie wymagało częstych kon- (http://elance.com) znajdziemy ponad 100 czy wykonawcach. Istotne jest również po-
sultacji, na pewno łatwiej będzie dojść do tysięcy projektów i obszerne informacje i ważne podejście do spraw formalnych –
porozumienia w języku ojczystym. Jeżeli porady na temat zamawiania i świadcze- zastosowanie się do opisanych przez nas
obowiązuje nas deadline, najważniejszą nia usług (w tym wzory umów, m.in. Non- wskazówek pozwoli zmniejszyć ryzyko
wytyczną będzie doświadczenie. Warto disclosure agreement). Listy serwisów fre- wystąpienia niemiłych niespodzianek czy
też zamieścić informację, że do jednego elancingowych znajdziemy pod adresami wręcz natknięcia się na oszusta. Życzymy
projektu możemy przydzielić kilku wyko- http://www.phpkitchen.com/index.php?/ udanych interesów! n
nawców. archives/670-PHP-Freelance.html (uwaga
Nie powinniśmy też zatrudniać wyko- – są tu również typowe serwisy do wyszu-
nawcy, który robił dotychczas projekty za kiwania ofert pracy, np. monster.com czy
stawki wielokrotnie niższe niż oferowane mojolin.com) oraz http://allfreelance.com
przez nas, gdyż ich waga i złożoność mu- (All Freelance). Pamiętajmy jednak, że O autorze
siała być znacznie mniejsza. Jeśli jednak oprócz dużych i często wykorzystywa-
mamy więcej czasu, niż pieniędzy, to mo- nych serwisów, znajdują się tam również Krzysztof Trynkiewicz od wielu lat zaj-
muje się tworzeniem witryn w PHP i
żemy rozważyć zatrudnienie początkują- takie, które niedawno zaczęły swoją dzia-
Flash. Współpracuje z magazynem PHP
cego freelancera – ostatecznie i tak po- łalność lub nie są zbyt popularne. Solutions, wykonuje także zlecenia fre-
siadamy zabezpieczenie naszych finan- Jeśli szukamy zleceń do wykona- elancingowe. Obecnie rozwija kilka rów-
sów w postaci escrow. Serwisy pośredni- nia, możemy zajrzeć również na ta- noległych projektów autorskich dostęp-
nych na witrynie http://eldoras.com.
czące oferują często możliwość komuni- kie serwisy, jak Get A Freelancer (http://
kacji z wykonawcami jeszcze przed za- getafreelancer.com/), który posiada bazę Kontakt z autorem:
akceptowaniem ich ofert w celu określe- kilku tysięcy otwartych projektów i może chris.trynkiewicz@gmail.com
nia szczegółów i wyjaśnienia wszelkich być przydatny mimo braku wielu udogod-

40 www.phpsolmag.org PHP Solutions Nr 5/2006


Dla zaawansowanych

Zend API – tworzymy


własne rozszerzenie dla
PHP
Stopień trudności: lll
Marcin Staniszczak

Twój skrypt działa zbyt wolno? Wydaje Ci się,


że przyczyna tkwi w wydajności PHP?
A może chcesz połączyć się z inna aplikacją
lub wykorzystać swoją ulubiona bibliotekę z C?
Rozwiązaniem Twoich problemów może okazać
się Zend API.

Z
end API jest interfejsem progra- CodeGen_PECL
misty, pozwalającym pisać roz- – przygotowujemy się
szerzenia dla PHP w języku C. do pracy
Do pisania bardzo prostych rozszerzeń Stworzenie rozszerzenia całkowicie samo-
W SIECI wystarczy podstawowa znajomość tego dzielnie jest zadaniem bardzo skompliko-
języka – jak zobaczycie dalej, dostęp- wanym i żmudnym. Szczególnie począt-
ne są narzędzia generujące cały szablon kowy etap implementacji jest dość złożo-
l http://www.php.net/manual/ takiego rozszerzenia za nas – jednak do ny. Na szczęście dziś w większości przy-
en/internals.php – informacje
na temat Zend API znajdują- pisania bardziej zaawansowanych pro- padków nie musimy się już o to martwić.
ce się w oficjalnej dokumen- gramów, potrzebna jest dobra znajomość Wystarczy na przykład skorzystać z Co-
tacji PHP – niestety dość
skromne C oraz zacięcie detektywistyczne, z po- deGen_PECL. Skrypt ten na podstawie
l http://somabo.de/talks/ wodu bardzo słabej dokumentacji niektó- pliku XML, w którym opisujemy tworzone
-- materiały z różnych kon-
rych elementów Zend API. Nie obejdzie- rozszerzenie, generuje dla nas wszystkie
ferencji dotyczących PHP
– znajdują się tu także dwie my się więc bez konieczności analizo-
dobre prezentacje porusza- wania źródeł rozszerzeń dostarczanych
jące problem rozszerzeń dla Co należy wiedzieć...
PHP standardowo z PHP. Potrzebna będzie podstawowa znajo-
l http://www.zend.com/php/ Na potrzeby tego artykułu wspólnie mość języka C i PHP.
internals/extension-writin-
g1.php – pierwsza część tu- stworzymy rozszerzenie wzbogacające
toriala na temat pisania roz- PHP o funkcję pozwalającą obliczać pro- Co obiecujemy...
szerzeń z wykorzystaniem Z artykułu dowiesz się, jak z wykorzy-
ste wyrażenia matematyczne – zawierają-
Zend API
l http://www.zend.com/php/ ce podstawowe operacje takie jak =, -, *, staniem Zend API zbudować przykła-
internals/extension-writin- dowe rozszerzenie dla PHP pozwala-
czy /. Będzie można także do woli korzy-
g2.php – druga część po- jące na wykonywanie prostych opera-
wyższego tutoriala stać z nawiasów okrągłych ( ), a także cji matematycznych.
kwadratowych [ ].

42 www.phpsolmag.org PHP Solutions Nr 5/2006


Zend API Dla zaawansowanych

niezbędne pliki oraz szablon rozszerze-


nia (zalążek pliku źródłowego w C, któ- Szybki start
Już teraz możecie zapoznać się z efektem działania prezentowanego tu rozszerzenia.
ry następnie rozbudowujemy o planowa-
Pamiętajmy jednak, że do jego kompilacji będziemy potrzebować systemu uniksowego
ną funkcjonalność). Pozwala to zaoszczę- (np. Linux czy FreeBSD). Teoretycznie w źródłach znajdują się pliki projektu dla Visual
dzić sporo czasu oraz nerwów, które stra- Studio, generowane automatycznie przez skrypt CodeGen_PECL, jednak autor nie spraw-
cilibyśmy starając się samodzielnie wyko- dzał, czy rozszerzenie uda się skompilować pod systemem Windows.
Aby przeprowadzić kompilacje, musimy posiadać PHP z zainstalowanymi plika-
nać całą pracę.
mi nagłówkowymi (pliki z rozszerzeniem .h wykorzystywane w programach pisanych
Zanim rozpoczniemy opisywanie w języku C), a także kompilator C z narzędziami (gcc, make i itp.).
rozszerzenia za pomocą XML-a, musi- W celu skompilowania rozszerzenia na swoim systemie przegrywamy jego źródła do
my najpierw zainstalować skrypt Code- siebie na dysk, przechodzimy do katalogu z rozszerzeniem, a następnie wydajemy pole-
cenie:
Gen_PECL. Można to zrobić na kilka spo-
sobów. Jeśli posiadacie dostęp do Sieci, phpize; ./configure; make; sudo make install
najlepiej wydać z poziomu terminala po-
lecenie: Jeśli podczas wykonywania make install otrzymamy informacje o błędzie podczas pró-
by wykonania polecenia (brak uprawnień, czyli odpowiedniego wpisu w pliku sudoers), za-
logujcie się do systemu jako root i po przejściu do katalogu ze źródłami rozszerzenia po-
pear install –o Codegen_PECL nownie wydajcie polecenie make install.
Po skompilowaniu i zainstalowaniu rozszerzenia, musimy jeszcze odpowiednio skonfi-
Spowoduje to pobranie oraz zainstalowa- gurować PHP dodając w pliku php.ini następującą linijkę:
nie skryptu. Jeśli nie mamy aktualnie do- extension=RPN.so
stępu do Internetu, na CD dołączonym
do magazynu znajduje się archiwum ze Upewnijcie się także, że dyrektywa extension_dir w php.ini jest odpowiednio ustawiona
skryptem w wersji 1.0.0. Możemy je zain- i wskazuje na katalog, do którego make install skopiowało rozszerzenie. U mnie na
przykład wygląda ona tak:
stalować przechodząc do katalogu z tym
archiwum oraz wydać następujące pole- extension_dir = "/usr/local/php5/lib/php/extensions/"
cenia:
Teraz wystarczy restart serwera HTTP i wszystko powinno działać.

pear install CodeGen-1.0.0.tgz Odwrotna Notacja Polska


pear install CodeGen-PECL-1.0.0.tgz Odwrotna Notacja Polska jest specyficznym sposobem zapisu wyrażeń matematycznych.
W ONP znak wykonywanej operacji umieszcza się po operandach, dzięki czemu eliminuje
się całkowicie konieczność stosowania nawiasów. Na przykład wyrażenie:
Spowoduje to zainstalowanie odpowied-
nio skryptu CodeGen wymaganego do (15 – 5) / 2
poprawnego działania przez CodeGen_
PECL oraz samego CodeGen_PECL. zapisuje się jako:

15 5 – 2 /
Opisujemy rozszerzenie za po-
mocą XML-a Więcej na temat ONP oraz algorytmów przekształcenia wyrażenia z zapisu in-
fiksowego (tradycyjnego) do ONP oraz algorytmów pozwalających na obliczenie
Gdy mamy już zainstalowany skrypt
tak przekształconego wyrażenia znaleźć można na przykład w Wikipedii: http://
CodeGen_PECL, możemy przystąpić do two- pl.wikipedia.org/wiki/Odwrotna_notacja_polska
rzenia pliku XML z opisem naszego roz-
szerzenia.
Cały opis rozszerzenia umieszcza się Skrypt CodeGen_PECL na podstawie in- <description>. Autorzy skryptu Code-
w znaczniku <extension>. Przyjmuje on formacji z pliku XML potrafi wygenero- Gen_PECL zalecają umieszczanie obu
jeden parametr będący nazwą tworzone- wać tabelkę z informacjami, która ukaże tych znaczników zaraz pod otwierają-
go rozszerzenia. Nasze rozszerzenie na- się na stronie zwracanej przez phpinfo(). cym znacznikiem <extension>.
zwiemy RPN – od Reverse Polish Nota- Nazwę rozszerzenia skrypt pobiera z pa- Za umieszczenie informacji o au-
tion – nazwy sposobu, w jaki będzie liczy- rametru name znacznika <extension>. Je- torach rozszerzenia odpowiada znacz-
ło wyrażenie (będzie ono zamieniane do śli chcemy w phpinfo() umieścić krótką nik <maintainers>, zawierający tag
postaci Odwrotnej Notacji Polskiej, a na- informację o rozszerzeniu (podsumowa- <maintainer> opisujący każdego z auto-
stępnie obliczane – więcej na temat ONP nie), należy ją umieścić wewnątrz znacz- rów (Listing 1). Na taki opis składa się je-
w ramce Odwrotna Notacja Polska). Opis nika <summary>: go nazwa (może to być np. nick) umiesz-
rozszerzenia umieszczamy zatem we- czona w znaczniku <user>, imię i nazwi-
wnątrz następującego bloku: <summary> sko umieszczone w znaczniku <name>,
Przykładowe rozszerzenie PHP - RPN e-mail umieszczony w znaczniku <email>
<extension name=”RPN”> </summary> oraz rola jaką on odgrywa w zespole ludzi
<!—tutaj będzie umieszczony pracujących nad rozszerzeniem umiesz-
opis rozszerzenia--> Główną informację o rozszerzeniu na- czona w znaczniku <role>. Znacznik
</extension> leży analogicznie umieścić w bloku <maintainers> może oczywiście zawierać

PHP Solutions Nr 5/2006 www.phpsolmag.org 43


Dla zaawansowanych Zend API

CodeGen_PECL
Skrypt CodeGen_PECL można zna-
leźć w repozytorium PEAR. Poprzed-
nio nosił on nazwę PECL_Gen i był
częścią repozytorium PECL. Skrypt
ten w znacznym stopniu ułatwia two-
rzenie rozszerzeń dla języka PHP, ge-
nerując na podstawie XML-a komplet-
ne szablony rozszerzeń. Dokumen-
tacja CodeGen_PECL znajduje się
pod adresem http://php-baustelle.de/
CodeGen_PECL/manual.htm, same
rozszerzenie zaś można pobrać albo
za pomocą programu pear wchodzą-
cego w skład PHP albo ze strony http:
//pear.php.net/package/CodeGen_
PECL/download.

wiele bloków <maintainer> z opisami po-


szczególnych członków zespołu.
Dodajmy teraz informację o wer-
Rysunek 1. Przykładowa informacja o rozszerzeniu, wygenerowana na podstawie sji naszego rozszerzenia. Służy do te-
danych zawartych w pliku XML go blok <changelog> zawierający znacz-
niki <release> opisujące poszczególne
Listing 1. Opis członków zespołu pracującego nad rozszerzeniem wersje. Każde wydanie może zostać opi-
sane za pomocą numeru umieszczone-
<maintainers> go w znaczniku <version>, daty wydania
<maintainer> umieszczonej w znaczniku <date>, statu-
<user>Staniszczak</user>
su wydania (np. beta, stable itd.) w znacz-
<name>Marcin Staniszczak</name>
<email>marcin@staniszczak.pl</email> niku <state> oraz opisu wydania umiesz-
<role>lead</role> czonego w znaczniku <notes>. W bloku
</maintainer> <changelog> można umieścić opis wielu
wersji. Na Listingu 2 znajduje się opis wy-
<!—możesz tutaj umieścić więcej bloków maintainer -->
dania naszego rozszerzenia.
</maintainers>
W phpinfo() można jeszcze umie-
Listing 2. Opis wydania rozszerzenia – informacje tu zawarte są wykorzystywane ścić obrazek, jednak zdarzają się czasa-
podczas wyświetlania numeru wersji w phpinfo() mi problemy z jego poprawnym wyświe-
tlaniem. Jeśli chcemy spróbować, należy
<changelog>
<release>
skorzystać ze znacznika <logo>. Znacznik
<version>0.0.1</version> ten przyjmuje dwa parametry – src będą-
<date>2006-03-20</date> cy nazwą pliku z obrazkiem oraz mimetype
<state>beta</state> określający typ MIME dla obrazka (para-
<notes>Pierwsza beta wersja</notes>
metr ten można zignorować w przypad-
</release>
ku obrazków w formacie gif, png lub jpeg
<!-- możesz tutaj umieścić opis także innych wydań, każde opisując tak jak to – skrypt CodeGen_PECL wówczas sam
powyżej --> potrafi rozpoznać typ obrazka):
</changelog>
<logo src="calc.gif"
Listing 3. Opis funkcji tworzonego rozszerzenia
mimetype="image/gif"/>
<function name="rpn_calculation">
<proto>float rpn_calculation(string phrase)</proto> Ciekawym znacznikiem jest <license>,
</function> powodujący dodanie informacji licencyjnej
<function name="rpn_errormsg">
do plików źródłowych rozszerzenia oraz
<proto>string rpn_errormsg()</proto>
</function> wygenerowanie pliku LICENSE z treścią
<function name="rpn_error"> licencji. Znacznik w tej chwili może przyj-
<proto>bool rpn_errormsg()</proto> mować wartości PHP, BSD lub LGPL – in-
</function> ne traktuje jako wartość nieznaną, na pod-
stawie której nie potrafi wygenerować od-
powiednich danych.

44 www.phpsolmag.org PHP Solutions Nr 5/2006


Zend API Dla zaawansowanych

PHP Solutions Nr 5/2006 www.phpsolmag.org 45


Dla zaawansowanych Zend API

Na Rysunku 1 znajduję się informacja Tabela 1. Typy danych dozwolone w opisach prototypów funkcji w pliku XML
phpinfo() o naszym rozszerzeniu. Nazwa typu Opis
Skrypt CodeGen_PECL zawsze sam
bool Typ logiczny przyjmujący wartości true oraz false
dodaje ramkę z informacjami o zmiennych
wymaganych przez rozszerzenie, ustawia- int Typ numeryczny – całkowity
nych z poziomu pliku php.ini. float Typ numeryczny – zmiennoprzecinkowy
Przyszedł czas na to, aby zastano- string Ciąg znaków – napis
wić się, jakie funkcje będą potrzebne array Tablica
w naszym rozszerzeniu. Niech będą to
object Obiekt
rpn_calculation() obliczająca wartość
mixed Nieokreślony jednoznacznie typ danych
wyrażenia, rpn_errormsg() wyświetlają-
ca ewentualną informację o błędzie oraz callback Funkcja zwrotna
rpn_error zwracająca true/false w zależ- resource Zasoby
ności od tego, czy błąd wystąpił nie. Tylko stream Strumień danych
funkcja rpn_calculation będzie przyjmo-
wała parametr, wszystkie natomiast będą W opisach funkcji można bezpośred- leży korzystać z sekcji CDATA, aby parser
zwracały wartości. Opiszmy więc w XML-u nio umieszczać kod w języku C. Dla przy- XML ignorował błędy XML, jakie mogły-
funkcje tak, aby skrypt CodeGen_PECL kładu w pliku XML umieściłem kod dla by powodować znaki umieszczone w ko-
mógł dla nas wygenerować ich szablony specjalnych funkcji Zend API: RINIT oraz dzie. Ze znacznika <code> możemy ko-
do wypełnienia kodem. Posłużymy się w RSHUTDOWN. Funkcje te są wywoływane au- rzystać także w funkcjach deklarowanych
tym celu znacznikiem <function>, przyj- tomatycznie odpowiednio przed oraz po przez nas samych (np. w naszych funk-
mującym parametr name, będący nazwą wykonaniu skryptu PHP lub w przypad- cjach rpn_calculation, rpn_errormsg i
tworzonej funkcji. Sama nazwa to jed- ku skryptów CGI / CLI zaraz po wystarto- rpn_error). Jest to wygodne w przypad-
nak za mało. Musimy jeszcze zdeklaro- waniu PHP (dokładniejszy opis wszystkich ku kodu tak prostego jak na Listingu 4,
wać prototyp funkcji, zawierający dodatko- funkcji systemowych znajduje się w Tabe- jednak gdy kod staje się bardziej złożony
wo informację o typie zwracanych danych li 2). Jeśli chcemy zdefiniować ciało funk- (taki jak np. kod funkcji rpn_calculation)
oraz nazwie i typie przyjmowanych para- cji specjalnej, trzeba użyć parametru rolę wygodniej jest kazać skryptowi CodeGen_
metrów. Na Listingu 3 znajduje się przy- znacznika <function>, ustawiając go na PECL wygenerować jedynie szablon funk-
kład opisujący nasze trzy funkcje. wartość internal, nazwę funkcji zaś usta- cji, a cały kod napisać w ulubionym edyto-
Jak widzimy owe prototypy funk- wić na jedną z wartości z Tabeli 2. Na Li- rze lub środowisku programistycznym.
cji – umieszczone w znaczniku <proto> stingu 4 znajduje się przykładowa deklara- Na koniec pozostało zdeklarowanie
– przypominają deklaracje funkcji w C. cja ciał funkcji RINIT oraz RSHUTDOWN. zmiennych ustawianych z poziomu pli-
Różnią się jednak one nazwami typów. Zauważmy, że kod funkcji został ku php.ini. W naszym przypadku zmien-
Dozwolone nazwy typów zebrane zosta- umieszczony w znaczniku <code> oraz za- ne te będą określały rozmiar stosu oraz
ły w Tabeli 1. mknięty w sekcji CDATA języka XML – na- kolejki wykorzystywanych podczas prze-
kształcania wyrażenia matematyczne-
Listing 4. Deklaracja ciał funkcji bezpośrednio w pliku XML go do Odwrotnej Notacji Polskiej oraz do
późniejszego przeprowadzenia obliczeń.
<function role="internal" name="RINIT"> Zmienne te będą nosiły odpowiednio na-
<code> zwy rpn_stack_size oraz rpn_queue_size.
<![CDATA[
Zmienne ustawiane z poziomu php.ini de-
memset(errorMessage, 0, 255);
isError = 0; finiuje się w bloku <globals>, korzystając
ze znacznika <phpini>. Znacznik <phpini>
int s = RPN_G(rpn_stack_size); przyjmuje 5 parametrów:
stack_initialize(s);

• name – jest to nazwa zmiennej,


s = RPN_G(rpn_queue_size);
queue_initialize(s); • type – określa typ zmiennej (patrz Ta-
]]> bela 1),
</code> • value – określa wartość, jaką ma
</function> przyjąć zmienna, gdy nie została ona
określona w pliku php.ini (lub .htacces
<function role="internal" name="RSHUTDOWN">
<code> czy konfiguracji HTTP) – powinno tu
<![CDATA[ nadawać się jedynie wartości zmien-
stack_free(); nym numerycznym, wartości pozosta-
queue_free(); łych typów powinno się nadawać w
]]>
funkcji RINIT,
</code>
</function> • onupdate – nazwa metody, która ma
zostać wywołana przy zmianie warto-
ści – gdy nie podamy tego parametru,

46 www.phpsolmag.org PHP Solutions Nr 5/2006


Zend API Dla zaawansowanych

Na Listingu 5 możemy zapoznać się


Tabela 2. Wewnętrzne funkcje Zend API
z przykładową deklaracją dwóch zmien-
Nazwa funkcji Opis
nych modyfikowanych z poziomu pliku
MINIT Funkcja inicjalizująca rozszerzenie. Jest ona wywoływana raz, za- php.ini.
raz po wystartowaniu modułu PHP na serwerze. Opis umieszczony pomiędzy znacz-
MSHUTDOWN Funkcja sprzątająca po rozszerzeniu. Jest ona wywoływana raz, nikiem otwierającym a zamykającym
gdy moduł PHP kończy swoje działanie. Może ona nie być wywo- <phpini>, używany jest podczas genero-
łana gdy program/moduł PHP/serwer HTTP zakończy swoje dzia-
wania przez CodeGen_PECL dokumenta-
łanie niepoprawnie np. w wyniku błędu.
cji w formacie DocBook (tak jak i wiele in-
RINIT Funkcja ta jest wywoływana przed każdym uruchomieniem skryp-
nych informacji z pliku XML).
tu PHP lub zaraz po funkcji MINIT w przypadku skryptów CGI i
CLI. Mamy więc już gotowy plik XML.
Jeśli chciałbyś dowiedzieć się więcej o
RSHUTDOWN Funkcja ta jest wywoływana po wykonaniu skryptu PHP. Zosta-
nie ona wywołana nawet, gdy skrypt spowoduje wystąpienie błędu CodeGen_PECL, zajrzyj do Ramki Code-
krytycznego, może jednak nie zostać wywołana, gdy wystąpi błąd Gen_PECL.
na poziomie języka C. Gdy plik XML jest już gotowy, przyszła
MINFO Funkcja ta jest wywoływana przez phpinfo() podczas wyświetla- pora na wygenerowanie na jego podsta-
nia informacji o rozszerzeniu. To ona odpowiada za wyświetlanie wie ram naszego rozszerzenia. Przejdź-
ramki z informacjami. my więc do okna terminala i w katalogu,
w którym znajduje się plik XML, wydajmy
Tabela 3. Wartości, jakie może przyjmować parametr access znacznika <phpini> – czyli polecenie:
gdzie można zmieniać wartość danej zmiennej
Nazwa Opis pecl-gen nazwa_pliku.xml

System Wartość zmiennej może być ustawiona w pliku php.ini lub konfiguracji
serwera HTTP Jeśli wszystko się udało, skrypt powinien
utworzyć nowy katalog o nazwie takiej,
Perdir Wartość zmiennej może być ustawiona w pliku .htaccess
jak nasze rozszerzenie, a w nim wszyst-
User Wartość zmiennej może być zmieniona w kodzie PHP
kie niezbędne pliki. Czas więc zacząć pro-
All Wartość zmiennej może być zmieniona w jakimkolwiek z wymienionych gramowanie!
wyżej miejsc.
Piszemy program
użyta zostanie standardowa metoda części artykułu – niestety skrypt Co- Przyjrzyjmy się teraz zawartości wygene-
OnUpdateString. Możemy tu zastoso- deGen_PECL w tym już nie umie po- rowanego przez skrypt katalogu. Wśród
wać metodę o nazwie OnUpdateTYP, móc), wielu plików znajdują się tam dwa w tej
gdzie TYP to nazwa typu z Tabeli 1 • access – informacja o tym, gdzie użyt- chwili dla nas najistotniejsze – RPN.c oraz
(pisana z dużej litery). Można także kownik może ustawić wartość danej php_RPN.h. Otwórzmy teraz plik RPM.c,
podać nazwę własnej funkcji, jednak zmiennej – parametr ten może przy- bo nim będziemy się zajmowali, a na-
trzeba ją wówczas samemu zdefinio- jąć jedną z wartości zebranych w Ta- stępnie odszukajmy po nazwie funkcję
wać (więcej na ten temat w dalszej beli 3. rpn_calculation.

Funkcja
Listing 5. Deklaracja zmiennych o wartości modyfikowalnej z poziomu pliku php.ini
rpn_calculation znajduje się w dwóch
<globals> miejscach – najpierw w tablicy RPN_
<phpini name="rpn_stack_size" type="int" value="50" functions, a następnie już jako defini-
onupdate="OnUpdateLong" access="perdir"> cja funkcji (definicja nie przypomina Wam
Stack size used for calculations.
zapewne języka C – o tym jednak w dal-
</phpini>
szej części artykułu) – nie licząc komenta-
<phpini name="rpn_queue_size" type="int" value="150" rzy, które dodawał po drodze CodeGen_
onupdate="OnUpdateLong" access="perdir"> PECL. Jak można zauważyć, w tablicy
Queue size used for calculations. zdefiniowane są wszystkie trzy funkcje
</phpini>
naszego rozszerzenia – Listing 6. Ele-
</globals>
menty tablicy opisującej funkcje są ty-
Listing 6. Definicja funkcji oferowanych przez rozszerzenie pu function_entry, który jest strukturą o
trzech polach. Nas jednak nigdy nie bę-
function_entry RPN_functions[] = { dą one interesowały. Do zdefiniowania
PHP_FE(rpn_calculation , NULL)
funkcji będziemy używali skryptu Code-
PHP_FE(rpn_errormsg , NULL)
PHP_FE(rpn_error , NULL) Gen_PECL, tak jak to zostało już zapre-
{ NULL, NULL, NULL } zentowane albo makra – np. PHP_FE, tak
}; jak to robi CodeGen_PECL. Jako że cza-
sami prawdopodobnie będziemy musieli

PHP Solutions Nr 5/2006 www.phpsolmag.org 47


Dla zaawansowanych Zend API

samodzielnie dodać jakąś funkcję do roz- Tabela 4. Makra służące do definiowania funkcji
szerzenia, powinniśmy zapoznać się z Nazwa Makra Opis
makrami ułatwiającymi to zadanie. W Ta-
ZEND_FE(name, arg_types) Definicja funkcji o nazwie name. Parametr
beli 4 zostały zebrane wszystkie makra
arg_types powinien być zawsze ustawio-
wspomagające samodzielne definiowa- ny na NULL. Zdeklarowana w ten sposób
nie funkcji. Należy pamiętać, że ostatnim funkcja będzie widoczna w PHP pod na-
elementem tablicy z Listingu 6 musi być zwą name, natomiast odwoływała się bę-
{NULL, NULL, NULL}. dzie do funkcji C zif_name
Sama deklaracja funkcji jest dość ZEND_NAMED_FE(php_name, name, Definicja funkcji widocznej w PHP pod
nietypowa jak na język C. Do deklaracji arg_types) nazwą php_name natomiast odwołująca
wykorzystano makro PHP_FUNCTION ko- się do funkcji C o nazwie name. Makro
to powinno być używane wówczas, gdy
respondujące z makrem PHP_FE (gdy ko-
nie chcemy, aby makro ZEND_FE auto-
rzystasz z makr z serii ZEND, do deklara- matycznie nadawało prefix naszym funk-
cji funkcji należy użyć analogicznego do cjom. Tak jak poprzednio parametr arg_ty-
PHP_FUNCTION makra ZEND_FUNCTION). Jak pes powinien być ustawiony na NULL
widać na Listingu 7, nie zostało tu w ty- ZEND_FALIAS(name, alias, arg_types) Makro to definiuje alias o nazwie alias do
powy sposób określone jakie, ani nawet funkcji PHP o nazwie name. arg_types
ile parametrów przyjmuje funkcja. Na powinno być ustawione na wartość NULL.
szczęście istnieje makro ZEND_NUM_ARGS PHP_FE(name, arg_types) Stary odpowiednik makra ZEND_FE
zwracające liczbę parametrów przeka- PHP_NAMED_FE(runtime_name, name, Stary odpowiednik makra ZEND_NA-
zanych do funkcji. Można je wykorzy- arg_types) MED_FE
stać do sprawdzenia, czy przekazana ze
skryptu PHP liczba argumentów jest po- Pierwszym argumentem jest liczba pa- • b –określa, że zmienna na pozycji te-
prawna. Jeśli nie, należy wywołać makro rametrów przekazanych do funkcji pod- go znaku ma być typu boolean,
WRONG_PARAM_COUNT, które kończy działa- czas jej wywołania, drugi to łańcuch • r – określa, że zmienna na pozycji te-
nie funkcji, informacją o błędzie zbliżo- znaków opisujący typ parametrów ocze- go znaku ma wskazywać na zasób
ną do tej: kiwanych przez funkcję, dalej są to już (ang. resource) – zwraca wartość ty-
zmienne, w których mają zostać zapisa- pu zval*,
Warning: Wrong parameter count for ne parametry. Ważny jest tu drugi para- • a – określa, że zmienna na pozycji
rpn_calculation in metr, czyli łańcuch znaków opisujący tego znaku ma być tablicą – zwraca
/home/marcin/public_html/test.php parametry. Łańcuch ten może składać wartość typu zval*,
on line 3 się z następujących znaków: • o – określa, że zmienna na pozycji te-
go znaku ma być obiektem dowolnej
Kod wygenerowany przez Code- • l – określa, że zmienna na pozycji klasy – zwraca wartość typu zval*,
Gen_PECL do parsowania parame- tego znaku ma być typu long, • O – określa, że zmienna na pozycji te-
trów podanych podczas wywołania funk- • d – określa, że zmienna na pozycji go znaku ma być obiektem takiej sa-
cji z poziomu skryptu PHP używa funkcji tego znaku ma być typu double, mej klasy jak klasa kolejnego parame-
zend_parse_parameters. Jest to funkcja o • s – określa, że zmienna na pozy- tru na liście wywołania funkcji zend _
zmiennej liczbie parametrów. Jej nagłó- cji tego znaku ma być typu string parse _ parameters (parametr ten nie
wek wygląda następująco: – oprócz napisu, funkcja zend _ będzie używany do przechowywania
parse _ parameters zwraca także wartości kolejnego parametru przeka-
int zend_parse_parameters jego długość (do kolejnego parametru zanego do skryptu PHP – w tym celu
( int num_args TSRMLS_DC, na liście parametrów wywołania funk- użyty zostanie kolejny parametr z li-
char* type_spec, ... ) cji zend _ parse _ parameters), sty parametrów) – zwraca wartość ty-
pu zval*,
• z – parametr dowolnego typu – zwra-
Listing 7. Deklaracja funkcji rpn_calculation
ca wartość typu zval*,
PHP_FUNCTION(rpn_calculation) • | (pionowa kreska) – oznacza, że na-
{ stępne parametry są opcjonalne,
const char * phrase = NULL;
• / – oznacza, że wartość poprzednie-
int phrase_len = 0;
go parametru ma zostać skopiowana,
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/", a nie przekazana przez referencję,
&phrase, &phrase_len) == FAILURE) { • ! – oznacza, że poprzedni parametr
return; może przyjmować wartość NULL .
}
php_error(E_WARNING, "rpn_calculation: not yet implemented"); RETURN_FALSE;
Najwięcej wątpliwości może nasuwać
RETURN_DOUBLE(0.0); wartość O oraz s, gdyż korzystają one z
} dwóch parametrów. Skorzystanie z war-
tości s, jak widać na Listingu 7, powodu-

48 www.phpsolmag.org PHP Solutions Nr 5/2006


Zend API Dla zaawansowanych

Tabela 5. Makra zwracające wartość z funkcji – należy używać w tworzonych funkcjach, Często przydatne i ułatwiające wie-
zamiast typowych instrukcji return wartosc le zadań może okazać się wywołanie z
Makro Opis poziomu naszego rozszerzenia jednej z
funkcji PHP. Nie da się tego zrobić w spo-
RETURN_RESOURCE(resource) Zwraca wskaźnik do zasobów
sób naturalny, tak jak zrobilibyśmy to wy-
RETURN_BOOL(bool) Zwraca typ logiczny wołując funkcje napisane w C. Na szczę-
ście dostępna jest w Zend API funkcja
RETURN_NULL() Zwraca wartość NULL call_user_function. Oto jej nagłówek:

RETURN_LONG(long) Zwraca cyfrę całkowitą


int call_user_function (
RETURN_DOUBLE(double) Zwraca liczbę zmiennoprzecinkową HashTable* function_table,
zval** object_pp, zval* function_name,

RETURN_STRING(string, duplicate) Zwraca napis – drugi parametr określa, zval* retval_ptr, zend_uint param_count,
czy napis ma zstać zduplikowany zval* params[] TSRMLS_DC )

RETURN_STRINGL(string, length, dupli- Zwraca napis o długości określonej dru-


Parametry wyglądają dość zawile, na
cate) gim parametrem. Trzeci parametr określa,
czy napis ma być zduplikowany. Makro to szczęście wszystko jest prostsze, niż
jest szybsze od RETURN_STRING. może się teraz wydawać. Przyjrzyjmy
RETURN_EMPTY_STRING() Zwraca pusty napis się Listingowi 8. Jest to fragment funkcji
rpn_calculation, odpowiedzialny za za-
RETURN_FALSE Zwraca logiczny fałsz mianę wszystkich nawiasów kwadrato-
wych występujących w wyrażeniu mate-
RETURN_TRUE Zwraca logiczną prawdę matycznym, na nawiasy okrągłe. Jak wi-
dać, w wywołaniu funkcji, za pierwszy
je zwrócenie poza napisem (do zmiennej padku błędu, poza zwróceniem odpowied- parametr podaliśmy wywołanie makra
phrase) także jego długości (do zmiennej niej wartości, powoduje ona także wyświe- CG(function_table), które na podstawie
phrase_len). Gdyby funkcja oczekiwała po tlenie ostrzeżenia z informacją np. o nie- swojego parametru zwraca tabele funk-
napisie jeszcze jakiegoś parametru, zo- poprawnym typie parametrów użytkowni- cji. Drugi parametr otrzymał wartość NULL,
stałby on zwrócony do zmiennej umiesz- kowi skryptu). gdyż jest on istotny tylko wówczas, gdy
czonej na liście parametrów funkcji zend_ Tworząc w Zend API funkcje dostępne wywoływana funkcja jest metodą jakiegoś
parse_parameters zaraz za phrase_len. z poziomu PHP, należy pamiętać o tym, obiektu. Jako że my potrzebujemy funkcji
Podobnie działa znak O, z tą różnicą, że aby zwracać z niej wartości korzystając z str_replace, parametr ten ustawiamy na
drugi parametr wykorzystuje nie do zapi- odpowiednich makr. Makra te mają za za- wartość NULL. Kolejny parametr jest na-
sania długości, a do odczytu wymagane- danie odpowiednio skonwertować zwra- zwą zmiennej, do której ma zostać zapi-
go typu klasy. caną wartość tak, aby mogła ona być po- sany wynik działania wywoływanej funk-
Funkcja zend_parse_parameters po prawnie zinterpretowana przez Zend En- cji. W naszym przypadku jest to zmienna
zakończeniu swojego działania zwraca gine. Wszystkie makra, z których można phrase, więc ta sama, która zostaje pod-
wartość SUCCESS w przypadku powodze- korzystać do zwracania wartości z funkcji, dana działaniu funkcji. Ostatnie dwa argu-
nia lub FAILURE, gdy wystąpi błąd (w przy- zebrano w Tabeli 5. menty funkcji call_user_function to licz-
ba parametrów, jakie mają zostać przeka-
Listing 8. Deklaracja funkcji rpn_calculation zane do wywoływanej funkcji oraz tablica
zawierająca te parametry.
zval func; Prawdopodobnie zauważyliście już,
zval *params[3]; że większość parametrów oczekiwa-
nych przez funkcję call_user_function
MAKE_STD_ZVAL(params[0]);
MAKE_STD_ZVAL(params[1]); jest typu zval. Musimy więc je odpowied-
MAKE_STD_ZVAL(params[2]); nio utworzyć. Po zdeklarowaniu parame-
tru, musimy go zainicjować jeszcze przed
ZVAL_STRING(&func, "str_replace", 0); nadaniem mu jakiejkolwiek wartości. Słu-
ZVAL_STRING(params[0], "[", 0);
ży do tego makro MAKE_STD_ZVAL. Na-
ZVAL_STRING(params[1], "(", 0);
ZVAL_STRING(params[2], phrase, 0); stępnie po zainicjowaniu zmiennej, może-
call_user_function(CG(function_table), my nadać jej wartość korzystając z makr z
(zval**)NULL, &func, &phrase, 3, params TSRMLS_CC); serii ZVAL_NAZWATYPU. Makra te zostały ze-
brane i opisane dokładniej w Tabeli 6.
ZVAL_STRING(params[0], "]", 0);
Tak więc pierwsze wywołanie funkcji
ZVAL_STRING(params[1], ")", 0);
ZVAL_STRING(params[2], phrase, 0); str_replace, zamieniające nawias [ na (
call_user_function(CG(function_table), w PHP miałoby postać:
(zval**)NULL, &func, &phrase, 3, params TSRMLS_CC);
$phrase = str_replace(‘[‘,‘(‘, $phrase);

PHP Solutions Nr 5/2006 www.phpsolmag.org 49


Dla zaawansowanych Zend API

Tutaj natomiast potrzebowaliśmy na to 10


linijek. Zauważmy także, że przy drugim
����������������
wywołaniu funkcji str_replace (zamienia-
jącym nawias ] na )), nie trzeba już inicjo- ��������������������������������
wać zmiennych.
Typ zval jest wykorzystywany bardzo �����������������������������
intensywnie przez Zend API – tego wła- � �����������������������������������
� �������������������������
śnie typu są wszystkie zmienne, na któ- � ��������������������
rych operuje PHP. Do wygodnej pracy, � � ����������
� � ��������
potrzebne więc są także funkcje konwer-
� ������
tujące zawartość zval pomiędzy różny- � ��������������������������
mi typami obsługiwanymi przez PHP (np. � �������������������
� ���������������������
convert_to_boolean_ex(z)). Warto też � ����������������������
zapoznać się z Rysunkiem 2, na którym � ������
���������������
przedstawiona została budowa struktury
zval (gdy ją poznasz, możesz samodziel-
nie korzystać z jej pól).
��������������������� ���������������
Funkcje specjalne Zend API � ������������������� ���������������
�����������������
� �������������������
To, z czym zapoznaliście się do tej po- � ��������������������� ���������������
ry, pozwoli Wam już na pisanie nawet � ��������������� ����������������
�����������������
dość złożonych rozszerzeń. Przyjrzyj- ��
�����������������
my się teraz funkcjom specjalnym Zend �������������������
�������������������
API (przypomnijmy sobie, jak konfiguro- �������������������������
waliśmy za pomocą XML-a funkcje we- �������������������������
wnętrzne Zend API – Tabela 2). Tak jak
w przypadku funkcji oferowanych przez
rozszerzenie, tak i funkcje wewnętrzne Rysunek 2. Budowa struktury zval – w PHP wszystkie wartością zmiennych są typu zval
Zend API muszą zostać przed użyciem
zdeklarowane w odpowiedniej struktu-
Tabela 6. Makra przypisujące odpowiednią wartość zmiennej typu zval – pierwszy
rze. Przyjrzyjmy się więc Listingowi 9
parametr makra to zawsze nazwa zmiennej typu zval
oraz 10. Jak się prawdopodobnie już do-
Nazwa makra Opis
myślacie, struktura zend_module_entry,
służy do pisania tworzonego rozsze- ZVAL_RESOURCE(z, l) Przypisuje zmiennej zval wartość zasobu. Parametr l jest
wartością typu long
rzenia. Pierwsze jej cztery pola zawsze
zastępuje się makrem STANDARD_MODULE_ ZVAL_BOOL(z, b) Przypisuje zmiennej zval wartość logiczną – boolean.
Parametr b przyjmuje wartości 0/1.
HEADRE, kolejne pole jest nazwą rozsze-
ZVAL_FALSE(z) Jest odpowiednikiem wywołania makra ZVAL_BOOL(z, 0)
rzenia – w naszym przypadku jest to
RPN. Następnie podajemy nazwę tablicy
ZVAL_TRUE(z) Jest odpowiednikiem wywołania makra ZVAL_BOOL(z, 1)
opisującej funkcje oferowane przez roz-
szerzenie (u nas struktura ta nazywa się ZVAL_NULL(z) Przypisuje zmiennej zval wartość NULL
RPN_functions – Listing 6). Kolejne pięć
pól struktury, to deklaracje funkcji we- ZVAL_LONG(z, l) Przypisuje zmiennej zval wartość typu long, przekazaną
wnętrznych rozszerzenia – odpowied- w drugim parametrze (l).
nio MINIT, MSHUTDOWN, RINIT, RSHUTDOWN, ZVAL_DOUBLE(z, d) Przypisuje zmiennej zval wartość typu double, przekazaną
oraz MINFO. Jeśli któraś z tych funk- w drugim parametrze (d).
cji jest w naszym rozszerzeniu zbędna, ZVAL_STRING(z, s, dup) Przypisuje zmiennej zval wartość typu string (napis).
makro z serii PHP_ dodane podczas ge- Parametry: s – ciąg znaków char*, dup – jeśli ma wartość 1
nerowania pliku źródłowego w C przez zmienna ma zostać skopiowana, jeśli 0 ma zostać przeka-
zana przez referencję
skrypt CodeGen_PECL, zastępujemy
ZVAL_STRINGL(z, s, l, dup) Przypisuje zmiennej zval wartość typu string (napis). Pa-
wartością NULL. Wszystkie pozostałe po-
rametry: s – ciąg znaków char*, l – długość ciągu znaków,
la struktury zostają wypełnione poprzez dup – jeśli ma wartość 1 zmienna ma zostać skopiowana,
wywołanie makra STANDARD_MODULE_ jeśli 0 ma zostać przekazana przez referencję
PROPERTIES. ZVAL_EMPTY_STRING(z) Przypisuje zmiennej zval pusty ciąg znaków
Definicje funkcji wewnętrznych zo-
ZVAL_ZVAL(z, zv, dup, dtor) Kopiuje jedną zmienną zval do drugiej. Parametr dup okre-
stały wygenerowane przez Code- śla, czy wartość ma zostać skopiowana, czy przekazana
Gen_PECL. Tak jak w przypadku kodu przez referencję. Parametr dtor określa, czy zmienna źró-
funkcji oferowanych przez nasze roz- dłowa (zv) ma zostać zniszczona (1) po wykonaniu kopio-
wania, czy też nie (0).
szerzenie, tak i w przypadku funkcji we-

50 www.phpsolmag.org PHP Solutions Nr 5/2006


Zend API Dla zaawansowanych

wnętrznych Zend API, definicja funkcji Czasami zdarza się, że chcielibyśmy tości queue_size, musimy jej nazwę
realizowana jest z wykorzystaniem od- napisać własną funkcję wykonywaną auto- umieścić jako czwarty parametr makra
powiednich makr. Makra te noszą na- matycznie podczas zmiany wartości zmien- STD_PHP_INI_ENTRY. Jako że często po-
zwy PHP_nazwaFunkcji_FUNCTION, gdzie nej z php.ini. Jest to zadanie bardzo proste trzebna może być nam także długość
nazwa funkcji to odpowiednio MINIT, i sprowadza się do użycia odpowiedniego nowej wartości (np. gdy jest to napis),
MSHITDOWN, RINIT, RSHUTDOWN oraz makra podczas tworzenia funkcji: poniżej przedstawiamy definicję makra
MINFO. Funkcja MINFO została już praw- PHP_INI_MH:
dopodobnie wystarczająco dobrze wy- PHP_INI_MH(OnChangeQueueSize)
generowana przez skrypt, można jed- { #define PHP_INI_MH (name)
nak się jej przyjrzeć i w razie konieczno- zend_printf("Wartość RPM.rpn_queue_size int name(php_ini_entry *entry,
ści zmodyfikować wygenerowany kod. to %s<br>", new_value); char *new_value,
Nasze rozszerzenie wykorzystuje dodat- return SUCCESS; uint new_value_length,
kowo funkcje RINIT oraz RSHUTDOWN do } void *mh_arg1,
zainicjowania stosu oraz kolejki wyko- void *mh_arg2, void *mh_arg3)
rzystywanej podczas obliczeń (RINIT), a Jak widać, nowa wartość przekazywa-
następnie zwolnienia wykorzystywanych na jest w zmiennej new_value. Oczy- Poszczególne parametry funkcji stworzo-
przez te struktury zasobów (RSHUTDOWN). wiście, aby funkcja ta była automa- nej za pomocą tego makra, to odpowied-
Jeśli dobrze pamiętamy, ciało tych funk- tycznie wywoływana po zmianie war- nio – struktura opisująca zmienną, nowa
cji zostało określone jeszcze na pozio-
mie pliku XML. Listing 9. Struktura opisująca nasze rozszerzenie oraz funkcje wewnętrzne Zend
API
Zmienne php.ini
Do pełni szczęścia brakuje nam jesz- zend_module_entry RPN_module_entry = {
STANDARD_MODULE_HEADER,
cze umiejętności odczytywania wartości
"RPN",
zmiennych konfigurowalnych z poziomu RPN_functions,
pliku php.ini. Zmienne te są deklarowane PHP_MINIT(RPN),
z wykorzystaniem makr (Listing 11). PHP_MSHUTDOWN(RPN),
Makro STD_PHP_INI_ENTRY infor- PHP_RINIT(RPN),
PHP_RSHUTDOWN(RPN),
muje PHP, która globalna zmienna ma
PHP_MINFO(RPN),
być modyfikowana z poziomu pliku "0.0.1",
php.ini – zajrzyjmy do pliku nagłówko- STANDARD_MODULE_PROPERTIES
wego php_RPN.h, tam są zdeklarowane };
zmienne globalne. Parametry makra, to
Listing 10. Struktura zend_module_entry
odpowiednio: nazwa zmiennej, pod ja-
ką ma ona być modyfikowana z pozio- typedef struct _zend_module_entry zend_module_entry;
mu pliku php.ini; wartość zmiennej, jaka
ma zostać użyta, gdy nie zostanie ona struct _zend_module_entry {
unsigned short size;
nadana w pliku php.ini; sposób dostępu
unsigned int zend_api;
do zmiennej (możliwe wartości to PHP_ unsigned char zend_debug;
INI_SYSTEM, PHP_INI_USER, PHP_INI_ALL unsigned char zts;
oraz PHP_INI_PERDIR – Tabela 3). Ko- char *name;
lejny parametr jest nazwą metody, któ- zend_function_entry *functions;
int (*module_startup_func)(INIT_FUNC_ARGS);
ra ma zostać wywołana w momencie
int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
zmiany wartości zmiennej. Dostępne int (*request_startup_func)(INIT_FUNC_ARGS);
są standardowe funkcje obsługi zdarze- int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
nia aktualizacji. Posiadają one nazwy void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);
OnUpdateTyp, gdzie typ to odpowiednia char *version;
[pozostałe elementy struktury]
nazwa typu zmiennej – Long, String,
};
Bool, Double itd. Piąty parametr makra
to zmienna globalna, która ma otrzymać Listing 11. Zmienne konfigurowalne deklarowane z wykorzystaniem makr będą
wartość zmiennej z pliku php.ini. Osta- odczytywane z poziomu php.ini
nie dwa parametry są strukturami opi-
PHP_INI_BEGIN()
sującymi zmienne globalne naszej apli-
STD_PHP_INI_ENTRY("RPN.rpn_stack_size", "50", PHP_INI_PERDIR,
kacji – nie musimy się nimi przejmować, OnUpdateLong, rpn_stack_size, zend_RPN_globals, RPN_globals)
gdyż nawet jeśli chcemy ręcznie dodać STD_PHP_INI_ENTRY("RPN.rpn_queue_size", "150", PHP_INI_PERDIR,
zmienną modyfikowaną z poziomu pliku OnUpdateLong, rpn_queue_size, zend_RPN_globals, RPN_globals)
php.ini, ostatnie dwa parametry pozo- PHP_INI_END()

staną takie, jak wygenerował je skrypt


CodeGen_PECL.

PHP Solutions Nr 5/2006 www.phpsolmag.org 51


Dla zaawansowanych Zend API

wartość zmiennej, długość zmiennej oraz


trzy opcjonalne argumenty, które w tej
chwili nas nie interesują.
Zanim będziemy mogli odczy-
tać zmienne zdefiniowane wyżej, musi-
my jeszcze w funkcji MINIT wywołać ma-
kro ZEND_INIT_MODULE_GLOBALS. Makro to
przyjmuje trzy parametry – nazwę roz-
szerzenia (u nas jest to RPN), nazwę funk-
cji, która ma być wywołana podczas inicjo- Rysunek 3. Działanie przykładowej aplikacji korzystającej z utworzonego rozszerzenia
wania zmiennych, nazwę funkcji, która ma
być wywołana podczas niszczenia zmien- skrypt CodeGen_PECL, kompilacja i in- RPN.rpn_stack_size = wielkość_stosu
nych. stalacja rozszerzenie jest zaskakująco RPN.rpn_queue_size = wielkość_kolejki
Należy także pamiętać o tym, aby za- prosta. Sprowadza się do wydania w ka-
inicjować w wywołaniu w funkcji MINIT ma- talogu z rozszerzeniem następujących ko- Jeśli wszystko przebiegło pomyślnie, po-
kra REGISTER_INI_ENTRIES(), w funkcji mend: winniśmy móc uruchomić przykładową
MSHUTDOWN natomiast makra UNREGISTER_ aplikację PHP korzystającą z naszego
INI_ENTRIES(). phpize rozszerzenia (patrz Rysunek 3).
Teraz możemy się już odwoływać do ./configure
zmiennych globalnych, za pomocą makra make Na zakończenie
NAZWAROZSZERZENIA_G(nazwa_zmiennej), sudo make install Artykuł ten nie wyczerpuje tematu tworze-
czyli w naszym przypadku RPN_G(nazwa_ nia rozszerzeń dla PHP. Brak dobrej doku-
zmiennej) – spójrzmy na Listing 4, tam w Teraz wystarczy, że w pliku php.ini do- mentacji na pewno utrudnia życie. Z dru-
kodzie funkcji RINIT oraz RSHUDOWN odwo- damy linijkę nakazującą załadowanie giej strony duża liczba standardowych roz-
ływaliśmy się do zmiennych rpn_stack_ rozszerzenia (w przypadku rozszerze- szerzeń dostępnych ze źródłami PHP bar-
size oraz rpn_queue_size. nia przykładowego linijka ta ma postać: dzo dużo pomaga przy pisaniu własnych
extension=RPN.so) i restartujemy ser- rozszerzeń. Trzeba jednak być przygoto-
Kompilacja i wer HTTP. Możemy także odpowiednio wanym na konieczność analizowania cu-
uruchomienie zmodyfikować rozmiar stosu oraz kolej- dzego kodu np. parametrów przyjmowa-
Przetestujmy teraz nasze rozszerze- ki, dodając do pliku php.ini jeszcze dwie nych przez interesujące nas makro.
nie. Dzięki plikom wygenerowanym przez linijki: Jeśli zainteresował Was temat roz-
szerzeń i chciałbyście pogłębić swo-
Listing 12. Kod przykładowej aplikacji korzystającej z utworzonego rozszerzenia, ją wiedzę w tej dziedzinie, polecam
efekt jego działania wiadać na Rysunku 3 rozpocząć pracę od modyfikacji na-
szego rozszerzenia. Zostało ono ce-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/ lowo stworzone w sposób, który po-
xhtml1/DTD/xhtml1-strict.dtd">
zwoli Wam na dokonanie w nim wielu
<html>
<head> usprawnień. Zacznijcie od próby wyeli-
<meta name="Description" content= minowania konieczności inicjalizowa-
"Skrypt demonstrujący korzystanie z funkcji rozszerzenia RPN"/> nia stosu oraz kolejki – niech ich roz-
<meta name="Keywords" content="ZendAPI PHP extension rozszerzenie C" /> miar dostosowuję się do potrzeb (nie
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
jest to trudne zadanie w C). Następnie
<title>Strona demonstracyjna</title>
</head> możecie zmodyfikować kod tak, aby
<body> stos oraz kolejka nie zapamiętywały
<?php wartości typu double lub char, a od ra-
if (isset($_POST['phrase'])) { zu PHP-owe zval. n
$fltRet = rpn_calculation($_POST['phrase']);
if ($fltRet !== false) {
echo $_POST['phrase'].' = '.$fltRet.'<br/>';
} else {
echo '<em>'.rpn_errormsg().'</em>';
O autorze
}
}
Marcin Staniszczak jest studen-
?> tem pierwszego roku informatyki stu-
<form method="post"> diów uzupełniających magisterskich
Podaj wyrażenie do obliczenia: <input type="text" name="phrase"/> na WSHE w Łodzi. W PHP progra-
<input type="submit" value="Oblicz"/> muje od wielu lat. Jest autorem kilku-
</form> nastu publikacji o tematyce PHP. Jest
</body> autorem frameworka MVC dla PHP5
</html> (web.framework) oraz systemu szablo-
nów dla PHP5 (web.template).

52 www.phpsolmag.org PHP Solutions Nr 5/2006


Dla zaawansowanych

Budujemy własny kontener IoC,


czyli jak to się robi w Hollywood?
Stopień trudności: lll
Piotr Szarwas

Wyobraźmy sobie, że firma, dla której


stworzyliśmy aplikację, po jakimś czasie
powiększyła się znacznie i poprosiła nas o
migrację baz danych do jednej centralnej
pracującej w oparciu o LDAP. Niestety, jeśli
architektura naszej aplikacji nie została
zaprojektowana prawidłowo, czeka nas
wyjątkowo żmudna i ciężka praca.

N
a szczęście mamy kontener IoC dową implementacją zostanie jak w przy-
(ang. Inversion of Control), któ- padku poprzednich artykułów umieszczo-
ry już niejednokrotnie gościł na ny na stronie http://flexi.sf.net/.
łamach PHP Solutions. Czytelnicy, którzy
znają poprzednie artykuły, wiedzą już, co Czym jest kontener IoC?
mniej więcej potrafi IoC: kontener zapew- Kontener IoC to nic innego jak konfigu-
ni nam graf przygotowanych do użycia rowalna fabryka obiektów, która potra-
W SIECI obiektów czy np. umożliwi łatwe konfigu- fi nie tylko powołać obiekty do życia, ale
rowanie dekoratorów. także je skonfigurować tak, aby zaraz po
powołaniu były gotowe do użycia. O tym,
http://en.wikipedia.org/wiki/
Nie dzwoń do nas, my
l

Hollywood_Principle – Holly- jak właściwie składać obiekty i o różnych

l
wood Principle
http://www.theserverside.
zadzwonimy do Ciebie metodach składania obiektów, można pi-
com/tt/articles/article.tss?l=I Powyższe zdanie to dobrze znane progra- sać książki. Wbrew pozorom jest to pro-
OCBeginners – wprowadze- mistom tzw. prawo Hollywood (ang. Holly- ces dość skomplikowany i jeżeli robiony
nie do Dependency Injection
l http://www.martinfowler.com/
wood Principle). W myśl tego paradygma-
articles/injection.html tu tworzy się oprogramowanie spójne, z
– Inversion of Control
Containers/Dependency In-
luźnymi powiązaniami pomiędzy obiekta- Co należy wiedzieć...
mi, łatwe do testowania i utrzymania. Potrzebna będzie znajomość obiektowe-
jection pattern
http://picocontainer.codeha go programowania w PHP.
l
W obecnym artykule z cyklu Wzorce
us.org/ – implementacja IoC
projektowe i dobre praktyki programistycz-
l
dla Javy
http://flexi.sf.net/ – budowa- ne wspólnie stworzymy prosty kontener
Co obiecujemy...
ny przez nas framework, peł- Z artykułu dowiesz się, jak stworzyć
ne źródła omawiane w arty-
IoC, którego działanie w dużym uprosz- własny kontener IoC w myśl paradyg-
kule czeniu opiera się na wspomnianym prawie matu Hollywood Principle.
Hollywood. Kod kontenera wraz z przykła-

54 www.phpsolmag.org PHP Solutions Nr 5/2006


Jak zbudować własny kontener IoC? Dla zaawansowanych

jest niewłaściwie, może spowodować, że cję klasy odpowiadającej za zarządzanie Pamiętajmy, że relacje pomiędzy obiekta-
aplikacja, którą napiszecie będzie bardzo użytkownikami. O tym, jak to zrobić, do- mi pojawiają się dopiero w momencie uru-
trudna w utrzymaniu. Istnieje wiele spraw- wiecie się już za chwilę. chomienia aplikacji. Słabe wiązanie powo-
dzonych wzorców pokazujących, jak po- Należy jeszcze powiedzieć o innej, duje też, że klasy są bardziej elastyczne i
prawnie składać obiekty. Zachęcamy do bardzo ważnej zalecie IoC. Dzięki IoC generyczne, co oznacza, że można je wy-
przeczytania artykułu Obiektowa linia obiekty są słabo ze sobą powiązane, a korzystywać w większej ilość przypadków.
montażowa, czyli przejrzyste i elastyczne przez to łatwiejsze w testowaniu przy po- Wracając teraz do naszego przykładu,
aplikacje w PHP5, który ukazał się w PHP mocy Unit Testów (testów jednostkowych). można byłoby stworzyć klasę do uwierzy-
Solutions nr 2/2006 i jest poświęcony te-
mu zagadnieniu.
Zastosowanie kontenera IoC do skła-
dania obiektów w funkcjonalne grupy może ������� ������� �������
znacznie ułatwić budowanie i testowanie
złożonych aplikacji. Budowanie dlatego,
że obiekty komponujemy w funkcjonalne
grupy dopiero podczas uruchamiania apli-
kacji. Oznacza to, że praktycznie w do-
wolnym momencie życia aplikacji może-
������� �������
my wymieniać klasy, z których składa się
dana aplikacja. Taka wymiana potrafi być
oczywiście bardzo trudna, w szczególno-
ści jeżeli nie stosujemy pisania do inter-
fejsów i silnego typowania. Od pewnego
czasu jednak obie te funkcjonalności do-
stępne są w PHP, dlatego powinniśmy je
�������
stosować.

Przykład z życia wzięty


Wyobraźmy sobie następujący problem:
jakiś czas temu stworzyliśmy aplikację, Rysunek 1. Przykładowe drzewo obiektów, które kontener IoC powinien móc zbudować
w której istniała klasa odpowiadająca za
uwierzytelnienie i autoryzację użytkowni-
ków. Klasa ta operowała na bazie danych. Listing 1. Fragment index.php konfigurujący obiekty i uruchamiający front kontroler
Jako że była kluczowa w działaniu aplika- <?php
cji, używaliśmy jej w wielu miejscach.
Jeżeli nie wiedzieliśmy jeszcze o ist- $sessionFilter = new SessionFilter());
nieniu kontenera IoC, do przekazywa- $actionResolver =
new FilePerActionResolvingStrategy($currentDir.'/controllers/');
nia tej klasy pomiędzy różnymi warstwa-
$viewResolver = new PHPViewResolvingStrategy( $currentDir.'/views/');
mi aplikacji mógł posłużyć nam wzorzec $localeResolver = new NullLocaleResolvingStrategy();
singleton lub registry. Wyobraźmy sobie $httpRequest = new HttpRequest();
teraz, że klient, dla którego została napi- $frontController =
sana aplikacja, wymaga modyfikacji bazy new FrontControllerImpl(
$actionResolver,$viewResolver,$localeResolver, $filterChain);
użytkowników, jego firma powiększa się i
została podjęta decyzja o migracji wszyst- echo $frontController->doService( $httpRequest );
kich baz użytkowników do centralnej bazy ?>
działającej w oparciu o LDAP. Wyobraźmy
sobie przeszukiwanie całej aplikacji w sy- Listing 2. Fragment index.php konfigurujący obiekty i uruchamiający front
tuacji, gdy umieszczaliśmy wywołania kla- kontroler, ale tym razem z wykorzystaniem IoC
sy ręcznie lub poprzez wzorzec singleton <?php
czy registry. Dodatkową bolączką na pew-
no będzie przetestowanie takiej aplikacji. $mappingBuilder = new MappingBuilderFromArray( $iocMap );
Nie chcemy wpaść w takie tarapaty. $iocContainer =
new DefaultIoCContainter( $mappingBuilder->getApplicationMap() );
Gdybyśmy stosowali interfejsy i konte-
$frontController = $iocContainer->create( "frontController" );
ner IoC, sprawa byłaby bardzo prosta. Na- echo $frontController->doService( $iocContainer->create( "httpRequest" ) );
leżałoby stworzyć nową klasą implemen-
tującą odpowiedni interfejs i podmienić w ?>
pliku konfiguracyjnym kontenera defini-

PHP Solutions Nr 5/2006 www.phpsolmag.org 55


Dla początkujących Jak zbudować własny kontener IoC?

telnienia i autoryzacji poprzez LDAP, prze-


testować ją poza aplikacją, co jest z całą
pewnością łatwiejsze, a następnie umie- ��������������� ����������������

ścić przy pomocy kontenera IoC w aplika-


cji. Takie podejście zaoszczędzi nam spo-
ro czasu! Dodatkowo klasa taka mogłaby
z dużym prawdopodobieństwem zostać
bez zmian wykorzystana w innych pro-
jektach.
������������������� ��������
Wróćmy teraz do wymagań stawia-
nych kontenerowi IoC, po pierwsze ta-
ki kontener powinien móc powoływać do
życia dowolnie złożone drzewa obiek-
tów. Wyobraźmy sobie obiekt, który do
swojego działania potrzebuje dwóch in-
nych obiektów, które z kolei też potrze- ��������������
bują zewnętrznych obiektów do popraw-
nego działania. W taki sposób możemy
utworzyć dowolnie skomplikowane drze-
wo obiektów (Rysunek 1). Kontener IoC
Rysunek 2. Diagram UML klas mapujących plik konfiguracyjny kontenera IoC na
w takiej sytuacji powinien powołać do ży- obiekty zrozumiałe dla samego kontenera
cia obiekt będący korzeniem całej struk-
tury, a następnie powołać automatycznie
pozostałe obiekty i poskładać je w drze- Listing 3. Fragment konfiguracji naszego kontenera IoC, dzięki której jesteśmy w
wo. Tylko skąd kontener ma wiedzieć, ja- stanie odtworzyć drzewo obiektów, które powołaliśmy do życia ręcznie na Listingu 1
kie obiekty powołać i jak je poskładać?
Obiekty składamy dokładnie tak, jak robi <?php
$frameworkPath = "ścieżka do folderu z frameworkiem";
się to ręcznie: podając obiekt do konstruk-
tora, settera lub przypisując go do zmien- $iocMap = array(
nej publicznej. Najlepiej zilustrować to na "sessionFilter" => array(
przykładzie. Załóżmy, że obiekt A wymaga "className" => "SessionFilter",
do swojego działania zewnętrznego obiek- "file" => $frameworkPath.
"/web/mvc/controllers/filters/SessionFilter.class.php",
tu B. Obiekt B możemy przekazać obiek-
"singleton" => true,
towi A na trzy sposoby: "properties" => array(), "constructorParams" => array()
),
• $a = new A(new B()) – poprzez kon-
struktor, "actionResolver" => array(
"className" => "FilePerActionResolvingStrategy",
• $a->setB(new B()) – poprzez setter,
"file" => $frameworkPath.
• $a->b = new B() – poprzez zmienną "/web/mvc/actions/resolvers/FilePerActionResolvingStrategy.class.php",
publiczną. "singleton" => true, "properties" => array(),
"constructorParams" => array( $currentDir.'/controllers/' )
Zapomnijcie zatem o bardziej archaicz- ),

nych metodach takich jak singleton, re-


"viewResolver" => array(
jestr czy też ręczne powoływanie obiektu "className" => "PHPViewResolvingStrategy",
B wewnątrz obiektu A. Te metody tworzą "file" => $frameworkPath.
silne wiązania pomiędzy sobą i w rezulta- "/web/mvc/views/resolvers/PHPViewResolvingStrategy.class.php",
cie zmniejszają elastyczność kodu. "singleton" => true, "properties" => array(),
"constructorParams" => array( $currentDir.'/views/' )
Wiemy już, jak kontener powinien po-
),
składać obiekty w drzewa. Jest kilka moż-
liwości opisania takiego drzewa. Pierwsza "locateResolver" => array(
to zewnętrzny plik konfiguracyjny, który "className" => "NullLocaleResolvingStrategy",
opisuje wszystkie obiekty i drzewa obiek- "file" => $frameworkPath.
"/locale/resolvers/NullLocaleResolvingStrategy.class.php",
tów. Plik ściśle definiuje, jakie obiekty,
"singleton" => true, "properties" => array(),
stałe liczbowe lub znakowe należy wsta- "constructorParams" => array()
wić, w odpowiednie parametry konstruk- ),
tora czy też settera. Format pliku nie ma );
znaczenia, powinien być jedynie czytel- ?>

ny, gdyż będzie trzeba napisać go ręcz-


nie. Dlatego w naszej aplikacji parsowa-

56 www.phpsolmag.org PHP Solutions Nr 5/2006


Jak zbudować własny kontener IoC? Dla początkujących

nie pliku konfiguracyjnego zostanie wy- tami możemy być prawie pewni, że bę- z definicji znajdujących się w pliku konfi-
dzielone do osobnych klas tak, aby każ- dziemy mieli z tym do czynienia. Konte- guracyjnym.
dy mógł łatwo zmienić format i źródło da- ner, który napiszemy, będzie jedynie po- Istnieje jeszcze jedna metoda wiąza-
nych konfiguracyjnych. W szczególno- siadał opcję samego składania obiektów nia obiektów bardzo podobna do autowią-
ści można stworzyć plik konfiguracyjny
w oparciu o XML i Xschema, a do budowy Listing 4. Implementacja klas mapujących plik konfiguracyjny do modelu
plików konfiguracyjnych wykorzystywać obiektowego – klasa DefaultParamMap
jakieś graficzne narzędzie, które dodatko-
wo będzie za nas sprawdzać poprawność <?php

utworzonego pliku XML. My plik konfigura-


abstract class DefaultParamMap {
cyjny zbudujemy w oparciu o tablice aso-
cjacyjne. private $name;
Druga metoda definiowania drzewa private $value;
jest rozszerzeniem pierwszej, wymaga private $type;

jednak dodatkowo zastosowania w ko-


public function __construct($name, $value, $type) {
dzie silnego typowania. Wyobraźmy so- $this->setName($name);
bie, że obiekt A wymaga do swojego po- $this->setValue($value);
prawnego działania dowolnej klasy, któ- $this->setType($type);
ra implementuje interfejs BI lub też po }

prostu klasy B. Od pewnego czasu w


public function getName() {
PHP możemy wymusić typy obiektów, return $this->name;
dlatego konstruktor, czy też setter kla- }
sy A można zakodować w następują-
cy sposób: protected function setName( $name ) {
$this->name = $name;
}
class A {
public function __construct(BI $b){} public function getValue() {
public function setB(BI $b){} return $this->value;
} }

protected function setValue( $value ) {


a jeżeli nie chcemy interfejsu, korzystamy $this->value = $value;
jedynie z klasy B: }

class A { public function getType() {


return $this->type;
public function __construct(B $b){}
}
public function setB(B $b){}
} protected function setType( $type ) {
$this->type = $type;
Druga metoda zwana jest autowiązaniem }

obiektów. Powołując do życia obiekt A


}
sprawdza jego konstruktor i settery. Je- ?>
żeli znajdzie w nich deklarację obiek-
tów, które ma zdefiniowane w swoim pli- Listing 5. Implementacja klas mapujących plik konfiguracyjny do modelu
ku konfiguracyjnym, automatycznie po- obiektowego – klasa ConstructorParamMap
woła je do życia i umieści w obiekcie A.
<?php
Ta metoda składania obiektów jest bar-
dzo wygodna, gdyż nie wymaga tworze- require_once 'ioc/DefaultParamMap.class.php';
nia skomplikowanych plików konfigura-
class ConstructorParamMap extends DefaultParamMap {
cyjnych, niesie jednak ze sobą pewne
niebezpieczeństwo.
// dla parametru konstruktora nazwa parametru nie ma znaczenia
Wyobraźmy sobie, że klasa A wymaga // więc zmieniamy konstruktor tej klasy
do działania dowolnej klasy implementu-
jącej interfejs BI, w pliku konfiguracyjnym public function __construct($value, $type) {
parent::__construct(null,$value,$type);
mamy zdefiniowane dwie klasy implemen-
}
tujące interfejs BI, którą więc powinniśmy
wybrać? Takie problemy nie będą się po- }
jawiać w projektach prostych, gdzie plik ?>
konfiguracyjny zawiera kilka obiektów, ale
w projektach z kilkudziesięcioma obiek-

PHP Solutions Nr 5/2006 www.phpsolmag.org 57


Dla początkujących Jak zbudować własny kontener IoC?

zania. Polega ona na sprawdzaniu nazwy


settera i umieszczeniu przy jego pomocy Listing 7. Implementacja klas mapujących plik konfiguracyjny do modelu
obiektowego – klasa ClassMap
odpowiedniej klasy w obiekcie. Ponownie
najlepiej pokazać to na przykładzie, odwo- <?php
łajmy się więc do obiektu A i B. Załóżmy, require_once 'ioc/PropertyParamMap.class.php';
że klasa A jest zdefiniowana w następują- require_once 'ioc/ConstructorParamMap.class.php';
cy sposób: class ClassMap {

private $className;
class A { private $classFile;
public setB($b){} private $isSingleton;
} private $propertiesParams = array();
private $constructorParams = array();
public function __construct( $className, $classFile, $isSingleton ) {
Przy takiej budowie klasy i zastosowaniu
$this->setName($className);
trzeciej metody składania obiektów kon- $this->setClassFile($classFile);
tener IoC sprawdzi, czy klasa ma jakiś $this->isSingleton = $isSingleton;
setter badając, czy zawiera metody roz- }
poczynające się od słowa set a następ- public function getName() {
return $this->className;
nie sprawdzi, czy ma zarejestrowane w
}
pliku konfiguracyjnym klasy, których na- protected function setName($className) {
zwa pokrywa sie z pozostałą częścią na- $this->className = $className;
zwy settera, w naszym przypadku jest to }
klasa B. Ta metoda również niesie ze so-
public function getClassFile() {
bą pewne niebezpieczeństwo. Musimy
return $this->classFile;
uważać na konwencję nazywania sette- }
rów, gdyż w przypadku pomyłki w nazwie
możemy dostać obiekt, którego wcale protected function setClassFile( $classFile ) {
nie chcieliśmy. if ( !file_exists( $classFile ) ) {
throw new Exception("Plik {$classFile} nie istnieje");
Mając zdefiniowane założenia do bu-
}
dowy kontenera, zdefiniujmy teraz struktu- $this->classFile = $classFile;
rę pliku konfiguracyjnego. }

Konfiguracja kontenera public function getConstructorParams() {


return $this->constructorParams;
Do konfigurowania kontenera IoC wyko-
}
rzystamy prosty model obiektowy i ta-
public function setConstructorParam(
ConstructorParamMap $constructorParamMap ) {
Listing 6. Implementacja klas $this->constructorParams[] = $constructorParamMap;
mapujących plik konfiguracyjny }
do modelu obiektowego – klasa
ProperyParamMap public function setProperty( PropertyParamMap $propertyMap ) {
$this->propertiesParams[$propertyMap->getName()] = $propertyMap;
<?php }

require_once public function getProperty( $propertyName ) {


'ioc/DefaultParamMap.class.php'; if ( $this->hasProperty( $propertyName ) ) {
return $this->propertiesParams[$propertyName];
class PropertyParamMap } else {throw new Exception("Property {$propertyName} nie istnieje");}
extends DefaultParamMap { }

public function public function hasProperty( $propertyName ) {


getSetterMethodName() { return isset( $this->propertiesParams[$propertyName] );
return "set".ucfirst( }
$this->getName());
} public function getProperties() {
return $this->propertiesParams;
public function }
getGetterMethodName() {
return "get".ucfirst( public function isSingleton() {
$this->getName()); return (bool)$this->isSingleton;
} }
}
} ?>
?>

58 www.phpsolmag.org PHP Solutions Nr 5/2006


Jak zbudować własny kontener IoC? Dla początkujących

blice asocjacyjne. Konfiguracja, którą cjacyjnej, każdy klucz tej tablicy jedno- • properties – tablica asocjacyjna, w
będzie zarządzał programista, będzie znacznie identyfikuje obiekt. Na posta- której kluczem jest nazwa atrybutu
znajdować się w specjalnej tablicy, która wie tego identyfikatora z kontenera będą obiektu, a wartością zmienna, któ-
następnie będzie tłumaczona na zestaw pobierane obiekty. Zwróćmy uwagę, że rą należy podstawić pod ten atry-
obiektów przez specjalną klasę parsują- w jednym pliku można zdefiniować wie- but, w przypadku stałych znakowych
cą. Na tych obiektach będzie operował le konfiguracji dla jednej klasy, wystar- lub liczbowych wpisujemy tu ich
nasz kontener. Dzięki takiemu podej- czy, aby były one identyfikowane przez wartość, w przypadku referencji do
ściu łatwo będziemy mogli zmieniać for- inne klucze w tablicy. Pod identyfikato- obiektów wpisujemy znak & i identy-
maty i źródła danych konfiguracyjnych, rem obiektu znajduje się kolejna tablica fikator obiektu zdefiniowany w konte-
a w ekstremalnych sytuacjach będziemy asocjacyjna, która ma jasno zdefiniowa- nerze IoC,
mogli konfigurować kontener nie posia- ny zestaw kluczy: • constructorParams – tablica z pa-
dając w ogóle plików konfiguracyjnych. rametrami przeznaczonymi dla kon-
Listing 1 przedstawia wycinek skryp- • className – nazwa klasy, struktora obiektu, tym razem nie jest
tu index.php. Możemy tu znaleźć dwie • file– położenie pliku z definicją klasy, to tablica asocjacyjna, znaczenie ma
sekcje: jedna bardzo długa, konfiguru- • singleton – flaga mówiąca, czy w niej kolejność umieszczanych war-
jąca obiekty i druga znacznie krótsza, obiekt ma zostać utworzony tyl- tości, będą one w tej samej kolejności
uruchamiająca front kontroler. Teraz ko raz i potem umieszczony w ca- umieszczone w konstruktorze pod-
przyjrzyjmy się Listingowi 2, gdzie znaj- che'u, czy też za każdym razem czas powoływania obiektu do życia,
duje się ten sam skrypt index.php, jed- ma być tworzona kolejna instancja zasady wpisywania parametrów są
nak tym razem konfigurację obiektów i obiektu, takie same jak powyżej.
ich powołanie do życia przejął na sie-
bie kontener IoC. Już na pierwszy rzut
oka widać, że skrypt jest dużo prost- Listing 8. Implementacja klas mapujących plik konfiguracyjny do modelu
obiektowego – klasa ApplicationMap
szy. Warto dodać, że im większe będzie
drzewo obiektów, które musimy utwo- <?php
rzyć, tym większy będzie zysk na czy-
telności kodu. require_once 'ioc/ClassMap.class.php';
Na Listingu 3 znajduje się konfigura-
class ApplicationMap {
cja naszego kontenera IoC, dzięki któ-
rej jesteśmy w stanie odtworzyć drzewo private $classes = array();
obiektów, które powołaliśmy do życia
ręcznie na Listingu 1. I w tym momen- public function __construct() {
cie wielu z Was zapewne złapie się za }

głowę i powie – zaraz, ale przecież kon-


public function setClass($objKey, ClassMap $class) {
figuracja zajmuje więcej miejsca niż Li- $this->classes[$objKey] = $class;
stingu 1, tak więc co właściwie zyskuje- }
my, skoro ilość kodu, którą musimy na-
pisać dla kontenera jest w sumie więk- public function getClass($objKey) {
if ( $this->hasClass( $objKey ) ) {
sza, niż jakby go nie było. Na szczęście
return $this->classes[$objKey];
w ogólnym rozrachunku nie jest to praw- } else {
da. Po pierwsze konfigurację piszemy throw new Exception("Klasa {$objKey} nie istnieje");
raz, a wykorzystujemy wiele razy – w }
ramach jednej lub kolejnych aplikacji pi- }

sanych w oparciu o te same klasy. Może


public function hasClass($objKey) {
być więc tak, że wykorzystamy moduły return isset($this->classes[$objKey]);
z poprzedniej aplikacji w nienaruszonej }
wersji, a ich modyfikacja będzie polegać
jedynie na zmianie pliku konfiguracyjne- }
?>
go. Dodatkowo, jeżeli do pliku konfigu-
racyjnego przeniesiemy wszystkie infor- Listing 9. Każda implementacja kontenera musi implementować interfejs
macje o budowie obiektów, automatycz- IoCContainer
nie stworzymy świetną dokumentację, w
<?php
jednym miejscu będzie zawarta pełna
informacja o strukturze aplikacji. interface IoCContainer {
Wróćmy teraz do pliku konfiguracyj- public function create($className);
nego. Tylko na pierwszy rzut oka mo- }
że wydawać się on skomplikowany. Po
?>
pierwsze wszystkie definicje obiektów
umieszczone są w jednej tablicy aso-

PHP Solutions Nr 5/2006 www.phpsolmag.org 59


Dla początkujących Jak zbudować własny kontener IoC?

Na Rysunku 2 widać diagram UML, a na


Listingach 4–8 implementacje klas ma- Listing 10a. Pełna implementacja kontenera w podstawowej formie
pujących plik konfiguracyjny do modelu
<?php
obiektowego.
Każda z klas przedstawionych na dia- require_once 'ioc/IoCContainer.interface.php';
gramie mapuje jeden do jednego któ-
rąś z danych konfiguracyjnych. Klasa class DefaultIoCContainter implements IoCContainer {

ProperyParamMap odpowiada za mapowa-


private $applicationMap;
nie danych wstawianych do obiektów przy private $objectCache = array();
pomocy setterów, ConstructorParamMap
spełnia to samo zadanie w przypad- public function __construct( ApplicationMap $applicationMap ) {
ku konstruktora obiektu. Natomiast kla- $this->setApplicationMap( $applicationMap );
}
sy ClassMap i ApplicationMap są w dużej
mierze agregatorami właściwych danych protected function setApplicationMap(ApplicationMap $value) {
konfiguracyjnych. Pominiemy prezentację $this->applicationMap = $value;
kodu transformującego tablicę na obiekty. }
Kod ten podobnie jak i pozostałe elementy
protected function getApplicationMap() {
kontenera będzie można pobrać ze strony
return $this->applicationMap;
http://flexi.sf.net. }

Kontener IoC public function create($className) {


Przejdźmy teraz do implementacji sa- $classMap = $this->getApplicationMap()->getClass($className);
if ( $classMap->isSingleton() ) {
mego kontenera IoC. Po pierwsze każ-
if ( $this->inCache( $className ) ) {
da implementacja kontenera musi im- return $this->getFromCache( $className );
plementować interfejs IoCContainer. In- } else {
terfejs ten ma tylko jedną metodę pu- $classObj = $this->createObject($classMap);
bliczną create() (Listing 9). Pełna imple- $this->putInCache($className,$classObj);
return $classObj;
mentacja kontenera w podstawowej for-
}
mie pokazana jest na Listingu 10. Kla- } else {
sa DefaultIoCContainer implementu- return $this->createObject($classMap);
je interfejs IoCContainer. Do swojego }
poprawnego działania wymaga klasy }

ApplicationMap, która zawiera konfigu-


protected function createObject(ClassMap $classMap) {
rację kontenera. Klasa ta posiada tylko $className = $classMap->getName();
jedną metodę publiczną create(). Meto-
da na podstawie podanego identyfikato- if ( !class_exists( $className ) ) {
ra obiektu zwraca instancję tego obiektu require_once($classMap->getClassFile());
}
wraz z drzewem podrzędnych mu obiek-
$paramsArr = $classMap->getConstructorParams();
tów, jeżeli takowe zostały zdefiniowane if( !empty( $paramsArr ) ) {
w pliku konfiguracyjnym. $params = array();
Metoda create pobiera konfigura- $evalArr = array();
cję dla danego identyfikatora, następ- $i = 0;
foreach ($paramsArr as $constructorParam) {
nie sprawdza, czy obiekt jest typu sin-
$params[] = $this->getConstructorParamsRecursively($constructorPar
gleton (tzn. że podczas życia aplikacji am);
może zostać utworzona tylko jedna in- $evalArr[] = '$params['.$i++.']';
stancja danego obiektu). Jeżeli tak, to }
sprawdzamy, czy nie został już wcze- $evalStr = '$classObj = new '.$className.'('.implode(",",$evalArr).')
;';
śniej utworzony. Jeżeli tak to zwraca-
my go, jeżeli nie to tworzymy go przy eval($evalStr);
pomocy metody createObject. Metoda, } else {
po pierwsze, ładuje plik z definicją kla- $classObj = new $className();
sy, którą ma powołać do życia – ścież- }
$this->setProperties($classMap, $classObj);
ka do pliku zdefiniowana jest w konfi-
return $classObj;
guracji. }
Po drugie, sprawdza, czy obiekt przy
tworzeniu wymaga podania parametrów
do konstruktora. Jeżeli tak, to tworzony
jest obiekt przy pomocy funkcji eval, je-
żeli nie, to tradycyjnie przy pomocy ope-

60 www.phpsolmag.org PHP Solutions Nr 5/2006


Jak zbudować własny kontener IoC? Dla początkujących

ratora new. Jeżeli korzystacie z wersji Informacja o tym, jakiego settera użyć, Warto jeszcze zwrócić uwagę na
PHP 5.1.3 lub nowszej możecie two- nie jest zapisana w konfiguracji, konte- dwie metody getConstructorParamsR
rzyć obiekty z parametrami do konstruk- ner tworzy ją sam. Poprawna nazwa ecursively i getPropertyValueRecur
tora przy pomocy Reflection API i meto- settera składa się z przedrostka set i sively. Obie są bardzo podobne. Na
dy newInstanceArgs. nazwy propertisa ze zmienioną pierw- podstawie typu parametru, który mo-
Po utworzeniu obiektu jego in- sza literą na wielką. że przyjąć dwie wartości value lub
stancja przekazywana jest do metody Ta konwencja nazywania metod reference, kontener pobiera z kon-
setProperties, gdzie do obiektu przy dostępowych do atrybutów obiektów figuracji stałą lub ponownie urucha-
pomocy setterów dodawane są zmien- nie powinna być Wam obca, jest zgod- mia swoją metodę create, aby utwo-
ne, które w pliku konfiguracyjnym zo- na z konwencją stosowaną w innych rzyć obiekt. Dzięki temu kawałkowi ko-
stały zdefiniowane w sekcji properties. aplikacjach. du kontener może zwracać całe drze-
wa obiektów.
Listing 10b. Pełna implementacja kontenera w podstawowej formie – ciąg dalszy O tym, czy parametr jest typu va-
lue, czy reference, decyduje obecność
& przy definicji wartości parametru w
protected function getConstructorParamsRecursively(ConstructorParamMap pliku konfiguracyjnym.
$constructorParamMap) {
switch ( $constructorParamMap->getType() ) {
case 'value' : $param = $constructorParamMap->getValue(); break; Podsumowanie
case 'reference' : $param = Kontener IoC, którego uczyliśmy się
$this->create( $constructorParamMap->getValue() ); break; tutaj razem zbudować, jest od jakie-
default : throw new Exception( goś czasu wykorzystywany w codzien-
"Niewłaściwy typ parametru {$constructorParamMap->getType()}" );
nej pracy w mojej firmie. Z doświadcze-
}
nia wynika, że jego wdrożenie ułatwiło
return $param; nam pisanie aplikacji.
} Najwięcej zyskaliśmy w kwestii te-
stów i przejrzystości kodu. Dodatkowo
protected function setProperties(ClassMap $classMap, $object) {
spora część klasy po przystosowaniu
$properties = $classMap->getProperties();
do specyfiki kontenera IoC – dostarcza-
foreach($properties as $propertyParamMap) { my obiekty zewnętrzne przez konstruk-
$propertySetterName = $propertyParamMap->getSetterMethodName(); tor i settery – stała się na tyle elastycz-
$object->{$propertySetterName} na, że z powodzeniem bez przepisywa-
( $this->getPropertyValueRecursively($propertyParamMap) );
nia zaczęliśmy wykorzystywać je w kolej-
}
} nych projektach.
Udało się nam też usunąć z podsta-
protected function getPropertyValueRecursively( PropertyParamMap wowego kodu aspekty związanie z auto-
$propertyParamMap ) { ryzacją, logowaniem i walidacją. Jak to
switch ( $propertyParamMap->getType() ) {
zrobiliśmy? O tym przeczytacie w kolej-
case 'value' : $param = $propertyParamMap->getValue(); break;
case 'reference' : $param = $ nych artykułach w magazynie PHP So-
this->create( $propertyParamMap->getValue() ); break; lutions. n
default : throw new Exception(
"Niewłaściwy typ atrybutu {$propertyParamMap->getType()}" );
}

return $param;
}

protected function putInCache($key,$value) { O autorze


$this->objectCache[$key] = $value;
}
Piotr Szarwas jest pracownikiem
protected function getFromCache($key) { SUPER-MEDIA Interactive i dokto-
return $this->objectCache[$key]; rantem na wydziale Fizyki Politech-
} niki Warszawskiej. Od 2003 roku
projektuje aplikacje WWW w opar-
protected function inCache($key) {
return isset($this->objectCache[$key]); ciu o PHP4/5. Obecnie zajmuje się
} tworzeniem frameworka dla PHP
opartego na rozwiązaniach Hiberna-
} te i Spring.
?>
Kontakt z autorem:
piotr.szarwas@gmail.com

PHP Solutions Nr 5/2006 www.phpsolmag.org 61


Dla zaawansowanych

XML i PHP w praktyce


Stopień trudności: lll
Guillaume Ponçon

Bazy danych, dokumenty biurowe, RSS: coraz


więcej formatów gromadzenia i przesyłania
danych opiera się na XML-u. Jego główną
zaletą jest łatwość tworzenia i przetwarzania
dokumentów XML niezależnie od platformy
sprzętowej i systemowej. Jako programiści PHP,
mamy szerokie możliwości wykorzystania XML-
a przy użyciu co najmniej kilku technik...

S
tandard XML, czyli eXtensible DTD czy XML Schema), gdyż w innym
Markup Language został określo- wypadku program odczytujący ten doku-
ny przez World Wide Web Con- ment zgłosi błąd.
sortium (W3C). Obecnie jest przeważnie
wykorzystywany jako format gromadze- Techniki przetwarzania
nia i wymiany danych (np. SXW, ODT, XML-a w PHP
RDF, RSS) oraz tworzenia specjalistycz- Pisząc skrypty w języku PHP możemy
nych języków opartych na znacznikach swobodnie korzystać z dokumentów XML-
– jest więc metajęzykiem. Każdy doku- owych. Do ich przetwarzania służą roz-
ment XML zawiera dane uporządkowa- szerzenia języka PHP o nazwach: SAX,
ne w hierarchii drzewiastej. Spójrzmy na
Listing 1: przedstawiamy na nim przykła- Co należy wiedzieć...
W SIECI dowy kod XML. Jego struktura opiera się Przydatna będzie znajomość podstaw
na znacznikach zawartych w nawiasach programowania obiektowego w PHP5.
trójkątnych (podobnie jak w przypadku
• http://www.w3.org/XML/ HTML-a). Para znaczników, np. <title> Co obiecujemy...
– oficjalna specyfikacja stan- i </title> określa element dokumentu Przedstawimy zasady działania roz-
dardu XML
• http://www.saxproject.org/
XML-owego, który może zawierać tekst szerzeń języka PHP: DOM, SAX oraz
– strona projektu SAX lub kolejne elementy (zwane podrzędny- SimpleXML oraz pokażemy, jak korzy-
• http://www.w3.org/DOM/
mi lub potomnymi). Składnia każdego do- stając z SimpleXML tworzyć i odczyty-
– oficjalna specyfikacja stan-
dardu DOM kumentu XML musi być ściśle przestrze- wać dokumenty XML-owe, w tym pliki
• http://books.evc-cit.info/ gana (nie wolno np. pozostawiać niedo- OpenOffice.org i dane programu Gant-
odbook/book.html – oficjalna
specyfikacja formatu Open- mkniętych znaczników; można też okre- tproject.
Document ślić inne zasady korzystając z plików

62 www.phpsolmag.org PHP Solutions Nr 5/2006


Dla zaawansowanych
PEAR

DOM-XML (PHP4), DOM (PHP5) oraz


SimpleXML (tylko PHP5). Omówimy po-
krótce SAX, DOM-XML oraz SimpleXML,
podając przykłady wykorzystania każdego
z tych rozszerzeń.

SAX
SAX (http://www.saxproject.org/, http://
www.php.net/xml).oznacza Simple API for
XML i umożliwia sekwencyjny odczyt do-
kumentów XML. Pozwala na odczyt prak-
Rysunek 1. Okno programu Ganttproject: po lewej stronie wykres Gantta
tycznie każdego rodzaju dokumentów
XML, nawet tych, które zostały utworzo-
ne niepoprawnie. SAX nie umożliwia nato- Instalacja
miast zapisu ani modyfikacji danych. Aby można było uruchomić przykłady z tego artykułu, zalecane jest posiadanie PHP
Na Listingu 2 przedstawiamy skrypt 5.1.4 lub nowszego. Pod systemem Windows możemy w tym celu skorzystać z ze-
służący do odczytu dokumentu XML-owe- stawu typu AMP (Apache, MySQL, PHP) czy też 3 w 1 o nazwie WampServer (http:
go z użyciem techniki SAX. Zaczynamy od //www.wampserver.com). Jego instalacja jest trywialna i odbywa się przy użyciu pro-
utworzenia parsera ($xml_parser), który stego wizarda.
jest zasobem (ang. resource) tworzonym
przy użyciu funkcji xml_parser_create() i
reprezentującym dokument XML, który bę- Listing 1. Dokument XML Simple (opis książki)
dziemy przetwarzali. Do tego ostatniego w
<document id="12">
technice SAX służą funkcje zwane handle- <author>Guillaume Ponçon</author>
rami (uchwytami), które są wywoływane <title>Best practices PHP 5</title>
przez język PHP podczas odczytu nasze- <chapter>
go dokumentu. Musimy je sami utworzyć, <title>Strumienie XML</title>
<paragraph type="introduction">...</paragraph>
zanim się do nich odwołamy. W naszym
</chapter>
przykładzie zdefiniujemy najpierw dwa </document>
podstawowe uchwyty: początku (startE-
lement()) i końca (endElement()) każdego Listing 2. Przeglądamy dokument XML-owy przy użyciu SAX (wyświetlanie
elementu XML-owego, uruchamiane od- znaczników i zawartości)
powiednio, gdy parser natrafi na znacznik
function startElement($parser, $name, $attrs) { echo $name . "\n"; }
rozpoczynający (np. <author>) i kończący function endElement($parser, $name) { echo $name . "\n"; }
(np. </author>) dany element. W przypad- function dataHandler($parser, $data) { echo '-> ' . trim($data) . "\n"; }
ku napotkania elementu danych pomiędzy
dwoma znacznikami XML wywoływany $xml_parser = xml_parser_create();
$xml='<document id="12"><author>Guillaume Ponçon</author></document>';
jest uchwyt dataHandler. Handlery począt-
ku i końca elementu definiujemy przy po- xml_set_element_handler($xml_parser, "startElement", "endElement");
mocy funkcji xml_set_element_handler(), xml_set_character_data_handler($xml_parser, 'dataHandler');
a uchwyt elementu danych używając xml_ xml_parse($xml_parser, $xml, true);
set_character_data_handler(). Przetwa- xml_parser_free($xml_parser);

rzanie dokumentu XML rozpoczniemy ko-


Listing 3. Przykład odczytu dokumentu XML przy użyciu DOMXML (wyświetlanie
rzystając z funkcji xml_parse(), jako jej informacji zawartych na Listingu 1)
parametry podając: instancję parsera (za-
sób $xml_parser), źródło dokumentu w $dom = new DOMDocument();
wersji tekstowej (np. odczytane z pliku; my $dom->LoadXML($xml);
$title = $dom->getElementsByTagName('title');
użyjemy zmiennej $xml, w której umieści-
echo "Rozdziały książki " . $title->item(0)->nodeValue . " : \n";
my fragment kodu z Listingu 1. Na koniec,
zwalniamy zasób $xml_parser korzystając foreach ($dom->getElementsByTagName('chapter') as $element) {
z funkcji xml_parser_free(). $titles = $element->getElementsByTagName('title');
echo "\n- " . $titles->item(0)->nodeValue . "\n";
$paragraphs = $element->getElementsByTagName('paragraph');
DOM-XML foreach ($paragraphs as $paragraph) {
Rozszerzenie DOM-XML umożliwia prze- echo ' * ' . $paragraph->nodeValue . "\n";
twarzanie plików XML w PHP zgodnie ze }
standardem DOM (skrót od Document }
Object Model). DOM-XML pozwala za-

PHP Solutions Nr 5/2006 www.phpsolmag.org 63


Dla zaawansowanych

Należy wiedzieć, że omówiona meto-


da getElementsByTagName() odnajdu-
je wszystkie węzły określone daną parą
znaczników (np. <title>...</title>) i spo-
rządza ich listę, więc nawet, gdy istnieje
tylko jeden węzeł o wybranej nazwie (jak
u nas), musimy go wskazać (np. używa-
jąc metody item(), choć istnieją też inne
sposoby). Analogicznie wyświetlimy listę
rozdziałów (element chapter) i akapitów
(paragraph). Tekst zapisany w danym
Rysunek 2. Skrypt odczytujący dokument utworzony przez program Ganttproject i wy- węźle odczytamy i zmodyfikujemy korzy-
świetlający jego strukturę stając z atrybutu nodeValue.
Utworznie nowego węzła nastę-
RSS – podstawy puje poprzez użycie aż dwóch metod:
RSS (Really Simple Syndication, znane dawniej jako Rich Site Summary czy RDF createElement(), która pozwala na utwo-
Site Summary) to popularny i prosty format strumienia przesyłania newsów (prze- rzenie obiektu węzła o określonej nazwie
ważnie krótkich) w Internecie. Zestawy newsów są dostępne w postaci plików okre- (u nas category) i zawartości (u nas PHP),
ślanych jako kanały (ang. channels), strumienie lub feeds. Każdy news zawiera na który go reprezentuje oraz appendChild(),
ogół tytuł, krótki opis, rozwinięcie i link do dłuższego artykułu. RSS opiera się na która z kolei pozwala dodać ten element w
standardzie XML. Sajty udostępniające wiadomości w postaci RSS (głównie gazety, odpowiednim miejscu drzewa węzłów. W
magazyny i portale internetowe, takiej jak Yahoo!, Slashdot (http://rss.slashdot.org/ naszym przypadku, element ten będzie
Slashdot/slashdot) czy freshmeat.net (http://rss.freshmeat.net/freshmeat/feeds/fm-re- podrzędny wobec pierwszego odnalezio-
leases-global)) są zwykle oznaczone ikonką zawierającą skrót RSS, RDF lub XML nego elementu drzewa (title), niższego
na stronie głównej. o jeden poziom od korzenia drzewa (u nas
Newsy RSS mogą być następnie pobierane i wyświetlane przez czytnik umiesz- document). Wreszcie, do zapisu gotowe-
czony na komputerze klienta lub na dowolnej witrynie internetowej, takiej jak np. pry- go dokumentu XML w pliku służy metoda
watna strona domowa czy blog, a także niektóre programy pocztowe (np. Thunder- save() obiektu $dom.
bird). Wyświetlanie zawartości kanałów RSS jest możliwe również przy użyciu prze- Jak widać, rozwiązanie DOM jest sku-
glądarki internetowej: po wpisaniu adresu RSS powinna się w niej pojawić pełna teczne i spójne logicznie, ale niestety bar-
struktura i zawartość pliku XML z newsami. dzo rozwlekłe i wymagające dużo kodu.
Dlatego też jest relatywnie rzadko sto-
równo na odczyt, jak i zapis (tworzenie i zeł, musimy go najpierw odnaleźć meto- sowane, choć zawiera bardzo pożytecz-
modyfikowanie) dokumentów XML. Stan- dą getElementsByTagName(), tworząc no- ne narzędzia, takie jak np. XSLT (połą-
dard DOM znajduje zastosowanie nie tyl- wy obiekt (najlepiej o nazwie odpowiada- czenie dokumentu XML i arkusza stylów
ko w PHP, ale także w innych językach jącej nazwie węzła). Następnie używamy XSL) czy XPath (język zapytań umożli-
(m.in. Python, Java, JavaScript). Głów- atrybutów i metod tego obiektu, umożli- wiający wyszukiwanie elementów i warto-
ną ideą DOM jest to, że struktura XML wiających odczytywanie i zapisywanie ści w dokumentach XML, będący niejako
jest traktowana jako hierarchiczne drze- danych w zaznaczonym węźle. My wy- XML-owym odpowiednikiem SQL-a).
wo węzłów (ang. nodes), z których każ- korzystamy omówioną metodę do zna-
dy jest reprezentowany jako obiekt języka lezienia węzła title (element pomię- SimpleXML
PHP (lub innego języka, dla którego istnie- dzy znacznikami <title> i </title>; pa- SimpleXML (http://www.php.net/simplexml)
je implementacja DOM). W PHP obsługę miętajmy, że posługujemy się cały czas to rozszerzenie, które pojawiło się wraz
standardu DOM umożliwiają rozszerzenia przykładowym kodem XML z Listingu 1). z PHP5. Jest bardzo łatwe w obsłudze i
DOM-XML (PHP4, http://www.php.net/
domxml) i DOM (PHP5). Jak już powie- Listing 4. Modyfikujemy dokument XML-owy korzystając z DOM-XML (dodanie
dzieliśmy, skorzystamy z tego pierwszego. węzła category w dokumencie XML z Listingu 1)
Na Listingu 3 przedstawiamy przy-
kład odczytu, a na Listingu 4 przykład $dom = new DOMDocument();
$dom->LoadXML($xml);
zapisu dokumentu XML za pomocą
DOM. W obu przypadkach zaczynamy $title = $dom->getElementsByTagName('title');
tworząc obiekt $dom klasy DOMDocument $title->item(0)->nodeValue = "Best practices in PHP5";
(musimy go utworzyć), do którego bę- $element = $dom->createElement('category', 'PHP');
dziemy się odwoływać przy wykonywa-
$rootElement = $dom->getElementsByTagName('document');
niu operacji na dokumencie. Następnie
$rootElement->item(0)->appendchild($element);
ładujemy kod XML w wersji tekstowej (ze
zmiennej łańcuchowej $xml) korzysta- $dom->save('/tmp/document.xml');
jąc z metody LoadXML obiektu $dom. Aby echo file_get_contents('/tmp/document.xml');
odczytać lub zmodyfikować dany wę-

64 www.phpsolmag.org PHP Solutions Nr 5/2006


Dla zaawansowanych

plik RSS, a na Listingu 8 skrypt odczy-


Czym jest Ganttproject? tujący strumień RSS spod adresu http:
Ganttproject (Rysunek 1) jest napisanym w Javie opensourcowym programem do //rss.freshmeat.net/freshmeat/feeds/fm-
sporządzania wykresów Gantta. Możemy go pobrać spod adresu http://ganttproje releases-global, czyli informacje o no-
ct.sourceforge.net. Zgodnie ze specyfiką wykresów Gantta, Ganttproject pozwa- wych projektach programistycznych, któ-
la na wizualizację organizacji zadań i zasobów przydzielonych do wybranego pro- re zostały zamieszczone na witrynie fre-
jektu w czasie. Jedną z podstawowych funkcji programu Ganttproject jest tworze- shmeat.net. Ładowanie zestawu wiado-
nie zadań, które możemy układać w kategorie. Narzędzie to pozwala nam również mości następuje poprzez zwykłe załado-
na śledzenie postępu zadań, sporządzanie listy zasobów (osób pracujących nad wanie pliku znajdującego się pod wspo-
projektem), wyświetlanie wykresu strat oraz wykonywanie wielu innych użytecz- mnianym adresem, po czym przystępu-
nych operacji. jemy do ich wyświetlania w oknie prze-
glądarki: zaczynamy od wypisania tytułu
kanału (element <title>, podrzędny wo-
bec <channel>). Następnie iterujemy wia-
domości, z których każda jest elemen-
tem podrzędnym wobec <channel> ozna-
czonym jako <item> i wyświetlamy ich
elementy potomne: <description> oraz
<date>. Korzeń (root) dokumentu XML
jest oznaczony jako <rss> (czasem też
jako <RDF>) i jest reprezentowany przez
sam obiekt $rss.
Po uruchomieniu tego skryptu zoba-
Rysunek 3. Przykład pliku tekstowego stworzonego w edytorze OpenOffice.org Writer
czymy zestaw wiadomości – zauważmy,
umożliwia zarówno odczyt (w PHP 5.0), jedynie skorzystać z metody addChild() że niektóre z nich zawierają również gra-
jak i zapis (w PHP 5.1.4) dokumentów i gotowe. fikę.
XML. Jest domyślnie skompilowane w
każdej z tych dystrybucji PHP. Odczyt strumienia RSS Manipulujemy
Na Listingu 5 pokazujemy, jak od- za pomocą SimpleXML wykresami Gantta za
czytywać zawartość dokumentu XML, RSS to standard przesyłania wiadomo- pomocą SimpleXML
a na Listingu 6, jak ją modyfikować. W ści w Internecie, o którym więcej mówi- oraz Ganttproject
obu przypadkach korzystanie z Simple- my w Ramce RSS – podstawy. Na Li- Wykres Gantta (ang. Gantt chart) to po-
XML zaczynamy od utworzenia obiek- stingu 7 przedstawiamy przykładowy pularna metoda wizualizacji etapów i po-
tu $simpleXml, do czego służy funkcja
simplexml_load_string(), która tworzy
Listing 5. Odczyt dokumentu przy wykorzystaniu SimpleXML (wyświetlanie infor-
dokument w oparciu o kod XML w po-
macji zawartych na Listingu 1)
staci tekstowej (zawarty w zmiennej łań-
cuchowej). Podobnie, jak w przypadku $simpleXml = simplexml_load_string($xml);
DOM, w SimpleXML struktura dokumen- echo '+ '.$simpleXml->title ."\n";
tu XML jest reprezentowana przez ze- echo ' by '.$simpleXml->author."\n\n";

staw obiektów języka PHP, tyle, że są


foreach($simpleXml->chapter as $chapter){
one tworzone automatycznie i mają na- echo '- ' . $chapter->title . "\n";
zwy odpowiadające nazwom węzłów
(elementów) naszego dokumentu, a ko- foreach($chapter->paragraph as $paragraph){
rzystanie z nich jest znacznie prostsze, echo ' -> ' . $paragraph . "\n";
}
co możemy sami zobaczyć porównując
echo "\n";
długość kodu w obu przypadkach: pod- }
czas, gdy w DOM trzeba było tworzyć
rozwlekły skrypt, w SimpleXML wystar- Listing 6. Przykład modyfikacji dokumentu XML-oweog za pomocą SimpleXML
czy parę linijek! Co więcej, z elementów (dodanie rozdziału do dokumentu XML z Listingu 1)
dokumentu XML korzystamy przeważnie $simpleXml = simplexml_load_string($xml);
(zarówno do odczytu, jak i zapisu) uży- $simpleXml->title = "Dobre wprawki w PHP 5";
wając atrybutów (pól) obiektów poszcze-
gólnych węzłów. Pola te mogą zawierać $newChapter = $simpleXml->addChild('chapter');
$newChapter->addChild('title', 'Programowanie obiektowe');
zarówno treść tekstową, jak i listy ele-
$newChapter->addChild('paragraph', 'Programowanie zorientowane obiektowo jest
mentów podrzędnych, które możemy ite- wspaniałe ...');
rować korzystając z instrukcji foreach. echo $simpleXml->asXml();
Znacznie prostsze niż w DOM jest rów-
nież dodawanie nowych węzłów: musimy

PHP Solutions Nr 5/2006 www.phpsolmag.org 65


Dla zaawansowanych

kolejne zadania w hierarchii zadań. Na


Czym jest przestrzeń nazw? Rysunku 2 przedstawiamy efekt działa-
Przestrzeń nazw umożliwia podzielenie dużych plików XML na kategorie, co uła- nia naszego skryptu: wylistowaną w linii
twia zorientowanie się w jego zawartości i manipulowanie nią. Z użyciem przestrzeni poleceń hierarchię zadań wraz ze wspo-
nazw mamy do czynienia, gdy znacznik w pliku XML-owym składa się z dwóch czę- mnianymi informacjami (datą początko-
ści oddzielonych dwukropkiem (:), np. <text:p>...</text:p>. Identyfikatorem prze- wą, czasem trwania i tytułem).
strzeni nazw jest słowo kluczowe znajdujące się przed dwukropkiem. Przykładowo,
każdy znacznik zawarty w pliku Open Office content.xml o nazwie text:p (więc nale- Modyfikujemy zadanie
żący do przestrzeni nazw text), odpowiada jednemu akapitowi tekstu. utworzone w Ganttproject
Modyfikacja istniejącego zadania pliku da-
nych Ganttproject z użyciem SimpleXML
jest równie prosta: jak widzimy na Listingu
11, potrzebujemy do tego zaledwie trzech
linijek kodu! W naszym przykładzie zmie-
nimy nazwę (parametr name) pierwszego
zadania na wykresie Gantta.

Dodajemy zadanie
Aby dodać zadanie do wykresu Gantta,
musimy umieścić nowy element <task> w
pliku XML (Listing 12). W przypadku Sim-
Rysunek 4. Zawartość pliku tekstowego model.odt pleXML służy do tego metoda addChild(),
stępów wykonania projektu, często sto- każdym z nich: datę początkową, czas o której już mówiliśmy. Zaczniemy od za-
sowana w firmach i innych organiza- trwania i tytuł. Potrzebujemy na to około ładowania pliku XML (openoffice.xml).
cjach. Istnieje wiele narzędzi służących dziesięciu linii kodu. Jak widzimy, nasz Aby dodać nowe zadanie, będziemy mu-
do sporządzania takich wykresów; jed- algorytm składa się z funkcji rekurencyj- sieli nadać mu identyfikator będący nu-
nym z nich jest opensourcowy program nej, dzięki której możemy przetwarzać merem większym o jeden od ID ostatnio
Ganttproject, o którym mówimy więcej
w Ramce Czym jest Ganttproject?. Po- Listing 7. Struktura strumienia RSS
nieważ dane programu Ganttproject są
gromadzone w postaci plików XML, więc <rss>
korzystanie z nich w skryptach PHP jest <channel>
<title>
całkiem proste.
Tytuł strumienia wiadomości
Każdemu wykresowi przypisany </title>
jest jeden plik XML, w którym zbierane (...)
są dotyczące go dane, takie jak np. ko- <item>
lor przypisany do każdego zadania. Ko- <title>
Tytuł pierwszej wiadomości
rzeniem (rootem) tego pliku jest element
</title>
o nazwie <project>. Jego atrybuty za- <description>
wierają różne informacje, takie jak da- Opis pierwszej wiadomości
ta ostatniego odczytu czy wersja pliku. </description>
Podrzędne wobec <project> elementy (...)
</item>
pierwszego poziomu stanowią kategorie
<item>
informacyjne: kalendarze, zadania, za- (...)
soby, przypisanie zasobów, zwolnienia, </item>
role i różne kategorie ustawień. Elemen- (...)
ty drugiego i następnych poziomów za- </channel>
</rss>
wierają informacje związane z każdą ka-
tegorią. Listing 8. Odczyt strumienia wiadomości RSS przy użyciu SimpleXML

Odczytujemy wykres Gantta z $rss = simplexml_load_file('http://rss.freshmeat.net/freshmeat/feeds/


fm-releases-global');
programu Ganttproject
Aby odczytać dokument programu Gant- echo '<h1>'.$rss->channel->title.'</h1>';
tproject, napiszemy niewielki skrypt
działający w linii poleceń i korzystają- foreach ($rss->channel->item as $item) {
cy z SimpleXML (Listing 10). Odczyta echo '<h3>' . $item->date . ' : ' . $item->title . '</h3>';
echo '<p>' . $item->description . '</p>';
on wszystkie zadania zawarte w pliku,
}
którego nazwę podajemy jako parametr
w linii poleceń i wyświetli informacje o

66 www.phpsolmag.org PHP Solutions Nr 5/2006


Dla zaawansowanych

cji wbudowanej PHP file_put_contents()


oraz metody asXml() obiektu $project,
który reprezentuje nasz dokument XML-
owy. Tak, jak w poprzednich przykładach,
dzięki zastosowaniu SimpleXML ograni-
czyliśmy zestaw czynności do niezbęd-
nych, unikając pisania dodatkowego ko-
du, które miałoby miejsce np. w przypad-
ku DOM.

Korzystamy z
Rysunek 5. Dokument tekstowy OpenOffice.org Writer generowany przez skrypt napi- dokumentów
sany w PHP OpenOffice.org za
zdefiniowanego zadania. Ponieważ kolej- nowego zadania (które umieścimy na tym pomocą SAX
ny identyfikator jest zawsze o 1 większy samym poziomie, co tests) będzie Base. OpenOffice.org to opensourcowy pa-
od poprzedniego, wystarczy znaleźć naj- Teraz dodamy nowe zadanie przy uży- kiet biurowy o ciągle rosnącej popular-
większe ID. W tym celu sporządzimy naj- ciu metody addChild(), która jest dostęp- ności. Korzystają z niego m.in. firmy, in-
pierw listę wszystkich zadań korzystając z na dla każdego węzła drzewa dokumentu stytucje publiczne, organizacje pozarzą-
xpath, a następnie odnajdziemy najwięk- w SimpleXML oraz nadamy mu atrybuty: dowe i osoby prywatne z całego świata.
szy identyfikator używając funkcji max() indentyfikator, nazwę, datę rozpoczęcia, Wszystkie formaty dokumentów OpenOf-
w pętli foreach. Po wykonaniu tych czyn- okres trwania i parametry wyświetlania fice.org (edytora, arkusza kalkulacyjne-
ności musimy wydłużyć czas trwania za- (m.in. kolor). Na koniec zapiszemy nasze go, programu do tworzenia prezentacji i
dania macierzystego, którym dla naszego modyfikacje w pliku korzystając z funk- innych są oparte na XML-u. W wersji 2
tego pakietu wprowadzono format Open-
Listing 9. Zawartość dokumentu XML programu Ganttproject Document, którym się posłużymy w na-
szym artykule.
<?xml version="1.0" encoding="UTF-8"?>
Kompozycja dokumentu
<project name="OpenOfficeManager"
company="Anaska" webLink="http://www.anaska.com" (...)>
OpenOffice.org
<description/> Każdy dokument OpenOffice.org to w rze-
<view zooming-state="default:3"/> czywistości skompresowane archiwum za-
<calendars> wierające pliki XML. Przykładowo, archi-
<!-- właściwości ogólne kalendarza (dni wolne, etc.) --> wum pliku example.odt utworzonego w
</calendars>
OO Writer (edytorze tekstu) będzie za-
<tasks color="#8cb6ce"> wierało tekst oraz style i obrazy osadzo-
<taskproperties> ne w tekście.
<!-- deklaracja typów zadań --> Głównym plikiem tego archiwum, le-
</taskproperties> żącym w jego katalogu głównym jest plik
content.xml, którego przykładową zawar-
<task id="0" name="kernel" (...) >
<task id="1" name="modelisation" (...)> tość (fragment) prezentujemy na Listingu
<depend id="2" type="2" difference="0" hardness="Strong"/> 13. Zawiera on dane naszego dokumen-
</task> tu i kilka deklaracji stylów. Zdefiniowane w
dokumencie style tekstowe są zapisane w
<task id="2" name="development" (...)>
pliku styles.xml.
<depend id="3" type="2" difference="0" hardness="Strong"/>
</task> My pokażemy sposób tworzenia tytu-
<task id="3" (...)/> łu i akapitu.
</task>
</tasks> Odczyt i modyfikacja dokumentu
<resources/>
OpenOffice.org
<allocations/> Zanim będziemy mogli odczytać lub zmo-
<vacations/> dyfikować dokument OpenOffice (test.odt),
musimy wydobyć z archiwum plik con-
<taskdisplaycolumns> tent.xml (Rysunek 3). W tym celu użyjemy
(...)
biblioteki pclzip , którą pobierzemy spod
</taskdisplaycolumns>
adresu http://www.phpconcept.net/pclzip/.
<previous/> Plik content.xml, którego fragment już
<roles roleset-name="Default"/> przedstawiliśmy na Listingu 13 składa się
</project> ze znacznika korzenia office:document
oraz pierwszego poziomu hierarchii XML

PHP Solutions Nr 5/2006 www.phpsolmag.org 67


Dla zaawansowanych

przedstawiającego kategorie zawartości. text korzystając ze znanej już nam meto- sję dokumentu XML, którą zapiszemy
W kategorii office:body zawarte są dane dy addChild(). Dla każdego węzła poda- w pliku content.xml przy użyciu funkcji
naszego dokumentu. jemy jego nazwę (text:p), zawartość (No- file_put_contents().
Odczytamy plik content.xml przy uży- wy tytuł i Nowy akapit) i przestrzeń nazw Pozostała nam już tylko czwarta
ciu SAX oraz zmodyfikujemy go korzysta- (text). Po dodaniu obu węzłów dodamy część: usunięcie starego pliku content.xml
jąc z SimpleXML i dodając tytuł i akapit. akapitowi atrybut text:style-name, który z archiwum test.odt przy pomocy pclzip i
Gotowy skrypt przedstawiamy na Listingu określa styl (u nas Standard). zastąpienie go zmodyfikowanym przez
14 – składa się on z 4 części: Przejdźmy teraz do zapisywania pli- nas oraz skasowanie pliku content.xml z
ku. Tak jak poprzednio, użyjemy meto- bieżącego katalogu (unlink()). Oczywi-
• wydobycie pliku content.xml zawiera- dy asXml() aby uzyskać tekstową wer- ście, tę ostatnią funkcję musimy wywołać
jącego zawartość naszego dokumen-
tu z archiwum, Listing 10. Odczyt listy zadań z dokumentu aplikacji Ganttproject
• odczyt pliku content.xml za pomocą
SAX, // Pobieranie pliku do przetwarzania
• dodanie tytułu i akapitu przy użyciu $file = $_SERVER['argv'][1];
// Otwieranie pliku przy użyciu SimpleXML
SimpleXML,
$xml = simplexml_load_file($file);
• zapis pliku na dysk. // Funkcja rekurencyjna odczytująca zadania
function display_tasks(&$xml, $level = 0) {
Zaczniemy od dołączenia biblioteki pcl- $prefix = str_repeat('|', $level).'+-> ';
zip (pclzip.lib.php) i utworzenia obiek- foreach ($xml->task as $task) {
echo $task['start'].' ';
tu $zip klasy PclZip, któremu jako pa-
echo ($task['duration'] > 9 ? '' : '0').$task['duration'];
rametr konstruktora przekazujemy na- echo ' day(s) '.$prefix." ".utf8_decode($task['name'])."\n";
zwę archiwum, które chcemy rozpako- display_tasks($task, $level + 1);
wać (test.odt). Następnie korzystając z }
metody listContent() tego obiektu prze- }
// Wywołanie funkcji rekurencyjnej display_tasks()
szukamy listę plików zawartych w archi-
display_tasks($xml->tasks);
wum, aż natrafimy na content.xml. Je-
go ekstrakcji dokonamy używając metody Listing 11. Zmiana nazwy pierwszego zadania w pliku programu Ganttproject
extractByIndex() obiektu $zip. Mając plik (kernel staje się Base)
content.xml poza archiwum, skorzystamy
$project = simplexml_load_file('openoffice.xml');
z PHP-owej funkcji file_get_contents(),
$project->tasks->task[0]['name'] = 'Base';
aby go załadować. file_put_contents('openoffice.xml', $project->asXml());
Czas na przetwarzanie odczytanego
źródła XML przy pomocy SAX. Dużą za- Listing 12. Dodanie zadania w pliku programu Ganttproject za pomocą
letą tego rozwiązania jest jego szybkość SimpleXML
i prostota, co zdążyliśmy już częściowo $project = simplexml_load_file('openoffice.xml');
poznać. Tworzymy więc parser SAX przy
użyciu xml_parser_create() i przy pomo- // Poszukiwanie identyfikatora największego zadania
cy funkcji rozszerzenia SAX xml_parse_ $xpath = $project->xpath("//task/@id");
$maxId = 0;
into_struct() przekształcimy odczyta-
foreach ($xpath as $item) {
ną zawartość pliku XML-owego w tabli- $maxId = max ((int) $item['id'], $maxId);
cę. Następnie użyjemy na tej tablicy pę- }
tli foreach, aby wyekstrahować z niej ty-
tuły i akapity. // Modyfikacja trwania zadania macierzystego
$project->tasks->task[0]['duration'] = 11;
Teraz utworzymy obiekt $xml, repre-
zentujący ten dokument w SimpleXML. W // Dodanie nowego zadania
tym celu skorzystamy z funkcji simplexml_ $newTask = $project->tasks->task[0]->addChild('task');
load_file() i załadujemy ponownie ten $newTask['id'] = $maxId + 1;
sam plik (content.xml). $newTask['name'] = "preprod_tests";
$newTask['color'] = '#0066ff';
Następnie użyjemy metody xpath()
$newTask['meeting'] = 'false';
obiektu $xml, aby wydobyć węzeł office: $newTask['start'] = '2006-12-03';
text, który obejmuje zawarty w pliku tekst. $newTask['duration'] = '5';
Węzeł ten zawiera ciąg deklaracji akapi- $newTask['complete'] = '0';
tów, a pobierzemy go z pierwszego ele- $newTask['priority'] = '1';
$newTask['expand'] = 'true';
mentu tablicy zwróconej przez xpath() (o
indeksie 0). // Zapisywanie modyfikacji
Dodamy teraz nowy tytuł i akapit. file_put_contents('openoffice.xml', $project->asXml());
W tym celu musimy utworzyć dwa wę-
zły text:p potomne wobec węzła office:

68 www.phpsolmag.org PHP Solutions Nr 5/2006


Dla zaawansowanych

na samym końcu, gdy już dodamy ten plik


Listing 13. Plik content.xml obejmujący zawartość dokumentu OO Writera
do archiwum.
<office:document-content office:version="1.0">
<office:scripts/> Generujemy dokument
<!-- Specyficzne style dokumentu --> OpenOffice.org Writer na
<office:font-face-decls>(...)</office:font-face-decls> podstawie szablonu
<office:automatic-styles/>
Utworzymy teraz dokument OO Writera
<!-- Zawartość dokumentu-->
<office:body> przy użyciu szablonu, czyli istniejącego
<office:text> dokumentu, który posłuży nam jako model
<text:sequence-decls>(...)</text:sequence-decls> (trochę podobnie jak w przypadku syste-
<text:p text:style-name="Heading">Tytuł</text:p> mów szablonów HTML-owych, takich jak
<text:p text:style-name="Standard">Paragraf...</text:p>
np. Smarty). Będzie to wymagało wydoby-
</office:text>
</office:body> cia pliku content.xml z archiwum szablo-
</office:document-content> nu (u nas będzie to model.odt) i wykorzy-
stania go w celu utworzenia nowego pli-
Listing 14. Manipulacja danymi w dokumencie OpenOffice.org Writer ku content.xml, zawierającego dane, któ-
include "pclzip.lib.php";
re dodamy. Na koniec umieścimy ten no-
wy plik w archiwum będącym kopią na-
$zip = new PclZip('test.odt'); szego szablonu.
foreach ($zip->listContent() as $file) { Wbudowane do PHP rozszerzenie
if ($file['filename'] == 'content.xml') {
ZIP nie pozwala niestety na zapisanie pli-
$zip->extractByIndex($file['index']);
$xml_txt = file_get_contents('content.xml');
ku: będziemy więc wywoływać programy
break; zip oraz unzip korzystając z funkcji shell_
} exec(). Nic nie stoi również na przeszko-
} dzie, abyśmy użyli biblioteki pclzip, którą
poznaliśmy w poprzednim przykładzie.
if (!isset($xml_txt)){
exit;
Naszemu szablonowi (Rysunek 4)
} nadamy nazwę model.odt, a następnie
użyjemy systemu szablonów Smarty (http:/
// Parsing dokumentu przy użyciu SAX i wyświetlanie danych /smarty.php.net) w celu wygenerowania do-
$p = xml_parser_create();
kumentu ze strumienia RSS (Rysunek 5).
xml_parse_into_struct($p, $xml_txt, $vals, $index);
xml_parser_free($p);
Nasz przykład (Listing 15) jest prze-
znaczony dla PHP5; wymaga również
foreach ($vals as $value) { dostępu do plików zip (używanego w ce-
if (isset($value['value'])) { lu dodania pliku do archiwum) i unzip (słu-
switch($value['tag']) {
żącego do wydobycia pliku z archiwum) w
case 'TEXT:H' :
echo '<h1>'.utf8_decode($value['value'])."</h1>\n";
systemie operacyjnym.
break; Główną częścią naszego skryptu ge-
nerującego plik OpenOffice Writera na
case 'TEXT:P' : podstawie szablonu jest klasa OpenOffice,
echo '<p>'.utf8_decode($value['value'])."</p>\n";
która dziedziczy ze Smarty, co pozwala
break;
}
nam używać metod i atrybutów tej dru-
} giej. W rzeczywistości, klasa OpenOffi-
} ce będzie więc stanowiła API posiadają-
ce silnik szablonów do generowania do-
// Dodanie tytułu i akapitu za pomocą SimpleXML
kumentów OpenOffice. Przykładowo,
$xml = simplexml_load_file('content.xml');
$contentPart = $xml->xpath('/office:document-content/office:body/office:text/');
metoda assign() należy do Smarty, zaś
$contentPart = $contentPart[0]; mergeAndRight() do OpenOffice.
W ramach waszych dalszych działań
$title = $contentPart->addChild('text:p', 'New title', 'text'); i rozwinięć, możecie umieścić klasę Ope-
$title['text:style-name'] = 'Heading';
nOffice w osobnym pliku, aby móc ją do-
$paragraph = $contentPart->addChild('text:p', 'New paragraph...', 'text');
$paragraph['text:style-name'] = 'Standard';
dać do innych przetwarzań. Korzystanie
file_put_contents('content.xml', $xml->asXml()); z tej klasy okazuje się bardzo proste, jak
tego dowodzą cztery linijki z Listingu 15.
// Zapisywanie modyfikacji Musimy tylko po prostu instancjonować
$zip->deleteByIndex($file['index']);
nowy obiekt podając w parametrze na-
$zip->add('content.xml');
unlink('content.xml');
zwę pliku OpenOffice Writer, który zamie-
rzamy stworzyć. Następnie, dokonujemy
przypisań i zakańczamy przez wywołanie

PHP Solutions Nr 5/2006 www.phpsolmag.org 69


Dla zaawansowanych

jeszcze zapisać otrzymany dokument ja-


Listing 15. Tworzenie pliku edytora OpenOffice.org Writer w oparciu o szablon ko content.xml oraz dodać ten plik do ar-
include 'smarty/Smarty.class.php';
chiwum test.odt, zastępując nim poprzed-
nio istniejący.
class OpenOffice extends Smarty { Pobierzemy teraz dane ze strumie-
const ZIP_EXECUTABLE = 'zip'; nia RSS (z portalu Yahoo!), wykorzystu-
const UNZIP_EXECUTABLE = 'unzip';
jąc do tego, jak poprzednio, SimpleXML.
private $vars = array();
Pozostało jeszcze utworzenie obiektu
private $OoFile = ''; $oo klasy OpenOffice, któremu przez para-
private $OoModel = ''; metr konstruktora przekażemy nazwę pli-
ku test.odt. Następnie korzystając z me-
public function __construct($oo_file, $oo_model = 'model.odt') {
tod klasy Smarty przypiszemy zmiennym
$this->OoFile = $oo_file;
news i name załadowanego szablonu odpo-
$this->OoModel = $oo_model;
$this->left_delimiter = '{{'; wiednio treść odczytanych newsów (któ-
$this->right_delimiter = '}}'; re przetworzymy w pętli foreach) oraz
$this->Smarty(); nazwisko twórcy dokumentu (Guillaume
$this->template_dir = dirname(__FILE__).'/';
Ponçon). Na koniec wywołamy metodę
$this->compile_dir = $this->template_dir;
mergeAndWrite() obiektu $oo, aby zapisać
$this->config_dir = $this->template_dir;
$this->cache_dir = $this->template_dir; zmieniony plik. Nasza praca z dokumenta-
$this->caching = false; mi OpenOffice.org jest zakończona.
}

// Tworzy dokument, łącząc w całość przesłane dane i szablon


Podsumowanie
public function mergeAndWrite() {
Pokazaliśmy, jak łatwo i bezproblemo-
copy($this->OoModel, $this->OoFile); wo korzystać z XML-a w PHP przy użyciu
shell_exec(self::UNZIP_EXECUTABLE.' '.$this->OoFile.' content.xml'); technik SAX, DOM i SimpleXML. Ta ostat-
$content = $this->fetch('content.xml'); nia metoda okazała się szczególnie war-
$this->clear_compiled_tpl('content.xml');
ta uwagi, ze względu na połączenie sze-
file_put_contents('content.xml', $content);
shell_exec(self::ZIP_EXECUTABLE.' '.$this->OoFile.' -mq content.xml');
rokich możliwości (włącznie z wykorzysta-
} niem XPath) z prostotą tworzenia oparte-
} go na niej kodu. Warto pamiętać, że cza-
sami najlepsze może się okazać połą-
// Pobieranie danych ze strumienia RSS
czenie kilku technik, jak to ukazaliśmy na
$news = array();
$xml_news = simplexml_load_file('http://rss.news.yahoo.com/rss/world');
przykładzie aplikacji przetwarzającej do-
foreach ($xml_news->channel->item as $item) { kumenty edytora OpenOffice.org Writer.
$news[] = (string) $item->title; Bądźmy również świadomi, że możliwo-
} ści pracy skryptów PHP z dokumentami
XML-owymi nie kończą się na SAX, DOM
// utworzenie i użycie obiektu OpenOffice
// do stworzenia nowego dokumentu "test.odt".
i SimpleXML: istnieją i wciąż powstają no-
$oo = new OpenOffice('test.odt'); we rozszerzenia i biblioteki, np. wysoko-
$oo->assign('news', $news); poziomowe API do łatwiejszego przetwa-
$oo->assign('name', "Guillaume Ponçon"); rzania wiadomości RSS czy danych pa-
$oo->mergeAndWrite();
kietu biurowego, czy też pakiety ułatwia-
jące tworzenie dokumentów XML według
metody mergeAndWrite , która stworzy do- której zadaniem będzie tworzenie doku- wzorca. W każdym przypadku, dobre opa-
kument, łącząc w całość dane zapisane i mentu, w którym połączymy dane otrzy- nowanie obsługi XML-a utoruje nam dro-
model model.odt. mane z pliku podanego w $this->OoFile gę do tworzenia nowoczesnych aplika-
Na początku tej klasy zdeklarujemy oraz szablonu. Rozpoczniemy ją kopiu- cji, które mogą współpracować z przyjęty-
stałe ZIP_EXECUTABLE i UNZIP_EXECUTABLE, jąc archiwum szablonu (model.odt) jako mi standardami, nie będziemy więc skaza-
którym przypiszemy nazwy programów zip test.odt. Następnie wydobędziemy z te- ni na izolację naszych rozwiązań od resz-
i unzip, co pozwala je zmienić wedle po- go archiwum plik content.xml i potraktuje- ty świata. n
trzeb (np. w ramach dostosowywania do my go jako szablon używając w tym ce-
innego niż Linux systemu operacyjnego). lu odziedziczonej po klasie Smarty() me-
Następnie zainicjujemy zmienne prywatne tody fetch(), która zwraca skompilowa-
$OoFile i $OoModel i przejdziemy do two- ny dokument powstający przy użyciu sza- O autorze
rzenia konstruktora, w którym zdefiniuje- blonu. Otrzymany dokument przypiszemy Guillaume Ponçon jest architektem
my parametry (nasze dwie zmienne i usta- do zmiennej $content, a następnie zwol- PHP i autorem Best practices PHP 5,
wienia Smarty'ego). Ostatnim krokiem bu- nimy zasób zajmowany przez skompilo- francuskiej książki wydanej nakładem
wydawnictwa Eyrolles.
dowania naszej klasy będzie utworze- waną wersję szablonu używając metody
nie metody publicznej mergeAndWrite(), $this->clear_compiled_tpl(). Pozostało

70 www.phpsolmag.org PHP Solutions Nr 5/2006


Projekty

Własne Google Video,


czyli video streaming
w PHP
Stopień trudności: lll
Rafał Malinowski

Zastanawiałeś się, jak działa odtwarzanie filmów


z poziomu WWW? Podoba Ci się Google Video?
Poznaj video streaming od kuchni i stwórz
własną, webową galerię filmów. Wystarczy
podstawowa znajomość PHP. To wszystko!

P
ublikacja filmów w Internecie w • część druga – rozbudujemy przykład,
oparciu o technologię Flash spro- który został opisany w pierwszej czę-
wadza się do wykonania trzech ści i pokażemy, jak można zautoma-
czynności: tyzować opisane czynności orazi jak
W SIECI uniezależnić się od systemu opera-
• konwersji filmu do formatu Flash Vi- cyjnego, na którym będzie działać
deo (FLV) obsługiwanego przez Flash galeria,
l http://ffmpeg.mplayerhq.hu/ Playera, • część trzecia – wykorzystamy nabytą
– FFMPEG • stworzenia odtwarzacza (Media Play- w części drugiej wiedzę i kod do stwo-
l http://ffdshow.faireal.net/
mirror/ffmpeg/ – FFMPEG
era), który pobierze z serwera film i rzenia ostatecznej wersji serwisu – w
(windows binary) zaprezentuje go klientowi w przeglą- pełni funkcjonalnej i w 100% uniwer-
http://lame.sourceforge.net/
darce, salnej galerii filmów.
l

– LAME
l http://klaus.geekserver.net/ • publikacji strony internetowej, w której
libflv/ – LIBFLV – program osadzimy odtwarzacz.
do konwersji plików FLV Co należy wiedzieć...
l http://www.geovid.com/ Przydatna będzie podstawowa znajo-
–GEO-VID Video to Flash Aby opisać powyższe zadania, artykuł po- mość PHP.
converter
l http://www.rivavx.com/
dzieliliśmy na trzy części:
?encoder – Riva FVL Enco- Co obiecujemy...
der
• część pierwsza – tzw. szybki start, bez Z artykułu dowiesz się, jak stworzyć
l http://ffmpeg-php.sourcefor-
ge.net/ – PHP-FFMPEG zbędnej teorii pokażemy, jak opubliko- własne Google Video – kompletną gale-
http://www.php.net/ming rię filmów z flashowym Media Playerem
l
wać w internecie pojedynczy film. Bę-
– MING – moduł PHP do na WWW pozwalającą na upload filmów
tworzenia SWF-ów dzie to prosty przykład tylko dla plat- i ich strumieniowe odtwarzanie.
formy Windows,

72 www.phpsolmag.org PHP Solutions Nr 5/2006


Video streaming Projekty

Szybki start
Mamy do wykonania trzy kroki:

• krok 1 – konwersja pliku wideo,


• krok 2 – wykonanie wideoodtwarzacza
we Flashu,
• krok 3 – osadzenie playera w HTML-u.

W zależności od systemu operacyjne-


go, jak i od tego, czy chcemy korzystać
z darmowych, czy też komercyjnych
rozwiązań, każdy z tych kroków może-
my wykonać na wiele sposobów. Na po-
czątku wykonamy prezentację nasta-
wioną na szybkie osiągnięcie celu. Uży-
jemy do tego programów komercyjnych
(wersje trialowe) dostępnych niestety
tylko dla platformy Windows. Wszyst-
ko zostanie pokazane krok po kroku.
W następnej części artykułu przedsta-
wimy rozwiązania niezależne od syste-
Rysunek 2. Macromedia Flash – obszar roboczy aplikacji
mu operacyjnego, wymagające jednak
większej pracy. dać na Rysunku 1, przy pomocy Riva FLV Dodatkowo program ten pozwala nam wy-
Encoder możemy: ciągnąć z filmu pojedynczą klatkę i zapi-
Konwersja pliku wideo. sać ją w postaci pliku graficznego JPEG
Flash Player pracuje z plikami wideo zapi- • ustawić rozdzielczość filmu wyjściowe- – zakładka Image (5).
sanymi w formacie Flash Video (FLV). Na go (1), Aby dokonać konwersji, ustawiamy
początku więc będziemy musieli przekon- • ustawić parametry jakościowe/ ścieżkę do pliku wejściowego, następnie
wertować nasz film do FLV. Użyjemy do kompresji pliku (2), do pliku wyjściowego i klikamy na przycisk
tego celu programu Riva FLV Encoder • odpowiednio przyciąć obraz filmu (3), FLV Encode (6). Zapisujemy pierwszy plik
(http://www.rivavx.com/?encoder). Jak wi- • ustawić parametry dźwięku (4). FLV. Nazwijmy go videotest.flv.

Flashowy wideoodtwarzacz
Macromedia Flash – program, którego
użyjemy do stworzenia pierwszego klipu
flashowego, dysponuje eleganckim Media
Playerem. Macromedia Flash jest odpłat-
ny, ale dla potrzeb prezentacji wykorzy-
stamy go w wersji trial, do ściągnięcia ze
strony: http://www.adobe.com/products/
flash/flashpro/ .
Po zainstalowaniu mamy możliwość
zrobienia efektownego flashowego wide-
oodtwarzacza przy minimalnym nakładzie
pracy. Uruchamiamy Flasha (Rysunek 2),
tworzymy nowy dokument np. o nazwie
testplayer.fla i przenosimy na scenę (2)
(ang. Stage, obszar roboczy dokumentu)
komponent MediaPlayback (1). Następnie
uruchamiamy Component Inspektor (3),
gdzie musimy wpisać adres URL do wcze-
śniej przygotowanego pliku FLV (4). URL
może mięć postać ścieżki względnej lub
bezwzględnej (rozpoczynającej się od fi-
le:// lub http://). My dla uproszczenia ustal-
my, że zarówno Media Player jak i film bę-
Rysunek 1. Riva FLV Encoder – konwerter plików wideo do formatu FLV dą znajdować się w tym samym folderze,

PHP Solutions Nr 5/2006 www.phpsolmag.org 73


Projekty Video streaming

dlatego wystarczy że wpiszemy nazwę pli-


ku wideo – videotest.flv..

Publikacja Movie Clipa


Na koniec musimy wykonać plik HTML,
w którym osadzimy Movie Player tak, by
mogli go oglądać inni. Całą resztą, czy-
li ściąganiem pliku wideo i odtwarzaniem
go w przeglądarce zajmie się sam Movie
Player. Również w tym przypadku Ma-
cromedia Flash bardzo ułatwia nam za-
danie. Wybieramy z menu polecenie File
-> Publish Settings (Rysunek 3). W no-
wym oknie, w zakładce Flash ustawiamy
parametry publikacji i wybieramy wer-
sję 7 Flash Playera (1). Klikamy na przy-
cisk Publish i w katalogu, w którym zapi-
saliśmy plik testplayer.fla, pojawiają się
dwa nowe pliki: testplayer.swf i testplay-
er.html. Jeśli pliki te wraz z plikiem wi-
deo umieścimy na serwerze, to po otwo-
rzeniu strony testplayer.html powinniśmy
zobaczyć w przeglądarce internetowej
opublikowany film (Rysunek 4).

I gotowe!
Tym sposobem szybko kończymy wstęp-
ną prezentację. Wykonana strona widocz-
na będzie we wszystkich przeglądarkach
internetowych posiadających zainstalowa-
ny plugin Flash Player 7 lub nowszy. Jak
widać, wykonana przez nas publikacja pli-
ków wideo, jest zadaniem prostym. Nie-
stety zastosowane rozwiązania nie speł-
niają naszych wymogów. Po pierwsze, są
komercyjne (dodatkowo sporo kosztują) i
są dostępne tylko dla platformy Windows,
a po drugie, wszystkie czynności musieli-
Rysunek 3. Macromedia Flash – Publish Settings śmy wykonać ręcznie. Teraz postaramy
się zautomatyzować proces konwersji fil-
mów, postawimy większy nacisk na uni-
wersalność, uniezależniając się od syste-
mu operacyjnego i do tego skorzystamy
jedynie z darmowego oprogramowania.

Czas na rozbudowę
Jeśli chcemy dać użytkownikom możliwość
uploadu własnych plików wideo, musimy
zadbać o automatyczną konwersję filmów.
Standard Flash Video (FLV) jest formatem
dla plików wideo wymyślonym przez firmę
Macromedia pozwalającym na tzw. stru-
mieniowe pobieranie (ang. streaming) fil-
mów z serwera za pomocą Macromedia
Flash Playera (wersje od 6 w górę). Stru-
mieniowe, czyli takie, które pozwala na po-
kazywanie filmu podczas jego ściągania.
Rysunek 4. Komponent MediaPlayback firmy Macromedia prezentujący film w prze- Flash Player dysponuje również innymi me-
glądarce internetowej todami dostarczania filmów do przeglądarki,

74 www.phpsolmag.org PHP Solutions Nr 5/2006


Video streaming Projekty

FFMPEG – konwersja plików


wideo
Jeśli zdecydujemy się na własnoręczną
kompilację źródeł programu FFMPEG na
serwerze, pamiętajmy o opcji -mp3lame.
Umożliwi ona konwersję ścieżek audio pli-
ków wideo do formatu MP3. Jeśli tego nie
zrobimy, nasze wideo na etapie odtwarza-
nia przez Flash Player zostanie pozbawio-
ne dźwięku (w przypadku, gdy podczas
kompilacji otrzymamy komunikat o braku
wymaganych bibliotek, zalecamy pobra-
nie źródeł LAME). Przykładowe polecenie
configure to:

Rysunek 5. phpinfo() – informacja o zainstalowanej wersji modułów MING i FFMPEG ./configure


--enable-mp3lame
np. pobieranie progresywne (ang. progresi- sji dwa programy Open Source: FFMPEG --enable-gpl
ve) lub zapisywanie pliku wideo wewnątrz (http://ffmpeg.sourceforge.net/index.php) --enable-memalign-hack
animacji Flash. Mają one swoje zalety, ale oraz LAME (http://lame.sourceforge.net/).
w przypadku dłuższych filmów najlepiej za- Pierwszy z nich to wszechstronny konwer- Lista wspieranych przez FFMPEG forma-
stosować streaming. ter dla plików wideo i audio, a drugi tylko dla tów, a tym samym możliwości konwer-
W pierwszym przykładzie do konwersji audio pozwalający na zapis w formacie MP3 sji jest długa. Komenda ffmpeg -formats
plików wideo użyliśmy programu Riva FLV (MP3 jest jak na razie jedynym odczytywa- prezentują ich listę, czyli pokazuje jakich
Encoder, który wykorzystuje do konwer- nym przez Flash Player formatem audio). konwersji możemy dokonać przy pomocy
zainstalowanego konwertera.
Listing 1. Inicjacja Movie Clipa Aby dokonać najprostszej konwersji z
pliku AVI do FLV, użyjmy komendy:
$movie =new SWFMovie(7);
$movie->setDimension(200,100); ffmpeg -i tmp\test.avi
$movie->setBackground(0xff,0xcc,0x0);
tmp\test.flv
$movie->setRate(30);
$movie->nextFrame();
$movie->save("empty.swf"); Program rozpozna rozszerzenia plików i
dopasuje odpowiednie formaty konwer-
Listing 2. Tworzymy przyciski dla Movie Clipa sji. Załaduje też domyślne parametry dla
$button = new SWFButton();
tej operacji.
$flags = (SWFBUTTON_UP | SWFBUTTON_HIT | SWFBUTTON_OVER | SWFBUTTON_DOWN); Przykładowe polecenie, które (okre-
ślając wielkość filmu i jakość dźwięku) do-
$button->addShape(getButtonShape(),$flags); kona konwersji filmu w formacie AVI do
FLV, to:
$action = new SWFAction($actionscript);
$button->addAction($action, SWFBUTTON_MOUSEDOWN);
$button_ref = $this->movie->add($button); ffmpeg -y -i tmp\test.avi
$button_ref->moveTo($x, $y); -ar 22050 -acodec mp3
-ab 32 -f flv -s 160x120
Listing 3. Programujemy kształt dla przycisków tmp\test.flv

function getButtonShape() {
$img = new SWFBitmap(fopen("image.dbl", "rb")); gdzie:
$w = $img->getWidth();;
$h = $img->getHeight(); • -ar rate – ustawia samplingrate w
Hz,
$shape = new SWFShape();
$shape->setLine(1,255,255,255); • -acodec codec – wymusza uży-
$fill = $shape->addFill($img, SWFFILL_TILED_BITMAP); cie wskazanego typu kodowania dla
$shape->setRightFill($fill); ścieżki audio,
$shape->drawLine($w,0); • -ab bitrate – ustawia bitrate w kbit/s,
$shape->drawLine(0,$h);
• -s size – ustawia rozmiar klatki WxH,
$shape->drawLine(-$w,0);
$shape->drawLine(0,-$h); • -f fmt – wymusza format kodowania
return $shape; pliku wyjściowego,
} • -y – nagranie pliku bez potwierdze-
nia.

PHP Solutions Nr 5/2006 www.phpsolmag.org 75


Projekty Video streaming

Polecenie, które wyciągnie pojedynczą nam metoda nextFrame(). Wynikiem wy- pa. Warto dodać, że wszystko układa-
klatkę z trzeciej sekundy filmu formatu AVI konania kodu z Listingu 1 jest pusty Movie my w obrębie jednego frama, czyli Mo-
i zapisze ją w postaci obrazu w formacje Clip o rozmiarach 200px na 100px i czer- vie Clip tak naprawdę składać się bę-
JPG to: wonym kolorze tła. dzie z jednej klatki. Media Player, któ-
Teraz przechodzimy do ustawia- rego tworzymy, składać się będzie z
ffmpeg -i tmp\test.avi nia elementów na scenie Movie Cli- trzech przycisków (Stop, Play, Pause)
-s 320×240 -vframes 1
-ss 3 -f mjpeg test.jpg
Listing 4. Tworzymy obiekt SWFVideoStream obsługujący video streaming

gdzie: $stream = new SWFVideoStream();


$stream->setDimension($width, $height);
$item = $this->movie->add($stream);
• -ss time_offset – ustawia offset
$item->moveTo($x, $y);
(przesunięcie względem początku fil- //nazwa obiektu VideoStream na scenie
mu) w sekundach, $item->setname("my_player_vstrm");
• -vframes number – ustawia ilość klatek
wideo, jakie mają być pobrane z pliku $action_script = "
stop();
wejściowego i przekazane do konwer-
netConn = new NetConnection();
sji. netConn.connect(null);
netStream = new NetStream(netConn);
MING – tworzymy Media Player my_player_vstrm.attachVideo(netStream);
Teraz, gdy zapoznaliśmy się już z samą netStream.setBufferTime(10);
netStream.play(_level0.video_path);
konwersją plików wideo, przejdziemy do
";
zrobienia własnego flashowego Media //netStream.play(_level0.video_path);
Playera. Na liście darmowych programów, $this->movie->add(new SWFAction($action_script));
które potrafią wygenerować Flasha mamy
dwie pozycje: Listing 5. Źródło pliku testplayer.html

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">


• MTASC – darmowy, kompilator Open <head>
Source dla Action Script (AS to język <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2" />
programowania używany we Flashu) <title>testplayer</title>
</head>
• MING – napisany w C generator pli-
<body bgcolor="#ffffff">
ków SWF.
<object
Obydwa programy mają bardzo du- classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
że możliwości. My wybierzemy MING-a, codebase="http://fpdownload.macromedia.com/pub/
shockwave/cabs/flash/swflash.cab#version=8,0,0,0"
bo posiada swoje rozszerzenie dla PHP.
width="550"
Dzięki temu możemy z poziomu PHP wy- height="400"
generować flashowego Movie Clipa. Wię- id="testplayer"
cej o MINGU można poczytać w numerze align="middle">
3/2005 Search Engine Optimization, w ar- <param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="testplayer.swf" />
tykule Flash w PHP, czyli MING w akcji.
<param name="quality" value="high" />
Po zainstalowaniu rozszerzenia MING <param name="bgcolor" value="#ffffff" />
(informacje na temat, jak to zrobić, znaj-
dziemy na stronie www.php.net/ming) <embed
przechodzimy do tworzenia pliku SWF. src="testplayer.swf"
quality="high"
Zaczniemy od stworzenia pod-
bgcolor="#ffffff"
stawowego Movie Clipa, co pokazu- width="550"
je Listing 1. Ustalamy w nim, dla jakiej height="400"
wersji Flash Playera tworzymy clip SWFMo name="testplayer"
vie($playerVersion), jakie ma mieć roz- align="middle"
allowScriptAccess="sameDomain"
miary setDimension($width, $height),
type="application/x-shockwave-flash"
dodatkowo ustalić możemy kolor tła pluginspage="http://www.macromedia.com/go/getflashplayer" />
setBackground($r,$g,$b)czy ilość klatek
wyświetlanych w ciągu sekundy setRa- </object>
te($frameRate). Zapisanie pliku odbywa
</body>
się po wywołaniu metody save($fileName)
</html>
obiektu $movie, przy czym, zanim to zrobi-
my, powinniśmy mieć na scenie minimum
jedną wygenerowaną klatkę, co zapewnia

76 www.phpsolmag.org PHP Solutions Nr 5/2006


Video streaming Projekty

oraz obszaru, w którym pokazywany To, co udało nam się przed chwilą wy- jego obszaru aktywnego. Do tego słu-
będzie film. konać, oznacza, że w przypadku, gdy ży następująca metoda $button->add
Budowę zaczniemy od stworzenia zostanie naciśnięty button (ustawia- Shape($shape,$flags), która ustala, że
przycisków. Z punktu widzenia użytkowni- liśmy akcje dla zdarzenia SWFBUTTON_ dla buttona $button aktywnym kształ-
ka, przycisk we Flashu to pewien obszar MOUSEDOWN) należy wykonać polecenie: tem dla zdarzeń $flags będzie obiekt
powierzchni o dowolnym kształcie, który myVideoPlayer.pause();. $shape. Dzięki temu, że możemy ustalić
reaguje na zachowanie myszki. Z punk- Polecenie to wykonuje metodę różne kształty obszarów dla wybranych
tu widzenia programisty przycisk to obiekt, pause() na obiekcie myVideoPlayer, zdarzeń, w przyszłości będziemy mogli
który reaguje na pewne zdarzenia wywo- o którym napiszemy za chwilę. Gdy np. zmieniać kolor przycisku, gdy naje-
łane przez kursor myszki nad zdefiniowa- określimy już, jak przycisk ma reago- dziemy na niego myszką. Na razie jed-
nym obszarem sceny. MING pozwala nam wać na dane zdarzenia, musimy okre- nak ustalimy jeden kształt dla wszyst-
zaprogramować dla przycisków następu- ślić kształt, rozmiar, wygląd i pozycję kich zdarzeń:
jące zdarzenia:

• SWFBUTTON_MOUSEUP – gdy puścimy


przycisk myszy nad zdefiniowanym
obszarem,
• SWFBUTTON_MOUSEOVER – gdy najedzie-
my kursorem na zdefiniowany obszar,
• SWFBUTTON_MOUSEOUT – gdy wyjedziemy
kursorem poza zdefiniowany obszar,
• SWFBUTTON_MOUSEDOWN – gdy naciśnie-
my przycisk myszki nad zdefiniowa-
nym obszarem,
• SWFBUTTON_DRAGOVER – gdy najedzie-
my na obszar kursorem myszki z na-
ciśniętym równocześnie przyciskiem
myszki,
• SWFBUTTON_DRAGOUT – gdy wyjedziemy
poza obszar kursorem myszki z naci-
śniętym przyciskiem myszki.

Po stworzeniu instancji przycisku $button


= new SWFButton(), dla każdego zdarze-
nia lub kombinacji zdarzeń możemy zde-
finiować pewną akcję. W tym przypadku
wykorzystamy tylko reakcję na zdarzenie
kliknięcia przycisku myszki, czyli tworzy-
my button, dla którego definiujemy pewną
akcję, gdy zaistnieje zdarzenie SWFBUTTON_
MOUSEDOWN. Takie polecenie w PHP wyglą-
da następująco: Rysunek 6. Formularz uploadu plików wideo

$button->addAction($action,
������
SWFBUTTON_MOUSEDOWN).
�����������������
Gdzie $action to akcja, jaka ma zostać ��������������������������
�������������������
wykonana po zdarzeniu. Obiekt $action ���������������
tworzymy w następujący sposób:
���������

$action = new SWFAction( �����������������


�����������������
$actionscript) �������������
������
przy czym $actionscript to kawałek ko-
du, który musimy już sami napisać w Ac- �����������������
��������������������������
tion Script, np. �������������������
���������������
$actionscript =
'myVideoPlayer.pause();'. Rysunek 7. Schemat bazy przechowującej informacje o zapisanych plikach wideo

PHP Solutions Nr 5/2006 www.phpsolmag.org 77


Projekty Video streaming

$flags = (SWFBUTTON_UP
| SWFBUTTON_HIT Listing 6. Konwersja plików wideo z poziomu PHP
| SWFBUTTON_OVER
<?php
| SWFBUTTON_DOWN); define('FFMPEG_FRAME_SIZE_PARAM','-s 160x120');
//-ar rate – ustawiamy częstotliwość próbkowania (w Hz)
W przypadku $shape mamy duże pole do define('FFMPEG_AUDIO_RATE_PARAM','-ar 22050');
popisu – możemy sobie bowiem naryso- define('MOVIE_SOUND_','160x120');
class VideoFile {
wać np. prostokąt o kolorze czerwonym
private $dbh;
lub inny, ciekawszy obrazek przycisku private $osh;
symbolizującego klawisz pauzy. W tym public function __construct(DatabaseHandler $dbh) {
drugim przypadku przyda się nam do te- $this->osh = PlatformFactory::getPlatformInstance();
go możliwość stworzenia kształtu, którego $this->dbh = $dbh;
}
tłem będzie bitmapa – możemy tu przywo-
public function uploadFile($files_name,$files_tmp_name,$size,$name,$desc) {
łać analogię do HTML-a, gdzie tworzymy $target_path = MY_MEDIA_DATA_DIR . basename($files_name);
tabelkę z tłem jako plik. Wykonanie tego // jeśli plik zostal wgrany, kopiujiemy go do katalogu z mediami
zadania wymaga stworzenia na początku if( move_uploaded_file($files_tmp_name, $target_path)) {
obiektu SWFBitmap:
} else{
throw new Exception("Powstał błąd podczas wgrywania pliku.");
$img = new SWFBitmap(
fopen("image.dbl", "rb"));. }
$this->dbh->addResource($name,$desc,$size);
Przy czym SWFBitmap() pozwala na pobra- $this->convertMovieToFlv(basename($files_name));
$this->convertMovieToImges(basename($files_name));
nie plików graficznych w formacie DBL lub
}
JPG (na stronach projektu MING dostęp-
ne jest narzędzie do konwersji plików GIF private function convertMovieToFlv($fname) {
lub PNG do DBL). Po zainicjowaniu obiek- $flvName = $this->osh->getFileBaseName($fname).'.flv';
tu $img, tworzymy nowy kształt $shape = $command = 'ffmpeg -i "'.MY_MEDIA_DATA_DIR.$fname.'"'.
' -ar 22050 -acodec mp3 -ab 32 -f flv -s 160x120 '.
new SWFShape() i ustawiamy jako jego wy-
'"'.MY_MEDIA_DATA_DIR.$flvName.'"';
pełnienie grafikę: if ($this->osh->exec($command)== 0) {
$this->dbh->addMovie($flvName );
$fill = $shape->addFill( return true;
$img, SWFFILL_TILED_BITMAP); }
else return false;
}
Następnie rysujemy prostokąt, którego
rozmiar pokrywa się z obrazkiem. Do tego private function convertMovieToImges($fname) {
celu wykorzystujemy polecenie:
if ($this->osh->isPhpFfmpegExtensionLoaded()) {
$mov = new ffmpeg_movie(MY_MEDIA_DATA_DIR.$fname);
$shape->setLine(1,255,255,255);
$duration = floatval( $mov->getDuration());
$step1 = $duration/3;
określające parametry linii, którą będzie- $step2 = $step1*2;
my obrysowywać obszar (grubość i skła- $temp = array(0,$step1,$step2);
dowe RGB koloru) oraz:
foreach ($temp as $k=>$v) {
$imgName = $this->osh->getFileBaseName($fname)."0$k.jpg";
$shape->drawLine($x,y); $command ='ffmpeg -i "'.MY_MEDIA_DATA_DIR.$fname.'" -s 160x120
-vframes 1 -f mjpeg -ss '.$v.' "'.MY_MEDIA_DATA_DIR.$imgName.'"';
które rysuje linie do punktu $x,$y, z po-
przedniego punktu, w jakim znajdował if ($this->osh->exec($command) == 0) {
$this->dbh->addImage( $imgName );
się kursor. Listing 3 pokazuje, jak wygląda
}
funkcja tworząca obrys przycisku, którego }
tłem jest obraz image.dbl }
Po wykonaniu tych operacji na sce- else {
nie będziemy już widzieli przyciski. Te- $command ='ffmpeg -i '.MY_MEDIA_DATA_DIR.$fname.' -s 160x120 -vframes 1
-f mjpeg '.MY_MEDIA_DATA_DIR.$this->osh->getFileBaseName($fname).'00.jp
raz skupimy się na odegraniu pliku vi-
g';
deo. Do tego celu wykorzystamy polece- $this->osh->exec($command);
nie $stream = new SWFVideoStream();, }
które inicjuje obiekt obsługujący video }
streaming. Następnie nadajemy obiek- }
?>
towi odpowiednie wymiary $stream-
>setDimension($width, $height); i po-

78 www.phpsolmag.org PHP Solutions Nr 5/2006


Video streaming Projekty

ka Action Script. Najpierw ustanawiamy


Listing 7. Wywołanie komendy dla różnych systemów operacyjnych połączenie:

<?PHP
class PlatformUnix extends PlatformAbstract{ netConn = new NetConnection();
public function exec($command) { netConn.connect(null);
exec($command,$return,$return_value);
return $return_value; Następnie tworzymy strumień, który bę-
}
dzie korzystał z tego połączenia:
}

?> netStream = new NetStream(netConn);

<?PHP Kolejny krok to wskazanie instancji


class PlatformWindows extends PlatformAbstract{
my_player_vstrm(SWFVideoStream) ob-
public function exec($command) {
$command = 'cmd /c ' . $command . ''; sługującej video streaming tak, aby ko-
exec($command,$return,$return_value); rzystała z zainicjowanego właśnie stru-
return $return_value; mienia:
}
}
my_player_vstrm.attachVideo(

?> netStream);

Na końcu jeszcze ustawiamy wielkość bu-


zycję $item->moveTo($x, $y); oraz Action Scriptem, który wstawimy bezpo- fora dla strumenia video, czyli ile sekund
nazwę $item->setname("my_player_ średnio w pierwszą klatkę Movie Clipa. filmu chcemy pobrać z serwera do buffo-
vstrm");, którą posługiwać się będzie- Klasa SWFVideoStream, której in- ra, zanim zaczniemy odgrywać film, a na-
my przy sterowaniu przebiegiem odtwa- stancję o nazwie $stream tworzymy na stępnie możemy wskazać, jaki plik wideo
rzania filmu. Na koniec musimy jeszcze Listingu 4, zajmuje się obsługą strumieni ma zostać wgrany przez Media Player:
zainicjować na scenie proces wgrywa- we Flashu. Aby otworzyć taki strumień,
nia pliku wideo. Zrobimy to już samym wywołujemy odpowiednie metody języ- netStream.play(_level0.video_path);

Zauważmy, że do rozpoczęcia proce-


su wgrywania pliku FLV wykorzystujemy
zmienną o nazwie _level0.video_path.
Zmienną tą będziemy wykorzystywać do
przesyłania do wnętrza Movie Clipa na-
zwy pliku video, który chcemy odegrać na
scenie.

Parametryzowanie SWF-a
Przyjrzyjmy się przykładowi, który stwo-
rzyliśmy na samym początku niniejsze-
go artykułu. Warto obejrzeć sobie plik te-
stplayer.html, który po odpowiednim sfor-
matowaniu wygląda mniej więcej jak na
Listingu 5.
Jest to standardowy sposób na wsta-
wienie pliku SWF w ciało strony interne-
towej. Widzimy tu tagi <object> i <embed>
opisujące zmienne środowiskowe dla pu-
blikowanego Movie Clipa, w tym przypad-
ku pliku testplayer.swf.
Zauważmy, że parametry i ich warto-
ści dublują się. Wynika to z braku kom-
patybilności przeglądarek. Tag <embed>
przeznaczony jest dla przeglądarek opar-
tych o engine Gecko (Netscape, Mozilla) i
dla Macintosh Internet Explorer, natomiast
tag <object> przeznaczony jest dla prze-
glądarek wspierających ActiveX. Aby prze-
Rysunek 8. Galeria zdjęć z klatek wyciętych z uploadowanych filmów kazać jakąś wartość do Movie Clipa, mo-

PHP Solutions Nr 5/2006 www.phpsolmag.org 79


Projekty Video streaming

żemy rozwinąć nazwę pliku SWF stosując


metodę, jaką wykorzystujemy przy przesy-
łaniu danych formularza w URL-u (używa-
jąc metody GET).
Jeśli zatem chcemy przekazać do Mo-
vie Clipa pojedynczą zmienną o nazwie
np. video_path i nadać jej wartość np.
test.flv to napiszemy:

testplayer.swf?
video_path=test.flv

Czyli wnętrze tagów <embed> i <object>


zmieniamy odpowiednio na:

src=
"testplayer.swf?video_path=test.flv"
<param name="movie"
value="testplayer.swf?
video_path=test.flv" />

Od tej pory w środowisku Movie Clipa do-


stępna będzie zmienna video_path, (któ-
rą wykorzystaliśmy już podczas tworze-
nia pliku SWF), o wartości test.flv. W ten
sposób będziemy mogli przekazać Me-
dia Playerowi informację o filmie, który ma
być ściągnięty i odegrany, bez potrzeby
tworzenia nowego pliku SWF dla każde-
Rysunek 9. Stworzony przy pomocy MING-a Media Player odgrywa film Robot.flv
go nowego filmu.
Po kliknięciu na przycisk Upload File, tworzenie samego filmu, co widać na Ry-
Tworzymy galerię przechodzimy do konwersji i generowania sunku 9.
filmów klatek, do czego użyjemy polecenia PHP:
Wyposażeni we własny Media Player i za- exec($cmd), które wywołuje zewnętrzny Podsumowanie
instalowany konwerter plików video FFM- program (jednak polecenie to często nie W artykule przedstawiliśmy sposób stwo-
PEG możemy przejść do tworzenia serwi- jest zalecene do stosowania w środowi- rzenia własnej galerii video w oparciu o
su internetowego. Nasz portal powinien: skach produkcyjnych). rozwiązania komercyjne oraz darmowe.
Wywołanie komendy w zależności od Pokazaliśmy ręczny sposób przeprowa-
• umożliwiać wgrywanie przez użytkow- systemu operacyjnego wygląda jak na Li- dzenia streamingu video oraz metody au-
ników nowych plików wideo, stingu 7. tomatyzacji całego procesu publikacji stru-
• przedstawiać pliki video udostępnia- Efektem wykonania kodu przedstawio- mienia video na stronach WWW. Wklada-
ne na serwerze w postaci galerii zdjęć, nego na Listingach 6 i 7 są: jąc trochę więcej wysiłku możemy dopra-
przedstawiających trzy wybrane klatki cować i rozbudować naszą galerię, aby
z filmu, • trzy pliki graficzne w katalogu media- pod żadnym względem nie ustępowała ta-
• prezentować nagranie, w momencie data, kim rozwiązaniom, jak Google Video oraz
gdy użytkownik kliknie na wybraną se- • jeden plik FLV w katalogu mediadata, zintegrować ją z dowolnym serwisem in-
rię klatek. • informacje o plikach graficznych w ba- ternetowym. n
zie (tabela images),
Budowę witryny rozpoczniemy od stwo- • informacje o pliku FLV w bazie (tabela
rzenia formularza do uploadu plików. Ry- movies),
sunek 6 przedstawia przykładowy for- • informacje o nowo dodanym zasobie
O autorze
mularz. Serwis dokona konwersji uplo- (wpis w tabeli resources).
Rafał Malinowski jest programistą i pro-
adowanego pliku wideo do formatu FLV, jektantem aplikacji sieciowych w języ-
a następnie wybierze z filmu trzy klatki i Pozostaje nam teraz wywołać zapytanie kach PHP, JAVA i ActionScript. W wol-
zapisze je w postaci plików graficznych. SQL-owe, które pobierze z bazy (Rysu- nych chwilach tworzy biblioteki dla Fla-
sha i udziela się w kilku projektach Open
Wszystkie informacje przechowywać bę- nek 7) informacje o zasobach galerii i wy-
Source. Obecnie pracuje w SUPERME-
dziemy w bazie danych tak, by były póź- generowanie strony przedstawiającej li- DIA jako projektant / programista Java.
niej łatwo dostępne podczas generowa- stę wyciętych klatek z filmu (Rysunek 8). Kontakt malinr@wp.pl
nia galerii zdjęć. Po kliknięciu na jedną z nich nastąpi od-

80 www.phpsolmag.org PHP Solutions Nr 5/2006


W następnym numerze
PHP Solutions 6/2006(17)

BEZPIECZEŃSTWO
Kryptografia asymetryczna w PHP
Implementacja RS

PROJEKTY
Wielojęzyczny portal w 5 minut z wykorzystaniem
eZ publish
Nowe możliwości eZ publish 3.8.

DLA ZAAWANSOWANYCH
Więcej o IoC (Inversion of Control)
Zajmiemy się aspektami związanymi z logowaniem,
bezpieczeństwem i walidacją obiektów bizneso-
wych.

TESTY
Co nowego w IDE dla PHP?
Nowe, rewolucyjne możlowości środowisk programi-
stycznych

Ponadto planujemy: W sprzed


od 15 paź aży
■ TESTY – Test systemów CMS
dziernika
■ DLA ZAAWANSOWANYCH
Aspekt Oriented Programming w PHP
!
■ NARZĘDZIA – PHP-Qt

■ PEAR – XML Fast Create

oraz ciąg dalszy artykułów poświęconych bezpieczeństwu aplikacji


oraz wykorzystaniu wzorców projektowych

You might also like