PHP SimpleXML - Get
Getting Data Out of XML Documents In simple terms: Real XML documents are rarely as tidy as simple examples. You may need the third item, all elements matching a condition, every attribute of an eleme
Getting Data Out of XML Documents
In simple terms: Real XML documents are rarely as tidy as simple examples. You may need the third item, all elements matching a condition, every attribute of an element, elements whose names you do not know in advance, or data hidden behind namespaces (prefixes like dc: or g:). SimpleXML provides tools for all of these.
Technically: Beyond property and array access (see PHP SimpleXML Parser), SimpleXMLElement offers children(), attributes(), getName(), count(), and xpath(). XPath is a query language for selecting nodes in an XML tree. Namespaced content is accessed by passing the namespace to children()/attributes() or by registering it for XPath.
The examples use the catalog document from PHP XML Parsers ($catalogXml).
Accessing Elements by Position
<?php
$catalog = simplexml_load_string($catalogXml);
echo $catalog->product[0]->name . "\n"; // first product
echo $catalog->product[2]->name . "\n"; // third product
echo count($catalog->product) . " products\n";
$last = $catalog->product[count($catalog->product) - 1];
echo "Last: {$last->name}\n";
Output
Gel Pen
Wireless Mouse
3 products
Last: Wireless Mouse
Indexes are zero-based. A non-existent index (such as product[5]) returns null-like empty results, so check with isset($catalog->product[5]) before using it.
Iterating Unknown Children and Attributes
When element names are not known in advance — for example, to convert or display any record — iterate with children() and attributes():
<?php
$catalog = simplexml_load_string($catalogXml);
$product = $catalog->product[0];
echo "Element: " . $product->getName() . "\n";
foreach ($product->attributes() as $name => $value) {
echo " @$name = $value\n";
}
foreach ($product->children() as $child) {
echo " {$child->getName()} = $child\n";
}
Output
Element: product
@sku = PEN-01
@category = stationery
name = Gel Pen
price = 20.00
stock = 120
attributes()yields attribute names (as keys) and values.children()yields all child elements;getName()returns each tag name.- With this, a generic function can convert any flat record into an array:
function recordToArray(SimpleXMLElement $record): array
{
$data = [];
foreach ($record->attributes() as $name => $value) {
$data[$name] = (string) $value;
}
foreach ($record->children() as $child) {
$data[$child->getName()] = (string) $child;
}
return $data;
}
Searching With XPath
XPath selects nodes with path expressions, similar to file paths, plus conditions in square brackets:
<?php
$catalog = simplexml_load_string($catalogXml);
$electronics = $catalog->xpath('//product[@category="electronics"]/name');
echo "Electronics: " . implode(', ', array_map('strval', $electronics)) . "\n";
$expensive = $catalog->xpath('//product[price > 500]/@sku');
echo "Over ₹500: " . implode(', ', array_map('strval', $expensive)) . "\n";
$lowStock = $catalog->xpath('//product[stock < 10]');
echo "Low stock: {$lowStock[0]->name} ({$lowStock[0]->stock} left)\n";
Output
Electronics: Desk Lamp, Wireless Mouse
Over ₹500: LAMP-01, MOUSE-01
Low stock: Desk Lamp (6 left)
XPath Essentials
| Expression | Selects |
|---|---|
/catalog/product | <product> children of the root <catalog> |
//name | All <name> elements anywhere in the document |
//product[@category="electronics"] | Products with that attribute value |
//product[price > 500] | Products whose <price> is greater than 500 (compared numerically) |
//product/@sku | The sku attributes of all products |
//product[1] | The first product (XPath positions start at 1) |
//product[last()] | The last product |
//product[contains(name, "Lamp")] | Products whose name contains "Lamp" |
xpath() always returns an array of SimpleXMLElement objects (empty if nothing matches, false on an invalid expression). array_map('strval', ...) converts the results to strings.
Security note: Never insert user input directly into XPath expressions — this enables XPath injection, similar to SQL injection. Validate input strictly (for example, allow only
[A-Z0-9-]for SKUs) or filter the results in PHP instead.
Working With Namespaces
Many real-world formats use namespaces. RSS feeds often include elements from the Dublin Core vocabulary, such as <dc:creator>:
<?php
$rss = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>GoCourse Blog</title>
<item>
<title>PHP 8.5 Released</title>
<link>https://gocourse.in/blog/php-8-5</link>
<pubDate>Thu, 20 Nov 2025 09:00:00 +0530</pubDate>
<dc:creator>Asha</dc:creator>
</item>
<item>
<title>Learning SimpleXML</title>
<link>https://gocourse.in/blog/simplexml</link>
<pubDate>Thu, 15 Oct 2026 10:30:00 +0530</pubDate>
<dc:creator>Bala</dc:creator>
</item>
</channel>
</rss>
XML;
$feed = simplexml_load_string($rss);
echo $feed->channel->title . "\n";
foreach ($feed->channel->item as $item) {
$author = $item->children('http://purl.org/dc/elements/1.1/')->creator;
$date = DateTimeImmutable::createFromFormat(DATE_RSS, (string) $item->pubDate);
echo "- {$item->title} by $author, {$date->format('d M Y')}\n";
}
Output
GoCourse Blog
- PHP 8.5 Released by Asha, 20 Nov 2025
- Learning SimpleXML by Bala, 15 Oct 2026
How It Works
$item->creatorwould find nothing, because<dc:creator>belongs to a different namespace.children('http://purl.org/dc/elements/1.1/')switches to that namespace; the namespace URI identifies it reliably. (Passing the prefix withchildren('dc', true)also works, but prefixes may differ between documents, while URIs do not.)- RSS dates use the RFC 2822 format, which
DATE_RSSdescribes;createFromFormat()turns them into date objects for reformatting (see Date Formatting).
Default Namespaces and XPath
Documents such as sitemaps declare a default namespace (<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">). Property access works as usual, but XPath queries need the namespace registered with a prefix:
<?php
$sitemap = simplexml_load_string(<<<XML
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://gocourse.in/</loc></url>
<url><loc>https://gocourse.in/php/</loc></url>
</urlset>
XML);
$sitemap->registerXPathNamespace('s', 'http://www.sitemaps.org/schemas/sitemap/0.9');
foreach ($sitemap->xpath('//s:loc') as $loc) {
echo $loc . "\n";
}
Output
https://gocourse.in/
https://gocourse.in/php/
Without the registration, xpath('//loc') returns an empty array — a very common source of confusion.
Converting to Arrays: A Caution
A popular shortcut converts SimpleXML to arrays with json_decode(json_encode($xml), true). It works for simple documents but has pitfalls:
- A single child becomes a value, while several children become a list — the structure changes depending on the data.
- Attributes end up under a special
@attributeskey. - Namespaced elements are silently dropped.
- Empty elements become empty arrays instead of empty strings.
For reliable results, convert explicitly with functions like recordToArray() above.
Common Mistakes
Expecting XPath Positions to Start at 0
Effect: //product[0] selects nothing.
Fix: XPath positions start at 1; PHP array indexes start at 0.
Forgetting That xpath() Returns an Array
Effect: $catalog->xpath('//name')->name fails.
Fix: Loop over the result or use [0] after checking it is not empty.
Ignoring Namespaces
Effect: Elements such as <dc:creator> or sitemap <loc> seem to be missing.
Fix: Use children($namespaceUri) and registerXPathNamespace().
Building XPath Queries From User Input
Effect: XPath injection.
Fix: Validate input strictly or filter results in PHP.
Best Practices
- Use
children(),attributes(), andgetName()for generic processing. - Use XPath for searches with conditions instead of nested loops.
- Identify namespaces by their URI, and register them for XPath queries.
- Convert results into explicitly typed PHP arrays or objects.
- Treat all data read from external XML as untrusted input.
Official reference: https://www.php.net/manual/en/simplexml.examples-basic.php