4. diel - Práca so súbormi XML v PHP
V predchádzajúcej lekcii, Práca so súbormi CSV v PHP , sme sa zoznámili s prácou so súbormi pomocou tzv. Resources, so súbormi CSV a naše znalosti sme využili v praktickom príklade.
V tejto lekcii sa pozrieme na prácu s ďalším formátom, ktorý sme si predstavili v úvodnej lekcii tohto kurzu - XML. Ukážeme si dva spôsoby, ako k XML súborom pristupovať:
- metódou SAX (S IMPL A PI
for X ML) - triedami
XMLWriter
aXMLReader
a - triedou
SimpleXML
.
Oproti minulým lekciám, kde sme pracovali iba s funkciami, sa teraz presunieme k objektovo orientovanému programovaniu.
Generovanie XML metódou
SAX - trieda XMLWriter
Inšpirujeme sa článkom Úvod do XML a zápis Saxe z lekcie o jazyku C# a napíšeme podobný skript v PHP. Použijeme aj rovnaká vstupné dáta:
$uzivatele = new Uzivatele(); $uzivatele->Add(new Uzivatel("Pavel Slavík", 22, new Datum(2000, 3, 21))); $uzivatele->Add(new Uzivatel("Jan Novák", 31, new Datum(2012, 10, 30))); $uzivatele->Add(new Uzivatel("Tomáš Marný", 16, new Datum(2011, 1, 12)));
Pre generovanie XML metódou SAX je použijeme triedu
XMLWriter
:
$out = new XMLWriter(); $out->openMemory(); $out->startDocument('1.0', 'UTF-8'); $out->setIndent(TRUE); $uzivatele->exportXML($out); $out->endDocument(); echo $out->outputMemory(TRUE);
Najzaujímavejšie metódou je pravdepodobne exportXML()
objektu
$uzivatele
. Ostatné príkazy len nastavujú parametre výstupu
(verziu a kódovanie XML, odsadzovanie - indentaci, ...).
Trieda Uzivatele
má dve metódy:
- prvý pre pridanie ďalšieho používateľa a
- druhá pre export výsledného XML:
class Uzivatele { private $seznam = array(); public function Add($uzivatel) { $this->seznam[] = $uzivatel; } public function exportXML($out) { $out->startElement('uzivatele'); foreach($this->seznam as $uzivatel) { $uzivatel->exportXML($out); } $out->endElement(); } }
Trieda Uzivatel
obsahuje 3 atribúty, ale nie je problém si
podľa potreby pridať ďalšie:
class Uzivatel { private $jmeno; private $vek; private $registrovan; public function __construct($jmeno, $vek, $registrovan) { $this->jmeno = $jmeno; $this->vek = $vek; $this->registrovan = $registrovan; } public function exportXML($out) { $out->startElement('uzivatel'); $out->writeAttribute('vek', $this->vek); $out->writeElement('jmeno', $this->jmeno); $this->registrovan->exportXML($out); $out->endElement(); } }
Môžeme si všimnúť, že premenná pre jmeno
je pridaná ako
element, ale $vek
len ako atribút. V reáli by bolo výhodnejšie
použiť namiesto veku rok narodenia, ale chcel som, aby dátová štruktúra
bola podobná ako v prepojenom článku.
Privátne objekt $registrovan
ukladá dátum v troch číslach.
Je to len ukážka, ako pracovať s vnorenými objektmi. Celé dátum by sme
jednoduchšie uložili ako jednu hodnotu:
class Datum { private $den, $mesic, $rok; public function __construct($rok, $mesic, $den) { $this->den = $den; $this->mesic = $mesic; $this->rok = $rok; } public function exportXML($out) { $out->writeElement('registrovan', "$this->den.$this->mesic.$this->rok"); } }
To je všetko. Po spustení skriptu obdržíme nasledujúce výsledok:
<?xml version="1.0" encoding="UTF-8"?> <uzivatele> <uzivatel vek="22"> <jmeno>Pavel Slavík</jmeno> <registrovan>21.3.2000</registrovan> </uzivatel> <uzivatel vek="31"> <jmeno>Jan Novák</jmeno> <registrovan>30.10.2012</registrovan> </uzivatel> <uzivatel vek="16"> <jmeno>Tomáš Marný</jmeno> <registrovan>12.1.2011</registrovan> </uzivatel> </uzivatele>
SAX je rýchlou a pamäťovo nenáročnú metódou, ako priamo vygenerovať dokument XML alebo XHTML. Ak však budeme chcieť výstup ďalej spracovávať šablonovacím systémom, bude výhodnejšie použiť DOM, s ktorým šablónovacích systémami priamo pracujú.
Pozn .: Vstupné dáta pre triedu XMLWriter musí byť v kódovaní UTF-8. Inak nebude fungovať správne po slovensky. Výstupný kódovanie je možné zvoliť podľa potreby.
Čítanie XML metódou SAX -
trieda XMLReader
Opäť sa inšpirujeme článkom Čítanie XML Saxe v C# a napíšeme si podobnú aplikáciu v jazyku PHP. Použijeme XML dáta, ktoré sme si vygenerovali pred chvíľou:
<?xml version="1.0" encoding="UTF-8"?> <uzivatele> <uzivatel vek="22"> <jmeno>Pavel Slavík</jmeno> <registrovan>21.3.2000</registrovan> </uzivatel> <uzivatel vek="31"> <jmeno>Jan Novák</jmeno> <registrovan>30.10.2012</registrovan> </uzivatel> <uzivatel vek="16"> <jmeno>Tomáš Marný</jmeno> <registrovan>12.1.2011</registrovan> </uzivatel> </uzivatele>
Tentokrát je úloha oveľa ťažšie, ako generovanie XML metódou SAX. Musíme totiž čítať jednotlivé tokeny a ich postupnosťou riadiť ukladanie dát. Záujemcom odporúčam metódu použiť iba v špecifických prípadoch, napríklad keď z obrovského XML súboru potrebujú len vybrať niektoré dáta. V ostatných prípadoch je výhodnejšie použiť metódu DOM:
$data = new XMLReader(); $data->open('data.xml'); while($data->read()) { switch($data->name) { case 'uzivatele': $uzivatele = new Uzivatele($data); break; } } echo $uzivatele, "\n";
Pozornejší programátori si všimli, že vo switch
používame
len 1 × case
miesto kratšieho zápisu if
. Je tomu
tak preto, že pre čítanie používame metodiku regulárneho automatu, u
ktorej je použitie switch
obvyklé. Pri parsovanie zložitejšieho
dokumente určite oceníme ľahkú rozšíriteľnosť:
class Uzivatele { private $seznam = array(); public function __construct($data) { while($data->read()) { switch($data->name) { case 'uzivatel': if($data->nodeType == XMLReader::ELEMENT) { $this->seznam[] = new Uzivatel($data); } break; case 'uzivatele': return; } } } public function __toString() { $out = array(); foreach($this->seznam as $uzivatel) { $out[] = $uzivatel->__toString(); } return implode("\n", $out); } }
Ani tu použitie switch
nevyzerá príliš vábne, pri použití
zložitejšie štruktúry XML však iste oceníme jednoduchosť pridania
ďalších elementov:
class Uzivatel { private $jmeno; private $vek; private $registrovan; public function __construct($data) { $this->vek = $data->getAttribute('vek'); while($data->read()) { switch($data->name) { case 'jmeno': $data->read(); $this->jmeno = $data->value; $data->read(); break; case 'registrovan': $data->read(); $this->registrovan = $data->value; $data->read(); break; case '#text': break; default: return; } } } public function __toString() { return sprintf("%-20s %2d %10s", $this->jmeno, $this->vek, $this->registrovan); } }
Nakoniec je najkomplikovanejšie trieda Uzivatel
. Najprv ju
prečítame atribút vek
elementu uzivatel
. Potom ju
prečítame aj obsah elementov jmeno
a registrovan
. Ak
parser narazí na neznámy element, je konštruktor ukončený. Pseudoelement
#text
obsahuje biele znaky, ktoré sú medzi elementy zdrojového
XML a ktorých sa potrebujeme zbaviť.
Metódy __toString()
sú určené na diagnostické výstupu. Po
spustení skriptu obdržíme nasledujúce výsledok:
Pavel Slavík 22 21.3.2000 Jan Novák 31 30.10.2012 Tomáš Marný 16 12.1.2011
Uvedený príklad nie je naprogramovaný úplne najčistejšie. Určite by sa našla kombinácia validných vstupných dát, ktorá by neprešla. Mala to byť len ukážka, že aj v PHP je možné použiť metódu SAX pre čítanie dokumentov XML.
Čítanie XML triedou
SimpleXML
Hovorili sme o tom, že čítanie XML metódou SAX nie je pre bežné
použitie vhodné. Teraz si ukážeme ten lepší spôsob pomocou triedy
SimpleXML
.
Trieda SimpleXML
je určená pre jednoduchú konverziu dokumentu
XML do objektov v PHP. Na rozdiel od triedy XMLReader
však
dokument nečítame v cykle po jednotlivých elementoch, ale načítame ho celý
do objektovej štruktúry. To je veľmi výhodné, pretože najpomalší úkon
vykonáva štandardná knižnica, ktorá je pre tento účel
optimalizovaná.
Použijeme opäť rovnaké dáta:
<?xml version="1.0" encoding="UTF-8"?> <uzivatele> <uzivatel vek="22"> <jmeno>Pavel Slavík</jmeno> <registrovan>21.3.2000</registrovan> </uzivatel> <uzivatel vek="31"> <jmeno>Jan Novák</jmeno> <registrovan>30.10.2012</registrovan> </uzivatel> <uzivatel vek="16"> <jmeno>Tomáš Marný</jmeno> <registrovan>12.1.2011</registrovan> </uzivatel> </uzivatele>
Skript Vyhlasujúca dáta je veľmi krátky. Tu nám oproti metóde SAX
stačí vytvoriť iba jednu triedu Uzivatele
:
<?php $data = new Uzivatele('data.xml'); echo $data, "\n"; class Uzivatele { private $seznam; public function __construct($souborXML) { $this->seznam = new SimpleXMLElement($souborXML, NULL, TRUE); } public function __toString() { $out = array(); foreach($this->seznam as $uzivatel) { $out[] = sprintf("%-20s %2d %10s", $uzivatel->jmeno, $uzivatel['vek'], $uzivatel->registrovan); } return implode("\n", $out); } }
Podľa potreby môžeme samozrejme doplniť metódy pre vyhľadanie užívateľa, overenie hesla a pod. Pre náš účel postačí obyčajný výpis zoznamu používateľov. Po spustení skriptu sa v prehliadači objavia:
Pavel Slavík 22 21.3.2000 Jan Novák 31 30.10.2012 Tomáš Marný 16 12.1.2011
Ako vidíme, čítanie dokumentu triedou SimpleXMLElement
je
oveľa jednoduchšie, ako čítanie metódou SAX. Táto trieda je tiež asi 10
× rýchlejší, než XMLReader
a lepšie zdokumentovaná. Pre
spracovanie bežných dokumentov XML je preto omnoho výhodnejšie.
Kompletný kód všetkých príkladov z tejto lekcie nájdete ako vždy k stiahnutiu dole pod článkom:-)
V nasledujúcom cvičení, Riešené úlohy k 3.-4. lekciu práca so súbormi v PHP, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 85x (3.65 kB)
Aplikácia je vrátane zdrojových kódov v jazyku PHP