Komentáře k článku
XMLReader – když se zamotáme do SAX

V minulém dílu seriálu o práci s XML soubory v jazyce PHP jsme probírali rozhraní SAX. V dnešním pokračování je na řadě rozhraní XMLReader, které je do určité míry podobné (zpracovává XML soubor po částech), ale používá opačný směr toku událostí a dovoluje zjednodušit kód výsledné aplikace.
No nevim
Byl jsem na Webexpu a poprve slysel o XMLReader a tesil se na pokracovani clanku o XML tady, ale ted jsem trosku rozpacity.
I tenhle jednoduchy priklad ukazuje, ze to ma koncepcni chyby. Element link se poprve vyskytuje zde: /rss/channel/image/link. Takze prvni nalezeni tohoto elementu se tiskne jeste predtim nez mame title z /rss/channel/title takze H1 mame ve vysledku 2× a jako premii PHP Notice.
To by se dalo nejak opravit, ale z ceho me mrazi je ten vnoreny „while ($reader->read())“. Porad jsem marne hledal misto, kde se to opousti po ukonceni elementu item a nenasel. Ono to fakt bezi furt dal. Takze pokud bude jednomu itemu chybet title, tak se tam vesele natiskne ten predchozi.
Re: No nevim
V článku je jen primitivná ukázka využití XMLReaderu.
Nic vám nebrání udělat si dokonalejší logiku zpracování. Např. mazat $title v okamžiku, kdy narazíte na koncový tag elementu item.
Další varianta je zapnout validaci a parser pak havaruje v případě, kdy narazí na položky bez názvu – ten je v RSS povinný (pamatuju-li si to dobře).
Nicméně obecně v proudových parserech jako SAX a XMLReader si každopádně musíte držet nějakou stavovou informaci. Rozdíl je v tom, že v XMLReaderu ji nepotřebujete tolik a navíc pro obsluhu podelementů můžete do sebe cykly volající $reader->read() zanořovat (a vyskočit z nich při nalezení koncového tagu), čímž se vše zase zjednoduší.
Re: No nevim
Validace v tomto případě nepomůže, protože kód závisí na pořadí jednotlivých značek (které v RSS není určeno) a navíc na absenci povolených značek (konkrétně <image>).
Sám píšeš, že u proudových parserů je potřeba si držet informaci o stavu. Zásadní chybu příkladů (u minulého a tohoto článku) vidím v tom, že si tento stav nedrží. Příklady pak vůbec neodpovídají skutečnému použití.
Re: No nevim
Ten příklad není psán jako obecná čtečka RSS, ale jako čtečka XML formátu z prvního dílu seriálu – počítá se se pevným pořadím elementů (což obecně RSS nemá a je to přiznejme si spíše prasárna tohoto formátu).
Obecně formáty, ve kterých se přenášejí velká data a je potřeba používat proudové zpracování mají pevné pořadí podelementů, takže tam tenhle problém odpadá.
Ty příklady jsou trochu umělé a trochu zjednodušené, aby se ukázalo použití rúzných API pro řešení stejné úlohy. Samozřejmě v praxi si pro danou úlohu vyberete nejvhodnější prostředek. Konvertovat RSS do HTML čímkoliv jiným než XSLT je čirý masochismus.
Jinak obsluhu toho, aby se nezpracovával link uvnitř jiných elementů, lze jednoduše vyřešit tím, že tyto elementy se na začátku cyklu přeskočí:
// přeskočíme image a textinput, protože může obsahovat link, který nás nezajímá
if ($reader->nodeType == XMLReader::ELEMENT && in_array($reader->name, array(„image“, „textinput“)))
$reader->next();
Re: No nevim
SAX a XMLReader su v podstate skoro to iste. Osobne si myslim, ze SAX je z hladiska spracovania lepsi a z hladiska kodu prehladnejsi. Samozrejme pre dobreho programatora. :)
XMLReader mi pripada ako dorbeny neskor pre tych, ktori nevedia pochopit ako moze fungovat SAX. :)
Re: No nevim
Koncepční chyba není v XMLReaderu, ale v příkladu. Osobně mi přijde vhodnější všechno načítat v jednom while cyklu a kontext si sledovat v proměnné. Ukázka RSS importu pomocí XMLReaderu je na http://www.clipboard.cz/4ng. Má pouze to omezení, že očekává značky <item> až za <link> a <title> celého zdroje. Pokud by to tak nebylo, pak bychom výstup nemohli rovnou vypisovat, ale museli bychom si ho ukládat do dočasné proměnné.
Re: No nevim
On je cely ten XMLReader divny:
$xml->nodeType == XMLReader::ELEMENT
$xml->nodeType == XMLReader::END_ELEMENT
$xml->nodeType == XMLReader::TEXT
Takze uzavreny element je typ uzlu?
A tenhle priklad je uz natolik nepochopitelny, ze muze byt i spravne. Nicmene pouziti jednoho $xml->read() asi uplne rusi veskere vyhody XMLReaderu. Zatim rikam zlaty SAX (samozrejme ze pro pripad RSS je zlaty simplexml_load_file).
Re: No nevim
ELEMENT ~ počáteční tag
END_ELEMENT ~ koncový tag
(viz tabulka na straně 165 ve knize).
To pojmenování může vypadat trochu divně, ale všichni to okopírovali z .NETu (http://msdn.microsoft.com/…odetype.aspx) – ten použil jmenné konvence tak, aby se to hodně krylo s DOMem.
Re: No nevim
Důležitá je ještě vlastnost isEmptyElement, která umožňuje rozpoznat prázdné elementy (<x/>). Ty totiž END_ELEMENT nemají.
Re: No nevim
To držení všech předků na zásobníku a jejich porovnávání je hrozně pomalé (jasně na RSS to nevadí, ale XMLReader je určena na velké objemy dat a tam by to bylo znát).
Re: No nevim
Zásadně pomalejší to není. Kontext si nějak držet musíme, takže kromě vlastního zásobníků zbývá rekurzivní funkce. Udělal jsem ji co nejchytřeji (do rekurze vstupuje jen když potřebuje změnit kontext) a výsledky jsou tyto:
0.98 s – porovnávání pole s kontextem
0.91 s – chytrá rekurzivní funkce
Jedná se o medián ze tří pokusů při načítání titulku z exportu Wikipedie. Zdrojový kód je na http://www.clipboard.cz/5ng. Dlužno ale podotknout, že pokud bychom kontextů chtěli rozlišovat více, tak by se kód dramaticky zesložitil. Např. RSS import bych v tom psát opravdu nechtěl.