BLACK FRIDAY! Slevy až 80 % jsou všude. Tak je nepropásni a přejdi do rostoucího IT oboru!
BF Sales

Lekce 4 - Práce se soubory XML v PHP

V předchozí lekci, Práce se soubory CSV v PHP, jsme se seznámili s prací se soubory pomocí tzv. resources, se soubory CSV a naše znalosti jsme využili v praktickém příkladu.

V této lekci se podíváme na práci s dalším formátem, který jsme si představili v úvodní lekci tohoto kurzu – XML. Ukážeme si dva způsoby, jak k XML souborům přistupovat:

  • metodou SAX (Simple API for XML) – třídami XMLWriter a XMLReader a
  • třídou SimpleXML.

Oproti minulým lekcím, kde jsme pracovali pouze s funkcemi, se nyní přesuneme k objektově orientovanému programování.

Generování XML metodou SAX – třída XMLWriter

Inspirujeme se článkem Úvod do XML a zápis SAXem z lekce o jazyce C# a napíšeme podobný skript v PHP. Použijeme i stejná vstupní data:

$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)));

Pro generování XML metodou SAX je použijeme třídu XMLWriter:

$out = new XMLWriter();
$out->openMemory();
$out->startDocument('1.0', 'UTF-8');
$out->setIndent(TRUE);

$uzivatele->exportXML($out);

$out->endDocument();
echo $out->outputMemory(TRUE);

Nejzajímavější metodou je nejspíše exportXML() objektu $uzivatele. Ostatní příkazy pouze nastavují parametry výstupu (verzi a kódování XML, odsazování – indentaci, …).

Třída Uzivatele má dvě metody:

  • první pro přidání dalšího uživatele a
  • druhá pro 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();
  }
}

Třída Uzivatel obsahuje 3 atributy, ale není problém si podle potřeby přidat další:

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šimnout, že proměnná pro jmeno je přidána jako element, ale $vek jen jako atribut. V reálu by bylo výhodnější použít místo věku rok narození, ale chtěl jsem, aby datová struktura byla podobná jako v odkazovaném článku.

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Privátní objekt $registrovan ukládá datum ve třech číslech. Je to jen ukázka, jak pracovat s vnořenými objekty. Celé datum bychom jednodušeji uložili jako 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še. Po spuštění skriptu obdržíme následující výsledek:

<?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 rychlou a paměťově nenáročnou metodou, jak přímo vygenerovat dokument XML nebo XHTML. Pokud však budeme chtít výstup dále zpracovávat šablonovacím systémem, bude výhodnější použít DOM, se kterým šablonovací systémy přímo pracují.

Pozn.: Vstupní data pro třídu XMLWriter musí být v kódování UTF-8. Jinak nebude fungovat správně česky. Výstupní kódování je možné zvolit dle potřeby.

Čtení XML metodou SAX – třída XMLReader

Opět se inspirujeme článkem Čtení XML SAXem v C# a napíšeme si podobnou aplikaci v jazyce PHP. Použijeme XML data, která jsme si vygenerovali před chvílí:

<?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 mnohem obtížnější, než generování XML metodou SAX. Musíme totiž číst jednotlivé tokeny a jejich posloupností řídit ukládání dat. Zájemcům doporučuji metodu použít pouze ve specifických případech, třeba když z obrovského XML souboru potřebují jen vybrat některá data. V ostatních případech je výhodnější použít metodu DOM:

$data = new XMLReader();
$data->open('data.xml');

while($data->read()) {
  switch($data->name) {
    case 'uzivatele':
      $uzivatele = new Uzivatele($data);
      break;
  }
}

echo $uzivatele, "\n";

Pozornější programátoři si všimli, že ve switch používáme jen 1× case místo kratšího zápisu if. Je tomu tak proto, že pro čtení používáme metodiku regulárního automatu, u které je použití switch obvyklé. Při parsování složitějšího dokumentu jistě oceníme snadnou rozšiřitelnost:

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 zde použití switch nevypadá příliš vábně, při použití složitější struktury XML však jistě oceníme snadnost přidání dalších elementů:

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);
  }
}

Nakonec je nejkomplikovanější třída Uzivatel. Nejprve ji přečteme atribut vek elementu uzivatel. Poté ji přečteme i obsah elementů jmeno a registrovan. Pokud parser narazí na neznámý element, je konstruktor ukončen. Pseudoelement #text obsahuje bílé znaky, které jsou mezi elementy zdrojového XML a kterých se potřebujeme zbavit.

Metody __toString() jsou určeny k diagnostickému výstupu. Po spuštění skriptu obdržíme následující výsledek:

Pavel Slavík        22  21.3.2000
Jan Novák           31  30.10.2012
Tomáš Marný         16  12.1.2011

Uvedený příklad není naprogramován úplně nejčistěji. Určitě by se našla kombinace validních vstupních dat, která by neprošla. Měla to být jen ukázka, že i v PHP je možné použít metodu SAX pro čtení dokumentů XML.

Čtení XML třídou SimpleXML

Mluvili jsme o tom, že čtení XML metodou SAX není pro běžné použití vhodné. Nyní si ukážeme ten lepší způsob pomocí třídy SimpleXML.

Třída SimpleXML je určena pro jednoduchou konverzi dokumentu XML do objektů v PHP. Na rozdíl od třídy XMLReader však dokument nečteme v cyklu po jednotlivých elementech, ale načteme ho celý do objektové struktury. To je velmi výhodné, protože nejpomalejší úkon provádí standardní knihovna, která je pro tento účel optimalizována.

Použijeme opět stejná data:

<?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 vypisující data je velmi krátký. Zde nám oproti metodě SAX stačí vytvořit pouze jednu třídu 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);
  }
}

Podle potřeby můžeme samozřejmě doplnit metody pro vyhledání uživatele, ověření hesla apod. Pro náš účel postačí pouhý výpis seznamu uživatelů. Po spuštění skriptu se v prohlížeči objeví:

Pavel Slavík        22  21.3.2000
Jan Novák           31  30.10.2012
Tomáš Marný         16  12.1.2011

Jak vidíme, čtení dokumentu třídou SimpleXMLElement je mnohem jednodušší, než čtení metodou SAX. Tato třída je také asi 10× rychlejší, než XMLReader a lépe zdokumentovaná. Pro zpracování běžných dokumentů XML je proto mnohem výhodnější.

Kompletní kód všech příkladů z této lekce najdete jako vždy ke stažení dole pod článkem :-)

V následujícím cvičení, Řešené úlohy k 3.-4. lekci práce se soubory v PHP, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Stáhnout

Staženo 10x (2.24 kB)
Aplikace je včetně zdrojových kódů v jazyce php

 

Předchozí článek
Práce se soubory CSV v PHP
Všechny články v sekci
Soubory a práce s nimi v PHP
Článek pro vás napsal Kit
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
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 (3)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!