Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

obsahuje podprahová sdělení

PHP & DOM; interní poznámky

Pár postřehů k DOM API v PHP 5. Jde o funkce pro parsování, generování a manipulaci s XML a HTML soubory. Chování se může v různých verzích lišit.

$dom = new DOMDocument();

// konfigurace pro načtení
$dom->preserveWhiteSpace = FALSE;
$dom->load('input.xml');

// konfigurace pro uložení
$dom->formatOutput = TRUE;
$dom->encoding = 'utf-8';
$dom->save('output.xml');

Načtení XML souboru

  • metody $dom->load($fileName), $dom->loadXML($string)
  • konfigurační vlastnosti:
    • $dom->resolveExternals – načíst externí entity z DTD deklarace? Výchozí: FALSE
    • $dom->strictErrorChecking – throws DOMException on errors? Výchozí: TRUE
    • $dom->validateOnParse – načíst a validovat oproti DTD? Vychozí: FALSE
    • $dom->preserveWhiteSpace – zachovat redundantní white space? Výchozí: TRUE
    • $dom->substituteEntities – zaměnit jmenné entity? Výchozí: FALSE
  • validování nebo resolveExternals stahuje DTD – extrémně pomalé!
  • aby se nestahovalo DTD:
    • ponechat nastavené resolveExternals = validateOnParse = FALSE
    • nebo použít XML Catalogs (nepovedlo se mi rozchodit)
    • nebo modifikovat URL v <!DOCTYPE> na lokální kopii DTD
  • zpracování DTD je pomalé (tj. i bez stahování)
    • načtení XHTML dokumentu trvá cca 50× déle
    • validování už pak zpomalí jen mírně
  • bez zpracování DTD:
  • $dom->encoding určuje XML deklarace

Uložení XML souboru

  • metody $dom->save($fileName), $s = $dom->saveXML()
  • generují identický výstup (narozdíl od ukládání HTML)
  • konfigurační vlastnosti:
    • $dom->formatOutput – generuje „hezčí“ výstup (formátuje podivně)
    • $dom->encoding – kódování (např. utf-8, windows-1250)
    • $dom->standalone, $dom->version – XML deklarace
  • formatOutput lze povolit jen tehdy, pokud bylo načítáno s preserveWhiteSpace == FALSE
  • saveXML($node) s parametrem ignoruje encoding, vždy používá UTF-8

Načtení HTML souboru

  • metody $dom->loadHTMLFile($fileName), $dom->loadHTML($string)
  • chápe všechny HTML entity
  • ignoruje nastavení:
    • $dom->resolveExternals (vždy TRUE)
    • $dom->validateOnParse (vždy FALSE)
    • $dom->preserveWhiteSpace (vždy TRUE)
    • $dom->substituteEntities (vždy TRUE)
  • je nutné specifikovat kódování meta hlavičkou, jinak použje ISO-8859-1, ale vlastnost $dom->encoding nastaví na NULL a už nepůjde změnit při ukládání
  • přepisuje \r na &#13; → odstranit funkcí str_replace
  • funguje getElementById()
  • nevytváří plnohodnotný DOM, nedoplní např. element body (má volitelné značky)

Uložení HTML souboru

  • metody $dom->saveHTMLFile($fileName), $s = $dom->saveHTML()
  • negenerují stejný výstup!
  • pokud byl vstup načten jako XML (load, loadXML), ukládá špatně prázdné elementy
  • ignoruje nastavení:
    • $dom->formatOutput (vždy FALSE)
    • $dom->encoding
      • saveHTML použije vstupní kódování
      • saveHTMLFile použije 7bit ASCII, v meta hlavičce uvádí UTF-8 (není chyba),

Zpracování HTML souboru

// odstraníme \r
$s = file_get_contents('input.html');
$s = str_replace("\r", '', $s);

// parsujeme
$dom = new DOMDocument();
$ok = $dom->loadHTML($s);

// zjistíme obsah <title>
$title = $dom->getElementsByTagName('title')->item(0)->nodeValue;

// nebo šikovněji přes XPath:
$xpath = new DOMXPath($dom);
$title = $xpath->query('//title')->item(0)->nodeValue;

XPath: tutorial, XPather extension

Karma body: 16. Líbil se vám článek?

