Skip to content

21. Processing XML Documents

The web services we have written so far exchange JSON data. The XML (eXtensible Markup Language) has long been the most widely used exchange format, and it is still commonly encountered: SOAP web services, RSS feeds, configuration files, office documents (a [.odt] or [.docx] file is an archive of XML files). PHP provides two APIs for working with it:

  • [SimpleXML]: a very simple API, where the document elements become object properties;
  • [DOM] (Document Object Model): the standard API of W3C, which is more comprehensive and also allows you to modify a document. PHP 8.4 introduced a new version, in the [Dom] namespace, compliant with the current web standard.

The scripts in this chapter are located in the [scripts-console/xml] folder:

1
2
3
4
scripts-console/xml
├── data.xml
├── dom-01.php
└── simplexml-01.php

21.1. The document under consideration

We are considering the following file: XML [data.xml]:

<?xml version="1.0" encoding="UTF-8"?>

<tribe>
    <teacher>
        <person gender="M">
            <lastName>dupont</lastName>
            <firstName>jean</firstName>
            <age>28</age>
            this is a comment
        </person>
        <section>27</section>
    </teacher>
    <student>
        <person gender="F">
            <lastName>martin</lastName>
            <firstName>charline</firstName>
            <age>22</age>
        </person>
        <course>dess IAIE</course>
    </student>
</tribe>

A XML document is a tree of elements:

  • line 1: the prolog, which specifies the version of XML and the document’s character encoding;
  • lines 3–21: the root element [tribe]. A XML document has exactly one root;
  • lines 4–12: the [teacher] element, a child of [tribe]. It has two children, [person] and [section];
  • line 5: the element [person] has an attribute [gender] with a value of [M];
  • line 9: an element can contain both other elements and text;
  • lines 13–20: the element [student], which has the same structure as [teacher].

21.2. Processing with SimpleXML

We analyze this document using the following [simplexml-01.php] script:

<?php

declare(strict_types=1);

// the XML file to be used
const FILE_NAME = "data.xml";

// loading the XML document
$xml = simplexml_load_file(FILE_NAME);
if ($xml === false) {
    print "The file [" . FILE_NAME . "] could not be used\n";
    exit(1);
}
// display of the document
print_r($xml);
// the [gender] attribute of the teaching person
print_r($xml->teacher->person['gender']);
// values of elements and attributes, converted to strings
$lastName = (string) $xml->teacher->person->lastName;
print "lastName=$lastName\n";
$gender = (string) $xml->teacher->person['gender'];
print "gender=$gender\n";
$course = (string) $xml->student->course;
print "course=$course\n";
// existence of an element
print "isset(lastName)=" . var_export(isset($xml->teacher->person->lastName), true) . "\n";
print "isset(xx)=" . var_export(isset($xml->teacher->person->xx), true) . "\n";
// a conversion to an integer
$age = (int) $xml->student->person->age;
print "age of the student=$age\n";
// iterating over all the <person> elements of the document with XPath
foreach ($xml->xpath('//person') as $person) {
    print "person: {$person->firstName} {$person->lastName} ({$person['gender']})\n";
}

Comments

  • line 6: The filename XML is a constant. The script is executed in its own folder, that of [data.xml];
  • Line 9: The [simplexml_load_file] function loads the document and returns an object of type [SimpleXMLElement], which represents the root element [tribe]. In the event of an error (missing file, malformed XML), it returns [false], which we test on line 10;
  • line 15: display of the document. [print_r] shows how [SimpleXML] views the tree XML;
  • line 17: child elements are accessed as properties ([$xml->teacher->person]), and attributes as array keys ([['gender']]). Note that the root [tribe] does not appear in the code: [$xml] is already that element;
  • line 19: [$xml->teacher->person->lastName] is not a string but an object [SimpleXMLElement]. Converting [(string)] yields its text. With [declare(strict_types=1)], it is best to perform this conversion explicitly before passing the value to a function that expects a string;
  • Line 21: the value of the [gender] attribute;
  • Lines 26–27: [isset] determines whether an element exists. The script PHP 7 displayed the Boolean result using [print], which returns [1] for [true] and an empty string for [false]. We use [var_export], which displays [true] or [false];
  • line 29: the conversion [(int)] returns the integer value of the element’s text;
  • line 32: the method [xpath] evaluates an expression XPath. The expression [//person] refers to all [person] elements in the document, regardless of their level. This returns an array of [SimpleXMLElement].

Console output

> php simplexml-01.php
SimpleXMLElement Object
(
    [teacher] => SimpleXMLElement Object
        (
            [person] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [gender] => M
                        )

                    [lastName] => dupont
                    [firstName] => jean
                    [age] => 28
                )

            [section] => 27
        )

    [student] => SimpleXMLElement Object
        (
            [person] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [gender] => F
                        )

                    [lastName] => martin
                    [firstName] => charline
                    [age] => 22
                )

            [course] => dess IAIE
        )

)
SimpleXMLElement Object
(
    [0] => M
)
lastName=dupont
gender=M
course=dess IAIE
isset(lastName)=true
isset(xx)=false
age of the student=22
person: jean dupont (M)
person: charline martin (F)
  • Lines 2–38: the document XML in the form of a [SimpleXMLElement] object. Note that the text [this is a comment] does not appear: [print_r] shows only the elements and attributes;
  • lines 39–42: an attribute is also a [SimpleXMLElement] object;
  • lines 43–48: the values read from the document;
  • lines 49–50: the XPath traversal of the two people.

