Guillaume Lecanu Stopień trudności: Technologia XML podbija świat, a PHP opiera swój sukces na wsparciu dla czołowych rozwiązań. Zastosowanie pozwala poszerzyć i tak bardzo zaawansowane możliwości PHP w dziedzinie tworzenia i manipulacji dokumentami XML... W SIECI 1. http://pear.php.net/ packages/ klasa 2. http://xmlsoft.org strona główna projektu libxml 3. http://pear.php.net/ packages/cache_lite pakiet PEAR::Cache_Lite 4. http://lya.fr/pear/xml_ FastCreate/tests/ przykłady użycia Format XML (extensible Markup Language jest coraz częściej używany na stronach internetowych, gdzie zawitał przede wszystkim jako XHTML nowy, promowany przez W3C standard zapisu stron WWW. Innym popularnym zastosowaniem XML-a są formaty plików pakietów biurowych, w szczególności stosowane od dawna w OpenOffice.org formaty SXW czy SXC oraz nowatorskie i uznane za standard przez wiele firm instytucji ODT (tekst czy ODG (grafika. XML jest również wykorzystywany jako format eksportu i importu danych wielu programów czy też komunikacji pomiędzy aplikacjami znajdującymi się na różnych maszynach (klient-serwer, np. w protokołach typu SOAP czy XML-RPC. Język PHP dysponuje dużymi możliwościami tworzenia i przetwarzania dokumentów XML. W artykule pokażemy, jak użycie PEAR-owego pakietu (XFC poszerza tę funkcjonalność, pozwalając m.in. na wymuszanie zgodności generowanego kodu XML z zasadami określonymi przez DTD, informowanie użytkownika o niezgodności, szybką konwersję na XHTML oraz bardzo łatwe tworzenie rozbudowanych dokumentów, również jeśli chcemy je składać z wielu części. Instalacja Jak już powiedzieliśmy, XFC jest pakietem należącym do repozytorium PEAR (PHP Co należy wiedzieć... Potrzebna będzie podstawowa znajomość zagadnień programowania obiektowego w PHP5. Przydatna będzie również ogólna wiedza na temat standardu XML. Co obiecujemy... Pokażemy, jak za pomocą XML_Fast- Create utworzyć prawidłowy kod XML. Zademonstrujemy też, jak dokonywać transformacji znaczników XML-a, wymuszać sprawdzanie DTD, wykrywać błędy składni czy tworzyć dokumenty w XHTML-u. 2
Projekty Extension and Application Repository, http://pear.php.net. Aby go zainstalować, wystarczy w linii poleceń systemu operacyjnego użyć narzędzia pear. Jeżeli takowego nie posiadamy, jego instalacja jest bardzo prosta i została szczegółowo opisana na stronach PEAR-a. Mając narzędzie pear jesteśmy gotowi do instalacji XFC. W tym celu wystarczy wpisać pear install. Pamiętajmy, że instalacja niektórych pakietów opcjonalnych, które są w wersji beta lub alpha, może wymagać użycia dodatkowego parametru narzędzia pear. Przykładowo, dla wersji beta będzie to: pear -d preferred_state=beta install nazwa_pakietu Jeżeli ta składnia nie działa (co ma miejsce w przypadku starszych wersji narzędzia pear, to musimy sprawdzić na stronie pakietu, w jakiej fazie znajduje się jego najnowsza wersja (stable, alpha lub beta i uwzględnić tę fazę, jeśli jest różna od stable, w sposób następujący: pear install nazwa_pakietu-faza np.: pear install XML_Tree-beta Wśród pakietów opcjonalnych współpracujących z XFC można wymienić np. XML_Tree, XML_DTD, XML_Beautifier czy XML_HTMLSax. Ich użycie jest zalecane w celu pełnego wykorzystania możliwości. XFC: pierwsze kroki Aby skorzystać z, dołączamy plik XML/FastCreate.php (zob. Listing 1. Tworzenie instancji $x=&::factory('text', XML_FASTCREATE_DOCTYPE_XHTML_1_0_STRICT Listing 1 i tworzymy obiekt $x, będący instancją klasy (uwaga: używamy w tym celu należącej do klasy metody statycznej factory(. Zainicjowanie $x wymaga podania dwóch parametrów: pierwszy z nich określa format wygenerowanego XML-a. My używamy formatu Text, co oznacza, że po użyciu metody toxml( (którą omówimy później uzyskamy XML w formie tekstu (zserializowanej. Drugi parametr stanowi tablicę asocjacyjną opcji, którą również omówimy później. Wspomnijmy, że jedną z najistotniejszych spośród tych opcji jest właściwy nagłówek doctype, który zostanie następnie umieszczony w pierwszej linii pliku XML i będzie określał DTD, na którym oparto dokument. Nagłówek ten możemy zdefiniować ręcznie, ale daje nam do wyboru kilka gotowych definicji odpowiadających najczęściej używanym doctype. My użyjemy: XML_FASTCREATE_DOCTYPE_ XHTML_1_0_STRICT co w rezultacie daje nagłówek: XHTML 1.0 Strict. Rysunek 1. Przykład błędu polegającego na niezgodności XML-a z jego DTD Stwórzmy teraz najprostszy dokument XML, który będzie stroną XHTML-ową zawierającą napis Hello World! w akapicie (<p>..</p> oraz tekst jako tytuł (tag <title>..</title> i korzystającą z innych tagów typowych dla HTML-a i XHTML-a (<html>,<head> i <body>. Patrząc na kod z Listingu 2 zauważymy, że wykorzystane tam metody (oprócz toxml( obiektu $x mają takie same nazwy, jak użyte przez nas tagi XHTMLa. Wstawienie każdej z tych metod do skryptu powoduje automatyczne utworzenie znacznika o takiej samej nazwie. Jest to wielką zaletą, która ułatwia tworzenie nawet rozbudowanych dokumentów. Zwróćmy też uwagę na zagnieżdżenie metod reprezentujących znaczniki: zaczynamy od metody: której parametrem jest: $x->head( która z kolei zawiera wywołania head( i body(. W ten sposób tworzymy strukturę znaczników w, która po użyciu metody toxml( zostanie przekształcona na gotowy dokument XML (zob. Listing 3. UWAGA: toxml( jedynie wypisuje XML na ekran; aby zapisać dokument w zmiennej, trzeba użyć metody getxml(, np.: $xml_out=$x->getxml( W tym przykładzie nie określiliśmy atrybutów znaczników. Aby je dodać do określonego znacznika, wystarczy umieścić je w tablicy asocjacyjnej przekazanej jako pierwszy argument metody reprezentującej ten znacznik. Przykładowo, aby utworzyć link (tag <a href>...</a> prowadzący do strony głównej repozytorium PEAR, wpisujemy: $x->a( href => http:// pear.php.net, P.E.A.R. Listing 2. Hello World! w XML-u przy użyciu $x->head( $x->title('', $x->body($x->p('hello World!' $x->toxml( 3
Efektem będzie: <a_href= http://pear.php.net > P.E.A.R.</a> Już na podstawie tych przykładów możemy się przekonać, jak łatwe i szybkie jest tworzenie XML-a przy użyciu XML_Fast- Create. Wymuszanie i weryfikacja zgodności z DTD Tworząc lub modyfikując dowolny dokument XML powinniśmy się upewnić, czy będzie on zgodny ze standardem DTD, do czego często używamy dodatkowych narzędzi. Użycie pozwala nam zaoszczędzić czas dzięki funkcji automatycznego wyświetlania błędów składni XML-a. Aby wymusić sprawdzanie DTD, musimy mieć odpowiedni plik zawierający definicje DTD (niektóre z nich, np. XHTML, znajdują się w katalogu dtd zainstalowanego i uruchomić jego ładowanie podczas tworzenia instancji klasy. To drugie wykonamy dodając opcję dtd do wspomnianej wcześniej tablicy asocjacyjnej (Listing 4. My załadujemy plik xhtml_1_0_strict.dtd, będący definicją standardu XHTML 1.0 w wersji strict. Jeżeli w składni XML-a wystąpi niezgodność z DTD, komunikat o błędzie zostanie wyświetlony podczas wywołania wspomnianej już metody toxml(. Pozwala to np. na wyświetlenie go na końcu strony WWW. Na Listingu 5 podajemy przykład skryptu generującego stronę XHTML-ową, w której celowo wstawiliśmy błąd polegający na pominięciu atrybutu alt w znaczniku <img src> (co było dopuszczalne w HTMLu, ale jest zabronione w XHTML-u. Po utworzeniu strony, konwertujemy ją do postaci dokumentu XML jako zmienną $err. Następnie korzystając z metody statycznej PEAR::isError( sprawdzamy, czy zmienna ta zawiera komunikat o błędzie: jeżeli tak, to go wypisujemy przy pomocy metody $err->getmessage(. Przykład wykonania tego skryptu przedstawiamy na Rysunku 1. Manipulacja dokumentami XML Przejdźmy teraz do omówienia rozmaitych metod generowania i manipulacji dokumentami XML. Jak już wiemy, użycie argumentu Text podczas tworzenia instancji klasy powoduje, że skutkiem użycia metody toxml( będzie wygenerowanie XML-a w formie tekstowej (zserializowanej. Gdybyśmy zamiast Text podali XML_Tree, utworzony zostałby obiekt klasy XML_Tree, która jest dostępna jako osobny pakiet PEAR-owy. Co więcej, możemy złożyć dokument XML z kilku mniejszych bloków, co się często przydaje, np. gdy generujemy jego elementy korzystając z pętli czy instrukcji warunkowych. Jest to wręcz zalecane, gdyż umożliwia zachowanie przejrzystości zarówno kodu aplikacji generującej XML, jak i samego XML-a. Listing 3. Rezultat XHTML przykładu z Listingu 2 Bez ograniczeń możemy łączyć bloki wygenerowane z użyciem parametru Text podczas tworzenia instancji dokumentu. Nie będzie również problemu, jeżeli format jest inny (np. XML_Tree, o ile wszystkie bloki są tego samego formatu. Spójrzmy na Listing 6: łączymy na nim dwa bloki typu Text, wygenerowane przy użyciu utworzonej wcześniej instancji $x klasy (nie musimy podawać żadnych parametrów oprócz typu XML-a. <?xml version="1.0" encoding="utf-8" standalone="no" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/tr/ xhtml1/dtd/xhtml1-strict.dtd"> <html><head><title></title></head> <body><p>hello World!</p></body></html> Listing 4. Tworzymy instancję sprawdzającą zgodność XML z jego DTD XML_FASTCREATE_DOCTYPE_XHTML_1_0_STRICT, 'dtd' => 'xhtml_1_0_strict.dtd' Listing 5. Sprawdzamy zgodność XML z jego DTD 'doctype' => XML_FASTCREATE_DOCTYPE_XHTML_1_0_STRICT, 'dtd' => 'xhtml_1_0_strict.dtd' $x->head($x->title('', $x->body( $x->p($x->img('src' => 'soleil.png' if (PEAR::isError($err = $x->toxml( { echo nl2br(htmlspecialchars($err->getmessage( } Listing 6. Przykład konkatenacji (łączenia dwóch tagów $hello = $x->p('hello' $world = $x->p('world' $body = $x->body($hello,$world, $x->head($x->title('',$body Rysunek 2. Entytki umożliwiające zakodowanie znaków specjalnych w dokumentach XML 4
Projekty Transformacje XML XFC posiada opcję szybkiej transformacji, której zadaniem jest zamiana wybranych znaczników na inne. Przykładowo, możemy w ten sposób uprościć sobie składnię XML-a czy XHTML-a, oznaczając tagi po swojemu, w sposób dostosowany do naszych preferencji czy potrzeb programu, który z tych danych korzysta. Listing 7. Przykład zastosowania opcji translate XML_FASTCREATE_DOCTYPE_XHTML_1_0_STRICT, 'dtd' => 'xhtml_1_0_strict.dtd', 'translate' => 'news' => 'div', 'desc' => 'p', 'title' => '<h1 class="title"><span>', '</span></h1>', 'date' => '<span class="date">', '</span>' $x->head($x->_title('', $x->body($x->news( $x->title('news', $x->date('10-12-2005', $x->desc('blah blah blah' if (PEAR::isError($err = $x->toxml( { echo nl2br(htmlspecialchars($err->getmessage( } Listing 8. Wariant cytowany w Listing 7 bez opcji translate $x->div( $x->h1('class' => 'title',$x->span('news', $x->span('class' => 'date', '10-12-2005', $x->p('blah blah blah' Listing 9. Przykład zastosowania metody cdata( $x->style( 'type' => 'text/css', 'media' => 'all', $x->cdata("@import url('example.css'" Transformacja z XFC przyda się również przy konwersji z XML-a na HTML-a. Spójrzmy na Listing 7: przy tworzeniu instancji klasy w tablicy asocjacyjnej wpisujemy opcję translate, która z kolei otwiera tablicę znaczników do tłumaczenia.mamy tam tagi opisujące notkę informacyjną (<news>, <desc>, <title> i <date>, które będą zamienione Listing 10. Tworzymy instancję sprawdzającą zgodność XML z jego DTD poprzez program zewnętrzny XML_FASTCREATE_DOCTYPE_XHTML_1_0_STRICT, 'dtd' => 'xhtml_1_0_strict.dtd', 'file' => '/tmp/xfc.xml', 'exec' => 'xmllint --valid --noout /tmp/xfc.xml 2>&1', na oryginalne znaczniki XML-a. Zwróćmy szczególną uwagę na znacznik <title>: zostanie on przekonwertowany na cały zestaw tagów (<h1><span>...</span></h1>. Gdybyśmy nie korzystali z XFC w celu ułatwienia konwersji znaczników, aby osiągnąć ten sam efekt co na Listingu 7 musielibyśmy ręcznie wstawić wartości do odpowiednich tagów XML-a, jak na Listingu 8. Wracając do Listingu 7 zauważmy, że po zdefiniowaniu tablicy transformacji tworzymy kod XML metodą tradycyjną. Tam również wystąpi znacznik <title>, tyle że nie jest on tytułem naszej wiadomości, lecz standardowym tagiem <title> oznaczającym tytuł strony WWW i znanym z HTML-a oraz XHTML-a. Aby uniknąć jego konwersji (która nastąpiłaby zgodnie z regułami przetwarzania znaczników naszych wiadomości, poprzedzamy jego nazwę podkreśleniem (_title. Dzięki temu generowany będzie standardowy znacznik <title>..</title>. Opcje i metody XFC XFC zawiera kilka dodatkowych, przydatnych metod: comment( pozwala na dodawanie komentarzy w kodzie XML; ogranicznikami komentarza są oczywiście tagi <!-- i -->. Przykładowo, następujące użycie tej metody: $x->comment($x->p( Hello World! spowoduje wygenerowanie kodu: <!-- Hello World! -->. cdata( otacza wybraną zawartość znacznikami CDATA. Jest to niezbędne, kiedy musimy wprowadzić zawartość, która niekoniecznie jest zgodna z DTD, np. kod typu JavaScript czy jakiś rodzaj importu stylów w ramach strony XHTML. Bez CDATA zwyczajnie nie da się umieścić takiej zawartości w dokumencie XML. Przykład użycia cdata( przedstawiamy na Listingu 9. quote( metody tej używamy, aby przekonwertować wybrane znaki na entytki (ang. entities. Jest to konieczne, gdyż umieszczenie pewnych znaków (np. & w dokumentach XML jest niemożliwe i wygeneruje błąd. Metody quote( możemy używać w sposób zautomatyzowany, jeżeli zadeklarujemy ją podczas definiowania obiektu klasy jako true. W takiej sytuacji, jeśli nie chcemy używać tej opcji wobec określonych 5
części dokumentu, możemy wywołać opcję noquote( (Rysunek 2. UWA- GA: jeżeli tworzymy dokument XHTML, wyszukiwarka Microsoft Internet Explorer (wersja 6 i niższe nie rozpoznaje encji '. Aby uniknąć jej konwersji, musimy umieścić opcję apos w false. Należy też pamiętać o tym, że: Kod XML jest generowany bez formatowania. Oznacza to, że wszystkie znaczniki są wklejane i zagnieżdżane bez przechodzenia do nowej linii czy stosowania tabulacji. Jeżeli chcemy uczynić kod XML bardziej czytelnym, dodajmy opcję indent do true. Takie formatowanie może jednak sprawić, że dokumenty XML będą niezgodne z DTD. Niektóre nowe DTD, jak na przykład XHTML 1.1, bazujące na kilku kartotekach, jeszcze nie są tolerowane. Mimo to, za pomocą opcji file możemy wydać XFC polecenie utworzenia pliku tymczasowego i wykonania programu zewnętrznego za pomocą opcji exec, przeznaczonej do takiej analizy. Program xmllint doskonale sobie radzi z takimi problemami, a przykład jego użycia w ramach naszej aplikacji prezentujemy na Listingu 10. XFC ma kilka własnych, wbudowanych metod, co przy specyficznym sposobie traktowania znaczników (metoda ma taką samą nazwę, jak znacznik, do którego się odnosi powoduje problemy, jeżeli chcemy zdefiniować znaczniki o nazwach pokrywających się z nazwami tych metod. Aby uniknąć tego problemu i zmusić XFC do traktowania metody jako odnoszącej się do znacznika, przed nazwą tagu wstawiamy znak podkreślenia, np. _quote(. Wygoda użytkowania XFC Aby uprościć tworzenie projektu korzystającego z, zalecane jest, aby inicjowanie instancji tej klasy odbywało się w pliku, który będziemy dołączać do każdej ze stron. Inną opcją jest generowanie tego obiektu wewnątrz funkcji, która będzie wywoływana na końcu każdej strony. Pamiętajmy też, że generowanie XML-a zajmuje czas, przez co korzystanie z będzie wolniejsze, niż używanie gotowych, zapisanych na dysku (czy w bazie danych dokumentów XML. Aby przyspieszyć dostęp do nich (tylko w przypadku plików, możemy użyć narzędzia keszującego, np. PEAR::Cache_Lite. Konwersja dokumentów HTML Konwersję dokumentu HTML na XML ułatwi załączony w pakiecie skrypt HTML2XFC.php. Za jego pomocą możemy przekształcić cały plik (skrypt wywołujemy wtedy w linii poleceń albo wybrany fragment uruchamiamy wtedy HTML2XFC.php jako makro w wybranym edytorze programistycznym. W przypadku edytora vim wystarczy umieścić w ~/.vimrc linię: map,fc :!HTML2XFC.php<CR> wybrać do konwersji kod HTML i uruchomić makro wpisując,fc. Podsumowanie W tym artykule pokazaliśmy najciekawsze spośród podstawowych zastosowań. Jak widzimy, użycie tej biblioteki znacznie upraszcza tworzenie, manipulację i konwersję dokumentów XML. Nie bez znaczenia jest również to, że należy ona do repozytorium PEAR, do którego trafiają wyłącznie sprawdzone i działające projekty. Wraz z dodatkami typu XML_Tree, powinna zająć miejsce wśród niezbędnych narzędzi każdego programisty, który korzysta z XML-a. O autorze Guillaume Lecanu jest autorem pakietu programów XML FastCreate, rozwijanego od przeszło 12 lat. Jego pasja programowania zaczęła się od asemblera, którego nauczył go na domowym komputerze brat, twórca programów typu demo. Niedawno Lecanu stworzył własną firmę Noovea, która prezentuje pełnię jego możliwości. Kontakt z autorem: 6