<h3>Statyczne pliki XML</h3>

<p>
Pliki XML są plikami tekstowymi.
Zawierają one dane, 
których struktura jest ustalona znacznikami.
Przykładowy plik <span class="filename">data.xml</span>:
</p>

<pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;data&gt;
  &lt;dzien&gt;13&lt;/dzien&gt;
  &lt;miesiac&gt;maj&lt;/miesiac&gt;
  &lt;rok&gt;2004&lt;/rok&gt;
&lt;/data&gt;
</pre>

<p>
zostanie wyświetlony przez przeglądarkę 
<span class="program">Firefox</span> w postaci drzewa widocznego na rysunku 1.
Pierwsza linijka pliku XML:
</p>

<pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
</pre>

<p>
jest nazywana <em>prologiem</em>.
</p>


#####{$figures[1]}#####

<p>
Zwróć uwagę, że przed prologiem XML 
nie mogą znajdować się żadne białe znaki.
Jeśli powyżej prologu umieścisz kilka pustych linijek
(czy nawet pojedynczą spację),
to plik XML będzie błędny, o czym poinformuje cię przeglądarka
(rysunek 2).
</p>

#####{$figures[2]}#####

<p>
Pliki o rozszerzeniu <span class="filename">.xml</span>
są domyślnie opatrzone 
przez serwer <span class="program">Apache</span> nagłówkiem
HTTP:
</p>

<pre>
application/xml
</pre>

<p>
Możesz to sprawdzić korzystając z wtyczki 
<span class="program">HTTP Live Headers</span> (rysunek 3).
</p>

#####{$figures[3]}#####

<p>
Dokument RFC 3023
zatytułowany <em>&bdquo;XML Media Types&rdquo;</em>
wymienia dwa poprawne typy przeznaczone dla dokumentów XML.
Są to:
</p>

<pre>
application/xml
text/xml
</pre>

<p>
W obu przypadkach RFC zaleca dołączanie informacji o kodowaniu znaków.
</p>

