PHP DOM Parser is useful when you want to read and modify XML documents in PHP. It uses the DOMDocument class to load XML as a document tree. After that, you can read nodes, update values, add new elements, set attributes, and save the modified XML.
Use DOMDocument when you need more control than SimpleXML gives. For example, if you need to edit XML nodes or create new XML elements, DOMDocument is usually the better choice.
If you want to compare DOMDocument with SimpleXML, XMLReader, and event-based parsing, read the main guide on PHP XML Parser. This tutorial focuses only on PHP DOM Parser and XML node manipulation.
Quick Answer
To parse XML with PHP DOM Parser, create a DOMDocument object and load the XML file with load(). Then use methods like getElementsByTagName(), createElement(), setAttribute(), and appendChild() to read or modify XML nodes.
<?php
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('products.xml');
$products = $dom->getElementsByTagName('product');
foreach ($products as $product) {
echo $product->getAttribute('sku') . PHP_EOL;
}
?>
The PHP manual describes DOMDocument as the class that represents an entire XML or HTML document and serves as the root of the document tree. You can read the official reference for the DOMDocument class.
When should you use DOMDocument?
Use DOMDocument when you want to work with XML as a document tree. It is useful when you need to read nodes, change existing values, add new elements, remove nodes, or save the XML after modification.
For simple XML reading, SimpleXML is easier. For very large XML files, XMLReader is usually better. DOMDocument is best when XML editing is part of the task.
| Requirement | Recommended option |
|---|---|
| Read a small XML file with simple code | SimpleXML |
| Read a very large XML file node by node | XMLReader |
| Update an XML node value | DOMDocument |
| Add a new XML element | DOMDocument |
| Set or update XML attributes | DOMDocument |
| Use event handlers while parsing XML | PHP XML Parser |
Example XML file
The example project uses a product XML file. Each product has a sku attribute and child elements for name, category, price, and stock.
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product sku="P1001">
<name>Wireless Mouse</name>
<category>Computer Accessories</category>
<price currency="USD">24.99</price>
<stock>Available</stock>
</product>
<product sku="P1002">
<name>USB Keyboard</name>
<category>Computer Accessories</category>
<price currency="USD">39.50</price>
<stock>Available</stock>
</product>
<product sku="P1003">
<name>Laptop Stand</name>
<category>Office Desk</category>
<price currency="USD">31.25</price>
<stock>Out of Stock</stock>
</product>
</products>
Save this file as products.xml. The PHP code will load this XML file, read product records, update one stock value, and add one new product node.
Create the DOM parser class
The parser class loads the XML file with DOMDocument. It has separate methods to read product nodes, update a stock value, add a new product, and return the modified XML.
Set preserveWhiteSpace to false before loading the XML. This avoids unwanted whitespace text nodes when working with the document tree. Set formatOutput to true to make the saved XML easier to read.
<?php
declare(strict_types=1);
final class ProductDomParser
{
private DOMDocument $document;
public function __construct()
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->preserveWhiteSpace = false;
$this->document->formatOutput = true;
}
public function load(string $filePath): void
{
if (!file_exists($filePath)) {
throw new RuntimeException('XML file not found.');
}
libxml_use_internal_errors(true);
if (!$this->document->load($filePath)) {
$errors = libxml_get_errors();
$firstError = $errors[0]->message ?? 'Unable to load XML file.';
libxml_clear_errors();
throw new RuntimeException(trim($firstError));
}
libxml_clear_errors();
}
public function getProducts(): array
{
$products = [];
$productNodes = $this->document->getElementsByTagName('product');
foreach ($productNodes as $productNode) {
if (!$productNode instanceof DOMElement) {
continue;
}
$products[] = [
'sku' => $productNode->getAttribute('sku'),
'name' => $this->getNodeValue($productNode, 'name'),
'category' => $this->getNodeValue($productNode, 'category'),
'price' => $this->getNodeValue($productNode, 'price'),
'currency' => $this->getChildAttribute($productNode, 'price', 'currency'),
'stock' => $this->getNodeValue($productNode, 'stock'),
];
}
return $products;
}
public function updateStock(string $sku, string $stock): bool
{
foreach ($this->document->getElementsByTagName('product') as $productNode) {
if (!$productNode instanceof DOMElement) {
continue;
}
if ($productNode->getAttribute('sku') !== $sku) {
continue;
}
$stockNode = $productNode->getElementsByTagName('stock')->item(0);
if ($stockNode instanceof DOMElement) {
$stockNode->nodeValue = $stock;
return true;
}
}
return false;
}
public function addProduct(
string $sku,
string $name,
string $category,
string $price,
string $currency,
string $stock
): void {
$productsNode = $this->document->getElementsByTagName('products')->item(0);
if (!$productsNode instanceof DOMElement) {
throw new RuntimeException('Root products node not found.');
}
$productNode = $this->document->createElement('product');
$productNode->setAttribute('sku', $sku);
$productNode->appendChild($this->document->createElement('name', $name));
$productNode->appendChild($this->document->createElement('category', $category));
$priceNode = $this->document->createElement('price', $price);
$priceNode->setAttribute('currency', $currency);
$productNode->appendChild($priceNode);
$productNode->appendChild($this->document->createElement('stock', $stock));
$productsNode->appendChild($productNode);
}
public function saveXml(): string
{
return $this->document->saveXML() ?: '';
}
private function getNodeValue(DOMElement $parent, string $tagName): string
{
$node = $parent->getElementsByTagName($tagName)->item(0);
return $node instanceof DOMElement ? trim($node->textContent) : '';
}
private function getChildAttribute(DOMElement $parent, string $tagName, string $attribute): string
{
$node = $parent->getElementsByTagName($tagName)->item(0);
return $node instanceof DOMElement ? $node->getAttribute($attribute) : '';
}
}
?>
The getProducts() method reads product nodes and converts them into a PHP array. The updateStock() method finds a product by SKU and changes its stock value. The addProduct() method creates a new product element and appends it to the root <products> node.
Display the DOM parser result in HTML
After creating the parser class, include it in index.php. Then load the XML file, update one product stock value, add a new product, and display the result in the browser.
The example below updates product P1003 from Out of Stock to Available. It also adds a new product node to the XML document.
<?php
declare(strict_types=1);
require_once __DIR__ . '/ProductDomParser.php';
$products = [];
$updatedXml = '';
$errorMessage = '';
try {
$parser = new ProductDomParser();
$parser->load(__DIR__ . '/products.xml');
$parser->updateStock('P1003', 'Available');
$parser->addProduct(
'P1004',
'Noise Cancelling Headphones',
'Audio',
'89.00',
'USD',
'Available'
);
$products = $parser->getProducts();
$updatedXml = $parser->saveXml();
} catch (RuntimeException $exception) {
$errorMessage = $exception->getMessage();
}
function e(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHP DOM Parser Demo</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="page">
<section class="card">
<h1>PHP DOM Parser Demo</h1>
<p>This example reads, updates, and adds XML nodes using DOMDocument.</p>
<?php if ($errorMessage !== '') : ?>
<div class="message error">
<?php echo e($errorMessage); ?>
</div>
<?php else : ?>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>SKU</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Stock</th>
</tr>
</thead>
<tbody>
<?php foreach ($products as $product) : ?>
<tr>
<td><?php echo e($product['sku']); ?></td>
<td><?php echo e($product['name']); ?></td>
<td><?php echo e($product['category']); ?></td>
<td><?php echo e($product['currency'] . ' ' . $product['price']); ?></td>
<td><?php echo e($product['stock']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<h2>Updated XML</h2>
<pre><?php echo e($updatedXml); ?></pre>
<?php endif; ?>
</section>
</main>
</body>
</html>
Example output
When the page runs successfully, the browser shows the updated product list and the modified XML output.

DOM parser output showing product records after updating and adding XML nodes.
How the DOM parser code works
The code starts by creating a DOMDocument object. The version and encoding are passed to the constructor.
$this->document = new DOMDocument('1.0', 'UTF-8');
The next two settings make the XML easier to work with and easier to read after saving.
$this->document->preserveWhiteSpace = false;
$this->document->formatOutput = true;
preserveWhiteSpace removes unnecessary whitespace-only text nodes while loading the XML. formatOutput formats the saved XML with indentation.
The load() method reads the XML file into the DOM tree.
if (!$this->document->load($filePath)) {
$errors = libxml_get_errors();
$firstError = $errors[0]->message ?? 'Unable to load XML file.';
libxml_clear_errors();
throw new RuntimeException(trim($firstError));
}
The getElementsByTagName() method finds all <product> elements in the document.
$productNodes = $this->document->getElementsByTagName('product');
Each product node is a DOMElement. The SKU is stored as an attribute, so it is read with getAttribute().
$sku = $productNode->getAttribute('sku');
Child values such as name, category, price, and stock are read by finding the child element and reading its text content.
$node = $parent->getElementsByTagName($tagName)->item(0);
return $node instanceof DOMElement ? trim($node->textContent) : '';
To update a node, the code first finds the product by SKU. Then it finds the <stock> child node and changes its value.
$stockNode = $productNode->getElementsByTagName('stock')->item(0);
if ($stockNode instanceof DOMElement) {
$stockNode->nodeValue = $stock;
return true;
}
To add a new product, the code creates a new <product> element, sets the SKU attribute, creates child elements, and appends the product to the root node.
$productNode = $this->document->createElement('product');
$productNode->setAttribute('sku', $sku);
$productNode->appendChild($this->document->createElement('name', $name));
$productNode->appendChild($this->document->createElement('category', $category));
Finally, saveXML() returns the modified XML document as a string.
return $this->document->saveXML() ?: '';
Common errors and fixes
DOMDocument errors are usually caused by invalid XML, wrong file paths, missing PHP extensions, or whitespace text nodes. These are the common issues to check first.
Class “DOMDocument” not found
This means the DOM extension is not enabled in your PHP installation. You can check loaded PHP modules from the command line.
php -m | grep dom
If the extension is missing, enable the DOM or XML extension for your PHP setup and restart the web server.
XML file not found
This usually means the XML file path is wrong. Keep the XML file in the same project folder or pass the correct absolute path.
$parser->load(__DIR__ . '/products.xml');
DOMDocument shows #text nodes
Whitespace between XML tags can appear as text nodes. This often happens when you loop through childNodes directly.
To reduce this issue, set preserveWhiteSpace to false before loading the XML file.
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->load('products.xml');
In this tutorial, we also use getElementsByTagName() to read the specific nodes we need instead of looping through all child nodes blindly.
DOMDocument load() fails
This usually means the XML is not well formed. Use libxml_use_internal_errors(true) and read errors with libxml_get_errors().
libxml_use_internal_errors(true);
if (!$dom->load($filePath)) {
foreach (libxml_get_errors() as $error) {
echo trim($error->message);
}
libxml_clear_errors();
}
Updated XML is not formatted
Set formatOutput to true before saving the XML.
$dom->formatOutput = true;
echo $dom->saveXML();
Security considerations
XML content may come from uploads, APIs, feeds, exports, or partner systems. Treat it as external input unless you fully control the source.
- Validate the XML source before loading it.
- Use
libxml_use_internal_errors(true)to handle XML errors cleanly. - Escape XML values before printing them in HTML.
- Use file size limits if users can upload XML files.
- Do not expose full server file paths or raw parser errors in production pages.
- Avoid loading unknown remote XML URLs directly without validation and timeout control.
The example uses htmlspecialchars() before printing values in the browser. This is important because XML values can contain special characters or unsafe text.
echo htmlspecialchars($product['name'], ENT_QUOTES, 'UTF-8');
If your application accepts XML from outside systems, understand XML-related risks such as XXE. The OWASP page on XML External Entity processing explains this risk in detail.
Developer FAQ
What is PHP DOM Parser?
PHP DOM Parser uses the DOMDocument class to load XML as a document tree. After loading the XML, you can read nodes, update values, add elements, set attributes, and save the modified XML.
When should I use DOMDocument?
Use DOMDocument when you need to modify XML. It is useful for updating node values, adding new elements, removing nodes, changing attributes, and saving the XML again.
Is DOMDocument better than SimpleXML?
DOMDocument is better for editing XML. SimpleXML is easier when you only need to read small and simple XML files. If your task is just reading values, SimpleXML may be simpler.
Can DOMDocument read large XML files?
DOMDocument loads the full XML document into memory. So it is not the best choice for very large XML files. For large XML files, use XMLReader because it reads the document node by node.
How do I read an XML attribute using DOMDocument?
Use getAttribute() on a DOMElement.
$sku = $productNode->getAttribute('sku');
How do I add a new XML node using DOMDocument?
Create the new element with createElement(). Then append it to the parent node with appendChild().
$productNode = $dom->createElement('product');
$productNode->setAttribute('sku', 'P1004');
$productsNode->appendChild($productNode);
How do I save modified XML using DOMDocument?
Use saveXML() to get the modified XML as a string. Use save() if you want to write the modified XML back to a file.
$xmlString = $dom->saveXML();
$dom->save('products-updated.xml');
Conclusion
PHP DOM Parser is a good choice when you need to read and modify XML documents. It gives you control over nodes, attributes, and document structure.
Use SimpleXML when you only need to read simple XML. Use XMLReader when the XML file is very large. Use DOMDocument when you need to update, add, remove, or save XML nodes.
The downloadable example below gives you a complete working DOM parser project. It reads product records, updates one stock value, adds a new product node, and shows the modified XML output.
Download source code
Download the PHP DOM Parser example project and run it in your local PHP environment.