Generování HTML v PHP už jednodušší být nemůže. Stačí nějaké to echo a případně output buffer. Pokud je ale potřeba zpracovat již hotové HTML, může se to trochu komplikovat. Lze samozřejmě použít knihovny třetích stran, pro jednodušší úlohy ale naprosto dostačí základní PHP DOM.
DOMDocument
Základní třídou je DOMDocument, která načte HTML a následně jej lze zpracovávat jako XML. Ostatně tak je i HTML načítáno, s drobnými výjimkami. Níže je ukázaný příklad, jak HTML soubor načíst a selektovat jednotlivé elementy. Lze i traverzovat stromem pomocí referencí na rodiče, potomky a sourozence.
HTML na rozdíl od XML má i jiné typy uzlů, jako například TextNode. Pokud je tedy mezi tagy v HTML mezera, nové řádky apod., při traverzování pomocí next/previousSibling se narazí právě na tyto elementy.
// Bez tohoto volání při načítání HTML5 vyskakují podobné chyby: // Warning: DOMDocument::loadHTML(): Tag header invalid in Entity libxml_use_internal_errors(true); $data = file_get_contents("test.html"); $dom = new DOMDocument(); $dom->loadHtml(mb_convert_encoding($data, 'HTML-ENTITIES', 'UTF-8')); $finder = new DomXPath($dom); // Elementy $nodesByElement = $finder->query("//a"); # CSS: a $nodesByElement = $finder->query("//a/span"); # CSS: a > span $nodesByElement = $finder->query("//a//span"); # CSS: a span // ID a atributy $nodeById = $finder->query("//*[@id='testId']"); # CSS: #testId $nodeById = $finder->query("//div[@id='testId']"); # CSS: div#testId $nodeByAttr = $finder->query("//*[@data-city]"); # CSS: [data-city] // Třídy - těch může být více v jednom atributu, trochu se to komplikuje // CSS: .inactive $classToFind = "inactive"; $byClass = $finder->query("//*[contains(concat(' ', normalize-space(@class), ' '), ' ".$classToFind." ')]"); // XPath se v těchto případech nechová úplně stejně jako CSS $byIndex = $finder->query("//div/a[2]"); # CSS: div > a:nth-child(2) $lastNode = $finder->query("//div/a[last()]");# CSS: div > a:last-child // Vyhledávání pouze v dříve vyhledaném elementu $finder->query(".//a", $nodeById[0]); // Rodič, potomci, předchozí sourozenec, následující sourozenec // POZOR: Počítá i TextNode $parentNode = $nodeById->item(0)->parentNode; $previousSibling = $nodeById->item(0)->previousSibling; $nextSibling = $nodeById->item(0)->nextSibling; $children = $nodeById->item(0)->childNodes;
Zpracování HTML
Nalezené elementy lze i zpracovávat. Například získat HTML pouze určitého uzlu, získání a úpravy atributů, odstraňování nebo vložení uzlů a mnoho dalšího. Více je popsáno v dokumentaci k PHP DOM.
// Získání kusu HTML nalezeného selektorem $htmlPart = $dom->saveHtml($nodeById->item(0)); //Získání hodnotu atributu $linkNode->item(0)->getAttribute("href"); // Změnu atributu $linkNode->item(0)->setAttribute("href", "/"); // Smazání tagu img, který je přímým potomkem $toRemove = $finder->query("img", $nodeList->item(2)); $removedItem = $nodeList->item(2)->removeChild($toRemove->item(0)); //Vložení nového (nyní odstraněného) elementu $nodeList->item(0)->appendChild($removedItem);
Selektory XPath
Pro vyhledávání v DOMDocument se nepoužívají CSS selektroy, ale XPath. Ty se značně liší, ale dokáží toho mnohem více. Malý přehled a tutoriál například na stránce interval.cz. Pro základy ale budou dostačovat nejspíše výše zmíněné selektory.
Důležité je používat na začátek vždy //, jinak bude vyhledávání probíhat pouze v kořenovém adresáři. Výjimkou je, pokud se určí aktuální uzel, pak se vyhledává v aktuálním uzlu ale pouze v přímých potomcích.
Osobní zkušenosti s DOMDocument, nebo dalšími knihovnami sdílejte v komentářích.
K tomuto článku již není možné přidávat další komentáře
Komentáře
Hezký článek, já teda k DOMu nikdy moc netíhnul, radši používam parsery třetích stran, např. SimpleHtmlDom, ale někdy se to může hodit a povědomí by o tom měl mít každý programátor.
PS. Celkově zajímavý blog, přidávám do RSS.
PS. Do třetice, něco o Nette by nebylo?
Díky. Parsery třetích stran jsou OK, ale pro jednoduché věci naprosto dostačuje i ten základní.
O Nette se nic psát nechystám převážně z důvodu, že ho neumím a nikdy jsem jej nepoužil. Nejsem tedy dost znalý, abych o něm mohl psát.
V článku je chyba.
-> // Vyhledávání pouze v dříve vyhledaném elementu , chybí na začátku ".",
i když je předaný contextnode tak díky chybějící tečce, která značí současný úroveň to bude hledat opět v celém dokumentu.
Děkuji, chybu jsem opravil