<p>
Różnica pomiędzy typami 
<span class="variable">application/xml</span>
oraz 
<span class="variable">text/xml</span> 
jest marginalna i dotyczy 
aplikacji, które nie potrafią interpretować 
struktury konkretnego dokumentu
(ang. <em>casual user</em>; por. ostatni akapit na stronie 5 
dokumentu RFC 3023).
W przypadku niepewności co do wyboru typu,
cytowany dokument RFC sugeruje 
(por. nota na stronie 6 dokumentu RFC 3023)
stosowanie
typu <span class="variable">application/xml</span>.
Jak podaje Wikipedia 
(<a href="http://en.wikipedia.org/wiki/Xml">http://en.wikipedia.org/wiki/Xml</a>),
typ <span class="variable">text/xml</span> został wycofany.
</p>

<p>
W dalszej części będę konsekwentnie stosował typ:
</p>

<pre>
application/xml; charset="utf-8"
</pre>

<h3>Generowanie dokumentu XML w PHP</h3>

<p>
Generowanie dokumentu XML 
w języku PHP wymaga jedynie zmiany nagłówka HTTP.
Służy do tego funkcja <span class="filename">header()</span>.
Skrypt:
</p>

<pre>
&lt;?php

header('Content-type: application/xml; charset="utf-8"');

?&gt;&lt;?xml version="1.0" encoding="utf-8"?&gt;

&lt;data&gt;
  &lt;dzien&gt;13&lt;/dzien&gt;
  &lt;miesiac&gt;maj&lt;/miesiac&gt;
  &lt;rok&gt;2004&lt;/rok&gt;
&lt;/data&gt;
</pre>

<p>
wygeneruje dokument XML, który zostanie 
wyświetlony przez przeglądarkę WWW tak, jak na rysunku 1.
</p>


<p>
Pamiętaj o tym, że w powyższym przykładzie przed 
znacznikiem otwierającym PHP (tj. <span class="variable">&lt;?php</span>)
oraz przed prologiem XML 
nie mogą znaleźć się żadne białe znaki.
</p>


<p>
Uruchomienie powyższego przykładu 
wymaga także wyłączenia 
opcji konfiguracyjnej PHP
<span class="variable">short_open_tag</span>.
W pliku <span class="filename">php.ini</span> umieść wpis:
</p>

<pre>
short_open_tag = Off
</pre>

<p>
Spowoduje on, że krótkie znaczniki PHP:
</p>

<pre>
&lt;?
  ...
?&gt;
</pre>

<p>
przestaną działać.
</p>

<p>
Jeśli opcja <span class="variable">short_open_tag</span> 
pozostaje włączona, wówczas prolog XML
będzie powodował błąd PHP:
</p>

<pre>
Parse error: syntax error, unexpected T_STRING in ...
</pre>

<p>
przedstawiony na rysunku 4.
</p>

#####{$figures[4]}#####

<h3><span class="variable">echo</span> &mdash; drukowanie kodu XML</h3>

<p>
Zawartość dokumentu XML może być generowana 
instrukcją <span class="variable">echo</span>.
W takim przypadku 
skrypt musi wydrukować zarówno znaczniki 
XML jak i zawartą w nich treść:
</p>

<pre>
&lt;?php
header('Content-type: application/xml; charset="utf-8"');

echo '&lt;?xml version="1.0" encoding="utf-8"?&gt;';
echo '&lt;data&gt;';
echo '&lt;dzien&gt;13&lt;/dzien&gt;';
echo '&lt;miesiac&gt;maj&lt;/miesiac&gt;';
echo '&lt;rok&gt;2004&lt;/rok&gt;';
echo '&lt;/data&gt;';
?&gt;
</pre>

<p>
Oczywiście do generowania treści
można wykorzystać dowolne funkcje PHP, np. <span class="variable">date()</span>:
</p>

<pre>
echo '&lt;dzien&gt;' . date('j') . '&lt;/dzien&gt;';
echo '&lt;miesiac&gt;' . date('n') . '&lt;/miesiac&gt;';
echo '&lt;rok&gt;' . date('Y') . '&lt;/rok&gt;';
</pre>
 
 
<h3>Generowanie XML na podstawie tablicy</h3>

<p>
Kolejny przykład demonstruje, w jaki sposób
wygenerować plik XML na podstawie tablicy.
</p>

<p>
W skrypcie PHP zdefiniuj tablicę <span class="variable">$owoce</span>:
</p>

<pre>
$owoce = array('jabłko', 'gruszka', 'banan', ...);
</pre>



<p>
W pętli <span class="variable">foreach</span> 
przetwórz wszystkie elementy tablicy:
</p>


<pre>
foreach ($owoce as $owoc) {
    echo '&lt;owoc&gt;';
    echo $owoc;
    echo '&lt;/owoc&gt;';    
}
?>

</pre>

<p>
Powyższa pętla drukuje serię elementów <span class="variable">&lt;owoc&gt;...&lt;/owoc&gt;</span>:
</p>

<pre>
&lt;owoc&gt;jabłko&lt;/owoc&gt;
&lt;owoc&gt;gruszka&lt;/owoc&gt;
&lt;owoc&gt;banan&lt;/owoc&gt;
...
</pre>

<p>
Pamiętaj o zmianie nagłówka HTTP,
prologu XML oraz o 
korzeniu <span class="variable">&lt;owoce&gt;...&lt;/owoce&gt;</span>.
Kompletny skrypt jest przedstawiony na listingu 1.
</p>

#####{$listings[1]}#####

<h3>Generowanie XML na podstawie pliku tekstowego</h3>


<p>
Plik tekstowy o nazwie 
<span class="filename">owoce.txt</span> zawiera 
nazwy owoców.
Każdy wiersz pliku zawiera 
nazwę jednego owocu:
</p>

<pre>
jabłko
gruszka
banan
...
</pre>

<p>
W celu wygenerowania danych w formacie XML
należy odpowiednio przekształcić plik tekstowy.
</p>

<p>
Rozpoczynamy od wczytania całego pliku.
Następnie w pętli <span class="variable">foreach</span> 
przetwarzamy poszczególne wiersze:
</p>

<pre>
$owoce = file('owoce.txt');
foreach ($owoce as $owoc) {
    echo '&lt;owoc&gt;';
    echo trim($owoc);
    echo '&lt;/owoc&gt;';    
}
</pre>

<p>
Wewnątrz pętli podobnie jak poprzednio drukujemy
elementy <span class="variable">&lt;owoc&gt;...&lt;/owoc&gt;</span>.
Kompletny skrypt jest przedstawiony na listingu 2.
</p>

#####{$listings[2]}#####

<h3>Zapisywanie kodu XML do pliku</h3>


<p>
W celu zapisania 
wygenerowanego kodu do pliku
należy przechwycić XML do zmiennej,
a następnie wywołać funkcję <span class="variable">file_put_contents()</span>.
Kompletny skrypt jest przedstawiony na listingu 3.
Zauważ, że skrypt zapisujący XML do pliku nie 
wywołuje funkcji <span class="variable">header()</span>.
</p>

#####{$listings[3]}#####

<p>
Skrypt taki można uruchomić korzystając z przeglądarki
lub wywołując skrypt 
<span class="filename">generuj-xml.bat</span> o zawartości:
</p>

<pre>
php -f owoce.php
</pre>

<p>
Skrypt <span class="filename">.bat</span> może zostać uruchomiony w dowolnym
folderze, a nie tylko wewnątrz folderu <span class="filename">htdocs/</span>
widocznego w ramach usługi WWW.
Podany plik wsadowy działa 
poprawnie wyłącznie wtedy, 
gdy interpretator <span class="filename">php.exe</span>
znajduje się w aktualnych ścieżkach dostępu
konsoli.
</p>

<h3>Generowanie XML z wykorzystaniem <span class="program">Smarty</span></h3>



<p>
Stosując szablony <span class="program">Smarty</span> w skryptach generujących XML otrzymamy 
maksymalnie zwięzły kod.
W skrypcie należy wyłącznie przygotować dane
odczytując je z pliku
po czym przekazać je do szablonu:
</p>



<pre>
require_once 'Smarty.class.php';

$plk = file('owoce.txt');
$s = new Smarty;
$s-&gt;assign('owoce', $plk);
$s-&gt;display('owoce.tpl');
</pre>

<p>
Cały kod XML znajduje się
w pliku <span class="filename">owoce.tpl</span>.
W celu wygenerowania 
listy elementów <span class="variable">&lt;owoc&gt;...&lt;/owoc&gt;</span> 
stosujemy pętlę <span class="variable">section</span>,
która przetwarza tablicę <span class="variable">$owoce</span>:
</p>

<pre>
{section name=i loop=$owoce}
  &lt;owoc&gt;{$owoce[i]|trim}&lt;/owoc&gt;
{/section}
</pre>


<p>
Kompletny przykład jest przedstawiony na listingu 4.
</p>


#####{$listings[4]}#####

<h3><span class="program">Smarty</span>: zapisywanie dokumentów XML</h3>


<p>
Chcąc wykorzystać szablony <span class="program">Smarty</span> w skrypcie, 
który zapisuje dokument XML do pliku,
należy użyć metody <span class="variable">fetch()</span>.
Zwraca ona napis, zawierający przetworzony szablon.
Otrzymany wynik zapisujemy do pliku:
</p>



<pre>
$plk = file('owoce.txt');
$s = new Smarty;
$s-&gt;assign('owoce', $plk);
$wynik = $s-&gt;fetch('owoce.tpl');

file_put_contents('owoce.xml', $wynik);
</pre>



<h3>Konwersja pliku tekstowego do formatu XML</h3>

<p>
W kolejnym przykładzie przekształcimy nieco
bardziej skomplikowany plik tekstowy do formatu XML.
</p>


<p>
Plik o nazwie 
<span class="filename">nobel.txt</span> zawiera 
informacje o laureatach Nagrody Nobla.
Każdy wiersz pliku 
zawiera zestawienie nagród wybranego rocznika.
W pierwszym wierszu znajdują się dane na temat roku 1901:
</p>

<pre>
1901|W.C. Roentgen (N)|J.H. van't Hoff (Hol)|...
</pre>

<p>
Separatorem w pliku jest znak <span class="variable">|</span>, 
poszczególne kolumny to:
rok, laureat lub laureaci w dziedzinie fizyki, 
chemii, medycyny, literatury
oraz nagrody pokojowej.
</p>

<p>
Rozpoczynamy od wczytania całego pliku
po czym w pętli 
<span class="variable">foreach</span> przetwarzamy poszczególne linie:
</p>

<pre>
$plik = file('nobel.txt');
foreach ($plik as $linia) {
    $e = explode('|', trim($linia)); 
    $wynik .= "
        &lt;nagroda&gt;
            &lt;rok&gt;$e[0]&lt;/rok&gt;
            &lt;fizyka&gt;$e[1]&lt;/fizyka&gt;
            &lt;chemia&gt;$e[2]&lt;/chemia&gt;
            ...
        &lt;/nagroda&gt;
    ";
}
</pre>

<p>
Każda z linii jest krojona znakiem <span class="variable">|</span> 
(funkcja <span class="variable">explode()</span>).
Uzyskane informacje (tj. <span class="variable">$e[0]</span>, 
<span class="variable">$e[1]</span>, itd.)
są umieszczane w napisie 
pomiędzy znacznikami <span class="variable">&lt;rok&gt;...&lt;/rok&gt;</span>,
<span class="variable">&lt;fizyka&gt;...&lt;/fizyka&gt;</span>, 
itd.
Otrzymany element <span class="variable">&lt;nagroda&gt;...&lt;/nagroda&gt;</span>
dołączamy na końcu zmiennej
<span class="variable">$wynik</span>.
</p>


<p>
Na koniec zmienną <span class="variable">$wynik</span> 
zapisujemy do pliku <span class="filename">nobel.xml</span>.
</p>



<h3>Konwersja pliku tekstowego do XML z wykorzystaniem <span class="program">Smarty</span></h3>

<p>
Użycie szablonów <span class="program">Smarty</span> oraz funkcji 
<span class="variable">string2HArray()</span> skraca kod PHP.
Rola skryptu <span class="filename">txt2xml.php</span> sprowadza się do
odczytania pliku <span class="filename">mecze.txt</span>,
pokrojenia go
oraz przetworzenia szablonu:
</p>


<pre>
&lt;?php
require_once 'vh-array.inc.php';
require_once 'Smarty.class.php';

$p = file_get_contents('mecze.txt');
$k = string2HArray($p);

$s = new Smarty;
$s-&gt;assign('mecze', $k[1]);
$wynik = $s-&gt;fetch('index.tpl');

file_put_contents('mecze.xml', $wynik);
?&gt;
</pre>


<p>
Do odczytania pliku użyta jest funkcja 
<span class="variable">file_get_contents()</span>.
Krojenie wykonuje funkcja <span class="variable">string2HArray()</span>.
Przetworzony szablon
uzyskujemy wywołując metodę <span class="variable">fetch()</span>.
Zwrócony wynik zapisujemy do pliku funkcją <span class="variable">file_put_contents()</span>.
</p>

<h3>Przykład praktyczny</h3>


<h4>Strona z jednym menu</h4>


<p>
Plik <span class="filename">sitemap.xml</span>
oraz kanały RSS i Atom będą generowane dla przykładowej 
witryny WWW zawierającej jedno menu.
Witryna posiada cztery strony o adresach URL:
</p>


<pre>
index.php?id=2
index.php?id=3
index.php?id=4
index.php?id=5
</pre>

<p>
Menu witryny powstaje na podstawie pliku
<span class="filename">menu.txt</span> o zawartości:
</p>

<pre>
Lorem|lorem.txt|2
Ipsum|ipsum.txt|3
Dolor|dolor.txt|4
Sit amet|sit-amet.txt|5
</pre>

<p>
Separatorem pól jest znak <span class="variable">|</span>.
Kolejne kolumny to:
</p>

<ul>
<li>tytuł,</li>
<li>nazwa pliku tekstowego zawierającego treść,</li>
<li>identyfikator użyty w adresie URL.</li>
</ul>


<p>
Menu witryny ma postać:
</p>


<pre>
&lt;ul id="menu"&gt;
  &lt;li&gt;&lt;a href="index.php?id=2"&gt;Lorem&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="index.php?id=3"&gt;Ipsum&lt;/a&gt;&lt;/li&gt;
  ...
&lt;/ul&gt;
</pre>


<p>
Po wybraniu pierwszej opcji,
w treści witryny zostaje 
umieszczona zawartość pliku <span class="filename">lorem.txt</span>.
Wybór drugiej opcji skutkuje załadowaniem
pliku <span class="filename">ipsum.txt</span>, itd.
Nazwy plików z treścią 
stanowią druga kolumnę pliku <span class="filename">menu.txt</span>.
</p>



<h4>Mapa witryny: <span class="filename">sitemap.xml</span></h4>

<p>
Mapa witryny jest statycznym plikiem 
XML o strukturze opisanej na witrynie
<a href="http://www.sitemaps.org">http://www.sitemaps.org</a>.
Plik ten ułatwia robotom internetowym indeksację witryny.
</p>

<p>
Za wygenerowanie <span class="filename">sitemap.xml</span> 
odpowiada skrypt <span class="filename">generuj-sitemap.php</span>.
Mapa witryny jest generowana na podstawie pliku <span class="filename">menu.txt</span>.
Utworzenie pliku <span class="filename">sitemap.xml</span> sprowadza się do
odczytania pliku <span class="filename">menu.txt</span>
(funkcja <span class="variable">file_get_contents()</span>),
pokrojenia go (funkcja <span class="variable">string2VArray()</span>),
przetworzenia szablonu 
oraz zapisania 
wyniku do pliku (funkcja <span class="variable">file_put_contents()</span>):
</p>

<pre>
$p = file_get_contents('dane/menu.txt');
$menu = string2VArray($p);

$s = new Smarty;
$s-&gt;assign('menu', $menu[2]);
$mapa = $s-&gt;fetch('sitemap.tpl');

file_put_contents('sitemap.xml', $mapa);
</pre>

<p>
Mapa dostępna pod adresem <a href="http://www.gajdaw.pl/sitemap.xml">http://www.gajdaw.pl/sitemap.xml</a>
jest generowana w podobny sposób.
Różnica polega na tym, że 
do szablonu przekazywana jest tablica adresów URL,
która jest pobierana z bazy danych.
</p>

<p>
Jeśli generowanie pliku <span class="filename">sitemap.xml</span>
połączysz z dodawaniem nowych treści do strony,
wówczas Twoja mapa będzie zawsze aktualna.
Fragment wygenerowanej mapy jest przedstawiony na listingu 5.
</p>


#####{$listings[5]}#####


<h4>Kanał RSS</h4>

<p>
Składnia XML-owych plików z kanałami RSS 
jest opisana w specyfikacji
dostępnej 
pod adresem
<a href="http://cyber.law.harvard.edu/rss/rss.html">http://cyber.law.harvard.edu/rss/rss.html</a>.
</p>


<p>
Pojedynczy wpis kanału RSS może zawierać:
</p>

<ul>
<li>tytuł,</li>
<li>adres URL,</li>
<li>datę publikacji,</li>
<li>oraz opis.</li>
</ul>



<p>
Na podstawie pliku <span class="filename">menu.txt</span> możemy ustalić tytuł oraz adres URL.
Jeśli kanał RSS ma zawierać datę publikacji oraz skrócony opis,
to witrynę z jednym menu należy rozbudować, dodając kolejny plik danych.
</p>


<p>
W pliku <span class="filename">rss.txt</span> umieszczone zostały daty oraz opis:
</p>


<pre>
Mon, 7 January 2008 8:00:00 +0100|Opis pierwszy...
Mon, 8 January 2008 18:30:00 +0100|Opis drugi...
Mon, 9 January 2008 6:50:00 +0100|Opis trzeci...
Mon, 10 January 2008 11:05:00 +0100|Opis czwarty...
</pre>



<p>
Skrypt <span class="filename">rss.php</span> kolejno:
</p>

<ul>
<li>odczytuje i kroi plik <span class="filename">menu.txt</span>,</li>
<li>odczytuje i kroi plik <span class="filename">rss.txt</span>,</li>
<li>przekazuje dane do szablonu i przetwarza szablon:</li>
</ul>



<pre>
$menu = string2VArray(file_get_contents('dane/menu.txt'));
$rss = string2VArray(file_get_contents('dane/rss.txt'));

$s = new Smarty;
$s-&gt;assign('menu', $menu[2]);
$s-&gt;assign('rss', $rss[2]);
$s-&gt;display('rss.tpl');
</pre>

<p>
Oczywiście dwa pliki <span class="filename">menu.txt</span> oraz <span class="filename">rss.txt</span>
można zastąpić jednym, który będzie zawierał
cztery kolumny potrzebne do wygenerowania RSS:
tytuł,
datę publikacji,
adres URL
oraz opis.
W ten sposób dodanie nowych treści do strony
będzie odbywało się w jednym miejscu.
Wszystkie nowości będą automatycznie dostępne poprzez kanał RSS.
Generowany RSS został w zarysie przedstawiony 
na listingu 6.
</p>

#####{$listings[6]}#####

<h4>Kanał Atom</h4>

<p>
Generowanie kanału Atom (RFC 4287), zawierającego 
w każdym wpisie tytuł, adres URL,
datę publikacji
oraz opis
przebiega identycznie jak generowanie kanału RSS.
Oba rozwiązania różnią się wyłącznie szablonem <span class="filename">.tpl</span>
oraz formatem zapisu daty.
Fragment generowanego kanału Atom jest przedstawiony na listingu 7.
</p>


#####{$listings[7]}#####