Tvorba XML výstupu v PHP

11. listopadu 2003

Občas pravděpodobně potřebujete ve svých PHP skriptech generovat XML výstup. Můžete k tomu použít různé metody, od jednoduchého „lepení textu“, přes třídy pro generování XML, až po XML DOM funkce. V tomto článku vám ukáži, jak si můžete takovou jednoduchou třídu pro tvorbu XML vytvořit.

Struktura třídy

Nejdříve si určíme strukturu v níž budou data uložena, jaké metody a jak se budou používat.

Vlastní data

Všechny elementy XML budou uloženy v číselně indexovaném poli. Každý prvek pole je asociativním polem s těmito parametry:

  • parent – určuje index nadřazeného prvku
  • type – typ elementu může nabývat tyto hodnoty: node – uzel (element) XML tvořený startovním a případně i koncovým tagem, entity – XML entita ohraničená řetězci & a ;, cdata – textová nebo jiná data v XML (nejsou tím myšlena data v parametrech) a note – poznámka ohraničená znaky <!-- a -->
  • data – v případě, že se jedná o typ cdata, obsahuje tento prvek samotná data
  • text – v případě, že se jedná o typ note, obsahuje tento prvek text poznámky
  • name – v případě, že se jedná o typ node, obsahuje tento prvek jméno uzlu (tagů), pokud je typ entity, obsahuje název entity
  • params – v případě, že se jedná o typ node, může tento prvek obsahovat parametry počátečního tagu v asociativním poli, klíč prvku pole určuje název parametru a hodnota prvku hodnotu parametru

Tato data budou uložena ve vlastnosti třídy data.

Další vlasnoti třídy

  • xml_version – určuje verzi XML, která bude zapsána v hlavičce
  • xml_encoding – určuje kodování XML, které bude zapsáno v hlavičce. Třída neprovádí žádné překódování mezi znakovými sadami, tuto vlastnost nastavte na takovou hodnotu, aby odpovídala kódování, v němž budou ukládaná data.
  • auto_indent – určuje řetězec, kterým se odsazují jednotlivé úrovně XML, je přednastaven na dvě mezery. Můžete zadat například mezeru, tabulátor nebo i více mezer. Pokud zadáte prázdný řetězec, nebude se odsazení provádět.
  • append_after_element – určuje řetězec, který se přidá za konec tagu, je přednastaven na znak konce řádky \n.

Jen pro úplnost ještě uvedu zdrojový kód hlavičky třídy:

