Session 5.7 – PHP XML Processing

Module 5: Database Connectivity and Web Services | Duration: 1 hr

Learning Objectives

By the end of this session, students will be able to:

  • Understand XML structure and syntax
  • Parse XML documents using SimpleXML and DOM
  • Navigate and extract data from XML documents
  • Create and modify XML documents programmatically
  • Handle large XML files efficiently with XMLReader
  • Apply XPath queries for complex data extraction

Introduction to XML

XML (eXtensible Markup Language) is a markup language for storing and transporting data. It's widely used for configuration files, data exchange between systems, and web services.

Key Insight

XML is often described as a meta-language: it defines rules for creating other markup languages. HTML, SVG, XHTML, RSS, SOAP, and Android layout files are all XML-based. PHP can read, write, and query any of these formats using the same three extensions.

Unlike HTML, XML has no predefined tags. You invent tag names that describe your data (<book>, <price>). The XML parser only cares about structure, not semantics.

XML vs JSON

Both XML and JSON are used for data exchange. Their key differences:

FeatureXMLJSON
VerbosityMore verbose (opening + closing tags)More compact
AttributesSupports attributes on elements (id="bk101")No equivalent — all data is in values
CommentsSupports <!-- comments -->No comments allowed
Schema validationXSD, DTD, RelaxNGJSON Schema (informal)
QueryingXPath, XSLT, XQueryJSONPath (unofficial)
Common useConfig files, SOAP, RSS/Atom, Android layoutsREST APIs, browser-server communication
Well-Formed vs Valid XML

These are two distinct levels of XML correctness:

  • Well-formed — Follows basic XML syntax rules: one root element, all tags are closed, attributes are quoted, no illegal characters. Any XML parser can check this.
  • Valid — Well-formed AND conforms to a specific schema (DTD or XSD) that defines which elements and attributes are allowed. Validation is optional but recommended for data-exchange formats.
PHP Will Refuse to Parse Malformed XML

All three PHP XML extensions fail on malformed input. Always enable libxml_use_internal_errors(true) before loading untrusted XML, then check for errors with libxml_get_errors(). Otherwise a single malformed file will crash your script with a fatal error.

Sample XML Document
<?xml version="1.0" encoding="UTF-8"?> <catalog> <book id="bk101"> <author>John Doe</author> <title>XML Developer's Guide</title> <genre>Computer</genre> <price>44.95</price> <publish_date>2024-01-01</publish_date> </book> <book id="bk102"> <author>Jane Smith</author> <title>Web Services Handbook</title> <genre>Computer</genre> <price>49.95</price> <publish_date>2024-02-01</publish_date> </book> </catalog>
SimpleXML

Easy to use, object-oriented API for simple XML processing

DOM (Document Object Model)

Full-featured API for complex XML manipulation

XMLReader

Memory-efficient streaming parser for large files

SimpleXML Extension

How SimpleXML Works

SimpleXML loads the entire XML document into memory and exposes it as a tree of nested PHP objects. Each XML element becomes an object property; child elements of the same name become an array-like collection; attributes are accessed using array-bracket notation.