21.3. Processing with the new DOM API from PHP 8.4

Starting with PHP 8.4, the [Dom] namespace offers a new DOM API: the [Dom\XMLDocument] class for XML documents and the [Dom\HTMLDocument] class for HTML5 documents. It is a better replacement for the old [DOMDocument] class (which is still available): it complies with the current DOM standard and provides the [querySelector] and [querySelectorAll] methods familiar to JavaScript developers. These methods allow you to search for elements using CSS selectors.

The [dom-01.php] script uses the same document:

<?php

declare(strict_types=1);

// the same document used with the new DOM API of PHP 8.4

use Dom\XMLDocument;

// the XML file to be used
const FILE_NAME = "data.xml";

try {
    // loading the XML document
    $document = XMLDocument::createFromFile(FILE_NAME);
} catch (Throwable $ex) {
    print "The file [" . FILE_NAME . "] could not be used: {$ex->getMessage()}\n";
    exit(1);
}
// the root element
print "root=" . $document->documentElement->tagName . "\n";
// the 1st teaching person, found with a CSS selector
$person = $document->querySelector('teacher > person');
print "lastName=" . $person->querySelector('lastName')->textContent . "\n";
print "gender=" . $person->getAttribute('gender') . "\n";
print "course=" . $document->querySelector('student > course')->textContent . "\n";
// a missing element: querySelector returns null
print "xx exists=" . var_export($person->querySelector('xx') !== null, true) . "\n";
// all the persons of the document
foreach ($document->querySelectorAll('person') as $person) {
    // the nullsafe operator ?-> protects against a missing element
    $firstName = $person->querySelector('firstName')?->textContent ?? '?';
    $lastName = $person->querySelector('lastName')?->textContent ?? '?';
    print "person: $firstName $lastName ({$person->getAttribute('gender')})\n";
}
// modifying the document: we change the content of the teacher's <section> element
$section = $document->querySelector('teacher > section');
$section->textContent = "26";
// the modified document, as XML text
print $document->saveXml($document->querySelector('teacher')) . "\n";

Comments

  • line 7: the [XMLDocument] class in the [Dom] namespace;
  • Line 14: The static method [createFromFile] loads the document. Unlike [simplexml_load_file], it throws an exception if an error occurs;
  • line 20: the property [documentElement] is the root element; [tagName] is its name;
  • line 22: the selector CSS [teacher > person] refers to an element [person] that is a direct child of an element [teacher]. [querySelector] returns the first matching element, or [null] if there are none;
  • line 23: the [textContent] property is the text contained in the element;
  • line 24: the value of an attribute;
  • line 27: a missing element returns [null];
  • line 29: all [person] elements in the document;
  • Line 31: If the element [firstName] were missing, [querySelector('firstName')] would result in [null]. The null-safe operator [?->] (PHP 8.0) then returns [null] instead of causing an error, and the operator [??] replaces this [null] with ['?'];
  • line 37: the API DOM also allows the document to be modified: here, the text of the [section] element is changed;
  • line 39: [saveXml] returns the text XML from the document, or from one of its elements if it is passed as a parameter.

Console output

> php dom-01.php
root=tribe
lastName=dupont
gender=M
course=dess IAIE
xx exists=false
person: jean dupont (M)
person: charline martin (F)
<teacher>
        <person gender="M">
            <lastName>dupont</lastName>
            <firstName>jean</firstName>
            <age>28</age>
            this is a comment
        </person>
        <section>26</section>
    </teacher>
  • line 16: the modification made by the script.

These two examples do not show all the capabilities of [SimpleXML] and [DOM], but they are sufficient for us to write a new version of the application exercise. The documentation for PHP describes them in detail: SimpleXML and DOM.