PHP DOM Parser
What Is the DOM? In simple terms: The DOM (Document Object Model) represents an XML or HTML document as a tree of nodes — elements, attributes, text — that you can read, search, and change freely: ins
What Is the DOM?
In simple terms: The DOM (Document Object Model) represents an XML or HTML document as a tree of nodes — elements, attributes, text — that you can read, search, and change freely: insert new elements anywhere, move them, delete them, and save the result. It is the same model JavaScript uses for web pages, so the method names will feel familiar.
Technically: PHP's DOM extension implements the W3C DOM standard with classes such as DOMDocument, DOMElement, DOMNode, DOMNodeList, and DOMXPath. Documents are parsed with libxml into a tree held in memory. PHP 8.4 added a modern, standards-compliant API under the Dom0 namespace — including Dom\HTMLDocument`, an HTML5 parser with CSS selector support.
DOM vs SimpleXML
| SimpleXML | DOM | |
|---|---|---|
| Reading data | Very concise | More verbose |
| Modifying structure (insert, move, remove) | Limited | Full control |
| Pretty-printed output | No | formatOutput |
| XPath | xpath() | DOMXPath (with evaluate() for numbers and strings) |
| HTML documents | No | Yes (loadHTML(), PHP 8.4 Dom\HTMLDocument) |
Both work on the same underlying libxml tree, and simplexml_import_dom() / dom_import_simplexml() convert between them.
The examples use the catalog document from PHP XML Parsers ($catalogXml).
Loading and Reading a Document
<?php
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->loadXML($catalogXml);
echo "Root: " . $doc->documentElement->tagName . "\n";
echo "Store: " . $doc->documentElement->getAttribute('store') . "\n";
foreach ($doc->getElementsByTagName('product') as $product) {
$name = $product->getElementsByTagName('name')->item(0)->textContent;
printf("%-9s %s\n", $product->getAttribute('sku'), $name);
}
Output
Root: catalog
Store: GoCourse Shop
PEN-01 Gel Pen
LAMP-01 Desk Lamp
MOUSE-01 Wireless Mouse
How It Works
loadXML()parses a string (load()parses a file) and returnsfalsefor invalid XML.preserveWhiteSpace = falsediscards the whitespace-only text nodes between elements (the indentation), which simplifies navigation and enables clean re-formatting later.documentElementis the root element (<catalog>).getElementsByTagName()returns a liveDOMNodeListof all matching descendants;item(0)gets the first.getAttribute()reads an attribute;textContentreturns all text inside a node.
Unlike SimpleXML, these values are plain PHP strings — no casting is needed.
Key Node Properties and Methods
| Member | Purpose |
|---|---|
$node->nodeName, $element->tagName | Name of the node / element |
$node->textContent | All text inside the node (read and write) |
$element->getAttribute('x'), hasAttribute(), setAttribute() | Attribute access |
$node->parentNode, childNodes, firstChild, nextSibling | Tree navigation (includes text nodes) |
$element->firstElementChild, nextElementSibling, childElementCount | Navigation that skips text nodes (PHP 8.0+) |
XPath With DOMXPath
<?php
$doc = new DOMDocument();
$doc->loadXML($catalogXml);
$xpath = new DOMXPath($doc);
foreach ($xpath->query('//product[@category="electronics"]/name') as $node) {
echo $node->textContent . "\n";
}
var_dump($xpath->evaluate('sum(//product/stock)'));
var_dump($xpath->evaluate('count(//product[stock < 10])'));
echo $xpath->evaluate('string(//product[@sku="LAMP-01"]/price/@currency)') . "\n";
Output
Desk Lamp
Wireless Mouse
float(156)
float(1)
INR
query()returns aDOMNodeListof matching nodes.evaluate()also returns computed values: XPath functions likesum(),count(), andstring()produce numbers (as floats) and strings directly — no PHP loop required.- Namespaced documents require
$xpath->registerNamespace('prefix', 'uri')before querying, just as with SimpleXML.
Modifying a Document
<?php
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$doc->loadXML($catalogXml);
$xpath = new DOMXPath($doc);
// 1. Change a value
$lampStock = $xpath->query('//product[@sku="LAMP-01"]/stock')->item(0);
$lampStock->textContent = '5';
// 2. Remove an element
$xpath->query('//product[@sku="MOUSE-01"]')->item(0)->remove();
// 3. Create and append a new element
$bag = $doc->createElement('product');
$bag->setAttribute('sku', 'BAG-02');
$bag->setAttribute('category', 'bags');
$name = $doc->createElement('name');
$name->textContent = 'Backpack';
$bag->appendChild($name);
$price = $doc->createElement('price');
$price->setAttribute('currency', 'INR');
$price->textContent = '899.00';
$bag->appendChild($price);
$stock = $doc->createElement('stock');
$stock->textContent = '0';
$bag->appendChild($stock);
$doc->documentElement->appendChild($bag);
echo $doc->saveXML();
Output
<?xml version="1.0" encoding="UTF-8"?>
<catalog store="GoCourse Shop" updated="2026-10-15">
<product sku="PEN-01" category="stationery">
<name>Gel Pen</name>
<price currency="INR">20.00</price>
<stock>120</stock>
</product>
<product sku="LAMP-01" category="electronics">
<name>Desk Lamp</name>
<price currency="INR">1299.00</price>
<stock>5</stock>
</product>
<product sku="BAG-02" category="bags">
<name>Backpack</name>
<price currency="INR">899.00</price>
<stock>0</stock>
</product>
</catalog>
Step-by-Step Explanation
- Assigning
textContentreplaces an element's text; special characters are escaped automatically. remove()(PHP 8.0+) deletes a node from the tree; the older equivalent is$node->parentNode->removeChild($node).- New nodes are created by the document (
createElement()) and then attached withappendChild(). Other methods includeinsertBefore(),replaceChild(), and PHP 8'sbefore(),after(),prepend(), andappend(). formatOutput = true(combined withpreserveWhiteSpace = falsewhen loading) produces neatly indented output;saveXML()returns the document as a string, andsave($file)writes it to a file.
Note: Like SimpleXML's
addChild(), the second argument ofcreateElement('name', $value)does not escape&correctly. SettextContentinstead, as shown.
Parsing HTML
PHP 8.4+: Dom\HTMLDocument
PHP 8.4's HTML5 parser handles real-world HTML exactly as browsers do and supports CSS selectors:
<?php
$html = <<<HTML
<!DOCTYPE html>
<html>
<body>
<ul id="courses">
<li class="course">PHP</li>
<li class="course new">Laravel</li>
<li>About us</li>
</ul>
<a href="/courses/php">Start learning</a>
</body>
</html>
HTML;
$doc = Dom\HTMLDocument::createFromString($html);
foreach ($doc->querySelectorAll('li.course') as $item) {
echo "Course: {$item->textContent}\n";
}
echo "New: " . $doc->querySelector('li.new')->textContent . "\n";
echo "Link: " . $doc->querySelector('a')->getAttribute('href') . "\n";
Output
Course: PHP
Course: Laravel
New: Laravel
Link: /courses/php
querySelector() and querySelectorAll() accept the same CSS selectors as JavaScript. The new Dom0 classes are also available for XML (Dom\XMLDocument`) with improved, standards-compliant behavior.
Older Versions: DOMDocument::loadHTML()
Before PHP 8.4, loadHTML() used libxml's HTML 4 parser. It emits warnings for HTML5 elements (suppress them with libxml_use_internal_errors(true)), may misinterpret UTF-8 text unless the document declares its encoding, and requires XPath instead of CSS selectors. Prefer Dom\HTMLDocument on PHP 8.4+.
When processing HTML from other websites (web scraping), respect their terms of use and robots.txt, and treat all extracted content as untrusted.
Common Mistakes
Forgetting preserveWhiteSpace = false
Effect: firstChild returns whitespace text nodes; formatOutput cannot re-indent.
Fix: Disable whitespace preservation before loading, or use element-only navigation (firstElementChild).
Creating Nodes Without the Document
Effect: "Wrong Document Error" when appending nodes from another document.
Fix: Create nodes with the target document, or use importNode().
Modifying a Live DOMNodeList While Iterating
Effect: Skipped elements when removing nodes inside a foreach over getElementsByTagName().
Fix: Copy the nodes first (iterator_to_array($list)) or iterate backwards.
Using createElement() With Unescaped Text
Effect: Warnings and truncated values for text containing &.
Fix: Set textContent.
Best Practices
- Use DOM when you need to modify document structure, pretty-print output, or parse HTML.
- Load with
preserveWhiteSpace = falseand save withformatOutput = truefor readable output. - Use
DOMXPath::evaluate()for searches and calculations. - Set text through
textContentto ensure correct escaping. - On PHP 8.4+, use
Dom\HTMLDocumentwith CSS selectors for HTML.
Official reference: https://www.php.net/manual/en/book.dom.php