headerbanner

Lekce XSLT transformace a XML Schema validace, aneb jak v PHP vytvořit menu z XML (2)

V předchozím článku jsme definovali vstup a výstup, mohli bychom se rovnou zabývat generováním jednoho z druhého. Ale před tím se ještě podíváme jak ověřit, že náš XML vstup odpovídá schematu, které jsme vytvořili. Není to zbybečné, překlepnout se můžete. Jednou z cest je ověření pomocí XML Schema.

XML Schema definuje, jaké elementy musí a může mít náš XML soubor a v jakém pořadí. Zjednodušíme to a budeme kontrolovat jen strukturu XML, ne obsah elementů.

XML Schema je XML soubor s elementy ze jmenného prostoru nazvaného http://www.w3.org/2001/XMLSchema.

Tady se trochu zastavíme. Název jmenného prostoru vypadá jako webová adresa. Jenže je v podstatě nefunkční a jejím zadáním do prohlížeče narazíte třeba na neexistující stránku. Na této adrese žádnou definici nenajdete. Adresa v tomto tvaru jako konvence byla zvolena proto, že autor popisu (v tomto případě autor XML Schema) zpravidla vlastní nějakou doménu. Pokud bude označovat své jmenné prostory touto adresou, dá se předpokládat, že to bude dělat pouze on a nikdo jiný. Tím je prakticky zaručena jedinečnost názvu jmenného prostoru. Ten ovšem může být třeba obecnějším URN ve tvaru urn:blabla:blibli, což už webovou adresu nepřipomíná a tedy tolik nemate.

Jmenný prostor tedy není "webová adresa", ale prostě a jen jedinečné označení. Slouží k tomu, aby třeba element s názvem "product" byl interpretován jednoznačně. Nápad označit element tímto názvem bezpochyby nebude mít jen jediný člověk na světě, ale jen jeden je vlastníkem domény 2. řádu, dejme tomu "alfabeta.com". Jen on tedy může "vlastnit" jmenný prostor "http://alfabeta.com/jmprostor" a říci, že element "product" v tomto jmenném prostoru má taková a taková pravidla. Aby se v XML souboru poznalo, do jakého prostoru "product" patří (tedy že je to "ten náš" product), napíše se před název "product" buď celé jméno prostoru, nebo jen zkratka.

XML Schema se definuje v .xsd souboru, ten náš bude vypadat následovně:

 
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" >
<!-- xmlns:xs = jmenný prostor bude dále označován xs-->  
  <!-- začátek XML Schema-->
  <xs:element name="menu">
    <xs:complexType> <!-- element menu obsahuje další elementy, proto :complexType -->
           <xs:sequence>  <!-- elementy mají přesné řazení, proto :sequence  -->
           <!-- první element v menu je menugroup -->
           <!-- musí se vyskytnout alespoň jednou, maximum neomezeno -->
           <!-- menugroup je typu tmenugroup, který je popsán níže -->
           <!-- atribut "type" tak umožňuje popsat vyšší úroveň a nižší zvlášť -->
          <xs:element name="menugroup" minOccurs="1" maxOccurs="unbounded" type="tmenugroup" />
          </xs:sequence>
   </xs:complexType>  
  </xs:element>       
 
  <xs:complexType name="tmenugroup"><!-- popisuje obsah elementu menugroup -->
    <xs:sequence>
        <!-- menuitem je typu tmenuitem - popsáno níže -->
       <xs:element name="menuitem" minOccurs="1" maxOccurs="unbounded" type="tmenuitem"/>
    </xs:sequence>
  </xs:complexType>
 
  <xs:complexType name="tmenuitem"><!-- popisuje obsah elementu menuitem -->
    <xs:sequence>
       <xs:element name="inanchor" /><!-- element inanchor musí být právě jednou-->
       <xs:element name="url" minOccurs="0" maxOccurs="1" /><!-- maximálně jednou, ale nemusí být vůbec-->
       <xs:element name="title" minOccurs="0" maxOccurs="1" />
       <xs:element name="desc" minOccurs="0" maxOccurs="1" />
       <xs:element name="key" minOccurs="0" maxOccurs="1" />
       <xs:element name="pagetype" minOccurs="0" maxOccurs="1" />
       <xs:element name="yesnoinmenu" minOccurs="0" maxOccurs="1" />
       <xs:element name="langs" minOccurs="0" maxOccurs="1" />
       <xs:element name="menugroup" minOccurs="0" maxOccurs="unbounded" type="tmenugroup" />
    </xs:sequence>
  </xs:complexType>
 
</xs:schema>
 

Protože toto není učebnice XML a kontrolních nebo transformačních mechanismů, nebudu vysvětlovat význam elementů. Nicméně z komentářů je vidět návaznost na XML soubor. Výše uvedené popisuje pravidla našeho XML souboru, ve kterém máme definovaný web, respektive menu a položky v HEAD.

Nyní musíme nějak "spárovat" schema a vstupní XML, abychom mohli vstup validovat. To provedeme jednoduše pomocí PHP skriptu. Uvedeny jsou dvě varianty validace. První používá postupné načítání XML dokumentu, což je pro naše účely zbytečné, protože menu určitě nebude nijak dlouhé. Druhá varianta načítá celý XML najednou a pracuje s ním v paměti:

 
<?php
libxml_use_internal_errors(true);//abychom mohli vypsat chyby
//validace podle XML Schema příklad 1
//zpracovává soubor po částech - proudový parser (malé paměťové nároky)
$reader = new XMLReader();
$reader->open("menu.xml",LIBXML_NOENT|LIBXML_DTDLOAD|LIBXML_DTDATTR);
$reader->setSchema("menu.xsd");
while ($reader->read()){}
if (!$reader->isValid()){
    $errors=  libxml_get_errors();
    foreach ($errors as $error) {
        echo $error->file.'|'.$error->line.'|'.$error->message."\n";
 
    }
}else{
  echo "\nXMLReader: dokument je validní";
}
libxml_clear_errors();
 
//validace podle XML Schema příklad 2
//DOM rozhraní - nejprve načte celý soubor 
$doc= new DOMDocument();
$doc->load("menu.xml",LIBXML_NOENT|LIBXML_DTDLOAD|LIBXML_DTDATTR);
if (!@$doc->schemaValidate("menu.xsd")){
    $errors=  libxml_get_errors();
    foreach ($errors as $error) {
        echo $error->file.'|'.$error->line.'|'.$error->message."\n";
    }
} else{
  echo "\nDOMDocument: dokument je validní";
}
 

XML vstupní dokument je v souboru menu.xml, XML Schema je v souboru menu.xsd. skript si nazvěte jakkoli a spusťte ho ve stejném adresáři, kde je .xml soubor a tento .xsd soubor.

Po úspěšném spuštění skriptu máme jistotu, že XML podklad pro tvorbu menu obsahuje právě a jen elementy, které obsahovat má. Mohli bychom ho doplnit třeba kontrolou, zda každá položka menu má jedinečný URL, ale to už je "vyšší dívčí". V dalším článku vygenerujeme HTML kód menu.