class c_xml_generator {
  var $data = array();
  var $auto_indent = ‚  ‚;
  var $append_after_element = „\n“;
  var $xml_version = ‚1.0‘;
  var $xml_encoding = ‚iso-8859-2‘;

Metody třídy

PHP v současné produkční verzi (4.x) nerozlišuje mezi public a private metodami, proto, aby bylo jasné, o který typ metody se jedná, budeme odlišovat public metody podtržítkem na začátku názvu metody.

Zjištění volného indexu v poli data

function _get_new_id() {
  if (count($this->data) > 0)
    return(array_reduce(array_keys($this->data), ‚max‘) + 1);
  else
    return(1);
}

Tato metoda zjistí volný index v poli data. To je index, na který se bude ukládat další XML element. Tato metoda je využívána všemi metodami, které vkládají do XML obsah.

Přidání elementu

Metoda nejprve najde volný index v poli data, následně na toto místo uloží data elementu a nakonec vrátí id nově vytvořeného prvku. Obdobným způsobem fungují i následující metody:

function add_node($parent, $name, $params=array()) {
  $new_id = $this->_get_new_id();
  $this->data[$new_id] = array(
    ‚type‘ => ‚node‘,
    ‚parent‘ => $parent,
    ‚params‘ => $params,
    ‚name‘ => $name
  );
  return($new_id);
}

Parametr metody $parent obsahuje id nadřazeného prvku. V případe, že vytváříme element v nejvyšší úrovni, má tento parametr hodnotu 0. Dalším parametrem je $name, už podle názvu jistě poznáte, že obsahuje jméno elementu. Posledním parametrem $params, kterým předáváte parametry elementu (tento parametr není povinný).

Přidání textových dat

function add_cdata($parent, $data) {
  $new_id = $this->_get_new_id();
  $this->data[$new_id] = array(
    ‚type‘ => ‚cdata‘,
    ‚parent‘ => $parent,
    ‚data‘ => $data
  );
  return($new_id);
}

Parametr metody $parent obsahuje id nadřazeného prvku. Nadřazený prvek musí být typu node. Dalším parametrem je $data, který obsahuje samotná data.

Přidání entity

function add_entity($parent, $name) {
  $new_id = $this->_get_new_id();
  $this->data[$new_id] = array(
    ‚type‘ => ‚entity‘,
    ‚parent‘ => $parent,
    ‚name‘ => $name
  );
  return($new_id);
}

Parametr $parent obsahuje id nadřazeného uzlu a $name název entity.

A nakonec poznámky

function add_note($parent, $text) {
    $new_id = $this->_get_new_id();
    $this->data[$new_id] = array(
      ‚type‘ => ‚note‘,
      ‚parent‘ => $parent,
      ‚text‘ => $text
    );
    return($new_id);
  }

Parametr $parent obsahuje id nadřazeného uzlu a $text text poznámky bez úvodní a koncové značky (<!-- a -->). U této metody si musíte dát pozor na to, aby text vkládané poznámky neobsahoval počáteční a koncové značky poznámky.

Všechny tyto metody vracejí po vložení části XML její id. To budete potřebovat hlavně pro vytváření uzlů a poduzlů.

Formát vstupních dat

Ještě než se pustíme tvorby samotného XML kódu, řekneme si něco o tom, jak připravit data pro předcházející metody. Třída automaticky konvertuje nebezpečné znaky při tvorbě výsledného XML kódu, takže pokud použijete pro tvorbu uzlu například kód $xml_generator->add_node($id, 'uzel', array('parametr'=>'<!"');, ve výsledném kódu se objeví <uzel parametr="&lt;!&quot;">. Toto se provádí hlavně pro zjednodušení práce a také proto, abyste se vyhnuli zbytečným chybám.

Metody pro tvorbu XML kódu

Nejprve začneme pomocnými metodami:

function _encode_param_name($text) {
  return(htmlspecialchars($text));
}
function _encode_param_value($text) {
  return(str_replace(
    array(„\n“,“\r“),
    array(‚&x0A;‘,’&x0D‘),
    htmlspecialchars($text)
  ));
}
function _encode_cdata($text) {
  return(htmlspecialchars($text));
}

Tyto tři metody provádějí konverzi nebezpečných znaků pro textová data a parametry. Rozdíl mezi převodem textu a hodnot parametrů je ten, že u parametrů se navíc převádí i konce řádků \n a \r na entity.

Vlastní tvorba XML

function create_xml($start_node=0) {
  return($this->_create_xml_node($start_node));
}

Tato metoda vytvoří výsledné XML tím, že zavolá metodu _create_xml_node, která rekurzivně vytvoří celý dokument. Metoda má jeden nepovinný parametr, který určuje počáteční uzel. Výchozí hodnota je 0, to znamená celý dokument.

Nyní se podíváme na „jádro“ celé třídy, metodu _create_xml_node:

function _create_xml_node($id) {
  static $level = 0;
  $level++; // zvýší statickou proměnnou $level o jednu
// tato promenná se používá pro počítání odsazení
  $node = &$this->data[$id];
  switch($node[‚type‘]) {
    case ‚node‘ :  // pokud se jedná o typ node vytvoří nejprve
// startovní tag s parametry
      $ret = ‚<‚.$node[‚name‘];
      foreach($node[‚params‘] as $param_name => $param_value) {
        $ret .= ‚ ‚.$this->_encode_param_name($param_name).
          ‚=“‚.$this->_encode_param_value($param_value).'“‚;
      };
      $childs = “;
      $complete_tag = true;
// najde vsechny objekty patřící do tohoto node
      foreach($this->data as $node_id => $node_data)
        if ($node_data[‚parent‘] == $id) {
          $complete_tag = false;
          $childs .= $this->_create_xml_node($node_id);
// pro každý objekt volá rekurzívne metodat
// sama sebe
        };
// podle toho jestli existují vypise tag jako
// párový nebo nepárový
      if ($complete_tag)
        $ret .= ‚ />‘;
      else
        $ret .= ‚>‘.$this->append_after_element.
                $childs.
                str_repeat($this->auto_indent, $level – 1).
                ‚</‘.$node[‚name‘].‘>‘;
      break;
// pokud se jedná o ostatní typy cdata,entity
// note vypíše tak jak mají vypadat.
    case ‚cdata‘ :
      $ret = $this->_encode_cdata($node[‚data‘]);
      break;
    case ‚entity‘ :
      $ret = ‚&‘.$node[‚name‘].‘;‘;
      break;
    case ‚note‘ :
      $ret = ‚<!–‚.$this->_encode_cdata($node[‚text‘]).‘–>‘;
      break;
    case ‚root‘ :  // typ root se zpracovává podobně jako node
      $ret = ‚<‚.‘?xml‘.
        ($this->xml_version?‘ version=“‚.$this->xml_version.'“‚:“).
        ($this->xml_encoding?‘ encoding=“‚.$this->xml_encoding.'“‚:“).
        ‚?‘.“>\n“;
      foreach($this->data as $node_id => $node_data)
        if ($node_data[‚parent‘] == 0) {
          $complete_tag = false;
          $ret .= $this->_create_xml_node($node_id);
        };
      break;
  };
  $level–; // jsme na konci objektu, zmensi hodnotu odsazení
  $ret = str_repeat($this->auto_indent, $level).
         $ret.
         $this->append_after_element;
  return($ret); // a vrátí zpracovanou část dokumentu
}

A nakonec ještě ukázka, jak například pomocí třídy vytvořit XML export tabulky MySQL databáze. Používám třídu tMysql, o které jsem psal v článku Zjednodušte si práci s MySQL.

<?php
// pomocí hlavičky určíme mime typ text/xml
header(‚content-type: text/xml‘);
// vložíme třídu pro mysql a xml
include ‚xml_generator.class.php‘;
include ‚xmysql.class.php‘;
// název tabulky
$tb_name = ‚test‘;
// vytvoříme instanci třídy tMysql a připojíme k db
$sql = new tMysql;
$sql->dbName = ‚test‘;
$sql->dbHost = ‚localhost‘;
$sql->dbUser = ‚uzivatel‘;
$sql->dbPassword = ‚heslo‘;
$sql->connect();
// vytvoříme instanci třídy c_xml_generator
$xml = new c_xml_generator;
// vytvoříme si nejvyšší element
$top = $xml->add_node(0, ‚table‘, array(‚name‘ => $tb_name));
// vybereme data z požadované tabulky
$sql->query(‚export‘, ‚SELECT * FROM ‚.$tb_name);
// pro kazdý prvek výsledku vytvoříme jeden element
// —
while($row = $sql->fetch_assoc(‚export‘)) {
  $xml->add_node($top, ‚row‘, $row);
};
// —
$sql->free_result(‚export‘);
echo $xml->create_xml();
?>

Předchozí kód vytvoří export v následujícím formátu:

<?xml version=“1.0″ encoding=“iso-8859-2″?>
  <table name=“test“>
    <row id=“1285″ jmeno=“Pajda“ druh=“pes“ heslo=“123″ />
    <row id=“4″ jmeno=“Pepa“ druh=“andulka“ heslo=““ />
    <row id=“145″ jmeno=“Rum“ druh=“pes“ heslo=““ />
    <row id=“745″ jmeno=“Micka“ druh=“kočka“ heslo=““ />
  </table>

Nebo pokud část kódu mezi značkami // --- změníte takto…

while($row = $sql->fetch_assoc(‚export‘)) {
  // element pro řádek tabulky
  $xrow = $xml->add_node($top, ‚row‘);
  foreach($row as $field => $value) {
    // element pro pole tabulky
    $xfield = $xml->add_node($xrow, $field);
    // samotná data
    $xml->add_cdata($xfield, $value);
  };
};

…výsledek bude takový:

<?xml version=“1.0″ encoding=“iso-8859-2″?>
  <table name=“test“>
    <row>
      <id>1285</id>
      <jmeno>Pajda</jmeno>
      <druh>pes</druh>
      <heslo>123</heslo>
    </row>
    <row>
      <id>4</id>
      <jmeno>Pepa</jmeno>
      <druh>andulka</druh>
      <heslo> </heslo>
    </row>
    <row>
      <id>145</id>
      <jmeno>Rum</jmeno>
      <druh>pes</druh>
      <heslo> </heslo>
    </row>
    <row>
      <id>745</id>
      <jmeno>Micka</jmeno>
      <druh>kočka</druh>
      <heslo> </heslo>
    </row>
  </table>

Teď už vám zbývá jen stáhnout si zdrojový kód třídy a ukázky a začít s vlastními pokusy.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

2 komentářů

  1. Jozef mostka

    Led 22, 2010 v 12:49

    chýba mi tam destroy metóda.
    ako zničím triedu ?

    Odpovědět
  2. Wictorko

    Kvě 22, 2011 v 22:18

    Existuje an nejaky sposob ako to spravit opacne? Teda XML export z cudzieho webu si data natiahnut do vlastnej MySQL databazy?

    Odpovědět

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *