You are on page 1of 84

Spis treści

POCZĄTKI
Otwarty format OASIS dla
dokumentów biurowych i CMS-ów 16
Nie odkrywajmy Ameryki na nowo!
Z nam wielu programistów PHP, którzy piszą swoje apli- Bård Farstad
kacje bazując wyłącznie na własnych rozwiązaniach Wklejanie czystego tekstu do CMS-a i jego ręczne
– rozwijają frameworki, tworzą proste narzędzia do debu- formatowanie jest zadaniem żmudnym i podatnym
gowania, czy próbują sami walczyć z niedopasowaniem na błędy. Gdyby tak można było stworzyć odpo-
relacyjno-obiektowym (narzędzia ORM) w aplikacjach bazo- wiedni dokument w swoim ulubionym pakiecie biu-
danowych. Za nic nie określiłbym ich mianem domorosłych rowym, a potem po prostu skopiować plik i wkleić
programistów webowych – to deweloperzy z kilkuletnim go do CMS-a... Taką właśnie możliwość daje połą-
czenie trzech nowatorskich produktów: uniwersal-
doświadczeniem, którzy PRAWIE profesjonalnie tworzą CRM-y czy dedyko-
nego formatu OpenDocument, CMS-a eZ publish
wane CMS-y. Kiedy opowiadają mi o swoich problemach (programistycznych),
i pakietu biurowego OpenOffice.org 2.0.
rozwiązaniach i pomysłach, przyznaję – ich twórczość i innowacyjność robi na
mnie wrażenie. Z reguły jednak odpowiadam im następująco: słuchaj, przecież
pisaliśmy już o tym w PHP Solutions.
Ich praca jest często mało wydajna, gdyż sami odkrywają i implementują NARZĘDZIA
rzeczy już wcześniej wymyślone i zbudowane. Dzieje się tak dlatego, gdyż Programowanie sterowane
są nieufni i nieświadomi wielu pożytecznych i istniejących już rozwiązań. testami za pomocą PHPUnit 22
Z tego powodu użyłem sformułowania PRAWIE profesjonalnie.
Aby tę tendencję odwrócić, regularnie oddajemy w Wasze ręce magazyn Timo Haberkern
PHP Solutions. Projekty i technologie, które opisujemy w obecnym numerze, Im większy projekt programistyczny, tym trudniej
na pewno pomogą Wam w codziennej pracy. wyłapywać pojawiające się w nim błędy, a usunię-
Szczególnie zachęcamy do budowania aplikacji w oparciu o technologię cie jednych usterek powoduje często powstanie
AJAX, która – dzięki wykorzystaniu JavaScriptu i XML-a – pozwala na two- następnych, w innej części aplikacji. Ręczne
rzenie wydajnych i wyjątkowo interaktywnych aplikacji WWW. Zastosowanie tworzenie testów dla setek klas jest nieskuteczne,
AJAX uwolni użytkownika od irytującego klikania i zbędnego oczekiwania a poza tym przyprawia o ból głowy i paraliżuje
pracę. Z pomocą przychodzi PHPUnit: narzędzie
– teraz dane mogą być przekazywane bez przeładowywania strony.
pozwalające zautomatyzować proces tworzenia
Gorąco polecam też artykuł o narzędziu PHPUnit (odpowiednik Junit dla
i wdrażania testów.
Javy) służącym do testowania modułów aplikacji. Dzięki niemu zbudujecie
stabilny i pozbawiony błędów kod w dużo krótszym czasie.
W numerze weźmiemy również pod lupę kilka absolutnych nowości, Drupal, czyli wielodomenowe,
m.in. PHP-GTK2, które oferuje rewolucyjne możliwości budowania aplikacji wielojęzyczne i modularne portale
okienkowych. SDO to kolejne rozwiązanie ze świata Javy przeniesione do w oparciu o AJAX i SEO 30
PHP przez IBM i Zend Technologies, które zapewniając jednolity dostęp do Uwe Hermann
różnego typu danych (bazy danych, XML). Drupal to rewolucyjny CMS po- Czy potrzebujesz systemu zarządzania treścią
zwalający na tworzenie wielodomenowych, wielojęzycznych i modularnych (CMS) ogólnego zastosowania, będącego w stanie
portali z wykorzystaniem takich technologii, jak AJAX. obsługiwać w jednej instancji kilka niezależnych
Zapraszam do lektury kolejnego numeru PHP Solutions. Satysfakcja serwisów WWW, z których każdy dostępny ma być
gwarantowana! w kilku wersjach językowych? Czy pełna internacjo-
Dariusz Pawłowski nalizacja powinna być możliwa za pomocą zaledwie
Redaktor prowadzący kilku kliknięć myszą? Może chciałbyś także dodać
do swojego serwisu elementy dynamiczne korzy-
stające z AJAX, albo zwiększyć jego popularność
dzięki zastosowaniu najlepszych technik SEO? Nie
musisz dalej szukać: wypróbuj system Drupal.
Nasz magazyn ukazuje się w czterech językach!

polskim niemieckim francuskim włoskim TECHNIKI
Service Data Objects,
czyli standard uniwersalnego
dostępu do danych 42
Piotr Szarwas
Rozwiązania od dawna stosowane w Javie zale-
wają świat PHP. Należy do nich SDO, czyli Servi-
ce Data Objects: zunifikowany, wspierany przez
takie potęgi, jak IBM, Zend i BEA standard do-
Jeśli jesteś zainteresowany zakupem licencji na wydawanie naszych pism prosimy o kontakt: stępu do danych, eliminujący potrzebę tworzenia
Monika Godlewska monikag@software.com.pl tel.: 48 22 887 12 66, fax: 48 22 887 10 11
osobnych interfejsów dla każdego ich źródła.

4 www.phpsolmag.org PHP Solutions Nr 1/2006
Spis treści
AJAX – wyjątkowo interaktywne
i wydajne aplikacje WWW 48
Joshua Eichorn, Werner M. Krauß
Pytania dotyczące Strona WWW/Forum
PHP zawdzięcza swój sukces nie tylko potężnym
prenumeraty strona www: www.phpsolmag.org
możliwościom, ale również samemu modelowi tel. (22) 887 14 44 Tu znajdą Państwo informacje
programowania. Aplikacje tworzone w PHP e-mail: pren@software.com.pl dotyczące aktualnych i przyszłych
pozwalają osiągnąć bardzo wiele przy ograni- Software Wydawnictwo Sp. z o.o. numerów magazynu PHP Solutions.
czonym oprogramowaniu klienckim, co oznacza dział prenumeraty
łatwe wdrażanie i aktualizacje, a tym samym ul. Piaskowa 3 Forum: www.phpsolmag.org/newforum
szybkie efekty pracy. Architektura ta ma też dotkli- 01-067 Warszawa Zachęcamy do dyskusji na naszym
forum. Czekamy na propozycje
we wady, jak opóźnienia między wyświetlaniem CD tematów, które chcieliby Państwo
kolejnych stron lub brak możliwości pobierania tel. (22) 887 14 44
znaleźć w najbliższym numerze pisma.
nowych danych bez wysyłania formularza. Na e-mail: cd@software.com.pl
Zapraszamy także do wymiany
szczęście istnieje mechanizm AJAX. Software Wydawnictwo Sp. z o.o.
poglądów z innymi fanami PHP.
Defekty CD/DVD
ul. Piaskowa 3 Cena
01-067 Warszawa Prenumerata: 135 zł
PROJEKTY Zamówienia Przelew na konto nr:
46 1440 1299 0000 0000 0391 8238
advAJAX, czyli praktyczne /Numery archiwalne Nordea Bank Polska S.A.
tel. (22) 887 14 44
zastosowanie technologii AJAX 58 e-mail: pren@software.com.pl
II Oddział w Warszawie

Łukasz Lach sklep on-line: www.shop.software.com.pl
Ciągłe przeładowywanie strony WWW przy każ- Kontakt z redakcją
dej zmianie jej zawartości i żmudne czekanie na e-mail: redakcja@phpsolmag.org
wyświetlenie kolejnej porcji danych jest zmorą Software Wydawnictwo Sp. z o.o.
każdego użytkownika aplikacji webowych i pro-
Redakcja PHP Solutions
ul. Piaskowa 3
gramisty PHP. Nie jesteśmy jednak skazani na
01-067 Warszawa
te bolączki: wybawia nas od nich zyskująca na
znaczeniu PHP technologia AJAX. Dzięki niej
Wszystkie listingi z artykułów zostały zamieszczone na naszej stronie internetowej
ładujący się w nieskończoność pasek postępu pod adresem www.phpsolmag.org/pl
przechodzi do lamusa.

Nowe możliwości PHP-GTK2 64
PHP Solutions jest wydawany przez Software-Wydawnictwo Sp. z o.o.
Pablo Dall'Oglio Dyrektor Wydawniczy: Jarosław Szumski
Rozszerzenie PHP-GTK1 zapoczątkowało nowy Market Manager: Sylwia Tuśnio sylwia.tusnio@software.com.pl
Product Manager: Maciej Krawcewicz maciej.krawcewicz@phpsolmag.org
sposób myślenia o PHP. Język przeznaczony Redaktor prowadzący: Dariusz Pawłowski dpawlowski@phpsolmag.org
dla aplikacji sieciowych zaczął być powszechnie Redaktor: Krzysztof Sobolewski ksobolewski@phpsolmag.org
Opracowanie CD: Krzysztof Sobolewski
stosowany przy pisaniu samodzielnych aplikacji Stali współpracownicy: Paweł Kozłowski pkozlowski@phpsolmag.org, Paweł Grzesiak pgrzesiak@phpsolmag.org
klienckich z graficznym interfejsem użytkownika Kierownik produkcji: Marta Kurpiewska marta@software.com.pl
Projekt okładki: Agnieszka Marchocka
(ang. Graphical User Interface, GUI). Niemniej Skład i łamanie: Agnieszka Zadrożna aga.z@software.com.pl
jednak, to właśnie pojawienie się PHP-GTK2, Dział reklamy: adv@software.com.pl
Prenumerata: Marzena Dmowska pren@software.com.pl
umożliwiającego połączenie możliwości PHP5
Nakład: 6 000 egz.
i biblioteki Gtk-2.6, może dać początek prawdzi-
wej rewolucji. Adres korespondencyjny: Software-Wydawnictwo Sp. z o.o.,
ul. Piaskowa 3, 01-067 Warszawa, Polska
tel. +48 22 887 10 10, fax +48 22 887 10 11
www.phpsolmag.org cooperation@software.com.pl

VARIA Dołączoną do magazynu płytę CD przetestowano programem AntiVirenKit firmy G DATA Software Sp. z o.o.

Porównanie ofert polskich firm Redakcja dokłada wszelkich starań, by publikowane w piśmie i na towarzyszących mu nośnikach informacje
i programy były poprawne, jednakże nie bierze odpowiedzialności za efekty wykorzystania ich; nie gwarantuje
hostingowych 74 także poprawnego działania programów shareware, freeware i public domain.
Uszkodzone podczas wysyłki płyty wymienia redakcja.
Paweł Grzesiak Wszystkie znaki firmowe zawarte w piśmie są własnością odpowiednich firm
i zostały użyte wyłącznie w celach informacyjnych.
Rynek usług hostingowych w Polsce rozwija się
Redakcja używa systemu automatycznego składu
dynamicznie. Jeżeli planujemy zakup własne- Do tworzenia wykresów i diagramów wykorzystano program firmy
go skrawka miejsca w sieci, warto zapoznać
Osoby zainteresowane współpracą prosimy o kontakt: cooperation@software.com.pl
się z przygotowanym przez nas porównaniem
Druk: ArtDruk
usług najpopularniejszych polskich providerów
internetowych. Wysokość nakładu obejmuje również dodruki. Redakcja nie udziela pomocy technicznej w instalowaniu
i użytkowaniu programów zamieszczonych na płytach CD-ROM dostarczonych razem z pismem.
Sprzedaż aktualnych lub archiwalnych numerów pisma po innej cenie niż wydrukowana na okładce
Aktualności 6 – bez zgody wydawcy – jest działaniem na jego szkodę i skutkuje odpowiedzialnością sądową.

Pismo ukazuje się w następujących wersjach językowych:
Opis CD 8 polskiej , francuskiej , niemieckiej oraz włoskiej .

PHP Solutions Nr 1/2006 www.phpsolmag.org 5
Aktualności

PHPAudit
PHPAudit to rozbudowany i bardzo funkcjonal- Pro-PHP Podcast

C
ny dedykowany system dystrybucji i licencjono-
hcesz posłuchać, co mają do powie-
wania oprogramowania, które tworzymy w PHP.
Zapewnia wiele gotowych rozwiązań w zakresie dzenia czołowi programiści PHP na
licencjonowania skryptów, pozwalając ogra- świecie? Zacznij słuchać podcastów o te-
niczać ich wykonywanie do jednej instalacji,
określonej domeny lub podanych adresów IP. matyce związanej z PHP. Słowo podcast
PHPAudit może współpracować z koderem powstało z połączenia słów broadcasting
IonCube.
http://phpaudit.com/ (transmisja) oraz iPod (znany wszystkim
przenośny odtwarzacz plików mp3). W na-
PEAR 1.4.0 szym przypadku podcastami będą audycje
Wraz z wersją 1.4.0, instalator PEAR osiągnął
dojrzałość. Najważniejszą nowością jest zapisane w plikach MP3.
wprowadzenie obsługi i możliwości tworzenia PHP podcast to najbardziej udane
własnych kanałów (PEAR Channels), czyli
niezależnych od serwera pear.php.net repo- przedsięwzięcie tego typu dla świata PHP.
zytoriów pakietów, które możemy instalować W rozmowach, wywiadach i debatach
używając polecenia pear. Pozwala to na wy-
godną dystrybucję własnego oprogramowania.
biorą bowiem udział pionierzy PHP, czyli
PEAR 1.4.0 potrafi też rozpakowywać archiwa, ludzie najbardziej aktywni w środowisku.
w których umieszczono kilka aplikacji (jest to Za pośrednictwem audycji usłyszymy
tzw. bundling). Wydzielono również osobne
polecenie do obsługi archiwów PECL-a, a także ich komentarze, a także myśli i pomysły kie audycje są bezpłatne i dostępne dla
poprawiono wiele błędów z poprzednich wersji. odnoszące się do PHP. Tematyka kon- każdego, bez konieczności rejestracji lub
http://pear.php.net/
centruje się zdecydowanie wokół zaawan- wykonywania jakichkolwiek dodatkowych
Zend Core dla Oracle sowanych aspektów programowania. czynności. Podcast to doskonała metoda
Wynikiem współpracy firm Oracle i Zend Jeżeli więc chcemy posłuchać, co ma na odświeżenie słownictwa używanego
Technologies jest wypuszczenie na rynek wersji
beta silnika Zend Core dla Oracle. Jest on cer- do powiedzenia Greg Beaver na temat no- w środowisku programistów PHP, która
tyfikowanym, łatwym w instalacji środowiskiem wego PEAR-a, czy Paul M. Jones, twórca może okazać się szczególnie przydatna
PHP ze zintegrowaną obsługą bazy Oracle.
Oprogramowanie zawiera uaktualniony ste- Savanta i SOLAR-a, to nic nie stoi na podczas prezentacji konferencyjnej w ję-
rownik OCI8, dzięki czemu udało się uzyskać przeszkodzie. Wystarczy wejść na stronę, zyku angielskim.
większą stabilność i wydajność współpracy
z produktami bazodanowymi Oracle. Zamiarem
pobrać odpowiednią audycję i uruchomić Nowe audycje ukazują się z częstotli-
obu firm było stworzenie gotowego do użycia ją na komputerze albo na przenośnym wością jednej na dwa tygodnie.
środowiska PHP do zastosowań korporacyj- odtwarzaczu, np. w drodze do pracy czy
nych, ułatwiającego rozwijanie oprogramowania
opartego o PHP i bazę Oracle. podczas przerwy w zajęciach. Wszyst- http://www.pro-php.com/
http://zend.com

KPHPDevelop
KPHPDevelop to środowisko programistyczne MySQL 5.0RC

U
(IDE, ang. Integrated Development Environ-
kazała się prawdopodobnie najważ-
ment) pracujące pod KDE. Jest udostępniane
bezpłatnie i oferuje standardową funkcjo- niejsza odsłona systemu bazodano-
nalność, jak na aplikację tego typu. Koloruje wego MySQL w historii: 5.0 RC. Poprze-
składnię, podpowiada nazwy funkcji oraz
parametry wejściowe oraz pozwala na sprawne dziły ją liczne wersje beta. Liczba pobrań
zarządzanie wszystkimi plikami, które składają przekroczyła ponad dwa miliony.
się na projekt. Godna uwagi jest obsługa baz
danych, z analizatorem zapytań SQL na czele.
Piąta odsłona MySQL-a szykuje wie-
http://kphpdev.sourceforge.net le możliwości, które będą interesujące
przede wszystkim dla zaawansowanych to nierozerwalny blok operacji (zapytań),
Net_Curl
Ukazała się wersja 1.2.2 biblioteki Net_Curl, programistów. Istotę nowości wprowadzo- cechujący się tym, że gdy choć jedno
która wprowadza obiektowy interfejs do rozsze- nych w tej wersji dobrze oddają trzy sło- z nich nie będzie wykonane poprawnie,
rzenia cURL. Przyda się głównie programistom
tworzącym aplikacje zorientowane obiekto- wa: widoki (ang. views), wyzwalacze (ang. w systemie nie zostanie zapisany rezultat
wo. Wszystkie nazwy funkcji i ich parametry triggers) i transakcje (ang. transactions). żadnego z nich.
wejściowe pozostają bez zmian w stosunku
Widoki tworzymy w celu ogranicze- W MySQL 5.0 wprowadzono ponadto
do cURL. Jedyna różnica polega na tym, ze
odwołujemy się do nich jak do metod. nia dostępu do danych zgromadzonych wiele usprawnień wydajności (w szcze-
http://pear.php.net/package/Net_Curl w tabelach. Każdy widok może zawierać gólności InnoDB), obsługę kursorów,
HTML_Safe 0.9.0alpha1 dane umieszczone w różnych tabelach, zaawansowane zarządzanie procedurami
Wciąż rosnące zagrożenie ze strony ataków a użytkownik ma wrażenie, że pracuje na i funkcjami użytkownika. Rozszerzono
XSS powoduje powstawanie coraz większej
ilości bibliotek do walidacji wprowadzanych
normalnej, rzeczywistej tabeli. Stanowiące również rozmiar danych typu VARCHAR
przez użytkownika danych. XHTML_Safe to drugą nowość wyzwalacze są proce- do 65532 bajtów. Należy wspomnieć także
narzędzie należące do repozytorium PEAR, durami uruchamianymi pod warunkiem o implementacji dwóch nowych silników do
pozwalające dokładnie sprecyzować, jakie
dane mają prawo być kontrolowane, a jakie nie wystąpienia jakiegoś zdarzenia. Przykła- obsługi danych: ARCHIVE (rozległe bloby)
(możliwość stworzenia dokładnej listy tagów). dowo, gdy użytkownik spróbuje usunąć i FEDERATED (dla rozwiązań zdalnych).
Parser biblioteki usunie potencjalnie niebez-
pieczne części kodu HTML, w tym niezamknię- dane z którejś z tabel, wyzwalacz wykona Podsumowując: zmiany dokonane
te znaczniki, skrypty JavaScript oraz wszystkie zaprogramowaną wcześniej procedurę. w wersji 5.0RC MySQL-a są poważnym
inne tagi, które mogą się okazać zagrożeniem.
http://pear.php.net/package/HTML_Safe/
Można w ten sposób zapobiec rozher- argumentem na rzecz migracji na tę plat-
metyzowaniu danych. Trzeci element formę bazodanową.
wprowadzony w MySQL 5.0, transakcja, http://mysql.org

6 www.phpsolmag.org PHP Solutions Nr 1/2006
Aktualności

lchash 0.9.1
Zend Platform 2 LCHASH to mała biblioteka, która zapewnia

F
dostęp do natywnych tablic rozproszonych
irma Zend Technologies ogłosiła cje, błędy zaistniałe podczas połączeń,
(ang. Hash Tables), dostępnych w bibliotece
nową wersję swojej platformy do czy też błędy w samym kodzie. Teraz libC. Korzystając z tego mechanizmu można
zarządzania aplikacjami PHP. Zend Plat- będzie można prześledzić problemy, do- bardzo szybko i efektywnie tworzyć oraz
przechowywać duże porcje danych, używając
form 2 ma łączyć w sobie takie cechy jak cierając do ich źródła. przy tym jedynie pamięci podręcznej. Rozwią-
niezawodność, skalowalność i zgodność Nowa wersja platformy Zend to rów- zanie to jest co prawda trochę wolniejsze od
tablic PHP, lecz wykorzystuje znacznie mniej
wymaganą w ważnych aplikacjach biz- nież szybsze wykonywanie aplikacji PHP. pamięci.
nesowych. Dla bardzo obciążonych ser- PHP Performance Managment odpo- http://pecl.php.net/package/lchash
werów przewidziano Klastrowanie Sesji wiada za akcelerację kodu, dynamiczne
File 1.2.2
(ang. Session Clustering), czyli efektywną buforowanie danych wyjściowych oraz Ukazała się wersja 1.2.2 narzędzia File umożli-
wymianę danych sesyjnych pomiędzy kompresję danych, która pozwala ograni- wiającego obiektowy dostęp do plików. Zawiera
ona zestaw metod, które w znaczący sposób
serwerami pracującymi w strukturze kla- czyć do 10% nominalny transfer danych. przyspieszają wykonywanie operacji na plikach.
stra. W ten sposób serwery pracujące PHP/Java Integration Bridge to ko- Przydatny może okazać się także interfejs do
obsługi plików zapisanych w formacie CSV.
w klastrze mogą skuteczniej wyrównywać lejna ciekawa cecha Zend Platform 2. Licencja: PHP
obciążenie pomiędzy sobą. Wg oficjalnych Jak sama nazwa wskazuje, jest to most http://pear.php.net/package/File/
danych Zend, Klastrowanie Sesji powodu- pomiędzy światami PHP i Javy, umożli-
CodeGen_MySQL_UDF
je nawet dziesięciokrotny wzrost wydajno- wiający m.in. korzystanie z bibliotek i klas Istnieją dwa sposoby dodawania nowych funkcji
ści aplikacji. Javy z poziomu PHP (w tym bezpośred- do MySQL-a. Pierwszą jest wbudowanie ich
do natywnego kodu MySQL-a i skompilowanie
Kolejną ważną funkcjonalnością Zend nie wywoływanie metod tych klas). serwera bazodanowego. Drugą jest dodanie
Platform 2 jest PHP Intelligence: system Podsumowując: opisane rozwiąza- funkcji poprzez interfejs UDF (ang. user-defi-
oparty na zdarzeniach, służący do prze- nia dają ogromne możliwości i powinny nied function). Z tej możliwości korzysta biblio-
teka UDF_Gen, której zadaniem jest ułatwianie
prowadzania analizy aplikacji PHP w cza- zainteresować korporacje, które tworzą dodawania funkcji do MySQL-a. UDF_Gen
sie rzeczywistym. Jego wykorzystanie i korzystają ze złożonych aplikacji napisa- czyta konfigurację, prototypy funkcji i fragmenty
kodu z pliku XML i generuje gotowe do użycia
pozwoli nam na skuteczniejsze odnajdy- nych w języku PHP. rozszerzenie UDF. Przyda się każdemu, kto
wanie błędów spowodowanych ciągłymi chcąc przyspieszyć swoje aplikacje, powierza
niektóre zadania bazie danych.
zmianami dokonywanymi w kodzie pro- http://pear.php.net/package/CodeGen_
gramów. Zaprezentuje nieużywane funk- http://zend.com/ MySQL_UDF/

DB_QueryTool 1.0.1
DB_QueryTool to narzędzie pozwalające
Roundcube Webmail Project tworzyć zapytania bazodanowe w oparciu

C
o paradygmat obiektowy. Umożliwia budowanie
hoć bezpłatnych programów do ob- kwerend przy pomocy metod takich, jak
sługi poczty przez WWW jest wiele, setWhere(), setGroup(), setJoin(), itp.
W ten sposób rozległe zapytania wyglądają na
w Roundcube jest coś, co zwraca uwagę.
bardzo uporządkowane i takimi w istocie są.
Narzędzie to ma szczególnie estetyczny Dokonywanie późniejszych zmian w kodzie
i funkcjonalny wygląd, charakterystyczny staje się znacznie łatwiejsze. Warto jeszcze
dodać, że trendem w tworzeniu modułów
dla aplikacji pracujących na platformie działających pod PHP5 jest całkowite przejście
Macintosh. wszystkich bibliotek na model obiektowy.
http://pear.php.net/package/DB_QueryTool/
Roundcube jest łatwy w instalacji
i konfiguracji. Działa w oparciu o PHP PHPRunner
oraz systemy bazodanowe, takie jak: PHPRunner to narzędzie do zautomatyzowane-
go tworzenia interfejsów bazodanowych w PHP.
MySQL, PostgreSQL oraz SQLite (któ- Twórcy zapewniają, że za jego pomocą można
rego popularność rośnie wraz z rozwo- całkowicie uniknąć własnoręcznego programo-
wania. Generuje strony WWW na podstawie
jem PHP5). Wśród funkcji, które producent planu- danych wprowadzonych w panelu i pozwala
Motywy graficzne aplikacji stwo- je, znajduje się przekazywanie wiadomo- zabezpieczyć je hasłem oraz dodać formularz
logowania. Stworzony interfejs może być
rzone zostały w oparciu o standardy ści wraz z załącznikami, wyszukiwarka
wielojęzyczny. Wbudowany do PHPRunnera
projektowania stron WWW, takie jak wiadomości, obsługa filtrów i reguł, wspar- klient FTP pozwala automatycznie zamieszczać
XHTML i CSS 2.0. Roundcube jest cie dla standardu VCard (wizytówki), moż- stronę WWW na serwerze.
http://www.xlinesoft.com/phprunner/
aplikacją dostępną w różnych wersjach liwość importu oraz eksportu maili, edytor
językowych, obsługuje typy MIME i wia- wiadomości HTML (typu WYSIWYG), phpHtmlLib 2.5.4
Pojawiła się wersja 2.5.4 phpHtmlLib – zbioru
domości w formacie HTML. Pozwala narzędzia do sprawdzania pisowni czy klas i bibliotek służących do budowy, debugo-
również wysyłać załączniki (ang. at- wsparcie dla GPG/PGP. wania i przetwarzania dokumentów zapisanych
tachments) do wiadomości. W aplikację Produkt udostępniany jest na licencji w formatach XML, HTML, XHTML, WAP/WML
oraz SVG (Scalable Vector Graphics). Do
wbudowano prostą książkę adresową, GPL. Jego działanie przetestowano na phpHtmlLib dochodzi zaawansowany analizator
możliwość tworzenia katalogów i sys- przeglądarkach Firefox 1.0, Safari 2.0, formularzy, który pozwala budować złożone
formularze HTML/XHTML. W wersji 2.5.4 po-
tem buforowania, przyspieszający pra- Opera 8.0 oraz Internet Explorer 6.0. Ro- prawiono wiele błędów i dodano parę nowości,
cę całej aplikacji. Ponieważ Roundcube undcube działa w nich prawidłowo. takich jak możliwość szeregowania w klasie
FEComboListBox czy całkowicie nowe klasy
wciąż jest w stadium rozwoju alfa, wiele (DataObjectDataListSource i FEColorPicker).
opcji nie zostało jeszcze zaimplemento- Licencja: LGPL
wanych. http://roundcube.net/ http://phphtmllib.newsblob.com/

PHP Solutions Nr 1/2006 www.phpsolmag.org 7
Opis CD

Gregarius
Agregator RSS to aplikacja, która automa- HTML_Progress

P
tycznie pobiera i wyświetla aktualności z wielu
odczas przesyłania plików na ser-
kanałów RSS (również RDF i ATOM) jednocze-
śnie. Gregarius to kompletny skrypt pełniący wer przydatny jest pasek postępu.
rolę agregatora. Pobiera z wielu wskazanych HTML_Progress to biblioteka należąca
wcześniej źródeł internetowych świeże informa-
cje, aby je następnie przetworzyć i wyświetlić do repozytorium PEAR, która implemen-
na stronie głównej. Program oferuje sprawny tuje takowy. Jej możliwości są naprawdę
mechanizm przeszukiwania wiadomości
i generuje strony wynikowe w oparciu o XHTML zaawansowane. Mamy do wyboru ponad
i CSS. Do poważnych zalet narzędzia Grega- 30 różnych typów paska postępu, w tym
rius zalicza się również łatwa instalacja i prosty
pionowe, poziome, w postaci okręgu,
w obsłudze panel administracyjny.
Licencja: GPL eliptyczne, kwadratowe i prostokątne.
http://gregarius.net HTML_Progress może ponadto poka-
Libchart zywać liczbowo procent przesłanych
Tworzenie wykresów w PHP nie należy do danych.
najprzyjemniejszych czynności. Biblioteka Lib-
chart próbuje to zmienić, oferując bardzo prosty
Wszystkie elementy układu paska
interfejs do tworzenia wykresów słupkowych są w pełni konfigurowalne na poziomie
i kołowych. Produkt współpracuje z PHP4 HTML, a biblioteka spełnia warunki danych na serwer. Jedyne wymagania
i PHP5, a do pracy wymaga zainstalowanej
biblioteki GD obsługującej FreeType. Pozwala zgodności ze standardami XHTML stawiane przez aplikację to dostęp do
umieszczać etykiety danych (m.in. na osiach) i CSS. Najmniejszych problemów nie PHP oraz obsługa DHTML-a w przeglą-
oraz tworzyć legendę. Projekt ma w miarę do-
brą dokumentację (w tym tutorial). Najnowsza powinna również sprawić integracja darce WWW. Biblioteka została stwo-
wersja Libcharta nosi numer 1.0. biblioteki HTML_Progress z systemami rzona w oparciu o wzorzec projektowy
Licencja: GNU LGPL
http://naku.dohcrew.com/libchart
szablonów, takimi jak np. Smarty. Za Obserwator (ang. Observer), co po-
generowanie kolejnych etapów postępu zwala na dodawanie Listenerów. Pod-
User Files odpowiadają skrypty napisane w języku sumowując: HTML_Progress istotnie
Własna usługa w rodzaju e-dysku (dysku
internetowego, pozwalającego gromadzić JavaScript. wzbogaci funkcjonalność naszych stron
swoje pliki na koncie dostępnym przez prze- Możemy umieszczać wiele pasków WWW.
glądarkę WWW)? Z User Files to bardzo pro-
ste. Narzędzie to pozwala zarejestrowanym
postępu na jednej stronie i nie wymaga
użytkownikom przechowywać pliki na dysku to korzystania z wewnętrznych ramek Licencja: PHP License 3.0
naszego serwera. By skorzystać z usługi, (iframe). Użytkownik w każdej chwili http://pear.php.net/package/HTML_
każdy użytkownik musi założyć swoje konto,
podając swój prawdziwy adres e-mail. może zatrzymać proces przesyłania Progress/
Możliwe jest jednoczesne przesyłanie wielu
plików na serwer, określenie maksymalnych
limitów objętości oraz ustalenie dozwolonych
przez nas formatów plików. User Files potrafi PhpPeanuts

P
zmieniać rozmiary obrazów i dodawać do
nich opisy.
eanuts to zorientowany obiektowo
Licencja: Freeware framework do tworzenia aplikacji
http://www.playth.com/scripts/userfiles/ w PHP. Programowanie z jego użyciem
HTML_Table_Matrix 1.0.8 opiera się na architekturze kierowania
Łatwe i automatyczne tworzenie i uzupełnianie modelami (ang. Model Driven Archi- skrócić cykl rozwoju oprogramowania.
tabel staje się możliwe dzięki wykorzystaniu
biblioteki HTML_Table_Matrix. Jest ona bardzo
tecture), która jest tworem stosunkowo Wszystkie części frameworka są skalo-
zaawansowanym rozwiązaniem i pozwala do- świeżym, bazującym na założeniach walne, każda może zostać pominięta,
wolnie sortować dane, które chcemy wprowa-
programowania ekstremalnego (XP, ang. poszerzona lub uzupełniona.
dzić do tabeli (rosnąco, malejąco, losowo, itd.),
dbając przy tym o czystość kodu wynikowego eXtreme Programming). PhpPeanuts został napisany w PHP
tabeli. Podawanie wymiarów tworzonej tabeli PhpPeanuts pokazuje swoje zalety z wykorzystaniem HTMl-a i JavaScriptu.
nie jest wymagane, gdyż biblioteka sama doko-
na obliczeń na podstawie umieszczanych w niej już w bardzo wczesnych fazach rozwoju Może pracować na dowolnych konfigu-
danych. Do działania HTML_Table_Matrix oprogramowania. Wystarczy stworzyć kil- racjach i jest łatwy w instalacji. Intefejs
wymaga pakietu PEAR-owego HTML_Table.
Licencja: PHP v3.0
ka komponentów aplikacji i tabel w bazie użytkownika phpPeanuts całkowicie
http://pear.php.net/package/HTML_Table_ danych, a oprogramowanie automatycznie opiera się na wzorcu Model-Widok-Kon-
Matrix/
utworzy działający prototyp aplikacji. troler, czyli MVC (ang. Model-View-Con-
MP3_Playlist 0.5.0alpha1 Użytkownicy będą w stanie przete- troller). phpPeanuts pozwala na
Były już skrypty do odczytu zawartych w pli- stować prototyp i wyrazić swoją opinię jednoczesną obsługę wielu aplikacji na
kach MP3 tagów ID3, a teraz przyszła pora
narzędzie do obsługi list odtwarzania (ang. na jego temat, co w rezultacie pozwoli jednym serwerze.
playlists). MP3_playlist pozwala na tworzenie efektywnie dopracować i uzupełnić pro- Zdecydowanie polecamy ten frame-
i przetwarzanie tych list. Po uruchomieniu
gram. Twórcy frameworka przygotowali work zaawansowanym programistom,
rozpocznie przeszukiwanie folderów w celu
znalezienia plików MP3 i utworzenia z nich listy łatwe w użyciu komponenty do genero- którzy posiadają duże doświadczenie
odtwarzania. Plik wynikowy możemy zapisać wania tabel, zakładek, formantów, okien w zakresie programowania zorientowa-
w wielu formatach: M3U, SMIL, a nawet XML
i XHTML. Istnieje ponadto możliwość tworzenia dialogowych oraz wyszukiwania stron nego obiektowo.
kopii zapasowych list odtwarzania w oparciu w bazach danych.
o bazę SQLite.
Licencja: PHP Kiedy tylko dokonamy zmian, phpPe-
http://pear.php.net/package/MP3_Playlist/ anuts błyskawicznie zaktualizuje interfejs Licencja: Academic Free License v. 2.0
użytkownika. Pozwala to maksymalnie http://www.phppeanuts.org/

8 www.phpsolmag.org PHP Solutions Nr 1/2006
Opis CD

Time Management
PHP121 Instant Messenger Time Management to prosty, ale funkcjo-

P
nalny skrypt, który pełni rolę terminarza
HP121 Instant Messenger to komu-
zadań. Użytkownik widzi typowy miesięczny
nikator internetowy na stronie WWW, kalendarz podzielony na oznaczone datami
nie różniący się wyglądem od komunikato- kratki, w których wpisane są czynności do
wykonania o określonej godzinie. Nowe
rów działających po stronie klienta. zadania przypisujemy do konkretnych dni
Osoba, która chce korzystać i godzin, korzystając z bardzo wygodnego for-
mularza. Możemy także edytować i kasować
z PHP121 Instant Messengera na naszej zadania znajdujące się na liście. Tę pierwszą
witrynie, musi się najpierw zarejestrować, czynność wykonujemy również za pomocą
formularza. Zarządzanie kalendarzem jest
podając swój identyfikator (login), hasło
możliwe dla zalogowanego użytkownika. Time
oraz adres e-mail. Po zalogowaniu się Management wymaga dostępu do bazy My-
może rozmawiać z innymi użytkownikami, SQL 4+, a jego instalacja jest bardzo prosta.
Licencja: GPL
którzy w tym czasie przebywają w sieci. http://phptime.us/
Ciekawym zastosowaniem programu
może być ułatwienie obsługi klientów, oci8 1.1.1
Pojawiła się udoskonalona wersja interfejsu
którzy aktualnie znajdują się na naszym padku, gdy wybierzemy wydanie PHP- do obsługi baz Oracle. Narzędzie to korzysta
sklepie internetowym i chcieliby nas o coś Nuke, potrzebny będzie również system z Oracle Call Interface (OCI). W tym, jak
również poprzedzającym je o kilkanaście dni
zapytać. Jego głównym przeznaczeniem PHPNuke w wersji nowszej niż 6.0. poprzednim wydaniu poprawiono wiele błę-
jest jednak umożliwienie rozmów między Produkt dostępny jest w zasadzie dów, w tym błąd naruszenia ochrony pamięci
(segmentation fault) czy problemy pojawiające
zarejestrowanymi użytkownikami serwisu bezpłatnie, lecz aby otrzymać jego naj- się po użyciu funkcji oci_error() bez argumen-
internetowego. nowszą wersję, należy wesprzeć opro- tów. Wprowadzono też obsługę keszowania
oraz zewnętrznych list uwierzytelniających.
Istnieje osobne wydanie PHP121 gramowanie odpowiednim datkiem (co Dodano również nowe opcje konfiguracji
IM, stworzone z myślą o użytkownikach stanowi pewne kuriozum, gdy weźmiemy stałych połączeń oraz poprawiono błędne
pakietu PHPNuke. Integruje się ono z po- pod uwagę jego licencję). Tym niemniej, działanie funkcji oci_close(), odpowiedzialnej
za zamykanie połączenia z bazą.
zostałą częścią portalu. poprzednia wersja programu jest zawsze Licencja: PHP
Wymagania, jakie stawia PHP121 darmowa. http://pecl.php.net/package/oci8
Instant Messenger to parser PHP i baza Net_IPv6 1.0.5
MySQL. Po stronie przeglądarki musi być Licencja: GNU GPL Biblioteka Net_IPv6 pozwala na stwierdzenie
włączona obsługa JavaScriptu. W przy- http://www.php121.com/ czy wprowadzony adres IP jest adresem
z rodziny IPv6 –protokołu, który jest następ-
cą IPv4. Umożliwia też sprawdzenie, czy
adres IPv4 posiada końcówkę kompatybilną
DreamWeaver 8 z IPv6. Wśród jego funkcji jest również
kompresja i dekompresja adresów IPv6.

D reamWeaver to nazwa znana Wdrażanie protokołu IPv6 rozpoczęto w roku
2000, a jego wprowadzenie ma zaradzić pro-
każdemu chyba webmasterowi blemowi kończącej się puli adresów w IPv4.
i twórcy stron WWW. W ósmej odsłonie Licencja: PHP
tego produktu odnajdziemy sporo zmian. http://pear.php.net/package/Net_IPv6/

Oprogramowanie zaoferuje nam większą pecl_http 0.13.0
stabilność, wydajność, nowe narzędzia Biblioteka pecl_http implementuje rozsze-
rzoną obsługę protokołu HTTP. Umożliwia
oraz łatwiejszą obsługę. tworzenie bezwzględnych URI oraz spełnia-
W DreamWeaver 8 spotkamy się jących warunki RFC przekierowań. Zapewnia
zgodną z RFC obsługę daty. Pozwala parso-
z nowym pomysłem na menu, które
wać zarówno nagłówki, jak i treść wiadomo-
z rozwijalnego zamieniło się na zakład- ści. Umożliwia też buforowanie danych przez
kowe. Dla programistów przewidzianio użycie nagłówka Last-Modified, a także
wysyłanie danych, plików i potoków danych
nowy pasek narzędziowy usprawniają- ulepszony rendering powoduje, że wy- w różnych zakresach obsługi. Funkcjonal-
cy formatowanie. Tam, gdzie chcemy gląd strony stworzonej w edytorze jest ność biblioteki jest bardzo szeroka i przyda
się twórcom zaawansowanych aplikacji
lepiej zorganizować sobie duże partie bardzo zbliżony do tego, co zobaczymy internetowych.
kodu, przyda się możliwość rozwijania w oknie przeglądarki internetowej. Po- Licencja: PHP
http://pecl.php.net/package/pecl_http
i zwijania jego fragmentów. Zaznaczając prawiono również obsługę XML-a, CSS,
wybrane linie, grupujemy je, co pozwala PHP, WebDAV, ColdFusion 6.0 MX Math_Fraction 0.4.0
na ich zwinięcie (schowanie), a następ- i Flasha 8.0. Wciąż brakuje natomiast Biblioteka Math_Fraction pozwala na
wyświetlanie i manipulowanie ułamkami
nie rozwinięcie. wsparcia dla AJAX-a. Udoskonalono zwykłymi. Przy jej użyciu możemy dokony-
Pomyślano również o twórcach desi- za to synchronizację danych pomiędzy wać podstawowych operacji arytmetycznych
na ułamkach: porównywać je, odnajdywać
gnu, dodając opcję powiększania. Funk- edytowanym kodem a serwerem FTP.
największy wspólny dzielnik lub największą
cja ta, w połączeniu z linijką, pozwala Wszystkie operacje odbywają się w tle, wspólną wielokrotność dwóch liczb całkowi-
na bardzo precyzyjne ustalanie takich dzięki czemu nie trzeba czekać, aż pro- tych. Biblioteka pomoże przy upraszczaniu
(redukcji) ułamków, zwróci także jego odwrot-
parametrów, jak np. marginesy przygo- ces się zakończy. ność. Pozwala również przekształcić liczbę
towywanej strony. Usprawniono również całkowitą na ułamek. Math_Fraction znajduje
się obecnie w stadium rozwoju beta.
nawigator pozwalający poruszać się po Licencja: komercyjna Licencja: PHP
stylach CSS, który jest teraz łatwiej do- http://www.macromedia.com/software/ http://pear.php.net/package/Math_Fraction/
stępny i posiada swój własny panel. Zaś dreamweaver/

PHP Solutions Nr 1/2006 www.phpsolmag.org 9
Opis CD

File_Find 1.0.1
Jeżeli potrzebujemy biblioteki do przeszuki- PHP/Java Bridge

P
wania dużej ilości plików, powinniśmy zwrócić
HP/Java Bridge, czyli most pomiędzy chcą powoli rezygnować z rozwiązań
uwagę na bibliotekę File_Find. Obsługuje
poszukiwanie rekurencyjne (ang. recursi- PHP a Javą to moduł PHP, który po- opartych o Javę oraz integrować swoje
ve), co oznacza, że zagląda do wszystkich zwala na połączenie systemu obiektowe- oprogramowanie z PHP.
katalogów podrzędnych. Biblioteka pozwala na
odnajdywanie plików, których nazwy spełniają go PHP z systemem Javy lub ECMA. Jest Już dziś obserwujemy wzrastającą
postawione założenia. Możemy również szukać dostępny jako java.so (pod Linuksa) lub tendencję migracji z Javy na PHP. Wi-
ciągu znaków wewnątrz plików podając
określony szablon. File_Find sporządzi ponadto php_java.dll (pod Windows). Tam, gdzie dać to choćby po coraz liczniejszych
mapę plików w katalogu oraz rekursywną mapę to możliwe, implementuje JSR 223 (język projektach, próbujących przenieść funk-
wszystkich plików i katalogów podrzędnych.
skryptowy Javy) i może być używany do cjonalność Javy do PHP, co jest w dużej
Licencja: PHP
http://pear.php.net/package/File_Find/ połączenia z językami CLR (wspólnego mierze spowodowane nowym, ulepszo-
środowiska uruchomieniowego, stanowią- nym modelem (i silnikiem) obiektowym
HTTP_Session 0.5.1
Prezentowana biblioteka jest interfejsem cego podstawę frameworka Microsoftu PHP5.
obiektowym dla funkcji z rodziny session_*. .NET), takimi jak VB.NET, czy C#. Problemem PHP/Java Bridge może
Oprócz typowej zamiany nazw funkcji na nazwy
metod, mamy do czynienia z paroma dodatko-
Podstawowym środowiskiem, z któ- się jednak okazać jego jego niewystar-
wymi możliwościami, takimi jak składowanie rym moduł ten umożliwia połączenie, jest czająca wydajność. Zaleca się więc sto-
danych o sesjach w bazach danych przy użyciu Java, włączając w to również takie sowanie tego rozwiązania wszędzie tam,
pakietów (Pear) DB, MDB, MDB2. Pojawiają
się również nowe metody, w tym: isNew(), rozwiązania, jak KAWA czy Jruby. Aby gdzie nie istnieje zagrożenie przeciążenia
useCookies(), setExpire(), setIdle(), zapewnić efektywną wymianę informacji, serwera, czyli głównie w rozwiązaniach
isExpired(), isIdled() i inne.
Licencja: PHP most pomiędzy PHP, a Javą komunikuje intranetowych oraz ekstranetowych. Pod-
http://pear.php.net/package/HTTP_Session/ się z wirtualną maszyną Javy (ang. Java sumowując: kolejny dobrze pomyślany
Virtual Machine) przy użyciu lokalnych projekt, który przysparza popularności
crack 0.4
Ukazała się w pełni stabilna wersja narzędzia portów. Dzięki PHP/Java Bridge możemy PHP i pozwala temu językowi zdobywać
crack, nosząca nxumer 0.4. Zadaniem cracklib odwoływać się bezpośrednio do Javy, następne zastosowania i przełamywać
jest sprawdzanie jakości haseł. Dokonuje ono
setek testów, mających na celu ustalenie, korzystając z typowej składni PHP. Ten kolejne bariery.
czy wybrane przez nas hasło jest trudne do fakt w znaczący sposób ułatwia pracę
odgadnięcia, czy też nie. Wykorzystuje do tego
słownik, sprawdzając czy jako hasła nie używa-
z zewnętrznymi aplikacjami tworzonymi
my powszechnienie znanego słowa. Biblioteka w języku Java.
próbuje też znaleźć podobieństwo hasła do PHP/Java Bridge przyda się fir- Licencja: PHP License
nazwy użytkownika. Ostatecznie uzyskuje-
my informację, czy wybrane przez nas hasło mom, które doceniając możliwości PHP http://php-java-bridge.sourceforge.net/
spełnia podstawowe zasady bezpieczeństwa.
Crack bazuje na bibliotece libcrack, która jest
standardowo dostępna w większości systemów
unixowych. W wersji 0.4 narzędzia crack, za- PHPBeans

P
pewniono zgodność z PHP 4.1 oraz współpracę
z PEAR 1.4.0. Dodano także wersję binarną dla
HPBeans jest serwerem obiektów
Windows. i zgodnie z aktualną tendencją, sta-
Licencja: PHP nowi kolejne rozwiązanie przeniesione
http://pecl.php.net/package/crack
z Javy. Jest oparty o specyfikację RMI,
html2ps 0.7.1 czyli Remote Method Invocation (zdalne
Biblioteka html2ps oferuje funkcjonalność
zbliżoną do jej odpowiednika napisanego
wykonywanie metod). Jego działanie
w Perlu. Jej zadanie polega na konwertowa- opiera się na relacji klient-serwer. W roli pularnych protokołach dla Web Servi-
niu dowolnego dokumentu HTML (również
tego drugiego występuje PHPBeans ces, takich jak SOAP czy XML-RPC.
zawierającego DHTML) lub XHTML do formatu
PostScript. Pozwala ustawiać m.in. orientację Object Server, stanowiący repozytorium, Twórcy phpBeans Protocol wykorzystali
strony (pozioma lub pionowa) i rozmiar papieru w którym umieszczamy wszystkie nasze własne, dedykowane rozwiązania, by
w dokumencie wynikowym. Biblioteka radzi
sobie z obrazkami, skomplikowanymi tabelami klasy przeznaczone do udostępnienia komunikacja była łatwa, szybka i efek-
(również tymi używającymi tagów rowspan aplikacjom klienckim. tywna.
i colspan), warstwami, znacznikami <div> oraz
stylami CSS. W nowej wersji dokonano wielu
Interfejsem klienta jest z kolei Php- PhpBeans to jednak nie tylko opro-
udoskonaleń w metodzie odpowiedzialnej za Beans Client API. Może on połączyć gramowanie klienta i serwera. To przede
zwracanie dokumentu wynikowego (biblioteka
się zdalnie z serwerem w celu wykona- wszystkim metoda dostępu do klas
PDFLIB), m.in. poprawiono obsługę bloków.
Licencja: GPL nia zgromadzonych tam metod. Przyda ulokowanych poza naszym środowi-
http://www.tufat.com/script19.htm się to szczególnie tam, gdzie istnieje skiem pracy. PHPBeans to rozwiązanie
PHPlist 2.10.1 wiele rozproszonych instancji jednej kierowane do twórców zaawansowa-
PHPList to jeden z najpopularniejszych i naj- aplikacji. Istnieją dwie wersje oprogra- nych, komercyjnych rozwiązań na dużą
lepszych programów do masowego rozsyłania
mowania klienckiego: jedna z nich jest skalę.
poczty elektronicznej (mailingu) i tworzenia
newsletterów. Aplikację przeznaczono do obsługi przeznaczona dla PHP, a druga dla
wielu list mailingowych, wzbogacając ją o mecha- Ruby'ego (niezależny skryptowy język
nizm pozwalający gościom naszej strony zapisy-
wać się na wybrane listy. W nowej wersji (2.10.1) obiektowy). Oprogramowanie serwera
dodano wiele nowych funkcji. Najważniejszą i klienta pobieramy oddzielnie. Licencja: GNU GPL lub komercyjna (ser-
z nich jest edytor WYSIWYG, którego rolę pełni
znany produkt o nazwie FCK Editor 2, pracujący Do wymiany informacji między wer) oraz GNU LGPL (klienci dla PHP
zarówno pod przeglądarką Firefox, jak i Mozilla. serwerem a klientem służy phpBeans i Ruby'ego)
http://www.phplist.com
Protocol. Nie został on oparty na po- http://www.phpbeans.com/

10 www.phpsolmag.org PHP Solutions Nr 1/2006
Opis CD

GraPHPite

W ykresy w PHP możemy budować
od podstaw, ale znacznie prostsze
i wydajniejsze jest skorzystanie z goto-
wych, rozbudowanych narzędzi, takich
jak obiektowo zorientowana biblioteka
GraPHPite.
Pod względem swojej funkcjonal-
ności, biblioteka znacząco wyróźnia się
ponad przeciętność. GraPHPite pozwala
na szybkie generowanie wykresów linio-
wych, słupkowych, kołowych, opartych
o mapę geograficzną czy mających
kształt pajęczyny. Do tego dochodzą wa-
riacje poszczególnych typów wykresów.
Możliwe jest również łączenie paru typów
wykresów z legendą na jednej grafice,
a nawet rysowanie wykresu na wykresie. GraPHPite korzysta z czcionek TrueType. tworzenia trójwymiarowych wykresów
GraPHPite obsługuje antialiasing, półprze- Otrzymane wykresy możemy zapisać kołowych.
zroczystość i potrafi tworzyć gradientowe w formacie PNG lub JPEG. GraPHPite korzysta z biblioteki GD
tła. Pozwala na stosowanie skali logaryt- Docenieniem funkcjonalności bibi- lub GD2 i może współpracować z PHP4
micznej. lioteki GraPHPite jest umieszczenie jej i PHP5. Do biblioteki dołączana jest rozle-
Bibliotekę można w pełni dostosować w repozytorium PEAR. Będzie tam do- gła dokumentacja. Podsumowując: projekt
do własnych potrzeb. Dlatego też moż- stępna pod nazwą Image_Graph. Twórcy bardzo przydatny każdemu, kto chce two-
liwa jest zmiana wszystkich elementów GraPHPite planują dodanie obsługi więk- rzyć zaawansowane wykresy w PHP.
wyświetlanych jako grafika, od koloru szej ilości formatów wyjściowych, tj. SVG, Licencja: GNU LGPL
linii, aż po czcionkę używaną do opisów. SWF i PDF oraz zapewnienie możliwości http://graphpite.sourceforge.net/

eyeOS
e yeOS to niecodzienne rozwiązanie:
CMS przypominający swoim wyglą-
dem i zachowaniem okienkowy system
operacyjny, taki jak Windows czy Linux
z zainstalowanym środowiskiem X-Win-
dow. Działa w przeglądarce internetowej
i umożliwia wykonywanie typowych czyn-
ności biurowych, takich jak liczenie na kal-
kulatorze, tworzenie podręcznych notatek
czy edytowanie dokumentów tekstowych.
Obecnie w dystrybucji systemu znajduje
się 10 tego rodzaju aplikacji.
Przykładowo, aby dokonać obliczeń,
uruchamiamy pod eyeOS-em swój kal-
kulator, a gdy na chwilę nie będzie nam
potrzebny, minimalizujemy go i np. uru-
chamiamy edytor tekstu, który przypomina
Worda lub OpenOffice.org Writera. Każda
aplikacja działa w osobnym oknie, które
możemy dowolnie przesuwać w oknie
przeglądarki, a także minimalizować, po-
większać lub zamykać. (ma m.in. wersję polską, francuską, wło- cają do przenoszenia do niego kolejnych
Aplikacja dysponuje ponadto własnym ską i niemiecką). aplikacji ze świata PHP. Podsumowując:
komunikatorem oraz przeglądarką stron Produkt znajduje się jeszcze w fazie bardzo ciekawe rozwiązanie, które może
WWW. Nie zabrakło również narzędzia do rozwoju, a aktualną wersją pozostaje 0.8.3. zrewolucjonizować nasze podejście do
zarządzania plikami. Cały system sprawia EyeOS ma pewne problemy z obsługą kwestii związanych ze środowiskiem pracy
wrażenie funkcjonalnego i intuicyjnego, przeglądarek, choć nie szkodzi to zbytnio oraz interfejsami użytkownika w PHP.
bazując przy tym na nowoczesnym de- jego funkcjonalności. Z założenia jest rów- Licencja: GNU GPL
signie. eyeOS jest również wielojęzyczny nież rozbudowywalny, a jego twórcy zachę- http://www.eyeos.org

12 www.phpsolmag.org PHP Solutions Nr 1/2006
Opis CD

SugarCRM 3.5.0

J eżeli szukamy systemu do zarządza-
nia kontaktami z klientami (CRM),
bardzo możliwe, że to właśnie Sugar-
CRM spełni wszystkie nasze wymagania.
Oprogramowanie oferowane jest w dwóch która obalałaby dotychczasowe przeko- do uzyskania przychodów. Pokazuje,
wersjach. Pierwsza z nich jest bezpłatna nanie, że CRM-y to rozwiązania prze- jakie mamy możliwości zarobków w róż-
i dostępna na opensourcowej licencji znaczone wyłącznie dla najbogatszych nych dziedzinach naszej pracy. Przydatny
SPL (bazującej na MPL). Druga wersja firm. będzie też z pewnością kalendarz, peł-
SugarCRM-a jest komercyjna i ma nieco Jak już powiedzieliśmy, SugarCRM niący jednocześnie funkcję terminarza
większą funkcjonalność oraz zaplecze to narzędzie opensourcowe. System przypominającego o ważnych sprawach
w postaci pomocy technicznej. skupia wokół siebie społeczność progra- (spotkaniach, zadaniach, telefonach).
SugarCRM to aplikacja webowa mistów, dla których stworzono repozyto- Podstawowym zadaniem każdego
pracująca po stronie serwera. Jest prze- rium dodatków i udoskonaleń o nazwie systemu CRM jest składowanie pełnej
znaczona na platformę LAMP (Linux, SugarForge (analogia do SourceFor- korespondencji z danym klientem. Tak
Apache, MySQL, PHP), ale urucho- ge.net). W jego ramach swoje projekty stworzone archiwum spotkań, rozmów,
mimy ją wszędzie tam, gdzie jest PHP rozpowszechnia ponad 2000 programi- notatek i korespondencji emailowej po-
i MySQL, niezależnie od typu serwera stów. Znajdziemy tam zarówno pakiety zwala w szybki sposób prześledzić za-
WWW i systemu operacyjnego. Sugar- lokalizujące, jak i oprogramowanie po- chowania klienta lub znaleźć potrzebne
CRM działa w oknie przeglądarki WWW zwalające zintegrować naszego CRM-a informacje na jego temat. Bez wątpienia
i nie potrzebuje żadnych dodatkowych z systemami CMS takimi, jak Mambo. przyda się możliwość szybkiego dodawa-
modułów. Współpracuje ze wszystkimi SugarCRM pozwoli obsłużyć całą nia i zarządzania kontaktami. SugarCRM
przeglądarkami internetowymi. Niestety, armię ludzi, ograniczając jednocześnie pozwala importować kontakty zapisane
praca pod Internet Explorerem nie jest ich uprawnienia. Wszystkim spośród w formacie vCard, CSV, jak również całe
pozbawiona mankamentów, gdyż dane swoich pracowników możemy utworzyć bazy, z programów typu Microsoft Out-
przychodzące z serwera są szyfrowane, osobne konta, nadając im prawa do prze- look czy Act!2005.
a IE ma mniej wydajny algorytm deszy- glądania wyłącznie własnych materiałów. Pozyskiwanie nowych klientów ułatwi
frujący niż Firefox. Będąc szefem firmy, mamy możliwość moduł Przesłanki handlowe, a zakład-
SugarCRM przeznaczony jest dla przeglądania i nadzorowania efektów ka Okazje sprzedaży przyczyni się do
działów sprzedaży, marketingu i wspar- pracy wszystkich naszych podwładnych. wzrostu obrotów w firmie. Obsługę spraw
cia, w firmach, w których potrzebna jest Panel aplikacji zaprojektowano bardzo pozasprzedażowych ułatwi moduł Spra-
bardziej efektywna komunikacja z klien- funkcjonalnie. Poziomo rozmieszczono wy, pozwalajacy obsłużyć np. reklamacje
tami. W odróżnieniu od innych systemów zakładki, które stanowią menu całej gwarancyjne.
tej klasy, SugarCRM zrywa z konwencją aplikacji. Główna strona panelu pozwala SugarCRM oferuje znacznie więcej
systemu o wysokich kosztach, słabej szybko zorientować się, jakie w danym funkcji i nie sposób ich tu wszystkich
skalowalności i małych możliwościach dniu mamy spotkania i obowiązki, czym wymienić. Narzędzie znajduje się na
rozszerzania funkcjonalności. Zamiarem aktualnie się zajmujemy, jakie sprawy bardzo zaawansowanym etapie rozwoju
jego autorów było stworzenie aplikacji i zadania aktualnie prowadzimy. Tym, co i jest praktycznie pozbawione większych
o przystępnej, modułowej architekturze, rzuca się w oczy, jest wykres możliwych błędów projektowych. Gdy dodamy do
tego możliwość rozszerzania systemu,
uzyskamy znakomity, bezpłatny produkt.
Jeśli i to nam nie wystarczy, zawsze
możemy sięgnąć po komercyjną wersję
programu SugarCRM.

Licencja: GPL
Cena wersji komercyjnej (Sugar Professio-
nal): 239,00$
http://www.sugarcrm.com

14 www.phpsolmag.org PHP Solutions Nr 1/2006
Na CD
Narzędzia
UltraEdit-32 11.20 Programy z serii Ultra są 45-dniowymi trialami. Triale mogą być przedłużone na prośbę
UltraSentry v2.0 czytelników, którzy wyślą w tej sprawie mail na adres: idm@idmcomp.com
UltraCompare Professional v3.00
Roadsend Compiler for PHP 1.6 – 21-dniowy trial
Simpletest 1.0.0 – GPL e-biznes
php-time – GPL SugarCRM 3.5 – System do zarządzania
php-java-bridge-2.8.0 – GPL kontaktami z klientami na GPL!
php-mono-bridge-2.8.0 – GPL
EyeOS 0.8.4 – GPL
14 książek elektronicznych w tym 4 nowe
Samba-3 by example
IDE The Rise of Open Source Licensing
DreamWeaver 8 – 30-dniowy trial Linux in the Workplace
Maguma Workbench 2.6 – 30-dniowy trial Linux Device Drivers 3rd Edition
UltraStudio 05 – 45-dniowy trial z możliwością przedłużenia
na zasadach takich jak UltraEdit, Sentry oraz Compare
PHPDesigner – freeware PEAR
PhpwScite – GPL HTML_AJAX-0.2.3
SDO-0.6.0
CMS
Drupal 4.6 – GPL PHPUnit2-2.3.2
pecl_http-0.16.0
eZ Publish 3.7.0 – GPL
PDO
Bitrix Site Manager 4.0.5 – 30-dniowy trial. Czytelnicy PHP Solutions mają 5% zniżki
na pełną wersję programu. Szczegóły na www.bitrixsoft.com/phpsolutions
Content Manager 2.2.07 – Specjalna wersja – niewygasające demo

apisać pod a
proszę n dres
z płytą : cd
@s
ów of
b lem tw
are
p ro .co
z ie m
ra .p
W

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

na naszej stronie internetowej pod adresem www.phpsolmag.org/pl
Wszystkie listingi z artykułów zostały zamieszczone

Wszystkie listingi z artykułów zostały zamieszczone
Początki

Otwarty format OASIS
dla dokumentów biurowych
i CMS-ów
Bård Farstad

Wklejanie czystego tekstu do CMS-a i jego
ręczne formatowanie jest zadaniem żmudnym
i podatnym na błędy. Gdyby tak można było
stworzyć odpowiedni dokument w swoim
ulubionym pakiecie biurowym, a potem po
prostu skopiować plik i wkleić go do CMS-
a... Taką właśnie możliwość daje połączenie
trzech nowatorskich produktów: uniwersalnego
formatu OpenDocument, CMS-a eZ publish
i pakietu biurowego OpenOffice.org 2.0.

F
ormat OpenDocument, czyli otwar- zapisu dokumentów, niezależnej od
ty format dla aplikacji biurowych, wewnętrznych formatów poszczególnych
został opracowany przez kon- aplikacji. Wprowadzenie wspólnego for-
sorcjum OASIS (Organization for the matu oznacza dla użytkownika swobodę
Advancement of Structured Information wyboru aplikacji, a dla producentów opro-
Standards, czyli Organizację ds. Rozwoju gramowania zwiększoną konkurencję,
Strukturalizowanych Standardów Informa- a zarazem konieczność współpracy.
cyjnych) i jest propozycją standardowego Od strony technicznej, specyfikacja
W SIECI formatu zapisu i wymiany dokumentów OASIS jest dokumentem określającym
tworzonych w pakietach biurowych, metody opisu tekstu i treści multime-
1. http://www.oasis-
a więc arkuszy kalkulacyjnych, prezen- dialnych w typowych dokumentach
open.org/committees/ tacji, wykresów czy też tekstów. W tym pakietów biurowych. Standard definiuje
tc_home.php?wg_ artykule zajmiemy się przede wszystkim schemat XML pozwalający opisywać
abbrev=office – standard
OASIS OpenDocument dokumentami tekstowymi.
2. http://en.wikipedia.org/wiki/ Pierwsza wersja specyfikacji Open-
OpenDocument – wpis Powinieneś wiedzieć...
w Wikipedii na temat formatu Document powstała w wyniku współpra- Powinieneś znać podstawy pracy z PHP
OpenDocument cy organizacji zrzeszonych w ramach i eZ publish.
3. http://www.ez.no – strona
OASIS. Podstawą dla specyfikacji był
projektu eZ publish
4. http://www.ez.no/community/ XML-owy format plików OpenOffice.org,
Obiecujemy...
Po przeczytaniu artykułu będziesz
contribs/import_export/ jednak specyfikacja wprowadza też wiele
oasis_open_document_ wiedział jak przekazywać treść mię-
extension – rozszerzenie istotnych zmian. Format OpenDocument dzy programem OpenOffice.org Writer
OASIS dla eZ publish został zatwierdzony jako standard OASIS a systemem eZ publish oraz jak dołą-
5. http://www.ez.no/ czać obsługę formatu OpenDocument
documentation – pierwszego maja 2005 roku. Celem stan-
do aplikacji PHP.
dokumentacja eZ publish dardu jest dostarczenie wspólnej metody

16 www.phpsolmag.org PHP Solutions Nr 1/2006
Otwarty format dokumentów OASIS Początki

• OpenOffice.org 1.1.5 i 2.0,
• Scribus,
• TextMaker,
• Visioo Writer.

Migracja z formatów
poszczególnych aplikacji
Istniejące dokumenty zapisane we wła-
snych formatach aplikacji (na przykład
dokumenty MS Word) można konwerto-
wać do formatu OpenDocument za po-
mocą oprogramowania Open Source,
na przykład pakietu OpenOffice.org
– darmowej aplikacji obsługującej wiele
różnych formatów, w tym OpenDocument.
Konwersję dokumentów w OpenOffice.org
dodatkowo ułatwia możliwość wywoływa-
nia makr z linii poleceń. Oznacza to, że
wykorzystanie OpenOffice.org pozwala
nie tylko ręcznie konwertować dokumenty
Rysunek 1. Dokument edytowany w MS Word do formatu OpenDocument, ale również
automatyzować ten proces i wbudowywać
strukturę tekstu, nagłówków, list, tabel, Według Wikipedii, do aplikacji obsłu- go w inne aplikacje. Niektóre produkty (na
obiektów osadzonych i innych elemen- gujących format OpenDocument należą przykład eZ publish) już teraz obsługują tę
tów dokumentu. Wynikowy plik jest obecnie: metodę konwersji, choć korzystanie z ich
zwykłym archiwum ZIP, zawierającym możliwości wymaga zainstalowania wersji
pewną liczbę plików XML i ewentualnie • Abiword, 2 pakietu OpenOffice.org.
osadzonych w dokumencie obrazów lub • eZ publish,
obiektów multimedialnych. • IBM Workplace, Praktyczny przykład
Format OASIS OpenDocument jest • Knomos, Wykorzystanie standardu OpenDocu-
standardem otwartym i prawdopodobnie • KOffice, ment i oprogramowania Open Source do
stanie się w przyszłości standardem ISO.
Ponieważ jest to format otwarty, można
go używać do zapisu i wymiany informacji
w dowolnych aplikacjach.

Aplikacje obsługujące
format OpenDocument
Od czasu oficjalnego ogłoszenia standar-
du OpenDocument w maju 2005 r. pojawi-
ło się już kilka aplikacji ten format obsługu-
jących. Coraz szerszej obsłudze standar-
du w aplikacjach towarzyszy jego rosnąca
popularność wśród użytkowników.

Rysunek 2. Kopiowanie dokumentu do
systemu eZ publish za pośrednictwem
interfejsu WebDAV Rysunek 3. Dokument importowany do eZ publish

PHP Solutions Nr 1/2006 www.phpsolmag.org 17
Początki Otwarty format dokumentów OASIS

tworzenia, konwersji i udostępniania
danych prześledzimy na przykładzie.
Zaczniemy od stworzenia zwykłego do-
kumentu MS Word w formacie DOC. Do-
kument ten importujemy następnie do eZ
publish za pośrednictwem WebDAV, po
czym z pomocą pakietu OpenOffice.org
2.0 konwertujemy go do własnego forma-
tu danych eZ publish. Opisywany tu eZ
publish (http://www.ez.no) jest systemem
zarządzania treścią (CMS-em) klasy en-
terprise, napisanym w PHP i dostępnym
na zasadach Open Source.
Pierwszą czynnością będzie utworze-
nie w Wordzie zwykłego dokumentu, za-
wierającego sformatowany tekst i obrazek.
Ostateczny dokument powinien wyglądać
mniej więcej tak, jak na Rysunku 1 i po-
winniśmy go zapisać gdzieś na lokalnym
dysku. Następnym krokiem będzie prze-
niesienie zawartości tego dokumentu do
systemu eZ publish uruchomionego na in-
nym komputerze. Do samego przekazania
dokumentu posłużymy się obsługiwanym
przez eZ publish interfejsem WebDAV,
pozwalającym w prosty sposób kopiować
i publikować dokumenty. W tym przykła-
dzie użyjemy menedżera plików Konqu-
eror uruchomionego w linuksowym środo-
Rysunek 4. Edycja importowanego dokumentu w eZ publish
wisku graficznym KDE – dzięki WebDAV
wystarczy jedynie przeciągnąć dokument
z dysku windowsowego udostępnianego
poprzez SMB i „upuścić go” do eZ publish.
Rysunek 2 przedstawia widok interfejsu
WebDAV dla eZ publish z poziomu Ko-
nquerora – widoczny jest również plik
dokumentu przeciągany z dysku Windows.
eZ publish automatycznie rozpoznaje
format plików Worda publikowanych za
pośrednictwem interfejsu WebDAV i za
pomocą pakietu OpenOffice.org konwer-
tuje je na format OpenDocument. Zawar-
tość dokumentu po konwersji jest impor-
towana już bezpośrednio do systemu za-
rządzania treścią dzięki rozszerzeniu eZ
publish obsługującemu format OASIS. Po
zakończeniu importu, dokument jest od
razu dostępny jako artykuł opublikowany
w eZ publish – Rysunek 3 przedstawia
widok takiego artykułu.
Podstawowa wersja operacji importu
danych z formatu OpenDocument pobiera
całą sformatowaną treść, ale bez definicji
układu – importowane są na przykład
wszystkie nagłówki, ale już nie zdefiniowa-
ny w Wordzie rozmiar czy kolor czcionki.
Załóżmy, że chcemy taki artykuł edy-
tować za pośrednictwem standardowego
Rysunek 5. Eksport dokumentu z eZ publish interfejsu administracyjnego eZ publish

18 www.phpsolmag.org PHP Solutions Nr 1/2006
Otwarty format dokumentów OASIS Początki

Rysunek 6. Podgląd wydruku naszego
dokumentu w programie OpenOffice.org
Writer

– Rysunek 4 przedstawia ekran edycji
dokumentu. Jak widać, zachowywane
jest formatowanie zdefiniowane w ory-
ginalnym dokumencie, więc nagłówek,
lista wypunktowana i obraz pozostają na
swoich miejscach.
Teraz chcemy się zająć dalszą edy-
cją dokumentu, ale tym razem w progra- Rysunek 8. Wygląd bardziej złożonego dokumentu w programie OpenOffice.org Writer
mie OpenOffice.org Writer. W tym celu
wystarczy wyeksportować artykuł z eZ zakończeniu edycji w programie Open- Struktura pliku
publish do pliku w formacie OASIS Open- Office.org Writer. W porównaniu z pierwot- OpenDocument
Document. Rysunek 5 pokazuje, jak eks- nym dokumentem, dodaliśmy nagłówek Bez większego wysiłku można rozbudo-
portować artykuł z eZ publish. i stopkę strony oraz zdefiniowaliśmy styl wać dowolną aplikację PHP o obsługę
Po eksporcie dokument, możemy do- czcionki – elementy opisane w warstwie standardu OpenDocument – format pliku
wolnie modyfikować jego styl, na przy- prezentacji formatu OpenDocument. jest szczegółowo udokumentowany, a do
kład nakładając zwykły szablon Open- Po zapisaniu zmodyfikowanego do- generowania i zapisu dokumentów wystar-
Office.org. Pozwala to, w bardzo prosty kumentu w OpenOffice.org możemy go czają standardowe narzędzia PHP. Sam
sposób, utrzymywać jednolitą stylistykę ponownie importować do systemu eZ pu- plik OpenDocument (o rozszerzeniu .odt)
dokumentów, a podstawowy szablon blish z pomocą funkcji zastępowania (do- jest zwykłym archiwum ZIP, więc wystar-
można rozbudować na przykład o nagłów- kadnie funkcja Repleace OpenOffice.org czy go po prostu rozpakować, by przyjrzeć
ki i stopki stron. Rysunek 6 przedstawia w menu kontekstowym). Pozostaje już się jego zawartości (Rysunek 10).
podgląd wydruku naszego dokumentu po tylko podziwiać efekt końcowy, czyli arty- Jak sama nazwa wskazuje, plik me-
kuł eZ publish po edycji w OpenOffice.org ta.xml zawiera metadane dokumentu,
i ponownym imporcie (Rysunek 7). a więc datę utworzenia, czas edycji, sta-
W tym przykładzie skorzystaliśmy tystyki (np. liczbę słów i akapitów) itd. Plik
z bardzo prostego dokumentu. Rysunek 8 mimetype zawiera po prostu typ MIME
przedstawia dokument o znacznie bar- dokumentu – w naszym przypadku bę-
dziej złożonym formatowaniu, importowa- dzie to application/vnd.oasis.opendocu
ny bezpośrednio z pliku OpenDocument ment.text. W pliku styles.xml znajdują się
stworzonego w OpenOffice.org. Import definicje czcionek, wyrównania kolorów
treści do eZ publish zachowuje formato- i innych atrybutów stylu. Wreszcie plik
wanie dokumentu, ale szczegóły wyglądu settings.xml zawiera ustawienia interfejsu
artykułu są zależne od aktualnych usta- użytkownika dla aplikacji, w której ostat-
wień (Rysunek 9). nio edytowano dokument, a manifest.xml
Poznaliśmy więc przykład podstawo- wyszczególnia wszystkie pliki składające
wych możliwości wykorzystania otwartych się na dokument.
standardów, pozwalających bezproble- W tym przypadku będzie nas intere-
Rysunek 7. Aktualizacja artykułu mowo wymieniać treści między różnymi sować plik content.xml oraz wszelkie pliki
importowanego do eZ publish aplikacjami i systemami. znajdujące się w podkatalogu Pictures

PHP Solutions Nr 1/2006 www.phpsolmag.org 19
Początki Otwarty format dokumentów OASIS

– plik content.xml zawiera faktyczną treść
dokumentu, więc tam będziemy szukać
danych, natomiast w katalogu Pictures
spodziewamy się znaleźć plik osadzonego
w dokumencie obrazu w formacie JPEG.
Schemat XML używany w ramach
formatu OpenDocument wykorzystuje
przestrzenie nazw, więc stosowany do
przetwarzania dokumentów parser musi
je obsługiwać. Listing 1 przedstawia
uproszczoną wersję pliku content.xml
(usunąłem z niego znaczniki niezwiązane
z podstawową strukturą dokumentu). Opi-
sywany dokument składa się z nagłówka
i akapitu tekstu. Treść tą ujęto w znacznik
<office:text>, który zawiera się kolejno
w znacznikach <office:body> i <office:
document-content>.
Tu uwaga: oglądanie plików XML
generowanych przez OpenOffice.org jest
znacznie wygodniejsze po wyłączeniu ich
optymalizacji (menu Narzędzia -> Opcje
-> Ładuj/Zapisz -> Ogólne) – pliki bez
optymalizacji są dużo bardziej czytelne.

Wykorzystanie formatu
OpenDocument
w aplikacjach PHP
Rysunek 9. Bardziej złożony dokument po imporcie do eZ publish Wiemy już, na czym polega wymiana
danych OpenDocument między klien-
Listing 1. Dokument XML opisujący nagłówek i akapit tekstu tem i serwerem, pora więc zająć się
implementacją obsługi tego formatu
<office:document-content> w naszych aplikacjach PHP. Napisze-
<office:body>
my skrypt pobierający treści tekstowe
<office:text>
<text:h text:style-name="Heading_20_1" text:outline-level="1">To jest
z pliku ODT i przekształcający je na
nagłówek</text:h> stronę XHTML. Najpierw musimy się
<text:p text:style-name="Text_20_body"> zaopatrzyć w parser DOM XML – w tym
To jest zwyczajny akapit.</text:p> przykładzie wykorzystamy wchodzącą
</office:text>
w skład środowiska eZ publish bibliote-
</office:body>
</office:document-content>
kę eZ xml, ale równie dobrze sprawdzi
się dowolny inny parser XML.
Listing 2. Parsowanie zawartości pliku .odt w PHP Spójrzmy na przykładowy kod z Li-
stingu 2. Zaczynamy od wypisania tekstu
<h1>Test importu XML z pliku OpenDocument</h1>
HTML informującego o teście importu
<hr />
<?php XML-a z pliku OpenDocument, po czym
include_once('lib/ezxml/classes/ezxml.php'); wstawiamy poziomą linię. Kod PHP za-
// $xml jest instancją eZXML, a $dom - modelem obiektowym dokumentu XML czynamy od dołączenia biblioteki XML,
$xml = new eZXML(); której nakazujemy następnie wczytanie
$dom =& $xml->domTree(file_get_contents("documents/simpledoc/content.xml"));
danych XML i utworzenie dla nich drze-
$bodyNodeArray = $dom->elementsByNameNS( 'body',
'urn:oasis:names:tc:opendocument:xmlns:office:1.0' ); wa DOM. Potrzebne nam dane znajdują
// sprawdzenie, czy w pliku występuje pojedynczy znacznik ciała dokumentu się wewnątrz znacznika <body>, więc
if ( count( $bodyNodeArray ) == 1 ){ próbujemy pobrać zawartość węzła body,
$bodyNode =& $bodyNodeArray[0]; podając jego nazwę i URI przestrzeni
$xhtmlTextBody = "";
nazw. Warto zwrócić uwagę, że identyfi-
foreach ( $bodyNode->children() as $childNode ){
$xhtmlTextBody .= handleNode( $childNode ); // dopisywanie wynikowego kodu XHTML katorem przestrzeni nazw nie jest tu sam
} przedrostek office, ale pełna nazwa za-
print( $xhtmlTextBody ); sobu (URN), czyli urn:oasis:names:tc:
} opendocument:xmlns:office:1.0. Jest to
konieczne, gdyż przedrostek przestrzeni

20 www.phpsolmag.org PHP Solutions Nr 1/2006
Otwarty format dokumentów OASIS Początki

nazw jest jedynie aliasem dla pełnej na- kod nagłówka HTML odpowiedniego
zwy zasobu i może ulec zmianie. poziomu. Potem wystarczy już tylko do- ���������������
Sprawdzamy, czy znaleźliśmy dokład- pisać tekst nagłówka wraz ze stosownymi
��������
nie jeden pasujący węzeł drzewa DOM, co znacznikami do zmiennej $htmlTextCon-
������������
pozwala dodatkowo zweryfikować popraw- tent, w której przechowujemy zwracany ��������
ność dokumentu (powinien być tylko jeden przez funkcję kod HTML.
znacznik <body>). Gdy już mamy węzeł Przetwarzanie znacznika <p> jest nie- ����������
body, możemy przejść po wszystkich jego co bardziej skomplikowane. Węzeł akapitu �������������
�����������
węzłach potomnych w pętli foreach. Do może mieć węzły potomne, po których
przetwarzania węzłów potomnych stwo- przechodzimy w pętli foreach. W bloku ��������
rzyłem funkcję handleNode(), zwracającą switch sprawdzamy nazwę bieżącego
wyświetlany na końcu kod HTML. węzła – w naszym prostym przykładzie ��������
Kod funkcji handleNode() przedstawia obsługiwane są jedynie węzły o nazwach
������������
Listing 3. Zaczynamy od sprawdzenia #text i image, a napotkanie dowolnej
nazwy bieżącego węzła (czyli znacznika) innej nazwy znów kończy się komunika- ����������
w bloku switch. Dla potrzeb tego przy- tem Nieobsługiwany element. Zawartość
kładu obsłużymy jedynie <h> oraz <p> węzła #text jest doklejana do zmiennej
Rysunek 10. Zawartość pliku .odt
– napotkanie dowolnego innego znacz- przechowującej treść akapitu, natomiast
– struktura plików i katalogów
nika spowoduje wyświetlenie komunikatu w przypadku węzła image (czyli obrazu),
Nieobsługiwany element. będzie nas interesować tylko ścieżka do <img>. Dodatkowo usuwamy pierwszy
W przypadku znacznika <h> (czyli na- pliku obrazu, więc pobieramy wartość znak ciągu pobranego z href, dzięki
główka), zaczynamy od pobrania atrybutu atrybutu href węzła obrazu i używamy jej czemu otrzymujemy względną ścieżkę
level, na podstawie którego generujemy do zbudowania HTML-owego znacznika do pliku, nadającą się do bezpośrednie-
go użytku. Po przetworzeniu wszystkich
Listing 3. Kod funkcji handleNode() węzłów potomnych dopisujemy ich zawar-
tość oraz zamykający znacznik </p> do
function handleNode( $node ){ zmiennej przechowującej wynikowy kod
$xhtmlTextContent = "";
HTML akapitu.
switch ( $node->name() ){
case 'h' : {
$level = $node->attributeValueNS( 'level', Podsumowanie
'urn:oasis:names:tc:opendocument:xmlns:text:1.0' ); W tym artykule starałem się pokazać moż-
if ( $level >= 1 && $level <= 6 ){ liwości wykorzystania standardu OASIS
$xhtmlTextContent.="<h$level>".$node->textContent()."</h$level>";
OpenDocument w codziennej pracy i wy-
}
else print( "Nieobsługiwany poziom nagłówka" );
mianie informacji. Aplikacje Open Source
}break; dają tu ogromne możliwości, w tym łatwe
case 'p' : { przechodzenie z własnych formatów
$paragraphContent = ""; aplikacji (np. Worda) na formaty otwarte
foreach ( $node->children() as $childNode ){
oraz możliwość bezpośredniej edycji
switch ( $childNode->name() ){
case "#text" : {
konwertowanego dokumentu. Prześledzi-
$paragraphContent .= $childNode->content(); liśmy też przykład edycji treści w różnych
}break; aplikacjach i kontekstach, przekonując się,
case "image" : { że dane mogą równie dobrze występować
$href = ltrim( $childNode->attributeValueNS( 'href',
w postaci tradycyjnych dokumentów, jak
'http://www.w3.org/1999/xlink' ), '#' );
$paragraphContent .= "<img src='$href' alt=''/>";
i artykułów publikowanych w Internecie.
}break; Wszystko to pokazuje, jak wiele mogą
default: { zdziałać producenci oprogramowania, gdy
print( "Nieobsługiwany element: " . $childNode->name() . ustalą wspólny standard i zaimplementują
"<br>" );
go w swoich aplikacjach. n
}break;
}
}
$xhtmlTextContent .= '<p>' . $paragraphContent . '</p>';
}break; O autorze
default: {
print( "Nieobsługiwany element " . $node->name() . "<br/" ); Bård Farstad jest jednym z założycieli
} i głównym deweloperem eZ systems.
} CMS-ami profesjonalnie zajmuje się od
return $xhtmlTextContent; 1999 roku. Jest autorem wielu projek-
} tów oraz bibliotek dla PHP (np. ez XML
?> – parser XML-a).
Kontakt z autorem: bf@ez.no

PHP Solutions Nr 1/2006 www.phpsolmag.org 21
Narzędzia

Programowanie sterowane
testami za pomocą PHPUnit
Timo Haberkern

Im większy projekt programistyczny, tym
trudniej wyłapywać pojawiające się w nim
błędy, a usunięcie jednych usterek powoduje
często powstanie następnych, w innej części
aplikacji. Ręczne tworzenie testów dla setek
klas jest nieskuteczne, a poza tym przyprawia
o ból głowy i paraliżuje pracę. Z pomocą
przychodzi PHPUnit: narzędzie pozwalające
zautomatyzować proces tworzenia i wdrażania
testów.

W
yobraźmy sobie fikcyjną, łanie innego? Odbywa się to za pomocą
obiektowo zorientowaną apli- predefiniowanych testów. W każdym z nich
kację PHP, która składa się sprawdzana jest konkretna funkcjonalność
z bardzo wielu klas. Między tymi klasami wybranej klasy, a zwrócony rezultat funkcji
istnieją oczywiście zależności, co oznacza jest porównywany z wartością oczekiwaną.
również, że błędy występujące w klasie Jeżeli oba wyniki są równe, to znaczy,
bazowej powodują kolejne w klasach po- że badana funkcja działa poprawnie.
tomnych. W aplikacji zawierającej kilkaset W przeciwnym wypadku mamy do czynie-
klas może to doprowadzić do poważnych nia z błędem. PHPUnit pozwala na łatwe
problemów, gdyż nie możemy po każdej pisanie testów i gromadzenie ich większej
zmianie w klasach bazowych testować liczby w tzw. zbiorach testów (PHPUnit:
od nowa całej aplikacji. Przynajmniej TestSuites). PHPUnit sprawdza wszyst-
dotychczas tak było! W świecie Javy od kie testy w zbiorze i zwraca nam liczbę
dawna możemy przeprowadzać testy
zwane jednostkowymi (ang. unit tests). Co należy wiedzieć...
Są one wykonywane automatycznie przez Powinieneś znać podstawy programo-
framework testowy, a ich rezultatem jest wania obiektowego w PHP.
W SIECI m.in. liczba testów zakończonych powo-
dzeniem i niepowodzeniem. Stworzone
Co obiecujemy...
Po przeczytaniu artykułu będziesz wie-
przez Sebastiana Bergmanna narzędzie dział, jak automatycznie i bez zbytniego
1. http://www.phpunit.de – stro-
ny projektu PHPUnit PHPUnit jest frameworkiem zapewniają- wysiłku sprawdzać działanie aplikacji
2. http://www.testdriven.com cym tę funkcjonalność w PHP. PHP za pomocą testów jednostkowych
– witryna o TDD i frameworka PHPUnit. Artykuł bazuje na
3. http://www.junit.org – projekt Zapytacie pewnie, jak to możliwe, że
wersji 2.2.1 PHPUnit.
jUnit jeden program testuje automatycznie dzia-

22 www.phpsolmag.org PHP Solutions Nr 1/2006
PHPUnit Narzędzia

Instalacja przy użyciu PEAR-a Listing 1. Interfejs klasy słowni-
PHPUnit jest częścią repozytorium PEAR, więc aby go zainstalować, wpisujemy w linii kowej Dictionary dla pierwszych
poleceń: pear install --alldeps phpunit2. Dzięki użyciu parametru --alldeps zainsta- przykładów
lowane zostaną także wszystkie wymagane przez PHPUnit biblioteki.
<?php
Instalacja bez PEAR-a class Dictionary{
Instalacja PHPUnit jest możliwa również bez PEAR-a. W tym celu pobieramy go spod public function
adresu http://pear.php.net/package/PHPUnit. Potrzebujemy również pakietów Log addTranslation($strEnglish,
(http://pear.php.net/package/Log) i Benchmark (http://pear.php.net/package/Benchmark). $strTranslated){
Wszystkie pakiety rozpakowujemy w wybranym katalogu naszego serwera, a następnie }
dopisujemy ścieżkę tego folderu do parametru include_path w pliku php.ini. Jeżeli nie public function
mamy dostępu do pliku php.ini, możemy podawać ścieżkę include_path w każdym getTranslation($strEnglish){
skrypcie za pomocą wyrażenia: }
public function
ini_set("include_path", removeTranslation($strEnglish){
"path/to/phpunit/library".PATH_SEPARATOR.ini_get("include_path")); }
public function isEmpty(){
Ręczna instalacja nie jest, niestety, identyczna z PEAR-ową. Brakuje na przykład konso- }
lowej komendy phpunit, której używamy w tym artykule do wykonywania testów. Bez niej
public function loadFromDb(){
musimy się ograniczyć do skryptów PHP, które działają na serwerze WWW. Dlatego, o ile
}
to możliwe, powinniśmy zawsze wybierać instalację PEAR-ową.
public function saveToDb(){
}
Instalacja Xdebug private $m_hashTranslations;
W celu uzyskiwania informacji o działaniach zachodzących w kodzie, PHPUnit używa
}
rozszerzenia PHP o nazwie Xdebug (http://www.xdebug.org). Aby je zainstalować pod
?>
Windows, wystarczy je pobrać (w postaci pliku DLL) spod adresu http://www.xdebug.org/
link.php?url=xdebug200b1-50-win, a następnie zapisać w katalogu rozszerzeń PHP (php/
ext). Później dodajemy nazwę tego pliku do listy rozszerzeń PHP, dopisując w php.ini na-
stępującą linijkę: zend_extension_ts="c:\php\ext\xdebug-5.0-2.0.0beta1.dll".
Po restarcie serwera Xdebug jest do naszej dyspozycji. Ważne jest, aby zainstalować dziedziczy po klasie PHPUnit_Framework_
Xdebug jako rozszerzenie Zend, gdyż w przeciwnym razie nie będziemy mogli go stoso- TestCase. Na Listingu 2 pokazujemy nasz
wać wraz z PHPUnitem. Jeśli więc w naszym pliku php.ini jest już wpis: extension=php_ przykładowy test. Przy pomocy dwóch te-
xdebug.dll, to musimy go usunąć lub zablokować znakiem komentarza. Instalacja
stów jednostkowych chcemy się upewnić,
Xdebuga pod Linuksem jest nieco bardziej skomplikowana, a jej dokładny opis znajdziemy
pod adresem http://www.xdebug.org/install.php. czy funkcja ta pracuje tak, jak oczekujemy.
Aby wykonać test z Listingu 2, musimy
przejść do katalogu z testem i wykonać na
przeprowadzonych bezbłędnie oraz listę Pierwszy test konsoli następującą komendę:
tych, które nie zostały zakończone pozy- Tyle teorii – teraz czas na przyjrzenie się
tywnie (Rysunek 1). Rysunek 2 pokazuje tworzeniu i wykonywaniu testów. Dla tego phpunit FillTest
jeszcze raz zależności między opisanymi i następnych przykładów będziemy testo-
tutaj komponentami (testami i metodami). wać klasę, która implementuje słownik Wynikiem testu jest krótkie zestawienie
Ogromną zaletą testów jednostkowych polsko-angielski. Powinna ona zawierać pokazujące, ile testów zostało przepro-
w porównaniu do testów przeprowadza- metody umożliwiające dodawanie, ka- wadzonych oraz czy zakończyły się one
nych ręcznie przez programistę jest ich sowanie i odczytywanie tłumaczeń słów. sukcesem. Oprócz łącznej liczby testów
szybkość i możliwość dowolnej liczby W dalszej części artykułu rozszerzymy tę otrzymujemy ilość bezbłędnych i błędnych
powtórzeń. Wykonanie takiego zbioru klasę o pewne właściwości, jak np. połą- sprawdzeń. PHPUnit rozróżnia tutaj Fa-
testów wymaga bardzo niewiele czasu czenie bazodanowe. Na Listingu 1 przed- ilures oraz Errors. Errors to błędy, które
i (w zależności od liczby testów) i pokrywa stawiamy interfejs klasy Dictionary, której nastąpiły w czasie przetwarzania samego
dużą część naszego kodu. Umożliwia nam używamy w przykładach. PHP (np. wyjątki). Failures to z kolei testy,
to sprawdzenie funkcjonowania naszej W naszym pierwszym teście chcemy przy których klasa zwróciła wartości inne
aplikacji w najdogodniejszym czasie, np. sprawdzić, czy metoda isEmpty() naszej od oczekiwanych. Rysunek 3 pokazuje
pod koniec dnia pracy czy po wprowadze- klasy Dictionary działa poprawnie. Każ- każdorazowo przykład bezbłędnego
niu zmian. dy test utworzony za pomocą PHPUnit i błędnego przebiegu testu. Widzimy, że
w naszym przypadku zostały przeprowa-
dzone dwa testy. Po starcie testu PHPUnit
wykonuje każdą metodę, której nazwa
zaczyna się od test. Właściwe testo-
wanie odbywa się za pomocą obecnych
w PHPUnit metod assert. W metodzie
testIsEmptyWithoutFill() używamy
assertTrue(). Jeśli wywołanie funkcji
Rysunek 1. Strona z wynikami zbioru testów isEmpty() zwraca wartość true, to znaczy,

PHP Solutions Nr 1/2006 www.phpsolmag.org 23
Narzędzia PHPUnit

Tests run: 1, Failures: 1, Errors: 0,

������������ ������� Incomplete Tests: 0.

������ ��������
Ponieważ komunikaty zwracane przez nie-
������ �������� które metody assert, np. assertEqual() są
wyczerpujące, więc dodawanie własnych
������
������� informacji nie zawsze jest konieczne.
��� ��������
Gromadzenie testów w zbiory
������ �������� Z reguły nie chcemy wywoływać pojedyn-
czego testu. Postępowanie, które opisa-
��������� liśmy nie sprawdziłoby się w przypadku
dużej liczby testów. Na szczęście również
������ ������
tutaj PHPUnit oferuje nam wygodne me-
�������
������� chanizmy. Większą liczbę testów możemy
umieścić w zbiorze testowym (TestSuite),
a następnie wywołać pojedynczym po-
leceniem. Listing 4 pokazuje, jak można
Rysunek 2. Zasada działania zautomatyzowanych testów dodawać pojedyncze testy do TestSuite
za pomocą metody addTest(). TestSuite
że nasza klasa pracuje prawidłowo, jako że (jako parametr) własne komunikaty, które możemy następnie wykonać poleceniem:
nie mamy żadnych słówek w naszym słow- zostaną wyświetlone w przypadku nie-
niku. Jeżeli jednak isEmpty() zwróci false, powodzenia testu. Jeżeli zastąpimy linię phpunit DictionaryTestSuite
to mamy do czynienia z błędem. Założenie z metodą assert na Listingu 3 zapisem:
assertTrue() będzie niespełnione i PHPU- Wynik, który zobaczymy na ekranie, odpo-
nit stwierdzi błąd. Zadaniem metod assert $this->assertTrue("samolot" == $strResult, wiada temu, co znamy z dotychczasowych
jest porównywanie oczekiwanego wyniku "Tłumaczeniem 'plane' jest pojedynczych wywołań.
ze zwróconym przez funkcję. Do dyspo- '$strResult'. Oczekiwano: 'samolot'")
zycji mamy kilka rodzajów tych metod, setUp() i TearDown()
niektóre z nich przedstawiamy w Ramce to w przypadku niepowodzenia testu otrzy- Jeśli przyjrzymy się uważnie Listingowi 1,
Metody assert. Będziemy z nich korzystać mamy następujący komunikat: zwrócimy uwagę na funkcje loadFromDb()
w następnych przykładach. i saveToDb(). Jak sugerują nazwy, odpo-
Metoda testująca może zawierać F wiadają one za ładowanie (load) i zapisy-
więcej metod typu assert. Można to wy- Time: 0.004560 wanie (save) słownika do bazy danych.
korzystać, by sprawdzać wartości zależne There was 1 failure: Załóżmy, że w bazie znajduje się kilka
od siebie. 1) testTranslation(TranslateTest) tysięcy słów, więc ich ładowanie trochę
Tłumaczeniem 'plane' jest 'error'. potrwa. Ponieważ wiele testów korzysta
Ulepszone Oczekiwano: 'samolot'. z wypełnionego słownika, więc ładowanie
komunikaty o błędach FAILURES!!! danych dla każdego pojedynczego testu
Komunikaty zwracane przez PHPUnit
w razie zakończenia testu niepowodze-
niem mówią czasami niewiele. Załóżmy,
że metoda getTranslation() zwraca
nieprawidłowy rezultat, co spowoduje, że
nasz test z Listingu 3 zakończy się nastę-
pującym komunikatem:

F
Time: 0.012339
There was 1 failure:
1) testTranslation(TranslateTest)
FAILURES!!!
Tests run: 1, Failures: 1, Errors: 0,
Incomplete Tests: 0.

Informacje tego rodzaju nie są zbyt po-
mocne, zwłaszcza gdy mamy do czynienia
z dużą liczbą testów. Dlatego wszystkim
metodom assert możemy przekazywać Rysunek 3. Bezbłędne i błędne przebiegi testów w PHPUnit

24 www.phpsolmag.org PHP Solutions Nr 1/2006
PHPUnit Narzędzia

miałoby niewielki sens. Również z tego po-
wodu klasa PHPUnit2_Framework_TestCase Metody assert
oferuje metodę setUp(). Metoda ta jest PHPUnit oferuje szeroką paletę metod assert:
wywoływana przed wykonaniem testu, • assertTrue() – oczekuje ona, że wyrażenie zwróci wartość true, w przeciw-
stanowi więc idealne miejsce na załado- nym wypadku mamy do czynienia z niepowodzeniem testu. Przykład: $this->
wanie słownika z bazy. Musimy ją jedynie assertTrue($expected == $result).
nadpisać w naszej własnej klasie PHPUnit_ • assertFalse() – metoda przeciwna wobec assertTrue(). Oczekuje, że wyrażenie
zwróci wartość false.
Framework_TestCase i dokonać inicjalizacji.
• assertEquals() – za jej pomocą możemy sprawdzić równość dwóch wyników. Wywo-
Użycie tej funkcji ma sens wszędzie tam, łanie $this->assertEquals($expected, $result) odpowiada wywołaniu $this->
gdzie chcemy mieć dostęp do tych sa- assertTrue($expected == $result). Ma jednak tę przewagę nad assertTrue(), że
mych danych z różnych testów lub gdzie w przypadku niepowodzenia otrzymujemy bardziej szczegółowy komunikat.
• assertSame() – ta metoda sprawdza identyczność dwóch obiektów. Odpowiada
inicjalizacja używanych klas jest uciążliwa, wywołaniu $this->assertTrue($expected === $result). Również w tym wy-
a chcemy je stosować w wielu testach. padku mamy metodę przeciwną – assertNotSame().
Rzadziej niż setUp() używamy metody • assertNull() – sprawdza, czy wynik jest równy null. Jeśli nie, test kończy się
tearDown(). Jest ona przeciwieństwem
niepowodzeniem. Metodą przeciwną jest assertNotNull().
• assertRegExp() – ten assert używa wyrażenia regularnego do sprawdzenia wy-
setUp(), gdyż jest wywoływana po zakoń-
niku. Niepasujący wzór jest traktowany jako niepowodzenie. Za pomocą $this->
czeniu testów. Możemy w niej np. zwalniać assertRegExp('/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,5})+$/', $result)
użyte zasoby, takie jak połączenia bazo- możemy na przykład sprawdzić, czy został zwrócony adres e-mail i czy jego for-
danowe, wskaźniki do danych, itp. Użycie mat jest prawidłowy.
• assertType() – sprawdza, czy wynik jest wskazanego typu. Jeżeli zwrócony
obu metod przedstawiamy na Listingu 5. typ nie odpowiada oczekiwanemu, porównanie kończy się niepowodzeniem.
Przykładowo, $this->assertType('string', $result) pozwala sprawdzić, czy
Analiza pokrycia kodu wynik jest typu string. Metodą przeciwną jest assertNotType().
Skąd mamy wiedzieć, czy napisaliśmy • assertContains() – sprawdza, czy dany łańcuch (string) jest zawarty w innym
stringu. Jeżeli nie, zostaje zwrócone niepowodzenie. Przykładowe wywołanie:
wystarczającą liczbę testów dla naszej $this->assertContains('f','foo'). Metoda przeciwna to assertNotContains().
aplikacji? PHPUnit oferuje nam możliwość
sprawdzenia, czy nasze testy obejmują
cały kod. Uruchomiony z parametrem linii phpunit --coverage-html Musimy przygotować dwie rzeczy.
komend --coverage-html PHPUnit two- FillTest.html FillTest PHPUnit tworzy wprawdzie plik HTML
rzy plik HTML, który pokazuje kompletne ze wszystkimi koniecznymi informacjami
źródło przetestowanej klasy. Linie, które Na podstawie utworzonego pliku HTML o pokryciu kodu, jednak używa zewnętrz-
zostały przetestowane, są podświetlone. łatwo widzimy, czy potrzebujemy dal- nego pliku CSS (codecoverage.css). Plik
Na Rysunku 4 przedstawiamy tego rodza- szych testów, czy też te przeprowadzone ten nie jest tworzony, ani też nie znajduje
ju plik dla pierwszego przeprowadzonego dotychczas wystarczają, aby w pełni się w katalogach instalacyjnych PHP-
w tym artykule testu. Korzystając z konsoli, przetestować nasz kod źródłowy. Niestety, Unit. Na Listingu 6 przedstawiamy więc
uruchamiamy phpunit z następującymi braki w dokumentacji utrudniają używanie plik CSS, którego można używać jako
parametrami: tej opcji. codecoverage.css. Kolejną przeszkodą

R E K L A M A

PHP Solutions Nr 1/2006 www.phpsolmag.org 25
Narzędzia PHPUnit

w używaniu tej opcji jest zależność od
rozszerzenia xdebug, które jest zainsta- Metoda test-first
lowane na niewielu serwerach. W ramce W tym artykule pisaliśmy testy, które sprawdzają funkcjonalność klas już istniejących:
Instalacja opisujemy więc, jak można owe najpierw utworzyliśmy kod źródłowy w PHP, a potem odpowiedni test. Można jednak
postępować inaczej. Ze świata Extreme Programming (XP) pochodzi metoda test-first,
rozszerzenie zainstalować, aby PHPUnit czasem zwana również Test-Driven Development (TDD). Inaczej niż w dotychczasowych
mógł z niego korzystać. przykładach, w których pisaliśmy testy do już istniejących klas, tutaj najpierw definiujemy
PHPUnit potrafi też zapisywać infor- interfejs klasy jako pustą funkcję z listą parametrów (podobnie jak na Listingu 1). Następnie
macje o pokryciu kodu także w plikach tworzymy testy dla wszystkich możliwych zastosowań. Co się stanie, gdy wykonamy taki
zbiór testów? Wszystkie testy zakończą się niepowodzeniem, ponieważ testowana klasa
tekstowych (--coverage-text) oraz nie zawiera żadnego kodu. Potem implementujemy wszystkie metody. Jeżeli żaden test nie
w formacie, który może być łatwo używany zakończy się niepowodzeniem, to klasa jest gotowa.
przez inne programy (--coverage-data). Zaletą tej metody jest to, że przed implementacją funkcji możemy przemyśleć jej prze-
bieg i napisać dla niego test. Dzięki temu, gdy klasa zostanie zaimplementowana, będziemy
już dysponowali solidnym i obszernym zbiorem testów pozwalających stwierdzić, czy pra-
Funkcje pomocnicze cuje ona poprawnie. To, którą metodę postępowania wybierzemy, zależy od indywidualnych
Jak widzimy, tworzenie testów wymaga upodobań.
pisania sporej ilości kodu. Aby ułatwić nam
to zadanie, PHPUnit pozwala generować
szkielety testów, które następnie uzupełnia- Listing 2. Pierwszy test (FillTest.php)
my o właściwą funkcjonalność. Aby to zre-
alizować, wywołujemy polecenie phpunit <?
z parametrem będącym nazwą pliku, dla require_once ("Dictionary_inc.php");
require_once ("PHPUnit2/Framework/TestCase.php");
którego powinien być utworzony szkielet:
class FillTest extends PHPUnit2_Framework_TestCase {
phpunit --skeleton Dictionary. Szkielet function testIsEmptyWithoutFill() {
wynikowy dla naszej klasy Dictionary $objDictionary = new Dictionary();
przedstawiamy na Listingu 7. $this->assertTrue($objDictionary->isEmpty());
Zwrócenie wyników testu na konsoli }
function testIsEmptyWithFill() {
może się niekiedy okazać niewystarcza-
$objDictionary = new Dictionary();
jące. W przypadku większych zespołów $objDictionary->addTranslation("car", "samochód");
programistycznych testy są przeprowa- $this->assertFalse($objDictionary->isEmpty());
dzane o określonej porze, z reguły w nocy. }
Następnie wykonywany jest często sze- }
?>
reg procesów towarzyszących. Tak więc
oprócz testów jednostkowych tworzona Listing 3. Test zwracający niewiele mówiące komunikaty o niepowodzeniu.
jest dokumentacja API (np. za pomocą
phpDocumentora), a także bekap danych <?php
require_once ("dictionary_inc.php");
i synchronizacja systemu działającego live.
require_once ("PHPUnit2/Framework/TestCase.php");
Dlatego ważne jest, aby wyniki testów były class TranslateTest extends PHPUnit2_Framework_TestCase {
pokazywane nie tylko na ekranie, ale także function testTranslation() {
zapisywane do pliku, ażeby można było $objDictionary = new Dictionary();
w późniejszym czasie obejrzeć rezultaty. $objDictionary->addTranslation("car", "samochód");
$objDictionary->addTranslation("plane", "samolot");
Aby coś takiego zrealizować, wywołujemy
$strResult = $objDictionary->getTranslation("plane");
phpunit z parameterem --log-xml oraz $this->assertTrue("samolot" == $strResult);
nazwą pliku docelowego. }
}
phpunit --log-xml result.xml ?>

DictionaryTestSuite
Listing 4. Gromadzenie pojedynczych testów w TestSuite

Przykład pliku XML utworzonego przy <?php
takim wywołaniu przedstawiamy na Ry- require_once "PHPUnit2/Framework/TestSuite.php";
sunku 5. Plik ten może być użyty także require_once "FillTest.php";
require_once "TranslateTest.php";
do automatycznej generacji dokumentacji
class DictionaryTestSuite {
testowej w formacie HTML lub PDF za public static function suite() {
pomocą XSLT. $suite = new PHPUnit2_Framework_TestSuite;
$suite->addTest(new FillTest("testIsEmptyWithoutFill"));
Organizacja testów $suite->addTest(new FillTest("testIsEmptyWithFill"));
$suite->addTest(new TranslateTest("testTranslation"));
jednostkowych
return $suite;
Teraz, gdy dysponujemy całą wiedzą po- }
trzebną nam do pisania testów jednostko- }
wych, możemy się zająć ostatnią otwartą ?>
sprawą: jak organizować własne testy?

26 www.phpsolmag.org PHP Solutions Nr 1/2006
PHPUnit Narzędzia

Jak to często bywa, nie ma tutaj jedno-
znacznej odpowiedzi. W praktyce najczę-
ściej stosujemy dwa wzorce. Nazywają się
one Inline-Testing i Outline-Testing. Róż-
nica leży w miejscu zapisywania testów.
W przypadku Inline-Testing kod testu jest
zapisywany w samej testowanej klasie.
Dzięki temu zawsze wiemy, gdzie znajduje
się kod testowy danej klasy i możemy go
łatwiej dopasować, gdy zmienimy samą
klasę.
Kolejna zaleta takiego podejścia
ujawnia się, gdy używamy systemu kon-
troli wersji, jak np. CVS czy Subversion,
bowiem test jest zawsze dopasowany
do odpowiedniej wersji klasy. Wspólne
zapisywanie kodu testującego i klasy
Rysunek 4. Analiza pokrycia kodu za pomocą PHPUnit. Linie, które zostały przetesto-
funkcjonuje tylko dlatego, że pliki klas wane, są podświetlone
zazwyczaj nie są bezpośrednio wywoły-
wane, lecz dołączane w innych plikach
za pomocą include. Możemy z tego
skorzystać, aby umieścić kod testujący
w pliku PHP. Trzeba jeszcze tylko dopi-
sać zapytanie, które rozstrzyga, czy plik
będzie dołączany przez include, czy też
wywołany przez PHPUnit. Listing 8 poka-
zuje ów warunek.
W przeciwieństwie do Inline-Testing,
w przypadku Outline-Testing, kod te-
stujący jest zapisywany oddzielnie od Rysunek 5. Wynik testów w formacie XML
testowanej klasy. Outline-Testing omija
niektóre problemy, które tam się pojawia- językiem interpretowanym, kod testujący w tym artykule. Pliki z testami możemy
ją. Jeżeli kod testujący jest zapisywany jest przy każdym wywołaniu parsowany oddzielać na kilka sposobów. Rysunek
razem z kodem klasy, to znajduje się rów- na nowo, co zajmuje czas. Dlatego może 6 pokazuje trzy możliwości zapisywania
nież w późniejszej wersji produkcyjnej, okazać się, że oddzielenie kodu testują- testów. Wielu programistów zapisuje
jeśli klasy zostaną umieszczone na ser- cego od klas testowanych ma sens – po- pliki testowe w tym samym katalogu, co
werze w Internecie. Ponieważ PHP jest stępujemy tak we wszystkich przykładach klasy. Jest to najprostsze, ale niestety
mało przejrzyste rozwiązanie. Innym,
Listing 5. Zastosowanie metody setUp() dla wspólnie używanych danych bardzo często stosowanym wyjściem jest
utworzenie osobnego, przeznaczonego
<?php dla testów folderu tests w danym podka-
require_once ("dictionary_inc.php"); talogu z klasami. Pozwala to na wyraźne
require_once ("PHPUnit2/Framework/TestCase.php");
class TranslateTest extends PHPUnit2_Framework_TestCase {
function setUp() { Listing 6. Arkusz stylów CSS (co-
$this->m_objDictionary = new Dictionary(); decoverage.css) do formatowania
$this->m_objDictionary->loadFromDb();} pliku z informacjami o pokryciu kodu
function testTranslation() {
$strResult = $this->m_objDictionary->getTranslation("plane"); td.ccLineNumber, td.ccCoveredLine,
$this->assertTrue("samolot" == $strResult, td.ccUncoveredLine,
"Tłumaczeniem 'plane' jest '$strResult'. Oczekiwano 'samolot'");} td.ccNotExecutableLine{
function testIncorrectWord() { font-family: Verdana, Arial,
$strResult=$this->m_objDictionary->getTranslation("not in dictionary"); monospace;
$this->assertNull($strResult);} font-size: 10pt;
function testResultType() { white-space: pre;}
$strResult = $this->m_objDictionary->getTranslation("plane"); td.ccLineNumber {
$this->assertType("string", $strResult);} background-color: #aaaaaa;}
function testSize() { td.ccCoveredLine {
$this->assertTrue($this->m_objDictionary->getSize() != 0);} background-color: #F0F7B9;}
private $m_objDictionary; td.ccNotExecutableLine {
} color: #aaaaaa;}
?>

PHP Solutions Nr 1/2006 www.phpsolmag.org 27
Narzędzia PHPUnit

oddzielenie testów od właściwego kodu.
Najbardziej radykalnym posunięciem jest
całkowite oddzielenie kodu od aplikacji.
Możemy wówczas przy jej ładowaniu na
serwer internetowy wysłać wszystko poza
wybranym katalogiem z testami, a więc
nasza aplikacja nie posiada plików, które
nie są jej konieczne do funkcjonowania.
Organizacja testów pozostaje tak czy in-
aczej w gestii programisty. W zależności
od wielkości projektów każda z metod ma
Rysunek 6. Możliwości oddzielenia testów od klas testowanych swoje wady i zalety.

Listing 7. Część automatycznie utworzonego szkieletu testowego dla klasy słow- Podsumowanie
nikowej PHPUnit jest interesującym projektem,
który bardzo ułatwia kontrolowanie bez-
<?php błędności naszej aplikacji w trakcie jej
if (!defined("PHPUnit2_MAIN_METHOD")) {
pisania. Drastyczne zmniejszenie nakła-
define("PHPUnit2_MAIN_METHOD", "DictionaryTest::main");
} dów pracy odczujemy przede wszystkim
require_once "PHPUnit2/Framework/IncompleteTestError.php"; dzięki automatycznemu dodawaniu testów
require_once "PHPUnit2/Framework/TestCase.php"; dla każdej metody, które uwalnia nas od
require_once "PHPUnit2/Framework/TestSuite.php"; żmudnego, ręcznego wykonywania tych
require_once "Dictionary.php";
operacji. W polączeniu z łatwością obsłu-
// Test class for Dictionary.
// Generated by PHPUnit2_Util_Skeleton on 2005-09-11 at 18:09:24. gi PHPUnita, możliwości te zadają kłam
class DictionaryTest extends PHPUnit2_Framework_TestCase { pokutującemu przekonaniu, że z powodu
public static function main() { konieczności dotrzymania terminów nie ma
require_once "PHPUnit2/TextUI/TestRunner.php"; czasu na testowanie programu w czasie
$suite = new PHPUnit2_Framework_TestSuite("DictionaryTest");
jego powstawania.
$result = PHPUnit2_TextUI_TestRunner::run($suite);
} Przyszłość PHPUnita rysuje się jesz-
// @todo Implement testAddTranslation(). cze ciekawiej. Niedługo ukaże się wersja
public function testAddTranslation() { dla PHP 5.1, korzystająca z jego nowych
throw new PHPUnit2_Framework_IncompleteTestError; możliwości, a także (co nie ma związku
}
z PHP 5.1) oferująca funkcjonalność
// @todo Implement testGetTranslation().
public function testGetTranslation() { taką, jak atrapy obiektów (ang. mock
throw new PHPUnit2_Framework_IncompleteTestError; objects) – to drugie zbliża ją do pierwo-
} wzoru ze świata Javy, którym jest jUnit.
// @todo Implement testRemoveTranslation(). Autorzy zapowiadają również możliwość
public function testRemoveTranslation() {
tworzenia testów przy użyciu interfejsu
throw new PHPUnit2_Framework_IncompleteTestError;
} graficznego (GUI) działającego pod GTK.
// @todo Implement testIsEmpty(). Ma on zostać napisany, gdy pojawi się
public function testIsEmpty() { GTK dla PHP5. W odległej przyszłości
throw new PHPUnit2_Framework_IncompleteTestError; planowana jest integracja PHPUnita ze
}
środowiskami programistycznymi, takimi
}
if (PHPUnit2_MAIN_METHOD == "DictionaryTest::main") { jak Eclipse, Zend Studio czy Maguma
DictionaryTest::main(); Workbench. Jak będzie, pokaże czas,
} a na razie warto zacząć korzystać z do-
?> stępnych możliwości PHPUnita, wdraża-
jąc go w swoich projektach. n
Listing 8. Umiejscowienie testu wewnątrz pliku klasy

<?php
class example {
// some class code here
} O autorze
if ( strstr($_SERVER['PHP_SELF'], "TextUI/TestRunner.php") != false ) {
require_once ("PHPUnit2/Framework/TestCase.php"); Timo Haberkern jest architektem opro-
class exampleTest extends PHPUnit2_Framework_TestCase { gramowania biznesowego w firmie EME-
// your test cases here DIA OFFICE. Poza tym bierze aktywny
} udział w rozwijaniu wielu projektów open
} source w językach PHP, Java i Java-
?> script.

28 www.phpsolmag.org PHP Solutions Nr 1/2006
Narzędzia

Drupal, czyli wielodomenowe,
wielojęzyczne i modularne
portale w oparciu o AJAX i SEO
Uwe Hermann

Czy potrzebujesz systemu zarządzania treścią
(CMS) ogólnego zastosowania, będącego w stanie
obsługiwać w jednej instancji kilka niezależnych
serwisów WWW, z których każdy dostępny ma
być w kilku wersjach językowych? Czy pełna
internacjonalizacja powinna być możliwa za
pomocą zaledwie kilku kliknięć myszą? Może
chciałbyś także dodać do swojego serwisu
elementy dynamiczne korzystające z AJAX, albo
zwiększyć jego popularność dzięki zastosowaniu
najlepszych technik SEO? Nie musisz dalej
szukać: wypróbuj system Drupal.

D
rupal to opensourcowy, napisany categorizing) i znakowanie (ang. tagging)
w PHP CMS. Opiera się na rela- treści, optymalizacja pod kątem wyszuki-
cyjnej bazie danych (MySQL lub warek (ang. Search Engine Optimization,
PostgreSQL). Jest modularny, elastyczny SEO) oraz użycie technologii AJAX w in-
i skalowalny. Rdzeń systemu Drupal, po- terfejsie użytkownika. W artykule zajmuje-
dobnie jak większość modułów i motywów my się przyszłą, jeszcze niewydaną wersją
graficznych przygotowanych przez jego Drupala o numerze 4.7, która będzie ofe-
użytkowników, udostępniany jest na licen- rowała wiele ciekawych rozszerzeń w sto-
cji GNU General Public License. sunku do bieżącej wersji stabilnej.
Drupal ma potężne możliwości
w porównaniu z rozwiązaniami konkuren-
cyjnymi, a jednocześnie jest wystarczająco Powinieneś wiedzieć...
Powinieneś znać PHP, Apache'a i My-
elastyczny, by mógł być wykorzystywany SQL-a. Przydatna będzie również wiedza
na serwisach WWW dowolnego rodzaju: ogólna o systemach CMS oraz o SEO.
portalach, stronach osobistych, forach
W SIECI dyskusyjnych, blogach, fotoblogach czy Obiecujemy...
Po przeczytaniu artykułu będziesz wie-
podcastach – możliwości jest wiele.
dział, jak za pomocą Drupal stworzyć
Pokażemy, jak wykorzystać Drupala wielodomenowy, wielojęzyczny serwis
1. http://drupal.org – oficjalna
strona domowa Drupal do zbudowania złożonego serwisu portalo- WWW. Skupimy się przy tym na niektó-
2. http://cvs.drupal.org – repo- wego, koncentrując się na najbardziej za- rych spośród jego najbardziej zaawanso-
zytoria CVS Drupal wanych elementów, takich jak instalacja
3. http://drupaldocs.org – doku- awansowanych komponentach systemu,
dla wielu serwisów, internacjonalizacja,
mentacja API programistycz- takich jak wielowitrynowość, czyli instalacja interfejsy użytkownika AJAX oraz Search
nych Drupal
4. http://opensourcecms.com
dla wielu serwisów (ang. multi-site installa- Engine Optimization.
– strona o CMS tion), wielojęzyczność, kategoryzacja (ang.

30 www.phpsolmag.org PHP Solutions Nr 1/2006
Drupal Narzędzia

Etymologia Drupal Instalacja
Poniższe instrukcje dotyczą bieżącej wersji HEAD Drupal w CVS, więc po pojawieniu się
Projekt Drupal został powołany do życia
wydania 4.7 konieczna będzie zmiana numeru wersji w kilku miejscach.
przez Driesa Buytaerta w roku 2000.
Jego nazwa pochodzi od holenderskiego Ÿ Pobierz i rozpakuj archiwum Drupala do katalogu /var/www/testsite:
słowa druppel, które znaczy kropla (ang.
drop). Dries Buytaert wybrał taką nazwę, mkdir /var/www/testsite
ponieważ przypadkowo wpisał drop.org
wget http://drupal.org/files/projects/drupal-cvs.tar.gz
zamiast dorp.org (dorp to po holendersku
tar xfvz drupal-cvs.tar.gz
wioska) szukając domeny dla serwisu
mv drupal-cvs/* drupal-cvs/.htaccess /var/www/testsite
społeczności, na którym planował korzy-
stać z Drupala. Nazwa drop przyjęła się
Ÿ Stwórz bazę danych dla Drupala i załaduj odpowiednie skrypty bazodanowe:
i tak narodził się Drupal.
mysqladmin -u database_user_name -p create dbtestsite mysql -u
database_user_name -p dbtestsite < database/database.mysql
Koncepcja
Zmodyfikuj odpowiednio zmienne $db _ url i $base _ url w sites/default/settings.php:
naszego portalu Ÿ

Pokażemy możliwości Drupala budu- $db_url = "mysql://nazwa_użytkownika:hasło@serwer_bazodanowy/dbtestsite";
jąc prosty portal dla społeczności osób $base_url = "http://www.example.com/testsite";
interesujących się darmową, legalnie
Ÿ I to wszystko. Nasz serwis jest dostępny pod adresem http://www.example.com/
dostępną w Internecie muzyką. Witryna testsite.
będzie ukierunkowana na fanów i artystów
niezależnych. Ma zapewniać fora dysku- Kroki opcjonalne, ale zalecane:
syjne, blogi, cotygodniowe głosowanie
Ÿ Stwórz katalog files/ z prawem zapisu przez serwer WWW. W katalogu tym system
i wiele więcej. Planujemy stworzenie Drupal będzie przechowywał przesyłane przez klientów obrazki oraz inne pliki:
odrębnego serwisu dla każdego gatunku
muzyki, gdzie wykorzystamy wielowitry- mkdir /var/www/testsite/files
nowość Drupala. Każdy z tych serwisów chmod 777 /var/www/testsite/files

będzie miał poddomenę w ramach naszej Ÿ Stwórz zadanie crona pytające regularnie o plik cron.php. Przykładowo, aby wywo-
wspólnej domeny example.com. Główny ływać tę czynność co godzinę, należy dodać do /etc/crontab następującą linijkę:
serwis będzie dostępny pod adresem http:
0 * * * * wget -O - -q http://www.example.com/testsite/cron.php
//www.example.com; osobne poddomeny
dla gatunków będą nosiły nazwy takie, jak
Wymagania
http://rock.example.com, http://blues.exam- Minimalne wymagania instalacyjne Drupala to: PHP (PHP4 od wersji 4.3.3 lub PHP5),
ple.com czy http://electronica.example.com. system relacyjnych baz danych (MySQL lub PostgreSQL) oraz serwer HTTP (Apache lub
Witryna dla każdego gatunku będzie miała IIS). Opcjonalne, ale zalecane są: rozszerzenie XML dla PHP, obsługa mod_rewrite i plików
.htaccess.
własne ustawienia, motywy graficzne, blo-
ki, treść, fora itd. Z drugiej jednak strony
chcemy, by nasi użytkownicy mogli założyć
pojedyncze konto i wykorzystywać je do lo- Instalacja wyszukiwania przez system Drupal pli-
gowania się we wszystkich serwisach (ang. wielowitrynowa ków konfiguracyjnych, modułów oraz in-
single sign-on). Z tego względu poszcze- (wieloserwisowa) nych elementów. W przypadku instalacji
gólne serwisy będą korzystały ze wspólnej Instalacja wielowitrynowa pozwala nam jednoserwisowej, pełna ścieżka do pliku
bazy użytkowników. tworzyć wiele serwisów, dostępnych pod settings.php to sites/default/settings.php.
osobnymi domenami lub poddomenami W przypadku scenariusza wielowitry-
Listing 1. Definicja naszych poddo- czy podkatalogami pojedynczej domeny. nowego musimy natomiast stworzyć
men dla Apache'a Każdy z tych serwisów może mieć własne odpowiedni podkatalog dla każdego
ustawienia, moduły i motywy graficzne serwisu w sites, a następnie umieścić
<VirtualHost *:80> pomimo, że korzysta z tej samej co inne w nim dostosowany do tej witryny plik
DocumentRoot /var/www
instalacji systemu Drupal. Ponadto w za- setttings.php.
ServerName www.example.com
</VirtualHost> leżności od potrzeb, poszczególne serwisy Nazwy tych podkatalogów zawierać
<VirtualHost *:80> mogą korzystać z całkowicie niezależnych, muszą nazwę poddomeny (vhost) dome-
DocumentRoot /var/www częściowo wspólnych, bądź całkowicie ny, numer portu (tylko w przypadku, gdy
ServerName rock.example.com wspólnych tabel w bazie danych. jest on inny niż 80) oraz nazwy folderów
</VirtualHost>
na danym serwisie. Poszczególne części
<VirtualHost *:80>
DocumentRoot /var/www Jak działa instalacja składające się na taką nazwę oddzielone
ServerName blues.example.com wielowitrynowa są kropkami. Dotyczy to również numeru
</VirtualHost> Podstawowym mechanizmem stojącym portu, który zazwyczaj oddzielamy dwu-
za instalacją wieloserwisową jest sposób kropkiem.

PHP Solutions Nr 1/2006 www.phpsolmag.org 31
Narzędzia Drupal

zamiast poddomen wykorzystamy pod-
Terminologia Drupal katalogi.

Ÿ Węzeł (ang. node) – zawartość naszego serwisu przechowywana jest w węzłach; ist- Tworzenie tabel w bazie danych
nieje szereg typów węzłów, takich jak: strona, wpis w blogu, ankieta czy też zdarzenie.
Ÿ Blok (ang. block) – niewielki fragment zawartości, umieszczany zwykle po lewej lub Kolejnym krokiem jest stworzenie tabel dla
prawej stronie strony serwisu. Jako przykłady można wymienić Kto jest zalogowany każdego serwisu w bazie danych. Mamy
lub Najświeższe komentarze. tutaj kilka możliwości. Jeżeli chcemy, by
Ÿ Moduł (ang. module) – rozszerzenie Drupala (napisane w PHP), dodające do nasze- żadna z tabel nie była dzielona między
go serwisu określoną funkcjonalność.
Ÿ Taksonomia (ang. taxonomy) – pozwala porządkować zawartość naszego serwisu serwisami, tj. żeby były one od siebie cał-
według kategorii bądź znaczników. kowicie niezależne, po prostu tworzymy
Ÿ Motyw (ang. theme) – przesłania możliwe do modyfikacji funkcje Drupal, zapewnia- osobną bazę dla każdego serwisu:
jąc unikalny wygląd naszego serwisu.
Ÿ Silnik motywów (ang. theme engine) – każdy motyw wymaga odpowiedniego silnika
motywów. Domyślnym jest PHPTemplate, ale możemy skorzystać z innych, jak np. mysql -u dbuser -p database1 §
XTemplate czy Smarty. < database/database_1.mysql
mysql -u dbuser -p database2 §
< database/database_2.mysql
W przypadku naszego przykładowego by działały w nim hosty wirtualne. Ponie-
portalu, struktura katalogów i plików dla waż używamy Apache'a, którego drzewo W przeciwnym przypadku dzielone będą
serwisów www.example.com, blues.exam- dokumentów znajduje się w /var/www, mu- wszystkie tabele, a wszystkie serwisy bę-
ple.com i rock.example.com powinna wy- simy dodać do naszego pliku /etc/apache/ dą miały identyczne ustawienia, motywy
glądać następująco: httpd.conf zawartość Listingu 1. graficzne, treść itd. Wystarczy nam wtedy
Spowoduje to skierowanie poddomen: pojedyncza baza dla wszystkich serwisów:
Ÿ sites/example.com/settings.php, www, rock oraz blues do naszego drzewa
Ÿ sites/rock.example.com/settings.php, dokumentów, w którym zainstalujemy mysql -u database_user_name -p §
Ÿ sites/blues.example.com.subdir/ pojedynczą kopię kodu Drupal, mającą db_for_all < database/database.mysql
settings.php. obsługiwać wszystkie nasze serwisy. Opi-
sany scenariusz wieloserwisowy powinien W przypadku naszego portalu muzycznego
Jeżeli serwis ma być dostępny zarówno ja- działać także w przypadku większości skorzystamy z oferowanych przez Drupal
ko example.com jak i www.example.com, dzielonych kont hostingowych. Konieczne możliwości pracy z prefiksami tabel. Tabele
zaleca się utworzenie katalogu o nazwie może okazać się jednak pozostawienie dla wszystkich podserwisów naszej witryny
example.com. konfiguracji poddomen administratoro- będą przechowywane w jednej bazie, ale
wi, albo skorzystanie z udostępnionego ich nazwy będą się zaczynały odpowied-
Konfiguracja poddomen przez niego w tym celu interfejsu webo- nimi przedrostkami. Pozwoli to wyraźnie
Przejdźmy teraz do stworzenia opisanych wego, gdyż przeważnie nie będziemy określić, do którego serwisu należy dana
poddomen. W tym celu zmodyfikujemy mieli dostępu do pliku httpd.conf. Jeżeli tabela. Chcąc uniknąć zbędnych kompli-
konfigurację naszego serwera WWW tak, żadne z tych rozwiązań nie jest możliwe, kacji, nadamy tabelom głównego serwisu
przedrostek shared_ (jako, że część
z nich będzie dzielona z innymi serwisa-
mi). Tak więc, serwis rock.example.com
otrzyma prefiks rock_, a blues.exam-
ple.com – blues_. Aby uzyskać taki re-
zultat, użyjemy najpierw skryptu prefix.sh
z katalogu scripts/, który nada odpowiednie
przedrostki nazwom tabel z pliku database/
database.mysql, a następnie załadujemy
tabele do bazy danych:

./prefix.sh "shared_" database.mysql §
> database.mysql.shared
./prefix.sh "rock_" database.mysql §
> database.mysql.rock

oraz:

./prefix.sh "blues_" database.mysql §
> database.mysql.blues

Rysunek 1. Strona ustawień Drupal pozwala nam modyfikować rozmaite aspekty Patrząc na polecenia SQL, plik databa-
naszego serwisu se.mysql.shared zawiera teraz linijki takie,

32 www.phpsolmag.org PHP Solutions Nr 1/2006
Drupal Narzędzia

jak CREATE TABLE shared_users zamiast podania w tych plikach odpowiednich na sztywno ustawić pewne parametry po-
wcześniejszej CREATE TABLE users, pod- wartości zmiennych $base_url, $db_url szczególnych serwisów. W szczególno-
czas gdy w database.mysql.rock znajduje i $db_prefix. Zmienna $base_url zawiera ści, Drupal pozwala nam na przesłonienie
się polecenie CREATE TABLE rock_users, główny URL danego serwisu (np. http:// dowolnych spośród swoich zmiennych
itd. Teraz, kiedy nazwy tabel w każdym pli- rock.example.com), podczas gdy $db_url konfiguracyjnych (przechowywanych
ku mają odpowiednie przedrostki, możemy wskazuje na miejsce przechowywania pa- zwykle w tabeli variable w bazie da-
załadować je do naszej bazy MySQL: rametrów serwisu w bazie. nych) przy wykorzystaniu następującej
W przypadku naszego portalu, konstrukcji:
mysql -u dbuser -p dbname § wszystkie serwisy korzystają z tej samej
< database.mysql.shared bazy, więc wartość $db_url jest identycz- $conf = array(
mysql -u dbuser -p dbname § na we wszystkich plikach settings.php. 'site_name'=>
< database.mysql.rock Zmienna $db_prefix zawiera tablicę 'Rock music discussion site',
asocjacyjną kojarzącą nazwy tabel z ich 'theme_default'=>'pushbutton',
oraz: przedrostkami (patrz ramka Zawartość 'comment_preview'=>'1'
plików settings.php). Jeżeli nie chcemy );
mysql -u dbuser -p dbname § dzielić żadnych tabel, $db_url zamiast
< database.mysql.blues tablicy będzie zawierać łańcuch, np. W powyższym przykładzie definiujemy
prefix_, albo "" (pusty tekst), jeżeli nie własną nazwę serwisu oraz jego domyśl-
Konfiguracja serwisów chcemy w ogóle stosować przedrostka. ny motyw graficzny, a także ustawiamy
Ostatnim krokiem instalacji wieloserwiso- Należy pamiętać, że chcemy zaim- funkcjonalność podglądu publikowanych
wej jest edycja każdego pliku settings.php plementować wspólne logowanie do komentarzy na wymaganą. Oprócz wła-
tak, by jego treść odpowiadała wyma- wszystkich serwisów naszego portalu, snego pliku settings.php, każdy serwis
ganiom danego serwisu. W wariancie a więc część tabel będzie dzielona. może mieć swoje katalogi modules/
minimalnym oznacza to konieczność Decyzja o tym, które tabele powinny i themes/. Pozwala to powiązać określone
być dzielone nie jest łatwa, ponieważ moduły i motywy graficzne z konkretnymi
wymaga ona bliższej znajomości we- serwisami. Wymienione foldery zakładamy
Zawartość plików wnętrznych mechanizmów działania sys- w odpowiednim dla danego serwisu kata-
settings.php temu Drupal i w dużym stopniu zależy od logu konfiguracyjnym, na przykład:
Oto istotne ustawienia znajdujące się
jego docelowej konfiguracji. Dla naszych
w pliku sites/www.example.com/settings.
php: potrzeb będziemy dzielić następujące ta- Ÿ sites/rock.example.com/settings.php
bele: users, users_roles, sessions, role, Ÿ sites/rock.example.com/modules
$base_url="http://www.example.com"; authmap, sequences, profile_fields oraz Ÿ sites/rock.example.com/themes
$db_url="mysql://database_user:
profile_values.
database_password@database_host/
database_name";
Oprócz zdefiniowania zmiennych Teraz, kiedy już zakończyliśmy instalację
$db_prefix = "shared_"; $base_url, $db_url i $db_prefix, w odpo- wieloserwisową, przejdziemy do konfigu-
wiednich plikach settings.php możemy rowania naszego portalu.
Dla odmiany, w sites/rock.example.com/
settings.php będziemy mieli:

$base_url="http://rock.example.com";
$db_url= ...;
$db_prefix = array(
'default' =>'rock_',
'users' =>'shared_',
'users_roles' =>'shared_',
'sessions' =>'shared_',
'role' =>'shared_',
'authmap' =>'shared_',
'sequences' =>'shared_',
'profile_fields' =>'shared_',
'profile_values' =>'shared_',
);

Podczas gdy w sites/blues.example.com/
settings.php musimy ustawić:

$base_url=
"http://blues.example.com";
$db_url = ...;
$db_prefix = array(
'default' => 'blues_'
...
); Rysunek 2. Strona parametrów motywów pozwala nam włączyć jeden lub więcej
motywów, a także wybrać jeden z nich jako domyślny dla danego serwisu

PHP Solutions Nr 1/2006 www.phpsolmag.org 33
Narzędzia Drupal

Podstawowa
konfiguracja
Czas utworzyć naszego pierwszego
użytkownika. Po wejściu na stronę http://
www.example.com, klikamy na Utwórz
nowe konto w bloku Logowanie po lewej
stronie ekranu. Pierwszy stworzony przez
nas użytkownik, niezależnie od nadanej
mu nazwy, będzie administratorem po-
siadającym uprawnienia do wykonywania
wszelkich operacji na serwisie.
Po zalogowaniu się na to konto, pod
zakładką zarządzaj –> ustawienia zmieni-
my kilka podstawowych ustawień, takich
jak nazwa serwisu (Portal muzyczny).
Ustawimy też opcję Raportowanie błędów
na Zapisuj błędy do dziennika, nie chcemy
bowiem pokazywać użytkownikom na-
szych wewnętrznych błędów.
Uaktywnimy także opcję Czyste URL-
e, aby uzyskać ładniej wyglądające adresy Rysunek 3. Korzystając z podstawowych odnośników definiujemy menu w prawym
URL i włączymy wbudowany mechanizm górnym rogu serwisu
pamięci podręcznej, aby zwiększyć wy-
dajność. Warto pamiętać, że ponieważ pojawiał się komunikat napisany przez Ÿ profile – pozwala nam określić jakie
dzielimy między bazami tylko tabelę users użytkownika <nazwa> dnia <data>, nie pola powinien zawierać profil użytkow-
oraz tabele z nią powiązane, parametry te chcemy widzieć go na stronach statycz- nika, np. imię, dzień urodzenia, e-mail,
nie będą wspólne dla wszystkich serwi- nych, takich jak O .... W tym celu wyłączy- ulubione zespoły, itd.
sów. Nasze ustawienia przedstawiamy na my opcję Włącz wyświetlanie informacji nt. Ÿ blog – daje każdemu z zarejestrowa-
Rysunku 1. wypowiedzi dla węzłów typu strona. nych użytkowników możliwość posia-
dania bloga. Przykładowo, URL-em
Motywy graficzne Moduły bloga użytkownika naszego serwisu
Do dystrybucji systemu Drupal dołączono Rozszerzymy teraz funkcjonalność nasze- o nazwie 23 to http://www.example.com/
kilka motywów graficznych. Więcej może- go portalu, włączając na stronie zarządzaj blog/23. Dodatkowo, automatycznie
my znaleźć pod adresem http://drupal.org/ -> moduły (patrz Rysunek 4) kilka dodat- i bez żadnych dodatkowych działań
project/Themes. Procedura ich instalacji kowych, dołączonych do pakietu Drupal otrzymujemy blog grupowy (gdzie
jest całkiem prosta: należy pobrać odpo- modułów: agregowane są posty wszystkich użyt-
wiedni plik spod adresu drupal.org i rozpa-
kować go do katalogu themes/. W naszym
głównym portalu wykorzystamy domyślny
motyw graficzny, za to dla podstron dla
poszczególnych gatunków muzyki pobie-
rzemy i zainstalujemy inne; przykładowo,
na rock.example.com wykorzystamy mo-
tyw FriendsElectric, rozpakowując go do
katalogu sites/rock.example.com/themes/
friendselectric. Następnie wybierzemy
odpowiedni motyw za pomocą menu
zarządzaj –> motywy graficzne (patrz
Rysunek 2).
Korzystając z zarządzaj –> motywy
graficzne –> konfiguruj możemy też zała-
dować własne logo oraz obraz favicon dla
naszego serwisu. Natomiast aby wyświe-
tlać linki O ..., FAQ i Kontakt (patrz Rysu-
nek 3) wykorzystamy listę podstawowych
odnośników, podając ich nazwy i względne
URL-e. Oczywiście, musimy stworzyć od-
powiednie strony! Wreszcie, o ile chcemy, Rysunek 4. Strona przeglądu modułów Drupal pokazuje wszystkie zainstalowane
by we wpisach w blogach i komentarzach moduły

34 www.phpsolmag.org PHP Solutions Nr 1/2006
Drupal Narzędzia

Istnieje wiele innych, stworzonych przez
użytkowników modułów; znajdziejmy je
pod adresem http://drupal.org/project/
Module. W naszym portalu wykorzystamy
następujące spośród nich:

Ÿ image – pozwala nam przesyłać
obrazy na serwis, porządkować je
w galerie oraz wstawiać we wpisach
w blogach. Automatycznie tworzy mi-
niaturę każdego obrazka.
Ÿ audio – pozwala naszym użytkowni-
kom przesyłanie plików MP3 na ser-
wis i zarządzanie nimi. Artyści mogą
go wykorzystywać do publikowania
swoich utworów albo do podcastin-
gu. Wraz z modułem dostarczany
jest napisany we Flashu odtwarzacz,
pozwalający nam odtwarzać MP3
w przeglądarce WWW.
Rysunek 5. Drupal pozwala na implementację w naszym serwisie precyzyjnej kontroli dostępu Ÿ trackback – pozwala blogowiczom na
naszym serwisie wysyłać i otrzymy-
kowników naszego serwisu): http:// Ÿ upload – pozwala użytkownikom prze- wać trackbacki.
www.example.com/blog. Rzecz jasna, syłać na naszą witrynę pliki, np. obraz- Ÿ spam – potężny zestaw narzędzi auto-
każdy blog posiada własny strumień ki lub MP3. matycznie radzący sobie ze spamem
RSS, np. http://www.example.com/blog/ Ÿ forum – pozwala nam stworzyć jedno w komentarzach i trackbackach na
23/feed. Uaktywniając blogi dajemy lub więcej forów dyskusyjnych w ra- naszym serwisie.
naszym artystom platformę, na której mach naszego serwisu.
mogą oni mówić o swojej muzyce, Ÿ poll – pozwala nam tworzyć cotygo- Użytkownicy,
natomiast fanom zapewniamy miejsce dniowe ankiety (możliwe są pytania role i uprawnienia
wyrażania opinii o dziełach twórców. z wieloma odpowiedziami), w których Kolejnym krokiem będzie zdefiniowanie
Ÿ ping – automatycznie powiadamia nasi użytkownicy mogą głosować. na stronie zarządzaj –> reguły dostępu
serwisy takie, jak technorati.com Ÿ search – uaktywnia wbudowaną w sys- pewnych ról i nadanie im pożądanych
czy pingomatic.com o zmianach temie Drupal wyszukiwarkę, pozwa- przez nas uprawnień (patrz Rysunek
treści naszej witryny, co jest bardzo lającą przeszukiwać nasz serwis pod 5). W tym momencie stworzymy role:
istotne i wygodne dla naszych blogo- kątem konkretnych treści lub użytkow- artysta (otrzymuje dodatkowe upraw-
wiczów. ników. nienie do przesyłania plików na serwis)
oraz opiekun serwisu, dla zaufanych
użytkowników pomagających nam
opiekować się serwisem (rola ta otrzy-
muje dodatkowe uprawnienia do ad-
ministracji forami, itd.). Skorzystamy
ponadto z automatycznie zdefiniowa-
nych ról zarejestrowany użytkownik
(zalogowani użytkownicy) i anonimowy
użytkownik (użytkownicy niezalogowani),
które przypiszemy wszystkim pozostałym
użytkownikom. Wreszcie, musimy przypi-
sać właściwe role naszym użytkownikom,
klikając na stronie zarządzaj –> użytkow-
nicy na odnośniku edytuj dla interesują-
cego nas użytkownika. Końcowy zestaw
uprawnień użytkownika to skumulowane
uprawnienia wszystkich ról, do których
użytkownik został przypisany.

Tworzenie zawartości
Nadeszła pora, by rozpocząć tworzenie
Rysunek 6. Tworzymy prostą stronę z treścią zawartości naszego serwisu. Będzie to
statyczna strona O ... informująca, jaką

PHP Solutions Nr 1/2006 www.phpsolmag.org 35
Narzędzia Drupal

Rysunek 9. Oparty na JavaScript pasek
postępu przesyłania plików na serwis

Telemann. Dzięki temu, jednym kliknię-
Rysunek 8. Automatyczne uzupełnianie ciem możemy wybrać odpowiedniego
nazwy użytkownika przy pomocy AJAX-a użytkownika. Ten sam mechanizm wy-
korzystywany jest w przypadku znaczni-
niewielką, wbudowaną bibliotekę pod- ków (patrz Rysunek 8).
stawową AJAX, która już teraz wykorzy- Kolejnym mechanizmem korzystają-
stywana jest w niektórych (aczkolwiek cym z AJAX-a jest pasek postępu, który
nie wszystkich) obszarach do polepsze- jest wykorzystywany podczas przesyłania
nia obsługi serwisów na niej opartych. plików na naszą witrynę. Informuje on
Biblioteka ta oferuje API dla modułów użytkownika o tym, jaka część pliku zosta-
zewnętrznych, które dzięki temu mogą ła przesłana (patrz Rysunek 9).
wykorzystywać funkcjonalność AJAX
i JavaScript do własnych potrzeb. Edycja WYSIWYG
Jednym z ciekawszych elementów Aby ułatwić użytkownikom formatowanie
Rysunek 7. Zwijalne pole formularza
AJAX/JavaScript jest zwijalne pole for- tekstu w naszym serwisie, zainstalujemy
funkcję pełni nasz portal muzyczny. Dru- mularza, znacznie podnoszące wygodę prosty edytor typu WYSIWYG (What
pal udostępnia nam do wykorzystania korzystania ze stron zawierających You See Is What You Get). W tym celu
szereg rodzajów węzłów (np. strona, formularze. Przykładem jego użycia możemy skorzystać z jednego spośród
wpis w blogu czy wątek na forum). My są strony utwórz zawartość –> wpis stworzonych przez użytkowników mo-
wykorzystamy typ strona. Po kliknięciu do dziennika. Rzadko używane części dułów, takich jak HTMLarea, TinyMCE
na utwórz zawartość –> strona naszym formularza i całe formularze na takiej czy FCKeditor. Wybierzemy FCKeditor,
oczom ukaże się prosty formularz, w któ- stronie są domyślnie zwinięte (zamiast który został oparty na opensourcowym
rym określimy nazwę węzła oraz jego całego formularza widoczny jest tylko edytorze o tej samej nazwie, dostępnym
zawartość (patrz Rysunek 6). Jako, że jego tytuł), dzięki czemu cała strona pod adresem http://www.fckeditor.net.
uruchomiliśmy moduł path, możemy tak- staje się krótsza i bardziej zwięzła. Klik- Oferuje on naszym użytkownikom szeroki
że określić ścieżkę, pod którą nowa stro- nięcie lewym przyciskiem na zwiniętym wachlarz możliwości formatowania tekstu
na będzie dostępna. Tym razem użyjemy formularzu spowoduje jego rozwinięcie (Rysunek 10).
tutaj about, w wyniku czego adresem tej z wykorzystaniem JavaScript (patrz Ry-
strony będzie http://www.example.com/ sunek 7) bez potrzeby pobierania przez Kategorie i znakowanie
about. Dla nowej strony (lub innego wę- przeglądarkę kolejnej strony z serwera. Wbudowany w systemie Drupal system
zła) możemy ustawić szereg opcji, np. W naszym portalu zwijalne pola formu- taksonomii jest bardzo potężnym narzę-
zablokować na niej komentowanie, nie larza przydałyby się np. w formularzu dziem pozwalającym na porządkowanie
publikować jej na stronie głównej, itd. pozwalającym wysłać list do artysty, dowolnych treści w kategorie. Pod zarzą-
Możemy także, zaznaczając odpowied- zamieszczonym na jego (lub jej) stro- dzaj –> kategorie możemy zdefiniować
nie pole, stworzyć nową wersję węzła. nie, lub umożliwiającym wyświetlenie dowolną liczbę słowników (np. gatunek
Spowoduje to zapisanie zarówno jego bardziej szczegółowych informacji bio- muzyki), z których każdy zawierać będzie
starej, jak i nowej wersji, co umożliwi graficznych. tzw. terminy taksonomiczne, czyli kate-
nam ewentualny powrót do starszej wer- Kolejną przydatną możliwością AJAX gorie (np. rock, pop albo trance). Każda
sji bądź porównanie jej z nową. jest autouzupełnianie zawartości, póki kategoria ma własną stronę (np. http://
co dostępne tylko w polach z nazwą www.example.com/taxonomy/term/1), na
Rozszerzenia użytkownika i znacznikami (kategoria- której wyświetlana jest lista wszystkich na-
AJAX i JavaScript mi). Przykładowo, jeżeli chcemy zmienić leżących do niej węzłów, a użytkownikom
AJAX, czyli Asynchroniczny JavaScript właściciela/twórcę węzła w naszym ser- udostępniany jest strumień RSS (np. http://
z XML (ang. Asynchronous JavaScript wisie, musimy zmienić zawartość pola www.example.com/taxonomy/term/1/0/
and XML) to zyskująca coraz większą Stworzony przez dla odpowiedniego feed).
popularność metoda tworzenia inte- węzła. Jeżeli teraz w tym polu wpisze- Nową możliwością w systemie Drupal
raktywnych serwisów WWW. Piszemy my li, Drupal automatycznie pokaże 4.7 są słowniki wolnego znakowania (ang.
o niej szerzej w artykule AJAX – porząd- nam wszystkie nazwy użytkowników za- free tagging), pozwalające użytkownikom
kowanie aplikacji w tym numerze PHP wierające ciąg znaków li, np. Antonio wprowadzać w odpowiednim formularzu li-
Solutions. Drupal 4.7 będzie zawierał Salieri, Franz Liszt czy Georg Philipp sty oddzielonych przecinkami znaczników.

36 www.phpsolmag.org PHP Solutions Nr 1/2006
Narzędzia Drupal

W chwili zapisu edytowanego przez
użytkownika węzła, wszystkie znaczniki
automatycznie staną się normalnymi
kategoriami systemu Drupal. Pozwala to
blogowiczom korzystającym z naszej wi-
tryny znakować swoje wpisy, podobnie jak
ma to miejsce w innych serwisach “znako-
wania społecznego” (ang. social tagging),
takich jak del.icio.us, flickr czy technorati.
Na stronie zarządzaj –> kategorie –> do-
daj słownik tworzymy słownik Znaczniki
Blogów i oznaczamy go jako słownik
wolnego znakowania (patrz Rysunek 11).
Możliwości w tej dziedzinie znacząco
zwiększa zewnętrzny moduł tagadelic,
oferujący nam tzw. chmury znaczników
(ang. tag clouds)– zbiory nazw znaczni-
ków łączących się z odpowiednimi katego-
riami (np. kliknięcie na znacznik muzyka
wyświetli stronę kategorii/znacznika o tej
samej nazwie). Rozmiar czcionki każde- Rysunek 11. System taksonomii pozwala nam korzystać z szerokiego zakresu różnych
go znacznika zależy od jego popularno- słowników i konfiguracji
ści. Moduł tagadelic oferuje nam jedną
chmurę znaczników na słownik (np. http: Serwisy dodawać bądź usuwać języki na stronie
//www.example.com/tagadelic/chunk/1) międzynarodowe zarządzaj –> języki –> dodaj język oraz
oraz jedną dodatkową chmurę łączącą Chcemy, aby nasz portal był wielojęzycz- oznaczyć jeden z nich jako język do-
znaczniki ze wszystkich słowników. Aby ny. System Drupal pomaga nam tutaj na myślny (patrz Rysunek 13). W naszym
wyświetlić chmurę o nazwie Znaczniki Blo- kilka sposobów: do kodowania znaków przypadku językiem domyślnym będzie
gów w bloku, należy włączyć możliwość wykorzystuje Unicode oraz ułatwia nam angielski, udostępnimy jednak również
używania znaczników w Znacznikach Blo- tworzenie osobnych wersji językowych polski, niemiecki, francuski i włoski.
gów na stronie zarządzaj –> bloki (patrz interfejsu i zawartości. Tłumaczenie interfejsu możemy
Rysunek 12). Chmury znaczników ofe- przeprowadzić na dwa sposoby. Pierw-
rują gościom naszego serwisu wygodną Języki szym jest skorzystanie z wygodnego
możliwość przeglądania jego zawartości Wbudowany moduł locale pozwala na interfejsu webowego, dostępnego pod
pod kątem kategorii, które ich najbardziej tłumaczenie interfejsu użytkownika sys- zarządzaj –> języki –> zarządzaj na-
interesują. temu Drupal na dowolny język. Możemy pisami. Możemy przeszukiwać nasz
serwis pod kątem pewnych łańcuchów
znaków (przetłumaczonych bądź nie)
i tłumaczyć je na dowolny z dodanych
przez nas języków lub modyfikować
już dostępne tłumaczenie. Drugim
sposobem obsługi tłumaczeń są możli-
wości importu oraz eksportu dostępne
w module locale. Tłumaczenia Drupala
mogą być importowane z plików Porta-
ble Object (.po) systemu gettext, bądź
eksportowane do tego formatu. Jest
on bardzo popularny i istnieje mnóstwo
narzędzi ułatwiających manipulowanie
plikami z niego korzystającymi, takich
jak popularne aplikacje tłumaczące kba-
bel czy poEdit.
Warto jednak wiedzieć, że w przy-
padku większości języków nie ma ko-
nieczności tłumaczenia wszystkich
tekstów. Pod adresem http://drupal.org/
project/Translations znaleźć można
Rysunek 10. Moduł FCKeditor zastępuje standardowe formularze wprowadzania wiele gotowych, przygotowanych przez
tekstu przyjazną dla użytkownika wersją graficzną użytkowników tłumaczeń. Do tej pory

38 www.phpsolmag.org PHP Solutions Nr 1/2006
Drupal Narzędzia

następującej architekturze: tłumacze-
nia modelowane są przez zwyczajne
węzły, zawierające wskaźnik do wę-
złów, do których należą (których są
tłumaczeniem). Informacja o języku
przechowywana jest w URL-u danego
węzła (zamiast w cookie sesji), np. http://
www.example.com/fr/about w przypadku
węzła francuskiego.
Aby uczynić nasz serwis wielo-
języcznym, skonfigurujemy najpierw
wspomniany wyżej moduł pod zarządzaj
–> ustawienia –> i18n (patrz Rysunek
14). Uaktywnimy opcję detekcja języka
przeglądarki, pozwalającą wybrać język,
w którym wyświetlana będzie zawartość
serwisu w oparciu o ustawienia prze-
glądarki gościa portalu. Jest to zwykle
najwygodniejszy sposób. Jeżeli nie
włączymy tej opcji, wybór języka będzie
Rysunek 12. Chmurka znaczników „znaczniki blogów” (ang. „Blog Tags”) dla naszego następował kolejno na podstawie URL-
serwisu, pokazana w bloku a, ustawień użytkownika, konfiguracji
przeglądarki bądź domyślnego języka
Drupal zyskał ponad 40 wersji języ- możemy w menu zarządzaj –> języki –> systemu Drupal.
kowych. Na potrzeby naszego portalu zarządzaj napisami znaleźć tytuł bloku Pod zarządzaj –> ustawienia –>
pobierzemy z drupal.org tłumaczenia Kto jest aktywny i zmienić go np. na Ak- i18n mamy także możliwość ogranicze-
na polski, niemiecki, włoski i francuski, tywni użytkownicy. nia tłumaczenia treści do konkretnych
a następnie zaimportujemy pliki pl.po, typów węzłów. Chcemy jednak, aby na
de.po, it.po i fr.po korzystając z opcji Internacjonalizacja naszym portalu tłumaczone były wszyst-
zarządzaj –> języki –> importuj. Może- (tłumaczenie zawartości) kie rodzaje zawartości. Przykładowo,
my tutaj wybrać, czy chcemy nadpisać Ostatnim elementem, którego potrzebu- przetłumaczymy teraz stronę O ... na
już przetłumaczony tekst, czy też dodać jemy do stworzenia prawdziwie między- język niemiecki, klikając na odnośnik
tylko te łańcuchy znaków, które nie narodowego portalu muzycznego jest tłumacz węzła http://www.example.com/
mają jeszcze swoich tłumaczeń. Warto zewnętrzny moduł Internationalization about. Zobaczymy listę języków zde-
zauważyć, że moduł locale pozwala (i18n). Daje on nam możliwość tłuma- finiowaną przez nas wcześniej dla
nam zmodyfikować praktycznie każdy czenia zawartości naszego serwisu na modułu locale. Możemy przetłumaczyć
tekst w naszym serwisie. Przykładowo, inne języki. Moduł i18n oparty jest na ten węzeł na dowolny z nich. Kliknię-
cie odnośnika utwórz tłumaczenie dla
niemieckiego przywołuje standardowy
dialog tworzenia węzła, wzbogacony
o nowe pole o nazwie Język (w tym
przypadku domyślnie ustawione na
niemiecki). Możemy teraz wprowadzić
przetłumaczony tekst w treści węzła,
wybrać de/about jako ścieżkę dla prze-
tłumaczonego węzła – i gotowe.
Węzeł O ... będzie odtąd widoczny
w języku określonym albo na podstawie
URL-a, albo konfiguracji przeglądarki (tak,
jak opisaliśmy powyżej). Ponadto, u dołu
strony widnieć będzie jedna lub więcej
małych flag. Kliknięcie na jedną z nich po-
zwoli nam przejść do odpowiedniej wersji
językowej.
Moduł i18n oferuje nam także możli-
wy do uaktywnienia blok wyboru języka,
zawierający listę wszystkich zdefinio-
wanych języków. Kliknięcie na jednym
Rysunek 13. Dialog parametrów języków z nich powoduje zmianę bieżącego

PHP Solutions Nr 1/2006 www.phpsolmag.org 39
Narzędzia Drupal

języka (działa to również w przypadku
użytkowników anonimowych). Moduł ten
posiada jednak pewne ograniczenia: nie
ma możliwości tłumaczenia zawartości
bloków czy też menu.

Optymalizacja pod
kątem wyszukiwarek
Ponieważ chcemy, by nasz serwis
zajmował jak najwyższe pozycje w wy-
szukiwarkach internetowych, a jego po-
pularność ciągle wzrastała, istotnym
zagadnieniem jest dla nas optymalizacja
pod kątem wyszukiwarek (SEO).

Czyste URL-e i moduł path
Biorąc pod uwagę, że największe wy-
szukiwarki wyżej oceniają strony, których
URL-e zawierają poszukiwane frazy,
zoptymalizujemy odpowiednio adresy
Rysunek 14. Dialog parametrów internacjonalizacji
na naszej witrynie. Z systemem Drupal
jest to bardzo proste. Dysponuje on wiamy pole aliasu ścieżki puste, a moduł wnętrznego, dedykowanego dla Google
m.in. opcją Czyste URL-e, która usuwa ? pathauto automatycznie wygeneruje od- modułu Google Sitemaps (gsitemap).
z adresów na naszym serwisie. Efektem powiedni alias. Moduł ten tworzy XML-ową mapę serwisu
użycia tej funkcji będą adresy typu http: W naszym portalu skonfigurujemy wzo- w formacie określonym przez specyfikację
//www.example.com/node/123 zamiast rzec [user]/[yyyy]-[mm]-[dd]/[title] Google Sitemaps (http://www.google.com/
http://www.example.com/?q=node/123. dla wpisów w dziennikach. Jeżeli użytkow- webmasters/sitemaps) i umieszcza ją
Opcję Czyste URL-e możemy uaktywnić nik John Doe utworzy w Wigilię wpis o ty- pod adresem http://www.example.com/
pod zarządzaj -> ustawienia, będziemy tule Dlaczego lubię dobrą muzykę, moduł gsitemap.
jednak potrzebowali działającego modułu pathauto automatycznie wygeneruje alias Mapa serwisu to, w skrócie, lista
Apache'a o nazwie mod_rewrite. Dalszej ścieżki john-doe/2005-12-24/dlaczego-lu- stron naszego serwisu uporządkowana
optymalizacji naszych URL-i możemy bie-dobra-muzyke. wg ich priorytetu, którą Google wyko-
dokonać zastępując zawarte w nich licz- rzystuje, aby inteligentniej i dogłębniej
by znaczącymi słowami kluczowymi, Google Sitemaps przeglądać nasze strony, dając w efekcie
na co z kolei pozwala wbudowany mo- Kolejną rzeczą, którą możemy uczy- lepsze i bardziej aktualne wyniki wyszu-
duł path. Po jego włączeniu, tworząc nić, by zwiększyć nasze notowania kiwania. Moduł gsitemap pozwala nam
bądź edytując węzeł, każdorazowo w wyszukiwarkach, jest skorzystanie z ze- określić priorytet, jaki wybrane strony
będziemy proszeni o podanie dla niego
aliasu ścieżki. W ten sposób możemy
tworzyć adresy URL wyglądające jak
http://www.example.com/about-this-site,
co znacznie zwiększy ilość trafień na na-
sze strony w wynikach wyszukiwania.

Pathauto
Kolejne usprawnienia w tej dziedzinie
zapewnia nam zewnętrzny moduł pa-
thauto, wymagający standardowego
modułu path. Pozwala on na elastyczną
automatyzację generacji aliasów ścieżek
dla węzłów, wpisów do blogów, stron
użytkowników, terminów taksonomii (ka-
tegorii bądź znaczników) i wielu innych.
Główną ideą tego modułu jest, by za-
miast ręcznie wprowadzać alias ścieżki
podczas tworzenia węzła, jednorazowo
podawać wzorzec ścieżki pod zarządzaj
–> ustawienia –> pathauto. Od tej pory,
tworząc nowy węzeł po prostu pozosta- Rysunek 15. Dialog parametrów pathauto

40 www.phpsolmag.org PHP Solutions Nr 1/2006
Drupal Narzędzia

powinny posiadać w wygenerowanej <meta name="keywords" content= związanego z tworzeniem i zarządzaniem
mapie serwisu – na przykład według "music, free, independent, genre, osobnymi instalacjami dla każdego serwi-
rodzaju węzłów, ilości komentarzy dla artists, portal" /> su i wersji językowej. Intensywnie korzysta
węzła, jego czasu stworzenia czy aktuali- z AJAX i włącza jego funkcjonalność do
zacji lub też kwestii, czy węzeł związany Podsumowując, możemy użyć modułu interfejsu, a także pozwala użytkownikom
jest ze stroną główną. Możemy także po- nodewords, aby stworzyć zestaw meta- stosować go na własną rękę. Wreszcie,
lecić, aby moduł gsitemap powiadamiał informacji (w szczególności słów kluczo- możliwości systemu Drupal w zakresie
Google o zmianie mapy serwisu oraz by wych) dla każdej strony naszego serwisu SEO oferują nam wszystko, czego potrze-
odnotowywał fakt odwiedzenia naszej i mieć nadzieję, że będą one kolejnym bujemy, aby nasz serwis stał się widoczny
mapy przez pająka Google. elementem polepszającym naszą pozycję dla wyszukiwarek.
w wyszukiwarkach. Biorąc pod uwagę dynamiczną, tęt-
Słowa kluczowe węzłów niącą życiem społeczność skupioną wo-
Efektywność słów kluczowych meta Wydajność kół systemu Drupal oraz jego znakomitą
(metatagów) pod względem SEO jest W oczekiwaniu na (miejmy nadzieję) dokumentację, możemy ze spokojem wy-
wysoce niepewna: mówi się, że wiele rosnącą popularność naszego portalu próbować ten system. Jego modularność
wyszukiwarek zwyczajnie je ignoruje. muzycznego, pod zarządzaj –> ustawie- i co za tym idzie elastyczność, podobnie
Niemniej zakładając, że przynajmniej nia uruchomimy mechanizm pamięci pod- jak w systemach XOOPS czy Mambo, za-
niektóre z nich korzystają ze słów ręcznej systemu Drupal oraz dołączony pewnia dużą swobodę i łatwość tworzenia
kluczowych, możemy wykorzystać ze- do tego samego pakietu moduł throttle. rozbudowanych, wielodomenowych oraz
wnętrzny moduł nodewords. Pozwala Moduł zablokuje niektóre bloki i część wielojęzycznych portali. n
on nam określić streszczenie, opis oraz funkcjonalności serwisu, jeżeli jego ob-
notę o prawach autorskich dla każdego ciążenie przekroczy pewien (możliwy do
węzła, jak również listę słów kluczo- ustawienia) pułap.
wych do umieszczenia w znaczniku Możemy także uaktywnić tłumienie
<meta> w nagłówkach naszych stron. (ang. throttling) konkretnych modułów O autorze
Istnieje możliwość skonfigurowania i bloków w przypadku wystąpienia tzw.
globalnej noty o prawach autorskich efektu Slashdot. Dokonamy tego za po- Uwe Hermann jest studentem informa-
tyki na Technische Universität München
oraz globalnej listy słów kluczowych do mocą zarządzaj –> moduły oraz zarządzaj w Niemczech oraz niezależnym progra-
wykorzystania na wszystkich stronach, –> bloki, zaznaczając odpowiednie pola mistą. Pracuje na rzecz wielu projektów
z wyjątkiem węzłów, dla których zostaną throttle. Open Source, takich jak Debian GNU/
one przesłonięte. W naszym przypadku Linux i Drupal. Zainicjował i pielęgnuje
projekt Unmaintained Free Software
chcemy, by każda strona zawierała na- Podsumowanie (http://unmaintained-free-software.org),
stępujące znaczniki meta: Jak zademonstrowaliśmy w naszym przy- którego celem jest znajdowanie nowych
kładzie, Drupal jest czymś, na co użytkow- opiekunów dla opuszczonych projektów
<meta name="copyright" content= nicy systemów CMS czekali od dawna. Open Source.
Kontakt: uwe@hermann-uwe.de
"Copyright 2005 Music Community" /> Uwalnia administratora od zamieszania

R E K L A M A

PHP Solutions Nr 1/2006 www.phpsolmag.org 41
Techniki

Service Data Objects,
czyli standard uniwersalnego
dostępu do danych
Piotr Szarwas

Rozwiązania od dawna stosowane w Javie
zalewają świat PHP. Należy do nich SDO, czyli
Service Data Objects: zunifikowany, wspierany
przez takie potęgi, jak IBM, Zend i BEA standard
dostępu do danych, eliminujący potrzebę
tworzenia osobnych interfejsów dla każdego ich
źródła.

K
ażdy, kto interesuje się PHP i jego ki nim programista nie musi martwić się
rozwojem zdążył zauważyć, że o sposób fizycznego rozmieszczenia da-
wielkie firmy informatyczne, takie nych w różnych źródłach. Dane, wchodzą-
jak Oracle czy IBM zaczęły traktować ten ce do obiektów DAS i z nich wychodzące,
język i otaczające go środowisko progra- mają postać drzewa obiektów SDO. Bez
mistów bardzo poważnie. Świadczą o tym względu na fakt, czy operujemy na danych
chociażby specjalnie przygotowane wersje pochodzących z plików XML, czy też rela-
PHP dla baz danych Oracle i DB2. Dzięki cyjnej bazy danych, otrzymywane przez
temu, nie tylko PHP wkracza w świat apli- nas obiekty są identyczne.
kacji klasy Enterprise, ale także technolo- Pokażemy, w jaki sposób wykorzystać
gie wywodzące się z rozwiązań tej klasy SDO do stworzenia prostej aplikacji służą-
W SIECI wkraczają do PHP. Tak też stało się z SDO. cej do administracji i prezentacji systemu
SDO to skrót od Service Data Objects. Jest newsów na stronie WWW. Newsy będą
nazwą architektury stworzonej przez IBM podzielone na kategorie, a każdy z nich
1. http://pecl.php.net/package/ i BEA, mającej na celu ułatwić, czy wręcz będzie mógł należeć do jednej kategorii.
sdo – pakiet SDO w repozy-
torium PECL ujednolicić dostęp do danych pochodzących
2. http://us2.php.net/manual/ z wielu różnych źródeł. Dzięki niej, progra-
en/ref.sdo.php – rozsze-
miści będą mogli, poprzez spójny interfejs,
Co należy wiedzieć...
rzenie SDO – oficjalna Powinieneś znać podstawy MySQL-a
dokumentacja PHP zarządzać danymi m.in. z relacyjnych baz i programowania obiektowego.
3. http://www.zend.com/pecl/
danych, plików XML czy Web Services.
tutorials/sdo.php – tutorial
o SDO Spójrzmy na Rysunek 1, na którym Co obiecujemy...
4. http://www.ibm.com/ Po przeczytaniu artykułu będziesz wie-
przedstawiamy schemat architektury SDO.
developerworks/webservices dział jak wykorzystać SDO do stworzenia
– specyfikacje technologii Za dostęp do źródeł odpowiedzialne są prostego systemu newsów.
SDO obiekty DAS (Data Access Service). Dzię-

42 www.phpsolmag.org PHP Solutions Nr 1/2006
SDO Techniki

PHP i SDO
SDO powstało z myślą o platformie J2EE.
Jednak dzięki dużemu zainteresowaniu
IBM-a technologią PHP i porozumieniu
������
z ZEND Technologies, zostało ono włą-
czone również do PHP. Obecna imple-
mentacja nie jest tak dopracowana, jak
������ ��������������� ��� �������������
wydanie dla Javy i nie osiągnęła nawet
statusu wersji stabilnej. Dostępna jest
jedynie jako rozszerzenie PECL (PHP
�����
Extensions Community Library). Ponad-
to, do swojego poprawnego działania
wymaga parsera PHP w wersji 5.1, który
w czasie pisania tego artykułu również nie
był dostępny w wersji stabilnej. Rysunek 1. Architektura SDO
Mimo wczesnego stadium rozwoju,
SDO dla PHP pozwala już zarządzać warstwa obiektów DAS. Ukrywa ona Aktualna implementacja obiektów
danymi pochodzącymi z bazy danych przed programistą szczegóły implemen- DAS ma, niestety, szereg ograniczeń.
i plików XML. Niestety, aktualna imple- tacji związane z komunikacją ze źródłami Najważniejsze z nich to m.in. obsługa
mentacja ma szereg ograniczeń, o któ- danych. W przybliżeniu, jest to rozszerze- jedynie dwóch silników bazodanowych:
rych szczegółowo napiszemy w dalszej nie koncepcji warstwy dostępu do danych, DB2 i MySQL, czy możliwość zdefinio-
części artykułu. często spotykanej w korzystających z baz wania tylko jednego (jednopolowego)
danych aplikacjach PHP. klucza głównego na tabelę oraz brak
Tajemnice drzewa Twórcy specyfikacji DAS poszli krok transparentnej obsługi relacji typu wiele-
obiektów SDO dalej i nie tylko ujednolicili dostęp do baz do-wielu. To ostatnie oznacza, że model
Tajemnicę swojego działania SDO ukry- danych, ale zadbali także, aby w podobny SDO nie przewiduje przechowywania
wa w koncepcji tzw. grafów danych (ang. sposób można było się komunikować z in- informacji o relacji pomiędzy danymi
data graphs). Mówiąc najprościej, dane nymi źródłami, np. plikami XML. To ostat- z dwóch tabel w tzw. tabeli relacji.
są zorganizowane w postaci drzewia- nie jest szczególnie przydatne podczas
stych struktur obiektów. Przykładowo, eksportu danych z bazy oraz ich importu. Instalacja
jeżeli dane, z którymi aktualnie pracuje- Wymiana danych z bazami odbywa rozszerzenia SDO
my, pochodzą z bazy, to każdy z obiek- się z wykorzystaniem rozszerzenia PDO. Procedura instalacji zależy od platformy
tów SDO odpowiada jednemu rekordowi Do komunikacji z plikami XML zaprzęgnię- systemowej. W przypadku Windows, po-
w określonej tabeli znajdującej się to natomiast rozszerzenie SimpleXML. My bieramy ze strony PHP ostatnią skompi-
w tej bazie. Natomiast relacje pomiędzy skupimy się na pokazaniu, w jaki sposób lowaną wersję PHP 5.1 (w czasie pisania
obiektami znajdującymi się w drzewie są SDO komunikuje się z bazą danych. artykułu była to wersja RC1) i zestaw
tożsame z relacjami w bazie danych (np.
jeden-do-wielu). Pełną koncepcję drzewa
SDO obrazuje Rysunek 2.
Poza drzewem danych, w osobnej ������
gałęzi w ramach struktury obiektów SDO
przechowywane są informacje o wszyst-
kich zmianach wykonanych na danych.
Dzięki temu, obiekty DAS mogą w prosty
sposób synchronizować źródła danych ������ ������
z drzewami obiektów. SDO zapewnia �������� �����
pełne API do zarządzania zmianami
w drzewie, co umożliwia nam tworzenie
własnych obiektów DAS.
Drzewo SDO posiada jeszcze jedną
ciekawą cechę: umożliwia przeszukiwanie
wszystkich dostępnych w drzewie obiek-
tów przy pomocy zapytań XPath.

Data Access Services
Jak już wspomnieliśmy, jednym z głów-
nych elementów architektury SDO jest Rysunek 2. Drzewo obiektów SDO

PHP Solutions Nr 1/2006 www.phpsolmag.org 43
Techniki SDO

skompilowanych rozszerzeń PECL (gdy
pisałem artykuł, SDO było dostępne
w wersji 0.5.1). ���������� �������������
Jeżeli instalujemy SDO pod Linuk-
�� ������ �� ������
sem, proces instalacji jest dużo bardziej �������� �� ������
skomplikowany, głównie dlatego, że �������������� �����������
na php.net nie ma oficjalnej dystry- �������
����������
bucji binarnej PHP dla tego systemu.
Twórcy niektórych dystrybucji Linuksa
dostarczają pakiety binarne PHP, cza-
sem jednak trzeba go skompilować Rysunek 3. Diagram encji dla bazy danych newsów
samemu. Źródła PHP pobieramy ze
strony http://php.net. Podobnie uczy- Pierwszy przykład pisać system newsów. Pracę rozpocznie-
nimy z SDO, którego źródła można Jeżeli już zainstalowaliśmy i uruchomi- my od stworzenia prostej bazy danych.
pobrać z repozytorium PECL (http:// liśmy PHP 5.1 z rozszerzeniem SDO, Na Listingu 1. przedstawiamy schemat
pecl.php.net/package/sdo). Pamiętajmy, możemy zająć się utworzeniem wspo- bazy dla MySQL-a.
że do poprawnego działania SDO po- mnianego wcześniej systemu aktual- Aktualności przechowywane są
trzebuje rozszerzeń PDO i SimpleXML. ności. Spróbujmy więc przekonać się w dwóch tabelach. Pierwsza z nich,
Na szczęście są one dostępne standar- na własne oczy, jak szybko i elegancko o nazwie news_groups zawiera podsta-
dowo w kodzie źródłowym PHP 5.1. możemy przy pomocy tej technologii na- wowe informacje o grupach newsów,
takie jak id grupy (ng_id), nazwa grupy
Listing 1. Skrypt tworzący bazę newsów dla MySQL-a (ng_name) i opis (ng_description). Same
wiadomości gromadzone są w drugiej
/**************************************** tabeli, news_contents. Każdy rekord
* tabela z grupami newsów z tej tabeli zawiera następujące infor-
*
macje: id aktualności (nc_id), jej tytuł
*****************************************/
create table `news_groups` ( (nc_subject), streszczenie (nc_lead),
ng_id int(11) not null auto_increment, treść (nc_content) oraz referencje do
ng_name varchar(100) not null, jednej z grup (ng_id). Na Rysunku 3.
ng_description text, przedstawiony jest diagram encji dla
primary key (ng_id)
obu tabel. Wynika z niego, że tabela
);
/**************************************** news_contents znajduje się w relacji
* tabela z newsami. Zawiera relację do news_groups wiele-do-jednego w stosunku do tabeli
* news_groups.
*****************************************/ Przejdźmy teraz do pisania pierwsze-
create table news_contents (
go skryptu wykorzystującego rozszerze-
nc_id int(11) not null auto_increment,
ng_id int(11) not null, nie SDO. Pierwszą rzeczą, jaką musimy
nc_subject varchar(100) not null, uczynić, jest konfiguracja klasy DAS. Jak
nc_lead text, wiemy, klasa ta odpowiada za dostęp do
nc_content text not null, źródła danych (w naszym przykładzie
primary key (nc_id),
– MySQL). Sama klasa jest na tyle ogól-
);
na, że nie wie, jaką strukturę ma nasza
Listing 2. Konfiguracja klasy DAS dla dwóch tabel (news_groups i news_contents) baza. Dlatego też musimy powiedzieć
jej, do których tabel i kolumn ma zapisać
$newsGroupsMap = array (
odpowiednie elementy drzewa obiektów
'name' => 'news_groups',
'columns' => array( 'ng_id', 'ng_name', 'ng_description' ),
SDO. Aby skonfigurować DAS, należy
'PK' => 'ng_id' dla każdej tabeli utworzyć mapping
); – tablicę PHP zawierającą następujące
$newsContentsMap = array ( informacje:
'name' => 'news_contents',
'columns' => array('nc_id','ng_id','nc_subject','nc_lead','nc_content'),
'PK' => 'nc_id',
Ÿ nazwę tabeli,
'FK' => array ( Ÿ tablicę z nazwami pól,
'from' => 'ng_id', Ÿ nazwę pola z kluczem głównym
'to' => 'news_groups', (klucz główny w tabeli może składać
)
się z jednego pola),
);
$newsContentsRelationsMap = array(
Ÿ tablicę opisująca relacje do innej tabeli.
'parent' => 'news_groups',
'child' => 'news_contents' ); Dodatkowo, jeżeli nasz obiekt DAS ma
obsługiwać wiele tabel, pomiędzy którymi

44 www.phpsolmag.org PHP Solutions Nr 1/2006
SDO Techniki

są relacje, potrzebna jest jeszcze jedna Na Listingu 2. pokazujemy przykła- mieć tylko jedną relację (jeden klucz
tablica zawierająca informacje o kie- dową konfigurację dla tabel news_groups obcy). Na szczęście, w wielu prostych
runku tych relacji, tzn. która z tabel jest i news_contents. bazach danych, ograniczenia te nie mają
nadrzędna, a która podrzędna. Przypo- Patrząc na tablice konfiguracyjne, znaczenia. Ponadto, należy zakładać, że
mnijmy, że obecna implementacja obsłu- zauważamy jedne z największych bolą- w miarę rozwoju projektu, autorzy rozwiną
guje jedynie relacje jeden-do-wielu oraz czek obecnej implementacji SDO: klucz jego funkcjonalność również w tych dzie-
jeden-do-jednego. główny jest jednopolowy, a tablica może dzinach.
Skoro mamy już skonfigurowany
Listing 3. Skrypt dodający grupę newsów do bazy danych obiekt DAS, najwyższa pora utworzyć
kod, który zapisze grupę newsów do
<?php bazy danych. Odpowiedni skrypt z po-
require_once 'Relational.php'; minięciem mappingu przedstawiamy na
$dbConnection = new PDO('mysql:host=localhost;dbname=nazwa_bazy_danych',
Listingu 3.
'użytkownik','hasło');
$das = new SDO_DAS_Relational( array($newsGroupsMap,$newsContentsMap), Pierwszą operacją, jaką musimy
'news_groups',array( $newsContentsRelationsMap )); wykonać jest otwarcie połączenia do
$root = $das->createRootDataObject(); bazy danych. Pamiętajmy, że SDO
$newsGroup1 = $root->createDataObject('news_groups'); korzysta w tym celu z PDO. Po nawią-
$newsGroup1->ng_name = "Grupa Pierwsza";
zaniu połączenia tworzymy obiekt klasy
$newsGroup1->ng_description = "Opis dla pierwszej grupy";
$das->applyChanges($dbConnection, $root); SDO_DAS_Relational, który będzie odpo-
?> wiedzialny za zapisanie grupy newsów
do naszej bazy danych. Jako parametry,
Listing 4. Skrypt pobierający jeden rekord z bazy danych i modyfikujący go klasa ta musi otrzymać tablice konfi-
<?
gurujące wszystkie tabele, z którymi
require_once 'Relational.php'; będziemy się komunikować poprzez
$dbConnection = new PDO SDO, a także nazwę tabeli nadrzędnej
('mysql:host=localhost;dbname=nazwa_bazy_danych','użytkownik','hasło'); i tablicę z typami relacji pomiędzy tabe-
$das = new SDO_DAS_Relational( array($newsGroupsMap,$newsContentsMap),
lami. Nic nie stoi na przeszkodzie, aby
'news_groups', array( $newsContentsRelationsMap ) );
$root = $das->executeQuery($dbConnection, 'select * from news_groups where
dla każdej z tabel naszej bazy stworzyć
ng_id=1', array( 'news_groups.ng_id', 'news_groups.ng_name', osobne obiekty DAS. Pozbawiłoby nas
'news_groups.ng_description' ) ); to jednak możliwości jednoczesnego
$root['news_groups'][0]->ng_name = 'The future of GUIs for PHP'; zapisywania drzew obiektów, między
$das->applyChanges($dbConnection, $root);
którymi istnieje relacja.
?>
Wracając do przykładu: dwie naj-
Listing 5. Skrypt pobierający jeden rekord z bazy danych i kasujący go. Drugą ważniejsze klasy są już gotowe. Mu-
metodą usunięcia obiektu z drzewa jest przypisanie mu wartości null simy jeszcze utworzyć korzeń drzewa
obiektów SDO. Służy do tego metoda
require_once 'Relational.php';
createRootDataObject(), którą wywołu-
$dbConnection = new PDO
('mysql:host=localhost;dbname=nazwa_bazy_danych','użytkownik','hasło'); jemy na obiekcie DAS. Mając korzeń,
$das = new SDO_DAS_Relational( array($newsGroupsMap,$newsContentsMap), możemy teraz utworzyć obiekt news_
'news_groups', array( $newsContentsRelationsMap ) ); groups, który zapiszemy później do
$root=$das->executeQuery($dbConnection, 'select * from news_groups where bazy danych. Do tworzenia obiektów
ng_id=1', array( 'news_groups.ng_id', 'news_groups.ng_name',
w drzewie SDO, przeznaczona jest me-
'news_groups.ng_description' ) );
unset( $root['news_groups'][0] ); toda createDataObject(). Ma ona jeden
$das->applyChanges($dbConnection, $root); parametr: nazwę tabeli, dla której należy
utworzyć obiekt. W naszym przypadku
Listing 6. Skrypt umożliwiający jednoczesne dodanie do bazy danych grupy jest to news_groups. Obiekt news_groups
i związanego z nią newsa
posiada atrybuty publiczne, których na-
$newsGroup = $root->createDataObject('news_groups'); zwy są tożsame z nazwami odpowiednich
$newsGroup->ng_name = "PHP-related news"; kolumn w tabeli news_groups, znajdującej
$newsGroup->ng_description = "The best news around the world of PHP"; się w bazie danych. Informacji o nazwach
$newsContent1 = $newsGroup->createDataObject('news_contents'); tych kolumn dostarcza SDO mapping.
$newsContent1->nc_subject = 'SDO is becoming popular';
Przypisując tym atrybutom odpowiednie
$newsContent1->nc_lead = 'SDO or Service Data Objects is gaining more and more';
$newsContent1->nc_content = 'the content of article on SDO'; wartości możemy stworzyć pożądany
$newsContent2 = $newsGroup->createDataObject('news_contents'); obiekt. Aby zapisać go do bazy danych,
$newsContent2->nc_subject = 'AJAX and PHP'; należy na obiekcie DAS wykonać metodę
$newsContent2->nc_lead = 'AJAX is a solution for the interactive web pages'; applyChanges(). Ma ona dwa parametry:
$newsContent2->nc_content = 'the content of the article';
obiekt PDO i drzewo obiektów SDO.
$das->applyChanges($dbConnection, $root);
Tak oto, w siedmiu linijkach kodu,
bez napisania choćby jednego polecenia

PHP Solutions Nr 1/2006 www.phpsolmag.org 45
Techniki SDO

SQL, zapisaliśmy rekord w bazie da-
nych. Warto wspomnieć, że za jednym object(SDO_DataObjectImpl)[12]
public 'news_groups' =>
razem możemy zapisać wiele obiektów.
object(SDO_DataObjectList)[11]
Nic nie stoi bowiem na przeszkodzie,
aby metodę createDataObject() wy- object(SDO_DataObjectImpl)[14]
wołać wiele razy, tworząc tym samym public 'ng_id' => '10'
wiele obiektów news_groups. Możemy public 'ng_name' => 'Grupa Pierwsza'
public 'ng_description' => 'Opis dla pierwszej grupy'
nawet wielokrotnie zapisywać te obiek-
public 'ng_contents' =>
ty w bazie danych: SDO samo zadba object(SDO_DataObjectList)[13]
o to, co powinno zostać w niej zapisa-
object(SDO_DataObjectImpl)[15]
ne. Możliwe jest to dzięki opisanemu
public 'nc_id' => '4'
już mechanizmowi, który zapamiętuje public 'nc_subject' => '10'
każdą zmianę stanu drzewa obiektów. public 'nc_lead' => 'Jakiś tytuł'
Jeżeli więc cokolwiek dodamy, skasuje- public 'nc_content' => 'Jakaś zajawka'
my lub zmienimy, drzewo będzie miało
object(SDO_DataObjectImpl)[16]
informacje na ten temat.
public 'ng_id' => '11'
Każda operacja zapisu zmian zeruje public 'ng_name' => 'Grupa Pierwsza'
rejestr zmian, dlatego nawet po dwukrot- public 'ng_description' => 'Opis dla pierwszej grupy'
nym wykonaniu operacji applyChanges public 'ng_contents' =>
dane zostaną zapisane tylko raz. object(SDO_DataObjectList)[18]

Skoro potrafimy już zapisać dane do object(SDO_DataObjectImpl)[17]
bazy, spróbujmy je teraz odczytać, zmo- public 'nc_id' => '5'
dyfikować i ponownie zapisać. Tak jak public 'nc_subject' => '11'
poprzednio, posłużymy się przykładem. public 'nc_lead' => 'Jakiś tytuł'
public 'nc_content' => 'Jakaś zajawka'
Na Listingu 4. przedstawiamy kod, który
potrafi wykonać te zadania.
Tak jak w poprzednim przykładzie, Rysunek 4. Wynik działania zapytania, które za jednym razem wyciąga z bazy grupy
skrypt rozpoczyna się od inicjalizacji newsów i same wiadomości
klasy SDO_DAS_Relational i nawiązania
połączenia. W następnym kroku, przy SDO. Na otrzymanym drzewie może- padku, aby zapisać dokonane zmiany,
użyciu metody executeQuery() po- my wykonywać dowolne operacje, np. należy wykonać na obiekcie DAS me-
bieramy obiekty SDO z bazy danych. dodać do niego nowy element (metoda todę applyChanges().
Warto w tym miejscu zwrócić uwagę createDataObject), a także zmienić czy
na fakt, że mimo iż składnia polece- skasować istniejący element. Procedu- Drugi przykład
nia zawiera zapytanie SQL, wynikiem ra kasowania obiektu pokazana jest na Spróbujmy teraz napisać skrypt, który
jego działania jest drzewo obiektów Listingu 5. Tak jak w poprzednim przy- będzie potrafił dodać jednocześnie grupę
newsów i kilka związanych z nią wiado-
Listing 7. Kwerenda pobierająca z bazy danych drzewo z grupami i newsami mości. Kod prezentujący ten przykład
przedstawiamy na Listingu 6. Dla uprosz-
$root = $das->executeQuery($dbConnection, 'select * from news_groups, czenia pokazujemy tam tylko kod cha-
news_contents where news_groups.ng_id=news_contents.ng_id', rakterystyczny dla tej operacji, pomijając
array(
początkową inicjalizację obiektów.
'news_groups.ng_id', 'news_groups.ng_name', Zwróćmy uwagę, że po utworze-
'news_groups.ng_description','news_contents.nc_id', niu obiektu SDO dla grupy newsów,
'news_contents.nc_subject', 'news_contents.nc_lead', wywołujemy na tym obiekcie metodę
'news_contents.nc_content'
createDataObject(), która tworzy obiekt
));
SDO dla konkretnej wiadomości. Jeżeli
Listing 8. Kasowanie grupy wraz z newsami chcemy utworzyć więcej niż jedną wiado-
mość, to wystarczy po raz kolejny wywo-
$root = $das->executeQuery($dbConnection, 'select news_groups.ng_id, łać metodę createDataObject(). Warto
news_contents.nc_id from news_groups, news_contents where
zauważyć, że nie musimy martwić się
news_groups.ng_id=news_contents.ng_id',
o przekazanie obiektowi newsa id grupy,
array( gdyż SDO zrobi to za nas. Ponadto nic nie
'news_groups.ng_id', stoi na przeszkodzie, aby za jednym razem
'news_contents.nc_id'
stworzyć wiele grup i przypisać im wiele
));
newsów.
unset( $root['news_groups'][0] ); Kolejnym zadaniem jest pobranie
$das->applyChanges($dbConnection, $root); z bazy drzewa z grupami i związanymi
z nimi newsami przy pomocy jednego

46 www.phpsolmag.org PHP Solutions Nr 1/2006
SDO Techniki

zapytania. Jego rozwiązanie pokazujemy Na zakończenie pokażemy jeszcze, ju, jego funkcjonalność jest imponująca.
na Listingu 7. jak skasować grupę i związane z nią Miejmy nadzieję, że już wkrótce jego
Pomijając oczywiście operacje związa- newsy. Jest to bardzo proste, ponie- autorzy wydadzą wersję stabilną, która
ne z konfiguracją połączenia i klasy DAS waż wystarczy pobrać grupę wraz ze będzie pozbawiona obecnych manka-
widzimy, że wszystko, co musimy napisać, związanymi z nią newsami (wszystkimi) mentów. Warto pamiętać, że pomijając
zawiera się w jednej linii kodu! i wykonać na niej funkcję unset() lub wymienione ograniczenia, już obecnie
Metoda executeQuery() potrzebuje przypisać do niej wartość null. Jeżeli kod biblioteki SDO jest bardzo solidny.
dwóch parametrów: połączenia do bazy pobierzemy samą grupę, to SDO będzie Zachęcamy więc do własnych ekspe-
danych i zapytania SQL. Trzeci para- próbowało skasować jedynie ją. Nie jest rymentów i zapraszamy do następnego
metr jest opcjonalny, wymagany jedynie to oczywiście problemem, gdy pracuje- artykułu o technologiach SDO i XML,
w sytuacjach, gdy nazwa którejkolwiek my na bazie wyposażonej w mechanizm który ukaże się w kolejnym numerze
kolumny występuje dwa razy w mappin- kluczy obcych. Jeżeli jednak nasza PHP Solutions. n
gu. Taka sytuacja ma miejsce w naszym baza danych to np. MySQL, a tabele
przykładzie: kolumna ng_id pojawia na których operujemy, są w formacie
się zarówno w tabeli news_groups, jak MyISAM, to usunięcie samej grupy
i news_contents. Fakt, że są to inne ta- powiedzie się, ale dane w bazie będą
bele, nie ma znaczenia. Jeżeli więc nie niespójne. Skrypt ilustrujący kasowanie O autorze
chcemy podawać trzeciego parametru, grupy wraz z newsami pokazany jest na
musimy pamiętać, aby w naszym mo- Listingu 8. Piotr Szarwas jest pracownikiem SU-
PERMEDIA Interactive i doktorantem
delu nie powtarzała się nazwa żadnej na wydziale Fizyki Politechniki War-
kolumny. Wystarczy więc przyjąć odpo- Podsumowanie szawskiej. Od 2003 roku projektuje
wiednią strategię nazewnictwa. Rezul- Budując prostą bazę danych dla sys- aplikacje WWW w oparciu o PHP4/5.
tatem wykonania tego kodu jest drzewo temu newsów, pokazaliśmy możliwości Obecnie zajmuje się tworzeniem frame-
worka dla PHP opartego na rozwiąza-
obiektów SDO, które przedstawiamy na drzemiące w SDO. Mimo, że rozszerze- niach Hibernate i Spring.
Rysunku 4. nie to znajduje się ciągle w fazie rozwo-

R E C E N Z J A

PHP5 Zaawansowane programowanie «««««
Autorzy: Ed Lecky-Thompson, Heow Eide-Goodman, Steven D. Nowicki, Alec Cove
Wydanie: Helion 2005 Cena: 79,00 zł

Jestem nieco znudzony ofertą polskich wydań książek o PHP, pewnie dlatego również po tę pozycję sięgałem niechętnie. Przypuszcza-
łem, że będzie to materiał podobny do bardzo dobrej książki PHP Zaawansowane programowanie. Vademecum profesjonalisty autor-
stwa Georga Schlossnagle’a, tyle że z większym naciskiem na PHP5. Na szczęście PHP5 Zaawansowane programowanie okazało się
inne. Dużo inne. Książka nie stanowi vademecum profesjonalisty – nie przeczytamy w niej (w sposób encyklopedyczny) o wszystkich
zaawansowanych cechach języka PHP. Przeczytamy w niej o profesjonalnym podejściu do tworzenia złożonych aplikacji w PHP5.
Autorzy skupili się na dogłębnym opisaniu modelowania w UML, korzystania ze wzorców projek-
towych, testowania aplikacji (PHPUnit) czy tworzenia własnego warsztatu programisty – narzędzi
które będzie można wielokrotnie wykorzystać w każdym projekcie.
Bardzo ciekawa jest część (ponad 200 stron) opisująca Studium przypadku – automatyzacja
działu sprzedaży. Dowiemy się, jak podejść do dużego projektu informatycznego i jak nim zarzą-
dzać. Omówione zostaną pojęcia planowania i architektury systemu. Razem z autorami stworzymy
aplikację automatyzującą pracę zespołu sprzedaży i solidną platformę raportującą.
Szczerze poleciłbym tę książkę programistom PHP, którzy mają już za sobą kilka projek-
tów w PHP, ale chcą zacząć tworzyć aplikacje w sposób bardziej profesjonalny – przemyślany,
solidny i wydajny. Jeśli planujesz wykonanie większego projektu lub nie wierzysz (nie znasz)
w PHP5, a szukasz dobrej platformy programistycznej do tworzenia aplikacji WWW – ta lektura
jest dla Ciebie.

Dariusz Pawłowski

PHP Solutions Nr 1/2006 www.phpsolmag.org 47
Techniki

AJAX – wyjątkowo interaktywne
i wydajne aplikacje WWW
Joshua Eichorn, Werner M. Krauß

Aplikacje tworzone w PHP pozwalają
osiągnąć bardzo wiele przy ograniczonym
oprogramowaniu klienckim, co oznacza łatwe
wdrażanie i aktualizacje, a tym samym szybkie
efekty pracy. Architektura ta ma też dotkliwe
wady, jak opóźnienia między wyświetlaniem
kolejnych stron lub brak możliwości pobierania
nowych danych bez wysyłania formularza.
Na szczęście istnieje mechanizm AJAX.

A
JAX pozwala stworzyć dodatkowy asynchronicznej wymiany danych. Techni-
kanał komunikacji między klientem ki te są łączone w jedną całość za pomocą
a serwerem PHP, a tym samym JavaScriptu, odpowiedzialnego za logikę
wysyłać i odbierać dane bez przeładowy- aplikacji i dynamiczną aktualizację interfej-
wania strony. Otwiera to zupełnie nowe su użytkownika stosownie do potrzeb.
możliwości, a w połączeniu z operacjami Pomimo XML w nazwie, AJAX nie-
na modelu DOM z poziomu JavaScriptu, koniecznie wymaga używania formatu
oznacza nadejście ery bogato wyposażo- XML do wymiany danych. Poza XML-em
W SIECI nych, interaktywnych aplikacji PHP, wol- obsługiwane są między innymi zwykły
nych od irytującego klikania i czekania. tekst, sformatowany HTML (dodawany
W tym artykule przedstawimy prak- do bieżącej strony poprzez właściwość
1. http://sourceforge.net/
projects/jpspan/ – strona tyczne wprowadzenie do techniki AJAX innerHTML) oraz format JSON (JavaScript
główna projektu JPSpan na przykładzie dwóch bibliotek PHP i nie-
2. http://pear.php.net/package/
wielkiej aplikacji o działaniu podobnym do
HTML_AJAX/ – strona głów- Powinieneś wiedzieć...
na pakietu HTML_AJAX Google Suggest. Powinieneś się dobrze orientować
3. http://www.google.com/
webhp?complete=1&hl=en w zasadach programowania obiektowe-
– Google Suggest Czym jest AJAX? go w PHP4 lub PHP5. Przyda się też
4. http://www.ajaxpatterns.org/ pewna znajomość JavaScriptu.
AJAX (skrót od Asynchronous JavaScript
– serwis poświęcony wzor-
com aplikacji AJAX And XML) jest nazwą nowej metody pro- Obiecujemy...
5. http://www.ajaxpatterns.org/ gramowania, łączącej kilka różnych tech- Po przeczytaniu artykułu będziesz znał
Suggestion – opis wzorca
w stylu Google Suggest nik: (X)HTML i CSS do tworzenia interfej- zasadę działania i stosowania techniki
6. http://blog.joshuaeichorn. su użytkownika, DOM (Document Object AJAX oraz śledzenia kodu. Pokażemy
com/archives/category/php/ też możliwości bibliotek implementują-
ajax – blog Joshuy Eichorna
Model) do obsługi elementów dynamicz- cych tę technikę.
poświęcony AJAX-owi nych i interakcji oraz XMLHttpRequest do

48 www.phpsolmag.org PHP Solutions Nr 1/2006
AJAX Techniki

Object Notation), który można przepuścić
przez eval() w celu uzyskania typów ������������������������ ������
JavaScript. Można też korzystać z dowol-

����
nego innego formatu danych dającego się ������������������ ���
obsłużyć w JavaScripcie i PHP.
����������������
AJAX-a można najprościej zde-
finiować jako metodę wykorzystania ��������������
������������������������
JavaScriptu do komunikacji z serwerem ������������ ����
niezależnie od tradycyjnych żądań POST ������������������
������
i GET. Strona techniczna jest tu jednak
�������������������
mniej istotna – najważniejsze są zupełnie ���������������
nowe możliwości tworzenia aplikacji inter-
netowych.
��������������
Podstawą pracy AJAX-a jest obiekt ������������������������
����
XMLHttpRequest, stanowiący standar- �������������
������������������
dowy element wielu przeglądarek. Jeśli
������
postanowisz dodać obsługę AJAX-a do �������������������
swojej aplikacji za pomocą biblioteki, ���������������

to nie musisz wiele wiedzieć o samym
XMLHttpRequest, gdyż wszystkim zajmie
się biblioteka. Fizyczna implementacja
obiektu XMLHttpRequest zależy od kon- Rysunek 2. Przepływ danych w aplikacji AJAX
kretnej przeglądarki – w Internet Explo-
rerze jest to wbudowany obiekt ActiveX, ne bezpośrednio przez użytkownika, lecz możliwość uzyskania znacznie wyższego
natomiast w Firefoksie, Safari i większości poprzez XMLHttpRequest. Odebraną treść poziomu interaktywności w aplikacjach
innych przeglądarek jest on wewnętrznym można traktować jako zwykły tekst, ale internetowych. Reakcje programu na dzia-
obiektem JavaScriptu. w przypadku treści typu text/xml można łania użytkownika są dużo szybsze, bez
XMLHttpRequest udostępnia proste API, też stworzyć obiekt XML DOM. Obsługa nużącego klikania i czekania, przez co ob-
pozwalające wysyłać do serwera żądania modelu DOM przyczyniła się do popular- sługa całego programu znacznie bardziej
HTTP metodami GET i POST. Dla serwe- ności XML-a jako formatu wymiany da- przypomina pracę z tradycyjną aplikacją
ra są to zwyczajne żądania przeglądarki, nych między klientem a serwerem, nato- stacjonarną.
zawierające nawet wszystkie pliki cookie miast utrzymanie obsługi zwykłego tekstu Rysunek 1 przedstawia przepływ da-
dla bieżącej domeny oraz autoryzację pozwala korzystać z dowolnego formatu nych w typowej aplikacji internetowej. Użyt-
HTTP (jeśli oczywiście jest włączona). Od dającego się przetworzyć na poziomie kownik wypełnia formularz i wysyła go na
strony JavaScriptu, XMLHttpRequest daje JavaScriptu. serwer WWW, który przetwarza formularz
dostęp do treści i nagłówków podczas i odsyła dane do czekającego użytkownika.
wysyłania i odbioru żądań. Możliwość ta Dlaczego AJAX? Zwracanym wynikiem jest pełna strona
jest często używana do poinformowania Najważniejszym argumentem przema- HTML, którą przeglądarka klienta musi za-
serwera, że żądanie nie zostało zgłoszo- wiającym za korzystaniem z AJAX-a jest ładować w całości (treść i strukturę). Mar-
nuje się w ten sposób czas i pasmo, gdyż
kod strony wynikowej najczęściej niewiele
���������������� ������ się różni od kodu poprzedniej strony.
Aplikacja AJAX wysyła do serwera
����

���
������������������ wyłącznie żądania pobierające nowe, po-
�����������������
trzebne dane, a odpowiedź serwera jest
przetwarzana przez JavaScript po stronie
��������������������� klienta. Dzięki wprowadzeniu tej dodatko-
wej warstwy JavaScriptu, przetwarzanie
danych nie spowalnia działania interfejsu
������������������
��������������������� ���� użytkownika. Cała aplikacja działa znacz-
nie szybciej, gdyż między serwerem,
��������������������
������������������� a klientem przesyłanych jest nieporów-
����������������������� nanie mniej danych, a spora część prze-
twarzania odbywa się po stronie klienta
(Rysunek 2).
Praktycznie rzecz ujmując, stworze-
Rysunek 1. Przepływ danych w tradycyjnej aplikacji internetowej nie aplikacji AJAX wymaga zatem dwóch

PHP Solutions Nr 1/2006 www.phpsolmag.org 49
Techniki

elementów: odpowiednich skryptów po Explorerze z wyłączoną obsługą ActiveX, z Flasha. Warto w tym miejscu zaznaczyć,
stronie klienta i specjalnego kanału komu- co często dotyczy na przykład kafejek że pomimo teoretycznej możliwości połą-
nikacji z serwerem. internetowych. Może się także zdarzyć, czenia zalet AJAX-a i Flasha w ramach
że aplikacja będzie działać nieco inaczej jednej aplikacji, złożoność takiego rozwią-
Zalety techniki AJAX w różnych przeglądarkach i na różnych zania jest na tyle duża, że lepiej używać
AJAX ma wiele zalet, z których najbardziej platformach, choć to samo dotyczy tych technik osobno.
zauważalną jest znaczące rozszerzenie tworzenia tradycyjnych aplikacji interne-
zakresu możliwości interfejsu użytkowni- towych. Wykorzystanie bibliotek
ka. Jednak samo w sobie to nie wystar- AJAX oferuje spore możliwości inte- Istnieje wiele bibliotek narzędziowych
czy – w końcu istnieje też wiele innych rakcji, ale do wielu zadań po prostu się mających na celu ułatwienie integracji Ja-
technologii o zbliżonych możliwościach. nie nadaje, na przykład do dynamicznego vaScriptu i PHP. Wszystkie uwzględniają
O wyjątkowości AJAX-a stanowi przede rysowania elementów czy obsługi anima- jakąś metodę przesyłania danych, ale
wszystkim to, że bazuje on na uznanych cji – w takich sytuacjach lepiej korzystać większość oferuje dodatkowe możliwości,
standardach, więc w przeciwieństwie do
innych narzędzi do tworzenia interaktyw- Listing 1. Serwer JPSpan
nych aplikacji internetowych (na przykład
Flasha) można go z łatwością wpasować <?php
w istniejące procesy deweloperskie. Moż- session_start();

na więc dalej korzystać ze swojego ulu- // Klasa powitania
bionego edytora czy środowiska programi- class HelloWorld {
stycznego, bez konieczności poznawania function HelloWorld() {
nowych narzędzi. if (!isset($_SESSION['strings'])) {
$_SESSION['strings'] = array('Hello','World','Hello World');
Istnieje też wiele darmowych zesta-
}
wów narzędzi Open Source ułatwiających }
tworzenie i rozwijanie aplikacji AJAX, function addString($string) {
a przy okazji redukujących objętość kodu $_SESSION['strings'][] = $string;
JavaScriptu, jaki trzeba wpisywać ręcznie. return $this->stringCount();
}
W dalszej części artykułu zobaczymy, jak
function randomString() {
dołączać obsługę AJAX-a do własnych $index = rand(0,count($_SESSION['strings'])-1);
aplikacji z pomocą popularnych bibliotek. return $_SESSION['strings'][$index];
}
Wady AJAX-a function stringCount() {
return count($_SESSION['strings']);
Opisywana metoda interakcji z klientem
}
ma też swoje wady. Nie można przewi- }
dzieć, z jakiej przeglądarki korzysta użyt-
kownik, więc aplikacja może nie działać // Ustawienie stałej JPSPAN
na niekompatybilnych przeglądarkach require_once 'jpspan-0.4.3/JPSpan.php';
// Załadowanie serwera PostOffice
lub przy wyłączonej obsłudze JavaScrip-
require_once JPSPAN . 'Server/PostOffice.php';
tu. Oznacza to, że dobrą praktyką jest // Utworzenie serwera PostOffice
uwzględnienie awaryjnej metody obsługi, $S = & new JPSpan_Server_PostOffice();
na przykład poprzez stworzenie bazowej // Rejestracja klasy w serwerze
aplikacji z wykorzystaniem tradycyjnych $S->addHandler(new HelloWorld());

technik, a następnie rozbudowanie jej // Obsługa wyświetlania JavaScriptu po dodaniu
o opcjonalne usprawnienia używające // ciągu ?client do URL-a serwera
AJAX-a. if (isset($_SERVER['QUERY_STRING']) &&
Trzeba też pamiętać, że aplikacje strcasecmp($_SERVER['QUERY_STRING'], 'client')==0) {
// Wyłączenie kompresji wynikowego JavaScriptu
z AJAX-em nie będą działać w Internet
// (m.in. usuwania białych znaków) z powodu
// problemów wydajnościowych
JPSpan i PEAR define('JPSPAN_INCLUDE_COMPRESS',false);
Wersja biblioteki JPSpan dla repozy- // Wyświetlenie klienckiego JavaScriptu
torium PEAR jest dostępna w serwisie $S->displayClient();
http://www.pearified.com. Do instalacji }else {
będzie potrzebny PEAR w nowej wersji // Początek faktycznej obsługi żądań
1.4, obsługujący inne kanały niż tylko // Dołączenie obsługi błędów
http://pear.php.net. Poleceniem pear // błędy, ostrzeżenia i komunikaty serializowane do JS
channel-discover pearified.com na- // Obsługiwanie żądań
leży dodać kanał do repozytorium, po $S->serve();
czym można już zainstalować JPSpan }
poleceniem pear install pearified/ ?>
JavaScript_JPSpan.

50 www.phpsolmag.org PHP Solutions Nr 1/2006
AJAX Techniki

od bezpośredniego odwzorowania metod zwalającą odwzorowywać tablice PHP na
klas PHP na pośrednika JavaScriptu, po obiekty JavaScriptu. Strona z serwera jest HTML_AJAX
środowisko tworzenia elementów inter- dołączana do wynikowych stron HTML ze a nazwy klas
fejsu użytkownika. Przyjrzyjmy się bliżej znacznikiem client, generując pośrednie Nazwy klas zwracane przez PHP4 są
dwóm popularnym pakietom: bibliotekom klasy JavaScript o takim samym API, jak zawsze zapisane małymi literami. Jeśli
koniecznie chcesz zachować rozróż-
JPSpan i HTML_AJAX. klasy PHP. Ze względu na ograniczenia nienie wielkości liter lub potrzebujesz
PHP4, wszystkie nazwy klas i metod są zgodności między PHP4 i PHP5,
JPSpan zapisywane małymi literami. Domyślny musisz skorzystać z dodatkowych pa-
Najpierw zajmiemy się pakietem JPSpan serwer JPSpan nosi nazwę JPSpan_ rametrów metody registerClass(),
określających nazwę klasy rejestrowa-
– jedną z bardziej dojrzałych bibliotek Server_PostOffice i może służyć do
nej w JavaScripcie oraz tablicę ekspor-
AJAX dla PHP, dostępną od listopada odwzorowywania na JavaScript zarówno towanych metod:
2004 r. Podstawową funkcją biblioteki całych klas, jak i ich części. Korzystając
$server = new HTML_AJAX_Server();
jest niezależna od przeglądarki obsłu- z serwera w dużym serwisie można
$hello = new HelloWorld();
ga mechanizmu AJAX bazująca na rozważyć dodanie znacznika class, co
$methods = array('foo','bar');
XMLHttpRequest, z możliwością wyboru pozwoli ograniczyć liczbę klas dołącza- $server->registerClass($hello, §
pracy synchronicznej lub asynchronicz- nych i rejestrowanych na serwerze, a tym 'Example', $methods);
nej. Dostępne jest wspólne, obiektowe samym zmniejszy koszt przetwarzania.
API dla JavaScriptu i PHP. JPSpan obsłu- Osobiście nie doświadczyłem jednak
guje też wiele innych funkcji, na przykład żadnych problemów wydajnościowych Wywołania polegają na utworzeniu
przezroczyste odwzorowania obiektów nawet przy pięciu stale zarejestrowanych instancji odpowiedniej klasy JavaScrip-
z bardzo dobrą serializacją danych, po- klasach integracyjnych. tu, a następnie wywoływaniu jej metod.
W chwili utworzenia instancji w trybie
Listing 2. Klient JPSpan asynchronicznym, określana jest klasa
zwrotna, po czym wyniki są przesyłane
<html> jej metodom o takich samych nazwach,
<head> jak metody pierwotnie wywoływane.
<title>Hello World w JPSpan</title>
JPSpan dodatkowo obsługuje złożone ty-
<script type='text/javascript' src='jpspan_server.php?client'></script>
<script> py danych, w tym wielowymiarowe tablice
// Klasa JavaScript zawierająca metody zwrotne i obiekty, jak również serializację i prze-
var hwCallback = { kazywanie błędów PHP do JavaScriptu
randomstring: function(result) { z możliwością konfiguracji obsługi błędów
document.getElementById('canvas').innerHTML += '<p>'+result+'</p>';
po stronie klienta.
},
stringcount: function(result) {
document.getElementById('count').innerHTML = result; Strona serwera
}, Przyjrzyjmy się działaniu JPSpan nieco
addstring: function(result) { bliżej na prostym przykładzie typu Hello
document.getElementById('count').innerHTML = result;
World, wyświetlającym losowy napis
}
} w reakcji na kliknięcie i pozwalającym
// Utworzenie obiektu zdalnego. Jego nazwa jest odwzorowana małymi literami, dodawać nowe napisy. Zaczynamy od
// gdyż w nazwach klas i funkcji PHP4 wielkość liter nie jest rozpoznawana. utworzenia klasy helloworld, zawierającej
// Rejestrując każdą klasę na serwerze można przywrócić rozróżnianie proste metody PHP do obsługi dodawania
// wielkości liter.
napisów do tablicy sesyjnej, zwracania
var remoteHW = new helloworld(hwCallback);
function do_addString() { długości tablicy i wyświetlania losowe-
remoteHW.addstring(document.getElementById('string').value); go napisu z tablicy. Są to odpowiednio
document.getElementById('string').value = ''; metody addString(), stringCount()
} i randomString(), a ich kod przedstawia
</script>
Listing 1.
</head>
<body onLoad="remoteHW.stringcount()"> Praca z klasami JPSpan nie różni się
<input type="button" name="check" value="Pokaż losowy napis" onclick= niczym od obsługi zwykłych klas. Trzeba
"remoteHW.randomstring(); return false;"> tylko pamiętać, że klasa ta jest odtwa-
<div>Liczba losowych napisów: <span id="count"></span></div> rzana przy każdym wywołaniu ze strony
<div id="canvas" style="border: solid 1px black; margin: 1em; padding: 1em;"></div>
JavaScriptu, więc utrzymywanie danych
<div>
Podaj nowy napis: składowych między wywołaniami wymaga
<input type="text" name="string" id="string" size="20"> pamiętania instancji klasy w ramach sesji.
<input type="button" name="check" value="Dodaj nowy napis" onclick= Musimy jeszcze dołączyć JPSpan
"do_addString(); return false;"> i odpowiedni plik serwera PostOffice, po
</div>
czym możemy stworzyć nową instancję
</body>
</html> serwera i zarejestrować w niej naszą
klasę wywołując $S->addHandler(new

PHP Solutions Nr 1/2006 www.phpsolmag.org 51
Techniki

HelloWorld()). Pozostaje jeszcze tylko
określić, czy chcemy wysłać kliencki kod Listing 3. HTML_AJAX może posłużyć do pobierania treści z innej strony na tym
samym serwerze
JavaScript, czy też obsługiwać żądania.
Jak widać na Listingu 1, obiektowe API <html>
JPSpan bardzo ułatwia przygotowania po <head>
stronie serwera. <script type='text/javascript' src="server.php?client=main"></script>
<script type='text/javascript' src="server.php?client=dispatcher"></script>
<script type='text/javascript' src="server.php?client=HttpClient"></script>
Strona klienta <script type='text/javascript' src="server.php?client=Request"></script>
Teraz zajmiemy się stroną kliencką naszej <script type='text/javascript' src="server.php?client=json"></script>
prostej aplikacji – Listing 2 przedstawia </head>
kod klienta. Od razu zwraca uwagę wyraź- <body>
ne rozdzielenie kodu HTML i PHP podczas <script type="text/javascript">
function clearTarget() {
pracy z JPSpan. Wystarczy tylko umieścić
document.getElementById('target').innerHTML = 'clear';
w ramach strony HTML poniższy, automa- }
tycznie generowany kod, odpowiedzialny // Operacja 'grab' jest najprostszym zastosowaniem HTML_AJAX, polegającym na
za faktyczne połączenie HTML i PHP: // wysłaniu żądania do strony i pobraniu wyników. Można jej używać w trybie
// synchronicznym lub asynchronicznym (z wywołaniem zwrotnym).
var url = 'README';
<script type='text/javascript'
function grabSync() {
src='jpspan_server.php?client'> document.getElementById('target').innerHTML = HTML_AJAX.grab(url);
</script> }
function grabAsync() {
Przy odrobinie pracy nagłówkami po- HTML_AJAX.grab(url,grabCallback);
}
zwala to wygodnie obsłużyć składowanie
function grabCallback(result) {
znanych informacji po stronie klienta, document.getElementById('target').innerHTML = result;
co bardzo przydaje się na przykład przy }
dodawaniu do istniejącego serwisu pola // Operacja 'replace' może działać albo na adresie (tak jak grab), albo na
autouzupełniania. // zdalnej metodzie. W tym drugim przypadku trzeba ustawić defaultServerUrl na adres
// eksportujący wywoływaną metodę. Obecnie replace używa wyłącznie synchronicznych
Następnie tworzymy klasę JavaScriptu
// wywołań AJAX - wywołania asynchroniczne mogą się pojawić w przyszłości.
o nazwie hwCallback, zawierającą metody function replaceUrl() {
zwrotne zastępujące treść odpowiednich HTML_AJAX.replace('target',url);
elementów <div> wartościami podanymi }
przez serwer z wykorzystaniem właści- </script>
<ul>
wości innerHTML. Pozostaje już tylko utwo-
<li><a href="javascript:clearTarget()">Czyszczenie pola docelowego</a></li>
rzyć zdalny obiekt: <li><a href="javascript:grabSync()">Przykład pobrania synchronicznego</a></li>
<li><a href="javascript:grabAsync()">Przykład pobrania asynchronicznego </a></li>
var remoteHW=new helloworld(hwCallback); <li><a href="javascript:replaceUrl()">Zastąpienie zawartości treścią pobraną
spod wskazanego adresu</a></li>
</ul>
Klasa helloworld jest wyeksportowaną
<div style="white-space: pre; padding: 1em; margin: 1em; width: 600px; height:
klasą PHP, którą wcześniej utworzyliśmy 300px; border: solid 2px black; overflow: auto;" id="target">Pole docelowe</div>
po stronie serwera. Nazwa klasy zawie- </body>
ra wyłącznie małe litery, gdyż PHP4 nie </html>
rozróżnia wielkości liter w nazwach klas
Listing 4. Klasa implementująca serwer podpowiedzi (plik suggest.class.php)
i funkcji. Reszta kodu z Listingu 2. to już
tylko dodanie formularza HTML z odpo- class suggest {
wiednimi metodami obsługi – i już może- function suggest() {
my się pobawić naszą pierwszą aplikacją require_once 'pear_array.php';
$this->strings = $aPear;
stworzoną w technologii AJAX.
}
function getString($input='') {
HTML_AJAX if ($input == '') return '';
Biblioteka HTML_AJAX daje znacznie $input = strtolower($input);
większe możliwości niż JPSpan, ale dla $suggestStrings=array();
foreach ($this->strings as $string) {
uproszczenia przykładu skorzystamy z po-
if (strpos(strtolower($string),$input) === 0) {
dobnej konfiguracji, co w przypadku po- $suggestStrings[] = $string;
przedniego przykładu: zewnętrzna stro- }
na serwera generuje kod pośredni Java- }
Scriptu, który jest dołączany i faktycznie return $suggestStrings;
}
wykonywany na stronie HTML. HTML_
}
AJAX potrafi też generować cały kod po-
średnika i serwera w jednym skrypcie, ale

52 www.phpsolmag.org PHP Solutions Nr 1/2006
AJAX Techniki

in, Dispatcher, HttpClient, Request
i JSON), a w przyszłości zostanie do-
dana obsługa dodatkowych, opcjonal-
nych części.
Ÿ Żądanie generowanej namiastki, za-
wierające ?stub=nazwaklasy w ciągu
zapytania (można też podać wartość
all).
Ÿ Żądanie AJAX, zawierające w ciągu
zapytania ?c=nazwaklasy&m=nazwame
tody.

Pierwsze dwa rodzaje żądań można
Rysunek 3. Serwer podpowiedzi w akcji łączyć w jednym żądaniu, lecz trzeba
pamiętać, że wiąże się to z pewnym
nie polecałbym tej metody, gdyż tracimy Serwer kompromisem: mniej żądań to mniej po-
wtedy możliwość lokalnego składowania Strona HTML_AJAX działająca na serwe- łączeń z serwerem, ale z drugiej strony
wcześniej wygenerowanego kodu Java- rze jest bardzo prosta – jej działanie polega generowane namiastki zmieniają się
Scriptu. na utworzeniu instancji serwera HTML_AJAX_ częściej od danych klienckich, co może
Instalacja pakietu HTML_AJAX jest Server, zarejestrowaniu wszystkich ekspor- negatywnie wpłynąć na efektywność
bardzo prosta – wystarczy wykonać po- towanych klas (zwanych tu stubs, czyli na- lokalnego składowania kodu. Ostrożnie
lecenie pear install HTML_AJAX-alpha. miastkami) i obsługiwaniu nadchodzących należy też korzystać z żądań stub=all,
Jeśli na serwerze nie masz narzędzia żądań. Istnieją trzy możliwe rodzaje żądań: gdyż namiastka dla, na przykład, dzie-
PEAR, możesz po prostu pobrać pakiet sięciu klas może już być spora. W ko-
http://pear.php.net/package/HTML_AJAX, Ÿ Żądanie klienckie, zawierające lejnej wersji biblioteki HTML_AJAX po-
rozpakować go i ręcznie umieścić w wy- ?client=all w ciągu zapytania. jawi się możliwość podawania wielu klas
branym katalogu, wymienionym oczywi- Zamiast all można też podać jedną w ramach jednego żądania namiastki
ście w ramach parametru include_path z części składowych klienta. Obecnie w postaci listy rozdzielanej przecinkami,
w php.ini. obsługiwanych jest pięć części ( Ma- a więc stub=test,test2.

R E K L A M A

PHP Solutions Nr 1/2006 www.phpsolmag.org 53
Techniki

z tablicy zawierającej możliwe hasła wy-
Tryb synchroniczny i asynchroniczny szukiwania, choć w rzeczywistej aplikacji
Biblioteki JPSpan i HTML_AJAX obsługują pracę zarówno w trybie asynchronicznym
(z wywołaniami zwrotnymi), jak i synchronicznym (z bezpośrednim zwracaniem wartości).
byłyby one prawdopodobnie pobierane
Ogólnie lepiej jest stosować operacje asynchroniczne, gdyż wywołania synchroniczne z bazy danych. Lista wyszukiwania wy-
mogą wstrzymywać pracę interfejsu użytkownika w oczekiwaniu na odpowiedź drugiej maga tylko jednej metody getString(),
strony. Oczywiście niekiedy jest to zachowanie pożądane, ale przesyłając większe ilości która porównuje przekazany ciąg znaków
danych trzeba wtedy zawsze pamiętać o wyświetleniu komunikatu typu proszę czekać.
Wywołania synchroniczne są znacznie łatwiejsze do oprogramowania, ale mimo to
z kolejnymi pozycjami tablicy. Pasujące
lepiej oprzeć się pokusie bezkrytycznego ich stosowania. Żądania AJAX w sieci lokalnej elementy są następnie kopiowane do
najczęściej trwają nie dłużej niż 50 ms, ale w przypadku przesyłania danych przez Internet tablicy wyników, która jest ostatecznie
czas ten najczęściej wzrasta do ponad 250 ms. Oznacza to, że użytkownik nie będzie zwracana.
w stanie skorzystać z żadnego elementu strony czy nawet przełączyć się na inną zakładkę
przeglądarki przez ćwierć, a często i pół sekundy.
Teraz uruchamiamy serwer usługi (Li-
sting 5). W tym przykładzie skorzystamy
z klasy AutoServer, która rozszerza pod-
Łatwa aktualizacja zawartość elementu o identyfikatorze stawową klasę serwera i dodaje metodę
treści bez AJAX-a podanym jako target treścią pobraną inicjalizacyjną dla każdej klasy. Pozwala
HTML_AJAX pozwala też korzystać z pod- z adresu url. Ze względów bezpieczeń- to zarządzać eksportem kilku klas PHP
stawowych możliwości AJAX-a wyłącznie stwa można w ten sposób pobierać treści za pomocą jednego serwera – wystarczy
po stronie klienckiego JavaScriptu, dzięki wyłącznie z adresów na tym samym ustawić wartość zmiennej $initMethods
czemu można bardzo szybko dodać do serwerze. Nie jest to jednak ograniczenie na true i nadać metodom inicjalizacyjnym
strony proste elementy używające AJAX-a biblioteki HTML_AJAX, lecz ograniczenie nazwy w postaci initNazwaKlasy, co dla
lub włączyć HTML_AJAX do istniejącego przeglądarki, mające na celu zapobieganie naszej klasy oznacza utworzenie meto-
środowiska. Typowym zastosowaniem jest atakom cross site scripting (XSS). dy initSuggest(). Wykorzystanie klasy
aktualizacja zawartości elementu HTML za AutoServer w tak prostym przykładzie to
pomocą treści wygenerowanej przez inną Przykład w stylu Google oczywiście strzelanie z armaty do muchy,
stronę PHP, co daje elastyczność ramek Suggest z HTML_AJAX ale pokazuje ciekawe możliwości biblio-
<iframe> bez ich wad (patrz Listing 3). Pora na nieco bardziej zaawansowany teki HTML_AJAX, które mogą się bardzo
Po dołączeniu do strony niezbędnego przykład: pole podpowiedzi podobne do przydać w większych projektach.
kodu JavaScriptu można pobierać treść mechanizmu Google Suggest (http:// I to by było na tyle po stronie PHP.
ze wskazanego adresu na serwerze www.google.com/webhp?complete=1&hl= Jeśli nasza metoda działa poprawnie
w trybie synchronicznym za pomocą HTML_ en), ale służące do wyszukiwania pakie- i nie generuje żadnych błędów, to nie
AJAX.grab(url) lub w trybie asynchronicz- tów PEAR (patrz też Ramka Wzorzec musimy jej więcej zmieniać i możemy się
nym za pomocą HTML_AJAX.grab(url,grab AJAX Suggest). zająć implementacją po stronie klienta.
Callback), gdzie argument grabCallback HTML_AJAX pozwala zarządzać inte- Rzut oka na Listing 6 pokazuje, że za-
wskazuje funkcję zwrotną automatycznie rakcją klienta z serwerem w kilku prostych czynamy od dołączenia JavaScriptu dla
wywoływaną przez HTML_AJAX po po- wierszach kodu. Jak widać na Listingu 4., serwera:
braniu treści. Można też wywołać HTML_A klasa obsługująca stronę PHP jest dość
JAX.replace('target',url), by zastąpić prosta. Dla potrzeb przykładu korzystamy <script type='text/javascript'
src='auto_server.php?client=
all&stub=suggest'></script>
Wzorzec AJAX Suggest
Podpowiadanie użytkownikom możliwych sposobów uzupełnienia tekstu wprowadzanego
w polu tekstowym uatrakcyjnia stronę i ułatwia korzystanie z niej – wystarczy zapropono- Następnie tworzymy kod obsługi żądania
wać kilka słów lub wyrażeń, które mogą pasować do danych wprowadzanych przez użyt- w postaci metody do_suggest() oraz funk-
kownika. Do implementacji mechanizmu uzupełniania służy najczęściej połączenie pola cję zwrotną do wyświetlania wyników, a na
tekstowego z listą rozwijaną wraz z synchronizacją tych elementów.
koniec tworzymy nową instancję zdalnej
Użytkownik może podać dowolny tekst, a bieżąca pozycja na liście będzie odpowia-
dać dotychczas wprowadzonemu ciągowi. Możliwe jest również wybranie elementu z listy, wyszukiwarki AJAX. Reszta kodu to po
co spowoduje zastąpienie zawartości pola tekstowego wybranym hasłem. prostu formularz z jednym polem teksto-
Implementacja tego wzorca wiąże się z reguły z wykorzystaniem zwykłego pola tek- wym i elementem <div> do wyświetlania
stowego i stworzeniem niewidocznej z początku warstwy (elementu <div>), w której będą
wyników. Dodanie do pola tekstowego ob-
umieszczane kolejne podpowiedzi. Do pola tekstowego trzeba dołączyć funkcję obsługi
zdarzenia, kontrolującą zawartość pola w celu zapewnienia poprawnego wyświetlania sługi zdarzenia onkeyup="do_suggest();
pasujących podpowiedzi na liście. return false;" powoduje wywołanie
Nie będziemy oczywiście wysyłać żądania po każdym naciśnięciu klawisza – lepiej sko- funkcji do_suggest() po każdym zwolnie-
rzystać z techniki dławienia zgłoszeń. W tym przypadku przeglądarka sprawdza co (na przy-
niu klawisza (zdarzenie onkeypress byłoby
kład) 350 milisekund, czy zawartość pola uległa zmianie – jeśli tak, to do serwera wysyłane
jest odpowiednie żądanie. Pozwala to ograniczyć liczbę żądań (i tym samym zaoszczędzić obsługiwane zbyt wcześnie).
nieco pasma), a przy okazji nie będzie przeszkadzać szybko piszącemu użytkownikowi.
Serwer odpowiada na żądanie wysyłając uporządkowaną listę pasujących podpowie- Jak to działa?
dzi, którą po stronie klienta odbiera funkcja zwrotna. Funkcja ta wprowadza w modelu doku-
Każda zmiana wartości pola tekstowego
mentu odpowiednie zmiany, by użytkownik mógł przejrzeć nowe podpowiedzi i ewentualnie
wybrać jedną z nich. Z każdą pozycją listy skojarzona jest funkcja obsługi kliknięcia, odpo- powoduje wywołanie funkcji do_suggest(),
wiadająca za aktualizację pola tekstowego treścią wybranej przez użytkownika pozycji. która z kolei wywołuje metodę remoteSu
ggest.getstring() javascriptowej klasy

54 www.phpsolmag.org PHP Solutions Nr 1/2006
AJAX Techniki

HTML_AJAX. Ta komunikuje się z ser-
Listing 5. Serwer podpowiedzi w HTML_AJAX (plik auto_server.php) werem, który odsyła tablicę pasujących
podpowiedzi, przekazywaną następnie
session_start();
require_once 'HTML/AJAX/Server.php'; funkcji zwrotnej, która kończy cały proces
class AutoServer extends HTML_AJAX_Server { dokonując niezbędnych zmian w struktu-
// Ustawienie tego znacznika jest konieczne, by korzystać z metod inicjalizacyjnych rze dokumentu i wyświetlając podpowiedzi
var $initMethods = true; w ramach elementu <div>.
W ten sposób mamy już działający,
// Metody inicjalizacyjne dla klasy podpowiedzi
function initSuggest() { choć nie najpiękniejszy przykład. Po
require_once 'suggest.class.php'; pierwsze, funkcja autouzupełniania prze-
$suggest = new suggest(); glądarki w tym przypadku tylko przeszka-
$this->registerClass($suggest); dza. Możemy ją jednak łatwo wyłączyć
}
dodając do pola tekstowego atrybut
}
$server = new AutoServer(); autocomplete="off". Po drugie, sposób
$server->handleRequest(); wyświetlania podpowiedzi pozostawia
bardzo wiele do życzenia. Spróbujmy
Listing 6. Prosty klient podpowiedzi więc ulepszyć funkcję zwrotną – Listing 7.
<html>
przedstawia poprawiony kod.
<head> Po usunięciu poprzedniej zawartości
<title>HTML_AJAX Suggest</title> elementu resultDiv, wyświetlającego wy-
<script type='text/javascript' src='auto_server.php?client=all&stub=suggest'> niki, opakowujemy każdy wynik w osobny
</script>
znacznik <span> dla uzyskania lepszej kon-
<script>
function do_suggest() {
troli nad formatowaniem, po czym w pętli
remoteSuggest.getstring(document.getElementById('string').value); for dodajemy kolejne wyniki do warstwy
} resultDiv. Etap opakowania wykonujemy
wywołując metody JavaScriptu document.
// Stworzenie tablicy asocjacyjnej do składowania metod zwrotnych
createElement("span") i appendChild().
var suggestCallback = {
getstring: function(result) {
Dla zwiększenia czytelności można popra-
document.getElementById('suggestions').innerHTML = result; cować nad stylem (Listing 8). Najważniej-
} szy jest tu wpis powodujący wyświetlanie
} podpowiedzi jedna pod drugą zamiast
w jednym wierszu:
// Utworzenie obiektu zdalnego. Jego nazwa jest odwzorowana małymi literami,
// gdyż w nazwach klas i funkcji PHP4 wielkość liter nie jest rozpoznawana.
// Rejestrując każdą klasę na serwerze można przywrócić rozróżnianie #suggestions span {
// wielkości liter. display: block;
var remoteSuggest = new suggest(suggestCallback); }
</script>
</head>
<body> Listing 8. Style CSS dla klienta
<div> podpowiedzi
Podaj nazwę pakietu PEAR:
<input type="text" name="string" id="string" size="20" onkeyup=" * {
do_suggest(); return false;"> padding: 0;
<input type="button" name="check" value="Podpowiedz..." onclick="do_suggest(); margin: 0;
return false;"> font-family : Arial, sans-serif;
</div> }
<div id="suggestions">&nbsp;</div> #suggestions {
</body> max-height: 200px;
</html> width : 306px;
border: 1px solid #000;
Listing 7. Ulepszona metoda zwrotna overflow : auto;
margin-top : -1px;
var suggestCallback = { float : left;
getstring: function(resultSet) { }
var resultDiv = document.getElementById('suggestions'); #string {
resultDiv.innerHTML = ''; width : 300px;
for(var f=0; f<resultSet.length; ++f){ font-size : 13px;
var result=document.createElement("span"); padding-left : 4px;
result.innerHTML = resultSet[f]; }
resultDiv.appendChild(result); #suggestions span {
} display: block;
} }
}

PHP Solutions Nr 1/2006 www.phpsolmag.org 55
Techniki

Warstwa wyników powinna początkowo
Listing 9. Ostateczna wersja klienta podpowiedzi być ukryta, więc w pliku CSS podajemy
dla niej atrybut display: none, który po
<html>
<head> otrzymaniu wyników przełączamy na war-
<title>Podpowiedzi HTML_AJAX</title> tość block w ramach metody zwrotnej:
<link rel="StyleSheet" type="text/css" href="suggest3.css" />
<script type='text/javascript' src='auto_server.php?client=all&stub=suggest'> resultDiv.style.display='block';
</script><script>
if (!resultSet)
var string = '';
var oldstring = ''; resultDiv.style.display='none';
var timeout= 1000; /* czas w ms między sprawdzeniami - dobrą wartością jest 250*/
function do_suggest() { Dodatkowe sprawdzenie zapobiega wy-
string = document.getElementById('string').value; świetleniu warstwy, gdy serwer nie zwróci
if (string != oldstring) {
żadnych podpowiedzi.
/* Przy pustym polu nie wysyłaj żądania... */
if (string) { Pora dodać do wyników nieco interak-
remoteSuggest.getstring(string); cji – na razie tylko widzimy podpowiedzi,
} ale nie możemy wybierać pozycji z listy.
/* ... tylko ukryj warstwę */ Efekt wybierania osiągniemy dodając
else {
obsługę zdarzeń do elementu <span> każ-
document.getElementById('suggestions').style.display = 'none';
} dego wyniku:
oldstring = string;
} result.onmouseover = highlight;
window.setTimeout('do_suggest()', timeout); result.onmouseout = unHighlight;
}
result.onmousedown = selectEntry;
// Stworzenie tablicy asocjacyjnej do składowania metod zwrotnych
var suggestCallback = {
getstring: function(resultSet) { Spowoduje to dodanie funkcji Java-
var resultDiv = document.getElementById('suggestions'); Script do każdego zdefiniowanego
resultDiv.innerHTML = ''; zdarzenia. Działanie funkcji highlight()
resultDiv.style.display = 'block';
i unHighlight() polega po prostu na
if (!resultSet) resultDiv.style.display = 'none';
else{ zmianie klasy CSS elementu <span>:
for(var f=0; f<resultSet.length; ++f){
var result=document.createElement("span"); function highlight (){
result.innerHTML = resultSet[f]; this.className='highlight';
result.onmouseover = highlight;
}
result.onmouseout = unHighlight;
result.onmousedown = selectEntry;
resultDiv.appendChild(result); Klasa CSS highlight wygląda tak:
}
} .highlight {
}
background-color: 0000ff;
}
// Utworzenie obiektu zdalnego color: fff;
var remoteSuggest = new suggest(suggestCallback); }
// Funkcje obsługi interakcji
function highlight () { this.className = 'highlight'; } Minimalna wersja naszej wyszukiwarki
function unHighlight () { this.className = ''; }
powinna obsługiwać zastąpienie zawar-
function selectEntry () { document.getElementById('string').value =this.innerHTML;
} tości pola tekstowego podpowiedzią
</script> klikniętą przez użytkownika. Pole ma
</head> identyfikator string, więc podmiana jego
<body onload="do_suggest()"> treści jest prosta:
<h1>HTML_AJAX Example: Suggest</h1>
<p>Uwaga: czas między sprawdzeniami jest ustawiony na 1000ms dla celów
demonstracyjnych. W praktyce lepiej korzystać z wartości rzędu 350ms.</p> function selectEntry () {
<div id="error"></div> document.getElementById('string')
Podaj nazwę pakietu PEAR: .value = this.innerHTML;
<form method="get" id="suggest"> }
<input type="text" name="string" id="string" size="20" autocomplete="off">
<input type="button" name="check" value="Podpowiedz..." onkeyup="do_suggest();
return false;"> Wartość pola tekstowego jest zastępowa-
<div id="suggestions">&nbsp;</div> na zawartością danego elementu <span>,
</form> czyli jednym z wyników zwróconych przez
</body> serwer AJAX.
</html>
Całość wygląda już znacznie lepiej
(Rysunek 3). Przykład działa poprawnie,

56 www.phpsolmag.org PHP Solutions Nr 1/2006
AJAX Techniki

ale do serwera wysyłanych jest zbyt wie-
le żądań – jeśli użytkownik wpisze coś Komunikat ładowania
Uruchamiając przykłady z HTML_AJAX zauważysz, że przy każdym wywołaniu AJAX po-
bardzo szybko, a korzysta z powolnego
jawia się czerwone okienko z komunikatem loading. Powiadomienie to jest automatycznie
łącza, to może dochodzić do wysyłania wyświetlane przez HTML_AJAX i jest po prostu warstwą o określonym identyfikatorze,
kolejnego żądania podpowiedzi przed tworzoną jeśli wcześniej nie istniała. Jeśli więc chcesz zmienić ten komunikat, wystarczy
otrzymaniem odpowiedzi na żądanie gdzieś w kodzie HTML umieścić na przykład taki fragment:
poprzednie. Spróbujmy jakoś temu za-
<div id="HTML_AJAX_LOADING" style=
pobiec. "background-color : blue; color : white; display : none; position : absolute;
Skorzystajmy z techniki zwanej right : 50px; top : 50px;">
dławieniem zgłoszeń (ang. submission Ładowanie nowego napisu...</div>
throttling). W tym przypadku kliencki
Wyświetlania komunikatu nie da się w obecnej wersji wyłączyć po stronie serwera, ale
JavaScript będzie co jakiś czas (na przy- w przyszłych wersjach HTML_AJAX taka możliwość już będzie. Aby zapobiec wyświetla-
kład co 350 milisekund) sprawdzał, czy niu komunikatu trzeba nadpisać generującą go funkcję JavaScriptu:
wartość pola tekstowego uległa zmianie
HTML_AJAX.onOpen = function(){
– jeśli tak, to zostanie wysłane żądanie
// nic
do serwera (patrz Listing 9). Dodatkowo }
sprawdzimy też, czy pole nie jest przy-
padkiem puste – w takiej sytuacji nie
wysyłamy żądania i ukrywamy warstwę JavaScriptu, co wynika z tego, że JPSpan treści, niż dodawać interakcję korzystającą
wyników. przechwytuje również te błędy i zgłasza je z AJAX-a. Koniecznie trzeba też uwzględ-
Jak widać, dodanie imponującej in- jako ostrzeżenia. niać docelowych odbiorców – jeśli więk-
terakcji do formularzy i aplikacji nie jest Podczas pracy z HTML_AJAX można szość użytkowników z różnych względów
wcale trudne. Nasz prosty przykład moż- dodać własną funkcję obsługi błędu, która ma wyłączoną obsługę JavaScriptu, to
na oczywiście rozbudować, dodając na będzie podmieniać zawartość elementu wprowadzenie AJAX-a raczej nie będzie
przykład możliwość przechodzenia po li- <div> o identyfikatorze error: dobrym pomysłem. Podobną rolę odgrywa
ście wyników klawiszami kursora czy też skala – aplikacja wyposażona w AJAX-a
obsługę lokalnego składowania danych, HTML_AJAX.onError = function(e) { znacznie lepiej sprawdzi się w przypadku
pozwalającego oszczędzić sporo pasma. msg = "\nn"; niewielkich serwisów intranetowych (gdzie
for(var i in e) { łatwo rozwiązać problemy konfiguracyjne
Śledzenie kodu AJAX msg += i + ':' + e[i] +"\n"; i ujednolicić przeglądarki) niż w przypadku
Podczas eksperymentów z AJAX-em } rozbudowanych, publicznie dostępnych
zauważysz zapewne, że technika ta document.getElementById('error'). witryn. Krótko mówiąc, wprowadzenie
wymaga nowego podejścia do śledzenia innerHTML += msg; AJAX-a może dać dobre wyniki pod
kodu. Nie wystarczy już śledzić kodu PHP } warunkiem, że nadrzędnym celem pozo-
– trzeba jeszcze pilnować JavaScriptu stanie uzyskanie jak najlepszych walorów
i obsługiwanej przez AJAX-a komunikacji Pozwoli to przechwytywać wszystkie błę- użytkowych. n
między klientem i serwerem. Na szczęście dy AJAX – najczęściej będą to zwyczajne
nie jest to trudne. błędy PHP, ale mogą się też pojawiać błę-
Przede wszystkim, każdy moduł dy 404, błędy wygaśnięcia i inne.
kodu należy testować osobno. Pracując Na koniec zalecałbym tworzenie apli-
w JavaScripcie dobrze jest stworzyć funk- kacji dla przeglądarki Firefox, a dopiero O autorze
cję pomocniczą, na przykład prościutki potem testowanie ich w Internet Explo- Joshua Eichorn tworzy serwisy PHP
odpowiednik print_r() z PHP: rerze. Firefox ma nieporównanie lep- od siedmiu lat. Jest autorem phpDocu-
sze narzędzia programistyczne od IE, mentor – wielokrotnie nagradzanego
function print_r(input) { a w dodatku oferuje bardzo wiele przydat- i szeroko używanego narzędzia doku-
mentującego kod PHP. Jest też szefem
var ret; nych rozszerzeń. projektu HTML_AJAX, dostarczającego
..for(var i in input) { implementację techniki AJAX dla repozy-
....ret += "["+i+"] = "+input[i]+"\n"; Podsumowanie torium PEAR. Obecnie pracuje jako star-
..} Wiedząc już czym jest AJAX i jak z niego szy architekt oprogramowania w firmie
Uversa Inc., gdzie tworzy dla klientów
..alert(ret); korzystać, możesz rozważyć zastosowa- unikatowe rozwiązania. Technikę AJAX
} nie tej techniki w swoich stronach. Prawi- stosował jeszcze przed jej oficjalnym
dłowe używanie AJAX-a pozwala często opracowaniem i popularyzacją. Mieszka
Możliwości obserwacji w bibliotece osiągnąć imponujące wyniki, ale nie zna- w Phoenix w stanie Arizona (USA).
Kontakt: josh@bluga.net
JPSpan pozwalają też rejestrować między czy to, że należy bezkrytycznie stosować
innymi błędy i udane wywołania funkcji tę technikę we wszystkich witrynach. Za- Werner M. Krauß programuje w PHP od
AJAX. W domyślnej konfiguracji serwera wsze trzeba mieć na uwadze podstawowe 1999 r. Gdy nie gra na gitarze, zajmuje
błędy PHP są przekazywane jako ostrze- przeznaczenie danego serwisu – być mo- się tworzeniem dokumentacji dla frame-
worka Seagull.
żenia JavaScript. Niekiedy można też na- że w konkretnym przypadku lepiej byłoby Kontakt: werner.krauss@hallstatt.net
trafić na ostrzeżenia wynikające z błędów dopracować nawigację lub prezentację

PHP Solutions Nr 1/2006 www.phpsolmag.org 57
Projekty

advAJAX,czyli praktyczne
zastosowanie technologii
AJAX
Łukasz Lach

Ciągłe przeładowywanie strony WWW przy
każdej zmianie jej zawartości i żmudne czekanie
na wyświetlenie kolejnej porcji danych jest
zmorą każdego użytkownika aplikacji webowych
i programisty PHP. Nie jesteśmy jednak
skazani na te bolączki: wybawia nas od nich
wkraczająca do świata PHP technologia AJAX.
Dzięki niej ładujący się w nieskończoność pasek
postępu przechodzi do lamusa.

O
technologii AJAX pisaliśmy już Do stworzenia naszych przykładów
w tym numerze PHP Solutions, wykorzystamy napisany przez autora tego
w artykule AJAX – wyjątkowo artykułu obiekt języka JavaScript o nazwie
interaktywne i wydajne aplikacje WWW. advAJAX (ang. Advanced AJAX, http://
Teraz zajmiemy się jej praktycznym wy- advajax.anakin.us). Obiekt advAJAX uła-
korzystaniem i stworzymy kilka przykła- twia korzystanie z opisywanej technologii
dów obrazujących jej możliwości. Jak już i znacznie rozszerza jej możliwości, doda-
wiemy, główną zaletą AJAX-a jest to, że jąc m.in. pełną obsługę formularzy HTML,
pozwala on na dokonywanie zmian w in- przedawnień czasu połączeń, kontrolę
terfejsie użytkownika po stronie przeglą- nad pamięcią tymczasową przeglądarki
darki, bez potrzeby przeładowywania całej czy kontrolę nad kilkoma wywołaniami
strony i ponownego wczytywania arkuszy AJAX jednocześnie, za pomocą systemu
CSS czy plików graficznych.
Zyskuje na tym atrakcyjność serwisu,
Powinieneś wiedzieć...
gdyż możemy uczynić go dynamicznym, Powinieneś znać języki JavaScript i PHP.
jak i jego wydajność, ponieważ AJAX Pomocna w zrozumieniu technologii adv-
W SIECI umożliwia nam przesyłanie jedynie AJAX będzie również lektura artykułu
niewielkiej porcji danych (zamiast całej AJAX – wyjątkowo interaktywne i wy-
dajne aplikacje WWW.
strony WWW) pomiędzy serwerem,
1. http://advajax.anakin.us/
– projekt advAJAX
a klientem. To z kolei oznacza skrócenie Obiecujemy...
2. http://www.ajaxpatterns.org/ czasu ich dostarczania i zmniejsza ob- Po przeczytaniu artykułu będziesz wie-
– strona o wzorcach AJAX-a ciążenie serwera. Przesyłane za pomocą dział, jak korzystać z obiektu advAJAX
3. http://www.w3schools.com/ i z jego pomocą tworzyć aplikacje inter-
jsref/default.asp – dokumen- AJAX-a dane mogą mieć dowolny format,
netowe wykorzystujące AJAX-a.
tacja języka JavaScript np. CSV albo XML.

58 www.phpsolmag.org PHP Solutions Nr 1/2006
advAJAX Projekty

grupowań. Dzięki niemu, pobranie doku-
mentu z serwera sprowadzi się do wywo-
łania jednej funkcji, a my będziemy mieli
pełną kontrolę nad odbieraniem danych,
obsługą błędów czy dołączeniem funkcjo-
nalności AJAX-a do formularzy. Przejdźmy
więc do pierwszego przykładu.

System
bezpiecznego logowania
Niemal każdy serwis tworzony w PHP
zawiera panel administracyjny, za pomocą
którego zarządzamy jego zawartością. Nie-
odłączną częścią panelu administracyjnego
jest formularz logowania, który umożliwia
dostęp wyłącznie uprawnionym użytkowni-
kom. Potrzebujemy więc przesyłać nazwę
użytkownika i hasło, co pociąga za sobą
ryzyko przechwycenia tych danych przez
osoby niepowołane. Stosowanie połączeń
szyfrowanych SSH zmniejsza to zagroże-
nie. My postąpimy inaczej: wykorzystując
AJAX-a stworzymy system bezpiecznego
logowania, który nie używa SSH.
Jak już wiemy, zastosowanie techno-
logii AJAX uwalnia nas od konieczności Rysunek 1. System bezpiecznego logowania
przeładowywania całej strony. W przypad-
ku systemu logowania, potrzebujemy jedy- mularza. Jej pierwszym parametrem jest to loginForm. Drugim jest nieposiadają-
nie wysłać na serwer kilkadziesiąt bajtów, obiekt formularza, który ma zostać zmo- cy nazwy obiekt definiujący wywołanie
które składają się na nazwę użytkownika dyfikowany – w naszym przypadku jest – zestaw parametrów obiektu advAJAX.
i hasło. Odpowiedź serwera również bę-
dzie krótka: zamiast całego dokumentu
HTML, otrzymamy jeden bajt. Jest to
idealny przykład, pokazujący możliwości
optymalizacyjne AJAX-a.
Podstawą naszej pracy jest formularz
logowania, podobny do przedstawionego
na Listingu 1. W nagłówku widocznego
tam dokumentu HTML dołączamy trzy
skrypty w języku JavaScript. Pierwszy,
o nazwie advajax.js zawiera obiekt adv-
AJAX. W drugim, md5.js, umieszczone
zostały funkcje tworzące hash MD5,
które pozwolą nam na zakodowanie
hasła przed wysłaniem. Ostatni skrypt,
1.js, zawiera funkcję updateObjects(),
uruchamianą po załadowaniu dokumen-
tu. Umieszczony na stronie formularz,
któremu nadaliśmy unikalną nazwę
loginForm, przesyła dane do pliku 1.php.
Z pozoru jest to typowa strona logowania,
jednak po wywołaniu wspomnianej funkcji
updateObjects() wszystko się zmienia.
Funkcja ta wywołuje wewnętrzną
metodę obiektu advAJAX (patrz Listing
2) o nazwie assign(), która powoduje
włączenie funkcjonalności AJAX-a do for- Rysunek 2. Stronicowanie danych

PHP Solutions Nr 1/2006 www.phpsolmag.org 59
Projekty advAJAX

Należy w tym miejscu wspomnieć, że nie Przyjrzyjmy się elementom wspo- hasła algorytmem MD5 oraz informuje
musimy nigdzie podawać adresu URL mnianego już obiektu, będącego dru- użytkownika, że pobierany jest wynik lo-
dokumentu, który ma zostać wywołany, gim parametrem metody assign(). gowania, umieszczając opis proszę cze-
jak również metody jego wysłania (POST Pierwszym z nich jest funkcja on- kać na przycisku służącym do wysyłania
lub GET), ponieważ są one automa- Initialization() – pozwala ona okre- formularza. Również w tej funkcji odczy-
tycznie pobierane z atrybutów action ślić akcję, która ma zostać wykonana tujemy hasło. Jest ono przechowywane
i method znacznika form. Omawiany przed rozpoczęciem głównych procedur przez pole password obiektu parameters
obiekt zadba także o to, żeby zabloko- pobierania dokumentu. Dostęp do obiek- (obj.parameters), z którego korzystamy
wać wszystkie pola formularza aż do za- tu możliwy jest poprzez jedyny parametr podobnie, jak z tablic asocjacyjnych
kończenia zapytania, aby użytkownik nie tej funkcji o nazwie obj. w PHP.
mógł dokonywać żadnych zmian w trak- W naszym skrypcie, funkcja Następnym elementem jest on-
cie wykonywania i pobierania wyniku. onInitialization() tworzy skrót (hasz) Complete(). Powinien on wskazywać
funkcję, która zostanie wywołana po
Listing 1. Formularz logowania zakończeniu pobierania dokumentu.
W tym miejscu bardzo istotne jest to, że
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/ wewnątrz tej funkcji nie wiemy, czy wy-
DTD/xhtml11.dtd"> wołanie zakończyło się powodzeniem.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl" lang="pl">
Funkcja, uruchamiana po udanym
<head> wywołaniu, znajduje się pod elemen-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> tem onSuccess(). Sprawdzamy w niej,
<script type="text/javascript" src="advajax.js"></script> czy logowanie zakończyło się sukce-
<script type="text/javascript" src="md5.js"></script> sem: jeśli tak, to wartością zmiennej
<script type="text/javascript" src="1.js"></script>
obj.responseText powinien być znak 1
</head>
(obiecany jeden bajt). W takim wypad-
<body onload="updateObjects()"> ku, przekierowujemy użytkownika na
<form method="post" action="1.php" id="loginForm"> stronę przeznaczoną dla administrato-
<label for="username">Nazwa użytkownika:</label> ra, a w innym wyświetlamy odpowied-
<input type="text" name="username" id="username" />
ni komunikat i proces się powtarza.
<br />
<label for="password">Hasło:</label> Warto wspomnieć, że ciasteczka są
<input type="password" name="password" id="password" /> obsługiwane przez advAJAX, tak więc
<br /> można bez problemu stworzyć sesję po
<input type="submit" value="OK" id="submitBtn" /> stronie serwera i będzie ona dostępna
</form>
tak samo, jak w sytuacji niekorzystania
</body>
</html> z AJAX-a.
Ostatni element o nazwie onError tak-
Listing 2. Dołączenie funkcjonalności AJAX-a do formularza HTML że wskazuje na funkcję, która ma zostać
wywołana w przypadku problemu z pobra-
function updateObjects() {
niem dokumentu (nie z połączeniem), np.
advAJAX.assign(document.getElementById("loginForm"), {
kiedy plik nie istnieje i serwer zwróci kod
onInitialization : function(obj) { 404 Not Found.
obj.parameters["password"] = hex_md5(obj.parameters["password"]); Jak widzimy, cały proces dołączenia
document.getElementById("submitBtn").value = "Proszę czekać...";
możliwości AJAX-a do naszego formula-
},
rza sprowadził się do wykorzystania jednej
onComplete : function() { metody. W efekcie otrzymaliśmy przyjaź-
document.getElementById("submitBtn").value = "OK"; niejszy dla użytkownika i bardziej opty-
},
malny i – co najważniejsze – bezpieczny
onSuccess : function(obj) { system logowania. Przedstawiamy go na
if (obj.responseText == "1") Rysunku 1.
document.location = "/admin"; else {
alert("Nieprawidłowa nazwa użytkownika lub hasło."); Stronicowanie danych
document.getElementById("password").value = "";
Jeśli w naszym serwisie umieszczamy
window.setTimeout("document.getElementById('password').focus();",
100); większe ilości danych, to koniecznością
} staje się ich wyświetlanie z podziałem
}, na strony zawierające po kilka lub
kilkanaście rekordów. W tradycyjnej
onError : function(obj) {
aplikacji webowej, każdorazowa zmiana
alert("Nie można nawiązać połączenia z serwerem, spróbuj później.");
} strony tej listy pociąga za sobą przełado-
}); wanie całego dokumentu HTML, mimo,
że modyfikacji ulega tylko jego główna

60 www.phpsolmag.org PHP Solutions Nr 1/2006
advAJAX Projekty

część, a nagłówek czy menu nawigacyj-
ne pozostają niezmienione. Posługując
się AJAX-em unikniemy tej uciążliwej
konieczności – pozwala on wszak mody-
fikować zawartość otwartej w przeglądar-
ce strony WWW.
Jako przykład jego wykorzystania,
stworzymy prostą książkę telefoniczną
z podziałem na strony zawierające po 10
rekordów. Będzie ona wyświetlała odczy-
tane z serwera i przesłane w postaci do-
kumentu XML imiona, nazwiska i numery
telefonów. Dla uproszczenia, w naszym
przykładzie nie implementujemy komuni-
kacji z serwerem.
Zacznijmy od tabeli, w której będzie-
my umieszczać rekordy pobrane z ser-
wera. Jej kod źródłowy przedstawiamy
na Listingu 3. W nagłówku znajdują się
nazwy kolumn, otoczone hiperłączami
wskazującymi na funkcję changeSort(),
która zmienia sposób sortowania da-
nych. Na początku, tabela jest pusta:
zostanie wypełniona zawartością pobra-
ną poprzez wywołanie AJAX. W stopce
umieściliśmy pole, które będzie zawie-
rało dane o aktualnie przeglądanych
danych oraz przyciski nawigacyjne.
Znacznik tbody otrzymał ponadto uni- Rysunek 3. Prosty edytor tekstu na stronie WWW
kalny identyfikator dataTable, do któ-
rego będziemy się odwoływać podczas Listing 3. Tabela książki telefonicznej
wpisywania danych.
Spójrzmy teraz na kod źródłowy <table>
przedstawiony na Listingu 4. Metodą <thead>
<tr>
wywoływaną podczas zakończenia
<td>ID</td>
ładowania strony oraz przy zmianie <td><a id="nameSort" href="javascript:changeSort('name')" title="Sortowanie
strony jest getRecords. Jak w poprzed- po imieniu">Imię</a></td>
nim przykładzie, tak i teraz wywołuje on <td><a id="surnameSort" href="javascript:changeSort('surname')"
tylko jedną, wewnętrzną metodę obiektu title="Sortowanie po nazwisku">Nazwisko</a></td>
<td><a id="telephoneSort" href="javascript:changeSort('telephone')"
advAJAX. Tym razem jest to metoda
title="Sortowanie po numerze telefonu">Numer telefonu</a></td>
advAJAX.get(), która wykonuje wywoła- </tr>
nie typu GET na ustalony w parametrach </thead>
adres URL. advAJAX pozwala korzystać <tbody id="dataTable"></tbody>
zarówno z POST, jak i GET, którym <tfoot>
<tr>
odpowiadają nazwy metod obiektu adv-
<td colspan="4">
AJAX. <span id="dataStats" style="float: left; margin-top: 2px"></span>
Opisywana metoda przyjmuje jeden <span style="float: right">
parametr, którym jest lista parametrów <a id="btnFirst" href="javascript:changePage(-currentPage)"
obiektu. Element url jest wymagany, title="Pierwsza strona">&laquo;&laquo;</a>
<a id="btnPrev" href="javascript:changePage(-1)" title="Poprzednia
a jego wartością może być zarówno
strona">&laquo;</a>
pełny adres URL, jak i nazwa pliku. W na- <a id="btnNext" href="javascript:changePage(1)" title="Następna strona
szym skrypcie wywołujemy skrypt 2.php ">&raquo;</a>
z parametrami p (któremu przypisana jest <a id="btnLast" href="javascript:changePage(maxPage-currentPage-1)"
wartość zmiennej globalnej currentPage, title="Ostatnia strona">&raquo;&raquo;</a>
</span>
czyli numer pobieranej strony), oraz s,
</td>
wskazująca na kolumnę i tryb sorto- </tr>
wania pobieranych rekordów. Następ- </tfoot>
nym elementem jest znany nam już </table>
onInitialization(), którego wywołanie

PHP Solutions Nr 1/2006 www.phpsolmag.org 61
Projekty advAJAX

powoduje ukrycie przycisków nawigacyj- będącym instancją obiektu XMLDocument, Całość jest już w pełni funkcjonalna,
nych i wyświetlenie informacji, że trwa której kod również znajduje się na Li- jednak nic nie szkodzi na przeszkodzie,
pobieranie żądanych danych. Kiedy stingu 4. Aby dostać się do danych za- aby przedstawiony przykład rozbudować.
nastąpi wywołanie funkcji zapisanej wartych w pobranym dokumencie XML, Można dodać jeszcze jedną kolumnę,
pod onSuccess() powoduje uruchomienie korzystamy z wbudowanych metod DOM w której umieścimy hiperłącza pozwala-
metody parseRecords(), z parametrem (ang. Document Object Model). jące na usunięcie czy edycję wybranego
rekordu. Możliwości jest wiele, gdyż po-
Listing 4. Główny skrypt dynamicznej tabeli tencjał AJAX-a jest ogromny. Ostateczny
wynik zależy zaś wyłącznie od naszych
var maxPage; potrzeb.

function $(id) {
return document.getElementById(id);
Szybka edycja danych
} Ostatnim, równie przydatnym i efek-
tywnym przykładem będzie stworzenie
// Funkcja konwertująca dokument XML na kolejne wiersze tabeli edytora pozwalającego na zmianę tre-
function parseRecords(xml) { ści dokumentu bez przeładowywania
// Zapisanie danych bieżącej strony
strony, na której jest on wyświetlany.
with (xml.getElementsByTagName("records").item(0)) {
page = getAttribute("page")*1; Jego interfejs oprzemy na warstwach.
maxPerPage = getAttribute("max_per_page"); Edytor będzie uruchamiany po dwu-
startId = maxPerPage*page+1; krotnym kliknięciu na wybraną warstwę.
total = getAttribute("total")*1; Wówczas utworzona zostanie warstwa
maxPage = Math.ceil(total/maxPerPage);
zawierająca obiekt pola tekstowego
// ...wpisanie danych do stopki tabeli
} (textarea), do którego wpisana zo-
stanie treść edytowanego tekstu oraz
// Wpisanie danych rekordów do tabeli dwa przyciski, służące do zapisywania
d = $("dataTable"); i anulowania wprowadzanych zmian.
for (i = d.rows.length-1; i >= 0; i--)
Oczywiście edytor tego typu powinien
d.deleteRow(i);
record = xml.getElementsByTagName("record");
być dostępny jedynie, gdy zaloguje-
result = ""; my się jako administrator, dlatego też
for (i = 0; i < record.length; i++) { postaramy się maksymalnie ułatwić
tr = document.createElement("tr"); dołączenie jego kodu do istniejącej już
td = document.createElement("td");
strony. W tym celu stworzymy funkcję
td.innerHTML = startId + i;
tr.appendChild(td);
o nazwie assignEditor(), której para-
for (j = 0; j < 3; j++) { metrem jest unikalna nazwa identyfiku-
td = document.createElement("td"); jąca warstwę, wpisana w atrybucie id
td.innerHTML = record[i].childNodes[j].childNodes[0].nodeValue; dowolnego znacznika HTML. Pełny kod
tr.appendChild(td);
omawianej funkcji, jak i całego skryptu
}
d.appendChild(tr);
edytora znajduje się na Listingu 5.
} Istotną funkcją, którą stworzymy
return result; jest wywoływana w celu zapisania
} zmodyfikowanych danych na serwerze
saveData(). Tym razem korzystamy
// Główna funkcja wywołująca skrypt PHP i pobierająca dokument XML
function getRecords() {
z metody advAJAX.post(), ponieważ
advAJAX.get({ przesyłane dane mogą mieć większy
url : "2.php?p="+currentPage+"&s="+currentSort+"%20"+currentSortOrder, rozmiar, niż pozwala na to metoda GET.
onInitialization : function() { Również ta metoda przyjmuje jeden ar-
$("dataStats").innerHTML = 'Pobieranie danych...';
gument – parametry wywołania obiek-
$("btnPrev").style.visibility = $("btnNext").style.visibility =
$("btnFirst").style.visibility = $("btnLast").style.visibility =
tu advAJAX. Pierwszym elementem
"hidden"; jest url, wskazujący na skrypt 3.php,
}, który zapisze wyedytowane dane po
onSuccess : function(obj) { stronie serwera. Następnie mamy ele-
parseRecords(obj.responseXML);
ment parameters, omawiany już przy
}
},
skrypcie bezpiecznego logowania. Tym
razem przekazujemy unikalny identy-
onError : function(obj) { fikator edytowanego tekstu oraz jego
alert("Nie można nawiązać połączenia z serwerem, spróbuj później."); nową treść. Dostęp do tych zmiennych
});
uzyskamy poprzez tablicę $_POST
}
($_POST['id'] oraz $_POST['value'])
w skrypcie PHP. Funkcja zapisana

62 www.phpsolmag.org PHP Solutions Nr 1/2006
advAJAX Projekty

pod onInitialization() spowoduje
Listing 5. Kod źródłowy edytora wyłączenie kontrolek edytora, aby użyt-
kownik nie mógł modyfikować danych
function assignEditor(objectId) {
// Stworzenie edytora w przypadku podwójnego kliknięcia podczas ich przesyłania na serwer.
document.getElementById(objectId).ondblclick = function() { runEditor(this) } W przypadku błędu, obiekt advAJAX
} korzysta z funkcji umieszczonej pod na-
function saveData(objectId) { zwą onError(), która w tym przypadku
advAJAX.post({
wyświetla komunikat o błędzie i odblo-
url : "3.php",
parameters : { kowuje kontrolki, zezwalając jednocze-
id : objectId, śnie użytkownikowi na podjęcie kolejnej
value : $(objectId + "_editor_ta").value próby zapisania danych. W przypadku
}, powodzenia, wywołana zostaje funkcja
onInitialization : function() {
zapisana pod onSuccess(), a skrypt
$(objectId+"_editor_ta").disabled=$(objectId+"_editor_save").disabled=
$(objectId + "_editor_cancel").disabled = "disabled"; PHP powinien zwrócić znak 1 (jak wi-
}, dzimy, znowu jeden bajt zamiast całego
onError : function() { dokumentu HTML po przeładowaniu!).
alert("Nie można nawiązać połączenia z serwerem, spróbuj później."); Następnie edytor zostaje usunięty i od
$(objectId + "_editor_ta").removeAttribute("disabled");
tej pory każdy użytkownik odwiedzający
$(objectId + "_editor_save").removeAttribute("disabled");
$(objectId + "_editor_cancel").removeAttribute("disabled"); tę stronę zobaczy już nową, zapisaną
}, przed chwilą, treść warstwy. Nic nie
onSuccess : function(obj) { stoi na przeszkodzie, aby wykorzystać
if (obj.responseText == "1") pole typu input, jeśli mamy do czynie-
$(objectId).innerHTML = $(objectId + "_editor_ta").value;
nia z małą, zawierającą się w jednej
removeElement(objectId + "_editor");
} linijce porcją danych. Możemy również
}); stworzyć edytor graficzny, pozwalający
} dodatkowo na zmianę stylu czcionki
czy dodawanie hyperlinków i plików
function runEditor(obj) {
graficznych – wszystko znowu zależy
// Pobranie współrzędnych położenia edytowanej warstwy
l = t = 0; od potrzeb, a przedstawiony skrypt jest
obj2 = obj; idealną podstawą do dalszej rozbudo-
while (obj2.parentNode) { wy.
l += obj2.offsetLeft;
t += obj2.offsetTop;
obj2 = obj2.parentNode;
Podsumowanie
} AJAX nie jest nową technologią, ale dopie-
ro teraz został w pełni zaimplementowany
// Stworzenie warstwy edytora i jego komponentów w najpopularniejszych przeglądarkach
editor = document.createElement("div"); internetowych, dzięki czemu krąg jego
h = obj.offsetHeight >= 70 ? obj.offsetHeight : 70;
użytkowników rośnie.
with (editor.style) {
position = "absolute"; Zastosowanie obiektu advAJAX po-
zIndex = "100"; zwala natomiast na proste i efektywne
top = t + "px"; wykorzystanie jego możliwości, co sta-
left = l + "px"; nowi klucz do tworzenia prawdziwie inte-
width = obj.offsetWidth + "px";
raktywnych aplikacji internetowych. n
height = h + "px";
backgroundColor = "#ffffff";
}
editor.id = obj.id + "_editor";
editor.innerHTML = "<textarea id='" + obj.id + "_editor_ta'></textarea>
<br /><button id='" + obj.id + "_editor_save' onclick=\"saveData('" +
obj.id + "')\">Zapisz</button>&nbsp;<button id='" + obj.id +
"_editor_cancel' onclick=\"removeElement('" + obj.id + "_editor')">
Anuluj</button>"; O autorze
document.body.appendChild(editor);
Łukasz Lach jest studentem informa-
ta = $(obj.id + "_editor_ta");
tyki. Od kilku lat zajmuje się progra-
with (ta.style) { mowaniem serwisów internetowych
width = "100%"; z wykorzystaniem technologii takich,
height = (h - 25) + "px"; jak XHTML, PHP czy JavaScript. Jest
} autorem wielu publikacji dotyczących
ta.value = obj.innerHTML; szeroko pojętego programowania stron
} internetowych. Obecnie pracuje nad
projektem advAJAX.
Kontakt: anakin@php5.pl

PHP Solutions Nr 1/2006 www.phpsolmag.org 63
Projekty

Nowe możliwości PHP-GTK2
Pablo Dall'Oglio

Rozszerzenie PHP-GTK1 zapoczątkowało nowy
sposób myślenia o PHP, otwierając przed
językiem przeznaczonym do tworzenia aplikacji
sieciowych świat wyposażonych w graficzny
interfejs użytkownika (GUI) programów
klienckich. Jednakże dopiero pojawienie się
PHP-GTK2 może dać początek prawdziwej
rewolucji...

P
HP-GTK2 umożliwia połączenie Tak więc, w pierwszym menu bę-
funkcjonalności PHP5 i bibliote- dziemy mogli dodawać nowe produkty
ki Gtk-2.6. W artykule przyjrzy- (Dodaj), edytować już istniejące (Edytuj),
my się bliżej niezwykłym możliwościom wyczyścić całą bazę (Wyczyść), bądź
tej technologii na przykładzie aplikacji zakończyć działanie programu (Wyjdź).
umożliwiającej proste zarządzanie Korzystając z drugiego otworzymy prosty
przedmiotami (produktami) w sklepie. edytor tekstu, potencjalnie przydatny np.
Będziemy korzystać zarówno z nowo- do robienia notatek na temat naszych
ści, które pojawiły się w PHP-GTK2, zapasów. Trzecie natomiast będzie zawie-
jak i tych, które oferuje PHP5 (np. rało jedną opcję: Pomoc, wyświetlającą
nowy model obiektowy czy obsługa okno dialogowe O programie.
wyjątków). Dane będziemy składować Będziemy również potrzebowali okien
w bazie SQL. dialogowych m.in. dla opcji Wyczyść

Idea Powinieneś wiedzieć...
W SIECI Zastanówmy się, czego potrzebujemy, Powinieneś znać PHP5, a w szczególno-
aby stworzyć nasz przykładowy program. ści nowy model obiektowy oraz wyjątki.
Podstawowym elementem aplikacji GUI Przydatna będzie również ogólna wiedza
o graficznych interfejsach użytkownika.
1. http://gtk.php.net – strona jest okno, w którym znajdują się kontrolki
domowa projektu PHP-GTK
2. http://www.agata.org.br
(widgety). W nim umieścimy trzy rozwijane Obiecujemy...
– Agata Project – CRM/ERP menu: Plik, Edycja oraz Pomoc. Poszcze- Po przeczytaniu artykułu będziesz wie-
korzystający z PHP-GTK gólne pozycje w menu będą opisane za dział, jak napisać przykładową aplikację
3. http:///tulip.solis.coop.br –Tu- wykorzystującą nowe możliwości PHP-
lip – edytor dla programisty pomocą tekstu oraz towarzyszących mu
GTK2.
PHP oparty na PHP-GTK odpowiednich ikon.

64 www.phpsolmag.org PHP Solutions Nr 1/2006
PHP-GTK2 Projekty

(pytanie oraz informacja), Dodaj i Edytuj
(formularz umożliwiający dodawanie
nowych i edycję istniejących produktów),
a także dialogu plików i niezależnego
okna dla naszego edytora tekstu.

Zabieramy się do pracy
Nasze zadanie zaczniemy od napisania
głównej klasy Application, którą przed-
stawiamy na Listingach 1 i 2. Najpierw
stworzy ona obiekt klasy GtkWindow, który
będzie głównym oknem aplikacji i ustawi
jego podstawowe parametry (rozmiar,
położenie i tytuł). Następnie stworzymy
kontener porządkujący pozycjonowanie
naszych widgetów: wykorzystamy w tym
celu klasę GtkVBox, tworząc jej instancję
o nazwie $vbox. Rysunek 1. Menu naszej aplikacji
Przejdźmy do menu rozwijanych.
Zaczniemy od utworzenia menu głów- Druga, Dodaj, pokaże użytkownikowi głównego menu. Czynność ta przebiegnie
nego, zakładając obiekt $MenuBar klasy dialog zawierający formularz pozwalający dwuetapowo: najpierw dla każdej pozy-
GtkMenuBar. Następnie za pomocą dodać nowy produkt, podczas gdy trzecia, cji w pasku menu zastosujemy metodę
klasy GtkMenuItem stworzymy poszczegól- Edytuj, wyświetli listę produktów, pozwala- set_submenu(), która połączy tę pozycję
ne sekcje menu o nazwach Plik, Edycja jąc na edycję wybranej pozycji. Wreszcie, z odpowiadającym jej podmenu, a następ-
oraz Pomoc. Będą to osobne obiekty, kliknięcie na pozycji Wyjdź spowoduje nie dodamy te trzy pozycje do głównego
których póki co nie dodamy jeszcze do zakończenie działania aplikacji. paska ($MenuBar).
menu. W menu Edycja umieścimy tylko jedną Pozostałymi operacjami w konstruk-
opcję, Edytor – będzie ona uruchamiała torze są: wstawienie kontenera $vbox
Menu główne wspomniany już edytor tekstu. W menu w okno, dodanie do niego widgetów oraz
Kolejnym krokiem będzie stworzenie Pomoc znajdzie się również jedna opcja, wyświetlenie całej zawartości okna za po-
odpowiednich menu, otwieranych klik- Pomoc, której zadaniem będzie wyświe- mocą metody $window->show_all().
nięciem na wymienione sekcje. W tym tlenie okna dialogowego O programie.
celu użyjemy klasy GtkMenu, tworząc jej Wszystkie menu przedstawiamy na Ry- Metody callbackowe
instancję o nazwie $SubMenuFile... ale sunku 1. i dialogi wiadomości
zaraz, chcieliśmy, by opcje w tych menu Kolejnym punktem programu jest Najwyższy czas rozpocząć tworzenie
posiadały ikony! Na szczęście nie musimy połączenie sygnałów wysyłanych przez metod callbackowych dla poszczegól-
tworzyć ich sami, gdyż PHP-GTK2 oferuje elementy menu w chwili wystąpienia zda- nych elementów menu. Zaczniemy od
nam Obrazy Standardowe (Stock Ima- rzenia clicked z metodami callbackowymi onClear(). W pierwszej kolejności meto-
ges), czyli zbiór podstawowych, często wykonującymi odpowiednie operacje. da ta sprawdzi, czy baza, którą chcemy
używanych ikon dla przycisków, menu, list Metody te nosić będą nazwy: onClear(), wyczyścić, istnieje. Potem poprosi nas
itd. Przedstawiają one operacje takie, jak onAdd(), onList(), onEdit() i onHelp(). o potwierdzenie skasowania wszystkich
zapis, otwieranie, zamykanie, kasowanie, Powiązania tworzymy natomiast wy- zawartych w bazie rekordów. W tym celu
dodawanie, czyszczenie, wychodzenie wołując dla każdego elementu metodę użyjemy okna dialogowego GtkMessage-
z programu, opcje TAK i NIE, itd. Nazwy connect(). Dodamy teraz elementy do Dialog. Znajduje ono zastosowanie
ikon przestrzegają przejrzystej konwencji, odpowiednich menu. Ostatnią operacją,
co znacząco ułatwia korzystanie z nich. jaką musimy przeprowadzić na menu
Każda pozycja należąca do GtkMenu rozwijanych jest dodanie ich do paska
będzie obiektem klasy GtkImageMenuItem.
Ikonę będziemy przekazywać przez argu-
ment konstruktora tej klasy. Jeżeli nie po-
damy nazwy ikony, to pozycja nie będzie
posiadała obrazka.
W menu Plik dodajemy pozycje
o nazwach: Wyczyść, Dodaj, Edytuj oraz
Wyjdź. Kliknięcie na pierwszej spowoduje Rysunek 2. Dialog pozwalający nam
uruchomienie bazy, a następnie usunięcie zdecydować, czy chcemy zrestartować Rysunek 3. Wiadomość, że baza została
tabeli produktów i ponowne jej stworzenie. bazę danych wyczyszczona

PHP Solutions Nr 1/2006 www.phpsolmag.org 65
Projekty PHP-GTK2

w bardzo wielu sytuacjach, m.in. przy
Listing 1. Główny interfejs: główne okno naszej aplikacji; kod zawarty w pliku wyświetlaniu ostrzeżeń, komunikatów
product.php
o błędach, dialogów potwierdzenia, okien
<?php do wprowadzania danych, itd. Użyjemy
// Klasa Application – zawiera w sobie główny interfejs tego okna w trybie MODAL, co oznacza,
class Application{ że dopóki nie zostanie ono zamknięte,
private $window; będzie cały czas widoczne na ekranie,
function __construct(){
a wykonywanie innych operacji w ramach
// tworzy nowe okno i ustawia jego parametry
$this->window = new GtkWindow; aplikacji nie będzie możliwe. Chcemy, aby
... w obrębie dialogu widniał znak zapytania
$vbox = new GtkVBox; – żaden problem, użyjemy tych samych
// tworzy listwę menu standardowych ikon, co wcześniej. Za-
$MenuBar = new GtkMenuBar;
stosujemy również standardowe przyciski
// opcje menu
$MenuFile = new GtkMenuItem('_Plik'); YES i NO. Całość przedstawiamy na
... Rysunku 2.
// podmenu Plik, z elementami standardowymi Po wyświetleniu okna, aplikacja
$SubMenuFile = new GtkMenu; będzie czekać na reakcję użytkownika.
$ItemFile1= new GtkImageMenuItem(GTK::STOCK_CLEAR);
Jeżeli zezwoli on na skasowanie danych,
...
$ItemFile4= new GtkMenuItem; nawiążemy połączenie z naszą bazą
$ItemFile5= new GtkImageMenuItem(GTK::STOCK_QUIT); SQLite, a następnie usuniemy tabelę
// połącz opcje menu z metodami produktów i ponownie ją utworzymy. O po-
$ItemFile1->connect('activate', array($this, 'onClear')); prawnym wyczyszczeniu bazy informuje
...
użytkownika kolejne okno dialogowe, któ-
$ItemFile5->connect('activate', array($this, 'onQuit'));
// dodaj obiekty do podmenu re pokazujemy na Rysunku 3.
$SubMenuFile->append($ItemFile1);
... Dodawanie nowych produktów:
// podmenu Edycja formularz produktów
$SubMenuEdit= new GtkMenu;
Kolejną metodą odwoławczą jest onAdd(),
$ItemEdit1= new GtkImageMenuItem(GTK::STOCK_EDIT);
$ItemEdit1->connect('activate', array($this, 'onEdit')); która pozwala na dodanie nowego pro-
$SubMenuEdit->append($ItemEdit1); duktu. Wyświetla ona na ekranie dialog
// podmenu Pomoc widoczny na Rysunku 4. Właściwie jedy-
... nym zadaniem tej metody jest wykorzy-
$MenuFile->set_submenu($SubMenuFile);
stanie klasy ProductNew, która znajduje się
...
$MenuEdit->set_submenu($SubMenuEdit); w pliku ProductNew.class.php (patrz Li-
... sting 3). Pełne opisanie tej klasy zajęłoby
$this->window->add($vbox); zbyt wiele miejsca i mogłoby być nużące
$vbox->pack_start($MenuBar, false, false); dla czytelnika. Skoncentrujemy się więc
$this->window->show_all();
jedynie na najważniejszych zagadnieniach
}
// Metoda onClear – tworzy struktury w bazie danych z nią związanych.
function onClear(){ Po pierwsze, w konstruktorze
if (file_exists('data.db')){ ProductNew tworzymy taki sam obiekt kla-
$dialog = new GtkMessageDialog(null, Gtk::DIALOG_MODAL, sy GtkWindow, z jakim mieliśmy do czynie-
Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO,
nia wcześniej. Po drugie, do wyświetlania
'Czy chcesz dokonać ponownej inicjalizacji bazy ?');
$response = $dialog->run(); opisów wykorzystamy etykiety (GtkLabel).
$dialog->destroy(); Najbardziej interesującym aspektem ich
if ($response == Gtk::RESPONSE_YES){ użycia jest możliwość formatowania ich
// usuń tabelę products zawartości. W PHP-GTK2 jest to dużo ła-
$conn = sqlite_open('data.db');
twiejsze i bardziej elastyczne niż w PHP-
...
} GTK1, gdzie trzeba było wiele wysiłku,
else if ($response == Gtk::RESPONSE_NO){ by nadać tekstowi pożądany przez nas
return; wygląd (nawet w przypadku tak prostych
} efektów, jak pogrubienie, kursywa, pod-
}
kreślenie bądź kolory). PHP-GTK2 wyko-
// stwórz tabelę products
$conn = sqlite_open('data.db'); rzystuje Pango, opensourcowy framework
$sql = 'CREATE TABLE products (code,description,' . GTK zajmujący się wszystkimi szczegó-
'unit,amount,cost,price)'; łami związanymi z układem i rendero-
sqlite_query($conn, $sql); waniem. Pango umożliwia formatowanie
tekstu do wyświetlenia za pomocą wywo-
dzącego się z SGML języka znaczników.

66 www.phpsolmag.org PHP Solutions Nr 1/2006
PHP-GTK2 Projekty

Pozwala też na łatwe definiowanie stylów
i kolorów (patrz Rysunek 5). Chociaż nie
widać tego w przykładzie, równie łatwo
możemy wyświetlać tekst w pionie lub
pod kątem (wyrażonym w stopniach).
Dialog dodawania nowego produktu
będzie zawierał następujące etykiety:
Kod, Opis, Ilość, Jednostka, Koszt i Ce-
na. Pod nimi z kolei umieścimy etykietę
informującą o konieczności wypełnienia
pól Kod, Opis i Cena. Chcielibyśmy
jednak, by nie była ona widoczna przez
cały czas, gdyż nie będzie potrzebna
bardziej doświadczonym użytkownikom.
To ostatnie nie stanowi żadnego proble-
mu dla PHP-GTK2: możemy skorzystać
z komponentu GtkExpander, będącego
kontenerem pozwalającym na rozwijanie
i zwijanie kontrolek, które się w nim znaj-
Rysunek 4. Formularz do dodawania nowych produktów
dują. Umieścimy w nim naszą etykietę
pomocy. W tym celu zadeklarujemy obiekt
$expander klasy GtkExpander, a następnie
Listing 2. Główny interfejs: ciąg dalszy kodu głównego okna naszej aplikacji
stworzymy etykietę pomocy o nazwie
// poinformuj użytkownika o sukcesie $help. Na koniec, dodamy tę etykietę do
$dialog = new GtkMessageDialog(null, Gtk::DIALOG_MODAL, Gtk::MESSAGE_INFO, $expander. Domyślnie będzie ona roz-
Gtk::BUTTONS_OK, 'Baza wyczyszczona');
winięta, co zapewnia nam metoda set_
$response = $dialog->run();
$dialog->destroy(); } expanded(true).
Kolejnym wykorzystywanym przez nas
// Metoda onAdd – tworzy formularz produktów widgetem będzie pole wprowadzania tek-
function onAdd(){
stu (GtkEntry). Umieścimy je przy każdej
include_once 'ProductNew.class.php'; etykiecie oprócz $help. W celu ułatwienia
new ProductNew; } wprowadzania tekstu, PHP-GTK2 oferuje
bardzo przydatny mechanizm autouzupeł-
// Metoda onList– wyświetla listę produktów, zezwalając użytkownikowi
niania. Pozwala on na połączenie widgetu
// na jej edycję
GtkEntry z GtkListStore – modelem da-
function onList(){
nych zawierającym listę wartości używa-
include_once 'ProductList.class.php'; nych przy autouzupełnianiu. Połączenia
$obj = new ProductList; instancji obu klas dokonujemy przy użyciu
$obj->Show();
obiektu trzeciej klasy, GtkEntryCompletion.
$obj->showData(); }
Pojawiające się podpowiedzi będą się
// Metoda onEdit – Otwiera mały edytor tekstu zmieniać w miarę wprowadzania przez
function onEdit(){ użytkownika kolejnych znaków.
Autouzupełnianie jest rozwiązaniem
include_once 'TextEditor.class.php';
new TextEditor; } powszechnie używanym w aplikacjach
Gtk, szczególnie w dialogach wyboru
// Metoda onHelp – Wyświetla dialog O programie pliku, gdzie program podpowiada nazwy
function onHelp(){
pasujące do wprowadzonego wzorca
include_once 'AboutDialog.class.php'; (patrz Rysunek 6). W naszym dialogu
new AboutDialog('Produkty', 'Jest to program open-source'); } zastosujemy GtkEntryCompletion w polu
Jednostka, gdzie podpowiedzią będzie
// Metoda onQuit – wyjście z aplikacji
lista często stosowanych jednostek.
function onQuit(){
Gtk::main_quit(); }
}

// tworzy nową instancję Application
new Application;
Gtk::Main();
?>
Rysunek 5. Efekty wyświetlania tekstu
w PHP-GTK2

PHP Solutions Nr 1/2006 www.phpsolmag.org 67
Projekty PHP-GTK2

W tym celu, obok obiektu $this->
entries[3] Listing 3. Kod formularza produktów, przechowywany w pliku
ProductNew.class.php
klasy GtkEntry stworzymy instancję
klasy GtkListStore o nazwie $store. <?php
Następnie używając metody append() // Klasa ProductNew – formularz produktu do ich wstawiania
obiektu $store dodamy do niego po- class ProductNew extends GtkWindow{
wszechnie stosowane jednostki. Potem ...
// Konstruktor klasy – tworzy okno i całą jego zawartość
utworzymy obiekt $completion klasy
public function __construct(){
GtkEntryCompletion, po czym wskażemy ...
mu obiekt $store jako źródło danych dla // tworzy wszystkie etykiety i pola tekstowe, linia po linii
autouzupełniania, a na koniec użyjemy $this->labels[0] = new GtkLabel('<span foreground="red"><b>Kod
metody set_completion() pola Jednostka, </b></span>');
$this->entries[0] = new GtkEntry;
aby połączyć z nim cały mechanizm.
$this->entries[0]->set_size_request(80,-1);
Ostatnią potrzebną nam rzeczą ...
jest przycisk Zapisz, po naciśnięciu // Tworzy model danych
którego umieścimy nowo wprowadzony $store = new GtkListStore(Gtk::TYPE_STRING);
produkt w bazie danych. Utworzymy // dodaj wartości do modelu danych
$store->append(array('UN'));
więc instancję klasy GtkButton, którą
...
nazwiemy $button1. Na przycisku tym // Tworzy EntryCompletion
umieścimy standardową ikonę dyskietki, $completion = new GtkEntryCompletion();
a następnie połączymy jego zdarzenie $completion->set_model($store);
clicked z metodą callbackową o nazwie $completion->set_text_column(0);
$this->entries[3]->set_completion($completion);
onSaveClick(). Metoda ta pobierze wpro-
...
wadzone wartości z formularza, zapisze // upakuj wszystkie etykiety i pola tekstowe w pionowym boksie
je do bazy danych i, korzystając ze zde- ...
finiowanej przez nas metody Clear(), // stwórz przycisk Zapisz i boks dla niego
wyczyści formularz. $save_box= new GtkHBox();
$button1 = GtkButton::new_from_stock(Gtk::STOCK_SAVE);
$button1->connect('clicked', array($this, 'onSaveClick'));
Pokaż mi produkty, czyli $save_box->pack_start(new GtkHBox, true);
dlaczego lubimy drzewa ...
Kolejną metodą callbackową w klasie $expander = new GtkExpander('<b><i>Help</i></b>');
Application jest onList(), wywoływana $expander->set_use_markup(true);

po kliknięciu pozycji Edytuj w menu Plik. // Krótki tekst pomocy do umieszczenia w ekspanderze
Korzysta ona z klasy ProductList prze- $help = new GtkLabel;
chowywanej w pliku ProductList.class.php $help->set_alignment(0.2, 0.5);
(patrz Listingi 4 i 5). Nie musimy jej $help->set_markup('<b><u>Wypełnianie formularza</u></b>
Aby być w stanie wypełnić formularz, nie możesz pozostawić pól Kod,
dokładnie opisywać, więc podobnie jak
Opis i Cena pustymi...');
w przypadku poprzedniej metody, skon- $expander->add($help);
centrujemy się na jej najważniejszych $expander->set_expanded(true);
komponentach. $vbox->pack_start($expander, false, false);
Głównym zadaniem tej klasy jest wy- parent::add($vbox);
parent::show_all();
świetlenie edytowalnej listy wszystkich po-
}
siadanych przez nas produktów. W przy- // Metoda onSaveClick – Zapisuje dane z ekranu do bazy
padku edycji, dane mają być aktualizo- public function onSaveClick(){
wane bezpośrednio w bazie. Za wyświe- // odczyt danych z pól tekstowych i zapisywanie ich do bazy
tlanie listy będzie odpowiadał wprowa- $conn = sqlite_open('data.db');
$product->code = $this->entries[0]->get_text();
dzony w PHP-GTK2 widget GtkTreeView.
$sql = "INSERT INTO products (code, description, amount,
...
...
}
// Metoda Clear – czyści wszystkie pola formularza.
private function Clear(){
for ($n=0; $n<=5; $n++){
$this->entries[$n]->set_text('');
}
// ustawia kursor w pierwszym polu
parent::set_focus($this->entries[0]);
}

Rysunek 6. Autouzupełnianie

68 www.phpsolmag.org PHP Solutions Nr 1/2006
PHP-GTK2 Projekty

Stanowi on olbrzymi krok naprzód wzglę-
dem dostępnych już wcześniej, pro-
stych komponentów w rodzaju GtkCTree
czy GtkCList. O ile te poprzednie wid-
gety pozwalały wyświetlać tylko drze-
wa (pierwszy z nich; patrz Rysunek 7)
lub tylko listy (drugi; patrz Rysunek 8),
GtkTreeView umożliwia wyświetlanie obu
struktur, a w dodatku znacząco rozsze-
rza ich funkcjonalność. Przykładowo, po-
zycje listy lub drzewa mogą teraz zawie-
rać checkboksy oraz obrazki. Najciekaw-
sze dla nas jest to, że zarówno drzewa,
jak i listy mogą mieć kolumny, a zawarte
Rysunek 7. GtkTreeView w trybie drzewa w nich dane możemy edytować podobnie,
jak w arkuszu kalkulacyjnym.
W naszym przykładzie wykorzy-
stamy klasę GtkTreeView w trybie edy-
towalnej listy z kolumnami. Najpierw
stworzymy jej instancję, $this->list,
a później dostarczymy jej dane. Po-
winniśmy wiedzieć, że GtkTreeView
zapewnia całkowitą separację warstw:
Modelu, Widoku i Kontrolera, według
wzorca MVC (Model-View-Controller).
Oznacza to, że model danych jest całko-
wicie odseparowany od jego reprezen-
tacji na ekranie. Dane przechowywane
są w Modelu, którym może być obiekt
klasy GtkListStore (w przypadku list)
lub GtkTreeStore (w przypadku drzew).
My użyjemy GtkListStore, deklarując
jej instancję o nazwie $this->model. Na-
Rysunek 8. GtkTreeView w trybie listy
stępnie stworzymy kolumny dla Widoku,
w których będziemy wyświetlali dane.
Listing 4. Kod ProductList, przechowywanej w pliku ProductList.class.php
Wykorzystamy do tego osobną klasę
<?php o nazwie GtkTreeViewColumn.
// Klasa ProductList – lista produktów Dla każdej z kolumn określimy
class ProductList extends GtkWindow tytuł oraz stworzymy i dodamy (za
{
pomocą metody connect()) renderer
private $window;
odpowiedzialny za wizualizację da-
private $model;
private $list; nych przechowywanych w modelu.
public function __construct(){ W naszym przykładzie wykorzystamy
... klasę GtkCellRendererText, pozwalającą
// tworzy widok drzewa
na wyświetlanie tekstu. Warto wiedzieć,
$this->list = new GtkTreeView;
że istnieją renderery pozwalające wy-
$scroll->add($this->list);
// tworzy model, z sześcioma elementami świetlać inne typy danych, takie jak ob-
$this->model = new GtkListStore(Gtk::TYPE_STRING, Gtk::TYPE_STRING, razy czy checkboksy, a każda kolumna
Gtk::TYPE_STRING,Gtk::TYPE_STRING, Gtk::TYPE_STRING, Gtk::TYPE_STRING); może zawierać co najmniej jeden z nich.
// tworzy kolumny
Po dodaniu rendererów należy je odpo-
$column1 = new GtkTreeViewColumn();
wiednio skonfigurować, ustawiając m.in.
...
$column1->set_title('Code'); szerokość kolumny i możliwość edycji
... znajdującego się w niej tekstu oraz łą-
// definiuje renderery cząc funkcję callbackową onEdit() ze
$cell_renderer1 = new GtkCellRendererText();
zdarzeniem edited, które występuje przy
...
edycji dowolnego z elementów listy.
// łączy renderery z edycją danych przez użytkownika
$cell_renderer2->connect("edited",array($this,'onEdit'),1,'description'); Kolejnym krokiem będzie doda-
... nie kolumn do listy reprezentowanej
przez obiekt $this->list. W tym celu

PHP Solutions Nr 1/2006 www.phpsolmag.org 69
Projekty PHP-GTK2

skorzystamy z metody append_column()
tego obiektu.
Teraz musimy dodać Model do listy:
osiągniemy to za pomocą metody set_
model() obiektu $this->list. Póki co nie
zawiera on żadnych danych, które zosta-
ną dodane później. Przy okazji, istotną
zaletą separacji warstw GtkTreeView
jest możliwość wykorzystania modelu
danych przez więcej niż jeden obiekt
GtkTreeView, dzięki czemu te same dane
Rysunek 9. Prosty edytor tekstu mogą być wyświetlane w różnych miej-
scach aplikacji.
Listing 5. Kod ProductList, ciąg dalszy Nasza klasa ProductList jest już
prawie gotowa. Musimy jeszcze stworzyć
// upakuj renderery metodę wypełniającą model danymi,
$column1->pack_start($cell_renderer1, true); którą nazwiemy showData(). Będzie ona
...
wywoływana z naszej klasy Application,
// określ szerokość
$cell_renderer1->set_property('width', 50); a jej działanie będzie następujące: naj-
... pierw nawiąże ona połączenie z bazą
// zezwól użytkownikowi na edycję danych i pobierze dane, a następnie włączy je do
$cell_renderer2->set_property('editable', True); modelu za pomocą metody set() obiektu
...
$this->model.
// zdefiniuj pozycję modelu do połączenia z rendererami
$column1->set_attributes($cell_renderer1, 'text', 0); Kolejną niezbędną metodą callbacko-
... wą jest onEdit(). Jej wywołanie nastąpi,
$this->list->append_column($column1); gdy użytkownik zmieni jakąkolwiek komór-
... kę listy. Metoda ta automatycznie zaktuali-
$this->list->set_model($this->model);
zuje dane w bazie, wyszukując właściwy
$this->list->show_all();
... wiersz w odpowiedniej tabeli na podstawie
} kodu produktu. I to wszystko jeśli chodzi
// Metoda showData – wyświetla wszystkie produkty obecne w bazie danych o ProductList.
public function showData(){
// otwiera połączenie z bazą i czyta wszystkie dane za pomocą pętli
// while()
Edytor
$conn = sqlite_open('data.db'); Kolejnym elementem, który chcemy
$query = sqlite_query($conn, 'select code, description, amount,'. dodać do naszego projektu, jest prosty
' unit, cost, price from products'); edytor tekstu wywoływany przy użyciu
while ($data = sqlite_fetch_array($query)){ metody callbackowej onEdit() klasy
$iter = $this->model->append();
Application. Oprócz edycji tekstu, po-
$this->model->set($iter, 0, $data['code'],
1, $data['description'], 2, $data['amount'], 3, $data['unit'], winien on umożliwiać także jego wczy-
4, $data['cost'], 5, $data['price']); tywanie i zapisywanie. Jego wygląd
} przedstawiamy na Rysunku 9.
sqlite_close($conn); Stworzymy klasę TextEditor i zapi-
}
szemy ją w pliku TextEditor.class.php. Jej
// Metoda onEdit – Wywoływana, gdy użytkownik zmienia dane
public function onEdit($cell_renderer, $path, $new_text, $column_number, kod pokazujemy na Listingu 6.
$column_name){ Głównym komponentem tej klasy
// pobierz zaznaczenie jest okno tekstowe, do implementacji
$treeselection = $this->list->get_selection(); którego użyjemy klasy GtkTextView
list($model, $iter) = $treeselection->get_selected();
i GtkTextBuffer. W PHP-GTK1 skorzy-
// ustaw nową wartość iteratora
$model->set($iter, $column_number, $new_text); stalibyśmy z przestarzałego już kom-
// pobierz pierwszą kolumnę ponentu GtkText. Istotną sprawą jest,
$code = $this->model->get_value($iter, 0); że o ile w PHP-GTK1 ten sam obiekt
// otwiera połączenie z bazą danych i dokonuje aktualizacji przechowywał treść i odpowiadał za
$conn = sqlite_open('data.db');
jej wizualizację, w PHP-GTK2 dane
$query = sqlite_query($conn, "update products set " .
"$column_name='$new_text' where code='$code'"); przechowujemy poza widgetem, we
sqlite_close($conn); wspomnianej już klasie GtkTextBuffer.
} Co za tym idzie, ten sam tekst może
} być wyświetlany przez różne wid-
?>
gety GtkTextView, analogicznie jak
w przypadku GtkTreeView. GtkTextBuffer

70 www.phpsolmag.org PHP Solutions Nr 1/2006
PHP-GTK2 Projekty

udostępnia nam ponadto iteratory, czyli
Listing 6. Kod edytora tekstu, przechowywany w pliku TextEditor.class.php możliwość zaznaczania określonych
miejsc w tekście.
<?php
// Klasa Editor – mały edytor tekstu W konstruktorze naszej klasy two-
final class TextEditor extends GtkWindow{ rzymy instancję GtkTextView o nazwie
private $textview; $this->textview. To samo uczynimy
private $textbuffer; z GtkTextBufffer, tworząc obiekt $this-
function __construct(){
>textbuffer. Wreszcie, połączymy
// tworzy okno
... model danych z widgetem za pomocą
// Tworzy pasek narzędzi metody set_buffer() obiektu $this-
$toolbar = new GtkToolbar; >textview.
// Tworzy przyciski “zapisz” i “otwórz” Jak już wspomnieliśmy, chcemy
$save = new GtkToolButton;
mieć możliwość zapisywania i łado-
$save->set_label('zapisz');
$save->set_stock_id('gtk-save'); wania (otwierania) plików tekstowych.
$save->connect('clicked', array(&$this, 'saveFile')); Jak już pokazaliśmy na Rysunku 9,
... edytor będzie wyposażony w pasek
// Wstawia przyciski na pasek narzędzi zawierający dwa opatrzone
$toolbar->insert($open, 0);
odpowiednimi ikonami elementy: Zapisz
$toolbar->insert($save, 0);
$vbox->pack_start($toolbar, false, false); i Otwórz. W celu stworzenia tego pa-
... ska wykorzystamy klasę GtkToolbar,
// Tworzy parę TextView/TextBuffer; nazywając jej instancję $toolbar. Na-
$this->textview = new GtkTextView; stępnie utworzymy przyciski do zapisu
$this->textbuffer = new GtkTextBuffer;
i ładowania danych, konstruując obiekty
$this->textview->set_buffer($this->textbuffer);
... klasy GtkToolButton. Każdemu z nich
} nadamy odpowiedni opis i przypiszemy
// Metoda openFile – Wyświetla FileDialog i wczytuje zawartość pliku właściwą ikonę.
// do TextBuffer Kolejnym krokiem będzie połącze-
public function openFile(){
nie wydarzenia clicked każdego z tych
// Tworzy FileChooserDialog
$dialog = new GtkFileChooserDialog('Otwarcie pliku', NULL, przycisków z odpowiadającą mu metodą:
Gtk::FILE_CHOOSER_ACTION_OPEN,array(Gtk::STOCK_OK,Gtk::RESPONSE_OK, openFile() w przypadku ładowania pli-
Gtk::STOCK_CANCEL, Gtk::RESPONSE_CANCEL)); ków, saveFile() przy ich zapisie. Główna
// Wyświetla FileChooserDialog część naszego edytora jest już gotowa,
$response = $dialog->run();
zabierzmy się zatem za metody obsługu-
if ($response == Gtk::RESPONSE_OK){ // jeżeli użytkownik kliknął OK,
//wyczyść TextBuffer i wstaw do niego zawartość wybranego pliku jące zdarzenia.
$first = $this->textbuffer->get_start_iter(); W pierwszej z nich, openFile(),
$end = $this->textbuffer->get_end_iter(); chcemy mieć możliwość wyboru pliku
$this->textbuffer->delete($first, $end); z listy. PHP-GTK2 oferuje w tym celu
$this->textbuffer->insert_at_cursor(file_get_contents(
GtkFileChooserDialog (patrz Rysu-
$dialog->get_filename()));
} nek 10), zastępujący – jako bardziej
$dialog->destroy(); użyteczny i łatwiejszy w rozbudowie
} – GtkFileSelection z PHP-GTK1. Kom-
// Metoda saveFile – Wyświetla FileDialog i zapisuje zawartość TextBuffer ponent GtkFileChooserDialog jest
// do wybranego pliku
związany z klasą GtkFileChooser
public function saveFile(){
// Tworzy FileChooserDialog z biblioteki Gtk2, wykorzystywaną
$dialog = new GtkFileChooserDialog('Zapis do pliku', NULL, praktycznie we wszystkich aplikacjach
Gtk::FILE_CHOOSER_ACTION_SAVE,array(Gtk::STOCK_OK,Gtk::RESPONSE_OK, opartych na Gnome, takich jak Evolu-
Gtk::STOCK_CANCEL, Gtk::RESPONSE_CANCEL)); tion, Gnumeric, Gaim czy Gimp. Instan-
// Wyświetla FileChooserDialog
cję GtkFileChooserDialog nazwiemy
$response = $dialog->run();
if ($response == Gtk::RESPONSE_OK){ // jeżeli użytkownik kliknął OK, $dialog, a następnie nadamy jej tytuł
// pobierz zawartość TextBuffer i zapisz ją do pliku Otwarcie pliku i skojarzymy ją z reakcja-
$first = $this->textbuffer->get_start_iter(); mi na możliwe odpowiedzi, czyli na OK
$end = $this->textbuffer->get_end_iter(); i ANULUJ. Również odpowiedzi będą
$text = $this->textbuffer->get_text($first, $end);
posiadały odpowiednie ikony stan-
file_put_contents($dialog->get_filename(), $text);
} dardowe. Jeżeli potwierdzimy naszą
$dialog->destroy(); chęć otwarcia pliku, bufor tekstu zo-
} stanie opróżniony (za pomocą $this->
} textbuffer->delete()), a zawartość
?>
pliku – wstawiona na bieżącej po-
zycji kursora ($this->textbuffer->

PHP Solutions Nr 1/2006 www.phpsolmag.org 71
Projekty PHP-GTK2

insertatcursor()). Po wykonaniu tych
czynności dialog zostanie zniszczony.
Kolejna metoda nosi nazwę
saveFile(). Wykorzystamy w niej tę
samą klasę GtkFileChooserDialog,
aby pobrać nazwę i położenie pliku,
do którego chcemy zapisać nasz tekst.
Podobnie jak w przypadku otwierania
pliku, dialog będzie posiadał dwa przy-
ciski: OK i ANULUJ. Jeżeli użytkownik
kliknie OK, saveFile() odczyta tekst
z bufora korzystając z metody $this->
textbuffer->get_text(), a następnie
zapisze go w pliku o wybranej (lub
wpisanej) nazwie, za pomocą metody
file_put_contents(). Na koniec (ana-
logicznie jak w przypadku openFile())
dialog zostanie zniszczony.
Rysunek 10. Wybór pliku
Kto mnie stworzył:
wykorzystanie dialogu
Listing 7. Kod klasy AboutDialog, zawarty w pliku AboutDialog.class.php O programie
Ostatnią rzeczą, która pozostała nam do
<?php zrobienia, jest dialog O programie, po-
// Klasa AboutDialog – Wyświetla informację o działającej aplikacji jawiający się po wybraniu opcji Pomoc
final class AboutDialog extends GtkWindow{
z menu o tej samej nazwie. W tym celu
public function __construct($software, $text){
... stworzymy klasę AboutDialog i umie-
ścimy ją w pliku AboutDialog.class.php.
// spróbuj załadować obrazek Jej kod przedstawiamy na Listin-
try{
gu 7. Będziemy w niej korzystać
$pixbuf = GdkPixbuf::new_from_file('images/gnome.png');
$imagem = new GtkImage;
z wyjątków oraz możliwości manipulacji
$imagem->set_from_pixbuf($pixbuf); obrazem.
$vbox->pack_start($imagem); Na początku swojego działania,
} konstruktor klasy AboutDialog ustawi
catch{ (PhpGtkGErrorException $error)
(jak zwykle) podstawowe parametry
// jeżeli wystąpiły jakieś błędy, wyświetl je na ekranie okienka dialogowego. Następnie zała-
$dialog = new GtkMessageDialog(null, Gtk::DIALOG_MODAL, dujemy obrazek o nazwie gnome.png
Gtk::MESSAGE_ERROR,Gtk::BUTTONS_OK, $error->message); i wyświetlimy go (patrz Rysunek
$response = $dialog->run(); 11). W tym celu wykorzystamy klasę
$dialog->destroy();
GdkPixbuf. Obsługuje ona najpopu-
return;
} larniejsze formaty obrazu, takie jak
PNG, JPEG, itd., co zwalnia nas
// wyświetl komunikat na temat aplikacji z obowiązku dodatkowej konwersji pli-
$this->label = new GtkLabel($text);
ków graficznych. W PHP-GTK1 klasa
$vbox->pack_start($this->label);
ta była dostępna, jednak korzystano
// tworzy przycisk “Zamknij” z niej jedynie opcjonalnie. W GTK2
$this->button = new GtkButton('Zamknij'); stała się ona częścią zbioru klas na-
$this->button->connect('clicked', array($this, 'onClose')); tywnych i nie jest już opcjonalna. Mo-
$vbox->pack_start($this->button, false, false);
żemy ją wykorzystywać we wszystkich
parent::show_all();
} zadaniach związanych z manipulacją
obrazkami, również w przypadku uży-
// Metoda onClose – Zamyka okno wanych na paskach narzędzi i w menu
public function onClose(){
ikon. Nie ma potrzeby opierania się
parent::destroy();
}
na formacie XPM (XpixMap), który był
... stosowany powszechnie w PHP-GTK1
} w sytuacjach, gdy nie korzystaliśmy
?> z GdkPixbuf.
Podsumowując: stworzymy instan-
cję klasy GdkPixbuf o nazwie $pixbuf,

72 www.phpsolmag.org PHP Solutions Nr 1/2006
PHP-GTK2 Projekty

ny, a w przypadku jego nieznalezienia nowości w PHP-GTK2. Zachęcamy do
aplikacja nie zakończy działania zgła- jego rozbudowy i poznawania szersze-
szając błąd. Z tego względu całość ope- go spektrum fascynujących możliwości
racji związanych z obrazkiem zostanie tej wersji PHP-GTK na własną rękę. Na
zamknięta w bloku try{} konstrukcji pewno nie będziecie zawiedzeni: PHP-
try{}..catch{}. PHP-GTK2 współdziała GTK2 stanowi bowiem wielki krok na-
z wprowadzonym w PHP5 systemem wy- przód w stosunku do poprzedniej wersji,
jątków, generując je w sytuacjach takich, a w połączeniu z nowymi możliwościami
jak błędy występujące podczas konstru- PHP5 może uczynić PHP poważnym
owania obiektu, a także pojawiające się wyborem dla programistów piszących
w metodach wykorzystujących mecha- klienckie aplikacje GUI. n
nizm Gerror (takich jak np. statyczne
konstruktory w rodzaju GdkPixbuff::
Rysunek 11. Dialog O programie new_from_file()) oraz przy konwersji
stron kodowych. W naszym przypadku, O autorze
przekazując jej konstruktorowi ścieżkę nieznalezienie logo spowoduje wygene-
do pliku gnome.png. Następnie za po- rowanie wyjątku obsługiwanego w kon- Pablo Dall’Oglio jest autorem pierwszej
na świecie książki o PHP-GTK. Jest
mocą klasy GtkImage stworzymy wid- struktorze klasy AboutDialog. również autorem Agata Report (www.aga-
get obrazka, który nazwiemy $imagem. I to wszystko. Nasza aplikacja jest ta.org.br) oraz Tulip Editor (http://tulip.solis.
Korzystając z jego metody set_from_ już gotowa i możemy ją przetestować. coop.br), a także koordynatorem projektu
pixbuf(), przekopiujemy do niego za- GNUTeca (www.gnuteca.org.br) – open-
sourcowego oprogramowania do zarzą-
wartość $pixbuf. Podsumowanie dzania biblioteką.
Chcemy jednak zapewnić także, Pokazany przykład jest dość nieskompli- Kontakt z autorem: pablo@dalloglio.net
że plik gnome.png zostanie załadowa- kowany, ilustruje jednak najważniejsze

R E K L A M A

PHP Solutions Nr 1/2006 www.phpsolmag.org 73
Ranking

Porównanie ofert polskich
firm hostingowych
Paweł Grzesiak

R
ynek usług hostingowych w Pol- więcej w czasie, gdy Google ogłosiło, że a jednocześnie są wciąż tańsze od na-
sce rozwija się dynamicznie. Do- planuje uruchomienie własnego serwera szych krajowych dostawców. Jednak
wodem na to są coraz lepsze pocztowego Gmail, oferującego zawrotną w perspektywie czasu, serwer ulokowa-
parametry usług oferowanych przez firmy, pojemność 1 GB, stało się jasne, że szy- ny poza granicami Polski może przestać
podczas gdy średnia cena hostingu wciąż kuje się w Polsce wojna o klienta. Usługi się opłacać.
maleje. Jeżeli planujemy zakup własnego hostingowe musiały bowiem nadążać za Jeszcze półtora roku temu providerzy
skrawka miejsca w sieci, warto zapoznać rodzimymi portalami, które zaoferowały bronili się rękami i nogami przed two-
się z przygotowanym przez nas porówna- bezpłatne skrzynki pocztowe, docelowo rzeniem zbyt wielkiej liczby kont e-mail,
niem usług najpopularniejszych polskich o pojemności 1 GB. czy podpinaniem wielu domen. Dziś
providerów internetowych. Co zabawne, zwiększenie pojemności standardem stało się wyrażenie „bez
Rok 2005 z pewnością możemy na- kont wcale nie równało się poniesieniu ograniczeń”, a na serwerze za kilkaset
zwać rokiem, w którym polski Internet roz- ogromnych nakładów finansowych. Więk- złotych można z powodzeniem urucho-
wijał się bardzo szybko. Motorem dla całej szość użytkowników nie wykorzystuje mić kilkanaście niezależnych serwisów
branży jest coraz powszechniejszy dostęp bowiem nawet 1% swojej powierzchni na internetowych.
Polaków do łącz szerokopasmowych. Fir- korespondencję. Zadziałał tu więc raczej
my telekomunikacyjne walczą o klientów, marketing. Kolejnym powodem, dla które- Jak powstało
co odbija się zarówno na cenie usług, jak go pośrednio obniżyły się ceny usług, było porównanie?
i coraz wyższej jakości łączy. Dziś rynek wejście usług hostingowych, z serwerami Tworząc swoisty ranking usług ho-
zaspokaja potrzeby zarówno tych, którzy zlokalizowanymi poza Polską. Miejsce na stingowych, za podstawowe kryterium
potrzebują szybkiego dostępu (liczonego takim serwerze było swego czasu sporo wzięliśmy statystyki ilości obsługiwanych
w MBPS), jak i tych, dla których podsta- tańsze, dlatego że serwery lokowano za domen przez każdego z providerów, ba-
wowym kryterium jest cena (już nie prze- oceanem. Na korzyść serwerów w USA zując na wynikach z witryny top100.pl.
kraczająca 100 zł). Coraz większa liczba działała różnica czasu. Gdy Amerykanie Usługi hostingowe bardzo ciężko jest
szybkich łącz dla klientów indywidualnych, spali, my korzystaliśmy z ich serwerów. jednoznacznie ocenić, wyłaniając przy
pociąga za sobą rozwój infrastruktury tele- Jednocześnie w Stanach ceny usług ho- tym zwycięzcę. Zarówno oferty, jak i ich
komunikacyjnej. stingowych i sprzętu komputerowego są ceny są bardzo do siebie zbliżone. Mo-
Skoro użytkownicy pobierają więcej niższe, więc możliwe było zaoferowanie gliśmy bazować na popularności i opinii
plików z sieci, serwery muszą sprostać ciekawych warunków cenowych. Wadą klientów. Postawiliśmy na to, sądząc, że
wzrastającemu obciążeniu. W ten sposób, były długie czasy odpowiedzi serwera, największe zaufanie budzi ten provider,
w ciągu ostatniego roku obserwujemy na czyli tzw. pingi, co wynikało z długości który zarejestrował dla swoich klientów
rynku znaczący wzrost pojemności serwe- odległości połączenia i bariery kontynen- najwięcej domen. Oczywiście nie powin-
rów WWW, większe limity transferu mie- talnej. Dla przeciętnego użytkownika nie no to stanowić kryterium decydującego
sięcznego, a także niższe ceny za ruch były jednak zauważalne opóźnienia przy o zakupie danej usługi. Zaprezentowane
dodatkowy. Powodów sytuacji, gdzie para- ładowaniu stron. 12 firm prezentuje bowiem bardzo wy-
metry usług są coraz lepsze, a ceny usług Dziś już odchodzi się od lokowania równany, wysoki poziom. Należy jesz-
maleją, jest co najmniej kilka. Główny serwera w USA, na rzecz krajów Euro- cze tylko dodać, że do rankingu zostały
impuls do zmian dały zagraniczne serwisy, py Zachodniej, a konkretniej Holandii zakwalifikowane tylko te firmy, których
oferujące konta e-mailowe o wielkościach i Niemiec, które są nam geograficznie podstawowym przedmiotem działalności
(na tamte czasy) astronomicznych. Mniej bliższe (stąd strony ładują się szybciej), są usługi hostingowe. n

74 www.phpsolmag.org PHP Solutions Nr 1/2006
Limit transferu Ceny Lokalizacja
Firma Usługa Powierzchnia Restrykcje ilościowe Obsługa baz danych
(miesięczny) (netto) serwera
Bazy Domen / Baz Postgre- Abonament
FTP Poczta Kont e-mail Serwisów Kont FTP MySQL
Danych Subdomen danych SQL Roczny
Home (home.pl) Business Starter 1 GB 10 GB b.o. b.o. 1 1 1 Tak Tak 300
Business Server 5 GB 20 GB b.o. b.o. b.o. 1 5 Tak Tak 600 Polska
Business PRO 10 GB 30 GB b.o. b.o. b.o. b.o. 15 Tak Tak 900
Progreso (progreso.pl) Biznes 5 GB 12 GB b.o. b.o. b.o. b.o. b.o. Tak - 300
Polska
Biznes plus 10 GB 24 GB b.o. b.o. b.o. b.o. b.o. Tak - 600

PHP Solutions Nr 1/2006
Livenet (livenet.pl) Small 100 MB 1 GB 3 5 1 1 2 Tak Tak 32,78
Medium 200 MB 2,5 GB 10 7 2 5 4 Tak Tak 45,08
Large 500 MB 5 GB 30 20 5 10 8 Tak Tak 72,13
Big 1 GB 8 GB 50 100 7 10 12 Tak Tak 108,19 Holandia
X-big 2 GB 10 GB b.o. b.o. 9 10 16 Tak Tak 144,26
Portal 5 GB 15 GB b.o. b.o. 11 20 20 Tak Tak 198,36
Prioritaire 10 GB 30 GB b.o. b.o. 16 b.o. 40 Tak Tak 270,49
Netlink (nq.pl) nQ.START 1 GB 5 MB 4 GB 5 1 1 1 1 Tak Tak 199
nQ.BIZNES 2 GB 25 MB 10 GB 20 5 3 5 1 Tak Tak 399
Polska
nQ.PROFESJA 5 GB 200 MB 16 GB 50 15 5 15 2 Tak Tak 699
nQ.VIP 10 GB 300 MB 24 GB b.o. 30 10 b.o. 3 Tak Tak 999
Nazwa (nazwa.pl) Active 5 GB 10 GB b.o. b.o. b.o. 1 1 Tak Tak 300
Polska
Pro 10 GB 20 GB b.o. b.o. b.o. 1 b.o. Tak Tak 600
KEI Provider (kei.pl) Biuro Extra 5 GB 10 GB b.o. b.o. b.o. 1 1 Tak Tak 300
Polska
Lider 10 GB 20 GB b.o. b.o. b.o. 1 5 Tak Tak 600
Domeny.org (domeny.org) Start 4 GB 4 GB 2 GB 8,3 GB b.o. b.o. b.o. 1 5 Tak - 220
Polska
Classic 10 GB 10 GB 5 GB 16,7 GB b.o. b.o. b.o. b.o. b.o. Tak - 420
Futuro (futuro.pl) Flexo Start 2 GB 5 MB 1 GB b.o. b.o. b.o. b.o. b.d. Tak - 50
Flexo 5 GB 5 MB 10 GB b.o. b.o. b.o. b.o. b.d. Tak - 100 Polska
Flexo Biz 10 GB 7 MB 20 GB b.o. b.o. b.o. b.o. b.d. Tak - 500
Ogicom (ogicom.pl) Biznes 50 100 MB - 1 GB 4 b.o. 1 1 - - - 407
Biznes 150 300 MB - 3 GB 12 b.o. 1 1 - - - 660

www.phpsolmag.org
Biznes 250 500 MB 5 GB 30 b.o. 1 1 b.d. Tak - 880 Polska
Biznes 350 700 MB 7 GB 50 b.o. 1 1 b.d. Tak - 1199
Biznes 500 1 GB 10 GB b.o. b.o. 1 1 b.d. Tak - 1507
Agnat (agnat.pl) Profit 5 GB 5 GB 100 b.o. b.d. b.d. n.d.* - - 298
Polska
Expert 10 GB 10 GB b.o. b.o. b.d. b.d. n.d.* - - 598
Sisco (sisco.pl) Economic 300 300 MB 5 GB 5 b.o. b.o. b.o. 1 Tak - 300
Business 500 500 MB 8 GB 10 b.o. b.o. b.o. 2 Tak - 400
Pro 1000 1 GB 16 GB b.o. b.o. b.o. b.o. 3 Tak - 600 Polska
VIP 2000 2 GB 25 GB b.o. b.o. b.o. b.o. 5 Tak - 1000
Max 3000 3 GB 40 GB b.o. b.o. b.o. b.o. 8 Tak - 1600
AlphaNet (alpha.pl) Promo 500 MB 500 MB b.o. 5 GB b.o. b.o. 1 b.o. 1 Tak Tak 99
Prima 1 GB 1 GB b.o. 10 GB b.o. b.o. 2 b.o. 2 Tak Tak 190
Expo 5 GB 20 GB b.o. b.o. 4 b.o. 4 Tak Tak 290
Polska
Multi 3 GB 20 GB b.o. b.o. 20 b.o. 20 Tak Tak 399
Opti 6 GB 30 GB b.o. b.o. 50 b.o. 50 Tak Tak 599
Solid+ 15 GB 100 GB b.o. b.o. 150 b.o. b.o. Tak Tak 1500
AMM-Komputer (amm.net.pl) Start 1 GB 25 MB 2 GB b.o. b.o. b.d. b.d. 2 Tak - 100
Start+ 1,5 GB 50 MB 3 GB b.o. b.o. b.d. b.d. 3 Tak - 150
Mini 3 GB 75 MB 4 GB b.o. b.o. b.d. b.d. 3 Tak Tak 200
Polska
Standard 5 GB 10 GB b.o. b.o. b.d. b.d. 4 Tak Tak 300
Business 6 GB 15 GB b.o. b.o. b.d. b.d. 5 Tak Tak 400
Professional 7,5 GB 20 GB b.o. b.o. b.d. b.d. 7 Tak Tak 600
b.o. – bez ograniczeń; n.d. – nie dotyczy; b.d. – brak danych; * firma oferuje dostęp do bazy danych SQLite (moduł PHP5)

75
Ranking
Portal aktywnych internautów
C
odziennie w Polsce nowe firmy rozpo- w żadnych zbiorczych zestawieniach firmy
czynają działalność, łączą się, zmie- statystycznej Gemius SA. A wystarczy tylko
niają nazwy. Każdy przedsiębiorca sprawdzić, iż wyszukiwarka Google zaindekso-
i webmaster poszukuje sposobów, by pokazać wała 2,5 mln dokumentów portalu osemka.pl
w sieci siebie i swą ofertę z jak najlepszej – dla porówania w Google znajduje się 2,3 mln
strony, jak najniższym kosztem. W ostatnich dokumentów Wirtualnej Polski (webpark.pl),
latach było wiele firm oferujących usługi ho- czy 1,6 mln firmy Prv.pl. Świadczy to o ogrom-
stingowe, które po kilku miesiącach kończyły nej popularności usług proponowanych przez
swą działalność, a nasze pieniądze i trud wspomniany serwis oferujący 200 MB konta
w budowaniu stron legł w gruzach. Dla webma- na strony www oraz dostęp przez ftp. Wyróż-
stera podstawową sprawą jest znalezienie ta- niającą cechą tego portalu wśród konkurencji
kiego usługodawcy, który zapewni stabilność jest wysoka pozycja stron w domenie friko.pl
strony, szybki dostęp, a także wiele usług i za.pl w wyszukiwarkach (strona główna ma
dodanych. Jednak fundamentalnym kryterium Page Rank 6), a także możliwość skorzysta-
przy wyborze miejsca, w którym będziemy nia ze skryptów PHP 4.4.0. Niezwykle istotne
przetrzymywali naszą stronę jest cena. Two- jest, iż osemka.pl nie tworzy żadnych limitów
rząc strony w PHP staramy się, by czynność miesięcznych, czy rocznych na transfery pli-
ich aktualizacji przełożyć na barki właścicieli ków. Ta opcja jest praktycznie niedostępna u SZUKAMY WIĘCEJ MIEJSCA
stron. Korzystamy zatem z systemów umoż- konkurencji, a ponadto nawet serwisy płatne Naszą uwagę w poszukiwaniach darmowego
liwiających użytkownikowi samodzielne za- stosują limity. hostingu z obsługą php przykuła inna oferta
rządzanie treścią (CMS). Niestety na naszym polskiej firmy Spox.pl.
rynku istnieje niewielka liczba firm, których TESTUJEMY Wspomniany serwis oferuje całkowicie
oferta odpowiadałaby naszym potrzebom. Postanowiliśmy przetestować ofertę osem- bezpłatnie profesjonalny hosting o parame-
Najczęściej dostęp do serwera oferującego ka.pl. Rejestracja wyglądała prosto i sprawnie trach zarezerwowanych do tej pory dla komer-
kilkaset megabajtów miejsca na stronę WWW, – należało podać jedynie swoje dane oraz cyjnych i często bardzo drogich serwerów www.
możliwość dostępu do PHP w wersji nowszej adres email, następnie wybrać domenę dla Spox.pl proponuje aż 500 MB pamięci dysko-
niż 4.0, gwarantującego bezpieczeństwo na- naszej strony z łatwo zapadających w pamięć wej, transfer bez limitu, łatwy dostęp przez
szych danych, kosztuje od kilkudziesięciu do – za.pl i friko.pl. Momentalnie otrzymaliśmy FTP, cPanel. Dodatkowo istnieje także opcja
kilkuset złotych miesięcznie. wygenerowany automatycznie email z potwier- posiadania własnej domeny. Tutaj o rejestracji
dzeniem rejestracji i hasłem. Po zalogowaniu nie decyduje automat, a każde zgłoszenie jest
POSZUKUJEMY się nie napotkaliśmy jakichkolwiek problemów osobno rozpatrywane. Na odpowiedź i potwier-
DARMOWEGO HOSTINGU z edycją hasła, swoich danych itp. W panelu dzenie zgłoszenia rejestracyjnego czekaliśmy
Szukając rozwiązania naszych problemów użytkownika znaleźliśmy nieaktywną opcję niecałe 24h. Zaraz po zalogowaniu się i zmianie
związanych z koniecznością uiszczania opłat zakładania baz MySQL. Fakt jej umieszczenia hasła mogliśmy już swobodnie umieścić naszą
za hostowanie naszych stron, można natknąć może wskazywać, że programiści przewidzieli stronę w serwisie spox.pl. Transfer był szybki i
się na oferty kilku firm oferujących darmowe taką możliwość w przyszłości. Chwilę później nie zauważyliśmy jakichkolwiek problemów z
usługi. Jedną z nich jest oferta Ósemka.pl In- po szybkim transferze plikow na serwer, mo- funkcjonowaniem naszego site’a. Strona nadal
ternet Media, właściciela za.pl i friko.pl. Ósemka gliśmy się już pochwalić naszą stroną inter- działa bezbłędnie.
to jedna z najlepszych firm oferujących nie- netową, zbudowaną w systemie CMS, naszym
przerwanie od 1999 roku usługi hostingowe. znajomym i klientom, i to zupełnie za darmo! ZA GRANICĄ ZA DARMO
Obecnie w nowopowstałym Portalu Aktywnych Największą wadą tego serwisu, którą napotka- Przykładem sprawnego działania, bez koniecz-
Internautów, który połączył tradycję darmo- liśmy był tryb działania safe mode, który nieco ności obsługi skryptów PHP, jest zagraniczny
wych kont za.pl i friko.pl, zarejestrowanych jest ogranicza funkcje, lecz jednocześnie zwiększa serwis happyhost.org. Przede wszystkim należy
202 tysiące użytkownikow, a 66 tysięcy kont szansę, że nasz serwer bedzie działał dłużej. zaznaczyć, że jest to całkowicie darmowy serwis,
hostingowych jest aktywnych. Ze statystyk Zauważoną niedogodnością była funkcja inclu- bez limitów na przesył danych. Co prawda pingi
wynika, iż łącznie strony hostowane na ser- de, którą można stosować tylko do bieżącego wysyłane z Polski wracają po nieco dłuższym
werach portalu hosting.osemka.pl w miesiącu katalogu. Utrudnia to nieco pracę przy więk- czasie, niż w przypadku innych rodzimych
wrześniu 2005r. obejrzało blisko 3mln użyt- szych ilościach podstron, lecz zważywszy na serwisów, jednak sprawdzając połączenia z ser-
kownikow, generując ponad 25 mln odsłon. fakt, iż jest to całkowicie darmowy serwis – to werów znajdujących się w USA, wyniki wyglądają
Budzi zdziwienie fakt, iż nie jest to odnotowane i tak nieznaczne uchybienie. rewelacyjnie.

76
zamów prenumeratę PHP Solutions
nowa niższa cena: a otrzymasz prezent!
150zł 135zł
wybierz prezent:

» program PHPRunner o wartości 199$
» 20% zniżki na oprogramowanie firmy Zend
» dwa dowolne numery archiwalne PHP Solutions
» pakiet internetowy nQ.Biznes firmy Netlink o wartości 486,70 zł
» roczny abonament na Usługę Business Starter
» pakiet Xtreeme SiteXpert firmy Xtreeme
» Maguma Workbench Bundle Starter

* cena prenumeraty rocznej w promocji zimowej
oferta ważna do wyczerpania zapasów;
szczegółowe informacje: www.phpsolmag.org/prenumerata lub pren@software.com.pl
PHPRunner: Szybkie prototypowanie
PHP na twój sposób
F
irma Universal Data Solutions wydała PHPRunner jest świetnym rozwiązaniem do
PHPRunner 2.0 – biznesowe rozwiąza- tworzenia chronionych hasłem serwisów tylko
nie do budowania wizualnie atrakcyj- dla użytkowników; ponadto pozwala budować
nych interfejsów sieciowych dla najpopular- strony pozwalające przeprowadzać automatycz-
niejszych systemów baz danych, mianowicie: ną rejestrację użytkowników. Można z jego pomo-
MySQL, PostgreSQL, Oracle, MS Access oraz cą tworzyć funkcje przypominania i zmieniania
MS SQL Server. Jest zaprojektowane tak, by haseł. Operacjom wywoływanym za pomocą tych
spełniać oczekiwania wszystkich użytkowni- funkcji może towarzyszyć na przykład wysyłanie
ków – od początkujących po doświadczonych pocztą powiadomienia dla administratora w sytu-
deweloperów. acji, gdy zarejestrował się nowy użytkownik.
PHPRunner posiada różne zastosowania: Możliwe jest także ustawienie dla każdego przed wprowadzeniem niewłaściwych danych
przeszukiwanie baz danych przez sieć, wpro- użytkownika wielopoziomowych uprawnień do do dostępnych dla nich pól. Po wygenerowaniu
wadzanie danych bądź tworzenie sieciowych różnych tabel. Przykładowo: Użytkownik A może plików PHP, użytkownicy mogą szybko wysłać
raportów. Branże: samochodowa, nierucho- jedynie edytować i dodawać dane do tabel Orders pliki na zdalny serwer WWW za pomocą wbu-
mości, medyczna i turystyczna to tylko kilka i Customers, podczas gdy Użytkownik B może dowanego klienta FTP – z możliwością wyboru,
spośród wielu, w których można wykorzystać kasować dane w tabeli Orders oraz edytować ta- czy wysłane mają być wszystkie pliki, czy tylko
to rozwiązanie. belę Customers. Oprócz tego, PHPRunner może zmodyfikowane.
Istotnym elementem jest wykorzystywa- tworzyć użytkowników Administrator oraz Gość. PHPRunner jest tak prosty w obsłudze, że
nie przez PHPRunner interfejsu opartego na Administrator miałby dostęp do wszystkich tabel w pełni funkcjonalną aplikację można za jego
kreatorach oraz zbioru szablonów. Oznacza to, i rekordów, Gość zaś – tylko prawa odczytu. pomocą wygenerować w zaledwie 15 minut.
że administrator bazy danych nie musi napi- PHPRunner czyni edycję danych dziecinnie PHPRunner rozpowszechniany jest elek-
sać ani jednej linii kodu, czy nawet posiadać łatwą. Zapewnia on szereg zaawansowanych tronicznie przez Internet. Darmowa wersja
doświadczenia programistycznego. Oprócz kontrolek edycyjnych takich jak selektory dat, próbna dostępna jest na stronie producenta. Cena
szczodrej kolekcji szablonów, dostępny jest łączone rozwijane listy, możliwość przesyłania pojedynczej kopii to 199 USD. Dystrybutorom
także ich edytor, pozwalający administratorom plików i obrazków na serwer, edytor tekstu z oprogramowania oraz kupującym większe ilości
baz danych dopasowywać wygląd stron stwo- formatowaniem HTML, pola list, przyciski radio i kopii sugerujemy negocjację specjalnych zniżek.
rzonych w PHP do własnych potrzeb, przez wiele innych. Wersja próbna (ważna 21 dni) może być pobrana
wstawianie logo i etykiet oraz modyfikację PHPRunner dostarcza także szereg funkcji ze strony http://www.xlinesoft.com/phprunner/
czcionek i kolorów. Zaawansowani użytkowni- walidacji danych, chroniących użytkowników download.htm
cy mają także możliwość poprawiania genero-
wanego przez program kodu PHP.
Obecnie oprócz MySQL używać można
CO NASI KLIENCI MAJĄ DO POWIEDZENIA O PHPRUNNER:
także PostgreSQL, Oracle’a, MS Access i MS SQL l Cóż, stało się wreszcie! Znalazłem rozwiązanie umożliwiające szybkie prototypowanie
Server, co stanowi istotny krok w dziedzinie w świecie MySQL/PHP – i wprawdzie istnieją także i inne, jednak to jest pierwsze wi-
ulepszania tego rozwiązania. Ponadto PHPRun- dziane przeze mnie rozwiązanie, które jest zarówno praktyczne, jak i funkcjonalne. Tym
ner wzbogacony został o szereg przydatnych produktem jest PHPRunner – Peter B. MacIntyre.
funkcji, takich jak wielopoziomowe uprawnienia l Korzystając z PHPRunner właśnie zbudowałem serwis dla bazy MySQL w ciągu około
dostępu do różnych tabel, wsparcie dla wielu trzydziestu minut. FANTASTYCZNE. Po prostu nie mogę tego opisać. Kilku moich kole-
języków (wybierać można z ponad 20), a także gów nie może wyjść z podziwu, jak gładko to wszystko poszło – Randy Samms, SBC/
wiele innych możliwości. Southwest-ESAC.
l Muszę Ci to powiedzieć stary, ten program to najwspanialszy wynalazek od czasu krojonego
chleba. Jako obecny w sieci fotograf, piszący swoje własne galerie itp. korzystam z wielu
programów, ale PHPRunner to chyba najbardziej pomocny program w mojej biblioteczce. Po
Kontakt: Telefon: +1 (888) 290-6617
http://www.xlinesoft.com prostu tak bardzo upraszcza mi życie – Stephen Jones, Australia.
l PHPRunner to prawdopodobnie najlepszy generator PHP na rynku. Zanim trafiłem na to
Informacje o programie: narzędzie wypróbowałem coś koło trzech innych i mogę powiedzieć, że nie znajdziesz
http://www.xlinesoft.com/phprunner lepszego, ani nawet porównywalnego – Pedro Ruiz, Database Manager.

78
d e d s i t e s > > >
Recommen
Jeśli prowadzisz ciekawą stronę internetową
i chcesz abyśmy przedstawili ją w ramach akcji Recommended sites,
skontaktuj się z nami pod adresem redakcja@phpsolmag.org

Całkowicie przebudowany serwis poświęcony Strona zawierająca artykuły związane z PHP Serwis dla webmasterów, organizujący konkurs
skryptom. Obecnie skupia się na zbiorze skryp- napisane przez autora. Zawiera też kolekcję we współpracy z naszym magazynem, promują-
tów z całego świata. zdjęć oraz opisy ciekawych książek. cy rozwiązania bez użycia systemów CMS itp.
www.antraxja-skrypty.iweb.pl www.antylameriada.net www.webmaster.bbsoft.pl

Serwis stworzony z myślą o programistach Bogate archiwum skryptów PHP, JavaScript, Skryptoteka to jeden z największych i najbar-
PHP, który stara się prezentować także ogólne CGI, Perl i ASP. Zawiera także artykuły, porady, dziej znanych zbiorów skryptów. Zawiera także
wiadomości związane Internetem. tutoriale, książki. artykuły, czcionki, szablony i podręczniki.
www.compzone.org www.megaskrypty.com www.skryptoteka.pl

Młody, rozwijający się serwis związany z PHP. Serwis zawierający kompletne kursy 9 technolo- Portal, który oprócz emitowania newsów czy
Zdaje się, że autorowi zależy na niestandardo- gii związanych z webmasteringiem oraz skrypty, wywiadów gromadzi bogate materiały związane
wej oprawie i zawartości. elementy grafiki, czcionki i narzędzia. z projektowaniem i pisaniem stron WWW.
www.sm00f.boo www.tymex.org www.webinside.pl

Serwis powstał w celu związania społeczności Wortal webmastera bogaty w wiele materiałów. Xklonos to serwis istniejący już od 2001 roku.
webmasterskiej. Użytkownicy mogą wymieniać Związany jest z Webserv – pakietem Apache, Każdy użytkownik może wpływać na jego
doświadczenia oraz uzyskiwać porady. PHP, MySQL, MySQL CC oraz NoIP. formę.
www.webmade.org www.webpl.org www.xklonos.cal.pl

ites
Recommended s
www.shop.software.com.pl

Zaprenumeruj swoje ulubione magazyny
i zamów archiwalne numery!

Już teraz w kilka minut możesz zaprenumerować swoje ulubione pismo.
Gwarantujemy:
- preferencyjne ceny
- bezpieczną płatność on-line
- szybką realizację Twojego zamówienia
Bezpieczna prenumerata on-line wszystkich tytułów Wydawnictwa Software!
zamówienie prenumeraty

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

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

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

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

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

E-mail (niezbędny do wysłania faktury)............................................................................................................................................................................
automatyczne przedłużenie prenumeraty

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

Linux+ (2 płyty CD)
Miesięcznik o systemie Linux
12 250/1801

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

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

PHP Solutions (1 płyta CD)
Dwumiesięcznik o zastosowaniach języka PHP
6 135

Hakin9, jak się obronić (1 płyta CD)
Dwumiesięcznik o bezpieczeństwie i hakingu
6 135

.psd (1 płyta CD + film instruktażowy)
Dwumiesięcznik użytkowników programu Adobe Photoshop
6 140

Aurox Linux (4 płyty CD + 1 płyta DVD)
Magazyn z najpopularniejszym polskim Linuksem
4 1193

Suma

Jeżeli chcesz zapłacić kartą kredytową, wejdź na
stronę naszego sklepu internetowego:
1
Cena prenumeraty rocznej dla osób prywatnych
2
Cena prenumeraty rocznej dla osób prenumerujących już Software Developer’s Journal lub Linux+
3
Cena prenumeraty dwuletniej Aurox Linux www.shop.software.com.pl
W następnym numerze PHP Solutions

W sprzedaży od 20 lutego!

■ Ilia Alshanetsky, twórca FUDforum, autor książki Guide to PHP Security, a także współautor
wielu projektów Open Source i rozszerzeń takich jak: PDO, SQLite czy GD prezentuje:
Ataki XSS oraz CSRF na aplikacje internetowe

■ Aaron Wormus: PEAR::Structures_DataGrid – poznajemy narzędzia tworzące datagrid
w formatach HTML, XML, XLS, XUL i innych. Pakiet umożliwia także łatwe implementowanie
mechanizmów stronicowania i sortowania w celu limitowania wyświetlanych i przetwarzanych
danych. Idea wzorowana jest na .NET Framework DataGrid control.

Ponadto planujemy:

■ SDO (Service Data Objects) i XML

■ Phalanger, czyli z .NET do PHP

■ Wzorce projektowe