SimpleXML is ideal when:

  • The XML file is small enough to fit in memory.
  • You primarily need to read data (SimpleXML's write capabilities are limited).
  • You want simple, readable PHP code without a lot of boilerplate.
String Casting in SimpleXML

SimpleXML elements are SimpleXMLElement objects, not plain strings. When you concatenate or pass them to a function expecting a string, PHP implicitly casts them. However, when storing values in an array or comparing with ===, always cast explicitly: (string)$book->title. Failing to do so can produce unexpected results in comparisons and var_export output.

Loading XML Documents
<?php // Load from file $xml = simplexml_load_file('books.xml'); // Load from string $xmlString = '<?xml version="1.0"?> <catalog> <book> <title>PHP Basics</title> <author>John Doe</author> </book> </catalog>'; $xml = simplexml_load_string($xmlString); // Error handling libxml_use_internal_errors(true); $xml = simplexml_load_file('books.xml'); if ($xml === false) { echo "Failed to load XML\n"; foreach (libxml_get_errors() as $error) { echo $error->message; } } ?>
Accessing XML Data
<?php $xml = simplexml_load_file('books.xml'); // Access elements echo $xml->book[0]->title; // First book title echo $xml->book[1]->author; // Second book author // Access attributes echo $xml->book[0]['id']; // Book ID attribute // Iterate through elements foreach ($xml->book as $book) { echo "Title: " . $book->title . "\n"; echo "Author: " . $book->author . "\n"; echo "Price: $" . $book->price . "\n"; echo "---\n"; } // Count elements echo "Total books: " . count($xml->book); // Check if element exists if (isset($xml->book[0]->title)) { echo "Title exists"; } ?>
Iterating Over Elements

When multiple sibling elements share the same tag name (e.g. multiple <book> elements), SimpleXML treats them as an iterable collection. Use foreach ($xml->book as $book) to loop over them. Accessing $xml->book alone gives you only the first matching element if you access it like a scalar, which is a common beginner mistake.

XPath Queries
<?php $xml = simplexml_load_file('books.xml'); // Find all books $books = $xml->xpath('//book'); // Find books by author $books = $xml->xpath('//book[author="John Doe"]'); // Find books with price > 40 $books = $xml->xpath('//book[price > 40]'); // Find all titles $titles = $xml->xpath('//book/title'); foreach ($titles as $title) { echo $title . "\n"; } // Find book by attribute $books = $xml->xpath('//book[@id="bk101"]'); // Complex queries $books = $xml->xpath('//book[genre="Computer" and price < 50]'); foreach ($books as $book) { echo "Title: " . $book->title . "\n"; echo "Price: $" . $book->price . "\n"; } ?>
XPath Syntax Quick Reference
ExpressionSelects
//bookAll <book> elements anywhere in the document
/catalog/bookOnly <book> elements that are direct children of <catalog>
//book[@id]All <book> elements that have an id attribute
//book[@id="bk101"]The book whose id attribute equals "bk101"
//book[price > 40]Books whose <price> child value is greater than 40
//book/titleAll <title> elements inside a <book>
//book[1]The first <book> element (XPath is 1-indexed)
Modifying XML with SimpleXML
<?php $xml = simplexml_load_file('books.xml'); // Modify element value $xml->book[0]->price = 39.95; // Modify attribute $xml->book[0]['id'] = 'bk100'; // Add new element $xml->book[0]->addChild('isbn', '978-1234567890'); // Add element with attribute $newBook = $xml->addChild('book'); $newBook->addAttribute('id', 'bk103'); $newBook->addChild('title', 'New Book'); $newBook->addChild('author', 'New Author'); $newBook->addChild('price', '29.95'); // Save modified XML $xml->asXML('books_modified.xml'); // Output as string echo $xml->asXML(); ?>

DOM Extension

What Is the DOM?

The Document Object Model (DOM) is a W3C standard that represents an XML (or HTML) document as a tree of typed nodes in memory. PHP's DOM extension implements this standard, giving you the same tree-manipulation API used in browsers via JavaScript's document.getElementById() and similar calls.

Key node types in the DOM tree:

Node TypeExamplePHP Class
DocumentThe XML document itselfDOMDocument
Element<book>DOMElement
TextThe text inside an elementDOMText
Attributeid="bk101"DOMAttr
Comment<!-- remark -->DOMComment
DOM vs SimpleXML — When to Choose Which
  • Use SimpleXML for quick reads of small, well-structured files where you know the schema.
  • Use DOM when you need to create XML from scratch, manipulate the tree in complex ways (insert, move, replace, remove nodes), validate against a schema, or use XSLT transformations.
  • You can convert between them: simplexml_import_dom($domNode) and dom_import_simplexml($simpleXmlElement).
Loading and Parsing
<?php // Create DOMDocument $dom = new DOMDocument(); // Load from file $dom->load('books.xml'); // Load from string $xmlString = '<?xml version="1.0"?><root><item>Test</item></root>'; $dom->loadXML($xmlString); // Formatting options $dom->preserveWhiteSpace = false; $dom->formatOutput = true; // Validate against DTD if ($dom->validate()) { echo "Valid XML"; } // Get root element $root = $dom->documentElement; echo $root->nodeName; // catalog ?>
Navigating DOM
<?php $dom = new DOMDocument(); $dom->load('books.xml'); // Get elements by tag name $books = $dom->getElementsByTagName('book'); foreach ($books as $book) { // Get child elements $title = $book->getElementsByTagName('title')->item(0)->nodeValue; $author = $book->getElementsByTagName('author')->item(0)->nodeValue; $price = $book->getElementsByTagName('price')->item(0)->nodeValue; echo "Title: $title\n"; echo "Author: $author\n"; echo "Price: $price\n"; // Get attributes $id = $book->getAttribute('id'); echo "ID: $id\n"; } // XPath with DOM $xpath = new DOMXPath($dom); // Find all books $books = $xpath->query('//book'); // Find specific book $books = $xpath->query('//book[@id="bk101"]'); // Complex query $books = $xpath->query('//book[price > 40]'); foreach ($books as $book) { $title = $xpath->query('.//title', $book)->item(0)->nodeValue; echo "Title: $title\n"; } ?>
Creating and Modifying DOM
<?php $dom = new DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; // Create root element $catalog = $dom->createElement('catalog'); $dom->appendChild($catalog); // Create book element $book = $dom->createElement('book'); // Add attribute $book->setAttribute('id', 'bk101'); // Create and append child elements $title = $dom->createElement('title', 'XML Processing'); $book->appendChild($title); $author = $dom->createElement('author', 'John Doe'); $book->appendChild($author); $price = $dom->createElement('price', '44.95'); $book->appendChild($price); // Append book to catalog $catalog->appendChild($book); // Add another book $book2 = $dom->createElement('book'); $book2->setAttribute('id', 'bk102'); $title2 = $dom->createElement('title'); $title2->appendChild($dom->createTextNode('Web Development')); $book2->appendChild($title2); $catalog->appendChild($book2); // Save to file $dom->save('new_books.xml'); // Output as string echo $dom->saveXML(); ?>
Modifying Existing DOM
<?php $dom = new DOMDocument(); $dom->load('books.xml'); // Find element to modify $xpath = new DOMXPath($dom); $books = $xpath->query('//book[@id="bk101"]'); if ($books->length > 0) { $book = $books->item(0); // Modify element value $price = $book->getElementsByTagName('price')->item(0); $price->nodeValue = '39.95'; // Add new element $isbn = $dom->createElement('isbn', '978-1234567890'); $book->appendChild($isbn); // Remove element $genre = $book->getElementsByTagName('genre')->item(0); if ($genre) { $book->removeChild($genre); } // Replace element $oldTitle = $book->getElementsByTagName('title')->item(0); $newTitle = $dom->createElement('title', 'Updated Title'); $book->replaceChild($newTitle, $oldTitle); } // Save changes $dom->save('books_modified.xml'); ?>

XMLReader - Streaming Parser

Pull Parsing vs Tree Parsing

SimpleXML and DOM are tree parsers: they load the entire document into memory at once. For a 10 MB XML file this means ~100 MB of PHP memory usage, because every node is represented as a PHP object.

XMLReader is a pull parser (also called a streaming parser or cursor-based parser). It reads the file sequentially, one node at a time, keeping only the current node in memory. Memory usage stays constant regardless of file size.

The trade-off: you cannot navigate backwards or jump to an arbitrary node — you must process nodes in document order.

XMLReader Node Types

Each time you call $reader->read(), the cursor advances to the next node. The node's type is stored in $reader->nodeType. Common values:

ConstantValueMeaning
XMLReader::ELEMENT1Opening tag (e.g. <book>)
XMLReader::END_ELEMENT15Closing tag (e.g. </book>)
XMLReader::TEXT3Text content between tags
XMLReader::ATTRIBUTE2An attribute node
XMLReader::COMMENT8An XML comment
Reading Large XML Files
<?php // XMLReader for memory-efficient parsing $reader = new XMLReader(); $reader->open('large_books.xml'); // Read through document while ($reader->read()) { // Check node type if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'book') { // Get attribute $id = $reader->getAttribute('id'); // Read element content $xml = $reader->readOuterXML(); $book = simplexml_load_string($xml); echo "ID: $id\n"; echo "Title: " . $book->title . "\n"; echo "Author: " . $book->author . "\n"; echo "---\n"; } } $reader->close(); ?>
Processing Specific Elements
<?php $reader = new XMLReader(); $reader->open('books.xml'); $books = []; while ($reader->read()) { if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'book') { // Expand current node to DOM $dom = $reader->expand(); // Process with DOM $title = $dom->getElementsByTagName('title')->item(0)->nodeValue; $price = $dom->getElementsByTagName('price')->item(0)->nodeValue; // Store books with price > 40 if ($price > 40) { $books[] = [ 'title' => $title, 'price' => $price ]; } } } $reader->close(); // Process filtered books foreach ($books as $book) { echo "{$book['title']}: \${$book['price']}\n"; } ?>

Creating XML Documents

Why Use XMLWriter?

When generating XML from PHP you might be tempted to simply echo strings: echo "<book>" . $title . "</book>";. This approach is fragile: if $title contains an & or <, the resulting XML is malformed.

XMLWriter handles all escaping automatically. It also guarantees structurally valid output by tracking open elements — you cannot accidentally close the wrong tag or forget to close one. For generated XML in production, always use XMLWriter or DOMDocument rather than string concatenation.

XMLWriter - Generating XML
<?php // Create XMLWriter $writer = new XMLWriter(); $writer->openMemory(); // Or openUri('output.xml') $writer->setIndent(true); $writer->setIndentString(' '); // Start document $writer->startDocument('1.0', 'UTF-8'); // Start root element $writer->startElement('catalog'); // Write book element $writer->startElement('book'); $writer->writeAttribute('id', 'bk101'); $writer->writeElement('title', 'XML Processing'); $writer->writeElement('author', 'John Doe'); $writer->writeElement('genre', 'Computer'); $writer->writeElement('price', '44.95'); // End book element $writer->endElement(); // Write another book $writer->startElement('book'); $writer->writeAttribute('id', 'bk102'); $writer->writeElement('title', 'Web Services'); $writer->writeElement('author', 'Jane Smith'); $writer->writeElement('price', '49.95'); $writer->endElement(); // End root element $writer->endElement(); // End document $writer->endDocument(); // Output XML echo $writer->outputMemory(); // Or write to file // $writer = new XMLWriter(); // $writer->openUri('books.xml'); // ... write elements ... // $writer->flush(); ?>

Practical Examples

Database to XML Export
<?php // Export database data to XML function exportToXML($pdo) { $writer = new XMLWriter(); $writer->openMemory(); $writer->setIndent(true); $writer->startDocument('1.0', 'UTF-8'); $writer->startElement('users'); // Fetch data from database $stmt = $pdo->query("SELECT * FROM users"); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $writer->startElement('user'); $writer->writeAttribute('id', $row['id']); foreach ($row as $key => $value) { if ($key != 'id') { $writer->writeElement($key, htmlspecialchars($value)); } } $writer->endElement(); } $writer->endElement(); $writer->endDocument(); return $writer->outputMemory(); } // Usage $pdo = new PDO("mysql:host=localhost;dbname=mydb", "root", ""); $xml = exportToXML($pdo); // Save to file file_put_contents('users.xml', $xml); // Send as download header('Content-Type: application/xml'); header('Content-Disposition: attachment; filename="users.xml"'); echo $xml; ?>
XML to Database Import
<?php // Import XML data to database function importFromXML($pdo, $xmlFile) { $xml = simplexml_load_file($xmlFile); $pdo->beginTransaction(); try { $stmt = $pdo->prepare("INSERT INTO users (name, email, phone) VALUES (?, ?, ?)"); foreach ($xml->user as $user) { $stmt->execute([ (string)$user->name, (string)$user->email, (string)$user->phone ]); } $pdo->commit(); return true; } catch (Exception $e) { $pdo->rollBack(); echo "Import failed: " . $e->getMessage(); return false; } } // Usage $pdo = new PDO("mysql:host=localhost;dbname=mydb", "root", ""); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); if (importFromXML($pdo, 'users.xml')) { echo "Import successful!"; } ?>
RSS Feed Parser
<?php // Parse RSS feed function parseRSS($url) { $xml = simplexml_load_file($url); $items = []; foreach ($xml->channel->item as $item) { $items[] = [ 'title' => (string)$item->title, 'link' => (string)$item->link, 'description' => strip_tags((string)$item->description), 'pubDate' => (string)$item->pubDate ]; } return $items; } // Usage $feedUrl = 'https://example.com/feed.xml'; $items = parseRSS($feedUrl); foreach ($items as $item) { echo "<h3>{$item['title']}</h3>"; echo "<p>{$item['description']}</p>"; echo "<small>{$item['pubDate']}</small>"; echo "<hr>"; } ?>
Configuration File Parser
<?php // config.xml /* <?xml version="1.0"?> <config> <database> <host>localhost</host> <name>mydb</name> <user>root</user> <password></password> </database> <settings> <timezone>America/New_York</timezone> <debug>true</debug> </settings> </config> */ class Config { private $config; public function __construct($xmlFile) { $this->config = simplexml_load_file($xmlFile); } public function get($path) { $parts = explode('.', $path); $node = $this->config; foreach ($parts as $part) { if (isset($node->$part)) { $node = $node->$part; } else { return null; } } return (string)$node; } public function getArray($path) { $parts = explode('.', $path); $node = $this->config; foreach ($parts as $part) { $node = $node->$part; } $result = []; foreach ($node->children() as $key => $value) { $result[$key] = (string)$value; } return $result; } } // Usage $config = new Config('config.xml'); echo $config->get('database.host'); // localhost echo $config->get('settings.timezone'); // America/New_York $dbConfig = $config->getArray('database'); print_r($dbConfig); ?>

Session Summary

Key Points
  • XML is a markup language for storing and transporting structured data
  • SimpleXML provides easy object-oriented access to XML data
  • DOM offers full-featured XML manipulation capabilities
  • XMLReader efficiently processes large XML files using streaming
  • XMLWriter creates well-formed XML documents programmatically
  • XPath enables powerful querying of XML data
  • XML is commonly used for configuration, data exchange, and web services
  • Choose the right parser based on file size and complexity
Next Session Preview

In the next session, we will explore PHP AJAX Integration, learning how to create dynamic web applications that communicate with the server without page reloads.