Spis treści 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- oś wisi w powietrzu – chciało by się rzec. Wielki- znaczeniem przyszłych kierunków rozwoju dla tej mi krokami zbliża się PHP6, Oracle kupuje Zenda C technologii. – jedyną prawdziwą „firmę od PHP”, a Microsoft szko- 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 [email protected] 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 5 Spis treści 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: [email protected] 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: [email protected] Tu znajdą Państwo informacje Krzysztof Sobolewski Software Wydawnictwo Sp. z o.o. dotyczące aktualnych i przyszłych 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: [email protected] 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: [email protected] 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 [email protected] Product Manager: Maciej Krawcewicz [email protected] Redaktor prowadzący: Dariusz Pawłowski [email protected] PEAR Redaktor : Krzysztof Sobolewski [email protected] Stali współpracownicy: Paweł Kozłowski [email protected], Paweł Grzesiak [email protected] Generowanie kodu XML Kierownik produkcji: Marta Kurpiewska [email protected] za pomocą XML_Serializer 76 Projekt okładki: Agnieszka Marchocka Skład i łamanie: Sławomir Zadrożny [email protected] Aaron Wormus Dział reklamy: [email protected] Prenumerata: Marzena Dmowska [email protected] 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 [email protected] 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. 6 Wszystkie znaki firmowe zawarte w piśmie są własnością odpowiednich firm Aktualności 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: [email protected]

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. Listingi wszystkich opisywanych programów zo- Sprzedaż aktualnych lub archiwalnych numerów pisma po innej cenie niż wydrukowana na okładce – bez zgody wydawcy – jest działaniem na jego szkodę i skutkuje odpowiedzialnością sądową. 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 .

