In PHP, how do you read an element's text and an attribute with SimpleXML, and why do you cast the results to string?
answer
- children as properties, attributes as array keys
- foreach over repeated elements
- every value is a SimpleXMLElement
- === against a string is always false
- missing child: use isset()
basics
~20 ssimplexml_load_string() returns a SimpleXMLElement: child elements are read as properties ($p->name) and attributes with array syntax ($p['sku']). Both return SimpleXMLElement objects, so cast with (string), (int) or (float) before comparing, storing or passing them on.
solid answer
~40 s`simplexml_load_string($xml)` or `simplexml_load_file($path)` returns a `SimpleXMLElement` for the root, or `false` on a parse failure. Child elements are reached as properties, `$feed->product->name`, which gives the first match, and `foreach ($feed->product as $p)` walks all of them. Attributes use array access, `$p['sku']`. Every one of those results is itself a `SimpleXMLElement`, not a string, which is why the manual tells you to cast with `(string)` before comparing: `$p->name === 'Desk lamp'` is always false, the object cannot be an array key, and under `declare(strict_types=1)` passing it to a `string` parameter throws a `TypeError`. Cast numbers with `(int)` or `(float)`. A missing child does not return `null`, so test it with `isset($p->discount)`.
code
php · 26 lines<?php
declare(strict_types=1);
$xml = <<<XML
<feed>
<product sku="A-100"><name>Desk lamp</name><price currency="EUR">24.90</price></product>
<product sku="B-200"><name>Chair</name><price currency="EUR">89.00</price></product>
</feed>
XML;
$feed = simplexml_load_string($xml);
if ($feed === false) {
throw new RuntimeException('Feed is not well-formed XML');
}
$bySku = [];
foreach ($feed->product as $product) {
$sku = (string) $product['sku'];
$bySku[$sku] = [
'name' => (string) $product->name,
'price' => (float) $product->price,
'currency' => (string) $product->price['currency'],
];
}
var_dump($feed->product->name === 'Desk lamp'); // bool(false) - object vs string
var_dump((string) $feed->product->name === 'Desk lamp'); // bool(true)go deeper
Recall the syntax: properties for child elements, array access for attributes, foreach for repeated elements, and cast to string before use.
Explain why every value is a SimpleXMLElement, which operations break without a cast, and how isset() handles missing children.
Map external XML into typed arrays or DTOs at the boundary, validating required fields instead of letting empty casts pass through.
Decide where XML parsing lives in an integration and how feed schema changes are detected before they corrupt imported data.
## Loading a document SimpleXML is PHP's convenience API for XML. Two functions load a document: - `simplexml_load_string(string $data, ...)` parses a string; - `simplexml_load_file(string $filename, ...)` parses a file or URL. Both return `SimpleXMLElement|false`: the root element on success, `false` when the XML is not well-formed (with warnings unless libxml internal errors are enabled). `new SimpleXMLElement($xml)` does the same but throws an `Exception` on failure. ## Navigating: properties for elements, array syntax for attributes Given a supplier feed such as ```xml <feed> <product sku="A-100"> <name>Desk lamp</name> <price currency="EUR">24.90</price> </product> </feed> ``` the root is `<feed>`, so: | Expression | Returns | |---|---| | `$feed->product` | the first `<product>` (iterable over all of them) | | `$feed->product->name` | the first `<name>` inside it | | `$feed->product['sku']` | the `sku` attribute | | `$feed->product->price['currency']` | the `currency` attribute of `<price>` | | `$feed->product->{'list-price'}` | a child whose name is not a valid PHP identifier | | `count($feed->product)` | how many `<product>` siblings there are | `foreach ($feed->product as $product)` iterates every `<product>` child, which is the normal way to process repeated records. ## Why the cast matters Every expression in that table returns a **`SimpleXMLElement` object**, even when it represents plain text. The object converts itself to its text content when PHP asks for a string, but many operations never ask: - **strict comparison**: `$product->name === 'Desk lamp'` compares an object with a string, so it is always `false`; - **array keys**: `$bySku[$product['sku']] = $product;` fails, because an object is not a valid key; - **strict types**: with `declare(strict_types=1)`, `strlen($product->name)` throws a `TypeError`, since strict mode does not convert objects to `string`; - **numbers**: arithmetic on `$product->price` needs a number, so write `(float) $product->price`; - **storage**: putting the object into a session, cache or JSON payload stores the object's structure, not the text. The manual's own guidance is to cast with `(string)` whenever an element or attribute is compared with a string or passed to a function that needs one. In practice, cast once when you map the element into your own array or DTO: ```php $row = [ 'sku' => (string) $product['sku'], 'name' => (string) $product->name, 'price' => (float) $product->price, ]; ``` ## Missing elements and attributes Asking for a child that does not exist does not return `null`; it returns an empty `SimpleXMLElement`, and chaining further properties on it does not fail. Use `isset()`: - `isset($product->discount)` is `false` when there is no `<discount>` element; - `isset($product['sku'])` checks an attribute. Casting a missing element with `(string)` gives `''`, which can silently turn a required field into an empty value, so validate required fields explicitly. ## Where SimpleXML stops SimpleXML is ideal for reading and light edits (`addChild()`, `addAttribute()`, `asXML()`). It is not the tool for moving nodes, handling mixed content in detail, or parsing a multi-gigabyte file, since it loads the whole document into memory. ## Iterating and counting correctly A few idioms cover almost every read: - `foreach ($feed->product as $product)` visits every `<product>` child of `<feed>`; - `foreach ($product->children() as $child)` visits all child elements, whatever their names, and `$child->getName()` returns each name; - `foreach ($product->attributes() as $name => $value)` lists attributes; - `$feed->product[1]` is the second `<product>`, since numeric offsets pick among same-named siblings. ## A mapping checklist 1. Check the loader result for `false` before navigating. 2. Cast every value once, at the point where it leaves SimpleXML. 3. Use `isset()` for optional elements and attributes; fail on missing required ones. 4. Never pass `SimpleXMLElement` objects deeper into the application.
- How do you tell a missing <discount> element from an empty one in SimpleXML?`isset($product->discount)` is `false` when the element is absent and `true` when `<discount/>` exists, even if empty. `(string) $product->discount` returns `''` in both cases, so checking the cast value alone cannot separate them.
- What does simplexml_load_string() return for malformed XML?It returns `false` and, unless `libxml_use_internal_errors(true)` is active, emits warnings describing the parse errors. The constructor `new SimpleXMLElement($xml)` instead throws an `Exception`. Check for `false` before navigating.
saying these in an interview costs you the question
- Believing $product->name returns a plain PHP string
- Reading attributes as properties, like $product->sku
- Expecting a missing child element to be null
- Using an uncast SimpleXMLElement as an array key
- Comparing SimpleXML values with === without casting