IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

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 a XMLReader 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

 

Predchádzajúci článok
Práca so súbormi CSV v PHP
Všetky články v sekcii
Práca so súbormi v PHP
Preskočiť článok
(neodporúčame)
Riešené úlohy k 3.-4. lekciu práca so súbormi v PHP
Článok pre vás napísal Kit
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Jsem spokojeným uživatelem operačních systémů založených na linuxovém jádře. Zejména openSUSE a Ubuntu. Pro psaní veškerých textů a programů používám vynikající textový editor Vim. Aplikace se snažím psát vždy v tom nejvhodnějším programovacím jazyk...
Aktivity