4 www.phpsolmag.org PHP Solutions Nr 3/2006 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 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- T 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 dzie dalej rozwijany. • Zend_Mail i Zend_Mime (obsługa narzędziami i produktami Open Source wśród 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. 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 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 N 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 (JavaScript Object Notation) macje, zasypujemy hasłami przeglądar- łalnością firmy Zend (Zend Studio, Zend 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- szedł z ciekawą i potrzebną inicjatywą jednak nadzieję, że Developer Zone sta- niem wbudowanej funkcji eval() ). JSON jest 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- mistów PHP. Już teraz możemy znaleźć dłem informacji dla deweloperów PHP. twiejszy do parsowania niż XML. Wielu dewelope- 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ę HP-Qt to nowe rozszerzenie dla PHP 5 w najbliższym czasie, przewidziano między in- nymi wsparcie dla Mock Objects (imitacji obiek- P 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- nizmy sygnałów i slotów, widgety, systemy se i Zend Studio. 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- • PHP 5.1.1+, rzenia pozwalające stosować AOP, co jest 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- HOCOA to w 100% obiektowy, stero- jąc ostatecznie SimpleTest. Swoje argumenty wany zdarzeniami, oparty o kompo- podali na stronie: http://trac.agavi.org/trac.cgi/ P wiki/UnitTested. Bergman polemizuje z tymi nenty i MVC framework dla PHP 5. Pro- 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 jest z Propelem, FCK Editorem, Dynarch- przeprowadza egzaminy na Zend Certified En- 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. tycznych widgetów (Widget Toolkit), takich nerowanie kodu np. interfejsu użytkownika http://aspnet.cmp.com/ 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/ (ang. bindings), efektywne dzielenie da- frameworka są bardzo obszerne i ambit- 670-PHP-Freelance.html. 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 na każdą przeglądarkę Bezpieczeństwo AJAX-a Kupu i Bitflux to chyba jedyne edytory Open JAX umożliwia tworzenie interaktyw- czy przestrzegać kilku prostych, funda- Source typu WYSWIG wspierane zarówno nych i bogatych interfejsów webo- mentalnych zasad: przez Internet Explorer, jak i Mozillę. Pierwszy A to edytor XHTML, drugi służy do edycji i wali- wych, przez co stał się ostatnio ogromnie 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 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- tym bardzo łatwo zabezpieczyć, wystar- http://searchwebservices.techtarget.com/ mework Zenda i jest teraz dostępny dla wszyst- 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 inBinder to rozszerzenie dla PHP ADOdb Active Record 4 i PHP 5 pozwalające na two- ADOdb_Active_Record to narzędzie ORM (Ob- W ject Relation Mapping) podobne do Zend_ 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 sami i funkcjami. Mamy do dyspozy- Archiwum WinBindera zawiera na wywołaniu funkcji blenc_encrypt(), któ- 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- edytora GUI. rowania, nagłówki, różne języki i kodowanie. 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

Data Access Object (DAO) PHP 4.4.2 i PHP 5.1.2 Code Generator imo, że PHP w wersji piątej już od serwerach nielinuksowych; naprawienie DaoGen to generator kodu źródłowego dla klas jakiegoś czasu dominuje na serwe- ponad 30 innych błędów. DAO na bazie wzorca projektowego DAO. Ma M za zadanie ułatwić pisanie aplikacji bazodano- rach, nie zanosi się póki co, na zamknięcie W PHP 5.1.2 również wprowadzono 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 rozszerza SQL-owy SELECT. Doskonałą re- FUDforum – najlepsze darmowe forum w PHP? komendacją dla EZPDO może być jego zasto- całą pewnością FUDforum jest jed- Interfejs administratora jest dość sowanie we frameworku PRADO czy Symfo- ny. Na stronie projektu znajdziemy dobrą do- Znym 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 cyjnymi produktami takimi jak vBulletin phpBB. Drugim (i chyba ostatnim) minu- http://www.ezpdo.net 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 z .NET do PHP PEAR::PHP_Compat, czyli PHP 5 w PHP 4 Phalanger pozwala na kompilację aplikacji obre rozwiązanie dla tych, którzy z Compat nie jest zwykłym pakietem PEAR stworzonej w PHP do .NET, czyli jej urucho- jakichś powodów nie mogą sobie po- – nie trzeba go instalować. Wystarczy po- mienie na platformie ASP.NET. Żeby prze- D prowadzić kompilację, będziemy potrzebo- zwolić na nowe środowisko – PHP 5 na brać pliki i umieścić je tam, gdzie wskazuje 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 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ż M 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 ), 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. CMS zapewnia wielojęzyczność, ła- nizm statystyk. Deweloperzy projektu świadczą wsparcie, 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 onTEXT to wielookienkowy edytor C 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 HP Expert Debugger to darmowe i ła- P 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 FFNMS to rozbudowane narzędzie do J 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 poprzednim numerze PHP Solutions Wzamieś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 MP to program pocztowy działający I 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 a płycie CD ponownie zamieściliśmy (może okazać się konieczne ustawienie N PHP Solutions Live – bootowalną w BIOS-ie komputera odpowiedniej dystrybucję Linuksa opartą na Aurox Live 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 www.phpsolmag.org/pl www.phpsolmag.org/pl Wszystkie listingi z artykułów zostały zamieszczone zostały artykułów z listingi Wszystkie adresem pod internetowej stronie naszej na zamieszczone zostały artykułów z listingi Wszystkie adresem pod internetowej stronie naszej na Wywiad 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. : 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 17 Wywiad 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

16 www.phpsolmag.org PHP Solutions Nr 3/2006 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.

iestety dokument ten, ze wzglę- czy powoduje, że PHP musi przechowy- du na rozmiary i poziom szcze- wać informacje na temat nazw klas, metod N 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.

REKLAMA

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

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

Techniki streaming audio w PHP 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.

mpache to wydawany na licen- wimy różne metody monitorowania piose- cji GNU General Public Licen- nek aktualnie odsłuchiwanych przez użyt- A 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ż- wiedzieć... rfc2616.html liwość transkodowania, downsamplingu – RFC 2616 (nagłówki HTTP) Konieczna jest podstawowa znajomość 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 W niniejszym artykule stworzymy apli- mość terminologii związanej z audycjami (przykłady kodu) internetowymi. 3. https://svn.ampache.org/trunk/play/ kację do rekursywnego katalogowania pli- index.php ków muzycznych oraz wyświetlania tych – kod źródłowy, który wykorzystali- Co obiecujemy... śmy danych. Następnie zajmiemy się transmi- Stworzymy aplikację, która przygotuje re- kursywny katalog plików dźwiękowych 4. http://www.ampache.org 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 23 Techniki 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 '', Instalacja Ampache `artist` varchar(255) NOT NULL default '', l Pobierz najnowsze archiwum z `size` int(11) unsigned NOT NULL default '0', http://ampache.org/downloads/ KEY `album` (`album`), KEY `artist` (`artist`), current.tar.gz i rozpakuj je do głów- KEY `id` (`id`), KEY `file` (`file`), nego katalogu serwera WWW. ) TYPE=MyISAM; 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 oraz zmiennej tekstowej zania na dowiązanie symboliczne i podą- $tags narzędzi $pattern. Przydadzą się one później, na- żenia za nim, co mogłoby prowadzić do 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

22 www.phpsolmag.org PHP Solutions Nr 3/2006 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 Listing 2. Zawartość /catalog.php $results pod nazwą każdego ze znaczni- ków. Po zebraniu wszystkich potrzebnych '%t','album'=>'%a','artist'=>'%A'); Umieszczenie informacji w bazie $pattern = '%a/%A/%t'; //definicja wzorca nazwy pliku; ignoruje rozszerzenie danych // rozpoczęcie zbierania plików gather_files('/home/mymusic'); Czas na utworzenie funkcji insert_file(), function gather_files($path) { która doda zebrane informacje (podane $handle = opendir(stripslashes($path)); // otwiera katalog jako parametr) do bazy danych. Dane zo- while ( false !== ( $file = readdir($handle) ) ) { $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); Jest ona dostosowana do MySQL, więc w if (is_dir($full_file)) {gather_files($full_file); continue;} $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 ru pliku — informacja ta zostanie umiesz- @closedir($handle); // zamknij uchwyt katalogu } 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']); cą na uchwycie bazy da- $preg_pattern = preg_replace("/\%\w/",".+",$preg_pattern); mysql_query() $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]); } Kurtyna w górę return $results; 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']); całą kolekcję. Skrypt ten pokazuje wszyst- $size = filesize($data['file']); $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) { ku można go łatwo rozbudować. if (!is_resource($dbh)) { $dbh = $GLOBALS['dbh']; } 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ó- 4). Będzie to kompletny skrypt, zdolny header() przekażemy mu dwa nagłówki:

Oglądanie utworów:

do strumieniowania plików OGG, FLAC i Content-type:, zawierający przygotowane
MP3 na podstawie ID utworu. Umożliwi dane oraz z łańcu- do skryptu. Typ pliku ustawimy na podsta- Następnie powinniśmy ustawić w pliku
la file w wynikach. Będzie to MP3, OGG pobiegnie wygaśnięciu skryptu oraz wy-
lub FLAC — przygotujemy też odpowied- łączyć magic quotes podczas uruchamia-
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 i poukłada je we- Now Playing music 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 , zawierający informacje o l $name 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- padku, gdy użytkownik nacisnął next przed końcem utworu – nie działa jednak z chciwy- Transmisja strumieniowa: 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- branego pliku. Warto zauważyć, że nie `id` int(11) unsigned NOT NULL auto_increment `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. plik dźwiękowy, a nie skrypt PHP. Na ignore_ 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- 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(). $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)); // pobranie offsetu, długość utworu taka jak w $song_time ($song_bitrate/1000); $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); Następną modyfikację należy wpro- $eof = sprintf("%02d.%02d",$eofmm,$eofss); wadzić po wysłaniu kolejnych dwóch $song_file = escapeshellarg($filename); $downsample_command = "mp3splt -qnf $song_file $offset $eof -o - | ". nagłówków ( i Accept-Ranges 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 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.

poprzednim artykule na przy- Nazwane wyjątki kładzie prostego frameworka – dlaczego warto je WMVC 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- przez nas Framework będzie również lektura artykułu wprowa- 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... Poznasz trzy kolejne wzorce projektowe kacji w PHP5 zana jest implementacja nowej wersji (Adapter, Transfer Object i Intercepting 4. http://www.developer.com/ tej klasy. Zmiany wydają się dość po- design/article.php/3345121 Filter) i ich praktyczne wykorzystanie w – 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 29 Techniki 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.

28 www.phpsolmag.org PHP Solutions Nr 3/2006 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; Klient chciałby też móc w prosty sposób public function __construct(ActionResolvingStrategy $actionResolvingStrategy) { 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- { cji. Problem ten sprowadzimy do napi- $actionClass = $this->getActionResolvingStrategy()->resolveAction($reques 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 wane do innych klas, które będą umia- wykonania'); }

Listing 1b. Implementacja klasy if ( $actionClass instanceof MVCAction ) 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ó- { re specjalizują się w wykonywaniu pro- public function render(array $data); } 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- waniu. Klasa FrontControllerImpl wraz class SmartyViewResolvingStrategy implements ViewResolvingStrategy { z implementacjami różnych interfejsów private $renderingEngine; typu ResolvingStrategy jest idealnym private $tplPath; przykładem na to, jak zasada kompozy- private $tplPrefix; cji obiektów może spowodować, że nie- private $tplSuffix; 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); on w jednej dużej klasie, taki kod były $this->setTplPath($tplPath); najprawdopodobniej nieczytelny i trudny $this->setTplPrefix($tplPrefix); $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ć { skomplikowany – wystarczy jedynie, aby $fileName = $this->getTplPath().'/'.$this->getTplPrefix().$viewName; 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ć z rozwagą. Pojawia się więc pytanie, $fileName .= $this->getTplSuffix(); $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); części artykułu. Klasa ta zwraca na pod- $this->setTplFile($tplFile); 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-

obraźmy sobie teraz, że pojawia się na- return $this->getRenderingEngine()->fetch($this->getTplFile()); } 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- ność tę można powtarzać dalej, tzn. class MCacheActionResolverStrategy implements ActionResolvingStrategy { w momencie, gdy będziemy chcieli do- private $_actionRequestParamName; dać kolejny mechanizm cachowania private $_memcache; ponownie skopiujemy kod i utworzy- my kolejną klasę implementującą in- public function __construct($actionRequestParamName, $memcacheHost, terfejs ActionResolverStrategy. I w tym $memcachePort) { 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- nowić, czy nie można tak zmienić kodu, if ($actionName != '') 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ż- { nić się od implementacji jakiegokolwiek throw new RuntimeException('Nie wyspecyfikowano akcji do wywołania!'); 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 { może pobierać obiekty z dowolnego me- public function set($key,$data); chanizmu cachującego implementujące- public function get($key); 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- { cheLita. Listing 5c pokazuje implementa- private $actionRequestKey; private $cacheService; cję Adaptera dla CacheLite.

Dzięki oddzieleniu API mechani- public function __construct($actionRequestKey, CacheService $cacheService) zmów cachujących od klas ActionRe- { soverów udało się stworzyć mechanizm $this->setActionRequestKey($actionRequestKey); cachowania wspólny dla całego frame- $this->setCacheService($cacheService); } 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 CacheService. if ($this->getCacheService()->exists($actionName)) { 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. { Najbardziej znany to oczywiście Smar- throw new ObjectDoesnotExistsInCacheException( '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- wsze muszą mieć postać stron HTML, // gettery i settery zostały pominięte 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ń- { cu (ang. preprocessing i postproces- return $this->getCacheEngine()->save($data,$key); 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- } ny hasłem, jedną z pierwszych operacji, public function remove($key) 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- { meworka MVC nasz problem sprowa- if ( $this->getCacheEngine()->get($key) ) 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- żemy zapomnieć to zrobić. public function doPostProcessing(HttpRequest $request, 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- nocześnie pytanie: czy nie da się te- public function doPreProcessing(Request $request) { 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, mianach , w których filtry stanowią łań- ModelAndView $modelAndView = NULL) { 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- [email protected] 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 eyeOS – system operacyjny w PHP 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.

elem projektu jest opracowanie wym rogu ekranu umieszczono małą ikonę graficznego środowiska spełnia- umożliwiającą wylogowanie oraz zegar cy- C 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... Po przeczytaniu artykułu będziesz wie- 2. http://www.eyeapps.org dowanych aplikacji, które omówimy później, – repozytorium aplikacji dział jak uruchomić i wykorzystać własny 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 37 Narzędzia 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ń, systemu eyeOS wprowadzono także moż- • eyeNav – przeglądarka internetowa, liwość jednoczesnego uruchamiania wielu Sample App v. 1.0 aplikacji, przy zachowaniu możliwości ich Pau Garcia-Milà minimalizowania. 700 Edycja tekstu w eyeOS 575 Przyjrzyjmy się bliżej wbudowanym w sys- no no tem aplikacjom. Po kliknięciu New Docu- -1 ment w oknie, które opisywaliśmy, lub w -1 drugą ikonę w doku aplikacji u góry ekra- nu pojawi się edytor tekstu WYSIWYG (Rysunek 3). Umożliwia on wybranie kil- ku krojów czcionek i ustawienie ich roz- Rysunek 2. Działający pulpit eyeOS

36 www.phpsolmag.org PHP Solutions Nr 3/2006 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 ) po prawej nym katalogu WWW i otworzyć w przeglądarce odpowiedni adres. Instalator zapyta o wy- X 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- • – wysokość ( ), projekt zwany przez członków zespo- WINDOW_HEIGHT 200 ł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 • – format daty systemowej wicie oparta na tej technologii (pisali- SYSFMT_DATE ś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 • – maksymalna liczba ikon w numerze 1/2006 magazynu PHP So- MAXICONS 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- • – plik forum dyskusyjnego tworzyć własny pulpit, zgodny z wymaganą kumentacji. BBOARD (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. eyeApp.tar.gz. Zajmijmy się plikiem propietats.xml. W obecności pięciu plików: podawania kodowi aplikacji danych o pa- 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 "";

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 (......) 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: [email protected] 'no'. mi eyeOS zostanie uszkodzony. Aby użyć

PHP Solutions Nr 3/2006 www.phpsolmag.org 41 Narzędzia DBDesigner 4 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.

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- D 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 W SIECI Pewne przedsiębiorstwo aktualizu- 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 Zmiana będzie polegała na zastąpieniu datna będzie również choćby minimalna – oficjalna strona 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- jących, a także jak w prosty sposób ge- downloads.php 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 43 Narzędzia 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_

42 www.phpsolmag.org PHP Solutions Nr 3/2006 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, fejsu obsługi danych czy ułatwienie prze- PRIMARY KEY(idC_Zwrot) ); glądania bazy. Omówimy je po kolei. ------Tabela miast Eksport struktury bazy ------do skryptu SQL CREATE TABLE C_Miejsce ( Aby na podstawie naszego modelu wyge- idC_Miejsce INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, 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, Skrypt SQL zostanie wygenerowany, Nazwisko VARCHAR(30) NULL, 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 ) 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 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 . 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- [email protected] Aktualizowanie widoku jektować strukturę bazy, wczytać do niej

PHP Solutions Nr 3/2006 www.phpsolmag.org 49 Narzędzia Lokalizacja na bazie TMX 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.

tandard TMX (ang. Transla- blic jako kontenerów translacyjnych (ele- tion Memory eXchange) powstał mentów służących do przechowywania S 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 PHP-TMX narodowe). 2. http://www.lisa.org – witryna Co obiecujemy... organizacji LISA Po przeczytaniu artykułu będziesz wie- 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 51 Narzędzia 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 Z drugiej strony, składowanie zasobów 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- gokolwiek języka programowania. Wspo- mniane narzędzia pozwalają modyfikować

Internacjonalizacja Internacjonalizacja (ang. Internationaliza- tion; w skrócie: i18n) jest w kontekście in- The database is empty! żynierii oprogramowania procesem pla- nowania oraz implementacji produktów i usług w taki sposób, aby cechowały się Il database č vuoto! one łatwością adaptacji do specyficznych aspektów kulturowych (głównie lokalnych języków). Wspomniany proces adaptacji takich produktów nazywamy lokalizacją. Jednym z głównych aspektów in- Hello World! ternacjonalizacji jest odseparowanie ba- zowego kodu źródłowego produktu od wszelkiego rodzaju zasobów (tekstów, Ciao a Tutti! nagłówków, informacji) które są zmien- ne w kontekście różnych języków bądź kultur. Takie podejście znacznie uprasz- cza proces tłumaczenia – dzięki temu, ż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- cji, to zbiory sentencji zapisane w języku require_once('TMXResourceBundle.php'); // 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).

50 www.phpsolmag.org PHP Solutions Nr 3/2006 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 definiujemy wszystkie wersje językowe tekstu przechowywanego w da-

��� nym kontenerze. Każda wersja jest repre- ��� ���������� zentowana przez tag (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: .... 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: ; da- na Rysunku 1). Takie podejście pozwoliło- bardzo szybko rosnąć. W tej sytuacji do- lej otwierany jest element , 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-

, 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 i zawartych tam sekcji nie w kontekście czasu i pamięci). Niedo- Przygotowanie do .... 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 Ÿ tion), jednak biorąc pod uwagę niewielkie Asuni N, "TMXResourceBundle – TMX Java Bridge" [wersja online] 2005-01-08, 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 projektu potrzebować będziemy dwóch online] 1998-10-01, 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, Wspomniane zasoby opisane będą http://www.lisa.org/standards/tmx/tmx.html Ÿ 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 tmxphpbridge.sourceforge.net. $this->language = strtoupper($language); 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 dle do obsługi tłumaczenia prostego tekstu xml_set_object($this->parser, $this); potrzeba nam jedynie trzech elementów: // wyłączamy w parserze opcję “case-folding” , and (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. ), przy $tmxres 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 : { dostawców usług z branży IT. Ich celem default jest tworzenie wytycznych pracy oraz break; 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 , 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) cę zasobów. $this->current_key = ""; break; } Podsumowanie case 'tuv': { W niniejszym artykule przedstawiono // wariant jednostki translacyjnej; zawiera kod języka podstawowe techniki internacjonaliza- $this->current_language = ""; cji w kontekście języka PHP. Pokaza- break; } 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_ kacji, a danymi, możliwość ponowne- key, $this->resource)) { $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 } z internacjonalizacją programów tworzo- default: { n break; nych w PHP. } } }

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 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.

wego czasu znajomy zapytał Podstawowa struktura mnie, czy (i w jaki sposób) da- systemu S ł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 kownikom. Narzędzie to musi też umoż- umieszczone na stronie pliki graficz- sposób zabezpieczania pli- 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 57 Projekty 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-

l ry (podobnie jak same zdjęcia) będzie się 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']; suje je w tablicy $username. Za autoryzację if ($validuser == 10) { // 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 // Użytkownik nie jest jest zalogowany, więc otrzyma ekran logowania }else{ użytkownik ma prawo dostępu do strony, print (showLoginPage()); zostanie otwarta sesja użytkownika wraz exit; ?> z ustawieniem wspomnianej już zmiennej sesyjnej validuser, po czym do przeglą- Funkcja showLoginPage() (plik showloginpage.php) Listing 2. darki użytkownika zostanie wysłany kod strony głównej (index.html). W przypad- function showLoginPage($msg_type='general'){ 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.
\n'; }elseif($msg_type=='error'){ Struktura plików $message='Nieprawidłowe dane użytkownika, spróbuj jeszcze raz; '; }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="\n\n". jaką mamy do czynienia – ilustruje ją Rysu- "

\n". nek 1. Katalogiem głównym serwera WWW ... "
\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.

56 www.phpsolmag.org PHP Solutions Nr 3/2006 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: Wylogow wywołaniem session_unset(), a następ- jej argument. anie. 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- 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ć lokalne zapisywanie obrazów poprzez kli- session_destroy(); stron HTML zawierających obrazy funkcji print (ShowLoginPage('logout')); ?> JavaScriptu o nazwie rightmouse(), któ- ra będzie obsługiwać przechwycone zda-

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

PH P5. Księ g a ekspert a ««««« 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 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'); glądało mniej więcej tak: // Utworzenie komunikatu o błędzie i zapisanie go do pliku $errormessage = "Błąd: Nie można otworzyć pliku - ".$filename; fwrite($efp, $errormessage); } /* ------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 ne wprowadzanie znaczników jest zaję- $ext = get_extentsion($filename); // 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 ruje kod odpowiednich znaczników , if (!$fp) { // Zapisanie w logu komunikatu o nieudanej próbie otwarcia pliku 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- 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-

Witamy na naszej stronie!

treści testuser|pass. vault tworzymy plik o następującej treści: Ostatni etap to utworzenie dwóch testo- Kliknij tutaj, by zobaczyć zdjęcia wych stron HTML. Pierwszą jest wspomnia- na wcześniej strona index.html, czyli fak-

Listing 7. Skrypt linkgen.php generujący kod HTML ze znacznikami do- Ostatnim plikiem jest strona galerii pictu- stępnych obrazów res.html (Listing 8), również zapisana w katalogu public_html. \n". nego dostępnego odsyłacza przenie- "

\n". sie nas na stronę pictures.html: tym ra- " \n\n". " \n\n"; // Pobranie listy nazw udostępnianych plików graficznych Podsumowanie if ($handle = opendir('../imagevault/')) { Jak widać, kontrolowanie dostępu do (false !== ($file = ($handle))) { while readdir wybranych plików za pomocą skryp- $file = basename(trim($file)); 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 dla bieżącego pliku snej stronie domowej czy na witrynie $imgtag = ""; klubowej. Przedstawiony system może // Utworzenie wpisu w tabeli dla bieżącego pliku posłużyć za podstawę dla bardziej za- $content .= " \n". awansowanych rozwiązań, uwzględnia- "\n\n"; nicowane poziomy dostępu i wszelkie } } } inne operacje, jakie mogą być potrzeb- $content .= "
Nazwa pliku graficznego poprawnie. Kod HTML obrazu
".$file. jących na przykład automatyczną reje- "\n\n
\n"; ne. Tak czy inaczej, pamiętajmy za- closedir($handle); } wsze, że kluczem do sukcesu projektów $content; // Wypisanie plików z kodem odpowiednich znaczników print programistycznych jest ich jak najwięk- }else { print(showLoginPage()); } // Użytkownik nieautoryzowany n exit; sza prostota. ?>

Listing 8. Kod pliku pictures.html

O autorze

Our Pictures

Patrick O’Brien jest założycielem i twór-

cą serwisu Jpowered.com (http://www. jpowered.com), dostarczającego zaawan- sowane rozwiązania dla twórców witryn in- ternetowych i programistów.

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

Mariaż Pythona i PHP. Tworzymy interfejs grafi czny 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 grafi czny 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.

yobraźmy sobie książkę tele- w Pythonie z użyciem wxWidgets. adresową, w której będziemy W 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ść grafi cznych 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- Web Services gi sieciowe (ang. ), zwa- Co należy wiedzieć... ne też usługami sieciowymi, umożliwiają- 1. http://www.python.org/ Powinieneś znać model obiektowy i wy- – ofi cjalna 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/ liśmy już o nich w paru artykułach (Google tokołu SOAP i formatu XML. view/185 – Python kontra PHP API, XUL). 3. http://wiki.w4py.org/python- Jednym z najpopularniejszych protoko- vs-php.html 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 werze, korzystającą z bazy danych aplika- Side-Coding/Python/ 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 modyfi kację; 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 grafi cznym (GUI) a oprogramo- setup.py install waniem serwera. Po zainstalowaniu wszystkich elementów nasz zestaw jest gotowy do pracy. • interfejsu grafi cznego (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 bazy) interfejsowi SOAP-GUI, który public function __construct(){ $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 przedstawiamy na Rysunku 1. Typowa $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); }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 trafi a 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 grafi cznego 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 grafi czny), 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 gdzie za pomocą specjalnych odnośni- SOAP SOAP GUI 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 zdefi niowane 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){ $stmt->execute() jąc , 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); getMsg() $stmt=$this->db->prepare($query); z silnika ( )– 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 zdefi niowanie 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(); myślnie Python nie kopiuje tablicy, tylko $this->xarr=new XML_Array(); $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 grafi czny (GUI). $data=$this->xarr->retXMLToArray($dataXML); $result=$this->book->addEntry($userName,$pass, $data['data_rows']); Tworzymy interfejs ... } grafi czny w Pythonie Będzie on wyglądał jak na Rysunku 2. public function updateEntry($userName,$pass,$dataXML){ ... 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 modyfi kować 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: getTempEntry() dataXML=self.server.getOneEntry(userName,password,id) metod (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 grafi cznego. return self.dataInterface.getAllEntries('critto','1qazse4') def resetTempDataModel(self): Budujemy główną część # metoda publiczna dataTMP0=self.reloadData() aplikacji self.dataTemp={} Tworzenie interfejsu grafi cznego 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): zdefi niujemy 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,fl ag=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 zdefi nio-

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 zdefi niujemy 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 grafi cznego – 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- (edit Clicked). W pierwszym przypadku zawartość tabeli bazodanowej (populate- miast Python świetnie sprawdza się przy okienkiem dialogowym jest obiekt klasy BookList()). tworzeniu interfejsu grafi cznego. 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- O autorze sób reagowania na ich kliknięcie: po za- su grafi cznego jest dialog pozwalają- 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- fi czne i algorytmy rekurencyjne. Poza lu danych: pierwsza z nich kasuje pozy- torze tej klasy defi niujemy 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- [email protected] Każda z tych metod odświeża następnie bieranie wpisanych danych z pól edycyj-

REKLAMA

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 zatruwanie sesji w PHP 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ć.

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- O 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- Co należy wiedzieć... 1. http://www.phpsec.org 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/ mularz spreparować. Co obiecujemy... – PHP Security Project 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- dyfikowania na serwerze, ich sniffowania – Top 7 security blunders 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 73 Bezpieczeństwo 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- pamiętywane. Zmienne z tabeli $_COOKIE są przetrzymywane na dysku użytkowni-

SniffEdSess

ka w postaci plików ciasteczek zgroma- sniffer&edytor zmienych sesyjnych

dzonych w odpowiednim dla danej prze-
glądarki miejscu. Natomiast zmienne se- syjne nie są ani przesyłane jednokrot- nie, bo przeczyłoby to ich przeznacze- niu. Nie są też przechowywane po stro- nie użytkownika, gdyż narażałoby je to na modyfikacje, lecz w postaci plików na serwerze (który musi mieć do nich dostęp przez cały czas istnienia sesji). W systemach uniksowych są to pliki
numer sesji:

o nazwach sess_$ID ulokowane w kata- logu /tmp, gdzie oznacza przypisany $ID do danego użytkownika numer sesji. Struktura takiego pliku wygląda nastę-
Przechwycone dane

pująco:
nazwa zmiennej|typ:[długość:]wartość; A oto przykładowy plik sesji:
Zmienna:
Wartosc:
zalogowany|i:1;login|s:6:
"unknow";haslo|s:5:"tajne" Ustawiono $variable na wartość '.$var_value.'

'); $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('
Copyright © by Unknow - [email protected]'); sesji oddzielone są od siebie znakiem ?> średnika. Dla danych liczbowych nie

72 www.phpsolmag.org PHP Solutions Nr 3/2006 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. 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- seł. Natomiast sniffing sesji odnosi się do 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. Naturalnie, prezentowany sposób wstrzykiwania kodu JavaScript w kod strony jest tyl- (session_start). Jak wiemy, jeśli przed 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- wadzeniem własnych projektów programi- Jak się zabezpieczyć? stycznych. fer sesji. Na razie okno przechwyconych Istnieje kilka metod takiego pisania skryp- Kontakt z autorem: [email protected] 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 XML_Serializer 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.

d czasu stworzenia standardu cation, a wraz z popularyzacją technolo- XML pod koniec lat 90., po- gii AJAX XML coraz częściej pojawiają się O 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ó- rych został stworzony. Obecnie można po- 1. http://pear.php.net/package/ 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 PHP Solutions Nr 3/2006 www.phpsolmag.org 77 PEAR 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 , w którym znajduje znacznie zamrożeniu API w celu utrzy- SGML (ang. Standard General Markup się wiele znaczników . 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(); header('Content-type: text/xml'); echo $xml; Piąty Element High Fidelity Listing 4. Generowanie kodu XML w trybie simplexml $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 = array(array('attr' => array('name' => 'Piąty Element'), Luc Besson "writer" => "Luc Besson", 1997 "year" => "1997", tt0119116 "imdb" => "tt0119116"), array('attr' => array('name' => 'High Fidelity'), "writer" => "Nick Hornby", Nick Hornby "year" => "2000", 2000 "imdb" => "tt0146882")); tt0146882 $options = array ('rootName' => 'movies', 'attributesArray' => "attr", 'defaultTagName' => 'movie');

76 www.phpsolmag.org PHP Solutions Nr 3/2006 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- Przyglądając się kodowi XML z do- tu XML. Tabela 1 przedstawia najważniej- drzewa znaczników XML a strukturą ta- inne opcje, ale dla potrzeb tego artykułu Tryby działania 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

Luc Besson 1997 tt0119116 Milla Jovovich nm0000170 Bruce Willis nm0000246 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ó-

REKLAMA

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( mi dodatkowe informacje o filmach po- array( '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'), niego przykładu są pobierane w pę- 'actor' => 'Bruce Willis', '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", pojawia się również kilka nowych. Opcja 'imdb' => "tt0119116", '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){ wcięć w kodzie XML (to oczywiście tylko $tabs["tab"][]=array('label'=>$m['attr']['name']."({$m['year']})"); kosmetyka, ale dzięki temu kod jest bar- $url = "http://us.imdb.com/title/". $m['imdb']; $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- } su XUL wyświetlanego przez przeglądar- $data = array("tabbox" => array("tabs" => $tabs, "tabpanels" => $tab_panels)); $options = array ('addDecl' => TRUE, ki Mozilla. 'rootName' => 'window', "defaultTagName" => 'tab', Podsumowanie "scalarAsAttributes" => true, W tym krótkim artykule poznaliśmy zasa- "mode" => 'simplexml', dy wykorzystania pakietu XML_Serializer 'indent' => ' ', '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); w codziennej pracy. Istnieją wprawdzie $xml = $serializer->getSerializedData(); inne sposoby generowania kodu XML, ale header('Content-type: application/vnd.mozilla.xul+xml'); 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ę, że udało mi się pokazać możliwości tego równie użyteczny, jak dla mnie. n O autorze Aaron Wormus programuje w PHP od 1999 r. Zajmował się tworzeniem roz- wiązań intranetowych w Perlu i technolo- giach pokrewnych. Aaron koncentruje się na dostarczaniu przedsiębiorstwom wy- sokiej jakości rozwiązań intranetowych i wykorzystuje potęgę PHP w pracy kon- sultanta. Kontakt z autorem: [email protected]

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: [email protected]. 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

Ilość Od numeru Opłata Ilość zamawianych pisma lub w zł numerów Tytuł 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) 1 Miesięcznik o systemie Linux 12 250/180

Linux+DVD (2 płyty DVD) 1 Miesięcznik o systemie Linux 12 270/198

Linux+Extra! (od 1 do 7 płyt CD lub DVD) 2 Numery specjalne z najpopularniejszymi dystrybucjami Linuksa 8 232/198

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) 3 Magazyn z najpopularniejszym polskim Linuksem 4 119

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 sprzedaży

■ MSSQL i PHP od 20 czerwca! ■ IRC BOT w PHP ■ 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