XML Processing with SimpleXML

Parse XML with PHP SimpleXML, read elements and attributes, handle namespaces, report parsing failures, and avoid unsafe entity options.

Lesson 60 of 86Intermediate10 min read

An XML feed describes data with named elements, attributes, and sometimes namespaces. SimpleXML gives PHP an object-like view of that structure, which works well for compact, predictable documents. A product feed can become a loop over product elements rather than a collection of fragile string searches. This reference explains how to parse a string, inspect attributes, convert values, and handle invalid XML. It also covers namespaces and safe parser settings. SimpleXML is convenient, but it still loads a tree into memory and does not automatically validate business rules or make arbitrary external XML safe.

Read the XML structure as data

An element such as <product> contains child elements and may also have attributes. In SimpleXML, $xml->product refers to matching children, while square-bracket access can read an attribute on a particular node. Repeated children form an iterable selection, so foreach is clearer than assuming one product exists. A SimpleXMLElement is not an ordinary string: cast the leaf value to string before validating or comparing it as application data. The element's printed text does not include all descendants in every intended representation. Distinguish a missing element from an empty element according to the feed contract. XML is case-sensitive, so Product and product are different names. Never parse general XML using a regular expression that assumes nesting and quoting are trivial.

Handle parsing failure without leaking diagnostics

simplexml_load_string() returns a SimpleXMLElement or false. Compare strictly with === false because an object containing little data can behave unexpectedly in loose conditions. libxml_use_internal_errors(true) lets your handler collect parser messages instead of allowing warnings to mix with a JSON or HTML response. Save the previous setting, collect errors when parsing fails, clear the buffer, and restore the previous setting afterward. Internal diagnostics may include pieces of input or file information, so log them according to your privacy policy rather than displaying the full parser report publicly. A generic 'Invalid XML document' response can be paired with a request identifier. Parsing success establishes well-formed XML, not valid product codes, trusted prices, or authorized content.

Namespaces change how element names match

A namespace associates element names with a URI so feeds from different vocabularies can coexist. The visible prefix is only an alias; ns:product and p:product can mean the same expanded name if they use the same namespace URI. When a feed has a default namespace, unqualified property or XPath access may not find the elements you expect. Use children() with the namespace URI for direct traversal, or registerXPathNamespace() with your own stable prefix before XPath queries. Do not hardcode the supplier's prefix when you really mean its URI. Attributes have namespace rules too, and unprefixed attributes do not automatically inherit an element's default namespace. Inspect the feed contract and test a document that changes prefixes without changing URIs.

Validate text after parsing

XML values arrive as textual representations. Cast a quantity to string, validate its accepted integer range, then use the resulting integer. For a price, agree on currency and decimal format rather than assuming every numeric-looking string is acceptable. Trim fields only where surrounding whitespace is insignificant. Attribute values may be absent, repeated structures may contain zero records, and a supplier can send an unexpected version. Validate required paths, record counts, maximum text lengths, and allowed attributes before writing data. XML schema validation, when appropriate, checks structural constraints but does not replace every business rule. Escape extracted text when printing it in HTML. XML entity escaping and HTML escaping are related concepts but should be applied for the actual output destination.

Keep untrusted XML within a bounded policy

Do not turn on LIBXML_NOENT or DTD-loading options merely to make an example parse; those options can expand entities or change external-resource behavior. Contemporary libxml defaults are safer than older tutorials suggest, but explicit risky options can undo that protection. LIBXML_NONET blocks network access for applicable parser operations; it is not a blanket assurance that every filesystem or entity case is safe. For an input contract that forbids DTDs, reject documents containing a DOCTYPE rather than requiring those features. Limit total bytes and processing time, and use a maintained PHP and libxml combination. Never fetch a visitor-provided URL as a feed without a separate network-access policy. A safe XML parser does not fix server-side request forgery in the download step.

Choose SimpleXML when a tree is affordable

SimpleXML loads the document structure into memory. That is reasonable for a bounded configuration file or modest supplier feed but can become costly for enormous exports. DOM provides richer editing and node control at similar tree-storage cost. A streaming approach such as XMLReader or an event parser is a better fit when records can be processed independently. Choose according to document size and the transformations you need, not because one API has the shortest example. Preserve the original document if exact formatting matters; parsing and serialization can change representation without changing meaning. For imports, separate parsing, validation, and database writes so a malformed later record does not leave an unexplained partial update.

Practice task and worked solution

A supplier adds a default namespace to the products element. Adapt the reader using the documented namespace URI and children() rather than removing namespace declarations from the text. The solution traverses namespaced products, reads unprefixed code attributes, and validates quantities before collecting rows. Test the original namespace, a different prefix bound to the same URI, and an unsupported URI. Add an input byte limit and reject DTDs under the feed contract. If one quantity is invalid, report its record identifier and apply a documented all-or-nothing import rule. Do not hide the failure by casting arbitrary text to zero.

Continue learning

Compare JSON processing and file handling, then use exceptions for failure and cleanup boundaries.

See the SimpleXML parsing manual for return values and options.

XML Processing with SimpleXML Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Read product attributes and validated quantities

The fixed document is small, contains no DTD, and requires the SimpleXML extension. Run with PHP CLI.

Expected output is 0012: 0 and 0040: 7 on separate lines. The product code remains text and zero passes validation. Extension availability and libxml settings depend on the PHP installation.

PHP
<?php
$text = '<products><product code="0012"><quantity>0</quantity></product>'
      . '<product code="0040"><quantity>7</quantity></product></products>';
$previous = libxml_use_internal_errors(true);
try {
    $xml = simplexml_load_string($text, SimpleXMLElement::class, LIBXML_NONET);
    if ($xml === false) throw new RuntimeException('Invalid XML');
    foreach ($xml->product as $product) {
        $quantity = filter_var((string) $product->quantity, FILTER_VALIDATE_INT,
            ['options' => ['min_range' => 0]]);
        if ($quantity === false) throw new RuntimeException('Invalid quantity');
        echo (string) $product['code'] . ': ' . $quantity . PHP_EOL;
    }
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previous);
}
Output
0012: 0
0040: 7

XML Processing with SimpleXML FAQ

Why is a field an object?

SimpleXML wraps each node in a SimpleXMLElement object, so $product->price is an object even when it contains only text. Cast leaf content to the type you need, such as (string) $product->name or (float) $product->price, before comparing, storing or encoding it as JSON.

Why does XPath find nothing?

Usually because the document declares a default namespace, xmlns="...". Its elements are then in that namespace even though they have no prefix in the XML. Register a query prefix for the namespace URI with registerXPathNamespace() and use that prefix in the XPath expression.

Does parsing validate product data?

It only establishes well-formedness: the tags are properly nested and closed. It doesn't check that a price is a positive number or that a required element exists, unless additional validation is performed, either against a schema or with your own checks after reading the values.

Can I load huge feeds?

SimpleXML loads the whole document into memory as a tree, which is fine for small and medium files. When a complete tree would exhaust memory, prefer a bounded streaming design, such as XMLReader, which moves through the document one node at a time.

XML Processing with SimpleXML Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choicemedium

The input contract forbids DTDs. Which parser policy fits?

XML Processing with SimpleXML Quiz

2 questions to check what you learned.

  1. How should a parsed leaf quantity reach an integer validator?

    • Assume every node is already an integer
    • Strip the XML with a regex
    • Cast its content to string, then validate the range
    • Ignore invalid fields
  2. What identifies a namespace semantically?

    • The chosen prefix alone
    • The filename extension
    • The element's indentation
    • Its namespace URI