XmlService Class
XmlService is the entry point of the library: it encodes, decodes and validates XML documents in one place. It gives the work to three components, that can also be used alone.
Service Structure
- XmlEncoder: Converts PHP arrays to XML documents.
- XmlDecoder: Converts XML documents to PHP arrays.
- XmlValidator: Validates XML documents against XSD schemas.
XmlServiceInterface is the three interfaces together (XmlEncoderInterface, XmlDecoderInterface and XmlValidatorInterface).
Basic Usage
use Derafu\Xml\Service\XmlEncoder;
use Derafu\Xml\Service\XmlDecoder;
use Derafu\Xml\Service\XmlValidator;
use Derafu\Xml\Service\XmlService;
// Initialize the components.
$encoder = new XmlEncoder();
$decoder = new XmlDecoder();
$validator = new XmlValidator();
// Create the service.
$xmlService = new XmlService($encoder, $decoder, $validator);
In an application with the Symfony container, resources/config/xml-services.yaml of the library registers the interfaces with their classes.
Encoding (Array to XML)
The encode() method converts a PHP array to an XML document (see Array Structure):
$data = [
'invoice' => [
'@attributes' => [
'id' => '12345',
'date' => '2025-03-05'
],
'client' => [
'name' => 'Acme Corporation',
'tax_id' => '123456789'
],
'items' => [
'item' => [
[
'@attributes' => ['id' => '1'],
'description' => 'Product A',
'quantity' => '2',
'price' => '19.99'
],
[
'@attributes' => ['id' => '2'],
'description' => 'Product B',
'quantity' => '1',
'price' => '29.99'
]
]
],
'total' => '69.97'
]
];
$xmlDocument = $xmlService->encode($data);
$xmlDocument->saveXml() gives:
<?xml version="1.0" encoding="UTF-8"?>
<invoice id="12345" date="2025-03-05">
<client>
<name>Acme Corporation</name>
<tax_id>123456789</tax_id>
</client>
<items>
<item id="1">
<description>Product A</description>
<quantity>2</quantity>
<price>19.99</price>
</item>
<item id="2">
<description>Product B</description>
<quantity>1</quantity>
<price>29.99</price>
</item>
</items>
<total>69.97</total>
</invoice>
The encoding of the XML is UTF-8. To have another one: $xmlDocument->setEncoding('ISO-8859-1').
An array that can not be an XML throws an XmlEncoderException (see Exceptions).
Using Namespaces
The namespaces are declared in the @attributes of the element, with xmlns for the default one and xmlns:prefix for the others. The names of the elements have the prefix:
$data = [
'inv:invoice' => [
'@attributes' => [
'xmlns:inv' => 'http://example.com/invoice',
'id' => '12345',
],
'inv:client' => 'Acme Corporation',
],
];
$xmlDocument = $xmlService->encode($data);
This generates:
<?xml version="1.0" encoding="UTF-8"?>
<inv:invoice xmlns:inv="http://example.com/invoice" id="12345">
<inv:client>Acme Corporation</inv:client>
</inv:invoice>
Decoding (XML to Array)
The decode() method converts an XML document to a PHP array:
// Assuming $xmlDocument is a Derafu\Xml\XmlDocument instance.
$array = $xmlService->decode($xmlDocument);
// Or starting from a DOMElement of the document.
$element = $xmlDocument->getDocumentElement();
$array = $xmlService->decode($element);
A document without document element is an empty array.
The array has what the XML has: attributes and the namespaces that the elements declare in @attributes, comments in @comment, and text exactly as it is. See Array Structure and Round Trip and Fidelity.
Handling Repeated Elements
Elements with the same name are collected into a list:
<root>
<item>Value 1</item>
<item>Value 2</item>
<item>Value 3</item>
</root>
Becomes:
[
'root' => [
'item' => [
'Value 1',
'Value 2',
'Value 3'
]
]
]
An element that appears once is its value, not a list of one.
Validation
The validate() method checks an XML document against an XSD schema:
use Derafu\Xml\Exception\XmlException;
try {
$xmlService->validate($xmlDocument, '/path/to/schema.xsd');
echo "XML is valid!";
} catch (XmlException $e) {
echo "Validation failed: " . $e->getMessage();
// The errors of libxml, as XmlError.
foreach ($e->getErrors() as $error) {
echo "- $error\n";
}
// The document that was validated.
$document = $e->getXmlDocument();
}
The message has all the errors:
The XML validation failed using the schema schema.xsd: Line 1, column 0: Element 'wrong': No matching global declaration available for the validation root.
The errors are explained and translated from their code (see Exceptions), and the namespace of the names, that libxml writes between braces ({urn:example}), is taken out.
Schema Auto-Detection
If the XML document has an xsi:schemaLocation attribute, the schema path can be omitted: the schema is the path that comes after the namespace.
// XML with xsi:schemaLocation="http://example.com/ns /path/to/schema.xsd"
try {
$xmlService->validate($xmlDocument); // Will use /path/to/schema.xsd
} catch (XmlException $e) {
// Handle validation error.
}
The path has to be one of a file that exists. If the document does not have xsi:schemaLocation, or the path does not exist, an XmlException is thrown.
Integration Example
This example shows a typical workflow:
// Initialize the service.
$xmlService = new XmlService(new XmlEncoder(), new XmlDecoder(), new XmlValidator());
// Create XML from array.
$data = ['root' => ['element' => 'value']];
$xmlDocument = $xmlService->encode($data);
// Save to a file.
file_put_contents('document.xml', $xmlDocument->saveXml());
// Load from a file.
$loadedXml = new \Derafu\Xml\XmlDocument();
$loadedXml->loadXml(file_get_contents('document.xml'));
try {
// Validate.
$xmlService->validate($loadedXml, '/path/to/schema.xsd');
// Convert back to array.
$array = $xmlService->decode($loadedXml);
// Process the data.
// ...
} catch (\Derafu\Xml\Exception\XmlException $e) {
// Handle validation errors.
}