Komentáře » přidat

  1. avatar [1] David Grudl: nový

    Tohle není článek, jen souhrn mých rešerší, sepsaný pro vlastní potřebu. Text nadále doplňuji a upravuji. Budu rád, pokud téma doplníte o další postřehy.

    Posláno 27. 9. 2007 v 9.28 | Odpovědět
  2. [2] pixy: nový

    Prefix: mám s tímhle nulové zkušenosti; drobný nápad: nepomohlo by přeuložit nepoužívanější DTD na lokál a přesměrovat PHP na ně?

    Posláno 27. 9. 2007 v 10.37 | Odpovědět
    Na komentář reagoval [3] David Grudl
  3. avatar [3] David Grudl: nový

    [2] pixy: asi nejsnáze to lze udělat změnou URL v <!DOCTYPE>na lokální soubor, zprovoznit XML Catalogs se mi nepodařilo. Nicméně stále je zpracování DTD velmi pomalé – asi 50× prodlouží načtení XHTML souboru.

    Mnohem rychlejší je preprocesing vstupního souboru
    nahradit atribut id za xml:id
    nahradit jmenné entity za číselné

    a pak parsovat bez DTD.

    Posláno 27. 9. 2007 v 11.13 | Odpovědět
  4. avatar [4] marek: nový

    nevadí že napíšu komentář k nečlánku? :) Co vedlo k napsání komentáře o nečlánku po osmi hodinách od napsání?

    Posláno 27. 9. 2007 v 18.05 | Odpovědět
    Na komentář reagoval [5] David Grudl
  5. avatar [5] David Grudl: nový

    [4] marek: to je pozůstatek jednoho smazaného komentáře a smazaných reakcí na to, že jsem jej smazal ;-)

    Posláno 27. 9. 2007 ve 22.54 | Odpovědět
  6. avatar [6] xom: nový

    Děkuju za souhrn, velice se to hodí, zvláště, když přemýšlím nad tvorbou PHP OpenXML docx generátoru, který jsem po netu hledal a nějak nenašel – pokud byste někdo o tom generátoru věděl, tak napište, ať nedělám zbytečnou práci (pro tabulkový Excel již existuje PHP Excel

    Posláno 28. 9. 2007 v 10.26 | Odpovědět
  7. [7] pavel: nový

    Podle čeho DOM určuje typ node? Já se přes něj snažil naimportovat jednoduché XML typu:

    <items>
       <item>
          <types>
             <type/>
             <type/>
          </types>
        </item>
    </items>

    a smůla. Proste NODE „types“ se rozhodl považovat za DOMText a nešel tím pádem listovat přes ChildNodes :-(

    Posláno 30. 9. 2007 v 18.25 | Odpovědět
    Na komentář reagoval [8] Pavel
  8. [8] Pavel: nový

    [7] pavel: klasika, chyba mezi židlí a klávesnicí :)

    Dal jsem si tu práci a dohledal to (je to dávno přepsáno do simpleXML) a byla tam triviální bota v zaměření NODE :-(

    Posláno 30. 9. 2007 v 18.45 | Odpovědět
  9. avatar [9] Linus: nový

    Ahoj mám takovej nepěknej problém, a to mám nasledující kód(který z jednoho dokumentu vezme element a přepíše jim element se stejným id v jiném dokumentu).

    $parent = new DOMDocument();
    $parent->loadXML(
    '<foo>
      <bar xml:id="a">
        <bar xml:id="b">parent</bar>
      </bar>
    </foo>'
    );
    
    $child = new DOMDocument();
    $child->loadXML(
    '<foo>
      <bar xml:id="a">
        <bar xml:id="b">child</bar>
      </bar>
    </foo>'
    );
    
    $replace = $parent->getElementById('a');
    
    $replace->parentNode->replaceChild(
        $parent->importNode($child->getElementById('a'), TRUE),
        $replace
    );
    
    echo $parent->getElementById('b')->nodeValue;

    Element se přepíše, bohužel ho pak nelze získat pomocí getElementById a jeho potomky taky ne. Nevíte jak toto obejít je to snad omezení atributu xml:id místo id?

    Očekávaný výsledek: child
    Avšak výsledek je: parent

    Posláno 4. 11. 2007 v 9.42 | Odpovědět

Tento článek byl uzavřen. Už není možné k němu přidávat komentáře ani hlasovat

Výtah na začátek článku na první komentář

Názory čtenářů v diskusích nejsou názory provozovatele webu, a ten za jejich obsah neodpovídá.

La Trine © 2004, 2008 David Grudl – o webu
provozuje Pachollini.

Jakékoliv užití obsahu, včetně převzetí článků nebo jejich částí, je bez předchozího písemného svolení autora zakázáno.

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.